Skip to content

(RVIR) Relative Volatility Index, 1995 refined form #416

Description

@mario4tier

What it is

Donald Dorsey, "Refining the Relative Volatility Index", TASC V.13:9 (Sep 1995) 388-391 — the
revision of his V.11:6 (Jun 1993) close-only RVI. It runs the 1993 RVI over daily highs and over
daily lows and averages the two indices; the closes are the fallback when a feed carries no
high/low (Chart manual).

The leg this function is made of ships: TA_RVI landed 2026-09-05 in commit 8c0fedbcd
(#366), ta_codegen/input/rvi/{rvi.c,rvi.yaml,rvi.md} at HEAD. RVIR is that body run
twice and averaged; it invents nothing, and it inherits every variant choice that card made.

The refined form is a different series, not a re-parameterisation: MEASURED at (14,10) on the
252-bar corpus, max |RVIR − RVI(close)| = 18.05 index points, mean 5.52, Pearson r 0.709.

Name collision, and it must be said on both docs. The Chart manual ("which is the RVI used in
Chart"
) and DXcharts reserve the bare name RVI for this function and call
#366's function RVIorig; pandas-ta-classic and trading-signals default the other
way. TA_RVI has shipped under the close form, so the pair cannot be swapped. rvi.md:21 already
carries the collision note; rvir.md owes the reciprocal.

Formula

n1 = standard-deviation window (default 10), n2 = Wilder smoothing period (default 14).
RVI(...) is the shipped TA_RVI unchanged — its ties-feed-neither rule
(ta_codegen/input/rvi/rvi.c:137-141) and its 50.0 on a zero denominator (rvi.c:225) included.

RVIR_i = 0.5 * ( RVI(high)_i + RVI(low)_i )

Primary-source trace. The 1995 article's PDF is paywalled ($3.95) and was not purchased; the
store listing was fetched (HTTP 200) for title, author, pagination and the opening two paragraphs,
which are about trend and inertia and do not state the arithmetic. The arithmetic is traced instead
through a MetaStock formula-language reproduction of the article, published under the heading
"New Relative Volatility Index" and labelled "Appeared in the September 95 issue of Stocks &
Commodities magazine"
— code, not prose, in the formula language the article was written into
(provenance qualified under References: the host credits traders.com, not Equis):

(100 * Mov(If(HIGH > Ref(HIGH,-1), Std(HIGH,10),0),14,E) /
 (Mov(If(HIGH > Ref(HIGH,-1), Std(HIGH,10),0),14,E) +
  Mov(If(HIGH < Ref(HIGH,-1), Std(HIGH,10),0),14,E)) +
 100 * Mov(If(LOW > Ref(LOW,-1), Std(LOW,10),0),14,E) /
 (Mov(If(LOW > Ref(LOW,-1), Std(LOW,10),0),14,E) +
  Mov(If(LOW < Ref(LOW,-1), Std(LOW,10),0),14,E))) / 2

That settles four things structurally, in one source:

  1. Two full RVI legs, each with its own direction gate and its own Std — not RVI of an averaged
    price.
  2. Denominator is EMA(up) + EMA(down). A bar with HIGH == Ref(HIGH,-1) makes both If()
    legs zero, so its sigma enters neither numerator nor denominator — ties feed neither, which is
    exactly rvi.c:137-141.
  3. Std 10, smoothing 14 — the shipped defaults.
  4. No zero-denominator guard, so TA-Lib's 50.0 is a house choice. App::Chart and
    trading-signals 8.3.0 independently pick 50 as well.

The averaging spelling is not a variant axis. 0.5*(a+b), (a+b)*0.5, (a+b)/2.0 and
0.5*a + 0.5*b are bit-identical for operands in [0,100]: scaling by a power of two is exact, so
each is one rounding of a+b. MEASURED: 0 mismatches in 200 000 random pairs from [0,100]. No
spelling gate is owed.

Both legs are the same function at the same parameters, so they share a lookback and seed on the
same bar — no anchor question, no max(). Fuse them into one loop rather than KC's
malloc-two-scratch shape (ta_codegen/input/kc/kc.c:81,88): the result is bit-equal to two TA_RVI
calls by construction, which makes the differential gate below free.

lookbackTotal = (optInStdDevPeriod - 1) + (optInTimePeriod - 1)
              + TA_GetUnstablePeriod(TA_FUNC_UNST_RVI);   /* == TA_RVI_Lookback, rvi.c:15-19 */
if( startIdx < lookbackTotal ) startIdx = lookbackTotal;
if( startIdx > endIdx ) { *outBegIdx = 0; *outNBElement = 0; return TA_SUCCESS; }

/* Two independent copies of TA_RVI's state, one over inHigh, one over inLow:
 * var.c's shifted-sum cursors plus prevUp/prevDn.  Coefficients spelled ONCE,
 * wBeta first (ta_codegen/input/rma/rma.c:51-52); both legs share them.
 * Every read of bar i in BOTH legs must precede the store to outReal[i]: with
 * outReal aliasing inHigh or inLow the slack is exactly zero at n2 == 1. */
wBeta  = (double)(optInTimePeriod - 1) / (double)optInTimePeriod;
wAlpha = 1.0 - wBeta;
   ... seed both legs exactly as TA_RVI does, then per bar ...
outReal[outIdx++] = 0.5 * ( rviHigh + rviLow );

TA_RVIR_Lookback == TA_RVI_Lookback. MEASURED at (14,10), unstable period 0, on the 252-bar
reference: outBegIdx = 22, outNBElement = 230. The composed trading-signals arm reports the same
beg at all four tuples — (n2,n1) = (14,10) → 22, (30,30) → 58, (1,10) → 9, (2,2) → 2.

Rejected variants — MEASURED at (14,10) on TA_SREF_{high,low}_daily_ref_0_PRIV (252 bars, src/tools/ta_regtest/test_data.c:38,65), max abs deviation from the proposed form

Variant Source Max dev Why rejected
plain EMA 2/(n2+1), ties→neither community Pine; pandas' mamode default "ema" 12.94 (bar 73); tail bar 251 3.80 Dorsey defines RVI as "the relative strength index (RSI) with the standard deviation over the past 10 days used in place of daily price change" (V.11:6 p.253, quoted verbatim on the Equis 1993 page) — and RSI's smoother is Wilder's. The 1993 transcription is that recursion exactly, ((PREV*13)+x)/14, alpha = 1/14. Counter-evidence, stated because it is real: the 1995 page writes Mov(...,14,E), a plain EMA, against the 1993 page's exact Wilder form — the familiar MetaStock shorthand. It loses to the 1993 transcription and to Dorsey's own RSI sentence, and the smoother is #366's axis in any case.
plain EMA + ties→down as above 13.78 (bar 73) Both flips at once; the isolated EMA cost is the row above.
RVI( (high+low)/2 ) — average the prices, not the indices none published; the reimplementer's shortcut 13.58 The 1995 code has two independent Std() calls and two independent gates. Looks like a simplification; is not one.
thirds: (RVI(h)+RVI(l)+RVI(c))/3 pandas thirds=True (rvi.py:45-55) 6.02 at (14,10); 33.33 at L=3 and 15.86 at L=5 in pandas' own arithmetic Not Dorsey's; no other source carries it. A separate indicator, not a variant.
tie (x[i] == x[i-1]) → down App::Chart 275 RVI.pm (den = EMA(stddev)) 4.64 (bar 88); tail bar 251 still 2.24e-1 Both If() legs of the 1995 code are 0 on a tie. App::Chart is the documented negative control.
tie → up ProRealCode 3.76 (bar 81) Only source that does this.
9-period stddev fmlabs; DXcharts prose 3.23 (bar 83) One source family against five; also moves outBegIdx 22 → 21. DXcharts contradicts itself — prose says "9-period standard deviation", its own legend says "SD - 10-days' standard deviation" — so cite DXcharts for structure, never for the period.

Proposed API

TA_RetCode TA_RVIR( int    startIdx,
                    int    endIdx,
                    const double inHigh[],
                    const double inLow[],
                    int    optInTimePeriod,     /* From 1 to 100000, default 14 */
                    int    optInStdDevPeriod,   /* From 2 to 100000, default 10 */
                    int   *outBegIdx,
                    int   *outNBElement,
                    double outReal[] );

int TA_RVIR_Lookback( int optInTimePeriod, int optInStdDevPeriod );
/* (n1-1) + (n2-1) + TA_GetUnstablePeriod(TA_FUNC_UNST_RVI) */
  • Group Volatility Indicators, matching the shipped rvi.yaml:2 and pandas' category. One
    output outReal, flags: [line] — well under the cap of 4 (src/tools/ta_regtest/test_codegen.h:16).
  • Input bundle inPriceHL, price_components: [high, low] — the existing DONCHIAN bundle
    (ta_codegen/input/donchian/donchian.yaml:6-8). Arity 2. This is why the refined form cannot be a
    boolean flag on TA_RVI, which takes a bare inReal (rvi.yaml:5-7).
  • Parameters, ranges and defaults are the shipped rvi.yaml:8-22, unchanged and in that order.
    App::Chart is a third witness that the two periods are separate and defaulted 10/14, after
    thinkorswim and trading-signals 8.3.0.
  • Flags [stream] — not unstable_period, not path_dependent.
  • No new TA_FUNC_UNST_* id, and no second one either. RVIR reads TA_FUNC_UNST_RVI
    (id 26, src/tools/ta_regtest/test_internals.c:1158), the way efi.yaml:4 declares
    flags: [stream] only while efi.c:22 reads TA_FUNC_UNST_EMA. KC's two-id case does not arise:
    KC inherits two because its legs are different functions with different lookbacks; RVIR's legs
    are the same function at the same parameters, so a per-leg unstable period has no user story and
    would break outBegIdx == lookback (BBANDS with MAType_MAMA and period >= 34: middle band misaligned #99) by over-warming one leg. One id means
    doRangeTestEx, not doRangeTestMulti — ta_test_priv.h:254-256 still names KC as the only
    two-id function, and the shipped test_rvi.c:856 already uses doRangeTestEx.

Decisions

  1. Ties feed neither leg — inherited from the shipped TA_RVI (rvi.c:137-141) and supported by
    the 1995 and 1993 MetaStock reproductions, whose denominators are EMA(up) + EMA(down). Tally:
    ties→neither = 1993 formula + 1995 formula + pandas-ta-classic 0.6.52 + trading-signals 8.3.0 +
    shipped TA_RVI; ties→down = App::Chart alone, plus prose (Chart manual, DXcharts) that writes
    the denominator as "EMA of SD".
  2. Wilder smoothing (alpha = 1/n2), not 2/(n2+1) — inherited from (RVI) Relative Volatility Index #366; the 1993
    ((PREV*13)+x)/14 transcription plus Dorsey's own RSI sentence are the primary-source support.
    Cost of the flip is MEASURED at 12.94.
  3. 50.0 on a zero denominator — inherited; house choice, no source states a guard, and
    App::Chart and trading-signals both land on 50 independently.
  4. thirds declined. pandas ships it, nobody else does, and it is a different indicator, not a
    variant: MEASURED 33.33 at L=3 and 15.86 at L=5 against refined=True in pandas' own arithmetic.
    Not a parameter on this function; a separate card at most.
  5. Group Volatility Indicators; flags: [stream]; one unstable id, borrowed.

References

[F] = fetched this session, HTTP 200. [C] = cited, fetched for #366 rather than here.

  • [F] MetaStock-language reproduction of the 1995 article — "New Relative Volatility Index" and
    "Inertia", labelled "Appeared in the September 95 issue of Stocks & Commodities magazine",
    quoted in full under Formula; the source that settles the tie rule. Read at
    web.archive.org/web/20110803233955id_/http://trader.online.pl/MSZ/e-w-Inertia.html
    (trader.online.pl is dead). Provenance, stated exactly: the page is a third-party MetaStock
    formula collection and credits traders.com; it does not carry an Equis byline, unlike the
    same site's 1993 page. App::Chart's header cites both, calling the 1993 one "Reproducing
    equis.com original 1993 version"
    and this one "1995 revision".
  • [F] Equis/MetaStock, 1993 RVI (same site, credited to equis.com) —
    @RVI Down = ((PREV*13)+If(ROC(C,1,%)<0,Stdev(C,10),0))/14, @RVI Up likewise with >0,
    @RVI = (100*Fml("@RVI Up"))/(Fml("@RVI Up")+Fml("@RVI Down")). ((PREV*13)+x)/14 is the
    Wilder recursion. The same page quotes Dorsey verbatim from TASC V.11:6 (253-256): "The RVI is
    simply the relative strength index (RSI) with the standard deviation over the past 10 days used in
    place of daily price change."
    Read at
    web.archive.org/web/20110514040046id_/http://trader.online.pl/MSZ/e-w-Relative_Volatility_Index_RVI.html.
  • [F] Dorsey, "Refining the Relative Volatility Index", TASC V.13:9 (Sep 1995) 388-391 —
    https://store.traders.com/-v13-c09-refinin-pdf.html. Paywalled at $3.95, not purchased;
    title, author, pagination and the opening two paragraphs are from the listing, and those
    paragraphs are about trend and inertia, not the arithmetic.
  • [F] Chart manual (Kevin Ryde),
    https://user42.tuxfamily.org/chart/manual/Relative-Volatility-Index.html — "In 1995 Dorsey
    revised his idea, to apply RVI to the daily highs and daily lows and average the result, thus the
    following, which is the RVI used in Chart"
    , then RVI = (RVIorig of highs + RVIorig of lows) / 2;
    "The stddev period of 10 days and the default EMA of 14 days are parameters"; "The EMA period
    follows J. Welles Wilder's reckoning … the same as an RSI."
  • [F] DXcharts, https://devexperts.com/dxcharts/kb/docs/relative-volatility-index-rvi —
    "RVI = (RVIorig of highs + RVIorig of lows) / 2". Internally inconsistent on the stddev period
    (prose "9-period", legend "SD - 10-days'"); cite for structure only.
  • [F] App::Chart 275 (CPAN KRYDE/App-Chart-275, GPLv3), read from fastapi.metacpan.org.
    RVI.pm proc() = (high_proc(high//close) + low_proc(low//close)) / 2 over two independent
    proc_original closures — the refined form in code. proc_original is
    num = EMA(v > prev ? stddev : 0), den = EMA(stddev) — an EMA(S) denominator is ties→down —
    and rvi = (den == 0 ? 50 : 100*num/den). Its own header cites the 1995 store listing:
    "TASC 1995, high/low combination, and introducing Inertia." EMA.pm carries Wilder as
    N = 2W−1 (alpha = 1/W exactly) but is weight-normalised from zero (sum/weight), a different
    transient from an SMA-seeded Wilder — converged tail only. Stddev.pm divides by count:
    population.
  • [C] TradingView, thinkorswim, MotiveWave, eSignal, fmlabs, ProRealCode — fetched and quoted on
    card (RVI) Relative Volatility Index #366. TradingView's one structural sentence ("uses high and low price
    standard deviation") describes this form, not (RVI) Relative Volatility Index #366's.
  • Read locally this session: pandas-ta-classic 0.6.52 volatility/rvi.py (if refined: … return 0.5 * (high_rvi + low_rvi), "r"; _rvi_compute passes ONE length to both stdev and ma, so it
    cannot witness n1 != n2), utils/_core.py (ties→neither), statistics/stdev.py (ddof 0);
    trading-signals 8.3.0 dist/volatility/RVI/RelativeVolatilityIndex.js (WSMA on both legs,
    ties→neither, 50 on zero total, separate interval/stddevInterval, stddevInterval < 2
    rejected); tulip_serve/vendor/tulipindicators/beta/rvi.c.

Sample input/output (board policy)

Input: TA_SREF_{high,low}_daily_ref_0_PRIV, 252 bars — MEASURED equal element for element to
trading_signals_serve/corpus.json. optInTimePeriod = 14, optInStdDevPeriod = 10, unstable 0;
outBegIdx = 22, outNBElement = 230. Left column: the proposed body's spelling (var.c shifted-sum

  • reseed, rma.c wBeta-first, 100.0*(U/T) per leg, then 0.5*(a+b)). Right: the composed
    trading-signals arm, run. The hex column is the oracle's bits — those are what a golden test
    freezes, not the left column's.
bar TA_RVIR (proposed spelling) trading-signals 8.3.0 (2 legs, averaged) oracle hex
22 43.834741790037505 43.834741790037512 4045ead8d1a86866
23 40.773744308768570 40.773744308768570 4044630a0db2d046
24 37.279696373902450 37.279696373902446 4042a3cd173d5c43
…
250 53.521391948420400 53.521391948420100 404ac2bcf8ab6e58
251 52.847645335992624 52.847645335991670 404a6c7fa4725878

MEASURED over all 230 values: max abs 9.52e-13, max relative 1.80e-14; 42/230 bit-equal. The
residual is #366's three house FP-spelling choices; it is ~4x that card's close-form
figure (2.42e-13) because averaging combines two legs' residuals.

Oracles

Oracle Covers the refined form? Arm status Work to add the arm
inHigh == inLow reduction Yes, exactly — both legs collapse to one, and 0.5*(a+a) == a bit-exact (MEASURED: 0/230 bit mismatches) free, in-tree, no server none. RVIR on a zero-range series must be bit-identical to the shipped TA_RVI on it. Zero tolerance.
Composite differential vs two TA_RVI calls Yes, by construction free, in-tree, no server none — TA_RVI shipped in 8c0fedbcd. This is the primary gate.
pandas-ta-classic 0.6.52 Yes, natively — refined=True is 0.5*(RVI(high)+RVI(low)); mamode='rma', ties→neither; constrained to n1 == n2, so it cannot witness the shipped (14,10) default must be added one compute_RVIR + one SPECS row in pandas_serve/server.py, the compute_KC shape (2 price inputs), ~10 lines. length ∈ {3,5}, bars ≥ beg + 168.
trading-signals 8.3.0 Legs only — ships the close-form RVI; the average is the harness's must be added ~9-line ARMS entry whose ctor builds two RelativeVolatilityIndex instances and returns {update(b){ … 0.5*(h+l) }}, feed: 'hl'. No bar() widening — hl already exists in capture.mjs. The only arm that reaches the shipped (14,10) default.
1995 MetaStock formula reproduction Yes — a formula witness, not an executable arm not an arm The strongest evidence on this card; it is what settles the tie rule.
App::Chart 275 (Perl, GPLv3) Yes, natively — but ties→down not worth an arm A fifth server, and a negative control: the flip it represents is MEASURED at 4.64. Value banked by reading the source.
Tulip 0.9.2 No, structurally DO NOT ADD beta/rvi.c takes inputs[0] only — one series, so it cannot express a high/low average, on top of #366's MEASURED 36.04 finding that it is a different indicator.
ta4j 0.24.1 No impossible Verified on 0.24.1 (already in ~/.m2, so the pin bump is a pom edit, not a download): 406 indicator .java files in the sources jar; no Relative Volatility, no Relative Vigor. Same verdict as the 0.22.6 pin, on the newer jar.
PyneCore / pine_serve n/a n/a No rvi in pynecore/lib/ta.py; RVI is not a Pine ta.* builtin.

pandas convergence — MEASURED, ta.rvi(close, high=high, low=low, length=L, mamode='rma', refined=True) vs two TA_RVI calls averaged, max abs deviation from the given offset onward
(pandas-ta-classic 0.6.52 / pandas 3.0.3 / numpy 2.5.1):

length skip 0 skip 56 skip 112 skip 168
3 13.2 1.66e-9 1.41e-11 4.72e-12
5 7.12 3.35e-5 9.96e-11 6.96e-12
10 11.4 2.81e-2 6.97e-5 1.55e-7
14 9.33 7.56e-2 1.36e-3 2.70e-5

pandas' overlap/rma.py seeds from a one-sample mean over an input whose first length-1 bars are
NaN, so the error decays as ((L−1)/L)^k and at small L the arm converges onto a correctly
SMA-seeded Wilder. It witnesses the averaging step, the high/low leg assignment, the
smoother, the up/down gate and the tie rule, from another codebase and language. It does not
witness n1 != n2: only the composed trading-signals arm reaches the shipped default (14,10), and
there the average is the harness's arithmetic. That split is the honest state of the coverage.

Verification plan

  1. Composite differential, bit-exact memcmp, appended to
    src/tools/ta_regtest/ta_test_func/test_composite2.c. Reference: two TA_RVI calls, on inHigh
    and on inLow, both at RVIR's own startIdx, then 0.5*(a+b) — bit-exact because the fused
    legs execute the identical operation sequence. Grid (n1,n2) over {2,3,10,14,30} × {1,2,14,30}.
    Every piece is a shipped function; the only extra arithmetic is one exact multiply by 0.5.

  2. inHigh == inLow identity, zero tolerance. TA_RVIR(h,h,…) must memcmp-equal TA_RVI(h,…)
    over the whole range. MEASURED exact today; free, needs no server.

  3. External golden — trading-signals, composed. Freeze the arm's hex-of-IEEE-bits at
    (n2,n1) = (14,10), (30,30), (1,10), (2,2), compared with
    checkOracleValue(got, want, /*relTol*/ 1e-9, /*absTol*/ 1e-12) (ta_test_priv.h:294-319).
    relTol is set by the MEASURED worst relative — 1.80e-14 / 6.09e-15 / 1.42e-16 / 3.65e-12,
    worst at (2,2) — leaving 274x. absTol is the ta_test_priv.h abs-near-zero rule, not a number
    the data forces: at (1,10) want is exactly 0.0 on 88 of 243 bars, where relative error is
    undefined, and MEASURED both sides are bit-equal on every one of them. Size it from the (1,10)
    series max abs, 1.42e-14, so absTol 1e-12 leaves 70x.

  4. Second oracle — pandas converged tail, L ∈ {3,5}, bars ≥ beg + 168,
    checkOracleValue(relTol 1e-9, absTol 1e-9) (MEASURED worst 4.72e-12 and 6.96e-12, ~150x
    headroom). Record that the same arm is 13.2 and 7.12 away at skip 0, so the offset cannot be
    mistaken for arbitrary.

  5. Tie rule — an exact, non-decaying, SIX-bar probe. MEASURED tie census: high 128 up /
    120 down / 3 ties (bars 66, 88, 250); low 133 / 115 / 3 ties (bars 81, 176, 191); close
    123 / 127 / 1 (bar 101). At optInTimePeriod = 1 each leg has no memory, so a tie reads a literal
    and the average takes a quarter-point value:

    bar ties→neither ties→down ties→up
    66, 81, 88, 176 75.0 50.0 100.0
    191, 250 25.0 0.0 50.0

    MEASURED, the whole 243-value series at (1,10) takes only {0, 25, 50, 75, 100} — counts
    {0:88, 25:2, 50:47, 75:4, 100:102}, nothing else — and only {0, 50, 100} under either flip:
    the presence of a 25 or a 75 decides the rule in one assert. Assert the six literals and the
    value set. At (14,10) the flip is far more visible than in the close form — MEASURED max 4.64
    (ties→down, bar 88) and 2.24e-1 at the last bar, against (RVI) Relative Volatility Index #366's 1.97 and 5.33e-5,
    because a tie sits at bar 250.

  6. Streaming-tier aliasing rejection (Multi-output functions don't reject output-buffer aliasing (same buffer for two output args) #108, In-place aliasing (input==output) silently returns wrong values in 8 functions; 5 are an unreleased regression from #103 #130): the _OpenAndFill
    guard must reject non-distinct output buffers. This is the rejection case, and it is not the
    same thing as item 7.

  7. Batch in-place aliasing must be bit-identical — the opposite of item 6. Template exists and
    takes the same inPriceHL bundle: src/tools/ta_regtest/ta_test_func/test_donchian.c:430-460
    ("each output over each input in turn must match"). Alias outReal over inHigh, then over
    inLow, separately — two cases, not one. Both legs must finish reading bar i before the
    store; at n2 == 1 with unstable 0 the slack is exactly zero. ASan cannot see a violation.

  8. Hand-written edge cases — flat input: U + D == 0 in both legs, so exactly 50.0 on every
    bar, no NaN (IMI: successful call emits NaN on an all-flat window (0/0) — guard the divide, return 50.0 #112). Plus (RVI) Relative Volatility Index #366's list, inherited.

  9. Free once the YAML lands: --codegen, --xlang-hash / server_verify at zero tolerance,
    --fuzz-064, the Audit parameter-boundary OOB cases; extend ta_regtest to sweep min/default/default±1/max for every parameter #94 boundary sweep under ASan/UBSan, and the unstable sweep as
    doRangeTestEx(…, TA_STABLE_CONVERGING, TA_FUNC_UNST_RVI, …).

  10. No NaN/Inf can escape. Each leg is bounded to [0, 100] by its division-first combine; the
    average of two values in [0, 100] is in [0, 100]. The averaging adds no division and no
    overflow path.

Open questions

  1. Name — one word, and it is the only thing outstanding. Recommend TA_RVIR: it matches
    pandas' output naming (RVIr_<length>) and sorts adjacent to rvi. Alternative: TA_RVIHL.
    Swapping the pair so TA_RVI becomes this function is not available — TA_RVI shipped under the
    close form in 8c0fedbcd. There is no evidence that decides between the two candidates; it is a
    naming call, and the C symbol is part of the contract, so the card stays a proposal until it is
    made. Nothing else about the formula, the contract, the oracles or the gates waits on it.

Notes

  • Streaming is not a risk. The shipped rvi.yaml:4 carries flags: [unstable_period, stream]
    and Core::rvi_open exists at ta_codegen/output/rust/library/src/ta_func/rvi.rs:1016 —
    stream-census passes on RVI's IR. If RVI streams, RVIR streams. Each leg is a TA_RVI stream; the
    ring is n1 highs plus n1 lows, and the up/down gate needs no extra slot.
  • Not path_dependent (Declare start-dependent functions in YAML instead of the hardcoded rangeDependent[] test list #127): both legs seed at startIdx − lookbackTotal, exactly
    like RSI.
  • ABI and sorting: one new function; there is no C TA_FuncID — the ordinal ABI is the Rust
    crate's FuncId (ta_codegen/output/rust/library/src/abstract_api.rs:44), where a new variant
    renumbers every later discriminant. No new unstable id. rvir sorts between rvi and rvol
    (rvol landed as (RVOL) Relative Volume #370), so confirm ta_ref_serve still builds
    ((AC) Accelerator/Decelerator Oscillator #228 AC).
  • Follow-up, no card exists (0 hits for "inertia" on the board): the same 1995 article also
    introduced Inertia, a 20-period smoothing of RVIR. The sources disagree on the smoother — the
    1995 page's prose says "a Linear Regression indicator" and the Chart manual says a 20-day LSQMA,
    while that same page's code says Mov(Fml("New Relative Volatility Index"),20,SIMPLE) — so an
    INERTIA card must arbitrate it. TA_LINEARREG (the endpoint MA) is already shipped.
  • Gotchas: a full cargo run -- generate (a --func=RVIR filter skips Core.java and the
    servers); scripts/build.py servers, not build; register the new test .c in both
    CMakeLists.txt and src/tools/ta_regtest/Makefile.am; the CHANGELOG entry; and
    ta_regtest --function=RVIR matches the group tag, so confirm the filter ran tests instead of
    exiting 0 vacuously.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions