Simple risk percentage lot size calculation > Not working (yet) - page 2

 
luenbo:
Sorry, it is AccountInfoInteger(ACCOUNT_LEVERAGE) in MT5.

Leverage has nothing to do in this calculation, unless you use money management that takes into account free margin, which is not the case here.

wiptheman,

Did you solved your problem with lots size ?

 

I must apologize for very late reply.

Actually, I already wrote the answer but when I re-read my own answer ... well I don't understand it :). So I guess I just have to re-write my answer all over again.

Here we go :

1. To find out how much money (read :margin) needed to open position at given open price and at a given lot size, we use OrderCalcMargin. When we use OrderCalcMargin, there's no need to also calculate leverage, or lot standard per unit, into our margin calculation, because everything already calculated inside OrderCalcMargin.

2. To find out how much profit (or loss) we have from the open price (denoted in account currency), we use calculation that involve four components, they are : lot size, number of Point from open price, and PositionInfoDouble's SYMBOL_TRADE_TICK_VALUE and SYMBOL_TRADE_TICK_SIZE.  Here's the calculation :

code # 1 for profit (or loss) of current position in account currency

MqlTick Tick;
double tick_value, tick_size, value_per_point, open_price, open_lot, current_price, number_of_point, position_profit;

if(SymbolInfoTick(Symbol(),Tick) == true)                              //--- get the latest price
  {
  tick_value      = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);  //--- tick value per given tick size
  tick_size       = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);   //--- the smallest movement of tick in point  
 
  value_per_point = tick_value/ (tick_size/_Point);                 //--- value of single point (in account currency)
  
  if (PositionSelect(_Symbol) == true)
     {
     open_price      = PositionGetDouble(POSITION_PRICE_OPEN);          //--- current position open price
     current_price   = Tick.bid;                                   //--- current price. in this example it's calculated from Bid price

     number_of_point = (current_price - open_price)/_Point;         //--- number of point from current price to open price
  
     open_lot        = PositionGetDouble(POSITION_VOLUME);             //--- current position lot size 
  
     position_profit = open_lot*value_per_point*number_of_point;  //--- how much profit (or loss)in account currency
     }
  }



Now, let's have a problem   :).  

Let's say we have account in US Dollar for $ 10,000.- . Question is how much lot size to open if we want only spend US$ 400 to open sell GBPJPY including for its losses at 300 Points (30 pips).

As you can see, my account currency is US Dollar (USD) and I want to open sell a position for symbol that have UK Pound as base currency (GBP) and Japanese Yen as quote currency (JPY), and we want to spend only $ 400 for both open sell position and for its losses as well. 

First thing we do is, to calculate how much money (margin) needed if we want to open sell just one lot.

Well currently GBPJPY bid is at 140.825. Since my leverage is 1:500, the cost (money needed) to open sell 1 lot GPBJPY will be US$ 301.12. This cost will change with every single tick of bid price of GBPJPY and will be different if we using different leverage and different standard lot per unit.

code # 2 for how much margin need for 1 lot sell GBPJPY at 140.825

MqlTick Tick;
double money_needed, lot_size, open_price;

lot_size = 1.0;

if (SymbolInfoTick(Symbol(),Tick) == false) return;

open_price = Tick.bid; //--- for example the bid price is 140.825

if ( OrderCalcMargin(ORDER_TYPE_SELL,_Symbol, open_lot, open_price, money_needed) == true)
   {
   
   Print (DoubleToString(money_needed));
   //--- your other code goes here

   }

 

Second thing we do is calculate how much our losses is - in our account currency (US $) - if we set stop loss for 300 points or 30 pips.

Remember, we still have to use one lot size as our standard calculation. Another thing to remember, whether we using fractional pips broker or not, we have to use point to calculate this and not pips. So if we set stop loss at 47 pips and we are not using fractional pips broker, we should use 47 points instead 470 points for calculation.

The result from the code below show that if we have our 300 points stop loss executed with 1 lot size in GBPJPY, we will loss our money about US$ 320.70126677

code # 3 for how many losses in our account currency if we set stop loss (read : if we loss) 300 points

double tick_value, tick_size, value_per_point, lot_size, stop_loss, position_profit;

tick_value      = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);   
tick_size       = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);      

value_per_point = tick_value/(tick_size/_Point);                           
         
lot_size        = 1.0;
stop_loss       = 300;             

position_profit = lot_size*value_per_point*stop_loss; 

Now that we know the money needed to open sell  1 lot size of GBPJPY and its losses at 300 point, then we calculate how much lot size have to open if we only want to spend US$ 400 open sell GBPJPY with 300 point stop loss.

code # 4 how much lot size to open if we want only spend US$ 400 to open sell GBPJPY including for its losses at 300 Points (30 pips) 

double money_available = 400.0;         //--- available money to open position (US$ 400)
double money_needed    = 301.12;        //--- money needed to open 1 lot size
double cost_stop_loss  = 320.70126677;   //--- our losses when stop loss were hit
double raw_lot;                         //--- lot size to open

raw_lot = money_available / (money_needed + cost_stop_loss); 

The result of code above is that we have raw_lot of 0.64327166. 

Surely we can not send this 0.64327166 lot size to broker, because we must check and follow broker requirement to send lot, by using SymbolInfoDouble's SYMBOL_VOLUME_MIN, SYMBOL_VOLUME_MAX, SYMBOL_VOLUME_STEP, and SYMBOL_VOLUME_LIMIT.

