Complete MQL5 guide & code

MQL5 Risk Management: Position Sizing Code Templates & Guide

This page provides educational MQL5 code for estimating position size from a requested entry and stop, rounding volume down, and blocking new entries after an initialization-baseline loss threshold. These are testable controls, not a guarantee of a fixed loss or broker portability.

Implementation details & mechanics

Core implementation pattern

The EA uses the current bid relative to the last completed MA_TF moving average as a simple demonstration signal, checked once per chart bar. OrderCalcProfit estimates the loss at the requested stop for a reference volume; the calculated size rounds down to the symbol step and skips a trade below minimum volume. Only one matching symbol-and-magic position is allowed.

Error handling & safety rules

Orders request absolute initial SL and TP prices. An account-equity decline from the balance captured at initialization latches an entry block; it neither closes existing positions nor resets at midnight. Slippage, gaps, costs, rejected requests and other positions can exceed the estimate. Restarting resets this in-memory guard; it is not a funded-account daily-loss implementation.

MQL5 Code Implementation

//+------------------------------------------------------------------+
//|  RiskManagementEA.mq5                                            |
//|  Tutorial: Illustrative position sizing and entry 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 double   MaxBaselineLossPct  = 3.0;   // Entry block from initialization 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_startBalance = 0.0;
bool g_halted = false;
datetime g_lastBarTime = 0;

//+------------------------------------------------------------------+
//| Expert initialisation                                             |
//+------------------------------------------------------------------+
int OnInit()
  {
   if(RiskPercent<=0 || RiskPercent>100 || RewardRatio<=0 || StopLossPips<=0
      || MaxBaselineLossPct<=0 || MaxBaselineLossPct>=100 || MA_Period<=0 || MagicNumber<=0)
      return INIT_PARAMETERS_INCORRECT;
   if(AccountInfoDouble(ACCOUNT_BALANCE)<=0) return INIT_FAILED;
   // 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 initialization balance; this is not a daily reset
   g_startBalance = 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()
  {
   // Guard is evaluated on ticks and remains latched until reinitialization.
   if(IsBaselineLossBreached()) g_halted=true;
   if(g_halted) return;
   //--- Only act on new bar to avoid re-entry noise
   datetime currentBar = iTime(_Symbol, PERIOD_CURRENT, 0);
   if(currentBar == g_lastBarTime)
      return;
   if(currentBar<=0 || CountOpenPositions()>0) return;

   //--- Read MA for trend direction
   double maBuffer[];
   ArraySetAsSeries(maBuffer, true);
   if(CopyBuffer(g_maHandle, 0, 0, 3, maBuffer) < 3)
      return;

   g_lastBarTime = currentBar;
   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(ENUM_ORDER_TYPE side,double entry,double stop)
  {
   double riskAmount=AccountInfoDouble(ACCOUNT_BALANCE)*RiskPercent/100.0;
   double step=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
   double minimum=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
   double maximum=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
   if(step<=0 || minimum<=0 || maximum<=0 || riskAmount<=0) return 0;
   double referenceProfit=0;
   if(!OrderCalcProfit(side,_Symbol,minimum,entry,stop,referenceProfit) || referenceProfit>=0)
      return 0;
   double raw=riskAmount*minimum/MathAbs(referenceProfit);
   double volume=NormalizeDouble(MathFloor(MathMin(raw,maximum)/step)*step,8);
   return volume>=minimum?volume:0;
  }

//+------------------------------------------------------------------+
//| Open a trade with risk-managed SL and TP                         |
//+------------------------------------------------------------------+
bool OpenTrade(ENUM_ORDER_TYPE orderType)
  {
   double point       = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   double pipPoints   = (_Digits==3 || _Digits==5)?10.0*point: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);
     }

   double lots=CalculateLotSize(orderType,entryPrice,sl);
   if(lots<=0) { Print("Cannot size within budget and volume bounds"); return false; }
   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 the initialization-baseline entry block       |
//+------------------------------------------------------------------+
bool IsBaselineLossBreached()
  {
   double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
   double lossPercent   = ((g_startBalance - currentEquity) / g_startBalance) * 100.0;
   return lossPercent >= MaxBaselineLossPct;
  }
//+------------------------------------------------------------------+

Copy this code into MetaEditor, save it in your MQL5 folder (Experts, Scripts, or Include), and compile with F7.

Generate custom MQL5 code with Pineify AI

Describe the trade execution, indicator calculations, risk management, or data processing logic you need. Pineify generates editable MQL5 source code. Review its logic, request a syntax check, and compile and test the final file 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 accessStrategy orders are simulated; live automation needs a separate 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.

Frequently Asked Questions

Related MQL5 pages

Risk and testing note

These educational examples require independent compilation and testing. A compile pass or an illustrative setting does not establish execution correctness or profitability. Any reported performance must be supported by reproducible data and test settings; past results do not predict future returns. Trading involves significant risk of loss. This content is not financial advice.