TradingView indicator calculations

Volatility Ratio on TradingView: Choose the Numerator and Baseline

A volatility ratio indicator compares a volatility measurement with a chosen baseline. Some scripts divide short-period return volatility by long-period volatility; others divide one bar's true range by a smoothed range. This page implements current true range divided by its current EMA, with an eight-bar fixture and explicit threshold sensitivity. A high value measures relative range expansion, not trade direction.

See the decision guide

One name, different denominators

Incredible Charts documents a range-based Volatility Ratio as true range divided by an EMA of true range, describing it as similar to Schwager's measure. TradingView author atxnfo instead describes a Historical Volatility Ratio using short and long historical-volatility periods. These names do not identify interchangeable calculations. This page implements an original, explicitly specified TR/EMA example; it does not reproduce a community script.

FamilyNumerator / denominatorInterpretation
Short/long return volatilityFor example, population stdev of the latest s log returns / population stdev of the latest L log returns, s < L; both windows include the current return.Recent return dispersion relative to a longer, overlapping window. Fix return definition, lengths and ddof before comparing scripts.
Range anomaly ratio used hereCurrent true range / current EMA of true range.The latest bar's range, including gaps, relative to an adapting range baseline.
Relative Volatility Index (RVI)A different directional oscillator, described by TradingView on a 0 to 100 scale.Its midpoint and thresholds do not apply to this dimensionless TR/EMA ratio.

The calculation contract

For bar t, TR = max(H − L, abs(H − previous close), abs(L − previous close)). Set alpha = 2 / (n + 1), seed E with the first valid TR, then update E[t] = alpha × TR[t] + (1 − alpha) × E[t−1]. Output VR[t] = TR[t] / E[t] after n consecutive valid bars and only when E[t] > 0. The denominator includes the numerator bar. This current-bar convention, seed and missing-data policy define this example explicitly.

  • Use n = 3 for the calculation below. No extra smoothing, annualization, percentage multiplier, return transform, variance or ddof is used. This is an EMA, not Wilder's RMA or an SMA.
  • At the first bar, or after a missing/invalid OHLC bar, use high minus low because no previous valid close is retained. Reset the EMA and warm-up counter on an invalid bar. Reject open or close outside the reported low/high interval.
  • The EMA retains decaying history beyond n bars. The n-bar gate is a reporting convention, not a finite rolling window or a guarantee that initialization effects have disappeared.
  • Evaluate completed bars only. A value of 1.6 means that bar's true range is 1.6 times its current EMA baseline; it supplies no upward or downward price forecast.

Eight synthetic bars with a gap

These invented OHLC values use arbitrary price units and consecutive chart bars. They are a reproducibility fixture, not market observations or a backtest. The first close before the dataset is unavailable, so bar 1 uses high minus low. Bar 4 opens above bar 3's close, and bar 8 contains a second upward gap.

BarOpenHighLowCloseTR
1100101991002
2100101991002
3100101991002
41061081061078
51071081061072
61071101051085
7108108.5107.51081
811812011811912

Recompute the baseline before choosing a threshold

With n = 3, alpha = 0.5. Bar 4 has an intrabar high-low range of 2, but TR = max(2, 8, 6) = 8 because the previous close was 100. Its EMA is 0.5 × 8 + 0.5 × 2 = 5, giving 8 / 5 = 1.6. Dividing by the previous EMA instead gives 8 / 2 = 4.0: that is a different indicator. Values below are rounded only for display.

  • Bar 8 is 12 / 7.3125 = 64 / 39. Its true range is larger than bar 4's, yet its ratio is only slightly larger because the baseline has changed.
  • If high minus low replaced TR everywhere in this fixture, bar 4 would have ratio 1.0 and would fail the 1.50 rule. Removing gaps changes the input measurement, not just the plot scale.
BarTRCurrent EMAVRVR > 1.50
122Unavailable: warm-upNo
222Unavailable: warm-upNo
3221.000000No
4851.600000Yes
523.50.571429No
654.251.176471No
712.6250.380952No
8127.31251.641026Yes

Threshold sensitivity and a mathematical ceiling

Selection means VR is strictly greater than the threshold on every eligible completed bar. It is not a crossover rule. Bars 3 through 8 are eligible under the warm-up rule. The following sets use the same unrounded ratios; no threshold is estimated from returns or presented as optimal.

  • For nonnegative TR and EMA history, E[t] ≥ alpha × TR[t]. Whenever the ratio exists, VR[t] ≤ 1 / alpha = (n + 1) / 2. With n = 3, a strict threshold of 2 can never trigger. Equality is possible after a zero baseline turns positive.
  • For n = 14 the ceiling is 7.5; changing the length changes both smoothing and this bound. A threshold borrowed from a prior-bar denominator, another length, or a return-volatility ratio has different meaning.
  • A spike raises its own denominator and dampens its ratio. After a spike, ordinary bars can read below one while the EMA decays. Thin trading, bad ticks, splits or futures rolls can also produce large readings; inspect the underlying bars before interpreting them.
ThresholdSelected barsCount / 6 eligible bars
1.104, 6, 83 / 6
1.504, 82 / 6
1.6281 / 6
1.70None0 / 6
2.00None0 / 6

Runnable Pine v6: fixture and chart modes

Paste this complete indicator into Pine Editor and add it to a chart with at least eight loaded bars. Fixture mode assigns the synthetic data to the first eight loaded bars and keeps their results in a table; later bars have no fixture value. Leave length at 3 to match the tables. Disable fixture mode to calculate on actual chart OHLC. The real-time bar stays unavailable until its closing update.

