TradingView indicator comparisons

Relative Vigor Index vs RSI: Candle Structure and Close-to-Close Moves

Relative Vigor Index measures smoothed candle bodies relative to candle ranges. RSI measures smoothed gains and losses between successive closes. A gap up followed by a red candle can contribute negatively to RVI while contributing positively to RSI. In the same-data fixture below, RVI is -0.271186 while RSI is 100. RVI here means Relative Vigor Index, not the separate Relative Volatility Index.

See the decision guide

Resolve the name before comparing the formulas

The abbreviation RVI is also used for Relative Volatility Index. That is a different indicator and its formula is outside this comparison. Select Relative Vigor Index by full name in TradingView. Ehlers describes its basic close-minus-open, high-minus-low relationship in his original Stocks & Commodities article; TradingView documents the smoothed calculation used below.

PropertyRelative Vigor IndexClose-based Wilder RSI
Price inputOpen, high, low and closeCurrent and previous close
Direction componentC - ODelta = C - previous C
NormalizationSmoothed high-low rangesWilder average gains plus average losses
Center and scaleCenter 0; between -1 and +1 under valid OHLC and positive denominatorCenter 50; between 0 and 100 when defined
Additional lineFour-point weighted RVI signalNo additional RSI signal line in this comparison

Smooth the components before taking the RVI ratio

For bar t, define B = C - O and R = H - L. Let W4(x)t = (x_t + 2x_(t-1) + 2x_(t-2) + x_(t-3)) / 6. Compute U = W4(B) and D = W4(R), then RVI_t = sum(U, N) / sum(D, N). The same N in both sums makes this identical to SMA(U, N) / SMA(D, N). Signal_t = W4(RVI)t. These weights and the order of operations match TradingView's formula image.

  • The 1, 2, 2, 1 weights are not a four-period SMA or Wilder smoothing. Do not substitute a conventional linearly weighted moving average.
  • Averaging U / D gives a different indicator when D varies. Keep numerator and denominator separate until after the N-bar aggregation. A single zero-range bar contributes zero body and zero range; it does not require a per-bar division.
  • For RSI, set G = max(Delta, 0) and L = max(-Delta, 0). Seed each average with its first N consecutive changes, then update A_t = ((N - 1)A_(t-1) + input_t) / N. Compute RSI = 100 × averageGain / (averageGain + averageLoss), equivalent to 100 - 100 / (1 + RS) when both averages are positive.
  • All valid candles satisfy |C - O| ≤ H - L. Positive weights and sums preserve that inequality, proving |RVI| ≤ 1 when the denominator is positive. This mathematical bound does not supply an overbought threshold.

A complete OHLC fixture with opposing gap and candle directions

These 12 artificial bars use N = 3 for both calculations. No observations precede bar 0. Every row is a completed candle. Prices are arbitrary units, not a market sample. All input bars are included so the weighted components and Wilder seed can be reproduced.

  • Bar 5 opens at 112, three units above the previous 109 close, then closes at 111. Its body is -1 but its close-to-close change is +2. RSI counts a gain while RVI receives a negative body.
  • Bar 6 opens at 104, seven units below the previous 111 close, then closes at 105. Its body is +1 but its close-to-close change is -6. RSI counts a loss while RVI receives a positive body.
  • RVI is not a measurement of total overnight-plus-session return. Its denominator also excludes the gap from previous close to open. RSI includes that gap in the net close change but discards the intrabar path.
BarOpenHighLowCloseC - OH - LClose change
01001029910113na
1104105102103-132
2106107104105-132
3108110106107-142
4110111108109-132
5112113110111-132
610410710310514-6
710210410110313-2
81001029910113-2
998100979913-2
109698959713-2
119496939513-2

Check the weighted components and final outputs

Values are rounded to six decimals after calculation. U and D start at bar 3. Three such observations first produce RVI at bar 5; four consecutive RVI values first produce its signal at bar 8. RSI begins at bar 3 with changes +2, +2 and +2, so its seed is averageGain = 2 and averageLoss = 0.

  • At bar 3, U = (-1 - 2 - 2 + 1) / 6 = -2/3, while D = (4 + 6 + 6 + 3) / 6 = 19/6. At bar 5, the U sum is -8/3 and D sum is 59/6; RVI = -16/59 = -0.271186.
  • At bar 6, Wilder averageGain = (2 × 2 + 0) / 3 = 4/3 and averageLoss = (2 × 0 + 6) / 3 = 2. RSI = 100 × (4/3) / (10/3) = 40.
  • At bar 8, Signal = (0 + 2 × (-1/6) + 2 × (-4/15) - 16/59) / 6 = -0.189642. The signal is a weighted average of the last four RVI values, not of their numerators.
  • On the paired valid bars 5 through 11, strict RVI > 0 selects bars 9, 10 and 11; RSI > 50 selects bar 5. Bar 8 equals RVI zero and is excluded. These are filter selections, not entry signals or trading returns.
