Comprehensive lot size calculation function for mql4

 

Right after spending basically all day researching and double checking bits of code from all over the place, I didn't find a nice, reliable, but simple and easy to implement function in one place that I could drop into my code which would give me the lot size, adjusted for JPY and 3/5 digit brokers and based on a risk percentage of the margin in my account.  OK, I know the code below may not be complete, but from a few tests with different pairs it looks like it correctly calculates lot size when I print out what it is doing and compare the manually calculated values in an online pos size calculator.

For the benefit of the world at large, here is the code, and my question is: is this code (a) correct as far as you're concerned as regards the lot size calculations listed, and (b) the most efficient way of doing it to your mind?

Thanks

In init(), note calculation of tickValue:

    if(Point == 0.00001) {
        vPoint = 0.0001;
        vSlippage = slippage * 10;
        tickValue = tickValue * 10;
    } else
    if(Point == 0.001) {
        vPoint = 0.01;
        vSlippage = slippage * 10;
        tickValue = tickValue * 10;
    } else {
        vPoint = Point();
        vSlippage = slippage;
    }


then bearing in mind stopLoss is an integer in (adjusted) pips:

//+------------------------------------------------------------------+
//| Return lot size for trade                                                                            |
//+------------------------------------------------------------------+
//
double getLotSize() {
    string baseCurr = StringSubstr(Symbol(),0,3);
    string crossCurr = StringSubstr(Symbol(),3,3);

    // calculate risk based on free margin
    // 
    double riskCapital = AccountFreeMargin() * risk/100;
    
    if(crossCurr == AccountCurrency()) {
        lots = (riskCapital / stopLoss) / tickValue;
        Print("Calculated lots (A/C currency = Quote currency): ", lots);
    } else if(baseCurr == AccountCurrency()) {
        // 
        lots = ((Ask * riskCapital) / stopLoss) / tickValue;
        Print("Calculated lots (A/C currency = Base currency): ", lots);
    } else if(crossCurr != AccountCurrency()
           && baseCurr  != AccountCurrency()) {
        lots = riskCapital / (stopLoss * tickValue);
        Print("Calculated lots (A/C currency neither Base nor Quote currency): ", lots);
    }
    
    // snap lot size to broker-happy value
    //
    double  lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
    double  minLot  = MarketInfo(Symbol(), MODE_MINLOT);
    lots            = MathRound(lots/lotStep) * lotStep;
    
    if(lots < minLot) {
        lots = minLot;
    }
    
    return(lots);
}
 
  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. Account Balance * percent = RISK = (OrderOpenPrice - OrderStopLoss)*DIR * OrderLots * DeltaPerlot (Note OOP-OSL includes the SPREAD)
  3. Do NOT use TickValue by itself - DeltaPerlot
  4. You must also check FreeMargin to avoid stop out
 

 . . .  my question is: is this code (a) correct as far as you're concerned as regards the lot size calculations listed, and (b) the most efficient way of doing it to your mind?  . . .

My answer is: no and no.

Why did you vary your lot calculation (replicated below for illustration) based on whether the account currency was part of the currency pair? 

    ...
    if(crossCurr == AccountCurrency()) {
        lots = (riskCapital / stopLoss) / tickValue;
        Print("Calculated lots (A/C currency = Quote currency): ", lots);
    } else if(baseCurr == AccountCurrency()) {
        // 
        lots = ((Ask * riskCapital) / stopLoss) / tickValue;
        Print("Calculated lots (A/C currency = Base currency): ", lots);
    } else if(crossCurr != AccountCurrency()
           && baseCurr  != AccountCurrency()) {
        lots = riskCapital / (stopLoss * tickValue);
        Print("Calculated lots (A/C currency neither Base nor Quote currency): ", lots);
    }
    ...

If RiskAmount, StoplossValue, and PointValue (i.e., a ratio of tickvalue and ticksize) are all denominated in deposit currency, why the need to determine if the account currency is part of the currency pair?  I don't believe you need to do this.

 

bearing in mind stopLoss is an integer in (adjusted) pips

I'm not sure what you mean by "adjusted", but stoploss in this calculation (i.e., lotsize = RiskAmount / StoplossValue) should be a value in deposit currency, not a size in pips or points.  

  

lots            = MathRound(lots/lotStep) * lotStep;

In the above code snippet, what will happen when you round up?  The answer is that you will risk more than you defined in your riskCapital.  I believe the better course is to always round down (using MathFloor()).

 

if(lots < minLot) {
    lots = minLot;
}

What about maxlot?  While most of us will probably never reach our brokers' defined maxlot (mine is 50 standard lots and I'm still only trading at <=1 standard lot), it is better to check the calculated lotsize against maxlot just to make sure. 

 

The following is the fixed fractional position size function that I'm currently using.  It isn't perfect, but it might give you some insight to my comments above.

double PositionSize_FixedFractional(int StoplossDistance_inPoints, double RiskPercent, double AcctBalance = 0) {
   double lotstep = MarketInfo(Symbol(), MODE_LOTSTEP), 
          minlot = MarketInfo(Symbol(), MODE_MINLOT), 
          maxlot = MarketInfo(Symbol(), MODE_MAXLOT);
        
   // validate passed parameters
   if (RiskPercent <= 0 || StoplossDistance_inPoints <= 0)
      return (minlot);
   else if (AcctBalance <= 0)
      AcctBalance = AccountBalance();
      
   // calculate raw lotsize
   double PointValue = MarketInfo(Symbol(), MODE_TICKVALUE) / (MarketInfo(Symbol(), MODE_TICKSIZE) / Point),
          RiskAmount = MathFloor(AcctBalance * RiskPercent / 0.01) * 0.01,
          StoplossValue = StoplossDistance_inPoints * PointValue,
          lotsize = RiskAmount / StoplossValue;
                   
   // condition raw lotsize
   lotsize = MathFloor(lotsize / lotstep) * lotstep;
   lotsize = MathMax(MathMin(lotsize, maxlot), minlot);
   
   // return conditioned lotsize
   return (lotsize);
}

 
 double PointValue = MarketInfo(Symbol(), MODE_TICKVALUE) / (MarketInfo(Symbol(), MODE_TICKSIZE) / Point),
  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. Account Balance * percent = RISK = (OrderOpenPrice - OrderStopLoss)*DIR * OrderLots * DeltaPerlot (Note OOP-OSL includes the SPREAD)
  3. Do NOT use TickValue by itself - DeltaPerlot
  4. You must also check FreeMargin to avoid stop out
 
WHRoeder:
  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. Account Balance * percent = RISK = (OrderOpenPrice - OrderStopLoss)*DIR * OrderLots * DeltaPerlot (Note OOP-OSL includes the SPREAD)
  3. Do NOT use TickValue by itself - DeltaPerlot
  4. You must also check FreeMargin to avoid stop out


WHRoeder,

Re what you put:

1. don't understand what you mean, please clarify

2. I've already calculated risk, so I don't understand your point here either.

3. You presumably have predicted that in my code, tickValue is calculated as

double tickValue            = (MarketInfo(Symbol(),MODE_TICKVALUE));


and from your other forum post of DeltaPerLot, it should be

double tickValue            = MarketInfo(Symbol(),MODE_TICKVALUE) / MarketInfo(Symbol(),MODE_TICKSIZE;


?

4. I am already checking AccountFreeMargin in the line which assigns a value to riskCapital - or did you mean elsewhere?

Thanks

 
Thirteen:

My answer is: no and no.

Why did you vary your lot calculation (replicated below for illustration) based on whether the account currency was part of the currency pair? 

If RiskAmount, StoplossValue, and PointValue (i.e., a ratio of tickvalue and ticksize) are all denominated in deposit currency, why the need to determine if the account currency is part of the currency pair?  I don't believe you need to do this.

I'm not sure what you mean by "adjusted", but stoploss in this calculation (i.e., lotsize = RiskAmount / StoplossValue) should be a value in deposit currency, not a size in pips or points.  

In the above code snippet, what will happen when you round up?  The answer is that you will risk more than you defined in your riskCapital.  I believe the better course is to always round down (using MathFloor())

What about maxlot?  While most of us will probably never reach our brokers' defined maxlot (mine is 50 standard lots and I'm still only trading at <=1 standard lot), it is better to check the calculated lotsize against maxlot just to make sure. 

The following is the fixed fractional position size function that I'm currently using.  It isn't perfect, but it might give you some insight to my comments above.


Thirteen, thanks for your input.


I varied my calculation due to http://www.babypips.com/school/undergraduate/senior-year/position-sizing/calculating-position-sizes.html which suggests different calculations based on different scenarios. I've been hacking around with mql4 for a few months now, but it sounds from your post that there is a way to get round these differences.  Surely having different ways of calculating lot size mentioned on the above URL, I need to represent this in code? 

Good point about rounding down - I should be doing that.  Also good point about maxlot, which I need to change.  I also see in your code about the SL calc as an amount rather than a number of pips.  Cheers


What do you think about WHRoeder's modification of your code in his most recent post?  That's quite a significant difference.

 

WHRoeder:

double PointValue = MarketInfo(Symbol(), MODE_TICKVALUE) / (MarketInfo(Symbol(), MODE_TICKSIZE) / Point),
  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. Account Balance * percent = RISK = (OrderOpenPrice - OrderStopLoss)*DIR * OrderLots * DeltaPerlot (Note OOP-OSL includes the SPREAD)
  3. Do NOT use TickValue by itself - DeltaPerlot
  4. You must also check FreeMargin to avoid stop out

 

  1. This has nothing to do with the code I posted above.
  2. Yes, I understand your equation.  We are saying the same thing, just expressing it in a different way.  Your DeltaPerlot is similar to my PointValue.
  3. As you can see, I'm not using TickValue alone.  Not sure why you repeated this mantra.
  4. I do a FreeMargin check in another part of the code.  
Now, regarding my PointValue calculation you've quoted.  TickValue is the value of TickSize in deposit currency.  TickSize is a multiple of Point.  I want a value per point; I don't want (at the moment) the value per lot or per pip.  I simply want to calculate the value per point in deposit currency.  I use that PointValue to calculate the value of the stoploss distance; I also use PointValue to perform a FreeMargin check.  On my terminal for the eur/usd, my tickvalue = 1.0 and my ticksize = 0.00001.  If one were to simply divide 1.0 by 0.00001, one would get 100000.0.  That obviously is not a valid value per point in deposit currency.  Points are integers: Point * MathPow(10, Digits) = 1.  Accordingly, TickSize is an integer since it is a multiple of Point.  This allows one to view TickValue as a value per integer TickSize, which is a multiple of integer points.  To calculate the value per integer point, TickValue is divided by the quotient of TickSize divided by Point.  So, on my terminal for the eur/usd, the value per integer point is TickValue / (TickSize / Point): 1.0 / (0.00001 / 0.00001) = 1.0.  Therefore, my PointValue calculation is correct.

double PointValue = MarketInfo(Symbol(), MODE_TICKVALUE) / (MarketInfo(Symbol(), MODE_TICKSIZE) / Point),

 
Thirteen:

My answer is: no and no.

Why did you vary your lot calculation (replicated below for illustration) based on whether the account currency was part of the currency pair? 

If RiskAmount, StoplossValue, and PointValue (i.e., a ratio of tickvalue and ticksize) are all denominated in deposit currency, why the need to determine if the account currency is part of the currency pair?  I don't believe you need to do this.

 

I'm not sure what you mean by "adjusted", but stoploss in this calculation (i.e., lotsize = RiskAmount / StoplossValue) should be a value in deposit currency, not a size in pips or points.  

  

In the above code snippet, what will happen when you round up?  The answer is that you will risk more than you defined in your riskCapital.  I believe the better course is to always round down (using MathFloor()).

 

What about maxlot?  While most of us will probably never reach our brokers' defined maxlot (mine is 50 standard lots and I'm still only trading at <=1 standard lot), it is better to check the calculated lotsize against maxlot just to make sure. 

 

The following is the fixed fractional position size function that I'm currently using.  It isn't perfect, but it might give you some insight to my comments above.


Thirteen, I put in your fractional lot size function and now my functions are coming out as (eg) 337.01 instead of 0.337.  Can you tell me why this might be please?
 

If you ask something like that, you still got a lot of homework to do.

Have you ever try different calculation logic and figure out why it happen like that?

 
strontiumDog:

Thirteen, I put in your fractional lot size function and now my functions are coming out as (eg) 337.01 instead of 0.337.  Can you tell me why this might be please?

Please post an example which shows: (1) risk amount, (2) stoploss distance, (3) currency pair, (4) tickvalue, (5) ticksize, (6) Point, and (7) the value of MarketInfo(Symbol(), LOTSIZE).

Couple of quick things to remember: the stoploss distance you give to the function must be in integer points, and the RiskPercent must be decimal form (e.g., 10% = 0.1 and 2% = 0.02).

 
strontiumDog:

Right after spending basically all day researching and double checking bits of code from all over the place, I didn't find a nice, reliable, but simple and easy to implement function in one place that I could drop into my code which would give me the lot size, adjusted for JPY and 3/5 digit brokers and based on a risk percentage of the margin in my account.  OK, I know the code below may not be complete, but from a few tests with different pairs it looks like it correctly calculates lot size when I print out what it is doing and compare the manually calculated values in an online pos size calculator.

For the benefit of the world at large, here is the code, and my question is: is this code (a) correct as far as you're concerned as regards the lot size calculations listed, and (b) the most efficient way of doing it to your mind?

Thanks

In init(), note calculation of tickValue:


then bearing in mind stopLoss is an integer in (adjusted) pips:

@
23
Thank you very much. I'm a newbie of MT4. Your code is exactly the one I wanted.
Reason: