QuantScript paper trading strategy: uses a daily SMA trend filter with 15m RSI entries. Demonstrates data.loadCandles() with callback for cross-timeframe analysis in a trading context.
// 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"]
});
}
});