Strategy Lifecycle
function define(ctx)
Declare optimizable parameters with type, default, min, max
Called once
function init(ctx)
Register indicators using parameters from define()
Called once
function onBar(ctx, i)
Execute trading logic for each candle
Called N times · ↻ loop
Understanding when your code runs is essential for writing correct strategies.
The Three Functions
Every QSL strategy has exactly three functions:
| Function | Purpose | Called |
|---|---|---|
define(ctx) | Declare parameters | Once when strategy loads |
init(ctx) | Declare indicators, initialize state | Once before backtest |
onBar(ctx, i) | Trading logic | Once per candle |
Execution Order
1. define(ctx) — Parameters registered 2. init(ctx) — Indicators declared, state initialized 3. [Engine computes all indicators] 4. For each bar i: a. onBar(ctx, i) — Your logic runs with pre-computed data b. Orders queued 5. [Engine processes all orders with fill rules]
Lifecycle Functions
define(ctx)
Declare your strategy parameters. Called once when the strategy loads.
function define(ctx) { ctx.param('fastLength', { type: 'int', label: 'Fast EMA', default: 9, min: 5, max: 50, optimize: true, group: 'Trend' }); ctx.param('slowLength', { type: 'int', label: 'Slow EMA', default: 21, min: 10, max: 200, optimize: true, group: 'Trend' }); }
init(ctx)
Declare indicators and initialize state. Called once before the backtest starts.
function init(ctx) { // Declare indicators (computed by engine, not your code) ctx.indicator('fastEma', 'EMA', { period: ctx.p.fastLength, source: 'close' }); ctx.indicator('slowEma', 'EMA', { period: ctx.p.slowLength, source: 'close' }); // Initialize state ctx.state.tradeCount = 0; ctx.state.lastSignalBar = -1; }
Key insight: Indicators are computed by the engine on the host side. By the time onBar runs, all indicator values for all bars are already calculated.
onBar(ctx, i)
Your trading logic. Called for every bar in the dataset.
function onBar(ctx, i) { const fast = ctx.ind.fastEma[i]; const slow = ctx.ind.slowEma[i]; // Skip if indicators not ready if (q.isNaN(fast) || q.isNaN(slow)) return; // Check for crossover if (q.crossOver(ctx.ind.fastEma, ctx.ind.slowEma, i)) { ctx.order.market('ASSET', 1, { signal: 'buy' }); ctx.state.tradeCount++; } if (q.crossUnder(ctx.ind.fastEma, ctx.ind.slowEma, i)) { ctx.order.close('ASSET', { signal: 'sell' }); } }
Important rules:
onBarruns after the bar closes- You see the bar's OHLCV data, but cannot act within it
- Orders placed here execute on the next bar's open
Accessing Data in onBar
function onBar(ctx, i) { // Parameters const period = ctx.p.period; // Indicators (pre-computed arrays) const ema = ctx.ind.ema[i]; const macd = ctx.ind.macd.macd[i]; // Multi-output indicators // Price series const close = ctx.series.close[i]; const prevClose = ctx.series.close[i - 1]; const high = ctx.series.high[i]; // Position info const pos = ctx.position('ASSET'); if (pos.qty > 0) { /* in position */ } // Mutable state ctx.state.lastSignalBar = i; }
Stateless vs Stateful
Strategies can be stateless (logic based only on current bar and indicators) or stateful (maintaining custom state across bars).
Stateless Example
function onBar(ctx, i) { // Only uses current values — no memory needed if (ctx.ind.rsi[i] < 30 && ctx.position('ASSET').qty === 0) { ctx.order.market('ASSET', 1, { signal: 'buy' }); } }
Stateful Example
function onBar(ctx, i) { const rsi = ctx.ind.rsi[i]; // Track consecutive oversold bars if (rsi < 30) { ctx.state.oversoldCount = (ctx.state.oversoldCount || 0) + 1; } else { ctx.state.oversoldCount = 0; } // Enter after 3 consecutive oversold bars if (ctx.state.oversoldCount >= 3 && ctx.position('ASSET').qty === 0) { ctx.order.market('ASSET', 1, { signal: 'buy', reason: 'oversold_3bars' }); } }
Common Mistakes
- Assuming orders fill immediately — They don't. Orders fill on the next bar.
- Computing indicators in onBar — Use
ctx.indicator()ininit()instead. - Forgetting to check
q.isNaN()— Indicators have warm-up periods. - Using
Math.random()— Non-deterministic logic breaks reproducibility.
Related
- define() Reference — Parameter declaration
- init() Reference — Indicator declaration
- onBar() Reference — Trading logic
- Order Execution Model