DocsBest Practices

Warm-up Periods

Indicators like EMA, RSI, and ATR need a minimum number of bars before they produce valid output. The warm-up period prevents your strategy from trading on incomplete data.

The Problem

On the first few bars, indicators return NaN because they haven't accumulated enough data:

Bar 0:  EMA(20) = NaN
Bar 1:  EMA(20) = NaN
...
Bar 19: EMA(20) = NaN
Bar 20: EMA(20) = 48523.45  (first valid value)

If your strategy doesn't handle this, it will make decisions based on invalid data.

How warmupBars Works

The warmupBars parameter tells the engine to skip the first N bars of onBar() execution entirely. Your strategy code simply won't run until bar N.

// In your strategy parameters { warmupBars: 50 // Skip the first 50 bars }

The engine starts the onBar loop at bar warmupBars instead of bar 0. This is more efficient than checking q.isNaN() on every bar — the engine skips the loop iterations entirely.

Calculating the Right Value

Your warm-up should be at least as long as the longest indicator period, plus a safety margin:

IndicatorPeriodWarm-up Needed
EMA(20)2020 bars
RSI(14)1415 bars
BBANDS(20)2020 bars
MACD(12,26,9)26+935 bars
ATR(14)1415 bars

Rule of thumb: Take the largest indicator period and add 1.

// If your strategy uses EMA(50) and RSI(14): // Warm-up = max(50, 14) + 1 = 51 { warmupBars: 51 }

Defensive Coding: q.isNaN()

Even with warmupBars set, it's good practice to guard against NaN values. This protects against edge cases and makes your strategy robust:

function onBar(ctx, i) { const ema = ctx.ind.ema[i]; const rsi = ctx.ind.rsi[i]; // Early return if any indicator isn't ready if (q.isNaN(ema) || q.isNaN(rsi)) return; // Safe to use indicators here if (rsi < 30 && q.crossOver(ctx.series.close, ctx.ind.ema, i)) { ctx.order.market('ASSET', 1, { signal: 'buy' }); } }

Chained Indicators

When one indicator depends on another's output, warm-up compounds:

function init(ctx) { ctx.addIndicator('ema', 'EMA', { period: 50 }); ctx.addIndicator('rsi', 'RSI', { period: 14 }); // If you use EMA of RSI, you'd need 50 + 14 = 64 bars }

Impact on Backtests

  • Warm-up bars consume data but don't generate trades
  • A 200-bar warm-up on 500 bars of data leaves only 300 bars for actual trading
  • Shorter timeframes (1h) provide more bars, so warm-up is less impactful
  • Longer timeframes (1d) with large warm-ups may leave too few bars — use adequate date ranges

Common Mistakes

1. Setting warmupBars too low

// Wrong: EMA(200) needs at least 200 bars { warmupBars: 50 } // Correct { warmupBars: 201 }

2. Not accounting for all indicators

// Uses EMA(20), RSI(14), and BBANDS(50) // warmupBars should be max(20, 14, 50) + 1 = 51

3. Forgetting compound indicators MACD uses a 26-period EMA and a 9-period signal line, so it needs at least 35 bars.

Related

warmupwarm-upindicatorNaNbarsperiodinitialization