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
- RSI Mean Reversion — Counter-trend example
- Helper Functions — q.crossOver(), q.crossUnder()
Related
exampleemacrossovertrendmoving averageqsl