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
| Member | Meaning |
|---|---|
| ctx.i, ctx.n, ctx.time, ctx.symbols | Bar 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.portfolio | equity, cash, available, marginUsed, positions |
| ctx.position(sym, market?) | Netted position for a symbol on spot or perp |
| ctx.order.market / limit / stop | Place orders; qty or notional; market: 'spot' | 'perp'; reduceOnly; tag; ocoGroup |
| ctx.order.cancel / cancelAll / closeAll | Order 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.