Indicators

Technical indicators are already in scope in agent code. You construct them with new, at the root, and you read their values inside callbacks. You never import them.

Construct at the root, read in a callback

const symbol = 'BTC';
const candles = manager.getIntervalData(symbol, 60); // hourly candles
const ema = new EMA(new PartialIndicator(candles, 'close'), 20);

manager.onIntervalCandle(symbol, 60, () => {
  if (!ema.hasValue()) return;        // guard first
  const value = ema.getValue();
  if (value === undefined) return;    // many indicators stay `| undefined`
  manager.debugLog(`EMA20 is ${value.toFixed(2)}`);
});

Two building blocks appear constantly:

  • PartialIndicator picks one field of a candle series. Most numeric indicators (EMA, SMA, RSI, and so on) take a stream of numbers, not candles, so you wrap the series: new RSI(new PartialIndicator(candles, 'close'), 14).
  • History keeps the last N values of any indicator so you can look back: new History(candles, 4) gives the four most recent candles, oldest first, newest last.

Guarding values

getValue() returns nothing until an indicator has been fed enough data. The rules:

  • Always call hasValue() before getValue(). It is a type guard, so no non-null assertion is needed after it.
  • Many single-number indicators are still typed as possibly undefined even after hasValue(), so add a value === undefined check before doing arithmetic.
  • For History, hasValue() only means the series has started, not that the buffer is full. Return early while the array is shorter than the period you need, then read an element into a constant and check it before use.
  • Composite indicators (for example MACD, Stochastic) return an object whose fields are each independently possibly undefined. Read the value into one constant, then guard exactly the fields you use.

Common families

The catalog has more than 70 classes. The Indicator reference gives each one's exact constructor, input kind, parameters and defaults, value type and fields, warm-up length and a working snippet, grouped by family:

  • Building blocks: PartialIndicator, LambdaIndicator, DerivedIndicator, ManualIndicator, History, Crossover, Extreme, Percentile.
  • Moving averages and trend: SMA, EMA, WMA, RMA, HullMovingAverage, KAMA, VIDYA, VWAP, SuperTrend, SAR, Ichimoku, ADX, WilliamsAlligator.
  • Momentum and oscillators: RSI, MACD, Stochastic, CCI, Momentum, ROC, WilliamsPercentR, TRIX, SqueezeMomentum, Divergences.
  • Volatility, bands and ranges: TR (smooth it with RMA for an ATR), Bollinger, BBW, KeltnerChannels, DC, Chop.
  • Volume: OnBalanceVolume, MFI, ChaikinMoneyFlow, VolumeProfile, VWMA.
  • Market structure and patterns: OrderBlocks, FVG, MarketStructure (break of structure and change of character), Swings, SupportResistance, ZigZag, PivotPoints, Engulfing, Heikinashi, Renko and RenkoAtr.

Three things to check in a signature before using a class:

  • The input kind. ICandleIndicator or Indicator<Candle> takes the candle series itself; Indicator<number> takes a number stream that is never undefined (candles.close); Indicator<number | undefined> also accepts another indicator's output. new RSI(new EMA(candles.close, 10), 14) does not compile, because RSI wants Indicator<number>.
  • Required parameters. Some classes have no defaults: new MACD(candles.close, 'ema', 12, 26, 'ema', 9) needs all six arguments.
  • The value type. Numbers, objects with fields that are each possibly undefined (MACD, Bollinger, Stochastic), or labels (Crossover returns 'firstUp' | 'secondUp' | null, never a number).

If the concept you need is not in the catalog, compute it in code from candles (with History, DerivedIndicator or LambdaIndicator) rather than inventing a class name: an unknown class fails validation.

A worked reading

An ATR-style volatility read from true range, then a stop distance:

const symbol = 'BTC';
const candles = manager.getIntervalData(symbol, 240); // 4h
const tr = new TR(candles);
const atr = new RMA(tr, 14); // a smoothed average of true range

manager.onIntervalCandle(symbol, 240, () => {
  if (!atr.hasValue()) return;
  const atrValue = atr.getValue();
  const price = manager.getCurrentPrice(symbol);
  if (atrValue === undefined || price === undefined) return;
  const stopPrice = price - 3 * atrValue; // an absolute price, not a distance
  manager.debugLog(`stop would sit at ${stopPrice.toFixed(2)}`);
});
Browse the documentation