GARCH Volatility Estimation For ThinkOrSwim

joe0909

New member
Author states: The GARCH (Generalized Autoregressive Conditional Heteroskedasticity) model is a statistical model used to forecast the volatility of a financial asset. This model takes into account the fluctuations in volatility over time, recognizing that volatility can vary in a heteroskedastic (i.e., non-constant variance) manner and can be influenced by past events.

This quantitative indicator is capable of estimating the probable future movements of volatility. When the GARCH increases in value, it means that the volatility of the asset will likely increase as well, and vice versa.

mod note:
Original Tradingview script:
https://www.tradingview.com/script/a6jqpmu4-GARCH-Volatility-Estimation-The-Quant-Science/

See below posts for various ToS scripts modeling this concept.
 
Last edited by a moderator:

Join useThinkScript to post your question to a community of 21,000+ developers and traders.

Did not rewrite sorry but did come up with an indicator/scanner to find premium selling stocks based off GARCH vs HV vs IV

MetricWhat it measuresWhat it tells youGood for
HVPast volatility (realized)“What happened?”Confirmation only
GARCHForecasted future HV“What will happen?”Predictive positioning
IVPricing of volatility in options market“What the market thinks will happen”Edge + timing

Example Decision Logic

ConditionSetupBest Strategy
IV > GARCH forecastOptions overpricedSell premium (ICs, credit spreads, puts/calls)
GARCH forecast rising sharplyVolatility expectedAvoid short vol OR hedge spreads
GARCH < HV + IV trending downCompressionWatch for breakout

1764626621904.png

Code:
# ======================================================
# Full Premium Selling Scanner + Volatility Dashboard
# With GARCH Volatility Forecast Integration
# antwerks 12/01/2025
# ======================================================

input ivLength = 30;       # IV lookback (for context, not used directly)
input hvLength = 20;       # HV lookback
input trendLength = 50;    # Trend filter (SMA)
input emLength = 5;        # Expected Move days
input liquidityMin = 1000000;
input priceMin = 5;

# GARCH parameters
input garchAlpha = 0.10;   # reaction to last shock
input garchBeta  = 0.85;   # persistence of past variance
input edgeMode   = {default HVandGARCH, HVonly, GARCHonly};

# -----------------------------
#  PRICE / LIQUIDITY FILTERS
# -----------------------------
def liquid = volume > liquidityMin;
def validPrice = close > priceMin;

# -----------------------------
#  IMPLIED VOLATILITY (SAFE)
# -----------------------------
def IV_raw = if !IsNaN(imp_volatility()) then imp_volatility() else 0;
def IV = IV_raw * 100;

# -----------------------------
#  HISTORICAL VOLATILITY (SAFE)
# -----------------------------
def HV =
    if hvLength > 1 then
        StDev(Log(close / close[1]), hvLength) * Sqrt(252) * 100
    else 0;

# -----------------------------
#  GARCH(1,1) VOLATILITY FORECAST
# -----------------------------
# log returns
def ret = Log(close / close[1]);

# sample standard deviation and variance of returns
def sampleStd = if hvLength > 1 then StDev(ret, hvLength) else 0;
def sampleVar = Sqr(sampleStd);

# omega chosen so that unconditional variance matches sample variance
def garchOmega = sampleVar * (1 - garchAlpha - garchBeta);

# recursive GARCH variance
rec garchVar =
    if IsNaN(garchVar[1]) or garchVar[1] <= 0 then sampleVar
    else garchOmega + garchAlpha * Sqr(ret[1]) + garchBeta * garchVar[1];

# annualized GARCH volatility (%)
def GARCHVol = Sqrt(garchVar * 252) * 100;

# -----------------------------
#  IV RANK (1-year)
# -----------------------------
def IV_history = IV;

def belowCount =
    fold i = 1 to 252
    with total = 0
    do total + (if IV_history >= GetValue(IV_history, i) then 1 else 0);

