> library(quantmod) # for downloading stock data
> library(tseries) # for stationarity tests (ADF)
> library(rugarch) # for ARCH/GARCH estimation
> library(FinTS) # for ARCH-LM test
> # Download daily TCS stock prices from Yahoo Finance
> getSymbols("[Link]", from = "2015-01-01", src = "yahoo")
[1] "[Link]"
Warning message:
[Link] contains missing values. Some functions will not work if objects
contain missing values in the middle of the series. Consider using [Link](),
[Link](), [Link](), etc to remove or replace them.
> # Extract Adjusted Closing Price (preferred for returns)
> price <- Ad([Link])
> # Compute daily log returns: r_t = log(P_t) - log(P_{t-1})
> returns <- diff(log(price))
> # Remove first NA observation
> returns <- [Link](returns)
> plot(
+ returns,
+ main = "TCS Daily Log Returns",
+ ylab = "Returns",
+ xlab = "Time",
+ col = "steelblue"
+ )
> abline(h = 0, col = "red", lty = 2)
> plot(
+ returns^2,
+ main = "Squared Log Returns of TCS",
+ ylab = "Squared Returns",
+ xlab = "Time",
+ col = "darkorange"
+ )
> [Link]([Link](price))
Augmented Dickey-Fuller Test
data: [Link](price)
Dickey-Fuller = -1.2924, Lag order
= 14, p-value = 0.8779
alternative hypothesis: stationary
> [Link](returns)
Augmented Dickey-Fuller Test
data: returns
Dickey-Fuller = -13.808, Lag order
= 14, p-value = 0.01
alternative hypothesis: stationary
Warning message:
In [Link](returns) : p-value smaller than printed p-value
> acf(
+ returns,
+ main = "ACF of Returns (Check Mean Structure)"
+ )
> acf(
+ returns^2,
+ main = "ACF of Squared Returns (Volatility Clustering)"
+ )
> # Engle's ARCH-LM test on raw returns
> ArchTest(returns, lags = 5)
ARCH LM-test; Null hypothesis: no
ARCH effects
data: returns
Chi-squared = 133.49, df = 5,
p-value < 2.2e-16
> spec_arch1 <- ugarchspec(
+
+ [Link] = list(
+
+ # "sGARCH" refers to the standard GARCH framework in rugarch.
+ # Even when we want a pure ARCH model, rugarch still requires "sGARCH"
+ # because ARCH is implemented as a special case of GARCH.
+ model = "sGARCH",
+
+ # garchOrder = c(p, q)
+ # p = number of GARCH terms (lags of past conditional variance h_{t-1},
h_{t-2}, ...)
+ # q = number of ARCH terms (lags of past squared shocks ε_{t-1}^2,
ε_{t-2}^2, ...)
+ #
+ # Here we use c(1, 0):
+ # p = 1, q = 0
+ # Since q = 0, the GARCH part drops out in practice.
+ # This effectively gives:
+ # h_t = α0 + α1 * ε_{t-1}^2
+ # which is an ARCH(1) model.
+ #
+ # you could later change this to:
+ # c(2,0) -> ARCH(2)
+ # c(1,1) -> GARCH(1,1)
+ # c(2,1) -> GARCH(2,1)
+ garchOrder = c(1, 0)
+ ),
+
+ [Link] = list(
+
+ # armaOrder = c(p, q) specifies the mean equation for returns.
+ # p = number of AR terms, q = number of MA terms.
+ #
+ # c(0,0) means no AR and no MA terms, so the mean equation is simply:
+ # r_t = μ + ε_t
+ #
+ # If you wanted to allow predictability in returns, they could use:
+ # c(1,0) for AR(1)
+ # c(0,1) for MA(1)
+ armaOrder = c(0, 0),
+
+ # [Link] = TRUE means we estimate a constant mean μ.
+ # If this were FALSE, the model would force μ = 0.
+ [Link] = TRUE
+ ),
+
+ # [Link] specifies the assumed distribution of ε_t given past
information.
+ #
+ # "norm" means:
+ # ε_t | F_{t-1} ~ Normal(0, h_t)
+ #
+ # if you want you could later change this to:
+ # "std" for Student-t (if returns have fat tails)
+ # "sstd" for skewed Student-t
+ [Link] = "norm"
+ )
> fit_arch1 <- ugarchfit(
+
+ # 'spec' tells rugarch which model structure to estimate
+ spec = spec_arch1,
+
+ # 'data' is the series to which the model is fitted.
+ # This must be returns, not prices.
+ data = returns
+ )
> # Show results
> show(fit_arch1)
*---------------------------------*
* GARCH Model Fit *
*---------------------------------*
Conditional Variance Dynamics
-----------------------------------
GARCH Model : sGARCH(1,0)
Mean Model : ARFIMA(0,0,0)
Distribution: norm
Optimal Parameters
------------------------------------
Estimate Std. Error t value
mu 0.035495 0.000017 2130.6344
omega 0.000000 0.000000 2.0498
alpha1 0.991403 0.007112 139.4066
Pr(>|t|)
mu 0.000000
omega 0.040381
alpha1 0.000000
Robust Standard Errors:
Estimate Std. Error t value
mu 0.035495 0.000329 107.947337
omega 0.000000 0.000027 0.014584
alpha1 0.991403 0.322599 3.073174
Pr(>|t|)
mu 0.000000
omega 0.988364
alpha1 0.002118
LogLikelihood : -278.2397
Information Criteria
------------------------------------
Akaike 0.20424
Bayes 0.21069
Shibata 0.20424
Hannan-Quinn 0.20657
Weighted Ljung-Box Test on Standardized Residuals
------------------------------------
statistic
Lag[1] 13.24
Lag[2*(p+q)+(p+q)-1][2] 13.25
Lag[4*(p+q)+(p+q)-1][5] 13.63
p-value
Lag[1] 0.0002734
Lag[2*(p+q)+(p+q)-1][2] 0.0002647
Lag[4*(p+q)+(p+q)-1][5] 0.0010300
d.o.f=0
H0 : No serial correlation
Weighted Ljung-Box Test on Standardized Squared Residuals
------------------------------------
statistic
Lag[1] 0.06352
Lag[2*(p+q)+(p+q)-1][2] 0.08312
Lag[4*(p+q)+(p+q)-1][5] 0.13340
p-value
Lag[1] 0.8010
Lag[2*(p+q)+(p+q)-1][2] 0.9316
Lag[4*(p+q)+(p+q)-1][5] 0.9967
d.o.f=1
Weighted ARCH LM Tests
------------------------------------
Statistic Shape Scale
ARCH Lag[2] 0.03914 0.500 2.000
ARCH Lag[4] 0.08355 1.397 1.611
ARCH Lag[6] 0.10564 2.222 1.500
P-Value
ARCH Lag[2] 0.8432
ARCH Lag[4] 0.9874
ARCH Lag[6] 0.9989
Nyblom stability test
------------------------------------
Joint Statistic: 18.2009
Individual Statistics:
mu 0.1332
omega 4.3687
alpha1 15.1708
Asymptotic Critical Values (10% 5% 1%)
Joint Statistic: 0.846 1.01 1.35
Individual Statistic: 0.35 0.47 0.75
Sign Bias Test
------------------------------------
t-value prob
Sign Bias 6.109 1.147e-09
Negative Sign Bias 7.166 9.864e-13
Positive Sign Bias 5.494 4.290e-08
Joint Effect 111.428 5.407e-24
sig
Sign Bias ***
Negative Sign Bias ***
Positive Sign Bias ***
Joint Effect ***
Adjusted Pearson Goodness-of-Fit Test:
------------------------------------
group statistic p-value(g-1)
1 20 4779 0
2 30 4899 0
3 40 4874 0
4 50 5018 0
Elapsed time : 0.435992
> # pplot estimated ARCH volatility
> plot(fit_arch1, which = 2)
please wait...calculating quantiles...
> res_arch <- residuals(fit_arch1, standardize = TRUE)
> # ACF of squared standardized residuals
> acf(res_arch^2, main = "ACF of Squared Residuals (ARCH)")
> # Formal ARCH-LM test on residuals
> ArchTest(res_arch, lags = 5)
ARCH LM-test; Null hypothesis: no
ARCH effects
data: res_arch
Chi-squared = 0.20167, df = 5,
p-value = 0.9991
> spec_garch11 <- ugarchspec(
+ [Link] = list(model = "sGARCH", garchOrder = c(1, 1)), #
GARCH(1,1)
+ [Link] = list(armaOrder = c(0, 0), [Link] = TRUE),
+ [Link] = "norm"
+ )
> fit_garch11 <- ugarchfit(
+ spec = spec_garch11,
+ data = returns
+ )
> ##expalined code in deatil for the same thing you ran aboce
> spec_garch11 <- ugarchspec(
+
+ [Link] = list(
+
+ # "sGARCH" is the standard GARCH framework in rugarch.
+ # This is the usual specification for most basic GARCH models.
+ model = "sGARCH",
+
+ # garchOrder = c(p, q)
+ # p = number of GARCH terms (lags of past conditional variance h_{t-1},
h_{t-2}, ...)
+ # q = number of ARCH terms (lags of past squared shocks ε_{t-1}^2,
ε_{t-2}^2, ...)
+ #
+ # Here we use c(1, 1), which means:
+ # p = 1 (one lag of past variance)
+ # q = 1 (one lag of past squared shock)
+ #
+ # This gives the standard GARCH(1,1) variance equation:
+ # h_t = α0 + α1 * ε_{t-1}^2 + β1 * h_{t-1}
+ #
+ # you could later experiment with:
+ # c(2,1) -> GARCH(2,1)
+ # c(1,2) -> GARCH(1,2)
+ # c(2,2) -> GARCH(2,2)
+ garchOrder = c(1, 1)
+ ),
+
+ [Link] = list(
+
+ # armaOrder = c(p, q) specifies the mean equation for returns.
+ # p = number of AR terms, q = number of MA terms.
+ #
+ # c(0,0) means no AR and no MA terms, so the mean equation is simply:
+ # r_t = μ + ε_t
+ #
+ # If you wanted to model predictability in returns, they could use:
+ # c(1,0) for AR(1)
+ # c(0,1) for MA(1)
+ armaOrder = c(0, 0),
+
+ # [Link] = TRUE means we estimate a constant mean μ.
+ # If this were FALSE, the model would force μ = 0.
+ [Link] = TRUE
+ ),
+
+ # [Link] specifies the assumed conditional distribution of
ε_t.
+ #
+ # "norm" means:
+ # ε_t | F_{t-1} ~ Normal(0, h_t)
+ #
+ # you could later change this to:
+ # "std" for Student-t (if returns have fat tails)
+ # "sstd" for skewed Student-t
+ [Link] = "norm"
+ )
> fit_garch11 <- ugarchfit(
+
+ # 'spec' tells rugarch which model structure to estimate
+ spec = spec_garch11,
+
+ # 'data' is the series to which the model is fitted.
+ # This must be returns, not prices.
+ data = returns
+ )
> # Show results
> show(fit_garch11)
*---------------------------------*
* GARCH Model Fit *
*---------------------------------*
Conditional Variance Dynamics
-----------------------------------
GARCH Model : sGARCH(1,1)
Mean Model : ARFIMA(0,0,0)
Distribution: norm
Optimal Parameters
------------------------------------
Estimate Std. Error t value
mu 0.000369 0.000263 1.4048
omega 0.000014 0.000000 53.0211
alpha1 0.056348 0.003396 16.5923
beta1 0.875992 0.006766 129.4654
Pr(>|t|)
mu 0.16009
omega 0.00000
alpha1 0.00000
beta1 0.00000
Robust Standard Errors:
Estimate Std. Error t value
mu 0.000369 0.000261 1.4127
omega 0.000014 0.000001 27.1900
alpha1 0.056348 0.004003 14.0779
beta1 0.875992 0.008311 105.4006
Pr(>|t|)
mu 0.15776
omega 0.00000
alpha1 0.00000
beta1 0.00000
LogLikelihood : 7808.871
Information Criteria
------------------------------------
Akaike -5.6680
Bayes -5.6594
Shibata -5.6680
Hannan-Quinn -5.6649
Weighted Ljung-Box Test on Standardized Residuals
------------------------------------
statistic
Lag[1] 1.717
Lag[2*(p+q)+(p+q)-1][2] 2.431
Lag[4*(p+q)+(p+q)-1][5] 3.073
p-value
Lag[1] 0.1901
Lag[2*(p+q)+(p+q)-1][2] 0.2003
Lag[4*(p+q)+(p+q)-1][5] 0.3939
d.o.f=0
H0 : No serial correlation
Weighted Ljung-Box Test on Standardized Squared Residuals
------------------------------------
statistic
Lag[1] 0.06734
Lag[2*(p+q)+(p+q)-1][5] 4.00015
Lag[4*(p+q)+(p+q)-1][9] 9.81989
p-value
Lag[1] 0.7952
Lag[2*(p+q)+(p+q)-1][5] 0.2541
Lag[4*(p+q)+(p+q)-1][9] 0.0548
d.o.f=2
Weighted ARCH LM Tests
------------------------------------
Statistic Shape Scale
ARCH Lag[3] 3.287 0.500 2.000
ARCH Lag[5] 4.595 1.440 1.667
ARCH Lag[7] 9.908 2.315 1.543
P-Value
ARCH Lag[3] 0.06984
ARCH Lag[5] 0.12735
ARCH Lag[7] 0.01933
Nyblom stability test
------------------------------------
Joint Statistic: 161.0661
Individual Statistics:
mu 0.1683
omega 9.9383
alpha1 0.2588
beta1 0.1155
Asymptotic Critical Values (10% 5% 1%)
Joint Statistic: 1.07 1.24 1.6
Individual Statistic: 0.35 0.47 0.75
Sign Bias Test
------------------------------------
t-value prob sig
Sign Bias 0.1867 0.8519
Negative Sign Bias 1.0228 0.3065
Positive Sign Bias 0.1204 0.9041
Joint Effect 1.1880 0.7559
Adjusted Pearson Goodness-of-Fit Test:
------------------------------------
group statistic p-value(g-1)
1 20 127.6 4.147e-18
2 30 145.3 1.973e-17
3 40 152.6 2.338e-15
4 50 179.7 8.241e-17
Elapsed time : 0.4168251
> res_garch <- residuals(fit_garch11, standardize = TRUE)
> # Check autocorrelation in standardized residuals
> acf(res_garch, main = "ACF of Standardized Residuals (GARCH)")
> # Check remaining ARCH in squared residuals
> acf(res_garch^2, main = "ACF of Squared Standardized Residuals (GARCH)")
> # Test for autocorrelation in residuals (mean structure)
> [Link](res_garch, lag = 10, type = "Ljung")
Box-Ljung test
data: res_garch
X-squared = 9.1455, df = 10,
p-value = 0.5184
> # Test for remaining ARCH in squared residuals
> [Link](res_garch^2, lag = 10, type = "Ljung")
Box-Ljung test
data: res_garch^2
X-squared = 18.661, df = 10,
p-value = 0.04479
> # ARCH-LM test on GARCH residuals
> ArchTest(res_garch, lags = 5)
ARCH LM-test; Null hypothesis: no
ARCH effects
data: res_garch
Chi-squared = 6.9763, df = 5,
p-value = 0.2224
> plot(fit_arch1, which = 2) # ARCH volatility (spiky)
please wait...calculating quantiles...
> plot(fit_garch11, which = 2) # GARCH volatility (smoother, persistent)
please wait...calculating quantiles...
> infocriteria(fit_arch1)
Akaike 0.2042409
Bayes 0.2106906
Shibata 0.2042385
Hannan-Quinn 0.2065709
> infocriteria(fit_garch11)
Akaike -5.668025
Bayes -5.659426
Shibata -5.668029
Hannan-Quinn -5.664918
> acf(res_arch^2, main = "Squared Residuals: ARCH")
> acf(res_garch^2, main = "Squared Residuals: GARCH")
>
> # Typically:
> # Typically:
> # ARCH still shows some structure
> # Typically:
> # ARCH still shows some structure
> # GARCH cleans it up.