Negative Volume Index (Paul Dysart, popularised by Norman Fosback,
Stock Market Logic 1976) — a cumulative index that compounds a bar's
return only when that bar's volume was lower than the previous bar's.
The premise is Dysart's: the "smart money" trades on quiet days and the
crowd on busy ones, so the line built from quiet days alone is read as the
informed side of the tape. positiveVolumeIndex is the same
construction on the busy days.
Appends one column (nvi by default). Like obv there is no
period — there is no window to size — and no warm-up: bar 0 carries
start.
Conventions pinned
A flat volume holds.volume[i] === volume[i−1] compounds on
neither index. Fosback's rule is a strict comparison on each side, so
an unchanged volume is neither a "down-volume" nor an "up-volume" bar.
That is worth stating because the obvious implementation — < for NVI
and >= for PVI — silently makes the two indices partition every bar
and gives PVI a bar Fosback does not give it.
start is a base, not data.1000 is Fosback's; some vendors use
100, and some chart a 0-based percent form (100 · (index/start − 1)), which is a different column — the index is multiplicative, so
a 0 start would be 0 forever, and start rejects it. It scales the
whole line, so the study is invariant to it in shape.
The return is a simple price return, close[i]/close[i−1] − 1, on
whichever column is named — this is a ratio index, not a sum, so
there is no × volume term anywhere (contrast priceVolumeTrend,
which is the volume-weighted cousin).
TA-Lib has no NVI/PVI, so the oracle is a pandas replication, with cases
separating it from the two nearby misreadings: compounding on a flat
volume, and compounding the wrong side.
Edges
Scale-invariant in both inputs. Multiplying every price by k leaves
the index unchanged (the return is a ratio); multiplying every volume by
k leaves it unchanged (only the comparison is read). Both are
asserted as property tests.
A zero previous close makes the return undefined, and an index that
cannot be continued reads undefined from that bar onwards rather than
inventing a level. The guard is live because the division is at the
output.
A missing close or volume re-bases the index. Per the K6 gap
rule ([PND-SFOLD], kernels/fold.ts) the machine resets: the gap bar is
undefined and the next complete bar restarts at start. That is a
deliberate difference from obv, which propagates to the end, and
the difference is what the level means. OBV accumulates volume — a
quantity with units, where the distance between two points is the
reading — so a hole makes every later level wrong. NVI accumulates
returns from an arbitrary base, so re-basing after a hole loses only
the base, and the shape from there on is exactly right. A caller who
needs one continuous index across a hole fills before running it.
Negative Volume Index (Paul Dysart, popularised by Norman Fosback, Stock Market Logic 1976) — a cumulative index that compounds a bar's return only when that bar's volume was lower than the previous bar's.
The premise is Dysart's: the "smart money" trades on quiet days and the crowd on busy ones, so the line built from quiet days alone is read as the informed side of the tape. positiveVolumeIndex is the same construction on the busy days.
Appends one column (
nviby default). Like obv there is noperiod— there is no window to size — and no warm-up: bar 0 carriesstart.Conventions pinned
volume[i] === volume[i−1]compounds on neither index. Fosback's rule is a strict comparison on each side, so an unchanged volume is neither a "down-volume" nor an "up-volume" bar. That is worth stating because the obvious implementation —<for NVI and>=for PVI — silently makes the two indices partition every bar and gives PVI a bar Fosback does not give it.startis a base, not data.1000is Fosback's; some vendors use100, and some chart a0-based percent form (100 · (index/start − 1)), which is a different column — the index is multiplicative, so a0start would be0forever, andstartrejects it. It scales the whole line, so the study is invariant to it in shape.close[i]/close[i−1] − 1, on whichevercolumnis named — this is a ratio index, not a sum, so there is no× volumeterm anywhere (contrast priceVolumeTrend, which is the volume-weighted cousin).TA-Lib has no NVI/PVI, so the oracle is a pandas replication, with cases separating it from the two nearby misreadings: compounding on a flat volume, and compounding the wrong side.
Edges
kleaves the index unchanged (the return is a ratio); multiplying every volume bykleaves it unchanged (only the comparison is read). Both are asserted as property tests.undefinedfrom that bar onwards rather than inventing a level. The guard is live because the division is at the output.closeorvolumere-bases the index. Per the K6 gap rule ([PND-SFOLD],kernels/fold.ts) the machine resets: the gap bar isundefinedand the next complete bar restarts atstart. That is a deliberate difference from obv, which propagates to the end, and the difference is what the level means. OBV accumulates volume — a quantity with units, where the distance between two points is the reading — so a hole makes every later level wrong. NVI accumulates returns from an arbitrary base, so re-basing after a hole loses only the base, and the shape from there on is exactly right. A caller who needs one continuous index across a hole fills before running it.