Back to Strategies

EMA Crossover

Code Strategy by 0xsDp5JzBDn·2026-04-24

// EMA Crossover Strategy
// Fast EMA crosses above Slow EMA → LONG
// Fast EMA crosses below Slow EMA → EXIT

const FAST_EMA = 9;
const SLOW_EMA = 21;

const S = await $.Storage.get() || {
  prices: []
};

S.prices.push($.Price.price);

// Only keep what we need
if (S.prices.length > SLOW_EMA + 2) {
  S.prices = S.prices.slice(-(SLOW_EMA + 2));
}

// Need enough data for the slow EMA
if (S.prices.length < SLOW_EMA + 1) {
  await $.Storage.set(S);
  return;
}

// Build both EMAs from stored prices
const fastEma = new $.Ta.EMA(FAST_EMA);
const slowEma = new $.Ta.EMA(SLOW_EMA);

// Feed all but the last price (previous candle)
const prevPrices = S.prices.slice(0, -1);
for (const p of prevPrices) {
  fastEma.update(p);
  slowEma.update(p);
}

// Capture previous crossover state
const prevFast = fastEma.isStable ? fastEma.getResult().valueOf() : null;
const prevSlow = slowEma.isStable ? slowEma.getResult().valueOf() : null;

// Now feed the latest price (current candle)
const lastPrice = S.prices[S.prices.length - 1];
fastEma.update(lastPrice);
slowEma.update(lastPrice);

if (!fastEma.isStable || !slowEma.isStable) {
  await $.Storage.set(S);
  return;
}

const currFast = fastEma.getResult().valueOf();
const currSlow = slowEma.getResult().valueOf();
const lastAction = $.Strategy.lastTrigger.action;

// Detect crossover: fast crossed ABOVE slow
const crossedUp = prevFast !== null && prevFast <= prevSlow && currFast > currSlow;

// Detect crossunder: fast crossed BELOW slow
const crossedDown = prevFast !== null && prevFast >= prevSlow && currFast < currSlow;

if (crossedUp && lastAction !== $.Action.LONG) {
  await $.Strategy.action($.Action.LONG);

} else if (crossedDown && lastAction === $.Action.LONG) {
  await $.Strategy.action($.Action.EXIT);
}

await $.Storage.set(S);