Load Required Libraries

library(quantmod)
## Loading required package: xts
## Loading required package: zoo
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
## Loading required package: TTR
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
library(rugarch)
## Warning: package 'rugarch' was built under R version 4.4.3
## Loading required package: parallel
## 
## Attaching package: 'rugarch'
## The following object is masked from 'package:stats':
## 
##     sigma
library(tseries)
## Warning: package 'tseries' was built under R version 4.4.3
library(PerformanceAnalytics)
## Warning: package 'PerformanceAnalytics' was built under R version 4.4.3
## 
## Attaching package: 'PerformanceAnalytics'
## The following object is masked from 'package:graphics':
## 
##     legend
library(FinTS)
## Warning: package 'FinTS' was built under R version 4.4.3

Get GOOGL Stock Data

getSymbols("GOOGL", src = "yahoo", from = "2018-01-01", to = "2025-01-01")
## [1] "GOOGL"
goog_data <- na.omit(Cl(GOOGL)) #Use closing prices only
plot(goog_data, main = "GOOGL Closing Prices")

Convert to Log Returns

You can also embed plots, for example:

goog_returns <- dailyReturn(goog_data, type = "log")
plot(goog_returns, main = "GOOGL Log Returns")

Test for ARCH Effects

Box.test(goog_returns, lag = 20, type = "Ljung-Box")
## 
##  Box-Ljung test
## 
## data:  goog_returns
## X-squared = 74.216, df = 20, p-value = 3.679e-08
ArchTest(goog_returns, lags = 12)
## 
##  ARCH LM-test; Null hypothesis: no ARCH effects
## 
## data:  goog_returns
## Chi-squared = 129.04, df = 12, p-value < 2.2e-16

Specify and Fit GARCH(1,1) Model

Since p-value for ARCH LM-test is very small, we reject the null hypothesis

spec <- ugarchspec(variance.model = list(model = "sGARCH", garchOrder = c(1,1)), mean.model = list(armaOrder = c(0,0)), distribution.model = "std") # Student-t distribution for heavy tails

fit <- ugarchfit(spec = spec, data = goog_returns)
fit
## 
## *---------------------------------*
## *          GARCH Model Fit        *
## *---------------------------------*
## 
## Conditional Variance Dynamics    
## -----------------------------------
## GARCH Model  : sGARCH(1,1)
## Mean Model   : ARFIMA(0,0,0)
## Distribution : std 
## 
## Optimal Parameters
## ------------------------------------
##         Estimate  Std. Error  t value Pr(>|t|)
## mu      0.001539    0.000351   4.3887  1.1e-05
## omega   0.000013    0.000002   6.5273  0.0e+00
## alpha1  0.089658    0.002685  33.3953  0.0e+00
## beta1   0.886031    0.013216  67.0398  0.0e+00
## shape   4.037919    0.331074  12.1964  0.0e+00
## 
## Robust Standard Errors:
##         Estimate  Std. Error  t value Pr(>|t|)
## mu      0.001539    0.000295   5.2123  0.0e+00
## omega   0.000013    0.000003   4.0305  5.6e-05
## alpha1  0.089658    0.011869   7.5539  0.0e+00
## beta1   0.886031    0.017139  51.6958  0.0e+00
## shape   4.037919    0.525728   7.6806  0.0e+00
## 
## LogLikelihood : 4637.547 
## 
## Information Criteria
## ------------------------------------
##                     
## Akaike       -5.2613
## Bayes        -5.2457
## Shibata      -5.2613
## Hannan-Quinn -5.2555
## 
## Weighted Ljung-Box Test on Standardized Residuals
## ------------------------------------
##                         statistic p-value
## Lag[1]                     0.3666  0.5449
## Lag[2*(p+q)+(p+q)-1][2]    1.3846  0.3887
## Lag[4*(p+q)+(p+q)-1][5]    3.1197  0.3856
## d.o.f=0
## H0 : No serial correlation
## 
## Weighted Ljung-Box Test on Standardized Squared Residuals
## ------------------------------------
##                         statistic p-value
## Lag[1]                  1.048e-08  0.9999
## Lag[2*(p+q)+(p+q)-1][5] 1.097e+00  0.8375
## Lag[4*(p+q)+(p+q)-1][9] 2.188e+00  0.8814
## d.o.f=2
## 
## Weighted ARCH LM Tests
## ------------------------------------
##             Statistic Shape Scale P-Value
## ARCH Lag[3]   0.03834 0.500 2.000  0.8448
## ARCH Lag[5]   0.28664 1.440 1.667  0.9438
## ARCH Lag[7]   1.01153 2.315 1.543  0.9117
## 
## Nyblom stability test
## ------------------------------------
## Joint Statistic:  21.5902
## Individual Statistics:              
## mu     0.04618
## omega  1.86210
## alpha1 0.21768
## beta1  0.36155
## shape  0.36039
## 
## Asymptotic Critical Values (10% 5% 1%)
## Joint Statistic:          1.28 1.47 1.88
## Individual Statistic:     0.35 0.47 0.75
## 
## Sign Bias Test
## ------------------------------------
##                    t-value   prob sig
## Sign Bias          0.14921 0.8814    
## Negative Sign Bias 0.01854 0.9852    
## Positive Sign Bias 0.32110 0.7482    
## Joint Effect       0.10349 0.9914    
## 
## 
## Adjusted Pearson Goodness-of-Fit Test:
## ------------------------------------
##   group statistic p-value(g-1)
## 1    20     36.47     0.009244
## 2    30     49.34     0.010626
## 3    40     56.17     0.036845
## 4    50     70.94     0.021842
## 
## 
## Elapsed time : 0.193414

