Documentation/Workflow canvas engine

Code strategy node

JavaScript or Python strategy on the engine's order API: limit/stop orders, multi-leg, rebalancing.

JavaScript or Python strategy on the engine's order API: limit/stop orders, multi-leg, rebalancing.

Where it sits in the pipeline

This node accepts one upstream connection and passes its output downstream. Connect it between the data source and the trading logic block.

A code strategy replaces the Trading logic block. It implements the same interface the visual nodes compile to, so anything the engine supports — limit and stop orders, several symbols, spot and perpetual legs in one strategy, portfolio rebalancing — is available from code.

Lifecycle

  • onBar(ctx) / on_bar(ctx): once per closed bar. Market orders placed here fill at the next bar's open.
  • init(ctx), onFill(fill, ctx), onEnd(ctx): optional.
  • ctx.state is a dict/object that persists across bars.

JavaScript

function onBar(ctx) {
  for (const sym of ctx.symbols) {
    const px = ctx.close(sym), ma = ctx.value(sym, 'ma20');
    const pos = ctx.position(sym);
    if (!pos && px > ma) {
      ctx.order.market({ symbol: sym, side: 'buy', notional: ctx.portfolio.equity * 0.5 });
      ctx.order.stop({ symbol: sym, side: 'sell', notional: ctx.portfolio.equity * 0.5, stopPrice: px * 0.95, reduceOnly: true, tag: 'stop_loss' });
    } else if (pos && px < ma) {
      ctx.order.market({ symbol: sym, side: 'sell', qty: pos.qty, reduceOnly: true });
    }
  }
}

Python

def on_bar(ctx):
    for sym in ctx.symbols:
        px, ma = ctx.close(sym), ctx.value(sym, 'ma20')
        pos = ctx.position(sym)
        if pos is None and px > ma:
            ctx.order.market(sym, 'buy', notional=ctx.portfolio['equity'] * 0.5)
        elif pos is not None and px < ma:
            ctx.order.market(sym, 'sell', qty=pos['qty'], reduce_only=True)

Context API

MemberMeaning
ctx.i, ctx.n, ctx.time, ctx.symbolsBar index, bar count, ISO time, symbol list
ctx.close(sym, offset) ctx.value(sym, col, offset)Price / column value `offset` bars back (never the future)
ctx.history(sym, col, n)Last n values, oldest first
ctx.portfolioequity, cash, available, marginUsed, positions
ctx.position(sym, market?)Netted position for a symbol on spot or perp
ctx.order.market / limit / stopPlace orders; qty or notional; market: 'spot' | 'perp'; reduceOnly; tag; ocoGroup
ctx.order.cancel / cancelAll / closeAllOrder and position management
ctx.order.targetWeights({ sym: w })Portfolio rebalance; w is a fraction of equity, negative = short
ctx.log(msg) ctx.halt(reason)Write to the run log / stop the run
Spot and perp legs share the primary market's candles for pricing; the engine keeps separate positions, charges funding on the perp leg and margin-checks it.