Predictive Analytics Discussion 5: Mutual Funds

Setup

### Environment
  # Clear the workspace
  rm(list = ls()) # Clear environment
  gc()            # Clear unused memory
##          used (Mb) gc trigger (Mb) limit (Mb) max used (Mb)
## Ncells 522848 28.0    1159205   62         NA   669291 35.8
## Vcells 970910  7.5    8388608   64      16384  1840004 14.1
  cat("\f")       # Clear the console
  dev.off         # Clear the charts
## function (which = dev.cur()) 
## {
##     if (which == 1) 
##         stop("cannot shut down device 1 (the null device)")
##     .External(C_devoff, as.integer(which))
##     dev.cur()
## }
## <bytecode: 0x7fef54a43830>
## <environment: namespace:grDevices>
  #make vector containing the packages
  packages <- c("ggplot2",
                "knitr",
                "fpp3",
                "forecast",
                "dplyr",
                "lubridate",
                "grid",
                "gridExtra")
  #Install packages
  for (i in 1:length(packages)) {# Iterate through each package in the 'packages' vector.
    if (!packages[i] %in% rownames(installed.packages())) {#check if package is installed
      install.packages(packages[i], #install package if it isn't already
                       repos = "http://cran.rstudio.com/",
                       dependencies = TRUE)}
    library(packages[i], character.only = TRUE)} # Load the package into the R session.
## ── Attaching packages ────────────────────────────────────────────── fpp3 0.5 ──
## ✔ tibble      3.2.1     ✔ tsibbledata 0.4.1
## ✔ dplyr       1.1.1     ✔ feasts      0.3.2
## ✔ tidyr       1.3.0     ✔ fable       0.3.3
## ✔ lubridate   1.9.2     ✔ fabletools  0.4.1
## ✔ tsibble     1.1.4
## ── Conflicts ───────────────────────────────────────────────── fpp3_conflicts ──
## ✖ lubridate::date()    masks base::date()
## ✖ dplyr::filter()      masks stats::filter()
## ✖ tsibble::intersect() masks base::intersect()
## ✖ tsibble::interval()  masks lubridate::interval()
## ✖ dplyr::lag()         masks stats::lag()
## ✖ tsibble::setdiff()   masks base::setdiff()
## ✖ tsibble::union()     masks base::union()
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
## 
## Attaching package: 'gridExtra'
## The following object is masked from 'package:dplyr':
## 
##     combine
  # Remove unnecessary values from environment
  rm(i,packages)
  
### Data

# Import datasets
  # Energy
  ET <- read.csv("/Users/cam/Desktop/PREDICTIVE ANALYTICS/Discussions/Data/ET_stock.csv")
  XOM <- read.csv("/Users/cam/Desktop/PREDICTIVE ANALYTICS/Discussions/Data/XOM_stock.csv")
  
  # Pharma
  LLY <- read.csv("/Users/cam/Desktop/PREDICTIVE ANALYTICS/Discussions/Data/LLY_stock.csv")
  NVO <- read.csv("/Users/cam/Desktop/PREDICTIVE ANALYTICS/Discussions/Data/NVO_stock.csv")

  # Remove the dollar sign and convert to numeric
  ET$Close.Last <- as.numeric(sub("\\$", "", ET$Close.Last))
  XOM$Close.Last <- as.numeric(sub("\\$", "", XOM$Close.Last))
  LLY$Close.Last <- as.numeric(sub("\\$", "", LLY$Close.Last))
  NVO$Close.Last <- as.numeric(sub("\\$", "", NVO$Close.Last))

  # Combine to be 1 dataset
  stocks <-data.frame(
    Date = ET$Date,
    ET = ET$Close.Last,
    XOM = XOM$Close.Last,
    NVO = NVO$Close.Last,
    LLY = LLY$Close.Last
  )

  # Convert values to proper format
  stocks$Date <- as.Date(stocks$Date, format = "%m/%d/%y")
  
  # Aggregate daily values to monthly average
  stocks$Date <- format(stocks$Date, "%Y-%m") # Filters down to monthly
  stocks <- aggregate(cbind(ET, LLY, NVO, XOM) ~ Date, data = stocks, FUN = mean)

  # Remove unnecessary tables
  rm(ET,LLY,NVO,XOM) 
  
  # Round all values
  stocks$ET <- round(stocks$ET,2)
  stocks$XOM <- round(stocks$XOM,2)
  stocks$NVO <- round(stocks$NVO,2)
  stocks$LLY <- round(stocks$LLY,2)
  
