Momentum and oscillators

Bounded and unbounded oscillators, momentum measures and divergence detection. See the indicator reference for how to read a signature, and Indicators for the construction and guarding rules.

CCI

Commodity channel index: (typical price − its mean) / (0.015 × mean deviation) over period.

new CCI(candleIndicator: Indicator<Candle>, period: number)
Parameter Type Default Meaning
candleIndicator Indicator<Candle> required The candle series.
period number required The window length (20 classically).
  • Input: The candle series.
  • Value (getValue() after hasValue()): number | undefined
  • Warm-up: undefined until period candles have been seen.
const symbol = 'BTC';
const candles = manager.getIntervalData(symbol, 60);
const indicator = new CCI(candles, 20);

manager.onIntervalCandle(symbol, 60, () => {
  if (!indicator.hasValue()) return;
  const value = indicator.getValue();
  if (value === undefined) return;
  manager.debugLog('CCI20', value);
});

ChandeMomentumOscillator

Chande momentum oscillator: 100 × (sum of gains − sum of losses) / (sum of gains + sum of losses) over length, between −100 and 100.

new ChandeMomentumOscillator(sourceIndicator: Indicator<number | undefined>, length: number = 9)
Parameter Type Default Meaning
sourceIndicator Indicator<number | undefined> required The number stream.
length number 9 The window length.
  • Input: A number stream.
  • Value (getValue() after hasValue()): number | undefined
  • Warm-up: Returns a number from the first candle, NaN while there has been no price change; the window is complete after length candles.
const symbol = 'BTC';
const candles = manager.getIntervalData(symbol, 60);
const indicator = new ChandeMomentumOscillator(candles.close, 9);

manager.onIntervalCandle(symbol, 60, () => {
  if (!indicator.hasValue()) return;
  const value = indicator.getValue();
  if (value === undefined || Number.isNaN(value)) return;
  manager.debugLog('CMO', value);
});

Divergences

Finds regular and hidden divergences between price swings and a target series (an oscillator), using Swings pivots.

new Divergences(candleIndicator: Indicator<Candle>, source: 'close' | 'high/low', period: number, maxPivots: number, maxPivotAge: number, targetIndicator: Indicator<number | undefined>, minPivotAge: number)
Parameter Type Default Meaning
candleIndicator Indicator<Candle> required The candle series.
source 'close' | 'high/low' required Pivot price: 'close' or 'high/low'.
period number required Candles on each side that define a pivot.
maxPivots number required Maximum pivots kept.
maxPivotAge number required Pivots older than this many candles are dropped.
targetIndicator Indicator<number | undefined> required The series compared with price (for example new RSI(candles.close, 14)).
minPivotAge number required Minimum age, in candles, of the older pivot of a divergence.
  • Input: The candle series plus a target number stream (for example an RSI).
  • Value (getValue() after hasValue()): DivergenceInfo
    • index: number
    • positiveRegular: readonly Divergence[]
    • positiveHidden: readonly Divergence[]
    • negativeRegular: readonly Divergence[]
    • negativeHidden: readonly Divergence[]
  • Divergence is { pivotIndex: number; pivotTime: number; pivotPrice: number; pivotTarget: number }.
  • Warm-up: Ready from the first candle, with empty lists until pivots exist; a pivot is only confirmed period candles after it forms.
const symbol = 'BTC';
const candles = manager.getIntervalData(symbol, 60);
const indicator = new Divergences(candles, 'close', 5, 10, 60, new RSI(candles.close, 14), 5);

manager.onIntervalCandle(symbol, 60, () => {
  if (!indicator.hasValue()) return;
  const value = indicator.getValue();
  if (value.positiveRegular.length > 0) manager.debugLog('bullish divergence', value.positiveRegular.length);
});

DPO

Detrended price oscillator: close − SMA(period) as it was period / 2 + 1 candles ago.

new DPO(sourceIndicator: Indicator<number | undefined>, period: number = 20)
Parameter Type Default Meaning
sourceIndicator Indicator<number | undefined> required The number stream.
period number 20 The SMA length.
  • Input: A number stream.
  • Value (getValue() after hasValue()): number | undefined
  • Warm-up: First value on candle period + floor(period / 2) + 1 (31 for period 20).
const symbol = 'BTC';
const candles = manager.getIntervalData(symbol, 60);
const indicator = new DPO(candles.close, 20);

manager.onIntervalCandle(symbol, 60, () => {
  if (!indicator.hasValue()) return;
  const value = indicator.getValue();
  if (value === undefined) return;
  manager.debugLog('DPO', value);
});

HARSI

JayRogers' Heikin Ashi RSI Oscillator: Heikin Ashi candles of zero-centred RSIs (RSI − 50, from −50 to +50; its levels are ±20 and ±30). The open is the previous candle's close RSI, the high and the low the higher and the lower of the high's and the low's RSIs.

new HARSI(candleIndicator: ICandleIndicator, period: number, openSmoothingLength: number = 1)
Parameter Type Default Meaning
candleIndicator ICandleIndicator required The candle series.
period number required The RSI length.
openSmoothingLength number 1 How many times the Heikin Ashi open weighs the previous open against the previous close (1, the classic Heikin Ashi open, by default).
  • Input: The candle series.
  • Value (getValue() after hasValue()): HARSIValue | undefined
    • open: number
    • high: number
    • low: number
    • close: number
  • Sub-indicators: .open, .high, .low, .close, usable as inputs to other indicators.
  • Warm-up: First value on candle period + 1.
const symbol = 'BTC';
const candles = manager.getIntervalData(symbol, 60);
const indicator = new HARSI(candles, 14);

manager.onIntervalCandle(symbol, 60, () => {
  if (!indicator.hasValue()) return;
  const value = indicator.getValue();
  if (value === undefined) return;
  manager.debugLog('HA-RSI close', value.close, value.close > value.open ? 'green' : 'red');
});

KDJ

KDJ oscillator: raw stochastic dK, then K and D smoothed with a 1/3 weight, and J = 3K − 2D.

new KDJ(candleIndicator: ICandleIndicator, extremePeriod: number)
Parameter Type Default Meaning
candleIndicator ICandleIndicator required The candle series.
extremePeriod number required High/low look-back (9 classically).
  • Input: The candle series.
  • Value (getValue() after hasValue()): KDJValue
    • K: number
    • D: number
    • J: number
    • dK: number
  • Warm-up: Values from the first candle (K and D start at 50); allow extremePeriod candles before trusting them.
const symbol = 'BTC';
const candles = manager.getIntervalData(symbol, 60);
const indicator = new KDJ(candles, 9);

manager.onIntervalCandle(symbol, 60, () => {
  if (!indicator.hasValue()) return;
  const value = indicator.getValue();
  manager.debugLog('K', value.K, 'D', value.D, 'J', value.J);
});

MACD

Moving average convergence divergence: macd = fast MA − slow MA, signal = MA(macd), histogram = macd − signal.

new MACD(sourceIndicator: Indicator<number | undefined>, maType: 'sma' | 'ema', fastPeriod: number, slowPeriod: number, signalMaType: 'sma' | 'ema', signalPeriod: number)
Parameter Type Default Meaning
sourceIndicator Indicator<number | undefined> required The number stream (usually closes).
maType 'sma' | 'ema' required Average for the fast and slow lines: 'ema' or 'sma'.
fastPeriod number required Fast average length (12 classically).
slowPeriod number required Slow average length (26).
signalMaType 'sma' | 'ema' required Average for the signal line: 'ema' or 'sma'.
signalPeriod number required Signal average length (9).
  • Input: A number stream.
  • Value (getValue() after hasValue()): MACDValue
    • macd: number | undefined
    • signal: number | undefined
    • histogram: number | undefined
  • Warm-up: macd after slowPeriod candles; signal and histogram after slowPeriod + signalPeriod − 1 candles (34 with 12/26/9). hasValue() is true from the first candle: guard each field.
  • All six arguments are required; there are no defaults.
const symbol = 'BTC';
const candles = manager.getIntervalData(symbol, 60);
const indicator = new MACD(candles.close, 'ema', 12, 26, 'ema', 9);

manager.onIntervalCandle(symbol, 60, () => {
  if (!indicator.hasValue()) return;
  const value = indicator.getValue();
  if (value.macd === undefined || value.signal === undefined || value.histogram === undefined) return;
  manager.debugLog('MACD', value.macd, 'signal', value.signal, 'histogram', value.histogram);
});

