Indicators API

DocumentationWriting strategies

Indicators API

Indicators are computed causally by the engine, drawn on the chart, and readable by a strategy. The four built ins below have their own short calls; every indicator of the library is read with ctx.ind; and anything else is a custom formula or a plugin.

Built in (streamed via ctx)

  • SMA simple moving average. Params: period, source. ctx.sma(period).
  • EMA exponential moving average. Params: period, source. ctx.ema(period).
  • RSI relative strength index, 0 to 100. Params: period, source. ctx.rsi(period).
  • ATR average true range. Params: period. ctx.atr(period).

The library (ctx.ind)

Every indicator of the library, the ones the chart offers, is read by its id with ctx.ind(id, **inputs). Inputs go by name, and one left out takes its default. An indicator with one line gives a number, or None until warmed up. One with several lines gives a reading: each line by its name, None while that line warms up, and .value for the first. A reading is never None itself. Indicator library lists every one, with what it measures, its inputs and its lines.

pythonrsi = ctx.ind("rsi", length=14)          # a number, or None
upper = ctx.ind("bb", length=20).upper   # one line of several
  • MACD ctx.ind("macd"), lines macd, signal and hist.
  • Bollinger Bands ctx.ind("bb"), lines upper, middle and lower.
  • Stochastic ctx.ind("stoch"), lines k and d.
  • ADX ctx.ind("dmi"), lines adx, plus and minus.

Each is streamed like the built ins: fed every bar released so far on its first read, then one bar at a time, so a first read late in the run gives what an early one would have. An id or an input the library does not have stops the run with a message naming it. A strategy that reads a library indicator exports to TradingView, MetaTrader 5 and cTrader: each file carries the indicator as code of its own, computed as the engine computes it, never the platform's built in. To MetaTrader 5 and cTrader it goes the way Pine and blocks do, compiled and run beside the original first, so a strategy somebody else wrote asks before it runs.

Market structure (ctx.market_structure)

The Market structure group holds the ICT elements and the Fibonacci tools: swings with breaks of structure (BoS) and changes of character (CHoCH) structure, fair value gaps fvg, inversion fair value gaps ifvg, order blocks order_block, breaker blocks breaker_block, fib_retracement, fib_extension, premium_discount and the optimal trade entry ote; then liquidity: sweeps liquidity_sweep, equal highs and lows equal_levels, untaken swing highs and lows liquidity, balanced price ranges bpr, mitigation_block, rejection_block and displacement; and the ICT clock on New York time: killzones, midnight_open and the opening gaps opening_gaps (NWOG, NDOG). Each reads with ctx.ind like any other indicator, and ctx.market_structure(**inputs) reads all nineteen in one reading.

pythonms = ctx.market_structure(length=3, min_size=0.25)
if ms.choch == 1 and ms.ote.inside == 1:      # the trend just turned up, price in the OTE
    ctx.signal("LONG", sl_pips=20, tp_pips=40)
target = ms.fib_extension.level_1618
gap = ms.fvg.bull_top                         # the nearest open bullish gap, or None
if ms.killzones.london_open == 1 and ms.liquidity_sweep.down == 1:
    ctx.signal("LONG", sl_pips=15, tp_pips=30)  # the Asian low swept in London
  • The top level has the lines of structure: trend (+1 or -1, 0 before the first break), bos and choch (+1 or -1 on the bar of the break, else 0), swing_high, swing_low, bos_level and choch_level.
  • The parts are structure, fvg, ifvg, order_block, breaker_block, fib (the retracement), fib_extension, premium_discount, ote, liquidity_sweep, equal_levels, liquidity, bpr, mitigation_block, rejection_block, displacement, killzones, midnight_open and opening_gaps, each the reading ctx.ind gives for that element, with its lines by name.
  • The inputs are the group's own, one name for one setting in every element: length (swing bars each side), break_on ("close" or "wick"), zone ("wick" or "body"), atr_length, min_size (in ATRs), fill ("full" or "half"), max_zones (zones kept per side), tolerance (equal levels, in ATRs), min_wick (rejection blocks, a share of the candle), min_body (displacement, in ATRs) and days ("mon_fri" or "every_day"). Each element takes the ones it has.
  • Nothing repaints: a swing shows on the bar that confirms it, length bars after its extreme, and a zone on the bar that makes it.

It is the same streams as the nineteen ctx.ind calls, not a second implementation, so it runs on the server and in the Rust core like them. C# reads it with Indicators.MarketStructure(new { length = 3 }) (ms.Trend, ms.Fvg["bull_top"]), Rust with ctx.market_structure(&[("length", 3.into())]) (ms.trend, ms.fvg["bull_top"]). In blocks the nineteen are the Market structure group of the indicator blocks.

Custom formulas

A custom indicator carries a formula the engine compiles and computes causally. The formula language supports: inputs open high low close volume and hl2 hlc3 ohlc4; series access closes[i] highs[i] lows[i] opens[i] where i is bars back (0 is current); and the functions sma(p) ema(p) rsi(p) atr(p) plus abs min max sqrt log. End in an expression, or assign to result.

textfast = ema(12)
slow = ema(26)
fast - slow

Any indicator of the library is one call away: ind(id, output, inputs) reads one line of it at this bar, None while it warms up. The id and the line are in quotes, the inputs go by name, and an input left out takes its default. An indicator with a single line needs no line name.

textwidth = ind("bb", "upper", length=20, mult=2) - ind("bb", "lower", length=20, mult=2)
width / close * 100 if ind("rsi", length=7) > 50 else 0

The id, the line and each input are written out, a number, text in quotes or True or False, never a name or a calculation, so Validate finds an unknown id, line or input, or a value out of range, without a run. An indicator with several lines, such as ind("bb"), is refused with the names of its lines. A formula reads at most 64 different indicators.

Read a custom indicator from a strategy with ctx.custom("name").

OrionQuant