Correct way to calculate lot size

 

There are many different ways to calculate the lot size in MT4 platform. But most of the examples on the web are neither meaningful nor useful in a robust indicator/expert code. Following is the code I used in my indicators/experts to calculate the lot/volume size properly. Main advantage is this algorithm can be applied to all the markets/assets available for trading in MT4 platform. Ex. Currency pairs, Indices...


double CalculateLotSize(string argSymbol, double argRiskDecimal, int argStoplossPoints, int argExtraPriceGapPoints, double argAllowedMaxLotSize)
{
        // Calculate LotSize based on Equity, Risk in decimal and StopLoss in points
        double _availableMoney, _maxLotByEquity, _maxLot, _minLot, _lotSize1, _lotSize2, _lotSize;
        int _lotdigit;
           
        // Calculate margin required for 1 lot
        double _marginForOneLot = MarketInfo(argSymbol, MODE_MARGINREQUIRED); 
        // Step in lot size changing
        double _lotStep = MarketInfo(argSymbol, MODE_LOTSTEP); 
        // Amount of money in base currency for 1 lot
        double _lotBase = MarketInfo ( Symbol(), MODE_LOTSIZE );
        
        if ( _lotStep ==  1) 
                _lotdigit = 0;
        if ( _lotStep == 0.1 )  
                _lotdigit = 1;
        if ( _lotStep == 0.01 ) 
                _lotdigit = 2;

        // Get available money as Equity
        _availableMoney = AccountEquity();
        // Maximum allowed Lot by the broker according to Equity. And we don't use 100% but 98%
        _maxLotByEquity = MathFloor(_availableMoney * 0.98 / _marginForOneLot / _lotStep) * _lotStep;
        _maxLot = MathMin(_maxLotByEquity, MathMin(argAllowedMaxLotSize, MarketInfo(argSymbol, MODE_MAXLOT)));
        // Minimum allowed Lot by the broker
        _minLot = MarketInfo(argSymbol, MODE_MINLOT);
        // Lot according to Risk. 
        _lotSize1 = MathFloor ( argRiskDecimal * _availableMoney / ( argStoplossPoints + argExtraPriceGapPoints ) / _lotStep ) * _lotStep;
        _lotSize2 = _lotSize1 * Multiplicator(""); 
        _lotSize = MathMax(MathMin(_lotSize2, _maxLot), _minLot);
        _lotSize = NormalizeDouble ( _lotSize, _lotdigit );

        return ( _lotSize );
}

double Multiplicator(string currencyPairAppendix="")
{

        double _multiplicator = 1.0;
        
         if ( AccountCurrency() == "USD" )
                return ( _multiplicator );
        if ( AccountCurrency() == "EUR" ) 
                _multiplicator = 1.0 / MarketInfo ( "EURUSD" + currencyPairAppendix, MODE_BID );
        if ( AccountCurrency() == "GBP" ) 
                _multiplicator = 1.0 / MarketInfo ( "GBPUSD" + currencyPairAppendix, MODE_BID );
        if ( AccountCurrency() == "AUD" ) 
                _multiplicator = 1.0 / MarketInfo ( "AUDUSD" + currencyPairAppendix, MODE_BID );         
        if ( AccountCurrency() == "NZD" ) 
                _multiplicator = 1.0 / MarketInfo ( "NZDUSD" + currencyPairAppendix, MODE_BID );         
        if ( AccountCurrency() == "CHF" ) 
                _multiplicator = MarketInfo ( "USDCHF" + currencyPairAppendix, MODE_BID );
        if ( AccountCurrency() == "JPY" ) 
                _multiplicator = MarketInfo ( "USDJPY" + currencyPairAppendix, MODE_BID );
        if ( AccountCurrency() == "CAD" ) 
                _multiplicator = MarketInfo ( "USDCAD" + currencyPairAppendix, MODE_BID );               
        if ( _multiplicator == 0 )
                _multiplicator = 1.0; // If account currency is neither of EUR, GBP, AUD, NZD, CHF, JPY or CAD we assume that it is USD
        return ( _multiplicator );
}
 
If you are worried about margin and leverage, you are not controlling your risk. Never risk more than a small percentage of your account, certainly less than 2% per trade, 6% total. In code (MT4): Risk depends on your initial stop loss, lot size, and the value of the pair.
  1. You place the stop where it needs to be - where the reason for the trade is no longer valid. E.g. trading a support bounce the stop goes below the support.
  2. AccountBalance * percent/100 = RISK = OrderLots * (|OrderOpenPrice - OrderStopLoss| * DeltaPerLot + CommissionPerLot) (Note OOP-OSL includes the spread, and DeltaPerLot is usually around $10/pip but it takes account of the exchange rates of the pair vs. your account currency.)
  3. Do NOT use TickValue by itself - DeltaPerLot and verify that MODE_TICKVALUE is returning a value in your deposit currency, as promised by the documentation, or whether it is returning a value in the instrument's base currency.
              MODE_TICKVALUE is not reliable on non-fx instruments with many brokers.
  4. You must normalize lots properly and check against min and max.
  5. You must also check FreeMargin to avoid stop out
Most pairs are worth about $10 per PIP SL is $5/$10/5=0.1 Lots maximum.
 

Thanks William. The above function actually pays attention to all of the rules other than 5.I have intentionally ignored FreeMarginCheck() function usage because it can only be used in MQ4 expert advisors only.


 1. It accepts a relevant stop loss distance defined by points ( Pips * 10)  -

