TradingView indicators

Historical Volatility: Formula and Pine Script

Historical volatility (HV) measures the dispersion of past returns. This guide calculates the standard deviation of close-to-close log returns, then annualizes it using an explicit number of return periods per year. The output is a percentage describing a historical sample, not a price forecast or a trade direction.

To reproduce a reading, match the closing prices, lookback, population or sample divisor, and annualization convention. The copyable Pine Script exposes these choices and can wait for the chart bar to close. A worked example below shows why two correct calculations can disagree when only the divisor or calendar changes.

Input: log returnsDefault window: 10 returnsDefault divisor: NAnnual periods: explicit input

Historical volatility indicator formula

Let C be a positive closing price and r its continuously compounded return. For N returns, calculate their mean before measuring the deviations around that mean. Use returns in decimal units; multiply by 100 only when displaying a percentage.

r[t] = ln(C[t] / C[t - 1])

mean = sum(r) / N

population variance = sum((r - mean)²) / N

sample variance = sum((r - mean)²) / (N - 1)

HV (%) = 100 × sqrt(variance) × sqrt(A)

A is the number of return periods assumed per year. N is the number of observations in the rolling window. They serve different purposes: changing N changes the sample, while changing A rescales the same sample. Ten returns require eleven consecutive valid closes.

For identical data, sample HV equals population HV multiplied by sqrt(N / (N - 1)). With ten returns, the sample value is about 5.41% higher. In Pine v6 ta.stdev(), pass the biased argument explicitly to avoid an unnoticed convention mismatch.

The square-root-of-time conversion assumes a variance scaling rule. Serial dependence, changing volatility and uneven trading intervals can weaken that interpretation. Annualization is a reporting convention; it does not establish a distribution for next year's returns.

Recalculate a five-return example

Illustrative data only. Start at 100 and construct five exact log returns: 0.01, -0.01, 0.02, -0.02 and 0. Use unrounded prices for calculation; the displayed prices are rounded to six decimals. These are hypothetical observations, not a backtest or market record.

ObservationClosing priceLog returnSquared deviation
Start100.000000Not availableNot available
1101.0050170.0100000.000100
2100.000000-0.0100000.000100
3102.0201340.0200000.000400
4100.000000-0.0200000.000400
5100.0000000.0000000.000000

The mean return is zero and the squared deviations sum to 0.001. Dividing by five gives a population variance of 0.0002 and per-bar standard deviation of 0.014142136. Dividing by four gives a sample variance of 0.00025 and deviation of 0.015811388.

ConventionA = 252 daily periodsA = 365 daily periods
Population, divisor 522.4499%27.0185%
Sample, divisor 425.0998%30.2076%

The 365 result is about 20.35% higher than the 252 result without any change in prices. This is a convention difference, not evidence that the asset became riskier. In a spreadsheet, apply STDEV.P or STDEV.S to the five log returns, then multiply by SQRT(A) and 100. Do not round the return column first.

A useful counterexample: if all five log returns equal 0.01, both standard deviations are zero even though price rises every bar. HV measures variation around the mean return. It does not measure trend strength or cumulative gain.

Copy the Historical Volatility Pine Script

Paste this Pine v6 indicator into TradingView Pine Editor and add it to a standard time-based candlestick chart. The blue line shows annualized HV in a separate pane. The Data Window exposes the per-bar deviation for reconciliation before annualization.

Pine Script v6

//@version=6
indicator("Historical Volatility: Explicit Conventions", shorttitle="HV", overlay=false, precision=4)

length = input.int(10, "Return observations", minval=2)
periodsPerYear = input.float(252.0, "Return periods per year", minval=1.0)
useSample = input.bool(false, "Sample standard deviation (N - 1)")
confirmedOnly = input.bool(true, "Show only closed bars")

logReturn = close > 0 and close[1] > 0 ? math.log(close / close[1]) : na
validWindow = math.sum(na(logReturn) ? 1.0 : 0.0, length) == 0
returnStdev = ta.stdev(logReturn, length, not useSample)
hv = validWindow ? 100.0 * returnStdev * math.sqrt(periodsPerYear) : na
visibleHv = not confirmedOnly or barstate.isconfirmed ? hv : na

