Master this statistical measure of price fluctuation in TradingView's Pinescript, crucial for risk management, option pricing, and understanding market phases.
Historical Volatility (HV) is a statistical measure that quantifies the degree of variation of a security's price returns over a specific past period. Unlike indicators like Average True Range (ATR) which measure average price movement, HV specifically uses standard deviation of logarithmic returns to represent volatility as a percentage. It indicates how much price has deviated from its average over time, effectively showing how "jumpy" or "calm" an asset has been.
HV is crucial for risk management, particularly in options trading (where it's compared to implied volatility), and for understanding different market phases. High HV suggests unpredictable, large price swings, while low HV suggests stable, smaller price movements.
In Pinescript, calculating and interpreting Historical Volatility allows traders and investors to adapt their strategies based on the current market's inherent risk and opportunity profile.
The calculation of Historical Volatility involves several statistical steps, typically annualized for consistency across different assets and timeframes:
//@version=5
indicator("My Historical Volatility (HV)", overlay=false, format=format.percent) // overlay=false to plot in a separate pane
// Input for HV length (number of periods for standard deviation)
length = input.int(20, title="HV Length", minval=2) // Minval 2 needed for log return calculation
// Input for annualization factor
// 252 for daily trading days, 365 for calendar days (if using daily candles)
// 52 for weekly, 12 for monthly
annualizationFactor = input.int(252, title="Annualization Factor (e.g., 252 for daily)", minval=1)
// Calculate Logarithmic Returns
// Handle first bar case (close[1] will be NaN)
logReturn = math.log(close / close[1])
// Calculate Standard Deviation of Log Returns
// Use `fixnan` to handle the first `length-1` bars where stdev would be NaN
stdevLogReturns = ta.stdev(logReturn, length)
// Annualize the Standard Deviation
annualizedHV = stdevLogReturns * math.sqrt(annualizationFactor)
// Convert to percentage
historicalVolatility = annualizedHV * 100
// Plot the Historical Volatility line
plot(historicalVolatility, title="Historical Volatility (%)", color=color.blue, linewidth=2)
// Optional: Add moving average of HV to identify trends in volatility
hvMA = ta.sma(historicalVolatility, 20) // 20-period SMA of HV
plot(hvMA, title="HV SMA", color=color.gray, linewidth=1, style=plot.style_line)
// Highlight background when HV is significantly higher or lower than its average
bgcolor(historicalVolatility > hvMA * 1.2 ? color.new(color.red, 90) : na, title="High HV")
bgcolor(historicalVolatility < hvMA * 0.8 ? color.new(color.green, 90) : na, title="Low HV")
HV expresses volatility as a percentage, directly comparable across different assets, unlike ATR which is in absolute price units.
//@version=5
strategy("HV Volatility Regimes", overlay=true)
length = input.int(20, title="HV Length", minval=2)
annualizationFactor = input.int(252, title="Annualization Factor", minval=1)
hvUpperThreshold = input.float(30.0, title="High HV Threshold (%)", minval=10.0, step=1.0)
hvLowerThreshold = input.float(15.0, title="Low HV Threshold (%)", minval=5.0, step=1.0)
logReturn = math.log(close / close[1])
stdevLogReturns = ta.stdev(logReturn, length)
historicalVolatility = stdevLogReturns * math.sqrt(annualizationFactor) * 100
plot(historicalVolatility, "Historical Volatility", color.blue, display=display.pane_only)
hline(hvUpperThreshold, "High Vol Threshold", color.red, linestyle=hline.style_dashed, display=display.pane_only)
hline(hvLowerThreshold, "Low Vol Threshold", color.green, linestyle=hline.style_dashed, display=display.pane_only)
// Conditions for Volatility Expansion/Contraction
isHighHV = historicalVolatility > hvUpperThreshold
isLowHV = historicalVolatility < hvLowerThreshold
// Strategy Entry/Exit based on volatility shifts
// Example: Enter long when volatility is low and breaks out bullishly
// And short when volatility is high and reverses bearishly
maFast = ta.ema(close, 10)
maSlow = ta.ema(close, 20)
// Anticipate bullish breakout after low volatility
if (isLowHV[1] and ta.crossover(maFast, maSlow))
strategy.entry("Long (Vol Expand)", strategy.long)
// Anticipate bearish reversal after high volatility
if (isHighHV[1] and ta.crossunder(maFast, maSlow))
strategy.entry("Short (Vol Contract)", strategy.short)
// Basic exit logic
strategy.close("Long (Vol Expand)", when=ta.crossunder(maFast, maSlow))
strategy.close("Short (Vol Contract)", when=ta.crossover(maFast, maSlow))
//@version=5
indicator("HV for Options Context", overlay=false, format=format.percent)
length = input.int(20, title="HV Length", minval=2)
annualizationFactor = input.int(252, title="Annualization Factor", minval=1)
logReturn = math.log(close / close[1])
stdevLogReturns = ta.stdev(logReturn, length)
historicalVolatility = stdevLogReturns * math.sqrt(annualizationFactor) * 100
plot(historicalVolatility, "Historical Volatility (%)", color=color.blue, linewidth=2)
// To compare with Implied Volatility (IV), you would need to input IV manually or via external data.
// For demonstration, let's assume a hypothetical IV input.
// Note: This 'impliedVol' input is purely for illustration; real IV comes from option chain data.
impliedVol = input.float(na, title="Implied Volatility (%) (Manual Input for Demo)", group="Options Context")
// Plot the hypothetical Implied Volatility for visual comparison
plot(not na(impliedVol) ? impliedVol : na, "Implied Volatility", color=color.orange, linewidth=2)
// Determine if options are relatively expensive or cheap based on IV vs HV
areOptionsExpensive = historicalVolatility > 0 and impliedVol > historicalVolatility * 1.2 // IV is 20% higher than HV
areOptionsCheap = historicalVolatility > 0 and impliedVol < historicalVolatility * 0.8 // IV is 20% lower than HV
bgcolor(areOptionsExpensive ? color.new(color.red, 90) : na, title="Options Potentially Expensive")
bgcolor(areOptionsCheap ? color.new(color.green, 90) : na, title="Options Potentially Cheap")
alertcondition(areOptionsExpensive, "Options Expensive", "Implied Volatility is high relative to Historical Volatility.")
alertcondition(areOptionsCheap, "Options Cheap", "Implied Volatility is low relative to Historical Volatility.")
//@version=5
strategy("HV Dynamic Risk Management", overlay=true)
length = input.int(20, title="HV Length", minval=2)
annualizationFactor = input.int(252, title="Annualization Factor", minval=1)
hvStopLossMultiplier = input.float(1.5, title="HV Stop Loss Multiplier", minval=0.1, step=0.1) // e.g., 1.5x HV
accountRiskPerTrade = input.float(1000.0, title="Max $ Risk Per Trade", minval=10)
logReturn = math.log(close / close[1])
stdevLogReturns = ta.stdev(logReturn, length)
historicalVolatility = stdevLogReturns * math.sqrt(annualizationFactor) // Not converting to % for calculation
// Simple entry: crossing a 20-period SMA
maLength = input.int(20, "Entry MA Length", minval=1)
maValue = ta.sma(close, maLength)
longCondition = ta.crossover(close, maValue)
shortCondition = ta.crossunder(close, maValue)
// Calculate dynamic stop loss
// Ensure historicalVolatility is not zero to prevent division by zero
longStopLossPercentage = historicalVolatility * hvStopLossMultiplier
shortStopLossPercentage = historicalVolatility * hvStopLossMultiplier
longStopPrice = strategy.position_avg_price * (1 - longStopLossPercentage)
shortStopPrice = strategy.position_avg_price * (1 + shortStopLossPercentage)
// Calculate dynamic position size
// Risk_per_share = Entry Price * HV * Multiplier
// Qty = Account Risk / Risk_per_share
qtyToTrade = 0
if (historicalVolatility > 0)
riskPerShare = close * historicalVolatility * hvStopLossMultiplier
qtyToTrade := math.floor(accountRiskPerTrade / riskPerShare)
qtyToTrade := math.max(1, qtyToTrade) // Ensure at least 1 share
if (longCondition)
strategy.entry("Long HV", strategy.long, qty=qtyToTrade)
if (shortCondition)
strategy.entry("Short HV", strategy.short, qty=qtyToTrade)
// Apply dynamic stop losses
strategy.exit("Long Exit", from_entry="Long HV", stop=longStopPrice)
strategy.exit("Short Exit", from_entry="Short HV", stop=shortStopPrice)
plot(historicalVolatility * 100, "HV %", color=color.purple, display=display.pane_only) // Plot as percentage for viewing
plot(strategy.position_avg_price > 0 ? longStopPrice : na, "Long Stop", color=color.red, style=plot.style_linebr)
plot(strategy.position_avg_price < 0 ? shortStopPrice : na, "Short Stop", color=color.green, style=plot.style_linebr)
To get the most from Historical Volatility in Pinescript:
Markets often alternate between periods of low volatility (sideways, accumulation) and high volatility (trending, distribution). HV helps you understand where you are in this cycle.
Historical Volatility (HV) is a fundamental statistical measure in Pinescript for TradingView, providing a quantifiable understanding of an asset's price variability. By measuring the annualized standard deviation of logarithmic returns, it offers valuable insights into market behavior, aiding in crucial aspects like risk management, position sizing, and adapting strategies to changing volatility regimes. While it does not predict price direction, its ability to signal periods of volatility expansion (preceding breakouts) or contraction (preceding reversals/consolidation) is invaluable. By understanding its calculation, thoughtfully tuning its parameters, and integrating it strategically with directional indicators and robust risk management practices, you can leverage Historical Volatility to enhance your trading decisions and navigate various market environments with greater precision.
Get a high-performance Pinescript analysis tool for actionable market insights, designed for traders on the move.
This strategy runs in live mode on TradingView, helping you identify potential opportunities.
Get Pinescript Strategy