def IVR = (belowCount / 252) * 100;

# -----------------------------
#  IV PERCENTILE (1-year)
# -----------------------------
def IVmax = Highest(IV_history, 252);
def IVmin = Lowest(IV_history, 252);
def IVP = if IVmax != IVmin then
    ((IV_history - IVmin) / (IVmax - IVmin)) * 100
else 0;

# -----------------------------
#  EXPECTED MOVE (IV-based)
# -----------------------------
def EM = close * IV_raw * Sqrt(emLength / 365);

# -----------------------------
#  TREND FILTER
# -----------------------------
def sma = Average(close, trendLength);
def upTrend = close > sma;
def downTrend = close < sma;

# -----------------------------
#  VOLATILITY EDGE CONDITIONS
# -----------------------------
def volEdgeHV     = IV > (HV + 5);
def volEdgeGARCH  = IV > (GARCHVol + 5);

def volEdge =
    if edgeMode == edgeMode.HVandGARCH then volEdgeHV and volEdgeGARCH
    else if edgeMode == edgeMode.HVonly then volEdgeHV
    else volEdgeGARCH;

# -----------------------------
#  STRATEGY LOGIC
# -----------------------------
#  1 = Sell Puts
# -1 = Sell Calls
#  0 = Sell Both
def strategyFlag =
    if upTrend then 1
    else if downTrend then -1
    else 0;

# -----------------------------
#  SCAN OUTPUT
# -----------------------------
plot scan =
    volEdge and
    liquid and
    validPrice;

# -----------------------------
#  LABELS
# -----------------------------
AddLabel(yes,
    "IV: " + AsText(IV, "0.0") +
    " | HV: " + AsText(HV, "0.0") +
    " | GARCH: " + AsText(GARCHVol, "0.0") +
    " | IVR: " + AsText(IVR, "0.0") +
    " | IVP: " + AsText(IVP, "0.0") +
    " | EM: ±" + AsDollars(EM),
    Color.WHITE
);

AddLabel(yes,
    "Edge: " +
    (if edgeMode == edgeMode.HVandGARCH then "IV > HV & GARCH"
     else if edgeMode == edgeMode.HVonly then "IV > HV"
     else "IV > GARCH") +
    (if volEdge then " (ACTIVE)" else " (inactive)"),
    if volEdge then Color.GREEN else Color.GRAY
);

AddLabel(yes,
    if strategyFlag == 1 then "STRATEGY: Sell Puts (Bullish)"
    else if strategyFlag == -1 then "STRATEGY: Sell Calls (Bearish)"
    else "STRATEGY: Sell Both Sides (Neutral)",
    if strategyFlag == 1 then Color.GREEN
    else if strategyFlag == -1 then Color.RED
    else Color.YELLOW
);
1764626621904.png
 
Last edited by a moderator:
Here is the watchlist column that ranks 1-4, 4 being sell premiums!!! 3 is good, 1 and 2 wait
Code:
# =====================================================
# Premium Selling Ranking Score + Earnings Safety Filter
# =====================================================

input hvLength = 20;
input trendLength = 50;
input minPrice = 5;
input minVolume = 1000000;
input earningsCutoffDays = 10;

# ---------------------------
# Fetch IV & HV
# ---------------------------
def rawIV = imp_volatility();
def IV = if !IsNaN(rawIV) then rawIV * 100 else 0;

def HV =
    if hvLength > 1 then
        StDev(Log(close / close[1]), hvLength) * Sqrt(252) * 100
    else 0;

def spread = IV - HV;

# ---------------------------
# Trend Direction
# ---------------------------
def sma = Average(close, trendLength);
def upTrend = close > sma;
def downTrend = close < sma;

# ---------------------------
# IV Rank (1-year)
# ---------------------------
def IVhi = Highest(IV, 252);
def IVlo = Lowest(IV, 252);
def IVR = if IVhi != IVlo then (IV - IVlo) / (IVhi - IVlo) * 100 else 0;

# ---------------------------
# Earnings Safety Filter
# ---------------------------
def isEarningsNear = HasEarnings() and
    (GetEventOffset(Events.EARNINGS, 0) < earningsCutoffDays);

def volumeOK = volume > minVolume;
def priceOK = close > minPrice;

# ---------------------------
# Score Calculation (0–4)
# ---------------------------
def score =
    (if spread >= 5 then 1 else 0) +
    (if upTrend or downTrend then 1 else 0) +
    (if upTrend then 1 else if downTrend then 1 else 0) +
    (if IVR > 50 then 1 else 0);

# ---------------------------
# SAFE VALUE RETURNED
# ---------------------------
plot data =
    if volumeOK and priceOK and !isEarningsNear the
https://tos.mx/!fzK2pXIg scanner
 
i first looked at the histogram it made me cry. Hmm, so I looked at the math for the GARCH model. MAthematically, a quantitative indicator is capable of estimating probable future movements, in the study a volatility based one. Seemed it just doesn't work, quit and then tried a last ditch effort. Workaround pasted and WAhLah! A GARCH in progress.

Lines:
Blue, Prox GARCHVolatility,
Grey Base - Regime (buy/sell domination),
arrows and bubbles for extreme & indicate high or low volatility.
Difficult?

Settings:
Lookback,
Alpha and Beta GARCH,
threshold for smoothing detection,
annual days.

To use: Daily trade (15min) or swing (1hr).
Blue crossover yellow dashes - crosses back down
2025-12-02-TOS_CHARTSA.png


Code:
# =============================================================
# GARCH Proxy Volatility Scanner + Arrows + Bubbles
# Author: antwerks/adeodatus88b | 12/3/2025
# =============================================================

declare lower;

# -------------------------
# Inputs
# -------------------------
input lookback     = 50;     # Initial variance lookback
input alphaGARCH   = 0.10;   # Reaction to recent squared return
input betaGARCH    = 0.85;   # Persistence of previous variance
input annualizeDays = 252;   # Trading days for annualization
input showPercent   = yes;   # Display volatility in percent
input thresholdLength = 100; # Threshold smoothing length for arrows/bubbles
input arrowOffset    = 1.2;  # Multiplicative offset for arrow placement

# -------------------------
# Log returns
# -------------------------
def retLog = Log(close / close[1]);

# -------------------------
# Initial variance estimate
# -------------------------
def initVar = if lookback > 1 then StDev(retLog, lookback) * StDev(retLog, lookback) else 0;

# -------------------------
# GARCH proxy (EMA-like recursive variance)
# -------------------------
rec garchProxy =
    if BarNumber() <= lookback then initVar
    else alphaGARCH * Sqr(retLog) + betaGARCH * garchProxy[1];

# Annualized volatility in percent
def vol = Sqrt(garchProxy * annualizeDays);
def volDisplay = if showPercent then vol * 100 else vol;

# -------------------------
# Volatility thresholds
# -------------------------
def threshEMA = ExpAverage(volDisplay, thresholdLength);
def lowThresh  = threshEMA * 0.80;
def highThresh = threshEMA * 1.20;

# -------------------------
# Confidence bubbles
# -------------------------
AddChartBubble(volDisplay < lowThresh,
               volDisplay,
               "LOW VOL",
               Color.GREEN,
               no);

AddChartBubble(volDisplay > highThresh,
               volDisplay,
               "HIGH VOL",
               Color.RED,
               yes);

# -------------------------
# Volatility regime arrows
# -------------------------
def wasHigh = volDisplay[1] > highThresh[1];
def wasMid  = volDisplay[1] <= highThresh[1] and volDisplay[1] >= lowThresh[1];
def wasLow  = volDisplay[1] < lowThresh[1];

def isHigh = volDisplay > highThresh;
def isMid  = volDisplay <= highThresh and volDisplay >= lowThresh;
def isLow  = volDisplay < lowThresh;

