OrderCalcMargin() и OrderCalcProfit() формулы расчета

 

Добрый день

Подскажите пожалуйста формулы расчета данных функций. В частности интересует что произходит при кроссах - например имется счет в USD, а открывается позиция на EURGBP.

Так же по формуле OrderCalcProfit() хотелось бы подсчитать Volume имея Profit. 

Спасибо 

 
class COrderCalculateMargin{

public: double Result;

public: void COrderCalculateMargin(string symbol,ENUM_ORDER_TYPE orderType,double openPrice,double volume){
   Result=0;
   string currency=AccountInfoString(ACCOUNT_CURRENCY);
   if(StringSubstr(symbol,3,3)==currency)Result=CalcMarginFormula(symbol,openPrice,volume);
   else if(StringSubstr(symbol,0,3)==currency)Result=CalcMarginFormula(symbol,1,volume);
   else{
      string pair=StringSubstr(symbol,0,3)+currency;
      if(orderType==ORDER_TYPE_BUY)Result=CalcMarginFormula(pair,Bid(pair),volume);
      if(orderType==ORDER_TYPE_SELL)Result=CalcMarginFormula(pair,Ask(pair),volume);
      }
   return;
   }

double CalcMarginFormula(string symbol,double openPrice,double volume){
   return NormalizeDouble(openPrice*volume*SymbolInfoDouble(symbol,SYMBOL_TRADE_CONTRACT_SIZE)/AccountInfoInteger(ACCOUNT_LEVERAGE),0);
   } 
   
};
Писал сам, провел тесты, только на кроссах работает не всегда точно. Подскажите где ошибка.
 
Dimitr Trifonov:
Писал сам, провел тесты, только на кроссах работает не всегда точно. Подскажите где ошибка.

Скажем так, на кроссах будет не то что "не всегда", а всегда неправильно.

Сама формула расчета верна: Margin = quote * volume * contract_size / leverage.

Просто вместо quote в разных случаях нужно брать разные котировки. Если базовая валюта символа котируется через валюту счета (к примеру, EURUSD при валюте счета USD), то в качестве quote, действительно, нужно брать цену открытия трейда: Ask - для Buy и Bid - для Sell. Это единственный случай, для которого приведенный Вами метод расчета верен.

Немного по-другому происходит получение значения quote символов, у которых базовая валюта совпадает с валютой счета (USDCAD при валюте счета USD). В таких случаях quote будет равно: 1.0 / Ask - для Buy и 1.0 / Bid - для Sell.

И совсем иначе рассчитывается quote для кроссов (символов, в которых отсутствует валюта счета). Так, при валюте счета USD залог для трейда по EURJPY будет рассчитан по текущей цене EURUSD. Так как EURUSD и EURJPY имеют одинаковую базовую валюту, то и quote будет определяться как в случае с просто EURUSD: Ask (от EURUSD) - для Buy и Bid (от EURUSD) - для Sell.

В случае, если кросс попался вида CHFJPY, то quote нужно рассчитать по символу USDCHF. Здесь базовые валюты символа отличаются. Поэтому quote получаем как в случае с USDCAD: 1.0 / Ask (от USDCHF) - для Buy и 1.0 / Bid (от USDCHF) - для Sell.

Надеюсь, понятно объяснил. В свое время очень долго "въезжал" в эту схему. 

 
Dimitr Trifonov:
Писал сам, провел тесты, только на кроссах работает не всегда точно. Подскажите где ошибка.

вот в этой формуле: return NormalizeDouble(openPrice*volume*SymbolInfoDouble(symbol,SYMBOL_TRADE_CONTRACT_SIZE)/AccountInfoInteger(ACCOUNT_LEVERAGE),0);

вот в этом месте:  AccountInfoInteger(ACCOUNT_LEVERAGE)

тоже может быть не всегда верно. Сам не очень давно столкнулся с такой ситуацией. Вот подробное рассуждение и объяснение:  

С начала и до 3 страницы всё обсуждение по этому вопросу.

 

Спасибо всем за советы.

После нескольких тестов код выглядит так:

class COrderCalculateMargin{

public: double Result;

public: void COrderCalculateMargin(string symbol,ENUM_ORDER_TYPE orderType,double openPrice,double volume){
   Result=0;
   string currency=AccountInfoString(ACCOUNT_CURRENCY);
   if(StringSubstr(symbol,3,3)==currency)Result=CalcMarginFormula(symbol,openPrice,volume);
   else if(StringSubstr(symbol,0,3)==currency)Result=CalcMarginFormula(symbol,1,volume);
   else{
      string pair=StringSubstr(symbol,0,3)+currency;
      if(orderType==ORDER_TYPE_BUY)Result=CalcMarginFormula(pair,Ask(pair),volume);
      if(orderType==ORDER_TYPE_SELL)Result=CalcMarginFormula(pair,Bid(pair),volume);
      }
   return;
   }

double CalcMarginFormula(string symbol,double openPrice,double volume){
   return NormalizeDouble(openPrice*volume*SymbolInfoDouble(symbol,SYMBOL_TRADE_CONTRACT_SIZE)/AccountInfoInteger(ACCOUNT_LEVERAGE),0);
   } 
   
};

 Единственная разница выделена желтым цветом. А вот код, который проводил тесты:

   ENUM_ORDER_TYPE orderType=ORDER_TYPE_SELL;
   double price=0.5;
   double lots=1.3;
   
   string symbol[3]={"GBPUSD","USDCAD","EURJPY"};
   
   for(int i=0;i<3;i++){

      double mqlMargin=0;
      if(!OrderCalcMargin(orderType,symbol[i],lots,price,mqlMargin))Print("CompareCalcMargin Error");
      COrderCalculateMargin OrderCalculateMargin(symbol[i],orderType,price,lots);
      
      Print(symbol[i]+" ORDER_TYPE_SELL, price = "+price+", lots = "+lots+"; OrderCalcMargin() = "+(string)mqlMargin+"; COrderCalculateMargin.Result = "+(string)OrderCalculateMargin.Result); 
      }

 Вот результат, оба варианта практически одинаковы:

CompareMargin (GBPUSD,H4)       GBPUSD ORDER_TYPE_BUY, price = 2.5, lots = 1.3; OrderCalcMargin() = 6500; COrderCalculateMargin.Result = 6500
CompareMargin (GBPUSD,H4)       USDCAD ORDER_TYPE_BUY, price = 2.5, lots = 1.3; OrderCalcMargin() = 2600; COrderCalculateMargin.Result = 2600
CompareMargin (GBPUSD,H4)       EURJPY ORDER_TYPE_BUY, price = 2.5, lots = 1.3; OrderCalcMargin() = 2848.56; COrderCalculateMargin.Result = 2848
CompareMargin (GBPUSD,H4)       GBPUSD ORDER_TYPE_SELL, price = 2.5, lots = 1.3; OrderCalcMargin() = 6500; COrderCalculateMargin.Result = 6500
CompareMargin (GBPUSD,H4)       USDCAD ORDER_TYPE_SELL, price = 2.5, lots = 1.3; OrderCalcMargin() = 2600; COrderCalculateMargin.Result = 2600
CompareMargin (GBPUSD,H4)       EURJPY ORDER_TYPE_SELL, price = 2.5, lots = 1.3; OrderCalcMargin() = 2848.51; COrderCalculateMargin.Result = 2849
CompareMargin (GBPUSD,H4)       GBPUSD ORDER_TYPE_SELL, price = 0.5, lots = 1.3; OrderCalcMargin() = 1300; COrderCalculateMargin.Result = 1300
CompareMargin (GBPUSD,H4)       USDCAD ORDER_TYPE_SELL, price = 0.5, lots = 1.3; OrderCalcMargin() = 2600; COrderCalculateMargin.Result = 2600
CompareMargin (GBPUSD,H4)       EURJPY ORDER_TYPE_SELL, price = 0.5, lots = 1.3; OrderCalcMargin() = 2847.1; COrderCalculateMargin.Result = 2847
CompareMargin (GBPUSD,H4)       GBPUSD ORDER_TYPE_SELL, price = 0.5, lots = 1.3; OrderCalcMargin() = 1300; COrderCalculateMargin.Result = 1300
CompareMargin (GBPUSD,H4)       USDCAD ORDER_TYPE_SELL, price = 0.5, lots = 1.3; OrderCalcMargin() = 2600; COrderCalculateMargin.Result = 2600
CompareMargin (GBPUSD,H4)       EURJPY ORDER_TYPE_SELL, price = 0.5, lots = 1.3; OrderCalcMargin() = 2847.42; COrderCalculateMargin.Result = 2847

 Если в случае USDxxx поменять CalcMarginFormula(symbol,1,volume); на CalcMarginFormula(symbol,1/openprice,volume); результаты сильно расходятся

 Почему?

 
Dimitr Trifonov:
Писал сам, провел тесты, только на кроссах работает не всегда точно. Подскажите где ошибка.

кроссы считаются по другому:

      //USD/CHF Цена пункта = размер пункта * объем позиции / текущий курс
      //EUR/USD Цена пункта = размер пункта * объем позиции
      //EUR/CHF Цена пункта = объем позиции * размер пункта * текущая котировка базовой валюты по отношению к USD / текущий курс валютной пары (кросс-курс)   
 

Допустим депозит у Вас в USD

Пары XXXUSD - будут считаться верно по этой формуле,

а вот для остальных пар необходим перевод в валюту депозита по текущему курсу в зависимости от направления BUY/SELL.

На MQL4 community был алгоритм расчета, насколько помнится мне

Ребята, может пора бы уже от MQ запросить функцию получения маржи ордера ? хотя бы открытого. бывает очень нужно.

 

Прошло некое время, а формула расчета OrderCalcProfit() по прежнему точно не известна. Нашел код GetOrderProfit(), результат немного расходится:

double GetOrderProfit(ENUM_ORDER_TYPE action, string symbol, double volume, double price_open, double price_close){
   double tickValue=SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_VALUE);

   double profit = NULL;
   if ( action == ORDER_TYPE_BUY || action == ORDER_TYPE_BUY_LIMIT || action == ORDER_TYPE_BUY_STOP ) {
      profit = (price_close - price_open) * volume * tickValue / _Point;
      return profit;
      } 
   else if ( action == ORDER_TYPE_SELL || action == ORDER_TYPE_SELL_LIMIT || action == ORDER_TYPE_SELL_STOP ) {
      profit = (price_open - price_close) * volume * tickValue / _Point;
      return profit;
      }
   return profit;
   }

Подскажите что добавить, чтобы результаты OrderCalcProfit() и этой функции совпадали ? 

 
https://www.mql5.com/en/blogs/post/719643
Instant estimation of profit, margin level, drawdown on MetaTrader charts
Instant estimation of profit, margin level, drawdown on MetaTrader charts
  • 2018.07.16
  • www.mql5.com
I'm sure all of you use the crosshair tool on MetaTrader's charts. It's very useful to measure distance in bars or points between 2 spots. Yet I was always wondering, why it does not provide an option to show profit value, margin level, drawdown or gain percentage in addition to points. To remedy this problem I've developed a MQL program, which...
 
fxsaber:
https://www.mql5.com/en/blogs/post/719643
Спасибо
Причина обращения: