How to calculate proper lot size ?

 

Hi,

So i'm trying to build a scalping system that watch the market in 5 minutes timeframe and execute trades based on simple algorithm, until here its all okay (I hope so), but when it comes to lot size its a bit tricky.

The formula i calculate lot size is:

willingToSpend = accountValue * .01 // IN THIS CASE I WANT TO RISK 1% OF MY TRADING ACCOUNT

stopLossDistance = willingToSpend / NUMBER_OF_PIPS 
calculate = stopLossDistance / VALUE_PER_PIP // In this case my account is in euro and i do the math on what's the value per pip for EUR to USD // for example 0.88

And this calculates me lot size for example it prints 0.23.

But as a scalping strategy, this system sometimes executes trades even on distances lower than one pip for example .05 pip and this calculation return value of more then 1 lot. When it touches the TP the value of it is for example $3 profit that means 2 times lower or 3 times lower than 1% of the trading account, but when it touches SL it stops the SL of -$20 or even higher that is 2 times to 3 times bigger that my account value.

But when the trade size more than one pip for example 4 pips (more than one pip SL), this calculation formula somehow it works well, not accurate but its close to the ammount.

So is there any other formula on how can i calculate this more accurately ?

P.S my formula of lot size is based on this: https://www.babypips.com/tools/position-size-calculator

Position Size Calculator - BabyPips.com
  • www.babypips.com
The position size caalculator helps forex traders find the approximate amount of currency units to buy or sell to control your maximum risk per position.
 
Growth Dude:

Hi,

So i'm trying to build a scalping system that watch the market in 5 minutes timeframe and execute trades based on simple algorithm, until here its all okay (I hope so), but when it comes to lot size its a bit tricky.

The formula i calculate lot size is:

willingToSpend = accountValue * .01 // IN THIS CASE I WANT TO RISK 1% OF MY TRADING ACCOUNT

stopLossDistance = willingToSpend / NUMBER_OF_PIPS 
calculate = stopLossDistance / VALUE_PER_PIP // In this case my account is in euro and i do the math on what's the value per pip for EUR to USD // for example 0.88

And this calculates me lot size for example it prints 0.23.

But as a scalping strategy, this system sometimes executes trades even on distances lower than one pip for example .05 pip and this calculation return value of more then 1 lot. When it touches the TP the value of it is for example $3 profit that means 2 times lower or 3 times lower than 1% of the trading account, but when it touches SL it stops the SL of -$20 or even higher that is 2 times to 3 times bigger that my account value.

But when the trade size more than one pip for example 4 pips (more than one pip SL), this calculation formula somehow it works well, not accurate but its close to the ammount.

So is there any other formula on how can i calculate this more accurately ?

P.S my formula of lot size is based on this: https://www.babypips.com/tools/position-size-calculator

Show your code if you need coding help, not pseudo code.

How are you calculating the value per pip, most probably incorrectly ? If you need to convert using a cross you can't have exact value as the price of this cross when your position will be closed is unknown. You can also have slippage which will make your calculation inaccurate and also rounding. All in all don't expect to get the exact amount you want to risk.

 

Risk depends on your initial stop loss, lot size, and the value of the symbol. It does not depend on margin and leverage. No SL means you have infinite risk. Never risk more than a small percentage of your trading funds, certainly less than 2% per trade, 6% total.

  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 - MQL4 programming forum (2017)
              Is there an universal solution for Tick value? - Currency Pairs - General - MQL5 programming forum (2018)
              Lot value calculation off by a factor of 100 - MQL5 programming forum (2019)

  4. You must normalize lots properly and check against min and max.

  5. You must also check FreeMargin to avoid stop out

  6. For MT5, see 'Money Fixed Risk' - MQL5 Code Base (2017)

Most pairs are worth about $10 per PIP. A $5 risk with a (very small) 5 PIP SL is $5/$10/5 or 0.1 Lots maximum.


.
 
Growth Dude: So is there any other formula on how can i calculate this more accurately?

This is untested, uncompiled code, only to serve as a guide for you to further study and implement depending on your own requirements ...

// Calculate Max Lot Size based on Maximum Risk
double dblLotsRisk( double dbStopLoss, double dbRiskRatio )
{
   double
      dbLotsMinimum  = SymbolInfoDouble( _Symbol, SYMBOL_VOLUME_MIN       ),
      dbLotsMaximum  = SymbolInfoDouble( _Symbol, SYMBOL_VOLUME_MAX       ),
      dbLotsStep     = SymbolInfoDouble( _Symbol, SYMBOL_VOLUME_STEP      ),
      dbTickSize     = SymbolInfoDouble( _Symbol, SYMBOL_TRADE_TICK_SIZE  ),
      dbTickValue    = SymbolInfoDouble( _Symbol, SYMBOL_TRADE_TICK_VALUE ),
      dbValueAccount = fmin( fmin( 
                       AccountInfoDouble( ACCOUNT_EQUITY      )  , 
                       AccountInfoDouble( ACCOUNT_BALANCE     ) ),
                       AccountInfoDouble( ACCOUNT_MARGIN_FREE ) ),
      dbValueRisk    = dbValueAccount * dbRiskRatio,
      dbLossOrder    = dbStopLoss * dbTickValue / dbTickSize,
      dbCalcLot      = fmin(  dbLotsMaximum,                  // Prevent too greater volume
                       fmax(  dbLotsMinimum,                  // Prevent too smaller volume
                       round( dbValueRisk / dbLossOrder       // Calculate stop risk
                       / dbLotsStep ) * dbLotsStep ) );       // Align to step value

   return ( dbCalcLot );
};
 
Fernando Carreiro #:

This is untested, uncompiled code, only to serve as a guide for you to further study and implement depending on your own requirements ...

Compiled and tested your code.  It always gives 0.01 lot no matter how big is risk.

 
VikMorroHun #: Compiled and tested your code.  It always gives 0.01 lot no matter how big is risk.

Show us your code. What is the value of your variables (#2.3 footnotes).

 
William Roeder #:

Show us your code. What is the value of your variables (#2.3 footnotes).

Hello William,


The code is already shown above by @ Fernando Carreiro. I used dRiskRatio form 5% to 50%.

My main problem was I couldn't understand what is going on when people added SL, Point, etc. to calculate the _volume_ of a trade.

In my opinion volume (in other word, Lots) has to account for margin and free equity, nothing else.

So I've come up with my own solution.

//+------------------------------------------------------------------+
//|                                                      CalcLot.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict
#property script_show_inputs

input double dInpRisk = 5.0;            // Risk percent
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
{
        double dLots;
// Calculate Max Lot Size based on Maximum Risk
        //dLots = dLotsRisk( -50 * SymbolInfoDouble( Symbol(), SYMBOL_TRADE_TICK_SIZE ), 0.5 );
        dLots = dGetLot( dInpRisk );
}
//+------------------------------------------------------------------+

/*double dLotsRisk( double dStopLoss, double dRiskRatio )
{
        double
                dLotsMinimum  = SymbolInfoDouble( _Symbol, SYMBOL_VOLUME_MIN ),
                dLotsMaximum  = SymbolInfoDouble( _Symbol, SYMBOL_VOLUME_MAX ),
                dLotsStep     = SymbolInfoDouble( _Symbol, SYMBOL_VOLUME_STEP ),
                dTickSize     = SymbolInfoDouble( _Symbol, SYMBOL_TRADE_TICK_SIZE ),
                dTickValue    = SymbolInfoDouble( _Symbol, SYMBOL_TRADE_TICK_VALUE ),
                dValueAccount = fmin( fmin( AccountInfoDouble( ACCOUNT_EQUITY ), AccountInfoDouble( ACCOUNT_BALANCE ) ), AccountInfoDouble( ACCOUNT_MARGIN_FREE ) ),
                dValueRisk    = dValueAccount * dRiskRatio,
                dLossOrder    = dStopLoss * dTickValue / dTickSize,
                dCalcLot      = fmin(  dLotsMaximum,                  // Prevent too greater volume
                       fmax(  dLotsMinimum,                  // Prevent too smaller volume
                       round( dValueRisk / dLossOrder       // Calculate stop risk
                       / dLotsStep ) * dLotsStep ) );       // Align to step value

   return ( dCalcLot );
};*/

double dGetLot( double dRisk )
{
        double dAccountValue, dAccountValueEffective, dLotMin, dLotMax, dLotStep, dCalcMargin, dLots, dFreeMargin;
        int i, iStepMax;
        bool bStat;
        string str;
        
        dAccountValue = MathMin( MathMin( AccountInfoDouble( ACCOUNT_EQUITY ), AccountInfoDouble( ACCOUNT_BALANCE ) ), AccountInfoDouble( ACCOUNT_MARGIN_FREE ) );
        dAccountValueEffective = dRisk * dAccountValue / 100;
        dLotMin = SymbolInfoDouble( _Symbol, SYMBOL_VOLUME_MIN );
        dLotMax = SymbolInfoDouble( _Symbol, SYMBOL_VOLUME_MAX );
        dLotStep = SymbolInfoDouble( _Symbol, SYMBOL_VOLUME_STEP );
        dFreeMargin = AccountInfoDouble( ACCOUNT_MARGIN_FREE );
        iStepMax = ( int ) MathRound ( ( dLotMax - dLotMin ) / dLotStep );              // FRA40: 999, EURUSD: 9999
        ResetLastError();
        for ( i = 0, dLots = dLotMin; i < iStepMax; i++, dLots += dLotStep )
        {
                bStat = OrderCalcMargin( ORDER_TYPE_BUY, Symbol(), dLots, SymbolInfoDouble( Symbol(), SYMBOL_BID ), dCalcMargin);                               //free margin required for x lot!
                if ( bStat )
                {
                        str = "Calculated Margin = " + DoubleToString( dCalcMargin, 2 );
                        str += ", risk = " + DoubleToString( dRisk, 2 );
                        str += ", account value effective = " + DoubleToString( dAccountValueEffective, 2 );
                        str += ", current lot = " + DoubleToString( dLots, 2 );
                        if ( dFreeMargin < dCalcMargin || dCalcMargin > dAccountValueEffective )
                        {
                                dLots -= dLotStep;
                                if ( dLots < dLotMin )
                                {
                                        dLots = dLotMin;str += ", minimum lot size used.";
                                }
                                if ( dLots > dLotMax )
                                {
                                        dLots = dLotMax;str += ", maximum lot size reached.";
                                }
                                Print( str, "\nOverstepping boundary, calculated lot size is ", DoubleToString( dLots, 2 ) );
                                return dLots;
                        }
                        else Print( str );
                }
                else Print( "Margin calculation error, error code: ", IntegerToString( GetLastError() ) );
        }
        return dLots;
}

Initial testing looks promising.

 
VikMorroHun #:

Hello William,


The code is already shown above by @ Fernando Carreiro. I used dRiskRatio form 5% to 50%.

My main problem was I couldn't understand what is going on when people added SL, Point, etc. to calculate the _volume_ of a trade.

In my opinion volume (in other word, Lots) has to account for margin and free equity, nothing else.

So I've come up with my own solution.

Initial testing looks promising.

The risk of a trade depends on the money your trade will loose if it will hit stop loss, margin doesn't matter.

If you don't use a stop loss your risk is theorically infinite (means equal to the whole account). 

So, only in this last case using the free margin can be useful for calculating, but anyway, the "risk" is just a factor based on the free margin and not the amount of money that your trade can potentially loose.
 

For further reference,

This worked for my pretty much flawlessly, I only tried it with the GBPJPY pair, but it should work for all/most of them.
I politely took some ideas from @Fernando Carreiro :)


double LotsToBuyPerLostPercentage(         
   double entry_price,
   double stop_loss_price,
   double percentage_to_lose){

         // Get Symbol Info
         double lots_maximum = SymbolInfoDouble( Symbol(), SYMBOL_VOLUME_MAX);
         double lots_minimum = SymbolInfoDouble( Symbol(), SYMBOL_VOLUME_MIN);
         double volume_step = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP);
         double tick_size = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_SIZE);
         double tick_value = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_VALUE);         
        
         // Get trade basic info
         double available_capital = fmin( fmin( AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE)), AccountInfoDouble(ACCOUNT_MARGIN_FREE));
         double amount_to_risk = available_capital * percentage_to_lose / 100;
         double sl_distance = MathAbs(entry_price - stop_loss_price); // Get the Abs since it might be a short (EP < SL)
         
         // Calculate steps and lots
         double money_step = sl_distance / tick_size * tick_value * volume_step;
         double lots = fmin( lots_maximum, fmax(lots_minimum, NormalizeDouble(amount_to_risk  / money_step, 2))); // The number 2 is due to my brokers volume step, depends on the currency pair
         return(lots);
      }
 