Momentum

Momentum: value − value length candles ago.

new Momentum(sourceIndicator: Indicator<number | undefined>, length: number = 10)
Parameter Type Default Meaning
sourceIndicator Indicator<number | undefined> required The number stream.
length number 10 How many candles back to compare.
  • Input: A number stream.
  • Value (getValue() after hasValue()): number | undefined
  • Warm-up: undefined for the first length candles.
const symbol = 'BTC';
const candles = manager.getIntervalData(symbol, 60);
const indicator = new Momentum(candles.close, 10);

manager.onIntervalCandle(symbol, 60, () => {
  if (!indicator.hasValue()) return;
  const value = indicator.getValue();
  if (value === undefined) return;
  manager.debugLog('momentum', value);
});

ROC

Rate of change, in percent: 100 × (value − value length ago) / value length ago.

new ROC(sourceIndicator: Indicator<number | undefined>, length: number = 9)
Parameter Type Default Meaning
sourceIndicator Indicator<number | undefined> required The number stream.
length number 9 How many candles back to compare.
  • Input: A number stream.
  • Value (getValue() after hasValue()): number | undefined
  • Warm-up: undefined for the first length candles.
const symbol = 'BTC';
const candles = manager.getIntervalData(symbol, 60);
const indicator = new ROC(candles.close, 9);

manager.onIntervalCandle(symbol, 60, () => {
  if (!indicator.hasValue()) return;
  const value = indicator.getValue();
  if (value === undefined) return;
  manager.debugLog('ROC %', value);
});

RSI

Relative strength index (Wilder): 100 − 100 / (1 + average gain / average loss) over period, between 0 and 100.

new RSI(sourceIndicator: Indicator<number>, period: number)
Parameter Type Default Meaning
sourceIndicator Indicator<number> required The number stream (usually closes).
period number required The averaging length (14 classically).
  • Input: A number stream whose values are never undefined (candles.close, a PartialIndicator). It does not accept the output of another averaging indicator, which is number | undefined.
  • Value (getValue() after hasValue()): number | undefined
  • Warm-up: undefined for the first period candles; the first value arrives on candle period + 1.
const symbol = 'BTC';
const candles = manager.getIntervalData(symbol, 60);
const indicator = new RSI(candles.close, 14);

manager.onIntervalCandle(symbol, 60, () => {
  if (!indicator.hasValue()) return;
  const value = indicator.getValue();
  if (value === undefined) return;
  if (value < 30) manager.debugLog('oversold', value);
});

RSILambda

A function (price) => rsi that returns the RSI the current candle would have if it closed at price. Use it to find the price that would push RSI through a level.

new RSILambda(sourceIndicator: Indicator<number>, period: number)
Parameter Type Default Meaning
sourceIndicator Indicator<number> required The number stream (usually closes).
period number required The RSI length.
  • Input: A number stream whose values are never undefined.
  • Value (getValue() after hasValue()): (value: number) => number | undefined
  • Warm-up: The function returns undefined during the first period candles.
const symbol = 'BTC';
const candles = manager.getIntervalData(symbol, 60);
const indicator = new RSILambda(candles.close, 14);

manager.onIntervalCandle(symbol, 60, () => {
  if (!indicator.hasValue()) return;
  const value = indicator.getValue();
  const ifDown2pct = value(0.98 * (candles.getValue()?.close ?? 0));
  if (ifDown2pct === undefined) return;
  manager.debugLog('RSI if the next close is 2% lower', ifDown2pct);
});

SqueezeMomentum

Squeeze momentum (LazyBear): detects Bollinger bands inside Keltner channels (squeezeState: 'on') and measures momentum with a linear regression of the price against its range midpoint.

