DocsExamples

EMA Crossover Strategy

A classic trend-following strategy that trades EMA crossovers.

Code

/** * EMA Crossover Strategy * * Buys on bullish crossover (fast EMA crosses above slow EMA) * Sells on bearish crossover (fast EMA crosses below slow EMA) */ function define(ctx) { ctx.param('fastLength', { type: 'int', label: 'Fast EMA Period', default: 9, min: 5, max: 50, optimize: true, group: 'Trend' }); ctx.param('slowLength', { type: 'int', label: 'Slow EMA Period', default: 21, min: 10, max: 200, optimize: true, group: 'Trend' }); } function init(ctx) { ctx.indicator('fastEma', 'EMA', { period: ctx.p.fastLength, source: 'close' }); ctx.indicator('slowEma', 'EMA', { period: ctx.p.slowLength, source: 'close' }); } function onBar(ctx, i) { const fast = ctx.ind.fastEma; const slow = ctx.ind.slowEma; // Skip until indicators are ready if (q.isNaN(fast[i]) || q.isNaN(slow[i])) return; const pos = ctx.position('ASSET'); // Bullish crossover: buy if (q.crossOver(fast, slow, i) && pos.qty <= 0) { if (pos.qty < 0) { ctx.order.close('ASSET', { signal: 'cover' }); } ctx.order.market('ASSET', 1, { signal: 'buy', reason: 'ema_crossover' }); } // Bearish crossover: sell if (q.crossUnder(fast, slow, i) && pos.qty >= 0) { if (pos.qty > 0) { ctx.order.close('ASSET', { signal: 'sell' }); } ctx.order.market('ASSET', -1, { signal: 'short', reason: 'ema_crossunder' }); } }

Key Concepts

Using q.crossOver() / q.crossUnder()

The q namespace provides crossover detection:

// Bullish: fast crosses above slow if (q.crossOver(ctx.ind.fastEma, ctx.ind.slowEma, i)) { ctx.order.market('ASSET', 1, { signal: 'buy' }); } // Bearish: fast crosses below slow if (q.crossUnder(ctx.ind.fastEma, ctx.ind.slowEma, i)) { ctx.order.close('ASSET', { signal: 'sell' }); }

Optimization-Ready Parameters

The optimize: true flag marks parameters for optimization:

ctx.param('fastLength', { type: 'int', default: 9, min: 5, max: 50, optimize: true // Will be included in optimization runs });

Parameter Groups

Group related parameters in the UI:

ctx.param('fastLength', { ..., group: 'Trend' }); ctx.param('slowLength', { ..., group: 'Trend' }); ctx.param('stopLoss', { ..., group: 'Risk' });

Long-Only Variant

If you only want to go long (no shorting):

function onBar(ctx, i) { const fast = ctx.ind.fastEma; const slow = ctx.ind.slowEma; if (q.isNaN(fast[i]) || q.isNaN(slow[i])) return; const pos = ctx.position('ASSET'); // Buy on bullish cross if (q.crossOver(fast, slow, i) && pos.qty === 0) { ctx.order.market('ASSET', 1, { signal: 'buy' }); } // Sell on bearish cross (no shorting) if (q.crossUnder(fast, slow, i) && pos.qty > 0) { ctx.order.close('ASSET', { signal: 'sell' }); } }

Performance Notes

  • Works best in trending markets
  • Whipsaws in ranging markets
  • Consider adding filters (ATR, ADX, volume) for robustness

Related

exampleemacrossovertrendmoving averageqsl