library(tidyverse)
## Warning: package 'ggplot2' was built under R version 4.5.1
library(quantmod)
library(tsibble)
library(fable)
## Warning: package 'fabletools' was built under R version 4.5.1
library(feasts)
## Warning: package 'feasts' was built under R version 4.5.1

APPL Data Wrangle

getSymbols("AAPL", src = "yahoo", from = "2015-01-01")
## [1] "AAPL"
apple_daily <- tibble(
  date     = as.Date(zoo::index(AAPL)),
  adjusted = as.numeric(AAPL$AAPL.Adjusted)
)

glimpse(apple_daily)
## Rows: 2,736
## Columns: 2
## $ date     <date> 2015-01-02, 2015-01-05, 2015-01-06, 2015-01-07, 2015-01-08, …
## $ adjusted <dbl> 24.23755, 23.55475, 23.55696, 23.88728, 24.80508, 24.83168, 2…

Wrangle Data

apple_monthly <- apple_daily %>%
  drop_na(adjusted) %>%                 
  mutate(month = yearmonth(date)) %>%   
  group_by(month) %>%
  summarize(price = last(adjusted), .groups = "drop") %>% 
  arrange(month)

nrow(apple_monthly)
## [1] 131
ncol(apple_monthly)
## [1] 2
summary(apple_monthly$price)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   21.25   37.88   88.47  106.12  168.95  270.11
min(apple_monthly$month); max(apple_monthly$month)
## <yearmonth[1]>
## [1] "2015 Jan"
## <yearmonth[1]>
## [1] "2025 Nov"

Tsibble Conversion

apple_ts <- apple_monthly %>%
  as_tsibble(index = month)

apple_ts
## # A tsibble: 131 x 2 [1M]
##       month price
##       <mth> <dbl>
##  1 2015 Jan  26.0
##  2 2015 Feb  28.6
##  3 2015 Mar  27.7
##  4 2015 Apr  27.9
##  5 2015 May  29.1
##  6 2015 Jun  28.0
##  7 2015 Jul  27.1
##  8 2015 Aug  25.3
##  9 2015 Sep  24.8
## 10 2015 Oct  26.8
## # ℹ 121 more rows
interval(apple_ts)
## <interval[1]>
## [1] 1M
autoplot(apple_ts, price) +
  labs(
    title = "Apple (AAPL) Monthly Adjusted Close Price",
    x = "Month",
    y = "Price (USD)"
  )

Forecasting MEAN, NAIVE, SNAIVE, DRIFT

fit_apple <- apple_ts %>%
  model(
    mean   = MEAN(price),
    naive  = NAIVE(price),
    snaive = SNAIVE(price),
    drift  = RW(price ~ drift())
  )

fit_apple
## # A mable: 1 x 4
##      mean   naive   snaive         drift
##   <model> <model>  <model>       <model>
## 1  <MEAN> <NAIVE> <SNAIVE> <RW w/ drift>

Forecast ahead

fc_apple <- fit_apple %>%
  forecast(h = "12 months", level = c(80, 95))
autoplot(fc_apple, apple_ts) +
  labs(
    title = "Forecasts for Apple (AAPL) Monthly Prices",
    x = "Month",
    y = "Price (USD)"
  )

Error Metrics

apple_errors <- accuracy(fit_apple)
apple_error_table <- apple_errors %>%
  select(.model, RMSE, MAE, MAPE)

apple_error_table
## # A tibble: 4 × 4
##   .model  RMSE   MAE   MAPE
##   <chr>  <dbl> <dbl>  <dbl>
## 1 mean   74.1  67.0  121.  
## 2 naive   9.11  6.70   6.50
## 3 snaive 31.0  23.9   22.1 
## 4 drift   8.92  6.61   6.81

What Models Performed Best

Based on the error table (RMSE, MAE,& MAPE), the Naive model performed the best overall. This makes sense for a Financial Time Series like Apple; where prices are volatile, non stationary in patter and often prices are predicted based on closing prices from day previous. Apple’s stock price trends upward over the long-run, offset by earnings, market trends, and policy change; Structured models (e.g.Mean or Seasonal Naive) less effective. The Drift model performed reasonably well but lagged behind Naive; Likely due to Apple’s Month-to-Month returns are not stable enough for a long-term drift pattern.