https://www.mql5.com/en/code/28029


 
//+------------------------------------------------------------------+
//| Calculate the appropriate volume for the trade operation planned.|
//|                                                                  |
//| ordertype      : ORDER_TYPE_BUY or ORDER_TYPE_SELL only.         |
//| symbol         : Symbol name                                     |
//| risk_money     : Loss money when SL is hit, in account currency. |
//| price_open     : Open price                                      |
//| price_sl       : Close price                                     |
//| commission_lot : Comm. per lot per side, in account currency.    |
//+------------------------------------------------------------------+
double OrderCalcVolume(ENUM_ORDER_TYPE ordertype, string symbol, double risk_money, double price_open, double price_sl, double commission_lot = 0.0)
 
Forex Calculators
Forex Calculators
  • www.mql5.com
Margin Calculator, Point Value Calculator, Position Size Calculator, Profit Calculator and Swap Calculator.
 
There is a little mistake in my code, here's the corrected version:

double LotsToBuyPerLostPercentage(         
   double entry_price,
   double stop_loss_price,
   double percentage_to_lose){

         // Get Symbol Info
         double lots_maximum = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MAX);
         double lots_minimum = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN);
         double volume_step = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP);
         double tick_size = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_SIZE);
         double tick_value = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_VALUE);         
        
         // Get trade basic info
         double available_capital = fmin( fmin( AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE)), AccountInfoDouble(ACCOUNT_MARGIN_FREE));
         double amount_to_risk = available_capital * percentage_to_lose / 100;
         double sl_distance = MathAbs(entry_price - stop_loss_price); // Get the Abs since it might be a short (EP < SL)
         
         // Calculate steps and lots
         double money_step = sl_distance / tick_size * tick_value * volume_step;
         double lots = fmin( lots_maximum, fmax(lots_minimum, NormalizeDouble(amount_to_risk / money_step * volume_step, 2))); // The number 2 is due to my brokers volume step, depends on the currency pair
         return(lots);
      }
Reason: