This is a **mean-reversion scalping strategy** optimized for **XAUUSD on the 1-minute (or the 5-minute) timeframe**. It combines a classic **RSI(14)** oscillator with an optional **200-period EMA trend filter** to identify short-term overextended conditions in gold price action, while enforcing fixed, conservative exits to maintain strict risk control.
### Core Logic & Why This Combination:
The strategy enters counter-trend when RSI reaches extreme levels (below 30 for longs, above 70 for shorts), capturing quick snap-backs that frequently occur in gold's intraday volatility.
- **RSI(14)** acts as the primary momentum filter — detecting oversold/overbought exhaustion.
- The **200 EMA trend filter** (enabled by default) adds directional context: longs are allowed only when price is above the EMA (overall uptrend bias), shorts only below it (downtrend bias). This reduces whipsaws in strong trending sessions and improves trade quality without overly restricting opportunities.
- Disabling the trend filter allows pure mean-reversion trading (useful in ranging/Asian sessions), but enabling it is strongly recommended for better expectancy in most market conditions.
Entries occur only when flat (no pyramiding), keeping the system clean and directional.
### Exit Rules (Fixed at Entry):
- **Take Profit**: +10 pips (0.10 in XAUUSD price terms)
- **Stop Loss**: -5 pips (0.05 in price terms)
- **Risk:Reward** = 1:2
Exits are **set once on the entry bar** using limit/stop orders — this prevents dynamic recalculation and mimics real broker behavior more closely. The 2:1 RR gives the strategy mathematical edge even with moderate win rates.
### Performance & Realism Guidelines:
This strategy is tuned for **high-frequency scalping** on gold, which typically produces hundreds to thousands of trades per year on 1-minute data.
Recommended **backtest/publication settings** (update these in the strategy properties before publishing):
- **Initial Capital**: $10,000 – $50,000 (realistic for retail prop/swing traders; the script defaults to $1,000,000 only for visual scaling — change it!)
- **Order Size**: Fixed 0.10–0.50 lots (adjust according to account size)
- **Commission**: 3–7 USD per round-turn lot (typical ECN/raw-spread gold commission on brokers like IC Markets, Pepperstone, etc.)
- **Slippage**: 2–5 ticks (≈0.02–0.05 in price) — gold can be slippery during news/volatility spikes on 1-minute charts
- **Pyramiding**: 1 (default — no stacking)
- **Dataset**: At minimum 1–3 years of 1-minute data (aim for >500–1000 closed trades for statistical significance)
With realistic costs applied, expect win rate ≈55–70%, profit factor >1.4–1.8 in favorable periods, but results vary significantly across trending vs. ranging regimes and news events.
**Risk per trade** remains very controlled (typically <0.5–1.5% depending on SL distance and position sizing) — never exceeds sustainable levels.
### Important Usage Notes:
- Check thoroughly the curve-fitted back tests.
- Gold is extremely volatile on 1-minute charts; check major news (NFP, FOMC, FED) unless you widen SL/TP dynamically (you can manually implement it here).
- Always forward-test on demo first and use proper position sizing.
- The built-in dashboard shows live stats (net P&L, win rate, profit factor, current RSI, position status, etc.) for quick monitoring.
- Restricted Sample Size for Precision
Happy scalping — trade responsibly! 🚀
![]()
//@version=6
strategy("XAUUSD 1-Minute Scalping Strategy - FIXED Exits", overlay=true,
default_qty_type=strategy.fixed, default_qty_value=1,
initial_capital=1000000, currency=currency.USD)
// ── Inputs ────────────────────────────────────────────────
length = input.int(14, title="RSI Length")
overbought = input.int(70, title="Overbought Level")
oversold = input.int(30, title="Oversold Level")
takeProfit = input.int(10, title="Take Profit (pips)")
stopLoss = input.int(5, title="Stop Loss (pips)")
use_trend_filter = input.bool(true, "Use 200 EMA Trend Filter (recommended)")
// ── Calculations ──────────────────────────────────────────
rsi = ta.rsi(close, length)
ema200 = ta.ema(close, 200)
// ── Entry Conditions ──────────────────────────────────────
longCondition = rsi < oversold and (not use_trend_filter or close > ema200)
shortCondition = rsi > overbought and (not use_trend_filter or close < ema200)
// ── Entries ───────────────────────────────────────────────
if (longCondition and strategy.position_size == 0)
strategy.entry("Long", strategy.long)
if (shortCondition and strategy.position_size == 0)
strategy.entry("Short", strategy.short)
// ── FIXED TP/SL (set once at entry) ───────────────────────
var float long_tp_price = na
var float long_sl_price = na
var float short_tp_price = na
var float short_sl_price = na
// Capture prices on the entry bar only
if strategy.position_size > 0 and strategy.position_size[1] <= 0
long_tp_price := close + takeProfit * syminfo.mintick
long_sl_price := close - stopLoss * syminfo.mintick
if strategy.position_size < 0 and strategy.position_size[1] >= 0
short_tp_price := close - takeProfit * syminfo.mintick
short_sl_price := close + stopLoss * syminfo.mintick
// Place exits only once right after entry (prevents overwriting)
if strategy.position_size > 0 and strategy.position_size[1] <= 0
strategy.exit("TP/SL Long", from_entry="Long",
limit = long_tp_price,
stop = long_sl_price)
if strategy.position_size < 0 and strategy.position_size[1] >= 0
strategy.exit("TP/SL Short", from_entry="Short",
limit = short_tp_price,
stop = short_sl_price)
// ── VISUALIZATION ─────────────────────────────────────────
hline(overbought, "Overbought", color=color.new(color.red, 50), linestyle=hline.style_dashed, linewidth=2)
hline(oversold, "Oversold", color=color.new(color.green, 50), linestyle=hline.style_dashed, linewidth=2)
hline(50, "Midline", color=color.new(color.gray, 70), linestyle=hline.style_dotted, linewidth=1)
rsiPlot = plot(rsi, title="RSI", color=color.new(color.blue, 0), linewidth=2)
fill(plot(50, display=display.none), rsiPlot, color=rsi > 50 ? color.new(color.blue, 90) : color.new(color.orange, 90), title="RSI Background")
bgcolor(rsi > overbought ? color.new(color.red, 85) : rsi < oversold ? color.new(color.green, 85) : na, title="RSI Zones")
plotshape(longCondition, "Long Entry", style=shape.triangleup, location=location.belowbar, color=color.new(color.lime, 0), size=size.small, text="BUY", textcolor=color.white)
plotshape(shortCondition, "Short Entry", style=shape.triangledown, location=location.abovebar, color=color.new(color.red, 0), size=size.small, text="SELL", textcolor=color.white)
// ── PROFESSIONAL DASHBOARD ────────────────────────────────
var table dashboard = table.new(
position=position.top_right,
columns=2,
rows=11,
bgcolor=color.new(color.black, 30),
border_color=color.new(color.blue, 70),
border_width=2,
frame_color=color.new(color.blue, 70),
frame_width=2)
if barstate.islast
// HEADER
table.cell(dashboard, 0, 0, "XAUUSD 1M SCALPER",
bgcolor=color.new(color.blue, 60), text_color=color.white,
text_size=size.large, text_halign=text.align_center)
table.merge_cells(dashboard, 0, 0, 1, 0)
// NET PROFIT
table.cell(dashboard, 0, 1, "Net P&L", bgcolor=color.new(color.gray, 50), text_color=color.white, text_size=size.small)
profitColor = strategy.netprofit > 0 ? color.new(color.green, 30) : color.new(color.red, 30)
profitTextColor = strategy.netprofit > 0 ? color.lime : color.red
table.cell(dashboard, 1, 1, "$" + str.tostring(strategy.netprofit, "#,##0.00"),
bgcolor=profitColor, text_color=profitTextColor, text_size=size.large, text_halign=text.align_right)
// TOTAL TRADES
table.cell(dashboard, 0, 2, "Total Trades", bgcolor=color.new(color.gray, 50), text_color=color.white, text_size=size.small)
table.cell(dashboard, 1, 2, str.tostring(strategy.closedtrades),
text_color=color.white, text_size=size.normal, text_halign=text.align_right)
// WIN RATE
table.cell(dashboard, 0, 3, "Win Rate", bgcolor=color.new(color.gray, 50), text_color=color.white, text_size=size.small)
winRate = strategy.closedtrades > 0 ? (strategy.wintrades / strategy.closedtrades) * 100 : 0
wrColor = winRate > 50 ? color.lime : winRate > 40 ? color.yellow : color.red
table.cell(dashboard, 1, 3, str.tostring(winRate, "#.##") + "%",
text_color=wrColor, text_size=size.normal, text_halign=text.align_right)
// PROFIT FACTOR
table.cell(dashboard, 0, 4, "Profit Factor", bgcolor=color.new(color.gray, 50), text_color=color.white, text_size=size.small)
pf = strategy.grossloss != 0 ? math.abs(strategy.grossprofit / strategy.grossloss) : 0
pfColor = pf > 1.5 ? color.lime : pf > 1.0 ? color.yellow : color.red
table.cell(dashboard, 1, 4, str.tostring(pf, "#.##"),
text_color=pfColor, text_size=size.normal, text_halign=text.align_right)
// WINNING / LOSING TRADES
table.cell(dashboard, 0, 5, "Winning Trades", bgcolor=color.new(color.gray, 50), text_color=color.white, text_size=size.small)
table.cell(dashboard, 1, 5, str.tostring(strategy.wintrades),
text_color=color.lime, text_size=size.normal, text_halign=text.align_right)
table.cell(dashboard, 0, 6, "Losing Trades", bgcolor=color.new(color.gray, 50), text_color=color.white, text_size=size.small)
table.cell(dashboard, 1, 6, str.tostring(strategy.losstrades),
text_color=color.red, text_size=size.normal, text_halign=text.align_right)
// CURRENT RSI
table.cell(dashboard, 0, 7, "Current RSI", bgcolor=color.new(color.gray, 50), text_color=color.white, text_size=size.small)
rsiColor = rsi > overbought ? color.red : rsi < oversold ? color.lime : color.yellow
table.cell(dashboard, 1, 7, str.tostring(rsi, "#.##"),
text_color=rsiColor, text_size=size.normal, text_halign=text.align_right)
// POSITION STATUS
table.cell(dashboard, 0, 8, "Position", bgcolor=color.new(color.gray, 50), text_color=color.white, text_size=size.small)
posText = strategy.position_size > 0 ? "LONG 📈" : strategy.position_size < 0 ? "SHORT 📉" : "FLAT ━"
posColor = strategy.position_size > 0 ? color.lime : strategy.position_size < 0 ? color.red : color.gray
table.cell(dashboard, 1, 8, posText,
text_color=posColor, text_size=size.normal, text_halign=text.align_right)
// RISK/REWARD
rrRatio = takeProfit / stopLoss
table.cell(dashboard, 0, 9, "TP/SL Ratio", bgcolor=color.new(color.gray, 50), text_color=color.white, text_size=size.small)
table.cell(dashboard, 1, 9, "1:" + str.tostring(rrRatio, "#.#"), text_color=color.aqua, text_size=size.normal, text_halign=text.align_right)
// FOOTER
table.cell(dashboard, 0, 10, "IC Markets • v6 Fixed", bgcolor=color.new(color.gray, 70), text_color=color.white, text_size=size.tiny, text_halign=text.align_center)
table.merge_cells(dashboard, 0, 10, 1, 10)
// ── WEBHOOK ALERTS (STRATEGY-SAFE) ───────────────────────
// BUY ALERT
if longCondition and strategy.position_size == 0
alert(
"{\n" +
" \"symbol\": \"XAUUSD\",\n" +
" \"quantity\": 1,\n" +
" \"action\": \"buy\"\n" +
"}",
alert.freq_once_per_bar_close
)
// SELL ALERT
if shortCondition and strategy.position_size == 0
alert(
"{\n" +
" \"symbol\": \"XAUUSD\",\n" +
" \"quantity\": 1,\n" +
" \"action\": \"sell\"\n" +
"}",
alert.freq_once_per_bar_close
)