Risk management separates profitable traders from those who blow accounts. This guide shows you how to implement proper risk management in your Expert Advisors.
Core Risk Management Principles
The 1% Rule
Never risk more than 1% of the account per trade:
- Survives losing streaks
- Allows recovery from drawdowns
- Enables consistent position sizing
Risk Components
Every trade has:
- Entry price: Where you enter
- Stop loss: Where you exit if wrong
- Position size: How much you trade
- Risk amount: Entry minus stop × position size
Calculating Position Size
The Formula
Lot Size = Risk Amount / (Stop Loss Pips × Pip Value)
Where:
- Risk Amount = Account Balance × Risk Percentage
- Stop Loss Pips = Entry - Stop Loss (in pips)
- Pip Value = Value of 1 pip for 1 lot
MQL5 Implementation
`mql5 double CalculateLotSize(double riskPercent, double stopLossPips) { // Get account information double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE); double riskAmount = accountBalance * riskPercent / 100;
// Get symbol information string symbol = _Symbol; double tickSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE); double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE); double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
// Calculate pip value for 1 lot double pipValue = (tickValue / tickSize) _Point 10;
// Calculate lot size double lots = riskAmount / (stopLossPips * pipValue);
// Round to lot step lots = MathFloor(lots / lotStep) * lotStep;
// Apply limits lots = MathMax(lots, minLot); lots = MathMin(lots, maxLot);
return NormalizeDouble(lots, 2); } `
Usage Example
void OnTick()
{
double stopLossPips = 50;
double riskPercent = 1.0; // 1% risk
double lots = CalculateLotSize(riskPercent, stopLossPips);
Print("Calculated lot size: ", lots);
// Use in trade
PlaceBuyOrder(lots, sl, tp);
}
Dynamic Stop Loss Calculation
ATR-Based Stops
Use Average True Range for volatility-adjusted stops:
int atrHandle;
double atrBuffer[];
int OnInit()
{
atrHandle = iATR(_Symbol, PERIOD_CURRENT, 14);
ArraySetAsSeries(atrBuffer, true);
return(INIT_SUCCEEDED);
}
double CalculateATRStop(double multiplier)
{
if(CopyBuffer(atrHandle, 0, 0, 1, atrBuffer) < 1)
return 0;
double atr = atrBuffer[0];
double stopDistance = atr * multiplier;
return stopDistance;
}
// Usage
double stopDistance = CalculateATRStop(2.0); // 2x ATR
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double stopLoss = bid - stopDistance; // For buy trade
Support/Resistance Stops
`mql5 double FindRecentLow(int bars) { double low = DBL_MAX;
for(int i = 1; i <= bars; i++) { double barLow = iLow(_Symbol, PERIOD_CURRENT, i); if(barLow < low) low = barLow; }
return low; }
double FindRecentHigh(int bars) { double high = 0;
for(int i = 1; i <= bars; i++) { double barHigh = iHigh(_Symbol, PERIOD_CURRENT, i); if(barHigh > high) high = barHigh; }
return high; }
// Usage for buy trade double recentLow = FindRecentLow(20); double buffer = 10 * _Point; // Add buffer double stopLoss = recentLow - buffer; `
Maximum Drawdown Protection
Daily Loss Limit
input double MaxDailyLossPercent = 3.0; // Maximum daily loss %
double dailyStartBalance;
datetime lastResetDay;
int OnInit()
{
dailyStartBalance = AccountInfoDouble(ACCOUNT_BALANCE);
lastResetDay = TimeCurrent();
return(INIT_SUCCEEDED);
}
bool CheckDailyLossLimit()
{
// Reset at new day
MqlDateTime currentTime;
TimeToStruct(TimeCurrent(), currentTime);
MqlDateTime lastReset;
TimeToStruct(lastResetDay, lastReset);
if(currentTime.day != lastReset.day)
{
dailyStartBalance = AccountInfoDouble(ACCOUNT_BALANCE);
lastResetDay = TimeCurrent();
}
// Check current loss
double currentBalance = AccountInfoDouble(ACCOUNT_BALANCE);
double dailyLoss = dailyStartBalance - currentBalance;
double lossPercent = (dailyLoss / dailyStartBalance) * 100;
if(lossPercent >= MaxDailyLossPercent)
{
Print("Daily loss limit reached: ", lossPercent, "%");
return false; // Don't trade
}
return true; // OK to trade
}
void OnTick()
{
if(!CheckDailyLossLimit())
{
// Close all positions and stop trading
CloseAllPositions();
return;
}
// Normal trading logic...
}
Maximum Drawdown Stop
input double MaxDrawdownPercent = 20.0;
double peakBalance;
int OnInit()
{
peakBalance = AccountInfoDouble(ACCOUNT_BALANCE);
return(INIT_SUCCEEDED);
}
bool CheckDrawdownLimit()
{
double currentBalance = AccountInfoDouble(ACCOUNT_BALANCE);
// Update peak
if(currentBalance > peakBalance)
peakBalance = currentBalance;
// Calculate drawdown
double drawdown = peakBalance - currentBalance;
double drawdownPercent = (drawdown / peakBalance) * 100;
if(drawdownPercent >= MaxDrawdownPercent)
{
Print("Maximum drawdown reached: ", drawdownPercent, "%");
Alert("EA STOPPED: Maximum drawdown exceeded!");
ExpertRemove(); // Stop EA
return false;
}
return true;
}
Position Limits
Maximum Open Positions
input int MaxOpenPositions = 3;
bool CanOpenNewPosition()
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0)
{
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber)
count++;
}
}
return (count < MaxOpenPositions);
}
Maximum Lots Per Symbol
input double MaxLotsPerSymbol = 1.0;
double GetCurrentExposure()
{
double totalLots = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0)
{
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
PositionGetInteger(POSITION_MAGIC) == MagicNumber)
{
totalLots += PositionGetDouble(POSITION_VOLUME);
}
}
}
return totalLots;
}
bool CanAddPosition(double newLots)
{
double currentExposure = GetCurrentExposure();
return ((currentExposure + newLots) <= MaxLotsPerSymbol);
}
Complete Risk Manager Class
`mql5 class CRiskManager { private: double m_riskPercent; double m_maxDailyLoss; double m_maxDrawdown; int m_maxPositions; double m_dailyStartBalance; double m_peakBalance; int m_magicNumber;
public: CRiskManager(double risk, double dailyLoss, double drawdown, int maxPos, int magic) { m_riskPercent = risk; m_maxDailyLoss = dailyLoss; m_maxDrawdown = drawdown; m_maxPositions = maxPos; m_magicNumber = magic; m_dailyStartBalance = AccountInfoDouble(ACCOUNT_BALANCE); m_peakBalance = m_dailyStartBalance; }
double CalculateLots(double stopLossPips) { double balance = AccountInfoDouble(ACCOUNT_BALANCE); double riskAmount = balance * m_riskPercent / 100; double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); double pipValue = (tickValue / tickSize) _Point 10;
double lots = riskAmount / (stopLossPips * pipValue); double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
lots = MathFloor(lots / lotStep) * lotStep; lots = MathMax(lots, minLot);
return NormalizeDouble(lots, 2); }
bool CanTrade() { if(!CheckDailyLoss()) return false; if(!CheckDrawdown()) return false; if(!CheckPositionCount()) return false; return true; }
bool CheckDailyLoss() { double balance = AccountInfoDouble(ACCOUNT_BALANCE); double loss = m_dailyStartBalance - balance; double lossPercent = (loss / m_dailyStartBalance) * 100; return (lossPercent < m_maxDailyLoss); }
bool CheckDrawdown() { double balance = AccountInfoDouble(ACCOUNT_BALANCE); if(balance > m_peakBalance) m_peakBalance = balance;
double drawdown = m_peakBalance - balance; double ddPercent = (drawdown / m_peakBalance) * 100; return (ddPercent < m_maxDrawdown); }
bool CheckPositionCount() { int count = 0; for(int i = PositionsTotal() - 1; i >= 0; i--) { if(PositionGetTicket(i) > 0) if(PositionGetInteger(POSITION_MAGIC) == m_magicNumber) count++; } return (count < m_maxPositions); }
void ResetDaily() { m_dailyStartBalance = AccountInfoDouble(ACCOUNT_BALANCE); } };
// Usage CRiskManager* riskMgr;
int OnInit() { riskMgr = new CRiskManager(1.0, 3.0, 20.0, 3, MagicNumber); return(INIT_SUCCEEDED); }
void OnDeinit(const int reason) { delete riskMgr; }
void OnTick() { if(!riskMgr.CanTrade()) { Print("Risk limits reached - no trading"); return; }
double lots = riskMgr.CalculateLots(50); // 50 pip stop // ... trading logic } ``
Key Takeaways
- Never risk more than 1% per trade
- Calculate position size based on stop loss distance
- Use ATR for volatility-adjusted stops
- Implement daily loss limits
- Set maximum drawdown protection
- Limit number of open positions
- Use a risk manager class for clean code
- Always test risk management thoroughly
What's Next?
Learn how to backtest your Expert Advisors to validate performance before live trading.
This article is for educational purposes only and does not constitute financial advice.