new SqueezeMomentum(candleIndicator: ICandleIndicator, length: number = 20, mult: number = 2.0, lengthKC: number = 20, multKC: number = 1.5, useTrueRange: boolean = true)
Parameter Type Default Meaning
candleIndicator ICandleIndicator required The candle series.
length number 20 Bollinger length.
mult number 2.0 Bollinger multiplier (see note).
lengthKC number 20 Keltner length.
multKC number 1.5 Keltner multiplier.
useTrueRange boolean true Use true range (true) or high − low (false) for the Keltner width.
  • Input: The candle series.
  • Value (getValue() after hasValue()): SqueezeMomentumValue
    • momentum: number | undefined
    • squeezeState: SqueezeState | undefined
    • momentumColor: MomentumColor | undefined
  • SqueezeState is 'on' | 'off' | 'none'.
  • MomentumColor is 'lime' | 'green' | 'red' | 'maroon'.
  • Warm-up: Fields are undefined until the Bollinger, Keltner and regression windows are full (about length + lengthKC candles).
  • Faithful to the original script, the Bollinger width uses multKC rather than mult.
const symbol = 'BTC';
const candles = manager.getIntervalData(symbol, 60);
const indicator = new SqueezeMomentum(candles);

manager.onIntervalCandle(symbol, 60, () => {
  if (!indicator.hasValue()) return;
  const value = indicator.getValue();
  if (value.momentum === undefined || value.squeezeState === undefined) return;
  manager.debugLog('squeeze', value.squeezeState, 'momentum', value.momentum, value.momentumColor);
});

Stochastic

Stochastic oscillator: stochastic = 100 × (close − lowest low) / (highest high − lowest low) over extremePeriod, k = its kPeriod SMA, d = the dPeriod SMA of k.

new Stochastic(sourceIndicator: Indicator<Candle | number | undefined>, extremePeriod: number, kPeriod: number, dPeriod: number)
Parameter Type Default Meaning
sourceIndicator Indicator<Candle | number | undefined> required Candles, or a number stream.
extremePeriod number required High/low look-back (14 classically).
kPeriod number required %K smoothing (3 for the slow stochastic, 1 for the fast one).
dPeriod number required %D smoothing (3).
  • Input: The candle series (uses high/low/close), or a number stream (then the same stream serves as high, low and close).
  • Value (getValue() after hasValue()): StochasticOutput
    • stochastic: number | undefined
    • k: number | undefined
    • d: number | undefined
  • Warm-up: stochastic from the first candle; k after kPeriod candles; d after kPeriod + dPeriod − 1 candles. The range uses a partial window during the first extremePeriod candles.
const symbol = 'BTC';
const candles = manager.getIntervalData(symbol, 60);
const indicator = new Stochastic(candles, 14, 3, 3);

manager.onIntervalCandle(symbol, 60, () => {
  if (!indicator.hasValue()) return;
  const value = indicator.getValue();
  if (value.k === undefined || value.d === undefined) return;
  manager.debugLog('%K', value.k, '%D', value.d);
});

TRIX

TRIX: the one-candle change of a triple EMA of the log price, times 10,000.

new TRIX(sourceIndicator: Indicator<number | undefined>, period: number = 18)
Parameter Type Default Meaning
sourceIndicator Indicator<number | undefined> required The number stream.
period number 18 Length of each of the three EMAs.
  • Input: A number stream.
  • Value (getValue() after hasValue()): number | undefined
  • Warm-up: undefined until the triple EMA has two values (about 3 × period − 1 candles).
const symbol = 'BTC';
const candles = manager.getIntervalData(symbol, 60);
const indicator = new TRIX(candles.close, 18);

manager.onIntervalCandle(symbol, 60, () => {
  if (!indicator.hasValue()) return;
  const value = indicator.getValue();
  if (value === undefined) return;
  manager.debugLog('TRIX', value);
});

WilliamsPercentR

Williams %R: 100 × (close − highest high) / (highest high − lowest low) over length, between −100 and 0.

new WilliamsPercentR(sourceIndicator: Indicator<Candle>, length: number = 14)
Parameter Type Default Meaning
sourceIndicator Indicator<Candle> required The candle series.
length number 14 The look-back length.
  • Input: The candle series.
  • Value (getValue() after hasValue()): number | undefined
  • Warm-up: undefined until length candles have been seen.
const symbol = 'BTC';
const candles = manager.getIntervalData(symbol, 60);
const indicator = new WilliamsPercentR(candles, 14);

manager.onIntervalCandle(symbol, 60, () => {
  if (!indicator.hasValue()) return;
  const value = indicator.getValue();
  if (value === undefined) return;
  manager.debugLog('%R', value);
});
Browse the documentation