MQL5 Risk Management: Position Sizing Code Templates & Guide
This page provides production-ready MQL5 code templates for implementing professional risk management in Expert Advisors, covering percentage-based position sizing, dynamic stop-loss placement, and account drawdown controls. Whether you are building your first EA or hardening an existing system, these patterns will help you protect capital while maximizing consistency across multiple currency pairs.
Strategy Logic
Entry Conditions
N/A — this is a tutorial/indicator reference page. The example code demonstrates how to calculate a proper lot size before entering any trade, not a specific entry signal. Actual entry signals (moving average crossovers, breakouts, etc.) are kept minimal so the risk logic remains the focus.
Exit Conditions
N/A — this is a tutorial/indicator reference page. The code shows how to attach a correctly sized stop-loss and take-profit to every order so that the risk-to-reward ratio is enforced automatically at the moment of order placement.
MQL5 Expert Advisor Code
//+------------------------------------------------------------------+
//| RiskManagementEA.mq5 |
//| Tutorial: Professional position sizing and risk controls |
//| For educational purposes — test thoroughly before live use |
//+------------------------------------------------------------------+
#property copyright "Pineify Educational Series"
#property version "1.00"
#property strict
//--- Input parameters
input double RiskPercent = 1.0; // Risk per trade (% of account balance)
input double RewardRatio = 2.0; // Reward-to-risk ratio (R:R)
input int StopLossPips = 30; // Stop-loss distance in pips
input int MagicNumber = 202401; // Unique EA identifier
input int MaxOpenTrades = 3; // Maximum simultaneous positions
input double MaxDailyLossPct = 3.0; // Daily loss circuit-breaker (% of balance)
input int MA_Period = 50; // Moving average period for trend filter
input ENUM_TIMEFRAMES MA_TF = PERIOD_H1; // MA timeframe
//--- Global variables
int g_maHandle = INVALID_HANDLE;
double g_dailyStart = 0.0;
datetime g_lastBarTime = 0;
//+------------------------------------------------------------------+
//| Expert initialisation |
//+------------------------------------------------------------------+
int OnInit()
{
// Create MA handle for simple trend filter
g_maHandle = iMA(_Symbol, MA_TF, MA_Period, 0, MODE_EMA, PRICE_CLOSE);
if(g_maHandle == INVALID_HANDLE)
{
Print("ERROR: Failed to create MA indicator handle");
return INIT_FAILED;
}
// Record starting balance for daily loss tracking
g_dailyStart = AccountInfoDouble(ACCOUNT_BALANCE);
Print("RiskManagementEA initialised on ", _Symbol,
" | Risk: ", RiskPercent, "% | SL: ", StopLossPips, " pips");
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert deinitialization |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(g_maHandle != INVALID_HANDLE)
IndicatorRelease(g_maHandle);
Print("RiskManagementEA removed. Reason code: ", reason);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- Only act on new bar to avoid re-entry noise
datetime currentBar = iTime(_Symbol, PERIOD_CURRENT, 0);
if(currentBar == g_lastBarTime)
return;
g_lastBarTime = currentBar;
//--- Circuit-breaker: halt trading if daily loss limit exceeded
if(IsDailyLossBreached())
{
Print("Daily loss limit reached. No new trades today.");
return;
}
//--- Respect maximum open-trade cap
if(CountOpenPositions() >= MaxOpenTrades)
return;
//--- Read MA for trend direction
double maBuffer[];
ArraySetAsSeries(maBuffer, true);
if(CopyBuffer(g_maHandle, 0, 0, 3, maBuffer) < 3)
return;
double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double maValue = maBuffer[1]; // confirmed previous bar value
//--- Simple demo entry: long above MA, short below MA
if(price > maValue && CountOpenPositions() == 0)
OpenTrade(ORDER_TYPE_BUY);
else if(price < maValue && CountOpenPositions() == 0)
OpenTrade(ORDER_TYPE_SELL);
}
//+------------------------------------------------------------------+
//| Calculate lot size based on account risk percentage |
//+------------------------------------------------------------------+
double CalculateLotSize(double stopLossPips)
{
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskAmount = balance * (RiskPercent / 100.0);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
// Convert pips to points (accounts for 5-digit brokers)
double pipInPoints = 10.0 * point;
double slInPoints = stopLossPips * pipInPoints;
if(tickSize == 0 || tickValue == 0 || slInPoints == 0)
return 0;
double moneyPerLotPerPip = (pipInPoints / tickSize) * tickValue;
double rawLots = riskAmount / (stopLossPips * moneyPerLotPerPip);
// Normalise to broker lot-step constraints
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lots = MathFloor(rawLots / lotStep) * lotStep;
lots = MathMax(minLot, MathMin(maxLot, lots));
return NormalizeDouble(lots, 2);
}
//+------------------------------------------------------------------+
//| Open a trade with risk-managed SL and TP |
//+------------------------------------------------------------------+
bool OpenTrade(ENUM_ORDER_TYPE orderType)
{
double lots = CalculateLotSize(StopLossPips);
if(lots <= 0)
{
Print("Lot size calculation returned 0 — trade skipped.");
return false;
}
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
double pipPoints = 10.0 * point;
double slDistance = StopLossPips * pipPoints;
double tpDistance = slDistance * RewardRatio;
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double entryPrice, sl, tp;
if(orderType == ORDER_TYPE_BUY)
{
entryPrice = ask;
sl = NormalizeDouble(entryPrice - slDistance, _Digits);
tp = NormalizeDouble(entryPrice + tpDistance, _Digits);
}
else
{
entryPrice = bid;
sl = NormalizeDouble(entryPrice + slDistance, _Digits);
tp = NormalizeDouble(entryPrice - tpDistance, _Digits);
}
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = lots;
request.type = orderType;
request.price = entryPrice;
request.sl = sl;
request.tp = tp;
request.deviation = 10;
request.magic = MagicNumber;
request.comment = StringFormat("Risk %.1f%% SL%dpips", RiskPercent, StopLossPips);
request.type_filling = ORDER_FILLING_FOK;
bool sent = OrderSend(request, result);
if(!sent || result.retcode != TRADE_RETCODE_DONE)
{
Print("OrderSend failed. Retcode: ", result.retcode,
" | Lots: ", lots, " | SL: ", sl, " | TP: ", tp);
return false;
}
Print("Trade opened | ", EnumToString(orderType),
" | Lots: ", lots,
" | Entry: ", entryPrice,
" | SL: ", sl,
" | TP: ", tp,
" | Risk$: ", NormalizeDouble(AccountInfoDouble(ACCOUNT_BALANCE) * RiskPercent / 100.0, 2));
return true;
}
//+------------------------------------------------------------------+
//| Count open positions for this EA on this symbol |
//+------------------------------------------------------------------+
int CountOpenPositions()
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 &&
PositionGetString(POSITION_SYMBOL) == _Symbol &&
(int)PositionGetInteger(POSITION_MAGIC) == MagicNumber)
count++;
}
return count;
}
//+------------------------------------------------------------------+
//| Check if the daily loss circuit-breaker has been triggered |
//+------------------------------------------------------------------+
bool IsDailyLossBreached()
{
double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
double lossPercent = ((g_dailyStart - currentEquity) / g_dailyStart) * 100.0;
return lossPercent >= MaxDailyLossPct;
}
//+------------------------------------------------------------------+Copy this code into MetaEditor (F4 in MT5), save in the Experts folder, and compile with F7.
Generate a Custom Multi-pair Educational EA →
Pineify AI generates syntactically validated MQL5 Expert Advisors from plain English descriptions. Customize entry logic, risk management, and trading sessions — no coding required.
Pine Script vs MQL5: Same Strategy, Different Platforms
| Aspect | Pine Script (TradingView) | MQL5 (MetaTrader 5) |
|---|---|---|
| Execution | Bar-based, backtesting only | Tick-based, live trading |
| Deployment | TradingView alerts | Runs 24/5 on VPS/MT5 |
| Broker access | Via TradingView broker integration | Direct broker connectivity |
| Backtesting | Built-in, no data download needed | Strategy Tester, tick data required |
| Code complexity | Simpler, functional syntax | C++-like, more powerful |
Pineify supports both platforms. Prototype your strategy visually in TradingView Pine Script, then generate the equivalent MQL5 EA for live MT5 trading.