Designing a durable MT5 system is not about a clever entry. It’s a production problem that spans economics (why it works), statistics (proof it works), market microstructure (costs & execution), and software engineering (so it keeps working). Below is a practitioner’s blueprint—concepts, guardrails, and concrete MQL5 patterns—to build a system that survives regime shifts, costs, and live operations.
1) Economic Thesis → Strategy Design
Non-negotiable: every rule must trace back to an economic hypothesis or a repeatable behavioral pattern.
- Macro/structural edges: carry (rate differentials), terms-of-trade proxies (commodity FX), policy path repricing (OIS/expectations), risk-on/off cycles.
- Behavioral/technical edges: trend persistence (session flows), mean reversion in range regimes, event-driven breakouts/fades.
- Regime filters: volatility state (ATR/EWMA), liquidity window (session, holidays), risk state (cross-asset proxies), spread filter.
Decision template
- When to trade? (state + event conditions)
- What direction? (signal sign/strength)
- How much? (sizing)
- Where to exit? (invalidations, profit harvesting)
- When to stand down? (news blackout, abnormal spreads)

2) MT5 System Architecture (modules)
Keep components decoupled so you can test and replace them independently.
- DataHub: symbol handling, feeds, session/calendar, feature bundle
- SignalModel: produces direction and confidence
- RiskManager: converts signal→position; enforces limits/kill-switches
- ExecutionEngine: order creation/routing, slippage accounting, re-quotes handling
- Telemetry: logs, metrics, alerts; deterministic backtest replays
- Config: externalized parameters (set files), versioned
Minimal MQL5 skeleton (state machine)
//+------------------------------------------------------------------+
//| PerfectSystem.mq5 (skeleton) |
//+------------------------------------------------------------------+
#include <Trade/Trade.mqh>
CTrade Trade;
enum Mode { MODE_LIVE, MODE_DEMO, MODE_BACKTEST };
int g_mode;
datetime g_nextTimer;
int OnInit() {
g_mode = (MQLInfoInteger(MQL_TESTER) ? MODE_BACKTEST : (AccountInfoInteger(ACCOUNT_TRADE_ALLOWED)? MODE_LIVE : MODE_DEMO));
EventSetTimer(1); // 1-second heartbeat (cheap, controllable)
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason) { EventKillTimer(); }
void OnTimer() {
if(!MarketInfoReady(_Symbol)) return;
if(!TradingWindowOK()) return;
if(SpreadTooWide()) return;
Features feats = BuildFeatures(); // vol, trend, session, spreads, etc.
Signal sig = ModelPredict(feats); // dir, strength, confidence [0..1]
RiskCaps caps = GetRiskCaps(); // gross/net, per-symbol, correlation caps
double target = PositionTarget(sig, feats, caps); // desired lots (+/-)
RebalanceTo(target); // place/modify orders
TelemetryUpdate(sig, feats); // log metrics
}
void OnTick() { /* Keep empty; drive logic from OnTimer for determinism */ }
Why
OnTimer
? You get deterministic scheduling (easier to replay), simpler throttling, and fewer “accidental” micro-reactions to every tick.
3) Symbol, Costs, and Data Hygiene
Production sanity starts here.
- Symbol contract:
SYMBOL_TRADE_TICK_VALUE
,SYMBOL_TRADE_TICK_SIZE
,SYMBOL_POINT
,SYMBOL_DIGITS
,SYMBOL_SWAP_LONG/SHORT
,SYMBOL_TRADE_STOPS_LEVEL
. Cache them. - Costs model: spread, commissions, swaps, slippage. Keep a rolling median for each by session.
- Data quality: normalize timestamps, guard against missing ticks & spikes; build session calendar (London open, NY overlap) and holiday filters.
struct SymSpec {
double pt, tickSize, tickValue;
int digits;
};
SymSpec LoadSpec(const string s){
SymSpec x;
x.pt =(double)SymbolInfoDouble(s, SYMBOL_POINT);
x.tickSize =(double)SymbolInfoDouble(s, SYMBOL_TRADE_TICK_SIZE);
x.tickValue =(double)SymbolInfoDouble(s, SYMBOL_TRADE_TICK_VALUE);
x.digits =(int) SymbolInfoInteger(s, SYMBOL_DIGITS);
return x;
}
4) Signals & Features (practical set)
- Trend: EWMA cross (fast>slow), channel breakout with volatility filter.
- Mean reversion: z-score of price vs. rolling mean when vol is low and spreads are tight.
- Event awareness: pause N minutes around high-impact events; MT5 has an economic calendar—use it or import from file.
- Regime features: ATR_N, realized vol, spread rank, session id, risk proxy.
Avoid look-ahead: build features only from data known at decision time.
5) Risk & Money Management
Volatility targeting (position sizing)
Target annualized vol σ_T
(e.g., 10%). Use EWMA to estimate current pair vol σ̂
.
Target notional (in lots):
value_per_pip = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
pip_risk = ATR(N)*pip_value_multiplier;
risk_$ = RiskBudget * AccountEquity();
lots = (risk_$ / pip_risk) / ContractPipValueInAccountCurrency;
Simple per-trade approach:
Lots = clip( Fraction * Equity * IR_adj / (ATR_N * pipValue), minLot, maxLot )
Where Fraction
is a conservative Kelly fraction (0.1–0.3), IR_adj
shrinks information ratio by live slippage penalty.
Hard limits
- Per-symbol exposure cap (e.g., 1.5× target vol), per-side (long/short).
- Factor caps: USD beta (don’t be 3× long USD across pairs inadvertently).
- Drawdown ladder: at -5% reduce gross by 30%; at -10% halve risk; at -15% flat.
Kill-switch triggers
- Median slippage > X pips for Y minutes.
- Spread rank in top 5% of last 30 days.
- Quote-to-fill collapse (fills < Z% of attempts).
6) Execution Engineering in MT5
- Use
CTrade
or rawMqlTradeRequest/MqlTradeResult
. Always checkresult.retcode
. - Smart retry: if
TRADE_RETCODE_REQUOTE
orTRADE_RETCODE_PRICE_OFF
, refresh book, re-price with tolerance (e.g., half-spread), and limit retry count. - Partial fills: MT5 supports fills policy
ORDER_FILLING_FOK/IOC/RETURN
(depends on broker). Prefer IOC/RETURN for flexibility. - Slippage accounting: store
(requested_price - filled_price)
signed by side; maintain per-session distribution.
bool PlaceMarket(const ENUM_ORDER_TYPE side, double lots, double sl=0, double tp=0){
MqlTradeRequest r={0}; MqlTradeResult res={0};
r.action = TRADE_ACTION_DEAL;
r.symbol = _Symbol;
r.type = side; // ORDER_TYPE_BUY / ORDER_TYPE_SELL
r.volume = lots;
r.deviation= 5; // price tolerance (points)
r.type_filling = ORDER_FILLING_IOC;
r.price = (side==ORDER_TYPE_BUY)? SymbolInfoDouble(_Symbol,SYMBOL_ASK)
: SymbolInfoDouble(_Symbol,SYMBOL_BID);
if(sl>0) r.sl=sl; if(tp>0) r.tp=tp;
if(!OrderSend(r,res)) return false;
LogFill(res);
return (res.retcode==TRADE_RETCODE_DONE || res.retcode==TRADE_RETCODE_PLACED || res.retcode==TRADE_RETCODE_DONE_PARTIAL);
}
Spread & latency controls
- Stand down if
CurrentSpread > Percentile(90%)
for the session. - Avoid thin handover minutes (NY close, roll), Friday late PM.
- VPS close to LP (LD4/NY4) to reduce ping; measure
TimeTradeServer()
drift.

