Grid trading gets a bad reputation in trading dev communities, and honestly — it's earned. Search GitHub for "forex grid EA" and you'll find dozens of repos implementing naive martingale grids that look great on a 3-month backtest and catastrophic on a 3-year one. I wanted to build something different: a grid system for XAUUSD with hard risk boundaries, dynamic spacing, and a bias filter that shuts the whole thing down when the market stops ranging.
Here's the architecture, the logic, and the mistakes I made getting there.
The Core Design Problem
A pure grid strategy is direction-agnostic by design — it profits from price oscillating through fixed levels, not from correctly calling a move. That's the appeal. The problem is that "direction-agnostic" also means "blind to trend," and gold trends hard when it wants to (think CPI surprises, risk-off flights to safety, central bank shocks). An uncapped grid on the wrong side of one of those moves doesn't just lose — it can compound losses if it's scaling position size on each new level.
So the build had three non-negotiable requirements before a single order got placed:
Exposure must be hard-capped, independent of how many grid levels are theoretically available.
Grid spacing must be a function of current volatility, not a static constant.
The system needs a "ranging vs. trending" classifier to decide whether the grid should even be active.
Component 1: The Ranging Classifier
Before deploying orders, the system checks higher-timeframe structure:
Has price made a break of structure (BOS) in the last N bars on the H4?
Is price currently contained within a recent swing high/low range (no expansion)?
Has a liquidity sweep occurred recently without a confirmed follow-through move?
If structure confirms ranging conditions, the grid is greenlit. If a fresh BOS or CHOCH fires, the classifier flags trending conditions, and the grid either shifts to a directionally-biased configuration or shuts down entirely, handing control to a directional entry model instead.
This is the single biggest difference between this system and the martingale grids you'll find in most public repos — it doesn't run blind. It runs conditionally.
Component 2: Dynamic Spacing (ATR-Scaled)
Fixed pip spacing is the fastest way to make a grid strategy stop working the moment volatility regime shifts. I scaled grid spacing directly off a rolling ATR value:
grid_spacing = ATR(period=14, timeframe=H1) * spacing_multiplier
During low-volatility Asian session hours, this naturally tightens the grid. During high-volatility windows (London open, US data releases), it widens automatically — preventing the grid from getting chopped to pieces by noise that would otherwise trigger multiple levels in seconds.
Component 3: Exposure Caps and the Kill Switch
This is where most public grid EAs fail, so it got the most engineering attention:
Max concurrent grid levels: hard integer cap, not tied to available margin.
Max total lot exposure: calculated as a fixed percentage of account equity, recalculated on every new level fill — not on a static starting balance.
Drawdown kill switch: if floating drawdown on the grid cycle exceeds a defined threshold, the entire grid closes — win or lose — rather than letting it ride hoping for reversion.
None of this is exotic engineering. It's just risk logic that a lot of grid implementations skip because it makes the backtest curve look less impressive.
What the Backtests Actually Showed
Backtesting across mixed regimes (a ranging month, a trending month, and one high-impact news week) showed the expected pattern: strong, consistent small gains during ranging conditions, near-zero activity during confirmed trending conditions (by design — the classifier shuts it down), and controlled, capped losses during the one week it misclassified an early-stage range as ongoing before a breakout occurred.
That last case is the honest limitation of any grid system: the classifier isn't perfect, and there will be cycles where it's late to recognize a regime shift. The exposure cap exists specifically to make sure "late" costs a defined, small amount — not the account.
Where This Fits Into a Broader XAUUSD System
This grid module isn't meant to run standalone. It's built as one component of the broader Goldmine Strategy framework, which already handles the market structure and liquidity sweep logic used by the ranging classifier here. If you're building your own MQL5 or Python trading infrastructure for gold, treating grid trading as a conditional module — not a standalone strategy — is the difference between a system that survives multiple volatility regimes and one that gets liquidated the first time gold decides to trend for three weeks straight.
If you want the full rule set this classifier is built on — the structure shift and liquidity sweep logic — that's documented in the Goldmine Strategy.
Sample Logic Flow
For anyone thinking about implementing something similar, the high-level control flow looks roughly like this:
on_new_bar():
regime = classify_regime(structure_data)
if regime == TRENDING:
close_all_grid_orders()
return
atr = calculate_atr(period=14, timeframe=H1)
spacing = atr * spacing_multiplier
if current_exposure < max_exposure_cap:
deploy_grid_levels(spacing, max_levels)
if floating_drawdown > kill_switch_threshold:
close_all_grid_orders()
halt_new_deployments(cooldown_period)
This is deliberately simplified, but it captures the important part: the risk governance checks (exposure cap, kill switch) run independently of whether the regime classifier thinks conditions are favorable. Nothing in the deployment logic can override the risk layer.
Testing Gotchas Worth Flagging
A few issues came up during testing that aren't obvious until you hit them:
Spread modeling matters more than people assume. Early backtests used a fixed average spread, and results looked great. Switching to variable spread modeling — which widens automatically around news events, matching real broker behavior — knocked a meaningful chunk off the backtested returns. That's not a bug; that's the backtest becoming honest.
Classifier whipsaw during transition periods. There were sequences where the regime classifier flipped between ranging and trending multiple times within a short window, right at the edge of a genuine structural shift. Each flip triggers a grid close/reopen cycle, which racks up spread cost if not handled carefully. Adding a small confirmation delay (requiring the new classification to hold for N bars before acting on it) reduced this without meaningfully hurting responsiveness.
Broker-specific execution differences. An EA tested against one broker's historical tick data can behave differently on a live account with a different broker's execution model — slippage, requote behavior, and even how quickly pending orders fill can vary. Forward-testing on a demo account with your actual intended broker before going live isn't optional if you want the backtest numbers to mean anything.
Open Questions for Further Iteration
This system isn't a finished product — a few areas are worth continued work for anyone extending this kind of architecture: adaptive exposure caps that tighten automatically during elevated macro-event risk (rather than a single static percentage at all times), and a more granular regime classifier that outputs a confidence score rather than a binary ranging/trending flag, allowing grid spacing and exposure to scale smoothly with classifier confidence instead of switching abruptly at a threshold.
If you're building similar infrastructure for gold or other volatile instruments, I'd genuinely be interested in comparing notes on regime classification approaches — it's the piece of this system that took the most iteration to get right, and it's also the piece most tutorials skip entirely in favor of just showing the entry/exit logic.
If you'll love to get access to my grid trading system which has 95% win rate and consistent profit in indicator and ea bot
Top comments (0)