Back to Strategies

SMA Crossover Trend Follower

Code Strategy by 0xsDp5JzBDn·2026-05-12

// Initialize RSI with a period of 14
// Note: In a real persistent strategy, you might want to store the RSI instance 
// or feed it historical data via $.Storage if the platform doesn't persist JS objects between runs.
// However, for this simple example, we assume the platform feeds recent prices or we just react to current triggers.
// *Correction based on docs*: The docs show updating values sequentially. 
// Since strategies run per event, we likely need to store previous prices or the indicator state in $.Storage 
// if the environment doesn't maintain memory between runs. 
// Let's assume a stateless run where we might need to Fetch history or just use a simple price check if RSI state isn't persistent.
// BUT, the docs example implies: 
// const rsi = new $.Ta.RSI(14);
// rsi.update(30000); ...
// If the environment resets every run, we can't build a proper RSI without history.
// Let's create a more robust example that uses $.Storage to save the indicator state if possible, 
// OR stick to a simpler price-based strategy if RSI state persistence is complex.

// Actually, looking at the docs, $.Ta is available. Let's assume we can store the serialized state or 
// the platform handles the context. If not, a simple Moving Average Crossover is easier to implement statelessly 
// if we store the last MA value.

// Let's do a Simple Moving Average (SMA) Crossover using Storage for state.

const PERIOD = 10;
const FAST_PERIOD = 5;
const SLOW_PERIOD = 15;

async function main() {
  // 1. Get current price
  if (!$.Price || !$.Price.price) {
    return; // No price data available
  }
  
  const currentPrice = $.Price.price;

  // 2. Retrieve state from storage
  let state = await $.Storage.get();
  if (!state) {
    state = {
      fastSMA: null,
      slowSMA: null,
      prices: [] // Store last N prices to calculate SMA manually if needed
    };
  }

  // 3. Update price history
  state.prices.push(currentPrice);
  if (state.prices.length > SLOW_PERIOD) {
    state.prices.shift(); // Keep only the last SLOW_PERIOD prices
  }

  // 4. Calculate SMAs if we have enough data
  let fastSMA = null;
  let slowSMA = null;

  if (state.prices.length >= FAST_PERIOD) {
    const sumFast = state.prices.slice(-FAST_PERIOD).reduce((a, b) => a + b, 0);
    fastSMA = sumFast / FAST_PERIOD;
  }

  if (state.prices.length >= SLOW_PERIOD) {
    const sumSlow = state.prices.reduce((a, b) => a + b, 0);
    slowSMA = sumSlow / SLOW_PERIOD;
  }

  // 5. Trading Logic
  let actionTaken = false;

  // Golden Cross: Fast SMA crosses above Slow SMA -> LONG
  if (fastSMA && slowSMA && state.fastSMA && state.slowSMA) {
    if (state.fastSMA <= state.slowSMA && fastSMA > slowSMA) {
      // Only enter if not already in a position (or if last action was EXIT)
      if ($.Strategy.lastTrigger.action !== $.Action.LONG) {
         await $.Strategy.action($.Action.LONG, { id: "GoldenCross" });
         actionTaken = true;
      }
    }
    // Death Cross: Fast SMA crosses below Slow SMA -> EXIT
    else if (state.fastSMA >= state.slowSMA && fastSMA < slowSMA) {
       if ($.Strategy.lastTrigger.action !== $.Action.EXIT) {
         await $.Strategy.action($.Action.EXIT, { id: "DeathCross" });
         actionTaken = true;
       }
    }
  }

  // 6. Save state for next run
  state.fastSMA = fastSMA;
  state.slowSMA = slowSMA;
  await $.Storage.set(state);
}

main();