# BUY SIGNAL — volatility collapsing (High -> Mid/Low)
def buySignalBool = wasHigh and (isMid or isLow);

# SELL SIGNAL — volatility expanding (Mid/Low -> High)
def sellSignalBool = (wasMid and isHigh) or (wasLow and isHigh);

# Plot arrows
plot BuyArrow = if buySignalBool then volDisplay * arrowOffset else Double.NaN;
BuyArrow.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
BuyArrow.SetDefaultColor(Color.GREEN);
BuyArrow.SetLineWeight(3);

plot SellArrow = if sellSignalBool then volDisplay * arrowOffset else Double.NaN;
SellArrow.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);
SellArrow.SetDefaultColor(Color.RED);
SellArrow.SetLineWeight(3);

# -------------------------
# Main volatility plot
# -------------------------
plot VolLine = volDisplay;
VolLine.SetDefaultColor(GetColor(1));
VolLine.SetLineWeight(2);

# Baseline (optional)
plot Baseline = Average(volDisplay, lookback);
Baseline.SetDefaultColor(GetColor(8));
Baseline.SetStyle(Curve.SHORT_DASH);

# -------------------------
# Labels
# -------------------------
AddLabel(yes,
         "GARCH Proxy Vol: " + AsText(Round(volDisplay, 2)) + (if showPercent then "%" else ""),
         Color.GRAY);

AddLabel(yes,
         "Thresholds: Low=" + AsText(Round(lowThresh, 2)) + ", High=" + AsText(Round(highThresh, 2)),
         Color.LIGHT_GRAY);
 
@Adeodatus @antwerks very cool work on this, thank you. I've been using IV and Williams VIX Fix(as a proxy for RV) to keep an eye on when IV is still accounting for the possibility of outsized move. Was curious to see if GARCH could add anything to it. Can't wait to play around with this and see what I can find.
 
@Adeodatus @antwerks very cool work on this, thank you. I've been using IV and Williams VIX Fix(as a proxy for RV) to keep an eye on when IV is still accounting for the possibility of outsized move. Was curious to see if GARCH could add anything to it. Can't wait to play around with this and see what I can find.
I changed variable for closer study, and the analysis is post traded times, DYODD
2025-12-17-TOS_CHARTSP.png
 
Is anyone able to help me convert this indicator over to TOS?

https://www.tradingview.com/script/a6jqpmu4-GARCH-Volatility-Estimation-The-Quant-Science/

Thanks.
TOS does not accept some of the Pinescript code, so this is the best estimate I could achieve. I hope it helps!

Code:
# ============================================================
# GARCH Volatility Estimation
# Original Pine Script: The Quant Science
# ThinkScript Adaptation: Dr. Dre
# Platform: Thinkorswim (TOS)
# ============================================================

declare lower;

########################################
# Inputs
########################################
input alpha = 0.10;
input beta = 0.80;
input length = 20;
input forecastBars = 30;

input showGARCH = yes;
input showHV = yes;

########################################
# Price & EMA
########################################
def price = close;
def ema = ExpAverage(price, length);

########################################
# Error term
########################################
def error = price - ema;
def error2 = Sqr(error);

########################################
# GARCH(1,1) variance (recursive)
########################################
rec variance =
if IsNaN(variance[1]) then error2
else alpha * error2 + beta * variance[1];

def garchVol = Sqrt(variance);

########################################
# Historical Volatility
########################################
def logReturn = Log(price / price[1]);

input annualDays = 365;

def period =
if GetAggregationPeriod() <= AggregationPeriod.DAY then 1
else 7;

def histVol =
100 * StDev(logReturn, length) * Sqrt(annualDays / period);

########################################
# Plots (visual forecast offset)
########################################
plot GARCH =
if showGARCH then garchVol[-forecastBars] else Double.NaN;
GARCH.SetDefaultColor(Color.RED);
GARCH.SetLineWeight(2);
GARCH.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);

