Identify what is predictable in the mean and in the variance
Write , where . For a stationary series, an ARMA(1,1) conditional mean might be
In ARIMA, is the order of differencing used before specifying the ARMA dynamics. If the series is already a stationary return, is the natural starting point. For GARCH(1,1) errors, write
Suppose an AR(1) on daily returns gives a lag coefficient of 0.02 and a ten-lag residual Ljung–Box p-value of 0.48. Its squared residuals show persistent autocorrelation, and an ARCH LM test gives a p-value of 0.003. A sensible next comparison is a constant-mean GARCH model against a constant-variance baseline. The evidence for adding mean lags is weak in this sample, while the volatility diagnostics suggest a pattern worth testing. If tomorrow's variance forecast is 2.25 in percentage-point-squared units, tomorrow's volatility forecast is . The sign forecast still comes from the mean model.
Use the diagnostics to pick candidates, then score the forecasts you actually need on later data. Compare a constant mean with an ARMA mean using a loss for return forecasts. Compare variance forecasts using a variance-oriented score such as QLIKE on a nonnegative volatility proxy. If a more complicated mean model barely affects variance forecast loss, its extra coefficients may do little for a risk application. If residuals still have predictable squared magnitudes after modelling the mean, changing the AR order alone leaves the variance problem unanswered.
Fit a conditional mean and inspect its residuals
returns_pct to a chronologically ordered pandas Series of daily returns in percent units. This example reserves the last 60 observations and compares one-step forecast components at the first held-out date.from arch import arch_model
from statsmodels.stats.diagnostic import acorr_ljungbox, het_arch
from statsmodels.tsa.arima.model import ARIMA
train = returns_pct.dropna().iloc[:-60]
mean_fit = ARIMA(train.to_numpy(), order=(1, 0, 0)).fit()
resid = mean_fit.resid[1:]
print(acorr_ljungbox(resid, lags=[10], model_df=1))
print("ARCH LM p-value:", het_arch(resid, nlags=10, ddof=1)[1])
vol_fit = arch_model(train, mean="Constant", p=1, q=1).fit(disp="off")
print("AR(1) mean:", mean_fit.forecast(steps=1)[0])
print("GARCH variance:", vol_fit.forecast(horizon=1).variance.iloc[-1, 0])