plot(visibleHv, "Annualized HV (%)", color=color.rgb(41, 98, 255), linewidth=2)
plot(validWindow ? returnStdev * 100.0 : na, "Per-bar standard deviation (%)", display=display.data_window)

This is an explicit-convention teaching example. It does not promise identical values to every built-in or community HV script. The script uses the chart's close directly; it does not request daily data when placed on an intraday chart. Change the annual periods input whenever the bar frequency or calendar changes.

The default closed-bar setting hides the current annualized plot until the bar closes. It does not freeze the diagnostic series. The valid-window check rejects a window containing an unavailable or nonpositive-price log return instead of silently substituting zero. A missing time interval that has no chart bar still requires a separate data-quality check.

Parameters, frequency and trading-day assumptions

InputExample defaultEffect
Return observations10, minimum 2Number of log returns in the rolling standard deviation.
Return periods per year252Rescales per-bar deviation; never changes the source timeframe.
Sample standard deviationOffOff divides variance by N; on divides by N - 1.
Show only closed barsOnHides the open-bar annualized plot until confirmation.

Set A from the return frequency

Illustrative chartAnnual periods AConstraint
Daily exchange-traded equity252An assumed trading-day count, not a guaranteed session count for every calendar.
Daily crypto365Assumes seven-day trading and one daily observation; leap years and data gaps require a documented convention.
Weekly time bars52Uses weekly returns. A daily factor would rescale the wrong frequency.
One-hour continuous crypto8,760 = 365 × 24Assumes complete hourly observations throughout the year.
30-minute, 6.5-hour equity session3,276 = 252 × 13A simplifying count. Overnight returns, holidays and half-days make intervals unequal.

Do not apply the daily factor sqrt(252) to five-minute returns and label the result daily HV. For a daily comparison, calculate from daily closes. Range, Renko and other event-based bars do not have a fixed number of observations per year, so this time-scaling example is not an annualization method for those chart types.

Keep the price series consistent

Record the exchange-qualified symbol, chart session, timeframe and adjustment settings. A split handled differently in two feeds can create an artificial return. Dividend-adjusted and price-only series answer different questions around distributions. TradingView provides a dividend-adjustment setting; use the same choice in the chart and your external calculation.

Crypto's calendar does not identify its price source. Two BTC feeds may have different closes or daily boundaries. Compare the actual series before attributing a numerical difference to the HV formula.

How to use historical volatility without treating it as a forecast

ObservationWhat it establishesWhat remains unknown
HV rises on a closed barThe rolling return dispersion increased under unchanged settings.Whether the next move is up or down.
HV drops sharplyThe new window has less return dispersion; an outlier may have rolled out.Whether future conditions will remain quiet.
HV is above a prior baselineThe recent sample differs from the comparison sample.Whether a particular entry or option trade has an edge.
HV changes after a setting changeThe sample or scaling convention changed.Whether market behavior changed at all.

Three research uses with separate trading rules

  1. Compare a 10-return and a 60-return HV series with the same divisor and A. Record when their ordering changes at bar close. This describes recent dispersion relative to a longer sample; any entry, exit and position-size rule requires its own test.
  2. Review a jump by inspecting the entering return and the return leaving the window. A large observation can keep HV high until it rolls out. Mark the event date before interpreting a later decline as a durable change.
  3. Use HV as an explanatory column in a trade review. Group closed trades by the value available at entry, then inspect costs and sample sizes. Selecting thresholds after seeing results can overfit; reserve separate data for evaluation.

HV alone supplies no stop-loss distance, target or profitable options-selling rule. A low historical value does not remove jump risk. This page is educational and does not provide investment advice.

When the reading misleads

  • Warm-up: fewer than N + 1 valid closes cannot provide N consecutive returns. A blank value is preferable to an invented zero.
  • Outliers: the same large return affects several overlapping windows. Adjacent HV observations are not independent evidence.
  • Thin trading: repeated stale closes can suppress measured dispersion before a jump. Check the underlying timestamps and trading activity.
  • Revisions: feed corrections and price adjustments can change past returns. Closed-bar confirmation cannot prevent revisions to the underlying data.

HV, implied volatility, ATR and Bollinger Bands

MeasureInput and outputComparison constraint
Historical volatilityPast log returns; annualized percentage.Match return window, divisor, annualization and feed.
Implied volatilityOption price and a pricing model; volatility parameter.Match maturity and annualization; contract and model inputs matter.
ATRTrue ranges smoothed over bars; price units.Includes high-low range information that close-to-close HV omits.
Bollinger BandsA price average with bands based on price deviation.Price dispersion differs from return dispersion; band width is not annualized HV.

IV and HV can differ because they describe different information and horizons. Even after their units match, the spread alone does not establish a trading profit. HV also cannot substitute for a quoted option's IV in an options comparison without a stated model and purpose.

Write a checkable HV request for Pineify

Use the Pine Script AI Coding Agent to turn the calculation choices into editable source. Specify the conventions before requesting alerts or a strategy.

Create a Pine v6 Historical Volatility indicator for daily closes, using 20 log returns, population standard deviation and 252 periods per year. Show results only on confirmed chart bars. Expose per-bar deviation in the Data Window and leave invalid windows blank. Do not add trade entries.
  1. Open the Pine Script AI Coding Agent with your timeframe, feed and calendar assumptions.
  2. Specify the return formula, N or N - 1 divisor, lookback and annual periods.
  3. Review the generated code for source timeframe, invalid-data behavior and confirmation timing.
  4. Compare closed-bar results with an external calculation using the same unrounded closes.
  5. Define any strategy's entries, exits, fees and slippage separately, then evaluate it on reserved data.

A successful syntax check establishes that the script parses; it does not prove data equivalence, forecast accuracy or a trading advantage.

Build an HV script

Historical volatility questions

Historical volatility summarizes the dispersion of past returns over a chosen window. This example uses close-to-close log returns and reports their standard deviation on an annualized percentage scale. A reading of 20% describes that historical sample under the selected annualization convention. It does not mean the price rose 20%, will move 20%, or will stay within a 20% range.

Compute r = ln(close / previous close), subtract the mean return, square the differences, sum them and divide by N for a population estimate or N - 1 for a sample variance estimate. Take the square root, multiply by the square root of return periods per year, and multiply by 100. N counts returns, so N + 1 valid consecutive closing prices are required.

Open Pine Editor, paste the complete v6 example and add it to a standard candlestick chart. Set the return count, annual periods and population or sample convention. Compare a closed bar with the calculation from the same prices. The blue plot reports annualized HV; the Data Window also exposes per-bar standard deviation. The script does not place trades or create alerts.

Use a count that matches the frequency and calendar of the returns being measured. A daily equity example can assume 252 trading observations per year; a daily crypto series with seven-day trading can assume 365. These are conventions, not automatic facts about every feed. For unchanged returns, replacing 252 with 365 multiplies the reported HV by sqrt(365 / 252), about 1.2035.

Its biased argument defaults to true, which uses the population convention. This example passes the argument explicitly: the Sample setting selects false for the N - 1 divisor. For N = 10 and identical returns, the sample result is sqrt(10 / 9), about 1.0541 times the population result. Bessel correction makes the variance estimate unbiased under the usual sampling assumptions; it does not make every derived standard deviation estimate unbiased.

There is no setting that establishes a profitable strategy by itself. The example starts with 10 returns to make a short rolling window easy to inspect. Compare it with 20 or 60 using the same data and statistical convention. A longer window retains older observations for longer; a shorter one is more sensitive to a single observation entering or leaving. Select the window before evaluating any trading rule.

Yes. The script uses chart-bar returns, so a one-hour chart supplies hourly returns. An illustrative continuous crypto calendar uses 365 times 24 = 8,760 hourly observations per year. Session-based markets require a different count and attention to overnight gaps, shortened sessions and missing bars. Changing the annualization input does not convert intraday returns into daily returns.

No. HV comes from a recorded price sample. Implied volatility is inferred from an option price through an option-pricing model and depends on contract terms and model inputs. Comparing them requires compatible annualization and horizons. A difference between the two does not prove that an option is mispriced or that selling premium is profitable; future events and option risks still matter.

The current close changes while a bar is open, so its log return and rolling deviation can change too. The example hides the annualized plot until barstate.isconfirmed is true by default. The per-bar diagnostic in the Data Window still updates. Historical results can also change after feed revisions or adjustments, so recording the symbol, session, adjustment setting and timestamp is part of reconciliation.