Algorithmic Risk Management in MT5: Dynamic Position Sizing & MQL5 Architecture
Most retail trading failures stem from static lot sizing. Trading a fixed 1.0 lot on EUR/USD creates vastly different dollar drawdowns compared to 1.0 lot on Gold (XAU/USD) or NASDAQ (NAS100) due to differing point values and underlying volatility regimes.
In professional quantitative finance, position sizing is dynamically governed by the asset's Average True Range (ATR) and a fixed risk budget per trade:
$$\text{Position Size (Lots)} = \frac{\text{Account Balance} \times \text{Risk \%}}{\text{Stop Loss Distance (Points)} \times \text{Tick Value}}$$
In this technical guide, we implement an open-source, production-grade MQL5 risk engine that dynamically computes lot sizes and routes orders with slippage protection.
Original interactive tool and web calculator available at GuetaQuant MT5 Position Sizer.
1. MQL5 Dynamic Lot Calculation Class
//+------------------------------------------------------------------+
//| GQ_RiskEngine.mqh |
//| Copyright 2026, Gueta Quant (AGPLv3) |
//| https://guetaquant.com |
//+------------------------------------------------------------------+
#property copyright "Gueta Quant"
#property link "https://guetaquant.com"
class CGQRiskManager
{
private:
string m_symbol;
double m_risk_pct;
public:
CGQRiskManager(string symbol, double risk_pct) : m_symbol(symbol), m_risk_pct(risk_pct) {}
double CalculateLots(double sl_distance_price)
{
if (sl_distance_price <= 0) return 0.0;
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double risk_amount = balance * (m_risk_pct / 100.0);
double tick_size = SymbolInfoDouble(m_symbol, SYMBOL_TRADE_TICK_SIZE);
double tick_value = SymbolInfoDouble(m_symbol, SYMBOL_TRADE_TICK_VALUE);
double min_lot = SymbolInfoDouble(m_symbol, SYMBOL_VOLUME_MIN);
double max_lot = SymbolInfoDouble(m_symbol, SYMBOL_VOLUME_MAX);
double lot_step = SymbolInfoDouble(m_symbol, SYMBOL_VOLUME_STEP);
if (tick_size == 0 || tick_value == 0) return 0.0;
double loss_per_lot = (sl_distance_price / tick_size) * tick_value;
if (loss_per_lot <= 0) return 0.0;
double raw_lots = risk_amount / loss_per_lot;
// Normalize to broker lot step
double normalized_lots = MathFloor(raw_lots / lot_step) * lot_step;
if (normalized_lots < min_lot) normalized_lots = min_lot;
if (normalized_lots > max_lot) normalized_lots = max_lot;
return normalized_lots;
}
};
2. ATR Volatility Trailing Stops
Using historical volatility avoids getting stopped out during normal market noise while protecting capital during structural trend shifts:
double GetATRDistance(string symbol, ENUM_TIMEFRAMES tf, int period, double multiplier)
{
int handle = iATR(symbol, tf, period);
if (handle == INVALID_HANDLE) return 0.0;
double atr_val[1];
if (CopyBuffer(handle, 0, 0, 1, atr_val) <= 0) return 0.0;
return atr_val[0] * multiplier;
}
3. Open Source Tools Ecosystem
All our quantitative software is published under open-source AGPLv3:
- 44 Open Source Trading Tools: GitHub Repository
- CERN Zenodo DOI Registry: DOI 10.5281/zenodo.22012203
- Local-First Trading Journal: GuetaQuant Journal
Compliance: Strictly educational and research material. Does not constitute financial advice. Compliant with Colombian SFC Decreto 2555/2010.
Top comments (0)