# Create Mutual Fund variable (Puts about $200 towards each stock)
  stocks$Fund <- (13.01*stocks$ET) + (1.67*stocks$LLY) + (8.12*stocks$NVO) + (2.46*stocks$XOM)

# Break into testing and training
  # Convert values to proper format... again
  stocks$Date <- as.Date(paste(stocks$Date, "-01", sep=""), format="%Y-%m-%d")

# Make a porfolio table
  portfolio <- data.frame(
    Date = stocks$Date,
    ET = 13.01*stocks$ET,
    LLY = 1.67*stocks$LLY,
    NVO = 8.12*stocks$NVO,
    XOM = 2.46*stocks$XOM
  )
  
  # Round all values
  portfolio$ET <- round(portfolio$ET,2)
  portfolio$XOM <- round(portfolio$XOM,2)
  portfolio$NVO <- round(portfolio$NVO,2)
  portfolio$LLY <- round(portfolio$LLY,2)
  
  # Add fund and sector performance to portfolio
  portfolio$Fund <- portfolio$ET + portfolio$LLY + portfolio$NVO + portfolio$XOM
  portfolio$Energy <- portfolio$ET + portfolio$XOM
  portfolio$Pharma <- portfolio$LLY + portfolio$NVO
  
  # Training data
  train <- subset(portfolio, Date <= max(portfolio$Date) - years(1))
  
  # Testing data
  test <- subset(portfolio, Date > max(portfolio$Date) - years(1))
  
# Make time series for all
  # Training ts
  ET_train <- ts(train$ET,
              start = c(2019,04),
              frequency = 12
              )
  
  XOM_train <- ts(train$XOM,
              start = c(2019,04),
              frequency = 12
              )
  
  LLY_train <- ts(train$LLY,
              start = c(2019,04),
              frequency = 12
              )
  
  NVO_train <- ts(train$NVO,
              start = c(2019,04),
              frequency = 12
              )
  
  Fund_train <- ts(train$Fund,
              start = c(2019,04),
              frequency = 12
              )
  
  Energy_train <- ts(train$Energy,
              start = c(2019,04),
              frequency = 12
              )
  
  Pharma_train <- ts(train$Pharma,
              start = c(2019,04),
              frequency = 12
              )
  
  # Testing ts
  ET_test <- ts(test$ET,
              start = c(2023,05),
              frequency = 12
              )

  XOM_test <- ts(test$XOM,
              start = c(2023,05),
              frequency = 12
              )
  
  LLY_test <- ts(test$LLY,
              start = c(2023,05),
              frequency = 12
              )
  
  NVO_test <- ts(test$NVO,
              start = c(2023,05),
              frequency = 12
              )
  
  Fund_test <- ts(test$Fund,
              start = c(2023,05),
              frequency = 12
              )
  
  Energy_test <- ts(test$Energy,
              start = c(2023,05),
              frequency = 12
              )
  
  Pharma_test <- ts(test$Pharma,
              start = c(2023,05),
              frequency = 12
              )
  
  
  # Full data
  ET <- ts(stocks$ET,
              start = c(2019,04),
              frequency = 12
              )

  XOM <- ts(stocks$XOM,
              start = c(2019,04),
              frequency = 12
              )
  
  LLY <- ts(stocks$LLY,
              start = c(2019,04),
              frequency = 12
              )
  
  NVO <- ts(stocks$NVO,
              start = c(2019,04),
              frequency = 12
              )
  Fund <- ts(portfolio$Fund,
              start = c(2019,04),
              frequency = 12
              )
  
  Energy <- ts(portfolio$Energy,
              start = c(2019,04),
              frequency = 12
              )
  
  Pharma <- ts(portfolio$Pharma,
              start = c(2019,04),
              frequency = 12
              )

Data

The mock mutual fund for this segment invests about $200 dollars per stock into 4 stocks in April, 2019. This makes for 13.01 shares of Energy Transfer and 2.46 shares of Exxon Mobile in the Energy sector and 1.67 shares of Eli Lilly and Co and 8.12 shares of Novo Nordisk in the pharma sector.

Models

Top-Down

Forecasts the total mutual fund performance

Middle-Out

Forecasts sector performance

Bottom-Up

Forecasts individual stock performance

Conclusion

Based on this example, it appears that the top-down style is the most accurate predictor although I would not become overly reliant on it. It seems that the key reason for this is that the aggregated data has a much smoother upward trend than some individual stocks. This appears to mainly be influenced by the high performance pharma stocks. After that, the bottom-up and middle-out performed similarly with the bottom-up performing marginally better. The key reason for this is likely that the middle-out significantly underestimated the pharma sector.