7) Exits, Stops, and Profit Taking
- Invalidation stop: structure-based (behind recent swing) or ATR-scaled (
k*ATR
). - Profit harvesting: partial at RR=1, trail the rest with
k*ATR
or moving structure (higher lows / lower highs). - Time-based exit: if trade fails to gain MFE after
T
bars, cut (opportunity cost).
Robust trailing stop (ATR-scaled)
double atr = iATR(_Symbol, PERIOD_M15, 20, 1);
double trail = 1.8 * atr;
if(isLong) newSL = MathMax(PositionGetDouble(POSITION_SL), Bid - trail);
if(isShort) newSL = MathMin(PositionGetDouble(POSITION_SL), Ask + trail);
8) Backtesting & Validation (MT5 specifics)
Goal: believable results that survive forward use.
- Tick model & spread: use real ticks when possible; for variable spreads, test across percentiles (tight/median/wide). Add explicit slippage penalties (e.g., sampled from your live distribution).
- Commissions & swaps: set broker-accurate values or add ex-post cost adjustment.
- Walk-forward: split history into rolling train/test windows; re-optimize modestly; evaluate stability across windows.
- Monte Carlo: shuffle trade sequence and slippage to stress drawdown and max adverse excursion.
- Purged/embargoed CV (concept): when cross-validating parameters, ensure no leakage across overlapping windows.
What to report (beyond Sharpe)
- Net expectancy per trade, tail risk (CVaR/ES at 95%), hit-rate × payoff, turnover, capacity (lots vs. slippage), dependency on spread regime.
9) Telemetry, Logging, and Determinism
- Decision log (CSV): timestamp, features, signal, target lots, pre-trade price/spread, post-trade fill/slippage, reason codes (why trade/why not), ES/TP/SL values.
- Health metrics: quote-to-fill %, rejects/requotes %, median slippage, spread rank, drift of features.
- Deterministic replays: drive the strategy from
OnTimer
with a fixed schedule; persist inputs so you can recreate decisions.
void LogDecision(const Signal &s, const Features &f, double targetLots){
string row = StringFormat("%s,%s,%.4f,%.2f,%.2f,%.2f,%d",
TimeToString(TimeCurrent(),TIME_DATE|TIME_SECONDS),
_Symbol, targetLots, f.spread, f.atr, s.score, s.reason);
int h = FileOpen("decisions.csv", FILE_WRITE|FILE_READ|FILE_CSV|FILE_ANSI|FILE_SHARE_WRITE);
if(h!=INVALID_HANDLE){ FileSeek(h,0,SEEK_END); FileWrite(h,row); FileClose(h); }
}
10) Configuration & Parameter Governance
- Keep .set files under version control; record the hash in your logs.
- Limit free parameters; prefer coarse grids and theory-driven defaults.
- Anti-curve fit: if a parameter materially changes edge between windows, freeze it or drop the feature.
11) Deployment Plan (Demo → Live)
- Shadow mode (demo): make real-time decisions, write orders to log, don’t send. Compare to a benchmark.
- Canary capital (1–5% risk): live orders with reduced size; track slippage and behavior.
- Phased scale-up with automated rollback: if kill-switch trips or live metrics drift, step back.
Operations runbook
- What to do on disconnect, partial fills, widened spreads, calendar surprises.
- End-of-day tasks: rotate logs, snapshot positions, reconcile swaps/fees.
12) Compliance & Ethics (retail realities)
- Respect broker terms; avoid pathological behaviors (quote stuffing, toxic flow).
- Keep audit trails (orders, modifications, cancels) with clock sync.
- Monitor margin and leverage caps; simulate worst-case gap risk across weekends/events.
13) “Perfect System” Checklist
Thesis & Design
- Clear economic hypothesis and regime assumptions
- Stand-down rules (spreads, news, illiquid windows)
Risk
- Vol targeting & (fractional) Kelly with live slippage penalty
- Exposure caps (symbol, factor), drawdown ladder, kill-switch
Execution
- IOC/RETURN fills, smart retry on requotes
- Slippage measurement & budget, venue/broker sanity checks
Validation
- Real-tick tests with variable spreads, commissions, swaps
- Walk-forward + Monte Carlo; report CVaR, payoff, capacity
Engineering
- OnTimer-driven loop, deterministic inputs
- Structured logging & health metrics; config versioned
- Robust error handling and recovery (network, trade context)
Deployment
- Shadow → canary → scale; automated rollback
- Daily reconciliation, incident runbook
Closing Takeaways
A perfect MT5 system is not the one with the highest backtest Sharpe—it’s the one engineered to retain edge after costs, across regimes, under real execution. Anchor the rules in an economic thesis, validate honestly, price frictions explicitly, and instrument everything. If you build risk, execution, and observability into the architecture from day one, your MQL5 bot won’t just trade—it will survive.