@pond-ts/financial API Reference
    Preparing search index...

    Function relativeVolatilityIndex

    • Relative Volatility Index (Donald Dorsey, 1993) — rsi's form applied to the standard deviation of the price instead of to the price change: of the recent volatility, how much of it happened on up bars?

      σ[i]  = stdev(column over stdevPeriod)          population, ddof = 0
      up[i] = σ[i] if column[i] > column[i−1] else 0
      dn[i] = σ[i] if column[i] <= column[i−1] else 0
      U = Wilder(up, period); D = Wilder(dn, period)
      ${output} = 100 × U / (U + D)

      Appends one column, bounded 0..100. Dorsey built it as a confirming indicator rather than a standalone one: momentum studies and volatility studies fail on different kinds of market, so a breakout that RSI likes and this one does not is a breakout on falling volatility. The conventional thresholds are RSI's — above 50 (or 60) for confirmation of a long, below for a short.

      "RVI" is used in the wild for both this and the Relative Vigor Index, which is a completely different study (see relativeVigorIndex: a ratio of smoothed candle bodies to smoothed ranges, not bounded, not built on σ). So this study's default output is relVol, not rvi — the shorter name stays with the Vigor Index, which claimed it first and whose option is a prefix shared with a signal column. The two can therefore both be appended to one series with no output juggling, which is the point of not reusing it.

      Three real forks, all shipped by someone:

      • Dorsey's revision (1995), which is what ships: σ over 10 bars, Wilder-smoothed over 14, on the close. His 1993 original ran the whole thing on a 9-bar σ with a 14-bar smoothing; the revision is what every current write-up states, and both lengths are options here so the original is { period: 14, stdevPeriod: 9 }.
      • The high/low variant, also Dorsey's: compute the whole index separately on the highs and on the lows and average the two readings. That is a second study composed of two of these, and it is deliberately not an option — a caller who wants it runs this twice with column: 'high' and column: 'low' and averages, which is three lines and is visible.
      • The EMA-smoothed variant (TradingView's built-in rvi smooths the two legs with an EMA rather than Wilder's recursion). This ships Wilder's, because Dorsey defined it as "RSI's calculation with σ substituted for the price change" and RSI's smoothing is Wilder's — the rsi kernel's own argument, that a recursive average is defined by its seed as much as by its rate. The two differ materially, not cosmetically; the measured separation on the oracle input is recorded in the generator.

      TA-Lib has no RVI of either kind, so the oracle is a pandas replication with the analytic first-valid bar asserted and a measured separation from the EMA-smoothed fork.

      • Population σ (ddof = 0) — the package convention (bollinger, rollingStdev, zScore, historicalVolatility, and TA-Lib's own STDDEV). It comes from rollingValues' stdev, so it is rollingStdev's number bit-for-bit rather than a second implementation. A sample σ (ddof = 1) is a constant factor larger and cancels between the two legs, so — unusually — the choice does not move this reading at all; it is stated for consistency with the rest of the package rather than because it matters here.
      • An unchanged close counts as a down bar. Dorsey's rule is "up if the close rose", so everything else — including a flat bar — is the other leg. This is a deliberate asymmetry against rsi, whose upDownLegValues split gives a flat bar 0 on both legs; there, a flat bar contributes nothing, here it contributes its σ to the downside. Both are their authors' definitions and neither is being reconciled — the fork is stated per study, which is the package's rule for exactly this.

      σ first lands on bar stdevPeriod − 1; the legs need a direction as well, so from bar 1; the Wilder seed then steps over the leading gap and lands period − 1 bars later. The column first appears on stdevPeriod + period − 2 — bar 22 at the defaults. Length-preserving.

      Over another study's output the σ starts earlier than that rule suggests, because it reads the column door: its window counts rows and computes over the finite cells it holds, so a window with one value reports σ = 0 rather than nothing (rollingStdev's contract, the one historicalVolatility flags in its own edges). Measured: relativeVolatilityIndex({ column: 'sma', period: 3, stdevPeriod: 3 }) over an sma(3) first lands on bar 5, one earlier than the 2 + 3 + 3 − 2 the rule alone would give. Pinned by a test.

      • Scale-invariant, and shift-invariant. σ is homogeneous of degree one in price (so scaling cancels between the two legs) and unchanged by adding a constant, and the up/down test is a comparison of two prices, so neither transformation moves the reading. Both are pinned by property tests.
      • Bounded 0..100, both ends attainable: a window whose every direction was up reads 100, and its mirror reads 0. Pinned.
      • A flat window is 0/0 and needs no guard. U + D = 0 means every σ in the smoother's memory was zero, which forces U = 0 with it — so the ratio is JavaScript's own NaN and the study reports undefined on a halted instrument with no branch (the commodityChannelIndex finding: a guard no input can distinguish is dead code). σ is never negative, so there is no crossing-columns case here either; this is a genuinely guard-free study, unlike ulcerIndex and choppinessIndex in the same batch.
      • A stdevPeriod of 1 makes every σ zero, so the whole column is undefined — honest, and the reason the option is validated as a period rather than special-cased.
      • A misnamed column reads all-missing rather than throwing, and that is a reducer-level split rather than a study-level one worth knowing about: rollingValues' stdev (and avg) take the range-exact kernel, which reads a column that is not there as all-NaN, while its max / min fall through to core's sweep, which rejects the name. So this study answers empty where ulcerIndex and verticalHorizontalFilter — on the same door, one reducer along — throw. Both are pinned by tests; neither is being reconciled here, because the fix belongs in the kernel.
      • A leading gap shifts the start rather than emptying the study — the Wilder kernel steps its seed over one, so running this over another study's output begins late and is otherwise unaffected. An interior gap propagates to the end of the series: a recursion has no state to carry across a hole. That is rsi's and atr's behaviour for the same structural reason, and it is the sharpest difference between this study and the other six in its batch, all of which are windows and recover. Fill before smoothing if you need continuity.

      Type Parameters

      • S extends SeriesSchema
      • const Output extends string = "relVol"

      Parameters

      Returns TimeSeries<
          readonly [S[0], ValueColumnsForSchema<S>, OptionalNumberColumn<Output>],
      >