Reasons behind Naive Model

The Naive method works better because it aligns naturally with how markets behave. Stocks are known to follow a random pattern, meaning price changes are unpredictable to an exact and largely reflect new information that can’t be forecast-ed by patterns. Since Apple’s price movement are influenced by innovation cycles (e.g. AI Growth), interest rate expectations, and investor beliefs, seasonal structures in models like SNAIVE can’t leverage. The Mean model also fails because a constant flat forecast ignores trends and volatility in market. Oppositely, the Naive model adapts instantly to the latest price level, making it the more realistic baseline for short-term forecasts.

Limitations of model

A clear limitation is is that all basic models are extremely simple and don’t incorporate external factor (e.g. trading volume, earnings, or volatility measures). Stocks are noisy and basic time-series models can’t capture structural breakdowns, momentum, or periods of high volatility. Future models could use ARIMA or dynamic regression models with predictors like VIX, interest rates, or index returns. Overall, while the Naive model is a strong baseline for stock forecasting, more sophisticated tecniques would give a more informative forecast.

LS0tCnRpdGxlOiAiRmluYWwgUGFydCAxIgphdXRob3I6ICJDYW1lcm9uIFNtaXRoIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogb3BlbmludHJvOjpsYWJfcmVwb3J0Ci0tLQoKYGBge3IgbG9hZC1wYWNrYWdlcywgbWVzc2FnZT1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocXVhbnRtb2QpCmxpYnJhcnkodHNpYmJsZSkKbGlicmFyeShmYWJsZSkKbGlicmFyeShmZWFzdHMpCmBgYAoKIyBBUFBMIERhdGEgV3JhbmdsZQoKYGBge3J9CmdldFN5bWJvbHMoIkFBUEwiLCBzcmMgPSAieWFob28iLCBmcm9tID0gIjIwMTUtMDEtMDEiKQoKYXBwbGVfZGFpbHkgPC0gdGliYmxlKAogIGRhdGUgICAgID0gYXMuRGF0ZSh6b286OmluZGV4KEFBUEwpKSwKICBhZGp1c3RlZCA9IGFzLm51bWVyaWMoQUFQTCRBQVBMLkFkanVzdGVkKQopCgpnbGltcHNlKGFwcGxlX2RhaWx5KQpgYGAKCiMgV3JhbmdsZSBEYXRhIAoKYGBge3J9CmFwcGxlX21vbnRobHkgPC0gYXBwbGVfZGFpbHkgJT4lCiAgZHJvcF9uYShhZGp1c3RlZCkgJT4lICAgICAgICAgICAgICAgICAKICBtdXRhdGUobW9udGggPSB5ZWFybW9udGgoZGF0ZSkpICU+JSAgIAogIGdyb3VwX2J5KG1vbnRoKSAlPiUKICBzdW1tYXJpemUocHJpY2UgPSBsYXN0KGFkanVzdGVkKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lIAogIGFycmFuZ2UobW9udGgpCgpucm93KGFwcGxlX21vbnRobHkpCm5jb2woYXBwbGVfbW9udGhseSkKc3VtbWFyeShhcHBsZV9tb250aGx5JHByaWNlKQptaW4oYXBwbGVfbW9udGhseSRtb250aCk7IG1heChhcHBsZV9tb250aGx5JG1vbnRoKQpgYGAKCiMgVHNpYmJsZSBDb252ZXJzaW9uIApgYGB7cn0KYXBwbGVfdHMgPC0gYXBwbGVfbW9udGhseSAlPiUKICBhc190c2liYmxlKGluZGV4ID0gbW9udGgpCgphcHBsZV90cwppbnRlcnZhbChhcHBsZV90cykKICAKYXV0b3Bsb3QoYXBwbGVfdHMsIHByaWNlKSArCiAgbGFicygKICAgIHRpdGxlID0gIkFwcGxlIChBQVBMKSBNb250aGx5IEFkanVzdGVkIENsb3NlIFByaWNlIiwKICAgIHggPSAiTW9udGgiLAogICAgeSA9ICJQcmljZSAoVVNEKSIKICApCmBgYAoKIyBGb3JlY2FzdGluZyBNRUFOLCBOQUlWRSwgU05BSVZFLCBEUklGVCAKYGBge3J9CmZpdF9hcHBsZSA8LSBhcHBsZV90cyAlPiUKICBtb2RlbCgKICAgIG1lYW4gICA9IE1FQU4ocHJpY2UpLAogICAgbmFpdmUgID0gTkFJVkUocHJpY2UpLAogICAgc25haXZlID0gU05BSVZFKHByaWNlKSwKICAgIGRyaWZ0ICA9IFJXKHByaWNlIH4gZHJpZnQoKSkKICApCgpmaXRfYXBwbGUKYGBgCgojIEZvcmVjYXN0IGFoZWFkIApgYGB7cn0KZmNfYXBwbGUgPC0gZml0X2FwcGxlICU+JQogIGZvcmVjYXN0KGggPSAiMTIgbW9udGhzIiwgbGV2ZWwgPSBjKDgwLCA5NSkpCmF1dG9wbG90KGZjX2FwcGxlLCBhcHBsZV90cykgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJGb3JlY2FzdHMgZm9yIEFwcGxlIChBQVBMKSBNb250aGx5IFByaWNlcyIsCiAgICB4ID0gIk1vbnRoIiwKICAgIHkgPSAiUHJpY2UgKFVTRCkiCiAgKQpgYGAKCiMgRXJyb3IgTWV0cmljcyAKYGBge3J9CmFwcGxlX2Vycm9ycyA8LSBhY2N1cmFjeShmaXRfYXBwbGUpCmFwcGxlX2Vycm9yX3RhYmxlIDwtIGFwcGxlX2Vycm9ycyAlPiUKICBzZWxlY3QoLm1vZGVsLCBSTVNFLCBNQUUsIE1BUEUpCgphcHBsZV9lcnJvcl90YWJsZQpgYGAKIyBXaGF0IE1vZGVscyBQZXJmb3JtZWQgQmVzdCAKICBCYXNlZCBvbiB0aGUgZXJyb3IgdGFibGUgKFJNU0UsIE1BRSwmIE1BUEUpLCB0aGUgTmFpdmUgbW9kZWwgcGVyZm9ybWVkIHRoZSBiZXN0IG92ZXJhbGwuIFRoaXMgbWFrZXMgc2Vuc2UgZm9yIGEgRmluYW5jaWFsIFRpbWUgU2VyaWVzIGxpa2UgQXBwbGU7IHdoZXJlIHByaWNlcyBhcmUgdm9sYXRpbGUsIG5vbiBzdGF0aW9uYXJ5IGluIHBhdHRlciBhbmQgb2Z0ZW4gcHJpY2VzIGFyZSBwcmVkaWN0ZWQgYmFzZWQgb24gY2xvc2luZyBwcmljZXMgZnJvbSBkYXkgcHJldmlvdXMuIEFwcGxlJ3Mgc3RvY2sgcHJpY2UgdHJlbmRzIHVwd2FyZCBvdmVyIHRoZSBsb25nLXJ1biwgb2Zmc2V0IGJ5IGVhcm5pbmdzLCBtYXJrZXQgdHJlbmRzLCBhbmQgcG9saWN5IGNoYW5nZTsgU3RydWN0dXJlZCBtb2RlbHMgKGUuZy5NZWFuIG9yIFNlYXNvbmFsIE5haXZlKSBsZXNzIGVmZmVjdGl2ZS4gVGhlIERyaWZ0IG1vZGVsIHBlcmZvcm1lZCByZWFzb25hYmx5IHdlbGwgYnV0IGxhZ2dlZCBiZWhpbmQgTmFpdmU7IExpa2VseSBkdWUgdG8gQXBwbGUncyBNb250aC10by1Nb250aCByZXR1cm5zIGFyZSBub3Qgc3RhYmxlIGVub3VnaCBmb3IgYSBsb25nLXRlcm0gZHJpZnQgcGF0dGVybi4gCgojIFJlYXNvbnMgYmVoaW5kIE5haXZlIE1vZGVsIAogIFRoZSBOYWl2ZSBtZXRob2Qgd29ya3MgYmV0dGVyIGJlY2F1c2UgaXQgYWxpZ25zIG5hdHVyYWxseSB3aXRoIGhvdyBtYXJrZXRzIGJlaGF2ZS4gU3RvY2tzIGFyZSBrbm93biB0byBmb2xsb3cgYSByYW5kb20gcGF0dGVybiwgbWVhbmluZyBwcmljZSBjaGFuZ2VzIGFyZSB1bnByZWRpY3RhYmxlIHRvIGFuIGV4YWN0IGFuZCBsYXJnZWx5IHJlZmxlY3QgbmV3IGluZm9ybWF0aW9uIHRoYXQgY2FuJ3QgYmUgZm9yZWNhc3QtZWQgYnkgcGF0dGVybnMuIFNpbmNlIEFwcGxlJ3MgcHJpY2UgbW92ZW1lbnQgYXJlIGluZmx1ZW5jZWQgYnkgaW5ub3ZhdGlvbiBjeWNsZXMgKGUuZy4gQUkgR3Jvd3RoKSwgaW50ZXJlc3QgcmF0ZSBleHBlY3RhdGlvbnMsIGFuZCBpbnZlc3RvciBiZWxpZWZzLCBzZWFzb25hbCBzdHJ1Y3R1cmVzIGluIG1vZGVscyBsaWtlIFNOQUlWRSBjYW4ndCBsZXZlcmFnZS4gVGhlIE1lYW4gbW9kZWwgYWxzbyBmYWlscyBiZWNhdXNlIGEgY29uc3RhbnQgZmxhdCBmb3JlY2FzdCBpZ25vcmVzIHRyZW5kcyBhbmQgdm9sYXRpbGl0eSBpbiBtYXJrZXQuIE9wcG9zaXRlbHksIHRoZSBOYWl2ZSBtb2RlbCBhZGFwdHMgaW5zdGFudGx5IHRvIHRoZSBsYXRlc3QgcHJpY2UgbGV2ZWwsIG1ha2luZyBpdCB0aGUgbW9yZSByZWFsaXN0aWMgYmFzZWxpbmUgZm9yIHNob3J0LXRlcm0gZm9yZWNhc3RzLiAgCgojIExpbWl0YXRpb25zIG9mIG1vZGVsIAogIEEgY2xlYXIgbGltaXRhdGlvbiBpcyBpcyB0aGF0IGFsbCBiYXNpYyBtb2RlbHMgYXJlIGV4dHJlbWVseSBzaW1wbGUgYW5kIGRvbid0IGluY29ycG9yYXRlIGV4dGVybmFsIGZhY3RvciAoZS5nLiB0cmFkaW5nIHZvbHVtZSwgZWFybmluZ3MsIG9yIHZvbGF0aWxpdHkgbWVhc3VyZXMpLiBTdG9ja3MgYXJlIG5vaXN5IGFuZCBiYXNpYyB0aW1lLXNlcmllcyBtb2RlbHMgY2FuJ3QgY2FwdHVyZSBzdHJ1Y3R1cmFsIGJyZWFrZG93bnMsIG1vbWVudHVtLCBvciBwZXJpb2RzIG9mIGhpZ2ggdm9sYXRpbGl0eS4gRnV0dXJlIG1vZGVscyBjb3VsZCB1c2UgQVJJTUEgb3IgZHluYW1pYyByZWdyZXNzaW9uIG1vZGVscyB3aXRoIHByZWRpY3RvcnMgbGlrZSBWSVgsIGludGVyZXN0IHJhdGVzLCBvciBpbmRleCByZXR1cm5zLiBPdmVyYWxsLCB3aGlsZSB0aGUgTmFpdmUgbW9kZWwgaXMgYSBzdHJvbmcgYmFzZWxpbmUgZm9yIHN0b2NrIGZvcmVjYXN0aW5nLCBtb3JlIHNvcGhpc3RpY2F0ZWQgdGVjbmlxdWVzIHdvdWxkIGdpdmUgYSBtb3JlIGluZm9ybWF0aXZlIGZvcmVjYXN0LiAK