Two days ago, I built my first trading bot — an EMA crossover strategy on MetaTrader 5. It was basic, but it worked. So I did what any engineer would do: I tried to make it better.
48 hours and 9 bots later, I learned the hardest lesson in algorithmic trading: every "improvement" you add is a new way to lose money.
The Setup
- Platform: MetaTrader 5 on Mac (Wine)
- Symbol: XAUUSD (Gold) on M1 charts
- Account: Demo with ~$1,035 starting balance
- Pair programmer: Claude Code for rapid MQL5 iteration
- Goal: Build a profitable scalping bot with consistent 2% daily returns
Phase 1: EMA Crossover (Bots 1-2) — The False Start
My first bot used EMA 14/28 crossover with a scoring system — trend detection, strength, pullback, momentum, breakout, and crossover signals. It had market mode detection (TREND vs RANGE) and dollar-based TP/SL.
The problems showed up immediately:
- Spread killed everything. MetaQuotes demo has 30-40 point spreads on XAUUSD. My max spread filter was set to 25. No trades fired until I raised it to 50.
- Break-even triggered too early at $0.80 — five trades exited at exactly $0.00, wasting perfectly good entries.
- TP was too small ($1) vs SL ($3) — I needed a 74% win rate just to break even.
- Overtrading. 35 trades in 3 minutes with a 3-second cooldown.
Results:
- Smart Pro v3.0: 35 trades, 57% win rate, -$7.50
- Smart Pro v3.2: 6 trades, 33% win rate, -$6.00
Lesson learned: EMA crossover strategies lag too much for M1 scalping. By the time the signal confirms, the move is already over. Trend-following does not work on 1-minute gold charts.
Phase 2: Mean Reversion (Bots 3-4) — Finding the Edge
I scrapped trend-following entirely and switched to mean reversion: buy dips, sell rips, close on small profit.
The logic was simple: gold on M1 charts tends to bounce. If price drops X points from a recent high, buy. If it rips Y points from a recent low, sell. Close everything at $1 profit.
Dip Buyer v1.0 — BUY Only
- Buy when price dips 60 points from recent high
- Average down once (max 2 positions)
- Close all at $1 profit or $10 loss
Result: 6 rounds, 6 wins, 0 losses, +$6.45
First time I saw green. The strategy worked.
Dip Buyer v2.0 — Added SELL (Broke It)
Added sell-the-rip to complement buy-the-dip. But the triggers competed — only the strongest signal fired. In an uptrend, the rip was always bigger than the dip, so the bot only sold.
Result: 12 rounds, 10W/2L, -$4.20
Fix: Made triggers independent. Dip and rip fire separately.
Dip Buyer v2.1 — Independent Triggers
Both BUY and SELL working simultaneously. 22 buys + 9 sells.
Result: 34 rounds, 31W/3L (91%), +$3.30
Getting somewhere. But all 3 losses came from averaged positions — rounds where the bot held 2 positions and the total loss was 2x.
Dip Buyer v3.0 — The Winner
Removed averaging entirely. One position at a time. Tightened parameters.
| Parameter | Value |
|---|---|
| Max positions | 1 (no averaging) |
| Take Profit | $1.00 |
| Stop Loss | $5.00 |
| Dip trigger | 150 points |
| Cooldown | 15 seconds |
Result: 82 trades, 71W/11L (87%), +$34.99
This was the overnight run. I went to sleep with the bot running and woke up +$35. At 87% win rate with $1 TP / $5 SL, the math works: each loss costs 5 wins, and you win 87 out of 100 trades.
Phase 3: The Over-Engineering Spiral (Bots 5-9)
Here is where I made the classic engineering mistake. Dip Buyer C was making money, so I tried to make it "smarter."
Bot 5: Smart Reversal — The Sniper
Four entry filters: EMA 50 slope + dip size + RSI oversold/overbought + price below fast EMA. Plus a 3-minute timeout exit.
Result: 3 trades, 3 wins, +$5.00
100% win rate — but 3 trades in hours. It was too selective. Fine as insurance alongside Dip Buyer C, but it could not carry a session alone.
Bot 6: Precision Scalper — Adding Safety
Took Dip Buyer C and added "improvements":
- Tighter SL ($3 instead of $5)
- Waterfall detection (skip falling knife entries)
- Post-loss cooldown (60 seconds)
- Consecutive loss pause (3 losses = 5-minute break)
- 120-second time exit
Result: -$10
The waterfall filter was too aggressive — it blocked good entries along with bad ones. The tighter SL ($3) meant I now needed 75% win rate instead of the 83% I needed before. Marginal improvements in safety cost more in missed opportunity.
Bot 7: APEX Scalper — Maximum Complexity
This was the peak of over-engineering:
- Declining take-profit: Started at $1.15, dropped to $0.10 over time
- Confidence-based lot sizing: 0.02 lots on high-confidence, 0.01 on low
- Peak profit lock: If unrealized profit hit $0.50, lock it in
- EMA entry filter
- RSI entry filter
- Quick-cut logic: Exit at -$0.50 on low-confidence trades
Result: 68% win rate, -$20
Every "smart" feature backfired:
- Peak profit lock cut winning trades at $0.50 while losses stayed at $3. It was designed to protect gains but instead destroyed them.
- Quick-cut was supposed to limit losses to $0.50. Actual average loss: $1.35 due to execution delay + spread.
- EMA filter blocked good trades in range-bound markets where mean reversion works best.
Bot 8: Phoenix Scalper — The Worst
EMA-based entry, 10-second quick check, 3-position smart averaging.
Result: 24 trades, 38% win rate, -$30
The worst bot of all nine. Averaging amplified losses. EMA entry missed the best dip-buying opportunities. Everything that could go wrong did.
Bot 9: Back to Basics
Went back to Dip Buyer C. Added only two things: market-hours check and the 120-second timeout.
The Numbers Do Not Lie
| Bot | Strategy | Filters | Trades | Win Rate | P/L |
|---|---|---|---|---|---|
| Smart Pro | EMA Crossover | 6+ | 41 | 48% | -$13.50 |
| Dip Buyer C | Mean Reversion | 1 | 82 | 87% | +$35 |
| Smart Reversal | 4-Filter Sniper | 4 | 3 | 100% | +$5 |
| Precision | Waterfall + Safety | 3 | ~30 | 70% | -$10 |
| APEX | Declining TP + Peak Lock | 5+ | ~40 | 68% | -$20 |
| Phoenix | EMA + DCA | 5+ | 24 | 38% | -$30 |
The simplest bot with one filter — "did price dip enough?" — crushed every complex version.
~450 lines of code beat ~700 lines. One human observation ("gold bounces from dips") beat RSI, EMA crossover, and every technical indicator combined.
What I Learned from 200+ Live Trades
1. Simplicity Beats Complexity
Every "smart" feature was a new failure mode. Declining TP cut winners. Peak lock killed profits. EMA filters blocked good entries. Quick-cut failed due to execution delay.
The market does not reward complexity. It rewards discipline.
2. Test Live, Not on Paper
Real spreads (30-40 points), real slippage, and real execution delays kill strategies that look perfect in theory. Demo accounts with live market data reveal truth instantly. If your bot cannot handle real-world conditions, the strategy is broken.
3. Human Intuition Beats Indicators
"Gold always bounces from dips" — this one observation from watching the chart outperformed RSI, EMA crossover, MACD, and every technical filter I tried. The best algorithm was codifying a simple human observation, not stacking indicators.
4. No Averaging
All of my biggest losses came from averaged positions. When you hold 2-3 positions and the market keeps going against you, you lose 2-3x. Single position with a hard stop loss outperformed every DCA variant I tested.
5. R:R Ratio Is Everything
| TP / SL | Win Rate to Break Even |
|---|---|
| $1 / $10 | 91% |
| $1 / $5 | 83% |
| $1 / $3 | 75% |
At 87% win rate, $1 TP / $5 SL gives +$48 per 100 trades. Change the SL to $3 and you need 75% — sounds easier but the tighter stop gets hit more often in volatile conditions, dropping actual win rate.
6. Kill Your Darlings Fast
I retired 7 bots in 48 hours. No ego, just data. Every bot had rich logging — entry price, exit price, hold time, spread, balance, consecutive losses. When the data says your clever feature hurts performance, delete it.
7. AI Accelerates, You Decide
Claude Code built all 9 bots — from idea to deployed EA in minutes. The iteration speed was incredible. But the winning strategy came from watching the chart, not from code. AI is the best pair programmer I have ever had, but it cannot feel the market.
Technical Notes
The Stack
- MetaTrader 5 on Mac via Wine
- MQL5 for all bot logic
- Claude Code as AI pair programmer for rapid iteration
- Bot files symlinked from project directory to MT5 Experts folder — edit, hit F7 to compile
- Background monitoring script parses MT5 Expert logs (UTF-16LE) and updates a live scoreboard
Key Parameters (Winning Bot)
Strategy: Mean Reversion (Buy Dips / Sell Rips)
Take Profit: $1.00
Stop Loss: $5.00
Dip Trigger: 150 points from recent high
Rip Trigger: 150 points from recent low
Max Positions: 1 (no averaging)
Cooldown: 15 seconds
Time Exit: 120 seconds
Account Journey
| Time | Balance | Event |
|---|---|---|
| Start | $1,035 | Session begin |
| Hour 2 | $1,001 | Smart Pro losses |
| Hour 5 | $1,006 | Dip Buyer v1 recovery |
| Overnight | $1,040 | Dip Buyer C runs +$35 |
| Morning | $1,065 | Continuation |
| Afternoon | $1,055 | Testing complex bots, losses |
| End | ~$1,030 | Back to basics |
The account went from $1,035 to a peak of $1,065, then back to $1,030 as I tested progressively worse bots. The irony: I would have been up $35 if I had just left the simple bot running.
What Is Next
Going back to what works. Dip Buyer C with minimal additions:
- Market-hours check (skip low-liquidity periods)
- 120-second timeout (if it does not bounce in 2 minutes, exit)
- Rich analytics logging for continuous improvement
- Target: 2% daily ($21) with 85%+ accuracy
Building in public. More updates coming.
This is part 2 of my algorithmic trading journey. Read part 1 for how I went from zero MQL5 knowledge to a working bot in one day.
I am Manjodh Singh Saran, a Senior Software Engineer building products across web, mobile, and algorithmic trading. Follow my journey on LinkedIn.