argStoplossPoints

 2. It accepts a given risk percentage that the trader is comfortable with. ( if it 2% then the parameter value must be 0.02 )

argRiskDecimal

 3. It calculates the lot size according to the risk and stop loss distance using  MODE_LOTSTEP and not rely on unreliable  MODE_TICKVALUE. (  MODE_TICKVALUE is not get updated by the Broker consistently )

    Calculated lot size

_lotSize1 = MathFloor ( argRiskDecimal * _availableMoney / ( argStoplossPoints + argExtraPriceGapPoints ) / _lotStep ) * _lotStep;

 4. It normalizes the calculated lot size against following 4 lot sizes.

      Calculated lot size >= Minimum lot size allowed by the Broker.

      Calculated lot size <= Maximum lot size allowed by the Broker.

      Calculated lot size <= Maximum lot size possible with the current equity.

      Calculated lot size <= Maximum lot size allowed by the trader.

 

Hey all!

I use this in all my codes to get a dynamic lot based on the risk I want to asume on each trade.

I'll leave it here, so it might be usefull to others and maybe improved.

   double PipValue=(((SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_VALUE))*Point())/(SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE)));
   double stopLossPips=(1.5*atrBuff[0])/Point();
   double totalLots=Risk*AccountInfoDouble(ACCOUNT_EQUITY)/(PipValue*stopLossPips);
   totalLots=floor(totalLots*100)/100;

I frequently use 1.5 ATR for placing stoloss, but you can change this to whatever your need is!

Regards,

PS

 

Yes Pedro,


I use 1.5 * ATR to calculate the stop loss distance too. Following is the custom indicator used in all my manual trading.

https://www.mql5.com/en/code/25484

Money Managment Indicator
Money Managment Indicator
  • www.mql5.com
This indicator allows trader to calculate the proper lot size based on percentage risk and stop loss. The stop loss can be defined either using Average True Range ratio or fixed points.
 
Pedro Severin #:

Hey all!

I use this in all my codes to get a dynamic lot based on the risk I want to asume on each trade.

I'll leave it here, so it might be usefull to others and maybe improved.

I frequently use 1.5 ATR for placing stoloss, but you can change this to whatever your need is!

Regards,

PS

Don't think it works correctly. For the XAUUSD, for example, the value of totalLots would be HUGE.
 

I have improved the lot size calculation mql5 code with the permitted maximum lot size for the margin. This is more like a final implementation of the lot size calculation.

The parameter definitions are: 

pMarket: The market symbol

pMoneyCapital: The account balance

pRiskPercentage: Risk as a percentage; 1.0 means 1.0% risk

pStoplossPoints: Stoploss distance in points (not pips)

pAllowedMaxUnitSize: If you have a preferred maximum lot size 


   double CalculateUnitSize(string pMarket, double pMoneyCapital, double pRiskPercentage, int pStoplossPoints, double pAllowedMaxUnitSize) 
   {
      //---Calculate LotSize based on Equity, Risk in decimal and StopLoss in points
      double maxLots, minLots, oneTickValue, moneyRisk, lotsByRisk, lotSize;
      int totalTickCount;

      maxLots = MaxUnitSizeAllowedForMargin(pMarket, pMoneyCapital, pAllowedMaxUnitSize);
      minLots = SymbolInfoDouble(pMarket, SYMBOL_VOLUME_MIN);
      oneTickValue = SymbolInfoDouble(pMarket, SYMBOL_TRADE_TICK_VALUE); // Tick value of the asset

      moneyRisk = (pRiskPercentage/100) * pMoneyCapital;
      totalTickCount = ToTicksCount(pMarket, pStoplossPoints);

      //---Calculate the Lot size according to Risk.
      lotsByRisk = moneyRisk / (totalTickCount * oneTickValue);
      lotSize = MathMax(MathMin(lotsByRisk, maxLots), minLots);      
      lotSize = NormalizeLots(pMarket, lotSize);
      return (lotSize);
   }

   double MaxUnitSizeAllowedForMargin(string pMarket, double pMoneyCapital, double pAllowedMaxUnitSize) 
   {
      // Calculate Lot size according to Equity.
      double marginForOneLot, lotsPossible;
      if(OrderCalcMargin(ORDER_TYPE_BUY, pMarket, 1, SymbolInfoDouble(pMarket, SYMBOL_ASK), marginForOneLot)) { // Calculate margin required for 1 lot
         lotsPossible = pMoneyCapital * 0.98 / marginForOneLot;
         lotsPossible = MathMin(lotsPossible, MathMin(pAllowedMaxUnitSize, SymbolInfoDouble(pMarket, SYMBOL_VOLUME_MAX)));
         lotsPossible = NormalizeLots(pMarket, lotsPossible);
      } else {
         lotsPossible = SymbolInfoDouble(pMarket, SYMBOL_VOLUME_MAX);
      }   
      return (lotsPossible);
   }

   int ToTicksCount(string pMarket, uint pPointsCount) 
   {
      double uticksize = SymbolInfoDouble(pMarket, SYMBOL_TRADE_TICK_SIZE);
      int utickscount = uticksize > 0 ? (int)((pPointsCount / uticksize) * uticksize) : 0; //-- fix prices by ticksize
      return utickscount;
   }

   double NormalizeLots(string pMarket, double pLots) {       
      double lotstep   = SymbolInfoDouble(pMarket, SYMBOL_VOLUME_STEP);
      int lotdigits    = (int) - MathLog10(lotstep);
      return NormalizeDouble(pLots, lotdigits);   
   } 
Reason: