openalgo-charts
Version:
From-scratch, dependency-free canvas charting engine for OpenAlgo: professional-grade interactive rendering, advanced on-chart trading, and a one-call widget tier that adds the toolbar, drawing rail, dialogs and shortcuts.
1,875 lines • 103 kB
TypeScript
import { Bar, IndicatorSnapshotRequest, RequestedBarsSnapshot, TimeframeFeed, IndicatorBarsProviderAccess, IndicatorSettings, IndicatorDescriptor, ChartDataContext, IndicatorRequestState, IndicatorBarsRequest, IndicatorInput, IndicatorPlot, IndicatorValues, IndicatorStore, IndicatorCalcContext, IndicatorLevel, DataVariant } from 'openalgo-charts';
export { RequestedBarsSnapshot } from 'openalgo-charts';
/** Availability is provider knowledge, not inferred from the next observed candle. */
interface TimeframeProviderOptions {
availability(bar: Readonly<Bar>, request: IndicatorSnapshotRequest): {
at: number | null;
confirmed: boolean;
};
/** Supply historical value versions, or replay requests are rejected. */
historicalSnapshot?: (request: IndicatorSnapshotRequest) => Promise<RequestedBarsSnapshot>;
/** UTC seconds, also used for the five-minute closed-history cache lifetime. */
now?: () => number;
}
/** Independent, bounded confirmed history, shared requests and a refreshable forming tail. */
declare function createTimeframeProvider(feed: TimeframeFeed, options: TimeframeProviderOptions): IndicatorBarsProviderAccess;
/**
* A higher timeframe on a built-in study: a 15-minute EMA or an hourly
* Supertrend drawn on a 1-minute chart, without changing the chart.
*
* `withTimeframe(descriptor)` gives a study one more input, `timeframe`, an
* interval select whose empty default is the chart's own. Empty, the study is
* exactly what it was: its own `calc` and its own `calcTail`, handed the same
* arguments. Set, the chart's bars are folded to that interval by the fold
* `securityExpression` uses, the study's `calc` runs on the folded bars, and
* each bucket's result is read back onto the chart bars of the bucket after
* it: a value appears once its bucket has completed, and never moves after.
*
* Sub-day buckets start at the session open, the way an exchange cuts its own
* hourly bars, and the open is read from the bars in the chart's zone (see
* `sessionOpen`). A timeframe no coarser than the chart's is the chart's own.
*
* Only a calculation from the chart's bars alone folds, and only a causal one:
* a value at a bar reads no later bar. Which built-ins opt in, and why the
* rest do not, is in the indicators reference.
*/
/**
* The descriptor with a `timeframe` input. The input goes after the study's
* own ungrouped inputs, ahead of any grouped section, so a form renders it with
* them. A descriptor that already has the key, or that brings its own data
* through `attach`, cannot be folded and is refused.
*/
interface TimeframeOptions {
/** Confirmed requested observations needed before the chart window for this calculation. */
historyBars?: (settings: Readonly<IndicatorSettings>) => number;
}
declare function withTimeframe(descriptor: IndicatorDescriptor, options?: TimeframeOptions): IndicatorDescriptor;
declare const SMA: IndicatorDescriptor;
declare const WMA: IndicatorDescriptor;
declare const EMA: IndicatorDescriptor;
declare const BOLLINGER: IndicatorDescriptor;
declare const VWAP: IndicatorDescriptor;
declare const SUPERTREND: IndicatorDescriptor;
declare const PARABOLIC_SAR: IndicatorDescriptor;
declare const ICHIMOKU: IndicatorDescriptor;
/**
* HalfTrend: a trend-following level that only moves against the trend once
* the opposing side of the range genuinely gives way, so it holds flat through
* noise where a moving average would wobble.
*
* Two state machines run at once. `trend` is what is drawn; `nextTrend` is which
* flip is currently *armed*. While a down-flip is armed the indicator tracks the
* running maximum of the `amplitude`-bar low; the flip only fires when the mean
* high drops under that maximum **and** the bar closes below the previous bar's
* low. The up-flip is the mirror. Requiring both a mean crossing and a close
* beyond the prior bar's extreme is what keeps the level still.
*
* On a flip the new level starts from the level the other side ended on, which
* is why the line steps rather than jumping to price. `channelDeviation` half-ATR
* bands ride the level as a channel, and the flip bar is marked half an ATR
* inside the channel.
*
* Original implementation written from the algorithm's published behaviour, per
* ARCHITECTURE.md §0.1, not ported from any third-party source.
*/
declare const HALFTREND: IndicatorDescriptor;
declare const RSI: IndicatorDescriptor;
declare const MACD: IndicatorDescriptor;
declare const STOCHASTIC: IndicatorDescriptor;
declare const ADX: IndicatorDescriptor;
declare const CCI: IndicatorDescriptor;
declare const MFI: IndicatorDescriptor;
declare const ATR: IndicatorDescriptor;
/**
* CM Williams Vix Fix: a synthetic VIX from price alone.
*
* `wvf` is how far the current low sits below the highest close of the lookback,
* as a percentage: a spike means capitulation. The signal is not the level but
* the *breakout* (`wvf` piercing its own Bollinger upper band, or the top
* percentile of its recent range), so the histogram carries two colours and the
* bands are what it is measured against.
*/
declare const WILLIAMS_VIX_FIX: IndicatorDescriptor;
/**
* Tier-1 volume indicators, computed from the chart's own OHLCV.
* Part of the lazy `openalgo-charts/indicators` tier.
*/
declare const VOLUME: IndicatorDescriptor;
declare const OBV: IndicatorDescriptor;
declare const ADL: IndicatorDescriptor;
/** Per-bar position levels from the instrument itself, with no external alignment. */
/** A missing observation breaks the line; it is not an empty position. */
declare const OPEN_INTEREST: IndicatorDescriptor;
/** Adjacent observations only: a gap cannot support a bar-on-bar change. */
declare const OPEN_INTEREST_CHANGE: IndicatorDescriptor;
/**
* Price and position changes describe a regime, not a second price series.
* Publish candle colors only, so the study cannot distort the instrument axis.
*/
declare const OPEN_INTEREST_BUILDUP: IndicatorDescriptor;
/**
* The source is hard-coded to `close` in the reference (`source = close`,
* not an `input`), so there is no source setting to expose.
*/
declare const ALMA: IndicatorDescriptor;
/**
* `2 * ema - ema(ema)`. The second pass runs over a series that is already NaN
* for its own warmup, which is what pushes the first plotted bar out to
* `2 * length - 2`, see `emaOfGapped`.
*/
declare const DEMA: IndicatorDescriptor;
declare const HMA: IndicatorDescriptor;
declare const ENVELOPE: IndicatorDescriptor;
declare const DONCHIAN: IndicatorDescriptor;
/**
* Two stacked extremes: an ATR-padded high/low band, then the running extreme of
* *that* over `q` bars, which is what keeps each stop monotone through a pullback.
* The second pass is NaN-strict (`extremeStrict`), so nothing prints until the
* whole `q`-bar window is past the ATR warmup, bar `p + q - 2`.
*
* The long stop is padded off the lowest **low**, which is the published
* definition and what the short stop mirrors. The reference implementation takes
* both extremes over the high series (it never reads a low at all), so the two
* long stops disagree by roughly the average high-low range while the short
* stops agree to the last bit. Measured, deliberate, and left alone: matching it
* would move a stop line for every existing user onto a reading that the
* published definition of this stop contradicts.
*/
declare const CHANDE_KROLL_STOP: IndicatorDescriptor;
/**
* The reference imports an external helper library and calls `an external `chandelier()` helper`, whose
* body is not in the reference file. This is the published definition of that stop:
* the highest high of the window pulled down by `mult` ATRs, and its mirror.
*/
declare const CHANDELIER_EXIT: IndicatorDescriptor;
/**
* Bands centred on the regression **endpoint**, not on a moving average of price:
* the fitted line's value at the newest bar of the window, pushed out by
* `errors` standard errors, and only then smoothed. The middle plot is that
* smoothed endpoint, so it is not the same line as a plain regression curve.
* All three legs are smoothed independently, which is why the first bar of each
* lands at `(periods - 1) + (averagePeriods - 1)`.
*/
declare const STANDARD_ERROR_BANDS: IndicatorDescriptor;
/**
* Two independent legs, a mean of highs and a mean of lows, each with its own
* length and its own plot-time displacement. Not a mean of the close with a
* spread around it, and simple throughout: there is no smoothing method here.
*/
declare const MA_CHANNEL: IndicatorDescriptor;
/**
* Hull Suite: one hull average plotted twice, the second copy displaced two
* bars. The band is therefore the average against its own recent past rather
* than a second study, and "the first plot is above the second" is exactly the
* rising condition, which is what lets a plain two-colour fill carry the trend.
*
* Three variations are offered because they trade lag against smoothness
* differently, and two quirks of the published definition are preserved rather
* than tidied: the Thma branch is handed half the length the other two get, and
* the intermediate lengths truncate while only the outer square root rounds.
* Anyone reading this beside the study they already run needs the same line.
*
* Deliberately not offered, because nothing here could back them: a line
* thickness input (`style.lineWidth` is static and there is no width settings
* key), a band transparency input (`opacity` is a fixed number on the fill spec,
* not a settings key), and a higher-timeframe mode (`calc` is handed the chart's
* own bars and has no way to request another resolution).
*/
declare const HULL_SUITE: IndicatorDescriptor;
declare const OVERLAY_INDICATORS: readonly IndicatorDescriptor[];
/**
* Aroon: how recently the window made its extreme, as a percentage of the
* window.
*
* `highestbars` answers "how many bars back is the high" as a **negative
* offset**, `0` meaning the current bar. The whole study is that offset mapped
* onto 0..100, so the sign convention is load-bearing: a fresh high gives
* `100 * (0 + length) / length` = exactly 100, and a high at the far edge of
* the window gives 0. Note the window is `length + 1` bars, not `length`:
* the reference counts the gaps between bars, not the bars.
*/
declare const AROON: IndicatorDescriptor;
/**
* Aroon Oscillator: Aroon Up minus Aroon Down, so a single line through zero.
*
* The reference colours the *line* by sign and shades it to a hidden zero plot. This
* library's line renderer has no per-bar colour (only histogram and column
* honour `colorBy`), so the sign is carried by the band instead, which is what
* actually reads on a chart. `zero` is a value column with no plot, exactly as
* the reference `display.none` zero plot is: it exists only to give the fill a
* second edge.
*/
declare const AROON_OSCILLATOR: IndicatorDescriptor;
/**
* Awesome Oscillator: the gap between a fast and a slow midpoint average.
*
* The reference hard-codes 5 and 34 and exposes no inputs, so neither does this: the
* two periods are the definition of the study, not a preference. Colour is the
* second half of the signal (green while the histogram is building against the
* previous bar, red while it is fading), and that is a per-bar decision, hence
* `colorBy` on one column rather than two plots that would each carry holes.
* On the first printed bar the previous value is `na`, and the reference `na <= 0` is
* false, so it starts green.
*/
declare const AWESOME_OSCILLATOR: IndicatorDescriptor;
/**
* Balance of Power: where the close finished inside the bar's range, relative
* to where it opened. No smoothing and no inputs; the reference is one line.
*
* A zero-range bar makes this 0/0, which is `na` in the reference, so it draws a gap
* rather than a spike.
*/
declare const BALANCE_OF_POWER: IndicatorDescriptor;
/**
* Chande Momentum Oscillator: the same up/down split as RSI, but as a plain
* ratio of summed moves instead of a Wilder average, so it swings the full
* -100..100 rather than compressing towards the middle.
*
* `change` is `na` on the first bar, so the first published value is the one
* backed by `length` real changes: index `length`, not `length - 1`. The sums
* are therefore taken over the series from bar 1 onwards, which also keeps the
* running total in `rollingSum` from ever touching a NaN: one NaN would poison
* it for the rest of the series, because subtracting it back out when it leaves
* the window does not undo it.
*/
declare const CHANDE_MOMENTUM: IndicatorDescriptor;
/**
* Coppock Curve: a weighted average of two rates of change, one long and one
* short. Both `roc` terms must have a value before the WMA has anything to
* chew on, so the first print lands at `longRoCLength + wmaLength - 1`.
*/
declare const COPPOCK_CURVE: IndicatorDescriptor;
/**
* Detrended Price Oscillator: price against a moving average taken from half a
* cycle ago, which strips the trend and leaves the cycle.
*
* `barsback = period/2 + 1` indexes a series, so it has to be a whole number:
* the reference integer division truncates, and this floors, which agrees for both odd
* and even lengths (21 -> 11, 20 -> 11).
*
* The `centered` mode is the awkward one. the reference computes `close[barsback] - ma`
* and then draws it with `offset = -barsback`, i.e. shifted back in time.
* Plots here have no per-plot offset, so the shift is folded into the column
* itself: the value drawn at bar `i` is the one the reference computed on bar
* `i + barsback`, which reduces to `close[i] - ma[i + barsback]`. The picture
* matches the reference platform exactly, including the fact that the centered line stops
* `barsback` bars short of the right edge. The non-centered mode needs no such
* trick and is implemented straight.
*/
declare const DPO: IndicatorDescriptor;
/**
* Fisher Transform: squash the position of `hl2` inside its recent range into
* -1..1, then run it through the inverse hyperbolic tangent so the tails
* stretch out and turning points become sharp.
*
* Two recursions, each carrying two thirds / one half of the previous bar. The
* clamp to +/-0.999 is not cosmetic: at exactly +/-1 the log blows up, so the
* the reference `round_` is what keeps the series finite. Both recursions read the
* previous bar through `nz`, so a missing previous value counts as 0, which
* happens on the first printed bar and again after any bar that produced `na`.
* A flat window (`high_ == low_`) is not one of those: the range divide is
* floored at 0.001, so the ratio is 0, the bar still prints, and both
* recursions carry on.
*
* `Trigger` is simply `fish1[1]`, so it lags Fisher by exactly one bar.
*/
declare const FISHER_TRANSFORM: IndicatorDescriptor;
/**
* The Connors "updown" streak: how many consecutive bars price has risen or
* fallen, signed. An unchanged close resets it to 0; a rise continues a
* positive run or starts a new one at +1; a fall mirrors that.
*
* Exported because it is the one piece of Connors RSI worth testing on its own
* (the rest is three well-known series averaged).
*
* Bar 0 is deliberately -1, not 0. In the reference `close == close[1]` and
* `close > close[1]` are both false against `na`, so the first bar falls into
* the down branch with `nz(ud[1])` reading 0, which yields -1.
*/
declare function connorsStreak(values: readonly number[]): number[];
/**
* Connors RSI is the mean of three unrelated readings of the same bar: how
* overbought price is (a short RSI), how stretched the up/down streak is (an
* RSI of the streak itself), and where today's one-bar return sits in its own
* recent distribution (a percent rank).
*
* Averaging only works if all three have a value, and the percent rank is the
* slow one: `roc(close, 1)` is `na` on bar 0, and `percentrank` compares
* the current value against the previous `lenroc` of them, so the first
* complete reading is at index `lenroc + 1`: 101 on defaults. Running the rank
* over the series from bar 1 is what keeps that `na` out of the window.
*/
declare const CONNORS_RSI: IndicatorDescriptor;
declare const OSCILLATOR_INDICATORS: readonly IndicatorDescriptor[];
/**
* Bollinger Bands %b: where the source sits inside its own bands, rescaled so
* the lower band is 0 and the upper is 1. Unbounded on purpose: the reading
* only becomes interesting once it leaves 0..1, which is why the pane declares
* no fixed range.
*/
declare const BOLLINGER_PERCENT_B: IndicatorDescriptor;
/**
* Bollinger BandWidth: the band spread as a percentage of the basis, so it is
* comparable across instruments and across price levels.
*
* The two companion plots are rolling extremes **of the bandwidth itself**, not
* of price: they turn "is this narrow?" from a judgement call into a comparison
* against the last N bars of the same series.
*/
declare const BOLLINGER_BANDWIDTH: IndicatorDescriptor;
/**
* BBTrend: a short and a long Bollinger set compared band for band. When the
* short set's lower band has pulled further from the long set's lower band than
* the two upper bands have separated, the short-term range is expanding
* downward and the reading is negative; the reverse is positive. Normalised by
* the short basis so it reads as a percentage.
*/
declare const BB_TREND: IndicatorDescriptor;
/**
* Choppiness Index: how much ground the bar-by-bar travel covers compared with
* the net range it produced. A market that retraces everything spends the full
* `length` bars of true range inside one range and reads near 100; a trend
* covers the same range in a fraction of the travel and reads low.
*
* the reference writes the numerator as `sum(atr(1), length)`. `atr(1)` is
* `rma(tr, 1)`, which is the true range itself, so this is a plain rolling sum
* of true range, including bar 0, where the reference true range is `high - low`.
*/
declare const CHOPPINESS_INDEX: IndicatorDescriptor;
/**
* Historical Volatility: the annualised standard deviation of log returns.
*
* the reference derives the annualisation divisor from the chart's timeframe:
* `per = timeframe.isintraday or (timeframe.isdaily and multiplier == 1) ? 1 : 7`.
* A descriptor here is handed bars and settings and nothing else. It cannot
* see the timeframe, and guessing one from bar spacing would silently change
* the plot on a gappy or irregular series. So `per` is an input: leave it at 1
* for intraday and daily charts, set it to 7 for weekly and above, which is
* exactly the branch the reference takes.
*/
declare const HISTORICAL_VOLATILITY: IndicatorDescriptor;
/**
* Average Daily Range: the mean high-to-low range over `length` bars. "Daily"
* is the reference platform's name for it; the calculation is per bar, whatever the chart
* timeframe is.
*/
declare const AVERAGE_DAILY_RANGE: IndicatorDescriptor;
/**
* Chop Zone: the slope of a 34-bar EMA, read as an angle and bucketed into a
* nine-colour ladder from turquoise (steep rise) through yellow (flat) to dark
* red (steep fall).
*
* The plot itself is a constant 1: every bar is the same height and all the
* information is in the colour. That is not a stylistic choice we can improve
* on: it is the study. The slope is made comparable across instruments by
* `span`, which rescales it against the 30-bar range, so the angle means the
* same thing on a 20-rupee stock and a 20000-point index.
*
* `angle` is returned as an unplotted column so `colorBy` can read it; the
* ladder is a property of the bar, not of the value being plotted.
*/
declare const CHOP_ZONE: IndicatorDescriptor;
/**
* Chaikin Volatility: the rate of change of a smoothed high-to-low range, so it
* answers "is the bar getting wider?" rather than "how wide is it?".
*
* The smoother is an EMA, not an SMA: an average of ranges reacts to a single
* wide bar for `periods` bars and then drops it in one step, which prints a
* spurious second move on the rate of change. Zero is the neutral reading, and
* a zero denominator (a period of perfectly flat bars) has no rate of change to
* report, so it stays a gap rather than becoming Infinity.
*/
declare const CHAIKIN_VOLATILITY: IndicatorDescriptor;
/**
* Standard Deviation: the population standard deviation of the close over
* `periods` bars, scaled by `deviations` so it can be read as the same band
* width a Bollinger set would draw.
*/
declare const STANDARD_DEVIATION: IndicatorDescriptor;
/**
* Standard Error: the residual spread of the closes about the least-squares
* line fitted through them, which is what makes it an *error* rather than a
* deviation. Two degrees of freedom go into the fitted slope and intercept, so
* the divisor is `length - 2` and the input cannot go below 3.
*/
declare const STANDARD_ERROR: IndicatorDescriptor;
/**
* Volatility Squeeze: whether the Bollinger Bands have contracted inside the
* Keltner Channel, the quiet stretch that tends to come before a move, and a
* momentum histogram that says which way the move is leaning.
*
* basis = sma(close, length)
* bands = basis +/- bbMult * stdev(close, length)
* channel = basis +/- kcMult * sma(true range, length)
* on = lower band > lower channel and upper band < upper channel
* momentum = linreg(close - ((highest(high, length) + lowest(low, length)) / 2 + basis) / 2, length)
*
* Both envelopes share one basis and one length, so the squeeze is either on
* or off; `state` carries it (1 on, 0 off) for the dots on the zero line. The
* state needs one window and prints from `length - 1`; the momentum fits a line
* to `length` values that each read a window, so it prints from
* `2 * length - 2`. The histogram is coloured by sign and by direction against
* the previous bar, a missing previous bar reading as zero.
*/
declare const VOLATILITY_SQUEEZE: IndicatorDescriptor;
declare const VOLATILITY_INDICATORS: readonly IndicatorDescriptor[];
/**
* Built-in volume studies, ported to descriptor form.
* Part of the lazy `openalgo-charts/indicators` tier.
*
* Each `calc` reproduces the published reference definition numerically, warmup gaps
* included, so a plot here lines up bar for bar with the same study on
* the reference platform. Where the published reference definitions differ from this library's own
* helpers the reference-compatible variant in `./calc` is used: `smaSeededEma`, never the
* base bundle's `ema`, because the two seed from different windows and would
* disagree for the first `length` bars of every plot that touches them.
*/
/**
* Chaikin Money Flow: the money-flow term summed over the window and
* normalised by the volume traded in that same window, so the reading is a
* bounded -1..+1 share of participation rather than a raw quantity.
*/
declare const CHAIKIN_MONEY_FLOW: IndicatorDescriptor;
/**
* Chaikin Oscillator: a MACD of the A/D line.
*
* The two EMAs run over the *running total* of the money-flow term (the reference
* `accdist`), not the per-bar term, so what the oscillator measures is
* acceleration in accumulation rather than the flow itself.
*/
declare const CHAIKIN_OSCILLATOR: IndicatorDescriptor;
/**
* Ease of Movement: how far the midpoint travelled per unit of volume, scaled
* by the bar's range and by a divisor that only exists to bring the number into
* a readable magnitude.
*/
declare const EASE_OF_MOVEMENT: IndicatorDescriptor;
/**
* Elder Force Index: the bar's price change weighted by the volume behind it,
* smoothed. Direction and conviction in one number: a large move on thin
* volume scores less than a small move the whole market took part in.
*/
declare const ELDER_FORCE_INDEX: IndicatorDescriptor;
/**
* Net Volume: the bar's own volume, signed by the direction its close took.
*
* There is no warmup gap. Bar 0 has no previous close, so neither the up test
* nor the down test can hold and the reference falls through to its zero arm;
* an unchanged close lands on that same arm later in the series. Blanking bar 0
* instead would put a hole in a series that is defined on every other bar.
*/
declare const NET_VOLUME: IndicatorDescriptor;
/** Every the reference platform volume built-in in this module, in picker order. */
declare const FLOW_INDICATORS: readonly IndicatorDescriptor[];
/**
* Kaufman's Adaptive Moving Average: an EMA whose smoothing constant is chosen
* bar by bar from how *directed* the recent move was.
*
* The efficiency ratio divides the net distance travelled over `erLength` bars by
* the total path walked to get there. A clean trend covers ground in a straight
* line and scores near 1, which pulls the smoothing toward the fast alpha and the
* average onto price; chop retraces itself, scores near 0, and the average all but
* stops. Squaring the interpolated alpha is what makes that transition abrupt
* rather than linear, so KAMA sits flat through noise instead of drifting.
*
* The reference delegates to `an external `kama()` helper`, whose body is not in the file, so
* this follows Kaufman's published definition. It first prints at `erLength`, the
* earliest bar where both legs of the ratio exist, seeded there on the source
* itself, because there is no prior average to carry forward.
*/
declare const KAMA: IndicatorDescriptor;
/**
* Keltner Channels: a moving average with volatility rails, where the rail width
* is a range measure rather than a standard deviation. That is the whole point of
* the study: Bollinger's bands widen on *dispersion of closes*, Keltner's on how
* much ground each bar actually covers, so the two disagree exactly when a market
* gaps or trends in one direction without spreading its closes out.
*
* Three rail sources are offered because they answer different questions. ATR (the
* default) smooths the range over its own `atrlength`, so the rails breathe slowly.
* True Range uses the raw bar, so a single wide bar throws the rails out on that
* bar alone. Range is Wilder-smoothed high-minus-low over the channel `length`,
* ignoring gaps entirely. Each has its own warmup, and the plotted band starts at
* whichever of the rail and the basis is slower.
*/
declare const KELTNER_CHANNEL: IndicatorDescriptor;
/**
* Least Squares Moving Average: the endpoint of a least-squares line fitted over
* the last `length` bars, so unlike an SMA it has no lag against a straight trend:
* fit a line to a line and you get the line back.
*
* `offset` steps back down that same fitted line rather than re-fitting, which is
* why it can shift the plot without changing its shape. See the x-axis convention
* on `linreg` in `./calc`: x is 0 at the oldest bar of the window.
*/
declare const LSMA: IndicatorDescriptor;
/**
* Klinger Oscillator: volume signed by the direction of the typical price, then
* read as a MACD-style spread of two EMAs. Signing is what separates it from a
* plain volume study: a heavy bar only counts as accumulation if `hlc3` actually
* rose, so churn at an unchanged price nets out instead of registering as force.
*
* Two chained warmups stack here. The slow leg cannot print before bar 54, so the
* spread cannot either; the signal EMA then runs over a series that is `na` up to
* that point and the reference re-seeds it from an SMA, pushing its first bar a further
* `KLINGER_SIGNAL - 1` out. See `emaOfGapped`.
*/
declare const KLINGER_OSCILLATOR: IndicatorDescriptor;
/**
* Know Sure Thing: four rate-of-change readings taken over lengthening horizons,
* each smoothed, then summed with weights 1/2/3/4 so the slowest cycle dominates.
* The point is that a single ROC is a statement about one horizon; KST asks
* whether short, medium, and long momentum agree, and weights the answer toward
* the horizon least likely to be noise.
*
* Every term carries its own warmup and the sum is only real once the slowest one
* is. With the defaults that is bar 44, `roclen4 + smalen4 - 1`. The signal SMA
* then runs over a series with that gap in front of it, so it starts `siglen - 1`
* bars later again; `sma` in `./calc` refuses any window holding a warmup slot,
* which is what makes both boundaries fall where the reference puts them.
*/
declare const KNOW_SURE_THING: IndicatorDescriptor;
/**
* Linear Regression Slope: the gradient of the least-squares line fitted over the
* last `periods` closes, in price per bar.
*
* It asks a different question from LSMA, which plots where that same line ends.
* Dropping the level and keeping only the gradient is what lets the study say
* that an advance is still an advance but no longer as steep, at a bar where
* price itself is making a new high. Zero is the whole reading: above it the fit
* rises, below it the fit falls.
*
* The x axis is unit-spaced and the reference scales the result by nothing, so
* the answer is per bar rather than per window. Only that spacing matters: a
* least-squares slope is unchanged by shifting x, so running x from 1 to
* `periods` (the reference's own convention, newest bar highest) and running it
* from 0 give the identical number.
*/
declare const LINREG_SLOPE: IndicatorDescriptor;
declare const ADAPTIVE_INDICATORS: readonly IndicatorDescriptor[];
/**
* `close` is hard-coded in the reference (`sma(close, ...)`, not an
* `input`), so there is no source setting to expose.
*
* The long length defaults to 26, not 21: the reference pairs 9 against 26. A
* saved chart that never set the length explicitly draws a slower line now.
*/
declare const MA_CROSS: IndicatorDescriptor;
/**
* McGinley Dynamic: an average whose smoothing constant is itself a function of
* how far price has run from the line, so it tightens in a trend and loosens in
* a range instead of lagging by a fixed number of bars.
*
* Recursive, and the reference seeds it from `ema(source, length)` for as long as its
* own previous value is `na`: the first printed bar is therefore `length - 1`,
* where the EMA first prints, and the recursion takes over from the bar after.
* `close` is hard-coded in the reference (`source = close`), so there is no
* source setting.
*/
declare const MCGINLEY_DYNAMIC: IndicatorDescriptor;
/**
* Median: the nearest-rank 50th percentile of the source, banded by ATR and
* shaded against its own EMA. The percentile is a real member of the window
* rather than an interpolation (see `percentileNearestRank`), so on an
* even-length window it is the upper of the two middles, not their mean.
*
* The EMA is chained onto the percentile series, so it inherits that series'
* warmup and first prints at `2 * length - 2`: see `emaOfGapped`.
*/
declare const MEDIAN: IndicatorDescriptor;
/**
* Moving Average Ribbon: four independent averages on one overlay, so the
* spacing between them reads as trend strength and their order as trend
* direction. Every lane picks its own kernel, source, and length.
*
* the reference hides a lane by setting `display.none` on the plot; here a hidden lane
* returns an all-null column, which draws nothing and keeps autoscale clean.
*/
declare const MA_RIBBON: IndicatorDescriptor;
/**
* Triple EMA: `3 * ema1 - 3 * ema2 + ema3`, which cancels the lag of a linear
* trend exactly rather than merely reducing it.
*
* Three chained EMAs, each running over a series that is already `na` for its
* own warmup, so the first printed bar is `3 * length - 3` and not `length - 1`
* (see `emaOfGapped`). `close` is hard-coded in the reference, so there is no
* source setting.
*
* The three terms are added left to right, as the definition writes them, not
* regrouped as `3 * (ema1 - ema2) + ema3`: the two round differently in the last
* digits, and the grouped form can hide an overflowing term. Any absent term, or
* a sum that is not finite, leaves the bar absent (`nulls`).
*/
declare const TEMA: IndicatorDescriptor;
/**
* Time Weighted Average Price: the running mean of the source since the anchor,
* the volume-blind sibling of VWAP. Where VWAP asks what the average traded
* price was, TWAP asks what the average quoted price was, so a thin bar counts
* for exactly as much as a heavy one.
*
* Anchor substitution: the reference takes an `input.timeframe` (default `1D`) and resets
* on `timeframe.change(anchor)`, which needs a resolution resolver a chart
* library does not have. The `session` option resets on the exchange's own
* trading day, read back from the bar gaps exactly as the VWAP descriptor
* anchors, and `continuous` never resets. The option used to be labelled
* "Session (IST day)"; it names no zone now because it hardcodes none.
*/
declare const TWAP: IndicatorDescriptor;
/**
* Volume Weighted Moving Average: an SMA whose window is weighted by volume,
* so the bars that actually traded set the level. Identical to an SMA when
* volume is flat, and `na` on any window whose volume sums to zero, which is
* what a feed with no volume produces.
*/
declare const VWMA: IndicatorDescriptor;
/**
* Williams Alligator: three Wilder-smoothed medians of differing speed, each
* displaced forward in time. The lines braid when the market has nothing to say
* and fan out in order once a trend takes hold.
*
* the reference `smma` is `na(smma[1]) ? sma(src, length) : (smma[1] * (length - 1)
* + src) / length`, which is Wilder's RMA to the letter, so `rma` reproduces it
* exactly and first prints at `length - 1`.
*
* Lengths default to 21 / 13 / 8, not to Williams' original 13 / 8 / 5: the
* reference runs the slower set, and the offsets 8 / 5 / 3 are shared by both,
* so only the smoothing lengths move. A saved chart that never set them draws
* three slower lines now.
*
* The three plots carry `offset = 8 / 5 / 3`. A plot offset is not available
* per-plot here, so the displacement is applied to the values instead: the value
* computed on bar `i` is returned in slot `i + offset`, which leaves `offset`
* leading nulls and puts the shifted tail in the last `offset` slots. A line
* therefore first prints at `length - 1 + offset`.
*/
declare const ALLIGATOR: IndicatorDescriptor;
/**
* Smoothed Moving Average, Wilder's smoother over a plain price source. Its
* alpha is `1 / length` where an EMA of the same length uses `2 / (length + 1)`,
* so it lags further and turns only once a run of closes has genuinely shifted
* the level, which is the point: it is the noise filter, not the fast line.
*
* The reference `smma` is the recursion `rma` already implements, seeded from
* the simple average of the first `length` values, so it first prints at
* `length - 1` and needs no code of its own here.
*/
declare const SMMA: IndicatorDescriptor;
/**
* T3 Average, that generalised double average applied three times over. The
* result reads as smooth as a long moving average while turning close to as
* early as a short one, which no single exponential average of either length
* does.
*
* Warmup is the thing to get right here, and it is deeper than the length
* suggests. One layer is two chained averages, and the layers nest three deep,
* so the longest term is six averages of averages. Each is chained onto a series
* that is already `na` for its own warmup and so starts `length - 1` bars after
* the one it reads (see `emaOfGapped`), which puts the first printed bar at
* `6 * (length - 1)`: index 24 at the default length of 5, not index 4.
*
* Highlighting is a colour on one line, not a second plot: the line is
* continuous either way and only its paint changes, so a break in the colour
* must not become a break in the series.
*/
declare const T3: IndicatorDescriptor;
declare const AVERAGE_INDICATORS: readonly IndicatorDescriptor[];
/**
* Momentum: the rawest reading there is, today's price against the price `len`
* bars ago. No smoothing, no normalisation, so the scale is the instrument's
* own and only the sign and the slope carry meaning.
*/
declare const MOMENTUM: IndicatorDescriptor;
/**
* Rate of Change: the same comparison as Momentum, expressed as a percentage of
* the older price so readings are comparable across instruments and across time.
*
* A zero reference price makes the ratio undefined, which is drawn upstream as a
* gap rather than as an infinite spike; the `roc` helper already returns NaN.
*/
declare const ROC: IndicatorDescriptor;
/**
* Percentage Price Oscillator: MACD rewritten as a percentage of the slow
* average, so the histogram of a 20 dollar stock and a 2000 dollar index can be
* read on the same axis.
*
* Both the oscillator and its signal take a selectable EMA/SMA shape, and the
* signal is a MA of the PPO rather than of price, so it inherits the PPO's own
* warmup on top of its own: on defaults the oscillator prints from bar 25 and
* the signal and histogram from bar 33.
*/
declare const PPO: IndicatorDescriptor;
/**
* TRIX: the one-bar rate of change of a triple-smoothed log price.
*
* Working in logs is what makes the output a pure percentage rate (scaled by
* 10000, so a reading of 100 is one percent per bar) independent of price
* level, and three EMAs in series strip nearly everything that is not the
* underlying trend, which is why it crosses zero so cleanly and so late.
*
* That lateness is the cost: each EMA starts `length - 1` bars after its input,
* and `change` adds one more, so the first print is at `3 * length - 2`
* (bar 52 on the default 18).
*/
declare const TRIX: IndicatorDescriptor;
/**
* True Strength Index: the double-smoothed one-bar change divided by the
* double-smoothed size of that change.
*
* Dividing by the smoothed magnitude is what bounds the study to -100..100 and
* makes it read as "what fraction of recent movement went one way": a run of
* unbroken up bars gives exactly +100 because the numerator and denominator are
* then the same series.
*/
declare const TSI: IndicatorDescriptor;
/**
* SMI Ergodic Indicator: Blau's Ergodic, which is TSI plotted against its own
* EMA signal. Same maths as True Strength Index, different default lengths (20
* and 5 rather than 25 and 13), so it turns much faster.
*
* The source script draws no horizontal lines here, and none are declared:
* adding a zero line would be a nicer chart but a different one.
*/
declare const SMI_ERGODIC_INDICATOR: IndicatorDescriptor;
/**
* SMI Ergodic Oscillator: the gap between the Ergodic and its signal, drawn as
* a histogram. The same relationship the Indicator shows as two lines, reduced
* to the one number that crosses zero.
*/
declare const SMI_ERGODIC_OSCILLATOR: IndicatorDescriptor;
/**
* Stochastic Momentum Index: Stochastic measured from the *midpoint* of the
* range instead of from its low.
*
* That single change is what re-centres the study on zero and lets it say which
* half of the range price is in, rather than only how high in it. Both the
* distance from the midpoint and the range itself are double-smoothed before
* being divided, which is why the reading is far steadier than a raw %K.
*
* A bare `highest(lengthK)` / `lowest(lengthK)` upstream defaults to `high` and
* `low`, not to the source of the surrounding expression, so the window really
* is a high/low range and not a close-only one.
*/
declare const SMI: IndicatorDescriptor;
declare const STRENGTH_INDICATORS: readonly IndicatorDescriptor[];
/**
* Negative Volume Index: the price path compounded across only the bars where
* volume fell.
*/
declare const NVI: IndicatorDescriptor;
/**
* Positive Volume Index: the same construction as NVI over the complementary
* set of bars, the ones where volume rose.
*/
declare const PVI: IndicatorDescriptor;
/**
* Price Volume Trend: a running total of each bar's percentage price change
* weighted by the volume behind it.
*
* The distinction from On-Balance Volume is the weighting: OBV adds the whole
* bar's volume on any up close, so a 0.1 percent drift and a 5 percent gap
* count the same. PVT scales the contribution by how far price actually moved.
*/
declare const PVT: IndicatorDescriptor;
/**
* Percentage Volume Oscillator: MACD's construction applied to volume instead
* of price, expressed as a percentage of the slow average.
*
* The percentage normalisation is the point: raw volume differences are not
* comparable across symbols or across a decade of one symbol, whereas "the fast
* average is 12 percent above the slow one" is.
*/
declare const PVO: IndicatorDescriptor;
/**
* Mass Index: how much the range is expanding relative to its own recent
* expansion, summed over a window.
*
* The ratio of a 9-bar EMA of the range to a 9-bar EMA of *that* is near 1 while
* volatility is steady and rises as the range widens, so the sum reads as
* "volatility has been building for a while" rather than "this bar was wide".
* The nested EMA is the parity trap in this file: its input is already `na` for
* the first 8 bars, and the second average seeds past that rather than
* propagating it, which is why the plot starts two warmups plus a window deep.
*/
declare const MASS_INDEX: IndicatorDescriptor;
/**
* Ulcer Index: the root-mean-square percentage drawdown from the window's
* running high.
*
* Standard deviation treats an upside surprise as risk; this only counts the
* distance below the recent peak, and squaring before averaging makes one deep
* drawdown weigh more than several shallow ones. The reading is therefore a
* measure of how uncomfortable holding the instrument was, not of how much it
* moved, and it is 0 on any series that only rises.
*/
declare const ULCER_INDEX: IndicatorDescriptor;
/** Every descriptor in this module, in picker order. */
declare const INDEX_INDICATORS: readonly IndicatorDescriptor[];
/**
* Stochastic RSI: where RSI sits inside its own recent range, which turns a
* slow-moving oscillator into a fast one.
*
* The reference passes `rsi1` in as all three arguments of `stoch`, so the window
* is the RSI's high and low rather than price's, and every warmup adds to the
* one before it: 14 bars for the RSI, 14 more before the stochastic window is
* full of real RSI values, then two SMAs. First `K` lands at index 29 on the
* defaults and `D` two bars later.
*/
declare const STOCHASTIC_RSI: IndicatorDescriptor;
/**
* Williams Percent Range: the distance from the window's high down to the
* close, as a percentage of the window. The sign convention is the whole point:
* a close at a fresh window high is exactly 0 and one at the window low is
* exactly -100, so the scale runs -100..0 rather than 0..100.
*
* The high and low come from `high` and `low` (the reference single-argument
* `highest`/`lowest`), while the numerator reads the `source` input, so
* the three do not have to agree.
*/
declare const WILLIAMS_PERCENT_R: IndicatorDescriptor;
/**
* Ultimate Oscillator: buying pressure over true range, measured across three
* horizons at once and weighted 4:2:1 so the fast window leads without the
* slower two losing their vote.
*
* `high_`/`low_` reach back to the previous close, so bar 0 has no value: the reference
* `max(high, na)` is `na`, and that bar contributes to neither sum. Summing
* from bar 1 is what keeps it out: `rollingSum` keeps a running total, and one
* NaN in a running total never comes back out. With the default 28-bar window
* the first print is therefore at index 28, not 27.
*/
declare const ULTIMATE_OSCILLATOR: IndicatorDescriptor;
/**
* Relative Vigor Index: the bar's body over its range, on the theory that a
* rising market closes near its high. Both halves are smoothed by `swma`
* (the fixed 4-bar 1/2/2/1 kernel) before being summed, so a single wide bar
* cannot swing the reading on its own.
*
* Warmup stacks: 3 bars for the `swma`, `length` more for the sum (index 12 on
* the defaults), then 3 more for the signal's own `swma`.
*
* The reference `offset` input displaces both plots. The library has no per-plot
* offset, so it is a real shift of the columns instead: see `shifted`.
*/
declare const RELATIVE_VIGOR_INDEX: IndicatorDescriptor;
/**
* Relative Volatility Index: RSI's arithmetic applied to volatility instead of
* price: how much of the recent standard deviation arrived on up bars.
*
* Two details are easy to get wrong. The `length` input is the standard
* deviation's window only; the smoothing length is a hard-coded 14 in the reference
* and stays 14 whatever `length` is set to. And the two smoothed series are not
* clean: `change(src) <= 0 ? 0 : stddev` yields a real `0` on down bars but
* `na` on up bars while the standard deviation is still warming up, so the EMA's
* seed has to wait for a 14-bar window with no holes in it. That makes the first
* printed bar `length + 12` on a one-way market rather than a fixed index.
*/
declare const RELATIVE_VOLATILITY_INDEX: IndicatorDescriptor;
/**
* Woodies CCI: a 14-bar CCI drawn twice, as a colour-coded histogram and as a
* line, with a fast "turbo" CCI over the top. The pair is the method: the turbo
* line crossing the slow one is the trigger, and the histogram's colour says
* whether the trend is established enough to take it.
*
* The colour is a five-bar state, not a level (`cci14[5] .. cci14[1]` all on
* one side of zero), so it belongs to `colorBy` rather than to a second plot.
* Note the fallback branch: with no established run, the reference paints a negative
* reading teal and a positive one red, which is the opposite of the run colours.
* That is what the built-in ships, and parity beats tidiness here.
*/
declare const WOODIES_CCI: IndicatorDescriptor;
/**
* Pring's Special K: twelve rates of change from twelve different horizons, each
* smoothed and weighted, added into one line. Short-, intermediate- and
* long-term momentum in a single reading, which is why its turns are read as
* complete-cycle signals rather than as entries.
*
* Every term has to have a value before the sum does, so the slowest one sets
* the warmup; the reference itself refuses to run on a chart with fewer than 725
* bars.
*/
declare const SPECIAL_K: IndicatorDescriptor;
declare const RANGE_INDICATORS: readonly IndicatorDescriptor[];
/**
* Vortex Indicator: how much of the window's total travel was spent reaching up
* versus reaching down.
*
* Both movement terms straddle a bar boundary (`high` against the previous
* `low`), so bar 0 has no term at all: it is missing there, and so is any
* rolling-sum window containing it. Summing from bar 1 and shifting the answer
* forward one bar is how that hole stays out of the running total, and it is why
* the first printed value lands at index `length` rather than at `length - 1`
* like the denominator does.
*/
declare const VORTEX: IndicatorDescriptor;
/**
* Volatility Stop: a trailing stop an ATR multiple away from the running
* extreme of the source, which flips side when price closes through it.
*
* The recursion is the whole study, so it is reproduced term for term. Three
* details are load-bearing:
* - while the ATR is still warming there is no band to scale, and the formula
* falls back to the bar's own true range, unmultiplied, so the stop exists
* from bar 0. The ATR resumes after a missing bar, so this covers the
* warmup alone. An ATR that never resumed would leave the stop on the bare
* true range for the rest of the history, plausible and a factor too tight;
* - the stop only ever ratchets *towards* price in the live direction, which
* is what makes it a stop rather than a band;
* - a flip resets the running extreme to the current source and recomputes the
* stop from it, so the level jumps once and then resumes ratcheting.
*
* The published study draws one cross-style plot whose colour switches with the
* trend. This library has no per-bar line colour, so the level is split across
* two null-gated plots exactly as Supertrend and HalfTrend do, and the crosses
* are stood in for by markers-only dots (as Parabolic SAR does).
*/
declare const VOLATILITY_STOP: IndicatorDescriptor;
/**
* Trend Strength Index: the correlation between price and the passage of time.
*
* A perfectly straight rising line correlates +1 with the bar index and a
* straight fall correlates -1, so the reading is "how close to a straight line
* has this been", not "how fast". Being a correlation it is bounded to -1..1,
* hence the declared range.
*
* Pearson correlation is invariant under shifting either input, so counting the
* position within the supplied bars, rather than from the start of all history,
* changes nothing.
*
* The published study paints gradient fills between the line and zero, which
* this library cannot express (a fill needs two plot edges and one flat colour).
* The two colour inputs it spends on those gradients tint the +1 / -1 bands here
* instead, so they still mean bullish and bearish.
*/
declare const TREND_STRENGTH_INDEX: IndicatorDescriptor;
/**
* Williams Fractals: the pivot highs and lows the alligator is built around.
*
* The study is entirely shapes: two of them, and not one line. A descriptor
* still needs a series for the marker layer to hang off,
* so `fractals` is declared and returns an all-null column: it draws nothing and
* contributes nothing to autoscale, it exists to own the markers. The two signal
* columns beside it carry the price each shape is anchored to, which is what
* makes the markers testable without re-deriving them.
*
* A fractal at bar `p` is only known at bar `p + n`, which is why the study
* draws it `n` bars back. The columns follow that: the signal sits on the
* candidate bar, and the last `n` bars can never carry one, being unconfirmed.
*/
declare const WILLIAMS_FRACTALS: IndicatorDescriptor;
/**
* RSI Divergence Indicator: the RSI, plus a labelled plate wherever a fresh RSI
* pivot disagrees with the price pivot beside it.
*
* Four signal classes, each comparing the newest oscillator pivot with the one
* before it and the price at both:
* - regular bullish: oscillator higher low against a lower price low;
* - hidden bullish: oscillator lower low against a higher price low;
* - regular bearish: oscillator lower high against a higher price high;
* - hidden bearish: oscillator higher high against a lower price high.
*
* The bookkeeping is all "the previous pivot", said three ways: a pivot low of
* the oscillator fires on the bar that *confirms* it, `lbR` bars after it
* happened; the value-when lookup reads a series as it stood at the pivot before
* this one; and the range gate counts the bars since that previous pivot,
* rejecting pairs closer together than `rangeLower` or further apart than
* `rangeUpper`. The gate counts from the found flag delayed one bar, so the
* pivot being confirmed right now is not counted as its own predecessor.
*
* Every comparison is fed a missing value until a second pivot exists, and those
* comparisons are false, so nothing fires before then without an explicit guard.
*
* Not reproduced: the four connector plots, which join consecutive pivot
* readings and go transparent when the divergence does not hold. They need a
* line drawn between two isolated points and a per-bar line colour, neither of
* which this library's line renderer has. The labels carry the same information.
*/
declare const RSI_DIVERGENCE: IndicatorDescriptor;
/**
* Consolidation and Breakout: a mother bar's range, the inside bars that keep it
* alive, and the bar that finally leaves it.
*
* This one is a state machine rather than a formula. A single index is carried
* from bar to bar: the bar whose high and low currently define the range. Every
* later bar whose *body* (open to close, wicks ignored) sits inside that range
* extends the consolidation, and the first body to escape it becomes the new
* mother.
*
* The two reads of that index straddle the reassignment, and that ordering is
* the study:
* - the breakout tests read the range as it stood *before* this bar could
* claim it, which is what makes a breakout a statement about the old range;
* - the plotted range and the inside-bar tint read it *after*, so a bar that
* breaks out is already the new mother and is correctly left untinted.
* One bar therefore both fires a marker and opens the next consolidation.
* Collapsing the two into one read shifts every signal by a bar, which still
* looks plausible on a chart, so `tests/study-consolidation.test.ts` pins the
* breaking bar itself.
*
* Bar 1 is excluded from the inside test by the definition: with the carried
* index seeded at 0, bar 1 would otherwise be measured against a range that has
* had no chance to be broken.
*
* Not reproduced: the thickness control the published definition puts on the two
* range lines. `style.lineWidth` is static here and no settings key stands
* behind it, so the control would move nothing. The lines are fixed at 2.
*/
declare const CONSOLIDATION_BREAKOUT: IndicatorDescriptor;
declare const SIGNAL_INDICATORS: readonly IndicatorDescriptor[];
declare const CPR: IndicatorDescriptor;
declare const ALPHATREND: IndicatorDescriptor;
declare const RANGE_ANALYSIS: IndicatorDescriptor;
declare const STUDY_INDICATORS: readonly IndicatorDescriptor[];
declare const WAVETREND: IndicatorDescriptor;
declare const WAVETREND_INDICATORS: readonly IndicatorDescriptor[];
declare const SEASONALITY: IndicatorDescriptor;
declare const SEASONALITY_INDICATORS: readonly IndicatorDescriptor[];
/**
* Zero Lag Exponential Moving Average: an EMA of the source pushed ahead by its
* own change over half the length, so most of the lag an EMA carries on a trend
* is taken back out.
*
* lag = floor((length - 1) / 2)
* data = source + (source - source[lag])
* zlema = ema(data, length)
*
* `data` has no value for the first `lag` bars, so the EMA's first full window
* ends at `lag + length - 1`: bar 28 at the default length of 20. On a straight
* ramp with an odd length the line sits on the source exactly, which is the lag
* it exists to remove.
*/
declare const ZLEMA: IndicatorDescriptor;
/**
* Variable Index Dynamic Average: an exponential average whose weight each bar
* is scaled by how one-sided the recent movement was, so it follows a trend
* and stands still in a range.
*
* k = |CMO(source, cmoLength)| / 100
* vidya = alpha * k * source + (1 - alpha * k) * vidya[1], alpha = 2 / (length + 1)
*
* The seed is pinned: the line opens on the source itself at bar `cmoLength`,
* the first bar with `cmoLength` changes behind it, because there is no prior
* average to carry and starting from zero would drag the line up from the
* floor of the chart. A window that did not move reads k = 0 and holds the
* line, and a bar whose window holds a missing value is absent, with the line
* resuming afterwards from the value it held.
*/
declare const VIDYA: IndicatorDescriptor;
/**
* Elder-Ray: how far buyers pushed the high above the consensus value, and how
* far sellers pushed the low below it, the consensus being an EMA of the close.
*
* bull = high - ema(close, length)
* bear = low - ema(close, length)
*
* Both print from the EMA's first bar, `length - 1`. Bear power never exceeds
* bull power, since a bar's low never tops its high, so where both are above
* zero the bull column is the taller and where both are below the bear column
* is the deeper.
*/
declare const ELDER_RAY: IndicatorDescriptor;
/**
* Schaff Trend Cycle: the MACD run through a stochastic twice, each pass
* smoothed, so the trend the MACD measures turns into a cycle that spends its
* time pinned near 0 or 100 and moves between them quickly.
*
* macd = ema(source, fast) - ema(source, slow)
* f1 = 100 * (macd - lowest(macd, cycle)) / (highest(macd, cycle) - lowest(macd, cycle))
* pf = pf[1] + factor * (f1 - pf[1]), seeded on the first f1
* f2 = the same stochastic of pf over cycle
* stc = stc[1] + factor * (f2 - stc[1]), seeded on the first f2
*
* A window with no range repeats the stochastic's previous reading, and before
* there is one the bar is absent, so a series that never moves prints nothing.
* The first value lands at `slow + 2 * cycle - 3`: the MACD's warmup, then one
* full window for each stochastic, bar 67 at the defaults.
*/
declare const SCHAFF_TREND_CYCLE: IndicatorDescriptor;
declare const EXPONENTIAL_INDICATORS: readonly IndicatorDescriptor[];
/**
* ZigZag: the swings that are left once every move smaller than `deviation`
* percent is filtered out. The percent-reversal definition:
*
* - While a swing rises, its end is the highest high since it began. A bar
* whose low is at least `deviation` percent below that high confirms the high
* as a swing point and starts a falling swing at the bar's low. A falling
* swing is the mirror image. A bar that makes a new extreme extends the swing
* even when its other end would also have reversed it, and a tie keeps the
* earlier bar.
* - Before the first reversal the highest high and the lowest low since the
* first bar are both open, and the first time they lie `deviation` percent
* apart, the earlier of the two becomes the first swing point.
*
* `zigzag` holds each swing point's price on its own bar, the running end of
* the last leg included, and nothing elsewhere; the legs are drawn between
* them, the last one dashed. Only the last leg ever repaints. On a live tick
* that makes a new extreme its end moves onto the forming bar and the bar it
* left reads empty again; a tick that retraces by `deviation` confirms the end
* and starts a new last leg on the forming bar; a corrected bar can move the
* end back. Confirmed swing points never move, and `calcTail` returns exactly
* what a full `calc` of the same bars does (see `zigzagTail`).
*
* The legs are shapes, not a plot, so a leg is straight on a logarithmic scale
* too, and they take their colour, opacity, thickness and line style from the
* plot's own style settings.
*/
declare const ZIGZAG: IndicatorDescriptor;
/**
* 52 Week High/Low: the highest high and the lowest low of the 52 weeks ending
* with each bar, as two step lines.
*
* The window is calendar time, not a bar count: the 364 days, counted in the
* chart's zone, that end on the bar's own day. On daily bars that is the bar
* and the 52 weeks of sessions before it, so a high made on a Friday leaves the
* window on the Friday 52 weeks later; on intraday bars it is every bar of
* those days.
*
* The warmup is pinned to the history: a bar prints only once the loaded bars
* begin before its window's first day, so no bar of the window can be missing.
* A chart with less than a year loaded therefore draws nothing rather than a
* shorter range under a 52 week name, and on an intraday chart the study needs
* a year of that interval's history to start. A bar with no high or low is
* skipped and costs the window nothing else.
*/
declare const HIGH_LOW_52_WEEK: IndicatorDescriptor;
declare const SWING_INDICATORS: readonly IndicatorDescriptor[];
/**
* Array-based statistics with explicit observation windows. Results keep the
* input length and never mutate inputs. NaN and infinities are missing values.
* Numeric warmup and undefined statistics are NaN; boolean warmup is false.
*/
/** Missing-value policy shared by numerical statistics and predicates. */
interface NumericalWindowOptions {
/**
* `propagate` (default) uses chronological windows and rejects missing terms.
* `skip` uses finite observations, preserving their original order. Rolling
* numeric results hold across missing bars once the window is full.
* Predicates require a finite current observation and otherwise return false.
*/
missing?: 'propagate' | 'skip';
}
/** Estimator choice for {@link rollingVariance}. */
interface RollingVarianceOptions extends NumericalWindowOptions {
/** Divide by period-1 for a sample, or period for the default population. */
sample?: boolean;
}
/**
* Rolling median, averaging the middle pair for even periods. Uses a full
* period including the current bar, with NaN until ready. Missing values
* propagate by default; `skip` collects period finite observations and holds
* across gaps. Equal values retain their multiplicity. Invalid periods throw
* RangeError; an unknown missing policy throws TypeError.
*/
declare function rollingMedian(values: readonly number[], period: number, options?: NumericalWindowOptions): number[];
/**
* Most frequent value in a full period ending at the current bar; equal
* frequencies choose the smallest value. Warmup is NaN. Missing values
* propagate by default; `skip` collects period finite observations and holds
* across gaps. Invalid periods throw RangeError; unknown policies TypeError.
*/
declare function rollingMode(values: readonly number[], period: number, options?: NumericalWindowOptions): number[];
/**
* Variance of a full period ending at the current bar. Population is the
* default; `sample` divides by period-1 and gives NaN for period one. Ties are
* repeated observations. Warmup is NaN. Missing values propagate by default;
* `skip` collects period finite observations and holds across gaps. Invalid
* periods throw RangeError; invalid policy or sample options throw TypeError.
* A variance beyond the finite number range is Infinity.
*/
declare function rollingVariance(values: readonly number[], period: number, options?: RollingVarianceOptions): number[];
/**
* Maximum minus minimum in a full period ending at the current bar. Repeated
* extremes do not change the result. Warmup is NaN. Missing values propagate
* by default; `skip` collects period finite observations and holds across gaps.
* Invalid periods throw RangeError; unknown missing policies throw TypeError.
*/
declare function rollingRange(values: readonly number[], period: number, options?: NumericalWindowOptions): number[];
/**
* Sorted-window percentile at zero-based rank (period-1)*percentage/100,
* interpolating its adjacent values. Ties retain multiplicity; endpoints are
* the minimum and maximum. A full period includes the current bar; warmup is
* NaN. Missing values propagate by default; `skip` collects finite observations
* and holds across gaps. Invalid periods or percentages outside 0..100 throw
* RangeError; unknown missing policies throw TypeError.
*/
declare function percentileLinear(values: readonly number[], period: number, percentage: number, options?: NumericalWindowOptions): number[];
/**
* Spearman correlation of value ranks with chronological order, times 100.
* Tied values receive their average rank. A constant or one-value window is
* NaN, as is warmup before a full period including the current bar. Missing
* values propagate by default; `skip` ranks the last period finite observations
* and holds across gaps. Invalid periods throw RangeError; unknown policies
* throw TypeError.
*/
declare function rankCorrelation(values: readonly number[], period: number, options?: NumericalWindowOptions): number[];
/**
* Negative weighted sum divided by the ordinary sum over a full period,
* assigning weight one to the newest value and period to the oldest. Equal
* values remain separate observations. A zero compensated ordinary sum and
* warmup are NaN; a nonzero sum is never discarded using an epsilon cutoff.
* Ratios beyond the finite number range yield signed Infinity.
* Missing values propagate by default; `skip` weights the last period finite
* observations and holds across gaps. Invalid periods throw RangeError;
* unknown missing policies throw TypeError.
*/
declare function centerOfGravity(values: readonly number[], period: number, options?: NumericalWindowOptions): number[];
/**
* Minimum from the first bar through the current bar; equal minima retain the
* existing value. There is no fixed warmup. Default `propagate` makes the
* entire remaining history NaN after any missing term. `skip` holds the last
* finite minimum, with NaN before the first finite term. Unknown policies
* throw TypeError; empty input returns an empty array.
*/
declare function runningMin(values: readonly number[], options?: NumericalWindowOptions): number[];
/**
* Maximum from the first bar through the current bar; equal maxima retain the
* existing value. There is no fixed warmup. Default `propagate` makes the
* remaining history NaN after any missing term. `skip` holds the last finite
* maximum, with NaN before the first finite term. Unknown policies throw
* TypeError; empty input returns an empty array.
*/
declare function runningMax(values: readonly number[], options?: NumericalWindowOptions): number[];
/**
* Current a>b after previous a<=b. Current equality is false; prior equality
* qualifies. Default `propagate` requires finite pairs on adjacent bars;
* `skip` compares with the most recent jointly finite pair. Missing current
* pairs and the first pair return false. Unequal array lengths throw
* RangeError; unknown missing policies throw TypeError.
*/
declare function crossesAbove(a: readonly number[], b: readonly number[], options?: NumericalWindowOptions): boolean[];
/**
* Current a<b after previous a>=b. Current equality is false; prior equality
* qualifies. Default `propagate` requires finite adjacent pairs; `skip` uses
* the most recent jointly finite pair. Missing current pairs and the first
* pair return false. Unequal lengths throw RangeError; unknown policies
* throw TypeError.
*/
declare function crossesBelow(a: readonly number[], b: readonly number[], options?: NumericalWindowOptions): boolean[];
/**
* Crossing in either direction, with strict current inequality and inclusive
* prior inequality. Default `propagate` requires finite adjacent pairs;
* `skip` compares with the most recent jointly finite pair. Missing current
* pairs and the first pair are false. Unequal lengths throw RangeError;
* unknown missing policies throw TypeError.
*/
declare function crosses(a: readonly number[], b: readonly number[], options?: NumericalWindowOptions): boolean[];
/**
* Whether the finite current value strictly exceeds every one of period prior
* observations, excluding the current bar. Equality fails. Default `propagate`
* uses prior chronological bars; `skip` collects prior finite observations.
* Missing current values, gaps in required history, and insufficient history
* return false. Invalid periods throw RangeError; unknown policies TypeError.
*/
declare function rising(values: readonly number[], period: number, options?: NumericalWindowOptions): boolean[];
/**
* Whether the finite current value is strictly below every one of period prior
* observations, excluding the current bar. Equality fails. Default `propagate`
* uses prior chronological bars; `skip` collects prior finite observations.
* Missing current values, gaps in required history, and insufficient history
* return false. Invalid periods throw RangeError; unknown policies TypeError.
*/
declare function falling(values: readonly number[], period: number, options?: NumericalWindowOptions): boolean[];
/**
* Pure calculation helpers shared by the Tier-1 indicator descriptors
* (`openalgo-charts/indicators`). Every function returns an array the same
* length as its input, with `NaN` in warmup slots: the line renderer breaks
* across non-finite points and autoscale skips them, so a warmup gap draws as
* nothing rather than as a spike to zero.
*
* `ema`, `rsi`, `atr`, `trueRange`, and `supertrend` are NOT re-implemented
* here: they ship in the base bundle and the tier imports them from it.
*
* For helpers accepting missing-value options, omitted options use each
* helper's documented default. Supplied options treat NaN and infinities as
* missing. An empty options object selects chronological propagation.
*
* Varying window lengths must align with the source. Each bar uses its own
* positive safe-integer length; NaN lengths produce gaps without discarding
* source history. Other invalid numeric lengths throw RangeError, malformed
* elements throw TypeError, and unequal array lengths throw RangeError.
* Array lengths default to chronological propagation. With `skip`, changed
* lengths reevaluate finite history even on missing source bars. These paths
* use O(n + sum of evaluated window lengths) time and O(n) working storage.
*/
/**
* Simple moving average. Valid scalar windows without options sum oldest first
* afresh, then divide once. Missing or overflowing sums produce NaN and recover
* when they expire. This uses O(n * period) time and O(1) extra working storage.
* Unsupported scalar periods retain their historical behavior.
* Supplied `skip` collects period finite observations and holds across gaps;
* `propagate` (also the default for {}) requires a complete chronological
* window. Warmup is NaN. Option-path periods must be positive safe integers;
* invalid periods throw RangeError and malformed options throw TypeError.
*/
declare function sma(values: readonly number[], period: number, options?: NumericalWindowOptions): number[];
/** Bar-aligned lengths use each current window; NaN lengths give gaps. Arrays default to propagation. */
declare function sma(values: readonly number[], period: number | readonly number[], options?: NumericalWindowOptions): number[];
/**
* Linearly weighted average, newest observation carrying weight period.
* Valid scalar defaults sum weighted terms oldest first and omit nonfinite
* results. Supplied `skip` weights period
* finite observations and holds across gaps; `propagate` requires a full
* chronological window. Warmup is NaN. The option path validates a positive
* safe-integer period and a missing policy, throwing RangeError or TypeError.
*/
declare function wma(values: readonly number[], period: number, options?: NumericalWindowOptions): number[];
/** Bar-aligned lengths weight the newest selected observation most; NaN lengths give gaps. */
declare function wma(values: readonly number[], period: number | readonly number[], options?: NumericalWindowOptions): number[];
/**
* Wilder's smoothing (RMA): seed from the first complete finite window,
* then `(prev * (period - 1) + v) / period`. Valid scalar defaults retry
* nonfinite seeds, leave a gap for missing inputs and retain seeded state.
* Running overflow is unavailable without restarting. Supplied `skip` seeds from period
* finite observations and holds state across gaps. `propagate` clears state on
* a missing input and reseeds after period consecutive finite observations.
* Warmup is NaN. The option path requires a positive safe-integer period and
* valid policy, throwing RangeError or TypeError respectively.
*/
declare function rma(values: readonly number[], period: number, options?: NumericalWindowOptions): number[];
/**
* Population standard deviation. Valid scalar defaults accumulate squared
* deviations oldest first and omit nonfinite results.
* Supplied `skip` uses period finite observations and holds across gaps;
* `propagate` requires a full chronological window. Ties retain multiplicity;
* warmup is NaN. Invalid option-path periods throw RangeError, malformed
* options TypeError. A period must be a positive safe integer.
*/
declare function stdev(values: readonly number[], period: number, options?: NumericalWindowOptions): number[];
/** Bar-aligned lengths select the current population; NaN lengths give gaps. Arrays default to propagation. */
declare function stdev(values: readonly number[], period: number | readonly number[], options?: NumericalWindowOptions): number[];
/**
* Rolling maximum. Omitted options preserve legacy warmup and gap behavior.
* Supplied `skip` searches period finite observations and holds across gaps;
* `propagate` requires period chronological finite bars. Insufficient or
* all-missing history is NaN. Invalid option-path periods throw RangeError;
* malformed policies throw TypeError. Periods must be positive safe integers.
*/
declare function highest(values: readonly number[], period: number, options?: NumericalWindowOptions): number[];
/** Bar-aligned lengths select the current maximum window; NaN lengths give gaps. */
declare function highest(values: readonly number[], period: number | readonly number[], options?: NumericalWindowOptions): number[];
/**
* Rolling minimum. Omitted options preserve legacy warmup and gap behavior.
* Supplied `skip` searches period finite observations and holds across gaps;
* `propagate` requires period chronological finite bars. Insufficient or
* all-missing history is NaN. Invalid option-path periods throw RangeError;
* malformed policies throw TypeError. Periods must be positive safe integers.
*/
declare function lowest(values: readonly number[], period: number, options?: NumericalWindowOptions): number[];
/** Bar-aligned lengths select the current minimum window; NaN lengths give gaps. */
declare function lowest(values: readonly number[], period: number | readonly number[], options?: NumericalWindowOptions): number[];
/** NaN to null, so a warmup slot serialises as an explicit gap. */
declare function nulls(values: readonly number[]): (number | null)[];
/**
* EMA seeded with an SMA, then smoothed with alpha=2/(period+1). Valid scalar
* defaults seed from the first complete finite window, retry nonfinite seeds,
* and emit gaps for missing inputs while retaining seeded state. A nonfinite
* running update stays committed, without restarting. Supplied
* `skip` seeds from period finite observations and holds state across gaps.
* `propagate` clears state on any missing input and reseeds with period
* consecutive finite observations. Warmup is NaN. Invalid option-path
* periods throw RangeError; malformed options throw TypeError. Periods must
* be positive safe integers.
*/
declare function smaSeededEma(values: readonly number[], period: number, options?: NumericalWindowOptions): number[];
/** the reference `change(src, n)`: `src - src[n]`. NaN for the first `n` bars. */
declare function change(values: readonly number[], n?: number): number[];
/** the reference `roc`: `100 * (src - src[n]) / src[n]`. NaN for the first `n` bars. */
declare function roc(values: readonly number[], n: number): number[];
/**
* Mean absolute deviation from the average. Valid scalar defaults accumulate
* deviations oldest first and omit nonfinite results.
* Supplied `skip` uses period finite observations and holds across
* gaps; `propagate` requires a full chronological window. Ties retain their
* multiplicity and warmup is NaN. Invalid option-path periods throw RangeError,
* malformed options TypeError. Periods must be positive safe integers.
*/
declare function dev(values: readonly number[], period: number, options?: NumericalWindowOptions): number[];
/** Bar-aligned lengths select the current deviation window; NaN lengths give gaps. */
declare function dev(values: readonly number[], period: number | readonly number[], options?: NumericalWindowOptions): number[];
/**
* Percentage of period previous values less than or equal to the current
* subject, excluding the subject itself. Omitted options preserve legacy
* behavior. Supplied `skip` collects previous finite observations; `propagate`
* requires a full previous chronological window. A missing subject or
* insufficient history produces NaN under either policy. Equality counts.
* Invalid option-path periods throw RangeError; malformed policies throw
* TypeError. Periods must be positive safe integers.
*/
declare function percentRank(values: readonly number[], period: number, options?: NumericalWindowOptions): number[];
/** Bar-aligned lengths count previous observations. A missing current subject or length gives NaN. */
declare function percentRank(values: readonly number[], period: number | readonly number[], options?: NumericalWindowOptions): number[];
/**
* the reference `alma`: Gaussian-weighted MA, `offset` 0..1 and `sigma` > 0. A period
* that is not a whole number of 1 or more gives NaN throughout.
*/
declare function alma(values: readonly number[], period: number, offset: number, sigma: number): number[];
/** the reference `vwma`: `sma(src * volume, len) / sma(volume, len)`. */
declare function vwma(values: readonly number[], volumes: readonly number[], period: number): number[];
/**
* Highest-value bar offset, zero for current and negative into history; ties
* choose the latest bar. Omitted options preserve legacy behavior. Supplied
* `skip` searches period finite observations and retains original bar indices,
* so an offset can extend beyond period-1 and ages across missing current bars.
* `propagate` requires period chronological finite bars. Warmup and all-missing
* history are NaN. Invalid option-path periods throw RangeError; malformed
* policies throw TypeError. Periods must be positive safe integers.
*/
declare function highestBars(values: readonly number[], period: number, options?: NumericalWindowOptions): number[];
/** Bar-aligned lengths retain original offsets and latest ties; NaN lengths give gaps. */
declare function highestBars(values: readonly number[], period: number | readonly number[], options?: NumericalWindowOptions): number[];
/**
* Lowest-value bar offset, zero for current and negative into history; ties
* choose the latest bar. Omitted options preserve legacy behavior. Supplied
* `skip` searches period finite observations using their original indices;
* offsets age across gaps. `propagate` requires a full chronological window.
* Warmup and all-missing history are NaN. Invalid option-path periods throw
* RangeError; malformed policies throw TypeError. Periods must be positive
* safe integers.
*/
declare function lowestBars(values: readonly number[], period: number, options?: NumericalWindowOptions): number[];
/** Bar-aligned lengths retain original offsets and latest ties; NaN lengths give gaps. */
declare function lowestBars(values: readonly number[], period: number | readonly number[], options?: NumericalWindowOptions): number[];
/**
* Fresh oldest-first sum over each complete finite window; NaN on warmup or
* overflow. Missing values expire with the window. Valid integer periods use
* O(n * period) time and O(1) extra working storage, excluding the result.
* Unsupported scalar periods retain their historical behavior.
*/
declare function rollingSum(values: readonly number[], period: number): number[];
/** the reference `cum`: running total from the first bar. Non-finite terms count as 0. */
declare function cumulative(values: readonly number[]): number[];
/**
* the reference `linreg`: the least-squares regression line fitted over the last
* `period` values, evaluated `offset` bars back from its right-hand end.
*
* x runs 0 (oldest bar in the window) to period-1 (current bar), so the value
* at the current bar is `intercept + slope * (period - 1)`. A positive `offset`
* steps back down that line, which is how LSMA's offset input shifts the plot
* without recomputing the fit.
*/
declare function linreg(values: readonly number[], period: number, offset?: number): number[];
/** the reference `swma`: the fixed 4-bar symmetrically weighted average, 1/2/2/1 over 6. */
declare function swma(values: readonly number[]): number[];
/**
* the reference `stoch(source, high, low, length)`. Note the three series are
* independent: Stochastic RSI passes the RSI in for all three, which is why
* this cannot just take bars.
*/
declare function stoch(source: readonly number[], high: readonly number[], low: readonly number[], period: number): number[];
/**
* the reference `percentile_nearest_rank`. The nearest-rank method returns an
* actual member of the window rather than interpolating between two, so a
* 50th percentile over an even-length window is the upper of the two middles,
* not their mean. That difference is visible on Median's default length of 3.
* A percentage above 100, or NaN, has no reading.
*/
declare function percentileNearestRank(values: readonly number[], period: number, percentage: number): number[];
/**
* the reference `correlation`: population Pearson correlation of two series over
* `period`. Each window takes two passes, oldest first: both means are finished
* before any deviation is formed, then
* `(cross / period) / (sqrt(squaresA / period) * sqrt(squaresB / period))`.
* A window with a missing value or an overflowing step is NaN, and so is one
* whose deviations are all exactly zero, such as three bars of 5. A flat window
* whose mean is inexact is not caught by that: three bars of 0.1 average to
* slightly more than 0.1, the deviations are a few units in the last place, and
* the window reads what that arithmetic gives, as the reference does. Against a
* bar index that is exactly 0; against another series it is within rounding of
* 0. A period that is not a whole number above 1 gives NaN throughout, as it
* did before.
*/
declare function correlation(a: readonly number[], b: readonly number[], period: number): number[];
/**
* the reference `cci`: `(src - sma) / (0.015 * dev)`, where `dev` is the mean absolute deviation.
* A flat window (a deviation of exactly 0) has no reading here. The built-in
* CCI study prints 0 on such a window instead, so a study ported onto this
* function shows a gap where the built-in shows 0.
*/
declare function cci(values: readonly number[], period: number): number[];
/**
* the reference `pivothigh` / `pivotlow`. A pivot is confirmed `right` bars
* after it happens, so the answer lands on the confirming bar and refers to the
* value `right` bars back. Comparisons are strict on both sides, so a tie is
* not a pivot.
*/
declare function pivotHigh(values: readonly number[], left: number, right: number): number[];
/**
* Array widths align with the source and are read on the confirmation bar.
* NaN array elements give gaps; scalar companions and other array elements
* must be nonnegative safe integers. Mismatched array lengths throw RangeError.
* Zero widths are valid. Finite neighbors and strict comparisons are required.
* Results stay on confirmation bars, including repeated confirmations of one
* candidate. Invalid numbers throw RangeError; malformed elements TypeError.
*/
declare function pivotHigh(values: readonly number[], left: number | readonly number[], right: number | readonly number[]): number[];
declare function pivotLow(values: readonly number[], left: number, right: number): number[];
/**
* Array widths follow pivotHigh's validation and confirmation placement, with
* strictly lower candidates. Missing widths or neighbors give NaN. Both widths
* are read from the current confirmation bar, even if the candidate repeats.
*/
declare function pivotLow(values: readonly number[], left: number | readonly number[], right: number | readonly number[]): number[];
/** the reference `barssince`: bars elapsed since `cond` was last true, NaN before the first. */
declare function barsSince(cond: readonly boolean[]): number[];
/**
* the reference `valuewhen(cond, source, occurrence)`: the value of `source` the
* n-th most recent time `cond` was true, counting the current bar. Occurrence 0
* is the latest. An occurrence that is not a whole number of 0 or more gives
* NaN throughout, and so does a hit past the end of a shorter `source`.
*/
declare function valueWhen(cond: readonly boolean[], source: readonly number[], occurrence: number): number[];
/**
* Tier-2 contract: indicators whose data is **not** derived from the chart's
* OHLCV: cumulative volume delta, PCR, an external analytics
* feed. Where a Tier-1 descriptor is a pure `calc(bars, settings)`, a Tier-2
* descriptor owns a fetch / subscribe / merge lifecycle and its own series.
*
* `createTier2Indicator` wraps that lifecycle into an ordinary
* `IndicatorDescriptor`, so the chart runtime, the settings model, panes,
* levels, and removal all work identically: there is no second runtime.
*
* The alignment rule is deliberate and worth knowing: external points carry
* their own timestamps, which rarely match bar times. Each bar takes the most
* recent external point **at or before** that bar's time (last-known-value,
* never interpolated and never forward-looking), and bars before the first
* point are `null`.
*/
/** One external observation: a timestamp plus a value per plot key. */
interface Tier2Point {
/** UTC seconds. */
time: number;
values: Readonly<Record<string, number | null>>;
}
interface Tier2Context {
/** Host identity when supplied. Indicator settings remain independent. */
dataContext?: Readonly<ChartDataContext>;
/** Native source, provider and external-data revisions, when supplied. */
requestState?: Readonly<IndicatorRequestState>;
/** Inclusive availability cutoff during opted-in native timed replay. */
asOf?: number;
/** Cancelled when this request is obsolete or the instance is removed. */
signal?: AbortSignal;
settings: Readonly<IndicatorSettings>;
/** The chart's current source bars: use for the requested time window. */
bars: readonly Bar[];
/** UTC seconds of the first and last source bar (0 when there are none). */
from: number;
to: number;
/**
* The host's bar provider, when the runtime supplies one, so a `fetch` that
* needs another instrument's candles asks the host rather than carrying its
* own transport and credentials. Rejects when the host registered none. A
* request that names no `variant` is sent in the chart's session and
* adjustment (see `inheritedDataVariant`); naming one, `{}` included, wins.
*/
requestBars?(request: IndicatorBarsRequest): Promise<readonly Bar[]>;
}
interface Tier2Descriptor {
id: string;
name: string;
category?: string;
placement: 'onchart' | 'pane';
inputs: readonly IndicatorInput[];
plots: readonly IndicatorPlot[];
/**
* External columns to align besides the plots, by key. A point may carry
* more than what is drawn: a benchmark close that `calc` divides by, an
* open-interest figure a ratio is built from. Anything not named here or in
* `plots` is dropped at alignment.
*/
series?: readonly string[];
/**
* Combine the aligned external columns with the chart's own bars. Without
* it the aligned columns are the result, one per plot, exactly as before.
* With it, `external` holds every plot and `series` key aligned onto the
* bars (last-known-value, `null` before the first point), and the return is
* what the plots draw: a relative strength, a beta, a spread. Pure in its
* arguments, like any `calc`.
*/
calc?(bars: readonly Bar[], external: IndicatorValues, settings: Readonly<IndicatorSettings>, store: IndicatorStore, ctx?: IndicatorCalcContext): IndicatorValues;
/** A host/provider can explicitly decline data it cannot supply. */
supports?(ctx: Tier2Context): boolean;
/** Load the series for the current window. */
fetch(ctx: Tier2Context): Promise<readonly Tier2Point[]>;
/**
* Opt into native timed replay. fetch must honor finite ctx.asOf, return the
* values known then, and use point.time as availability time. Raw requestBars
* does not supply historical value versions automatically. Native legacy
* replay without an availability clock remains unsupported. Defaults false.
*/
supportsReplay?: boolean;
/**
* Optional live subscription. Call `push` with each incoming point; return an
* unsubscribe function.
*/
subscribe?(ctx: Tier2Context, push: (point: Tier2Point) => void): () => void;
/**
* Settings keys that invalidate the fetched data when they change (symbol,
* exchange, resolution). Changing anything else only re-runs alignment.
*/
refetchOn?: readonly string[];
levels?(settings: Readonly<IndicatorSettings>): readonly IndicatorLevel[];
range?(settings: Readonly<IndicatorSettings>): {
min: number;
max: number;
} | null;
}
/**
* Wrap a Tier-2 descriptor as a normal `IndicatorDescriptor`.
*
* ```ts
* export const POSITION_INDEX = createTier2Indicator({
* id: 'external-position-index', name: 'Position Index', placement: 'pane',
* inputs: [{ key: 'symbol', type: 'text', label: 'Symbol', default: '' }],
* plots: [{ key: 'position', type: 'line', title: 'Position' }],
* refetchOn: ['symbol'],
* fetch: async ({ settings, from, to }) => loadPositionIndex(settings.symbol, from, to),
* });
* registerIndicator(POSITION_INDEX);
* ```
*/
declare function createTier2Indicator(d: Tier2Descriptor): IndicatorDescriptor;
/**
* A higher-timeframe view of the chart's own bars, one value per source bar.
*
* securitySeries aligns aggregate OHLC values. securityExpression calculates
* on aggregate bars before alignment, so a rolling window counts requested
* bars rather than repeated aligned values. securitySeries names its readings:
*
* - `offset: 0` (the default) reads the bucket **as it stood at that bar**:
* its open so far, high and low so far, the bar's own close, volume so far.
* It is what the live bar sees and it never uses a later bar.
* - `offset: k` reads the bucket that completed `k` buckets before, held
* constant across the current one. The classic non-repainting reference,
* `close[1]` on the higher timeframe.
* - `lookahead: true` reads the current bucket's **final** values on every one
* of its bars. It uses bars that had not happened yet, which is what the
* source it is porting did; it is here so that can be reproduced, not
* recommended.
*
* Buckets follow the chart's calendar: a day is a day in `timezone`, a week
* starts on Monday there, and a sub-day interval is anchored to the epoch, or
* to the session open when `session` is given, which is how an exchange cuts
* its hourly bars.
*/
interface SecurityExpressionOptions {
timezone?: string;
session?: string;
/**
* confirmed (default) holds the previous observed bucket's result once the
* next bucket starts. developing calculates with the current partial bucket.
* lookahead aligns the current bucket's final result onto its earlier bars.
*/
mode?: 'confirmed' | 'developing' | 'lookahead';
}
interface SecurityOptions {
/** The calendar the buckets are cut in. Defaults to the shipped default zone. */
timezone?: string;
/** Read each bucket's final values on all of its bars. Default false. */
lookahead?: boolean;
/** Read the bucket completed this many buckets ago. Default 0, the current one. */
offset?: number;
/**
* A session window, `'0915-1530'`, that anchors sub-day buckets to the
* session open instead of the epoch. Without it a 30-minute bucket on a
* 09:15 open runs 09:00 to 09:30; with it, 09:15 to 09:45.
*/
session?: string;
}
interface SecuritySeries {
open: (number | null)[];
high: (number | null)[];
low: (number | null)[];
close: (number | null)[];
/** Null on a bucket none of whose bars carried volume. */
volume: (number | null)[];
/**
* Open interest as at the last source bar of the bucket, null where no bar in
* it carried any. A level rather than a flow, so it is the latest reading and
* never the sum the way `volume` is.
*/
oi: (number | null)[];
/** Time of the first source bar in the bucket being read, UTC seconds. */
bucketStart: (number | null)[];
/** True on the first source bar of each bucket. */
isNew: boolean[];
}
declare function securitySeries(bars: readonly Bar[], interval: string, options?: SecurityOptions): SecuritySeries;
/**
* Evaluate on aggregated bars first, then align named columns to source bars.
* This differs from applying a rolling formula to already aligned OHLC values,
* which counts each repeated requested value as another observation.
*
* The expression must be pure and causal, returning one value per requested
* bar in each column. Confirmed and lookahead modes evaluate the folded history
* once; developing mode evaluates every source prefix, with no future source
* bars, so its cost includes one expression call per source bar. Completed
* buckets are inferred only when the next observed bucket starts. Missing
* buckets are not fabricated and the last bucket is never clock-confirmed.
*
* Supply bars finer than or equal to the requested interval. This function
* cannot manufacture lower-timeframe observations from coarser source bars.
* Source timestamps must increase strictly. Empty data returns {} without
* invoking the expression. Non-finite expression results become null.
*/
declare function securityExpression(bars: readonly Bar[], interval: string, expression: (bars: readonly Readonly<Bar>[]) => IndicatorValues, options?: SecurityExpressionOptions): IndicatorValues;
/**
* A pure, causal calculation with one value per requested bar in each named
* column. Output at index i may depend only on input through i. Helpers cannot
* prove callback causality. Inputs are copied and frozen, including each bar.
*/
type RequestedExpression = (bars: readonly Readonly<Bar>[]) => IndicatorValues;
interface RequestedAlignmentOptions {
/**
* carry retains the latest eligible row, including null values. missing emits
* only when the selected row changes; the first target is an initial reading.
*/
gaps?: 'carry' | 'missing' | undefined;
}
/** Opening-time membership is [start, end); availability may equal end. */
interface RequestedTimeWindow {
start: number;
end: number;
}
interface RequestedIntrabarOptions {
/** Inclusive UTC availability cutoff in addition to each window's end. */
asOf?: number;
}
/** Arrays retain requested row order, including missing expression values. */
interface RequestedIntrabarValues {
times: number[][];
values: Record<string, (number | null)[][]>;
}
/**
* Calculate once on the whole requested history, then align to finite, strictly
* increasing UTC evaluation times. The confirmed prefix ends at the first
* unconfirmed bar or unknown availability; that bar and all later bars remain
* in expression input but cannot emit. Availability is the cumulative maximum
* across each prefix. Simultaneously available rows select the latest one.
*
* Missing/nonfinite results become null and replace older readings. An empty
* snapshot returns {} without invoking the expression; an empty target with
* nonempty requested history still evaluates to obtain named empty columns.
* Invalid timestamps, metadata, options or output shapes throw IndicatorInputError.
* Inputs are fully validated before the callback. Runtime is O((n + targets) *
* columns), plus expression cost. No transport, aggregation or clock inference.
*/
declare function alignRequestedExpression(targetTimes: readonly number[], snapshot: RequestedBarsSnapshot, expression: RequestedExpression, options?: RequestedAlignmentOptions): IndicatorValues;
/**
* Calculate once across all requested observations, then group by nonoverlapping
* ordered windows. Windows must have finite start < end; gaps are allowed.
* An observation belongs by its opening in [start, end), and emits only when
* its confirmed prefix is available by end and by optional finite asOf.
* Delayed observations never move to a later window. Expression history never
* restarts at window boundaries. Empty windows return empty arrays.
*
* Uses the same prefix, missing-value, validation and empty-snapshot rules as
* alignRequestedExpression. Result times are openings, not availability times.
* Runtime is O(n * columns + windows * columns), plus expression cost.
*/
declare function requestedIntrabars(targetWindows: readonly RequestedTimeWindow[], snapshot: RequestedBarsSnapshot, expression: RequestedExpression, options?: RequestedIntrabarOptions): RequestedIntrabarValues;
/** Source and settings visible to request selection and requested calculation. */
interface RequestedIndicatorContext {
bars: readonly Bar[];
settings: Readonly<IndicatorSettings>;
dataContext?: Readonly<ChartDataContext>;
requestState?: Readonly<IndicatorRequestState>;
calculation?: IndicatorCalcContext;
}
/** A requested expression with an instance-owned snapshot lifecycle. */
interface RequestedIndicatorDescriptor extends Pick<IndicatorDescriptor, 'id' | 'name' | 'category' | 'placement' | 'inputs' | 'plots' | 'levels' | 'range'> {
/** False refreshes on range/provider/data changes, not price-only source revisions. Default true. */
refreshOnSourceUpdate?: boolean;
/**
* Select a complete history window, including expression warmup. null means
* unsupported. The helper owns cancellation; signal is not a selector input.
* Symbol, exchange, interval, variant and explicit asOf changes start a new
* generation, and so does a change of the chart's own variant. A request that
* names no `variant` asks in the chart's session and adjustment (see
* `inheritedDataVariant`); naming one, `{}` included, overrides that.
* Window changes refresh the complete selection without merging partial pages.
*/
request(ctx: RequestedIndicatorContext): Omit<IndicatorSnapshotRequest, 'signal'> | null;
/**
* Calculate on copied, frozen requested bars before alignment. Each result at
* i must depend only on requested observations through i. Return every plot's
* named column with one value per requested bar. Nonfinite values become null.
* Under asOf, only the known, confirmed prefix available by that cutoff is
* supplied. Provider correctness for historical value versions remains required.
*/
expression(bars: readonly Readonly<Bar>[], settings: Readonly<IndicatorSettings>, ctx: RequestedIndicatorContext): IndicatorValues;
/** carry retains the latest eligible row, including null; missing emits a newly selected row only. */
gaps?: RequestedAlignmentOptions['gaps'];
/**
* Strictly increasing finite availability query times, one per source bar.
* Defaults to source opening times. Use an explicit final playhead time when
* a forming source bar should observe requested data available after opening.
*/
targetTimes?(ctx: RequestedIndicatorContext): readonly number[];
}
/**
* Manage one requested snapshot per instance. Changes are observed through the
* optional native request hooks, or through data notifications on older hosts.
* There is at most one active request and one latest pending refresh. Provider,
* source, history or market changes and backward replay cancel obsolete work.
* Synchronous style reattachment retains pending work when its selection is
* unchanged. Calculation performs no requests and never prealigns cached values.
*/
declare function createRequestedIndicator(d: RequestedIndicatorDescriptor): IndicatorDescriptor;
/**
* The part of a chart's data variant that a request for another instrument
* inherits: the session and the adjustment, which say how bars are observed,
* so a benchmark or a comparison lines up with the chart bar for bar. The
* currency and the unit belong to the instrument that was asked for, so they
* stay behind. Undefined when nothing is inherited. Throws a TypeError for a
* malformed variant, like `normalizeDataVariant`.
*/
declare function inheritedDataVariant(variant?: DataVariant): Readonly<DataVariant> | undefined;
declare const INDICATORS_TIER: "indicators";
/** Every built-in descriptor, in picker order. */
declare const BUILTIN_INDICATORS: readonly IndicatorDescriptor[];
/**
* Register every built-in indicator. Called as a side effect on import, and
* exported so bundlers that aggressively tree-shake a bare side-effect import
* can call it explicitly. Idempotent.
*/
declare function registerBuiltinIndicators(): void;
export { ADAPTIVE_INDICATORS, ADL, ADX, ALLIGATOR, ALMA, ALPHATREND, AROON, AROON_OSCILLATOR, ATR, AVERAGE_DAILY_RANGE, AVERAGE_INDICATORS, AWESOME_OSCILLATOR, BALANCE_OF_POWER, BB_TREND, BOLLINGER, BOLLINGER_BANDWIDTH, BOLLINGER_PERCENT_B, BUILTIN_INDICATORS, CCI, CHAIKIN_MONEY_FLOW, CHAIKIN_OSCILLATOR, CHAIKIN_VOLATILITY, CHANDELIER_EXIT, CHANDE_KROLL_STOP, CHANDE_MOMENTUM, CHOPPINESS_INDEX, CHOP_ZONE, CONNORS_RSI, CONSOLIDATION_BREAKOUT, COPPOCK_CURVE, CPR, DEMA, DONCHIAN, DPO, EASE_OF_MOVEMENT, ELDER_FORCE_INDEX, ELDER_RAY, EMA, ENVELOPE, EXPONENTIAL_INDICATORS, FISHER_TRANSFORM, FLOW_INDICATORS, HALFTREND, HIGH_LOW_52_WEEK, HISTORICAL_VOLATILITY, HMA, HULL_SUITE, ICHIMOKU, INDEX_INDICATORS, INDICATORS_TIER, KAMA, KELTNER_CHANNEL, KLINGER_OSCILLATOR, KNOW_SURE_THING, LINREG_SLOPE, LSMA, MACD, MASS_INDEX, MA_CHANNEL, MA_CROSS, MA_RIBBON, MCGINLEY_DYNAMIC, MEDIAN, MFI, MOMENTUM, NET_VOLUME, NVI, type NumericalWindowOptions, OBV, OPEN_INTEREST, OPEN_INTEREST_BUILDUP, OPEN_INTEREST_CHANGE, OSCILLATOR_INDICATORS, OVERLAY_INDICATORS, PARABOLIC_SAR, PPO, PVI, PVO, PVT, RANGE_ANALYSIS, RANGE_INDICATORS, RELATIVE_VIGOR_INDEX, RELATIVE_VOLATILITY_INDEX, ROC, RSI, RSI_DIVERGENCE, type RequestedAlignmentOptions, type RequestedExpression, type RequestedIndicatorContext, type RequestedIndicatorDescriptor, type RequestedIntrabarOptions, type RequestedIntrabarValues, type RequestedTimeWindow, type RollingVarianceOptions, SCHAFF_TREND_CYCLE, SEASONALITY, SEASONALITY_INDICATORS, SIGNAL_INDICATORS, SMA, SMI, SMI_ERGODIC_INDICATOR, SMI_ERGODIC_OSCILLATOR, SMMA, SPECIAL_K, STANDARD_DEVIATION, STANDARD_ERROR, STANDARD_ERROR_BANDS, STOCHASTIC, STOCHASTIC_RSI, STRENGTH_INDICATORS, STUDY_INDICATORS, SUPERTREND, SWING_INDICATORS, type SecurityExpressionOptions, type SecurityOptions, type SecuritySeries, T3, TEMA, TREND_STRENGTH_INDEX, TRIX, TSI, TWAP, type Tier2Context, type Tier2Descriptor, type Tier2Point, type TimeframeOptions, type TimeframeProviderOptions, ULCER_INDEX, ULTIMATE_OSCILLATOR, VIDYA, VOLATILITY_INDICATORS, VOLATILITY_SQUEEZE, VOLATILITY_STOP, VOLUME, VORTEX, VWAP, VWMA, WAVETREND, WAVETREND_INDICATORS, WILLIAMS_FRACTALS, WILLIAMS_PERCENT_R, WILLIAMS_VIX_FIX, WMA, WOODIES_CCI, ZIGZAG, ZLEMA, alignRequestedExpression, alma, barsSince, cci, centerOfGravity, change, connorsStreak, correlation, createRequestedIndicator, createTier2Indicator, createTimeframeProvider, crosses, crossesAbove, crossesBelow, cumulative, dev, falling, highest, highestBars, inheritedDataVariant, linreg, lowest, lowestBars, nulls, percentRank, percentileLinear, percentileNearestRank, pivotHigh, pivotLow, rankCorrelation, registerBuiltinIndicators, requestedIntrabars, rising, rma, roc, rollingMedian, rollingMode, rollingRange, rollingSum, rollingVariance, runningMax, runningMin, securityExpression, securitySeries, sma, smaSeededEma, stdev, stoch, swma, valueWhen, vwma, withTimeframe, wma };