Positive when the bar closes above the regression line's next step
(Chande reads that as the trend running ahead of its own fit) and
negative below; it crosses zero where the price meets the forecast.
Dividing by the price rather than by the forecast is what makes it
comparable across instruments — the disparityIndex shape, with
the regression forecast in place of a moving average.
TA-Lib has no Chande Forecast Oscillator, so the oracle is a pandas
replication built on the TA-Lib-checked TSF, with the analytic
first-valid bar (period − 1) asserted and a measured separation from
the plausible wrong turn — subtracting LINEARREG (the fit at the
window's last bar) instead of the one-bar-ahead TSF.
Edges
period must be at least 2 — timeSeriesForecast's rule,
inherited: a one-bar window has no slope.
A zero price reads undefined, and the guard is live. The
division sits at the study's output, so an unguarded x / 0 would
reach withColumn as ±Infinity — which throws rather than
mapping to a gap ([PND-WCNAN] covers NaN, not infinities). The
numerator is not forced to zero with the denominator: a window can
forecast a non-zero level for a bar that prints 0, so this is the
choppinessIndex live-guard case rather than the
ulcerIndex dead one. Unreachable on prices; reachable, and
unit-tested, when column is another study's output that crosses zero.
A negative price still produces a number, the
percentChange rule (=== 0, not <= 0) — a percentage off a
negative base is defined, if unusual, and clamping it would be
inventing a rule.
A flat window reads exactly 0 — the forecast is the flat price,
so the numerator is zero and the reading is "the price is exactly on
its forecast", which is information rather than a gap.
Scale-invariant, not shift-invariant. Both halves of the numerator
scale with the price and the denominator divides it out, so
multiplying every price leaves the reading unchanged; adding a
constant does not — it moves the base of the percentage without moving
the numerator, so every reading shrinks. Both halves are pinned, the
second as a direction rather than merely "different".
The strict window (all period cells finite) and the all-missing
answer for a misnamed column are linearRegressionValues'.
Chande Forecast Oscillator (Tushar Chande) — how far the price sits from its own timeSeriesForecast, as a percentage of the price:
Positive when the bar closes above the regression line's next step (Chande reads that as the trend running ahead of its own fit) and negative below; it crosses zero where the price meets the forecast. Dividing by the price rather than by the forecast is what makes it comparable across instruments — the disparityIndex shape, with the regression forecast in place of a moving average.
TA-Lib has no Chande Forecast Oscillator, so the oracle is a pandas replication built on the TA-Lib-checked
TSF, with the analytic first-valid bar (period − 1) asserted and a measured separation from the plausible wrong turn — subtractingLINEARREG(the fit at the window's last bar) instead of the one-bar-aheadTSF.Edges
periodmust be at least 2 — timeSeriesForecast's rule, inherited: a one-bar window has no slope.undefined, and the guard is live. The division sits at the study's output, so an unguardedx / 0would reachwithColumnas±Infinity— which throws rather than mapping to a gap ([PND-WCNAN] coversNaN, not infinities). The numerator is not forced to zero with the denominator: a window can forecast a non-zero level for a bar that prints0, so this is the choppinessIndex live-guard case rather than the ulcerIndex dead one. Unreachable on prices; reachable, and unit-tested, whencolumnis another study's output that crosses zero. A negative price still produces a number, the percentChange rule (=== 0, not<= 0) — a percentage off a negative base is defined, if unusual, and clamping it would be inventing a rule.0— the forecast is the flat price, so the numerator is zero and the reading is "the price is exactly on its forecast", which is information rather than a gap.periodcells finite) and the all-missing answer for a misnamedcolumnare linearRegressionValues'.