Complete .mq5 source code

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, save it in the MQL5/Experts folder, and compile with F7.

Generate a custom Multi-pair educational EA

Describe the ATR inputs, alert rules, visual style, or confirmation filter you want. Pineify generates editable MQL5 source code that you can inspect and compile in MetaEditor.

Pine Script vs MQL5: Same Strategy, Different Platforms

AspectPine Script (TradingView)MQL5 (MetaTrader 5)
ExecutionSeries evaluated on chart updatesEvent handlers with explicit buffers
DeploymentRuns in TradingView with alertsRuns in the MT5 terminal or on a VPS
Broker accessVia TradingView broker integrationDirect broker connectivity
BacktestingStrategy Tester for strategy scriptsStrategy Tester for Expert Advisors
Code complexitySimpler, functional syntaxC++-like, more powerful

The calculation can be implemented on either platform, but the runtime model and buffer APIs differ. Read the Pine Script Supertrend reference before porting logic between them.

Frequently Asked Questions

Related MQL5 pages

Risk and testing note

Past performance is not indicative of future results. Backtest statistics are based on historical data and do not guarantee future profits. Trading involves significant risk of loss. This content is for educational purposes only and does not constitute financial advice.