Plot GARCH Model Results

plot(fit, which = "all")
## 
## please wait...calculating quantiles...

Model Diagnostics

very small autocorrelation bars - this suggests that most serial correlation has been removed from the residuals.however, p-value for ljung-box is just slightly below 0.05 so we barely reject the null hypothesis. There’s a small amount of autocorrelation left in the residuals. Not perfect but not alarming either. ARMA can be tweaked for better results

for the ARCH test, p-value is very high so we fail to reject the null hypothesis. This suggests that the GARCH model has successfully captured the volatility clustering - no ARCH effects remain in the residuals

residuals <- residuals(fit, standardize = TRUE)
acf(residuals, main = "ACF of Standardized Residuals")

Box.test(residuals, lag = 20, type = "Ljung-Box")
## 
##  Box-Ljung test
## 
## data:  residuals
## X-squared = 32.378, df = 20, p-value = 0.03944
# ARCH test on residuals
ArchTest(residuals, lags = 12)
## 
##  ARCH LM-test; Null hypothesis: no ARCH effects
## 
## data:  residuals
## Chi-squared = 6.2963, df = 12, p-value = 0.9004

Forecast Volatility

forecast <- ugarchforecast(fit, n.ahead = 10)
plot(forecast, which = 3)

Modify ARMA order to (1,1)

spec <- ugarchspec(variance.model = list(model = "sGARCH", garchOrder = c(1, 1)),
                   mean.model = list(armaOrder = c(1, 1)),
                   distribution.model = "std")  # Student-t distribution for heavy tails

fit <- ugarchfit(spec = spec, data = goog_returns)
fit
## 
## *---------------------------------*
## *          GARCH Model Fit        *
## *---------------------------------*
## 
## Conditional Variance Dynamics    
## -----------------------------------
## GARCH Model  : sGARCH(1,1)
## Mean Model   : ARFIMA(1,0,1)
## Distribution : std 
## 
## Optimal Parameters
## ------------------------------------
##         Estimate  Std. Error  t value Pr(>|t|)
## mu      0.001507    0.000249   6.0495        0
## ar1     0.840009    0.055583  15.1128        0
## ma1    -0.887317    0.046709 -18.9965        0
## omega   0.000013    0.000002   7.7498        0
## alpha1  0.087831    0.004139  21.2227        0
## beta1   0.889828    0.012973  68.5886        0
## shape   3.911377    0.320824  12.1916        0
## 
## Robust Standard Errors:
##         Estimate  Std. Error  t value Pr(>|t|)
## mu      0.001507    0.000247   6.0993        0
## ar1     0.840009    0.042531  19.7507        0
## ma1    -0.887317    0.036366 -24.3995        0
## omega   0.000013    0.000003   5.0781        0
## alpha1  0.087831    0.010046   8.7433        0
## beta1   0.889828    0.015659  56.8259        0
## shape   3.911377    0.466670   8.3815        0
## 
## LogLikelihood : 4645.266 
## 
## Information Criteria
## ------------------------------------
##                     
## Akaike       -5.2678
## Bayes        -5.2460
## Shibata      -5.2678
## Hannan-Quinn -5.2597
## 
## Weighted Ljung-Box Test on Standardized Residuals
## ------------------------------------
##                         statistic p-value
## Lag[1]                      1.010  0.3150
## Lag[2*(p+q)+(p+q)-1][5]     1.310  0.9996
## Lag[4*(p+q)+(p+q)-1][9]     3.416  0.8181
## d.o.f=2
## H0 : No serial correlation
## 
## Weighted Ljung-Box Test on Standardized Squared Residuals
## ------------------------------------
##                         statistic p-value
## Lag[1]                   0.007733  0.9299
## Lag[2*(p+q)+(p+q)-1][5]  1.088510  0.8393
## Lag[4*(p+q)+(p+q)-1][9]  2.200653  0.8797
## d.o.f=2
## 
## Weighted ARCH LM Tests
## ------------------------------------
##             Statistic Shape Scale P-Value
## ARCH Lag[3]   0.01421 0.500 2.000  0.9051
## ARCH Lag[5]   0.24893 1.440 1.667  0.9535
## ARCH Lag[7]   1.01946 2.315 1.543  0.9104
## 
## Nyblom stability test
## ------------------------------------
## Joint Statistic:  24.7794
## Individual Statistics:              
## mu     0.07970
## ar1    0.02795
## ma1    0.02549
## omega  2.35690
## alpha1 0.22678
## beta1  0.35083
## shape  0.34723
## 
## Asymptotic Critical Values (10% 5% 1%)
## Joint Statistic:          1.69 1.9 2.35
## Individual Statistic:     0.35 0.47 0.75
## 
## Sign Bias Test
## ------------------------------------
##                    t-value   prob sig
## Sign Bias           1.2024 0.2294    
## Negative Sign Bias  0.4231 0.6723    
## Positive Sign Bias  0.8783 0.3799    
## Joint Effect        1.5647 0.6674    
## 
## 
## Adjusted Pearson Goodness-of-Fit Test:
## ------------------------------------
##   group statistic p-value(g-1)
## 1    20     37.63      0.00662
## 2    30     42.32      0.05252
## 3    40     49.27      0.12552
## 4    50     56.80      0.20714
## 
## 
## Elapsed time : 0.2920611

Plot GARCH Model Results again

plot(fit, which = "all")
## 
## please wait...calculating quantiles...

Model Diagnostics

p-value for ljung-box test is now well above 0.05, which suggests that there is no correlation left in the residuals. p-value for ARCH test is also slighty higher that the previous value.

residuals <- residuals(fit, standardize = TRUE)
acf(residuals, main = "ACF of Standardized Residuals")

Box.test(residuals, lag = 20, type = "Ljung-Box")
## 
##  Box-Ljung test
## 
## data:  residuals
## X-squared = 26.73, df = 20, p-value = 0.143
# ARCH test on residuals
ArchTest(residuals, lags = 12)
## 
##  ARCH LM-test; Null hypothesis: no ARCH effects
## 
## data:  residuals
## Chi-squared = 5.9293, df = 12, p-value = 0.9196

Forecast Volatility Again

forecast <- ugarchforecast(fit, n.ahead=10)
plot(forecast, which = 3)