QuantScript — cross-tf-strategy-paper.qs
// Cross-Timeframe Trend Strategy — ETHUSD Paper Trading
//
// Uses a daily SMA as a higher-timeframe trend filter:
//   - Daily trend UP → only take longs on 15m pullbacks
//   - Daily trend DOWN → only take shorts on 15m rallies

mode("paper", "Cross-TF Trend Strategy");
candles("ETHUSD", timeframe: "15m", bars: 200, provider: "demotrader");

broker("mybrokeraccount1");
risk(max_position_pct: 20, daily_loss_limit: 3);

// ── Inputs ───────────────────────────────────────────────────────────────────
const dailySmaLen = input.int(20, "Daily SMA Length", minval: 5, maxval: 100, group: "Trend");
const rsiLen      = input.int(14, "RSI Length", minval: 5, maxval: 50, group: "Entry");
const rsiOB       = input.float(65.0, "RSI Overbought", group: "Entry");
const rsiOS       = input.float(35.0, "RSI Oversold", group: "Entry");
const lotSize     = input.float(0.1, "Lot Size", minval: 0.01, step: 0.01, group: "Trade");

// ── Higher timeframe: daily trend filter ─────────────────────────────────────
var dailySma = data.loadCandles("demotrader", "ETHUSD", "1D", (c) => ta.sma(c.close, dailySmaLen));
var dailySmaPrev = data.loadCandles("demotrader", "ETHUSD", "1D", (c) => {
  const sma = ta.sma(c.close, dailySmaLen);
  const prev = sma[1];
  return prev;
});

// ── Strategy ─────────────────────────────────────────────────────────────────
event.onCandle((candle) => {
  const close = candle.close;
  const rsi = ta.rsi(close, rsiLen);

  // Daily trend: SMA rising = bullish, falling = bearish
  let trendUp = false;
  let trendDown = false;
  if (dailySma !== null && dailySmaPrev !== null) {
    trendUp = dailySma > dailySmaPrev;
    trendDown = dailySma < dailySmaPrev;
  }

  chart.plot(close, "ETH Close");
  chart.plot(dailySma, "ETH 1D SMA", "#FF9800");

  chart.pane("oscillator");
  chart.plot(rsi, "RSI", "#9C27B0");
  chart.hline(rsiOB, "Overbought", "#f85149");
  chart.hline(rsiOS, "Oversold", "#3fb950");

  // ── Trade logic (only on live/last candle) ───────────────────────────────
  if (!candle.isLive) return;
  if (rsi === null) return;

  let symbol = frame.symbol ?? "ETHUSD";
  let hasLong = false, hasShort = false;
  for (const pos of trade.positions()) {
    if (pos.side == "long") hasLong = true;
    if (pos.side == "short") hasShort = true;
  }

  let wantLong  = trendUp && rsi < rsiOS;
  let wantShort = trendDown && rsi > rsiOB;

  // Exit on trend flip
  if (hasLong && trendDown) {
    for (const pos of trade.positions()) {
      if (pos.side == "long") trade.closePosition({ positionId: pos.id });
    }
  }
  if (hasShort && trendUp) {
    for (const pos of trade.positions()) {
      if (pos.side == "short") trade.closePosition({ positionId: pos.id });
    }
  }

  // Entries
  if (wantLong && !hasLong) {
    trade.buy({
      symbol, qty: lotSize,
      reason: "Daily uptrend + RSI oversold (" + rsi.toFixed(1) + ")",
      tags: ["cross-tf", "trend_pullback"]
    });
  }
  if (wantShort && !hasShort) {
    trade.sell({
      symbol, qty: lotSize,
      reason: "Daily downtrend + RSI overbought (" + rsi.toFixed(1) + ")",
      tags: ["cross-tf", "trend_pullback"]
    });
  }
});

← Back to all examples