Cannot Calulate Lots on USDJPY

 

Hi All,


Was looking to get some help with a GetLotSize function please?

The attached code correctly gives a dynamic lot size on every pair apart form USDJPY and I'm racking my brains to see what's wrong with it. I've included the helper function below

I think the problem is in the moneyLotStep calculation which for GBPUSD would come out as 2.63 but for USDJPY comes out at 288 (for example is on a 28(ish) pip stoploss)


Any assistance would be greatly appreciated. Thanks in advance,


//+------------------------------------------------------------------+
//| GetLotSize()                                                             |
//+------------------------------------------------------------------+
double GetLotSize(string symbol, double _accEquity, double _riskPercent, double _slDistanceInPrice, bool _deBug)
  {

   double   tickSize    =  SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);    //from this function we want the tick size to be returned
   double   tickValue   =  SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);   // find the value of ticksize and invested with 1 lot, this will make a difference of $1
   double   lotStep     =  SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);        // smallest change in lot size = 0.01 for USDJPY

   if(tickSize == 0 || tickValue == 0 || lotStep == 0)
     {
      Print("tickSize, tickValue or lotStep failed to calculate " __FUNCSIG__ " aborted");
      return 0;
     }

   double   riskMoney    = (_accEquity / 100) * _riskPercent;
   double   moneyLotStep = (_slDistanceInPrice / tickSize) * tickValue * lotStep;

   if(moneyLotStep == 0)
     {
      Print("moneyLotStep is set to 0, you cannot divide by 0 " __FUNCSIG__ " aborted");
      return 0;
     }

   double lots =  MathFloor(riskMoney / moneyLotStep) * lotStep;

   if(!CheckLotSizeIsValid(symbol, lots))
     {
      Print("Lot size is invalid, either too small or too large for the broker's requirements "__FUNCSIG__ " aborted");
      return -1;
     }

   if(_deBug)
      Print("---" + __FUNCSIG__ + " DEBUG---");
      
   return lots;
  }// returns dynamic lot size based on the sl in price
//+------------------------------------------------------------------+





//+------------------------------------------------------------------+
//|CheckLotSizeIsValid()                                             |
//+------------------------------------------------------------------+
bool CheckLotSizeIsValid(string symbol, double & lots)
  {
   double   minLotValue =  SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
   double   maxLotValue =  SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
   double   stepValue   =  SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);

   if(lots < minLotValue)
     {
      Print("Lot size below MIN required, setting to minimum ", "lots: ", lots, " min: ", minLotValue);
      lots = minLotValue;
      return false;
     }

   if(lots > maxLotValue)
     {
      Print("Lot size above MAX required, setting to maximum ", "lots: ", lots, " max: ", maxLotValue);
      lots = maxLotValue;
      return false;
     }

   lots = (int)MathFloor(lots / stepValue) * stepValue;

   return true;
  }
//+------------------------------------------------------------------+
 

Consider using OrderCalcProfit instead.

Forum on trading, automated trading systems and testing trading strategies

SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE) sometimes zero

Fernando Carreiro, 2022.08.23 17:41

You can! These are the steps I take. I supply the function with a lot size equal to the “Max Lot Size” allowed for the symbol in question, then calculate the ratio needed to achieve the fractional risk that I wish to apply, to get the correct volume for the order. I then align that with the “Lot Step” and finally check it against both the maximum and minimum allowed lots for the symbol.

The reason I use the “maximum” lots instead of just “1.0” lots as a reference value is because there is no guarantee that the value of 1.0 is within the minimum and maximum values allowed. Given that using 1.0, or the maximum, gives equivalent results anyway (by using the ratio method), I choose to use the “max lots” as the reference point which also offers the most precision for the calculation.

Something like this ...

// This code will not compile. It is only a example reference

if( OrderCalcProfit( eOrderType, _Symbol, dbLotsMax, dbPriceOpen, dbPriceStopLoss, dbProfit ) )
{
   dbOrderLots = fmin( fmax( round( dbRiskMax * dbLotsMax / ( -dbProfit * dbLotsStep ) )
               * dbLotsStep, dbLotsMin ), dbLotsMax ); 
      
   // the rest of the code ...
};
 

Hi Fernando, 

Thank you very much for this solution. I come across your suggetions here often and they are always extremly helpful. 

One last thing if I may... The  'dbRiskMax ' you mention above, would that be the 'money at risk' in the account currency?

Thanks again, All the best, C

 
Chris Bakowski #: One last thing if I may... The  'dbRiskMax ' you mention above, would that be the 'money at risk' in the account currency?
Yes! For example if I were using 1% of a balance of $5000.00 for risk, then dbRiskMax would be 50.0
 

Fernando, This has really helped me out, Thank you again!


For anyone who comes across this post/issue moving forward, here's a full function that implements the solution proposed above:

//+------------------------------------------------------------------+
//| GetLotsByOrderCalcProfit()                                       |
//+------------------------------------------------------------------+
double GetLotsByOrderCalcProfit(ENUM_ORDER_TYPE orderType, string symbol, double dbAccEquity, double dbRiskPercentPerTrade, double dbEntry, double dbStopLoss)
  {
   double   dbOrderLots;
   double   dbProfit;
   double   dbLotsMin   =  SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
   double   dbLotsMax   =  SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
   double   dbLotsStep  =  SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
   double   dbRiskMax   = (dbAccEquity / 100) * dbRiskPercentPerTrade;

   if(OrderCalcProfit(orderType, symbol, dbLotsMax, dbEntry, dbStopLoss, dbProfit))
     {
      dbOrderLots = fmin(fmax(round(dbRiskMax * dbLotsMax / (-dbProfit * dbLotsStep)) * dbLotsStep, dbLotsMin), dbLotsMax);
     }
   else
     {
      Print("Lot size is invalid: " + __FUNCSIG__);
      dbOrderLots = -1;
     }
   return dbOrderLots;
  }