QuantScript backtest example: SMA Trend Pullback Strategy for XAUUSD 1-hour. Backtests a trend-following strategy with RSI entries and ATR-based risk management.
// ─────────────────────────────────────────────────────────────────────────────
// SMA Trend Pullback Strategy — XAUUSD 1H (Backtest Mode)
// ─────────────────────────────────────────────────────────────────────────────
//
// Backtests a trend-following strategy on XAUUSD 1-hour candles from
// June 1, 2026 to July 1, 2026 using the DemoTrader data provider.
//
// Strategy logic:
// 1. 50-period SMA defines the trend direction.
// 2. RSI(14) identifies pullback entries within the trend.
// 3. Long when price is above SMA and RSI crosses above 50 (bullish midline cross).
// 4. Short when price is below SMA and RSI crosses below 50 (bearish midline cross).
// 5. ATR-based stop loss and take profit for risk management.
//
// ─────────────────────────────────────────────────────────────────────────────
mode("backtest", "XAUUSD SMA Pullback");
data("XAUUSD", timeframe: "1h", bars: 720, from: 1780272000, to: 1782864000, provider: "demotrader");
broker("sim", {
initial_capital: 100000,
commission: 0.05,
commission_type: "percent"
});
broker("mybrokeraccount1");
risk(max_position_pct: 25, daily_loss_limit: 3);
// ── Indicator Parameters ───────────────────────────────────────────────────
const smaLen = input.int(50, "SMA Length", minval: 10, maxval: 200, group: "Trend");
const rsiLen = input.int(14, "RSI Length", minval: 5, maxval: 50, group: "Entry");
const atrLen = input.int(14, "ATR Length", minval: 5, maxval: 50, group: "Risk");
const rsiBullZone = input.float(50.0, "RSI Bullish Cross Level", group: "Entry");
const rsiBearZone = input.float(50.0, "RSI Bearish Cross Level", group: "Entry");
const slMult = input.float(1.5, "Stop Loss × ATR", minval: 0.5, maxval: 5.0, step: 0.1, group: "Risk");
const tpMult = input.float(2.5, "Take Profit × ATR", minval: 1.0, maxval: 10.0, step: 0.5, group: "Risk");
// ── State ──────────────────────────────────────────────────────────────────
var prevRsi = 50.0;
event.onCandle((candle) => {
// ── Warmup ────────────────────────────────────────────────────────────
if (candleIndex < Math.max(smaLen, rsiLen, atrLen)) { return; }
let close = candle.close;
let high = candle.high;
let low = candle.low;
// ── Indicators ────────────────────────────────────────────────────────
let smaVal = ta.sma(close, smaLen);
let rsiVal = ta.rsi(close, rsiLen);
let atrVal = ta.atr(high, low, close, atrLen);
// ── Debug: log indicator values every 50 bars ─────────────────────────
if (candleIndex % 50 == 0) {
console.log("[bar " + candleIndex + "] close=" + close + " sma=" + smaVal + " rsi=" + rsiVal + " prevRsi=" + prevRsi + " atr=" + atrVal);
}
// ── Chart Plots ───────────────────────────────────────────────────────
chart.plot(smaVal, "SMA(" + smaLen + ")", "#FF9800");
// ── Entry Signals ─────────────────────────────────────────────────────
let bullTrend = close > smaVal;
let rsiRecover = rsiVal > rsiBullZone && prevRsi <= rsiBullZone;
let bearTrend = close < smaVal;
let rsiDrop = rsiVal < rsiBearZone && prevRsi >= rsiBearZone;
// ── Long Entry ────────────────────────────────────────────────────────
if (bullTrend && rsiRecover && trade.positions().length < 2) {
let sl = close - atrVal * slMult;
let tp = close + atrVal * tpMult;
trade.buy({
symbol: "XAUUSD", qty: 1, comment: "Long",
reason: "SMA trend long: RSI crossed above " + rsiBullZone,
context: { close: close, rsi: rsiVal, sma: smaVal },
tags: ["trend", "long_entry"]
});
for (const pos of trade.positions()) {
if (pos.comment == "Long" && pos.isOpen) {
trade.modifyPosition({ positionId: pos.id, stopLoss: sl, takeProfit: tp });
}
}
}
// ── Short Entry ───────────────────────────────────────────────────────
if (bearTrend && rsiDrop && trade.positions().length < 2) {
let sl = close + atrVal * slMult;
let tp = close - atrVal * tpMult;
trade.sell({
symbol: "XAUUSD", qty: 1, comment: "Short",
reason: "SMA trend short: RSI crossed below " + rsiBearZone,
context: { close: close, rsi: rsiVal, sma: smaVal },
tags: ["trend", "short_entry"]
});
for (const pos of trade.positions()) {
if (pos.comment == "Short" && pos.isOpen) {
trade.modifyPosition({ positionId: pos.id, stopLoss: sl, takeProfit: tp });
}
}
}
// ── Exit on SMA cross ─────────────────────────────────────────────────
if (close < smaVal) {
for (const pos of trade.positions()) {
if (pos.comment == "Long" && pos.isOpen) {
trade.closePosition({ positionId: pos.id });
}
}
}
if (close > smaVal) {
for (const pos of trade.positions()) {
if (pos.comment == "Short" && pos.isOpen) {
trade.closePosition({ positionId: pos.id });
}
}
}
prevRsi = rsiVal;
});