Original TR/current-EMA indicator. Default length 3; fixture alerts disabled.pinescript
//@version=6
indicator("Pineify Volatility Ratio: TR / current EMA", overlay = false, precision = 6)
bool fixture = input.bool(true, "Use eight-bar synthetic fixture")
int length = input.int(3, "EMA length", minval = 2, maxval = 500)
float threshold = input.float(1.5, "Strictly above threshold", minval = 0, step = 0.01)
var array<float> os = array.from(100.0, 100, 100, 106, 107, 107, 108, 118)
var array<float> hs = array.from(101.0, 101, 101, 108, 108, 110, 108.5, 120)
var array<float> ls = array.from(99.0, 99, 99, 106, 106, 105, 107.5, 118)
var array<float> cs = array.from(100.0, 100, 100, 107, 107, 108, 108, 119)
int i = bar_index
bool inFixture = i < array.size(hs)
float o = fixture ? (inFixture ? array.get(os, i) : na) : open
float h = fixture ? (inFixture ? array.get(hs, i) : na) : high
float l = fixture ? (inFixture ? array.get(ls, i) : na) : low
float c = fixture ? (inFixture ? array.get(cs, i) : na) : close
bool valid = not na(o) and not na(h) and not na(l) and not na(c) and h >= l and o >= l and o <= h and c >= l and c <= h
var float previousClose = na
var float baseline = na
var int count = 0
float tr = na
float ratio = na
bool selected = false
float alpha = 2.0 / (length + 1)
if barstate.isconfirmed
    if valid
        tr := na(previousClose) ? h - l : math.max(h - l, math.max(math.abs(h - previousClose), math.abs(l - previousClose)))
        baseline := na(baseline) ? tr : alpha * tr + (1 - alpha) * baseline
        count += 1
        ratio := count >= length and baseline > 0 ? tr / baseline : na
        selected := not na(ratio) and ratio > threshold
        previousClose := c
    else
        previousClose := na
        baseline := na
        count := 0
plot(ratio, "TR / current EMA", color = color.blue, linewidth = 2, style = plot.style_linebr)
hline(1, "Baseline", color = color.gray)
hline(threshold, "Selected threshold", color = color.orange)
plotshape(selected, "Selected bar", shape.circle, location.top, color.orange, size = size.tiny)
plot(tr, "True range", display = display.data_window)
plot(baseline, "EMA denominator", display = display.data_window)
alertcondition(not fixture and selected, "Range ratio above threshold", "Confirmed volatility ratio is above the configured threshold")
var table results = table.new(position.top_right, 5, 9, border_width = 1)
if barstate.isfirst and fixture
    table.cell(results, 0, 0, "Bar")
    table.cell(results, 1, 0, "TR")
    table.cell(results, 2, 0, "EMA")
    table.cell(results, 3, 0, "Ratio")
    table.cell(results, 4, 0, "Selected")
if fixture and inFixture and barstate.isconfirmed
    table.cell(results, 0, i + 1, str.tostring(i + 1))
    table.cell(results, 1, i + 1, str.tostring(tr, "#.######"))
    table.cell(results, 2, i + 1, str.tostring(baseline, "#.######"))
    table.cell(results, 3, i + 1, na(ratio) ? "na" : str.tostring(ratio, "#.######"))
    table.cell(results, 4, i + 1, selected ? "yes" : "no")

Check unavailable values and alerts

A zero denominator produces an unavailable ratio, including a flat sequence with high = low = close. After three flat bars at 100, a bar with open 101, high 102, low 101 and close 101 has TR = 2, EMA = 1 and ratio = 2. It passes 1.50 but not a strict 2.00 threshold. A missing bar resets the state: three subsequent valid bars are needed before reporting again at n = 3.

  • The missing-data rule applies to an actual invalid observation delivered to the script. A weekend or an absent feed bar creates no Pine execution; the next available bar still measures the gap from the last delivered close.
  • In chart mode, use standard OHLC candles and record symbol, timeframe, session and price adjustments. Heikin Ashi or other synthetic bars change the input. The script does not request another symbol or timeframe.
  • To receive notifications, create a TradingView alert on Range ratio above threshold and select Once Per Bar Close. Each selected closed bar can alert, including consecutive bars. Fixture mode cannot send this alert.
  • Syntax validation checks source acceptance. Compare the table and chart values separately; neither syntax success nor a high ratio proves a useful trade. Entry direction, exits, costs and position size would require separate strategy rules.

Adapt the explicit rule with Pineify

The Pine Script AI Coding Agent can draft and edit Pine code and request syntax checks. Give it this formula, initialization policy and eight-bar fixture before asking for changes. For a prior-bar baseline variant, require a second output column and recompute the same sample so a different denominator cannot silently inherit the current thresholds.

  • A concrete request: preserve the current-EMA ratio, add a separate prior-EMA comparison, retain unavailable values after resets, and show which completed bars exceed each chosen threshold.
  • Inspect the generated source, request syntax validation and compare values with the fixture. Treat any claim about trading performance as a separate question requiring market data and a defined test.
Where Pineify fits

Pine Script AI Coding Agent

Draft a specified volatility-ratio variant, inspect its Pine source and request syntax checks. Keep the fixture as an independent numerical check.

Adapt this ratio in Pine

Frequently asked questions

Educational calculations, not investment advice. All fixture prices are synthetic. Ratios and threshold counts are not trading returns, forecasts or evidence of profitability.

Sources and verification

Published by Pineify. Pineify is an independent product and is not affiliated with TradingView. Sources support the technical explanations; examples do not establish trading performance.