I write about that later, and I also will explain why we should NOT use conversion function NormalizeDouble at all when sending any order to broker.

 
Oh wow !! Just saw your answer!! Thanks a lot I will have to test all that :D
 
NormalizeDouble is the solution! for... I fell into that pit so many times...
 
Yourock:
NormalizeDouble is the solution! for... I fell into that pit so many times...
It's only a solution in rare circumstances and for people that do not understand.
 

Just thought I would also share my volume function that only has two input parameters; the trade symbol name and margin to be invested. The input trade margin is in USD. 

 

      
double Volume(string TradeSymbol, double TradeMargin)
   {
      double _volume = 0.0;
      
      if(StringSubstr(TradeSymbol,3,3)=="USD"||StringSubstr(TradeSymbol,0,3)=="XAU"||StringSubstr(TradeSymbol,0,3)=="XAG"||StringSubstr(TradeSymbol,0,3)=="EUR"||StringSubstr(TradeSymbol,0,3)=="GBP"||StringSubstr(TradeSymbol,0,3)=="AUD"||StringSubstr(TradeSymbol,0,3)=="NZD"){
         
         if(StringSubstr(TradeSymbol,0,3)!="XAU"&&StringSubstr(TradeSymbol,0,3)!="XAG"){
            _volume=TradeMargin;
            if(SymbolInfoDouble(StringSubstr(TradeSymbol,0,3)+"USD",SYMBOL_BID)!=0.0) {_volume/=SymbolInfoDouble(StringSubstr(TradeSymbol,0,3)+"USD",SYMBOL_BID);}
            _volume*=double(AccountInfoInteger(ACCOUNT_LEVERAGE));
            if(SymbolInfoDouble(TradeSymbol,SYMBOL_TRADE_CONTRACT_SIZE)!=0.0) { _volume/=SymbolInfoDouble(TradeSymbol,SYMBOL_TRADE_CONTRACT_SIZE); }}
         
         if(StringSubstr(TradeSymbol,0,3)=="XAU"||StringSubstr(TradeSymbol,0,3)=="XAG"){
            _volume=AccountInfoInteger(ACCOUNT_LEVERAGE)*TradeMargin;
            if(SymbolInfoDouble(TradeSymbol,SYMBOL_TRADE_CONTRACT_SIZE)!=0.0) { _volume/=SymbolInfoDouble(TradeSymbol,SYMBOL_TRADE_CONTRACT_SIZE); }
            if(SymbolInfoDouble(StringSubstr(TradeSymbol,0,3)+"USD",SYMBOL_BID)!=0.0) { _volume/=SymbolInfoDouble(StringSubstr(TradeSymbol,0,3)+"USD",SYMBOL_BID); }}

         if(SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_STEP)!=0.0) { _volume/=SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_STEP); }
         _volume=NormalizeDouble(_volume,0);
         _volume*=SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_STEP);
         if(_volume<=0.0) { _volume+=SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_MIN);}}
      else if(StringSubstr(TradeSymbol,0,3)=="USD"){
         _volume=TradeMargin*AccountInfoInteger(ACCOUNT_LEVERAGE);
         if(SymbolInfoDouble(TradeSymbol,SYMBOL_TRADE_CONTRACT_SIZE)!=0.0) { _volume/=SymbolInfoDouble(TradeSymbol,SYMBOL_TRADE_CONTRACT_SIZE); }
         if(SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_STEP)!=0.0) { _volume/=SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_STEP); }
         _volume=NormalizeDouble(_volume,0);
         _volume*=SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_STEP);
         if(_volume<=0.0) { _volume+=SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_MIN);}}
      else {
         _volume=TradeMargin*SymbolInfoDouble("USD"+StringSubstr(TradeSymbol,0,3),SYMBOL_BID)*AccountInfoInteger(ACCOUNT_LEVERAGE);
         if(SymbolInfoDouble(TradeSymbol,SYMBOL_TRADE_CONTRACT_SIZE)!=0.0) { _volume/=SymbolInfoDouble(TradeSymbol,SYMBOL_TRADE_CONTRACT_SIZE); }
         if(SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_STEP)!=0.0) { _volume/=SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_STEP); }
         _volume=NormalizeDouble(_volume,0);
         _volume*=SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_STEP);
         if(_volume<=0.0) { _volume+=SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_MIN);}}
      if(_volume<0.0) { return(0.0); }
           
         if(_volume<=0.0) {
            if(AccountInfoDouble(ACCOUNT_FREEMARGIN)>TradeMargin) {
                return(SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_MIN));} 
            return(0.0); }
         
         if(SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_STEP)==0.0) {
            Print(TradeSymbol," volume step is zero!! ");
            if(AccountInfoDouble(ACCOUNT_FREEMARGIN)>TradeMargin) {
                return(SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_MIN));}  
            return(0.0); }
         
         if(SymbolInfoDouble(TradeSymbol,SYMBOL_TRADE_CONTRACT_SIZE)==0.0) {
            Print(TradeSymbol," contract size is zero!! "); 
            if(AccountInfoDouble(ACCOUNT_FREEMARGIN)>TradeMargin) {
                return(SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_MIN));}
            return(0.0); }
         
         if(_volume>SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_MAX)){
            _volume=SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_MAX);}
         
      return(_volume);
   }
Reason: