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

    Function choppinessIndex

    • Choppiness Index (E. W. Dreiss) — how much path the market walked to get where it got, on a 0..100 scale: the total distance travelled (summed true range) against the net ground covered (the window's own high-to-low range), normalised so that the scale does not move with period.

      ${output} = 100 × log10( ΣTR over period / (HHLL over period) ) / log10(period)
      

      Appends one column. Reads high, low and close, each named by an option defaulting to its DEFAULT_OHLCV name (the atr precedent) — the close is there because the sum is of true range, which reads the previous close.

      High is choppy, low is trending — and it says nothing about direction. A market that ranged sideways covers a lot of true range inside a narrow band, so the ratio is large and the reading is near 100; a market that went one way covers the same ground once, the ratio approaches 1, and the reading approaches 0. Both a crash and a melt-up read low. Dreiss' conventional thresholds are 61.8 and 38.2 (Fibonacci), and they are conventions, not properties of the number.

      It is what makes readings comparable across look-backs. ΣTR/(HH−LL) can be as large as roughly period (each bar re-walking the whole band), so its logarithm tops out near log10(period); dividing by that maps the ratio onto a fixed 0..100 axis whatever period is. That also rules out period: 1, where log10(1) = 0 — the study throws rather than dividing by zero, since there is no reading to give (one bar's true range over one bar's range is not a measure of anything).

      TA-Lib has no Choppiness Index, so the oracle is a pandas replication with the analytic first-valid bar asserted and a measured separation from the plausible wrong turn — the same formula on plain high − low instead of true range, which is the one substitution that leaves the shape of the curve intact while changing every value.

      • True range, and it is the package's own trueRangeValues — asserted bit-for-bit equal to talib.TRANGE in the oracle through ultimateOscillator's case, so this study and the whole ATR family measure range identically by construction. (Contrast chaikinVolatility and massIndex, which take plain range because Chaikin and Dorsey defined them that way.)
      • log10, not ln — and it does not matter. The base cancels: log_b(x)/log_b(n) is log_n(x) for every b, so the reading is really "the log, base period, of the path-to-range ratio". log10 is what every published statement writes and what ships; the generator asserts the two agree (1.4e-14) rather than leaving a reader to work it out. (The same cancellation applies to gopalakrishnanRangeIndex, which is why neither study takes a base option. Mixing the bases would be a real bug, and that is what the oracle separates against.)
      • One period for both halves. Every vendor uses a single length; the sum and the range are two readings of the same window, and splitting them would make the 0..100 normalisation meaningless.

      TR[0] is undefined (no previous close), so a period-bar sum of true ranges first exists on bar period — one row later than the HH/LL range's period − 1, and the later of the two is what the column shows. Bar 14 at the default. Length-preserving.

      • Scale-invariant, and shift-invariant. Both halves of the ratio are homogeneous of degree one in price and both are built from differences, so neither multiplying nor adding a constant moves the reading. Pinned by property tests.
      • Bounded 0..100, with one honest leak. ΣTR ≥ HH − LL always (the window's own span is covered by the bars that made it), so the ratio is at least 1 and the reading is never negative. The upper bound holds because each bar's true range is contained in the window's range — except for the first bar of the window, whose true range reads a close from before the window and can therefore exceed it. A hard gap into the window can push the reading a little above 100; nothing clamps it, because clamping would hide the gap. Measured on the oracle input, whose bars are gap-free: the reading spans 20.13 … 80.29 at period 14 and 22.85 … 87.18 at period 5, and the generator asserts 0..100 on both.
      • A flat window → undefined, and this is not the flat-bar case. When HH === LL the ratio is 0/0 on consistent bars, and the reading has no defensible value: unlike a flat bar's close location — whose numerator (c−l)−(h−c) is algebraically forced to zero, so 0 is the answer rather than a convention (accumulationDistribution) — there is nothing here that a limit picks out. Worse, the two candidate conventions are opposites: 0 would read "perfectly trending" and 100 "perfectly choppy", for a market that did not move at all. So the guard is explicit and reports missing (the stochastic flat-window precedent, applied on its own merits).
      • A zero true-range sum → undefined too. On consistent bars that implies HH === LL and is the same case, but it is reachable independently when close is redirected at a column outside the bar's own range: bars with high = low = previous value of that column have zero true range while their highs still differ from each other. The result would be log10(0) = −Infinity, so the guard is load-bearing rather than a formality, and a unit test reaches it.
      • A leading gap shifts the start; an interior gap blanks the bar and the bar after it (true range reads the previous close) and then every window over those, after which the study recovers. Note the HH/LL half and the ΣTR half treat a gap differently: the extremes come from core's rolling reducers, which skip a missing cell, while the sum comes from rollingMeanValues, which does not — so the sum is the half that blanks, and the study's mask is its mask.

      Type Parameters

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

      Parameters

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