plot HV =
if showHV then histVol else Double.NaN;
HV.SetDefaultColor(Color.PINK);
HV.SetLineWeight(2);
HV.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
 
Last edited by a moderator:
big question is how did you determine 1.45 or was it just an "eyeball" plot?
WERKS, eyeballed, but just to get a reg=ference for Shiller PE (CAPE) ratio and Shannon Entropy indicator. Read the higher Entropy as the Mean to Possibilities + less Usable Energy working to drive Spontaneous Processes toward Equilibrium. An indicator to enhance GARCH applies in visual moves. Studying it may result seeing movements taking off, (either run-downs or run-ups), whenever a cross over 1.14 says they're packing more dry powder. DYODD,
2025-12-18-TOS_CHARTSB.png
Base line =1.14

Move code down to over-lap the GarchMath. This indicator changes the background color red, beware!


Code:
#Entrophy#

declare upper;

# === Inputs ===
input entropyLength = 9;
input volLength = 30;
input rsiLength = 14;
input entropyThreshold = 1.140;

# === Log Returns ===
def logReturn = Log(close / close[1]);

# === Return Dispersion ===
def returnDispersion = StDev(logReturn, entropyLength);

# === Volatility Regime Instability ===
def atrPct = ATR(volLength) / close;
def volOfVol = StDev(atrPct, entropyLength);

# === Directional Uncertainty (RSI near 50 = max entropy) ===
def rsi = RSI(rsiLength);
def rsiEntropy = 1 - AbsValue(rsi - 40) / 60;

# === Normalize Components ===
def normReturnDisp = returnDispersion / Average(returnDispersion, entropyLength);
def normVolOfVol = volOfVol / Average(volOfVol, entropyLength);

# === Composite Entropy Proxy ===
def entropyProxy =
    (normReturnDisp + normVolOfVol + rsiEntropy) / 3;

# === Plot ===
plot Entropy = entropyProxy;
Entropy.SetDefaultColor(Color.ORANGE);
Entropy.SetLineWeight(2);

# === Threshold Line ===
plot EntropyAlert = entropyThreshold;
EntropyAlert.SetDefaultColor(Color.RED);
EntropyAlert.SetStyle(Curve.SHORT_DASH);

# === Visual Warning ===
AssignBackgroundColor(
    if entropyProxy > entropyThreshold then Color.DARK_RED else Color.CURRENT
);
 

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
1115 Online
Create Post

The Market Trading Game Changer

Join 2,500+ subscribers inside the useThinkScript VIP Membership Club
  • Exclusive indicators
  • Proven strategies & setups
  • Private Discord community
  • ‘Buy The Dip’ signal alerts
  • Exclusive members-only content
  • Add-ons and resources
  • 1 full year of unlimited support

Frequently Asked Questions

What is useThinkScript?

useThinkScript is the #1 community of stock market investors using indicators and other tools to power their trading strategies. Traders of all skill levels use our forums to learn about scripting and indicators, help each other, and discover new ways to gain an edge in the markets.

How do I get started?

We get it. Our forum can be intimidating, if not overwhelming. With thousands of topics, tens of thousands of posts, our community has created an incredibly deep knowledge base for stock traders. No one can ever exhaust every resource provided on our site.

If you are new, or just looking for guidance, here are some helpful links to get you started.

What are the benefits of VIP Membership?
VIP members get exclusive access to these proven and tested premium indicators: Buy the Dip, Advanced Market Moves 2.0, Take Profit, and Volatility Trading Range. In addition, VIP members get access to over 50 VIP-only custom indicators, add-ons, and strategies, private VIP-only forums, private Discord channel to discuss trades and strategies in real-time, customer support, trade alerts, and much more. Learn all about VIP membership here.
How can I access the premium indicators?
To access the premium indicators, which are plug and play ready, sign up for VIP membership here.
Back
Top