BarU: weighted bodyD: weighted rangeRVISignalWilder RSI
3-0.6666673.166667nana100.000000
4-1.0000003.333333nana100.000000
5-1.0000003.333333-0.271186na100.000000
6-0.6666673.333333-0.266667na40.000000
70.0000003.333333-0.166667na30.769231
80.6666673.3333330.000000-0.18964222.857143
91.0000003.1666670.169492-0.07175116.494845
101.0000003.0000000.2807020.07550311.636364
111.0000003.0000000.3272730.2046108.070618

Make readiness and undefined cases explicit

The script applies a strict policy to both series: missing or invalid OHLC invalidates the observation, even though standalone RSI only needs closes. This shared input policy makes the comparison auditable. It is an example convention, not a claim about undocumented built-in handling.

  • RVI requires N + 3 consecutive valid OHLC bars after a missing observation; its signal requires another three defined RVI bars. RSI requires N + 1 consecutive valid OHLC bars after reset to create N differences.
  • RSI has recursive memory beyond N bars after seeding; RVI uses a finite N + 3 bar input window. Matching N alone therefore does not match effective memory or sensitivity.
  • Adding earlier chart history can change the Wilder seed and later RSI. Compare both scripts on the same feed, session, standard candle type, timeframe and loaded history. Synthetic chart types change the input prices.
CaseDefined behavior in this example
Clean initializationRVI first at zero-based bar N + 2; signal at N + 5; RSI at N.
Missing or invalid OHLCReject if any field is na, H < max(O,C), or L > min(O,C). RVI windows propagate na. RSI resets and needs N new consecutive changes.
One zero-range candleValid only when O = H = L = C. Include its zero body and range; RVI may remain defined from other bars.
Whole smoothed denominator zeroRVI is na and its signal needs four new defined RVI observations. Do not carry the last reading.
RSI gains positive, losses zeroRSI = 100. With gains zero and losses positive, RSI = 0.
RSI gains and losses both zeroRSI is na by explicit policy. A neutral 50 fallback would be a different policy.
N = 1RVI still needs four bars because W4 remains. RSI is 100 for a rise, 0 for a fall, na for unchanged closes.
Exact threshold equalityRVI = 0 and RSI = 50 do not satisfy the strict positive-state filters.

Run the complete Pine Script v6 comparison

Keep fixture mode on and length at 3 to reproduce the tables. The script feeds the first 12 loaded chart bars with the synthetic OHLC matrix and shows their results in a persistent table. After those bars its fixture inputs are na, so the current plot is intentionally empty. The table requires at least 12 completed chart bars. Disable fixture mode for chart OHLC. The pane selector avoids overlaying RVI and RSI on one scale; both remain in the Data Window.

  • The fixture uses chart bars only as execution slots; it is not historical price data. Changing length changes the outputs and may leave the 12-bar fixture too short for the RVI signal.
  • barstate.isconfirmed gates plots, filter values and fixture table rows. No higher-timeframe request is used. Indicators still recalculate internally during an open bar, but unfinished values are not displayed here.
  • The script exposes RVI > 0 and RSI > 50 states only when both readings exist. A crossing rule would also need a valid preceding reading and a defined equality convention. Signal-line crossings are a separate RVI rule.
Original comparison indicator with explicit seeding and invalid-data rules. It displays confirmed bars and submits no orders.pine
//@version=6
indicator("Relative Vigor Index vs RSI: same bars", overlay = false, max_bars_back = 500)
fixture = input.bool(true, "Use 12-bar calculation fixture")
length = input.int(3, "Shared length", minval = 1, maxval = 200)
pane = input.string("RVI", "Pane scale", options = ["RVI", "RSI"])
var opens = array.from(100.0, 104.0, 106.0, 108.0, 110.0, 112.0, 104.0, 102.0, 100.0, 98.0, 96.0, 94.0)
var highs = array.from(102.0, 105.0, 107.0, 110.0, 111.0, 113.0, 107.0, 104.0, 102.0, 100.0, 98.0, 96.0)
var lows = array.from(99.0, 102.0, 104.0, 106.0, 108.0, 110.0, 103.0, 101.0, 99.0, 97.0, 95.0, 93.0)
var closes = array.from(101.0, 103.0, 105.0, 107.0, 109.0, 111.0, 105.0, 103.0, 101.0, 99.0, 97.0, 95.0)

weighted4(float x) =>
    (x + 2 * x[1] + 2 * x[2] + x[3]) / 6

strictSum(float x, int n) =>
    float total = 0.0
    for i = 0 to n - 1
        total += x[i]
    total

// Each call has its own state. Seed with N consecutive changes.
wilder(float x, int n) =>
    var float value = na
    var float seed = 0.0
    var int count = 0
    if na(x)
        value := na
        seed := 0.0
        count := 0
    else if na(value)
        seed += x
        count += 1
        if count == n
            value := seed / n
    else
        value := ((n - 1) * value + x) / n
    value

// Fixture occupies the first 12 loaded chart bars; later input is na.
int k = bar_index
float o = fixture ? (k < 12 ? array.get(opens, k) : na) : open
float h = fixture ? (k < 12 ? array.get(highs, k) : na) : high
float l = fixture ? (k < 12 ? array.get(lows, k) : na) : low
float c = fixture ? (k < 12 ? array.get(closes, k) : na) : close
valid = not na(o) and not na(h) and not na(l) and not na(c) and h >= math.max(o, c) and l <= math.min(o, c)
body = valid ? c - o : na
span = valid ? h - l : na
sourceClose = valid ? c : na
numerator = weighted4(body)
denominator = weighted4(span)
numSum = strictSum(numerator, length)
denSum = strictSum(denominator, length)
rvi = not na(denSum) and denSum > 0 ? numSum / denSum : na
signal = weighted4(rvi)
change = sourceClose - sourceClose[1]
gain = na(change) ? na : math.max(change, 0)
loss = na(change) ? na : math.max(-change, 0)
avgGain = wilder(gain, length)
avgLoss = wilder(loss, length)
// Both averages zero is undefined by this example's explicit policy.
rsi = not na(avgGain) and not na(avgLoss) and avgGain + avgLoss > 0 ? 100 * avgGain / (avgGain + avgLoss) : na
confirmed = barstate.isconfirmed
paired = not na(rvi) and not na(rsi)
plot(confirmed and pane == "RVI" ? rvi : na, "RVI pane", color.blue)
plot(confirmed and pane == "RVI" ? signal : na, "RVI signal pane", color.orange)
plot(confirmed and pane == "RSI" ? rsi : na, "RSI pane", color.blue)
plot(confirmed ? rvi : na, "RVI", display = display.data_window)
plot(confirmed ? signal : na, "Signal", display = display.data_window)
plot(confirmed ? rsi : na, "RSI", display = display.data_window)
plot(confirmed ? numerator : na, "Weighted body", display = display.data_window)
plot(confirmed ? denominator : na, "Weighted range", display = display.data_window)
plot(confirmed ? avgGain : na, "Wilder average gain", display = display.data_window)
plot(confirmed ? avgLoss : na, "Wilder average loss", display = display.data_window)
plot(confirmed and paired ? (rvi > 0 ? 1 : 0) : na, "RVI above zero", display = display.data_window)
plot(confirmed and paired ? (rsi > 50 ? 1 : 0) : na, "RSI above 50", display = display.data_window)
// Persist fixture values in a table so scrolling to the first bars is optional.
var report = table.new(position.top_right, 4, 13, bgcolor = color.white, border_width = 1)
if fixture and barstate.isfirst
    table.cell(report, 0, 0, "Bar", text_color = color.black)
    table.cell(report, 1, 0, "RVI", text_color = color.black)
    table.cell(report, 2, 0, "Signal", text_color = color.black)
    table.cell(report, 3, 0, "RSI", text_color = color.black)
if fixture and k < 12 and confirmed
    table.cell(report, 0, k + 1, str.tostring(k), text_color = color.black)
    table.cell(report, 1, k + 1, str.tostring(rvi, "#.######"), text_color = color.black)
    table.cell(report, 2, k + 1, str.tostring(signal, "#.######"), text_color = color.black)
    table.cell(report, 3, k + 1, str.tostring(rsi, "#.######"), text_color = color.black)

Turn a measurement choice into a testable rule

Use the Pine Script AI Coding Agent to draft or edit the chosen comparison rule. Supply the full name Relative Vigor Index, the W4 weights, N, the Wilder seed, zero-denominator policy and close-confirmation requirement. Explicitly request syntax checking, then compare the generated values against the fixture before adding entries and exits.

  • Neither formula establishes that a reversal is imminent. RSI can stay near 100 while closes continue rising; RVI can remain negative during a sequence of gap-ups and red bodies. Agreement between them is not independent evidence because both use the same price history.
  • To compare trading outcomes, hold position sizing, entries, exits, commission, slippage and fill timing fixed. Select parameters before evaluating a reserved period. This arithmetic fixture provides no evidence that either indicator produces higher returns.
Where Pineify fits

Pine Script AI Coding Agent

Draft an RVI and RSI comparison with explicit formulas and confirmation rules. Request syntax checking and review the values against a reproducible fixture.

Draft a comparison script

Frequently asked questions

Educational calculation comparison, not investment advice. The OHLC sequence is synthetic and is not a market backtest. Syntax validation does not establish profitable rules or correct execution.

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.