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.
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.
| Observation | Closing price | Log return | Squared deviation |
|---|---|---|---|
| Start | 100.000000 | Not available | Not available |
| 1 | 101.005017 | 0.010000 | 0.000100 |
| 2 | 100.000000 | -0.010000 | 0.000100 |
| 3 | 102.020134 | 0.020000 | 0.000400 |
| 4 | 100.000000 | -0.020000 | 0.000400 |
| 5 | 100.000000 | 0.000000 | 0.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.
| Convention | A = 252 daily periods | A = 365 daily periods |
|---|---|---|
| Population, divisor 5 | 22.4499% | 27.0185% |
| Sample, divisor 4 | 25.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.
//@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
| Input | Example default | Effect |
|---|---|---|
| Return observations | 10, minimum 2 | Number of log returns in the rolling standard deviation. |
| Return periods per year | 252 | Rescales per-bar deviation; never changes the source timeframe. |
| Sample standard deviation | Off | Off divides variance by N; on divides by N - 1. |
| Show only closed bars | On | Hides the open-bar annualized plot until confirmation. |
Set A from the return frequency
| Illustrative chart | Annual periods A | Constraint |
|---|---|---|
| Daily exchange-traded equity | 252 | An assumed trading-day count, not a guaranteed session count for every calendar. |
| Daily crypto | 365 | Assumes seven-day trading and one daily observation; leap years and data gaps require a documented convention. |
| Weekly time bars | 52 | Uses weekly returns. A daily factor would rescale the wrong frequency. |
| One-hour continuous crypto | 8,760 = 365 × 24 | Assumes complete hourly observations throughout the year. |
| 30-minute, 6.5-hour equity session | 3,276 = 252 × 13 | A 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
| Observation | What it establishes | What remains unknown |
|---|---|---|
| HV rises on a closed bar | The rolling return dispersion increased under unchanged settings. | Whether the next move is up or down. |
| HV drops sharply | The new window has less return dispersion; an outlier may have rolled out. | Whether future conditions will remain quiet. |
| HV is above a prior baseline | The recent sample differs from the comparison sample. | Whether a particular entry or option trade has an edge. |
| HV changes after a setting change | The sample or scaling convention changed. | Whether market behavior changed at all. |
Three research uses with separate trading rules
- 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.
- 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.
- 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
| Measure | Input and output | Comparison constraint |
|---|---|---|
| Historical volatility | Past log returns; annualized percentage. | Match return window, divisor, annualization and feed. |
| Implied volatility | Option price and a pricing model; volatility parameter. | Match maturity and annualization; contract and model inputs matter. |
| ATR | True ranges smoothed over bars; price units. | Includes high-low range information that close-to-close HV omits. |
| Bollinger Bands | A 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.
- Open the Pine Script AI Coding Agent with your timeframe, feed and calendar assumptions.
- Specify the return formula, N or N - 1 divisor, lookback and annual periods.
- Review the generated code for source timeframe, invalid-data behavior and confirmation timing.
- Compare closed-bar results with an external calculation using the same unrounded closes.
- 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 scriptHistorical volatility questions
Sources and further reading
Technical references reviewed October 17, 2026. The five-return dataset and annual-period examples are explicit teaching assumptions.