Article

Article

Modeling Asymmetric Volatility Using the GJR-GARCH Framework

Modeling Asymmetric Volatility Using the GJR-GARCH Framework

Oct 21, 2025

widget pic
widget pic

Momentum Trading | October 21, 2025 | 9 min read

Volatility is the heartbeat of financial markets—calm one moment, chaotic the next. It measures price fluctuations and serves as a key indicator of risk. But capturing volatility is tricky. A 2% market drop grabs more attention than a 2% rise, revealing an asymmetry that standard models often overlook.

Enter the GJR-GARCH model—a powerful tool for modeling this asymmetry. In this article, we’ll explore the GJR-GARCH framework, its differences from the traditional GARCH model, and how to implement it in Python using NIFTY 50 data. We’ll also evaluate its forecasting performance with visualizations and diagnostics.

Prerequisites

To fully grasp the GJR-GARCH model, a solid foundation in time series analysis is helpful. Start with these concepts:

  • Time Series Basics: Understand trends, seasonality, and autocorrelation through an Introduction to Time Series.

  • Stationarity: Learn how to prepare data for GARCH models with Stationarity in Time Series.

  • Hurst Exponent: Explore long-term memory in time series via The Hurst Exponent.

  • Mean Reversion: Study mean-reverting behavior, often tied to volatility clusters, in Mean Reversion in Time Series.

  • ARMA Models: Get familiar with the ARMA family, foundational to GARCH, through the ARMA Model Guideand ARMA Implementation in Python.

  • GARCH Basics: Review GARCH and Volatility Forecasting Using GARCH in the Quantra glossary.

  • Deep Learning Comparison: Compare traditional models with neural networks in Time Series vs. LSTM Models.

This article covers:

  1. GARCH vs. GJR-GARCH models

  2. Overview of the GARCH model

  3. Introduction to the GJR-GARCH model

  4. Volatility forecasting on NIFTY 50 with Python

GARCH vs. GJR-GARCH Models

The GARCH (Generalized Autoregressive Conditional Heteroskedasticity) model captures volatility clustering—periods of high or low volatility that tend to persist. However, it assumes positive and negative price shocks impact volatility equally, which doesn’t align with real-world markets where negative news often triggers sharper reactions.

The GJR-GARCH model, named after Glosten, Jagannathan, and Runkle, addresses this by incorporating an asymmetry term. It assigns greater weight to negative shocks, reflecting the leverage effect—where bad news (e.g., a market crash) amplifies volatility more than good news.

For instance, during the March 2020 COVID-19 crash, the NIFTY 50 experienced sharp declines and volatility spikes driven by panic selling. A standard GARCH model might underestimate this, but GJR-GARCH captures the heightened volatility from negative shocks more accurately.

Overview of the GARCH Model

Developed by Tim Bollerslev in 1986, the GARCH model extends the ARCH model by modeling volatility as a function of past squared returns (ARCH component) and past volatility estimates (GARCH component). This makes it ideal for time series with changing variance, like stock returns.

The GARCH(p, q) model’s conditional variance equation is:

σt2=ω+∑i=1pαiϵt−i2+∑j=1qβjσt−j2σt2​=ω+∑i=1p​αi​ϵt−i2​+∑j=1q​βj​σt−j2​

Where:

  • σt2σt2​: Conditional variance at time t

  • ωω: Long-run average variance

  • αiϵt−i2αi​ϵt−i2​: ARCH term (impact of past shocks)

  • βjσt−j2βj​σt−j2​: GARCH term (impact of past variances)

The simplest form, GARCH(1,1), is:

σt2=ω+α1ϵt−12+β1σt−12σt2​=ω+α1​ϵt−12​+β1​σt−12​

With just three parameters, GARCH(1,1) is easy to estimate and widely used for financial data.

Introduction to the GJR-GARCH Model

Introduced in 1993, the GJR-GARCH model builds on GARCH by adding an asymmetry term to capture the leverage effect. Its formula for GJR-GARCH(1,1) is:

σt2=ω+α1ϵt−12+γϵt−12It−1+β1σt−12σt2​=ω+α1​ϵt−12​+γϵt−12​It−1​+β1​σt−12​

Where:

  • γγ: Measures the additional impact of negative shocks

  • It−1It−1​: An indicator function (1 if ϵt−1<0ϵt−1​<0, 0 otherwise)

When returns are positive (ϵt−1≥0ϵt−1​≥0): σt2=ω+α1ϵt−12+β1σt−12σt2​=ω+α1​ϵt−12​+β1​σt−12​

When returns are negative (ϵt−1<0ϵt−1​<0): σt2=ω+(α1+γ)ϵt−12+β1σt−12σt2​=ω+(α1​+γ)ϵt−12​+β1​σt−12​

The γγ term amplifies volatility for negative shocks, making GJR-GARCH more realistic for financial markets.

Volatility Forecasting on NIFTY 50 Using GJR-GARCH in Python

Let’s implement the GJR-GARCH model to forecast volatility for the NIFTY 50 index from 2020 to 2025. We’ll use the arch package in Python for modeling and evaluation.

Step 1: Import Libraries


import pandas as pd
import numpy as np
import yfinance as yf
import matplotlib.pyplot as plt
from arch import arch_model
from tqdm import tqdm



The tqdm library adds a progress bar for loops, helping track long-running processes.

Step 2: Download NIFTY 50 Data

Fetch historical NIFTY 50 data and calculate daily log returns, scaled to percentages for better model convergence.



symbol = "^NSEI"  # NIFTY 50
data = yf.download(symbol, start="2020-01-01", end="2025-04-15")
nifty = pd.DataFrame(data["Close"])
nifty["LogReturn"] = np.log(nifty["Close"] / nifty["Close"].shift(1))
returns = nifty["LogReturn"].dropna() * 100  # Scale for GARCH



Step 3: Specify the GJR-GARCH Model

Use a GJR-GARCH(1,1) model with a Student’s t-distribution to capture fat-tailed returns common in financial data.


model = arch_model(
    returns,
    vol="GARCH",
    p=1,  # ARCH lags
    o=1,  # Asymmetry term (GJR)
    q=1,  # GARCH lags
    dist="t",  # Student's t-distribution
    mean="Constant"  # Constant mean model
)



Step 4: Fit the Model

Fit the model and examine the results.


res = model.fit(update_freq=10, disp="off")
print(res.summary())



Output Insights:

  • ARCH term (alpha[1]): Small but significant (e.g., 0.0123), showing past shocks’ impact.

  • GARCH term (beta[1]): High (e.g., 0.9052), indicating persistent volatility.

  • Leverage effect (gamma[1]): Significant (e.g., 0.1330), confirming asymmetry in negative shocks.

  • Student’s t degrees of freedom (nu): Around 7.6, supporting fat-tailed returns.

Step 5: Residual Diagnostics

Visualize returns, conditional volatility, and standardized residuals to assess model fit.



residuals = res.resid / res.conditional_volatility
plt.figure(figsize=(12, 6))
plt.subplot(2, 2, 1)
returns.plot(title="Returns")
plt.subplot(2, 2, 2)
res.conditional_volatility.plot(title="Conditional Volatility")
plt.subplot(2, 2, 3)
pd.Series(residuals).plot(title="Standardized Residuals")
plt.subplot(2, 2, 4)
pd.Series(residuals**2).plot(title="Squared Residuals")
plt.tight_layout()
plt.show()



The model captures volatility spikes during crises (e.g., 2008, 2020), with the asymmetry term amplifying responses to negative shocks.

Step 6: Rolling Volatility Forecasts

Generate one-step-ahead volatility forecasts using a 500-day rolling window.



rolling_window = 500
forecast_horizon = 1
forecast_vol = []

for i in tqdm(range(rolling_window, len(returns))):
    train_data = returns.iloc[i - rolling_window:i]
    model = arch_model(train_data, vol="GARCH", p=1, o=1, q=1)
    model_fitted = model.fit(disp="off")
    forecast = model_fitted.forecast(horizon=forecast_horizon)
    vol_forecast = np.sqrt(forecast.variance.values[-1, 0])  # Daily volatility
    forecast_vol.append(vol_forecast)

forecast_dates = returns.index[rolling_window:]
forecast_series = pd.Series(forecast_vol, index=forecast_dates)
annualized_forecast = forecast_series * np.sqrt(252)  # Annualize



Step 7: Compare Forecasted vs. Realized Volatility

Calculate realized volatility using a 5-day rolling standard deviation and compare it to forecasts.


realized_vol = returns.rolling(window=5).std().loc[forecast_dates]
annualized_realized = realized_vol * np.sqrt(252)

combined = pd.concat([annualized_forecast, annualized_realized], axis=1).dropna()
combined.columns = ["Forecasted", "Realized"]
correlation = combined.corr().iloc[0, 1]
print(f"Correlation between Forecasted and Realized Volatility: {correlation:.4f}")



Output: Correlation between Forecasted and Realized Volatility: 0.7443

A correlation of 0.74 shows the GJR-GARCH model effectively tracks market volatility, especially its sensitivity to negative shocks.

Conclusion

Volatility reflects market psychology, with fear often outweighing optimism. The GJR-GARCH model captures this asymmetry, making it a superior tool for forecasting volatility in assets like the NIFTY 50. Its ability to emphasize negative shocks aligns with real-world investor behavior, offering a robust framework for traders and risk managers.


By mastering GJR-GARCH and related techniques, you’ll be well-equipped to navigate the complexities of financial markets

Start Your Trading Evolution with Skydify.
Harness Trading automation, get started today.

Start Your Trading Evolution with Skydify.
Harness Trading automation, get started today.

Get in touch

Whatsapp

+971 507707126

info.skydify@gmail.com

Legal

Privacy Policy

Terms & Conditions

Social Links

Facebook

Instagram

Get in touch

Whatsapp

+971 507707126

info.skydify@gmail.com

Legal

Privacy Policy

Terms & Conditions

Social Links

Facebook

X(Twitter)

Linkedin

Instagram

Skydify is an Intelsity company, whenever you access the website.

Skydify is an Intelsity company, whenever you access the website.