Функция расчета лота от размера депозита.

 
Подскажите функцию для советника, чтобы открывал ордера процентом от депозита.
 
Denis Tishkin:
Подскажите функцию для советника, чтобы открывал ордера процентом от депозита.

А в чем проблема? Вычисляете размер лота, потом надо этот размер нормализовать, чтобы он был кратен минимальному изменению лота. Вот функция со всеми проверками для MQL4.

// открывает ордер с проверкой, нормализацией данных и возвратом измененных данных
int OpenOrderWithCheck(string symbol, int cmd, double& volume, double& price, int slippage, 
  double &stoploss, double &takeprofit, string comment, int magic, datetime expiration, 
  color arrow_color)
{
   int dig = (int) MarketInfo(symbol, MODE_DIGITS);   // Количество знаков после запятой по инструменту
   
   double minlot = MarketInfo(symbol, MODE_MINLOT);   // Минимальный размер лота
   double lotstep = MarketInfo(symbol, MODE_LOTSTEP); // Шаг изменения размера лота 
   double maxlot = MarketInfo(symbol, MODE_MAXLOT);   // Максимальный размер лота 
   
   int lot = (int)(volume/lotstep); // округлили до целого числа шагов изменения лота
   volume = (double)lot * lotstep;  // теперь имеем правильный объем, кратный шагу изменения лота 
   
   //int n1=123, n2=33, n3=222;
   //double d = (double)n1/n2 + (double)n3;
   
   if(volume < minlot)
   {
      // я предпочитаю такую обработку, но выдаю алерт, чтобы исправить ошибку, пару раз помогало :)  
      // ниже читаете коммент про логгирование в файлы
      volume = minlot;
      Alert("OpenOrder()", "Объем меньше минимального, задаю минимальное значение = ", volume, " лот(а)");
   }
   if(volume > maxlot)
   {
      volume = maxlot;
      Alert("OpenOrder()", "Объем больше максимального, задаю максимальное значение = ", volume, " лот(а)");
   }
   
   // При открытии рыночного ордера (OP_SELL или OP_BUY) в качестве цены открытия могут использоваться 
   // только самые последние цены Bid (для продажи) или Ask (для покупки). 
   if(cmd == OP_BUY )
      price = MarketInfo(symbol, MODE_ASK);
   if(cmd == OP_SELL)
      price = MarketInfo(symbol, MODE_BID);
   
   takeprofit = NormalizeDouble(takeprofit, dig); // цены надо округлять до количества знаков после запятой
   stoploss = NormalizeDouble(stoploss, dig);
   price = NormalizeDouble(price, dig);
   
   ResetLastError();
   int ticket = OrderSend(symbol, cmd, volume, price, slippage, stoploss, takeprofit, comment, magic, expiration, arrow_color);

   if(ticket < 0)
   {
      string err = GetMyLastError();
      // тут вместо принта можно выполнить свое действие, лично я пишу протокол в лог-файл
      Print("Ошибка открытия ордера, ", err);
   }
   else
   {
      bool os = OrderSelect(ticket, SELECT_BY_TICKET);
      if(os == true)
      {
         price      = OrderOpenPrice();
         // нужно запрашивать реальный объем, т.к. на счетах ECN при посылке ордера с большим объемом может открыться несколько ордеров
         // с разными объемами и разными ценами из-за того, что в стакане просто нет заявки с нужным лотом по запрошенной цене
         volume     = OrderLots();      
         stoploss   = OrderStopLoss();
         takeprofit = OrderTakeProfit();        
      }
   }   
   return (ticket);
}
 
Alexey Volchanskiy:

А в чем проблема? Вычисляете размер лота, потом надо этот размер нормализовать, чтобы он был кратен минимальному изменению лота. Вот функция со всеми проверками для MQL4.

Хорошо говорить, когда знаешь как.

Спасибо.

Буду разбираться. )

 
Denis Tishkin:

Хорошо говорить, когда знаешь как.

Спасибо.

Буду разбираться. )

Ну смотрите, вот простейший пример. У вас депо в баксах, допустим $1000, плечо 500. Вы хотите открыть сделку EURUSD 5% от суммы депо, то есть $50. 

Базовый контракт на лот 100000 евро, курс на сейчас 1,1285. При плече 1:500 получаем маржу на лот 100000*1,1285/500 = $225.

Теперь получаем кол-во лотов на наши $50. Это будет $50/$225 = 0.2222... лота. Подставляем в приведенную выше функцию, она округлит лот до нужного значения и откроет ордер.

 
Alexey Volchanskiy:

Ну смотрите, вот простейший пример. У вас депо в баксах, допустим $1000, плечо 500. Вы хотите открыть сделку EURUSD 5% от суммы депо, то есть $50. 

Базовый контракт на лот 100000 евро, курс на сейчас 1,1285. При плече 1:500 получаем маржу на лот 100000*1,1285/500 = $225.

Теперь получаем кол-во лотов на наши $50. Это будет $50/$225 = 0.2222... лота. Подставляем в приведенную выше функцию, она округлит лот до нужного значения и откроет ордер.

Тоже до недавнего времени использовал подход в округлении такой как указан (int(volume / lotstep) * lotstep). Но в итоге напоролся на случай, когда этот подход работает неправильно, т. к. он не учитывает ситуации, при которых lotstep > minlot. Получается что более универсальный алгоритм такой:

int lot = (int)(volume/lotstep); // округлили до целого числа шагов изменения лота
volume = (double)lot * lotstep;  // теперь имеем правильный объем, кратный шагу изменения лота 
if (lotstep > minlot)
   volume += minlot;
 
double NLot(double dlot,string sname="")
  {

   if(sname=="") sname=Symbol();
   int idigits=(int)MathRound(MathAbs(MathLog(MarketInfo(sname,MODE_LOTSTEP))/MathLog(10)));
   return (NormalizeLot(NormalizeDouble(dlot,idigits),sname));
  }

double NormalizeLot(double dlot,string sname="")
  {
   if(sname=="") sname=Symbol();

   if(dlot<MarketInfo(sname,MODE_MINLOT)) return (MarketInfo(sname,MODE_MINLOT));
   if(dlot>MarketInfo(sname,MODE_MAXLOT)) return (MarketInfo(sname,MODE_MAXLOT));
   return (dlot);
  }
для нормализации лота давно пользуюсь такой формулой, сбоев в расчетах не было.
 
Alexandr Gavrilin:
для нормализации лота давно пользуюсь такой формулой, сбоев в расчетах не было.
Как только наткнетесь на шаг, не кратный 10 (а такое реально бывает), сбой будет ))
 
Ihor Herasko:
Как только наткнетесь на шаг, не кратный 10 (а такое реально бывает), сбой будет ))
Теоретически верно, хотя в реале ни разу не встречал. Но вы правы, предусматривать надо все.
 
Denis Tishkin:
Подскажите функцию для советника, чтобы открывал ордера процентом от депозита.

Я использую такой расчет при использовании стоплосса в пунктах

Для МТ4

//+------------------------------------------------------------------+
//|                                                       test_3.mq4 |
//|                        Copyright 2016, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
enum ENUM_TYPE_BALANS
  {
   Balance,
   Equity,
   FreeMargin
  };

input ENUM_TYPE_BALANS Type_Balanse=Balance;//Method calculation Volume
input double Risk=0.1;
input double Lot=0.01;//Volume
input int StopLoss=300;
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   Lots=(Risk!=0.0)?Volumes(_Symbol,Risk,StopLoss):volume_n(_Symbol,Lot);
  }
//+------------------------------------------------------------------+
double Volumes(string symbol,double risk,double sl)
  {
   double lot=0.0;
   double procent=0.0;
   double balans=0.0;
   double tc = SymbolInfoDouble(symbol,SYMBOL_TRADE_CONTRACT_SIZE);
   double tv = SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_VALUE);
   double ts=SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_SIZE);
   double point=SymbolInfoDouble(symbol,SYMBOL_POINT);

   if(Type_Balanse==Balance) balans=AccountInfoDouble(ACCOUNT_BALANCE);
   if(Type_Balanse==Equity) balans=AccountInfoDouble(ACCOUNT_EQUITY);
   if(Type_Balanse==FreeMargin) balans=AccountInfoDouble(ACCOUNT_FREEMARGIN);

   procent=(balans/100.0)*risk;

   switch((int)MarketInfo(symbol,MODE_PROFITCALCMODE))
     {
      case 0: if(sl!=0 && tv!=0) lot=procent/(sl*tv);break;
      case 1: if(sl!=0 && point!=0 && tc!=0) lot=procent/(sl*point*tc);break;
      case 2: if(sl!=0 && point!=0 && tv!=0 && ts!=0) lot=procent/(sl*point*(tv/ts));break;
     }
   return(volume_n(symbol,lot));
  }
//+------------------------------------------------------------------+
double volume_n(string symbol,double lot)
  {
   double MinLot=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN);
   double MaxLot=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX);
   double LotStep=SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP);

   if(lot<MinLot)lot=MinLot;
   if(lot>MaxLot)lot=MaxLot;

   if(LotStep==0.001) return(NormalizeDouble(lot,3));
   if(LotStep==0.01)  return(NormalizeDouble(lot,2));
   if(LotStep==0.1)   return(NormalizeDouble(lot,1));

   return(NormalizeDouble(lot,0));
  }
//+------------------------------------------------------------------+

Для МТ5 

//+------------------------------------------------------------------+
//|                                                      Test_02.mq5 |
//|                        Copyright 2016, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
enum ENUM_TYPE_BALANS
  {
   Balance,
   Equity,
   FreeMargin
  };

input ENUM_TYPE_BALANS Type_Balanse=Balance;//Method calculation Volume
input double Risk=0.1;
input double Lot=0.01;//Volume
input int StopLoss=300;
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   Lots=(Risk!=0.0)?Volumes(_Symbol,Risk,StopLoss):volume_n(_Symbol,Lot);
  }
//+------------------------------------------------------------------+
double Volumes(string symbol,double risk,double sl)
  {
   double lot=0.0;
   double procent=0.0;
   double balans=0.0;
   double tc = SymbolInfoDouble(symbol,SYMBOL_TRADE_CONTRACT_SIZE);
   double tv = SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_VALUE_LOSS);
   double ts=SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_SIZE);
   double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
   double LotStep=SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP);
   if(Type_Balanse==Balance) balans=AccountInfoDouble(ACCOUNT_BALANCE);
   if(Type_Balanse==Equity) balans=AccountInfoDouble(ACCOUNT_EQUITY);
   if(Type_Balanse==FreeMargin) balans=AccountInfoDouble(ACCOUNT_MARGIN_FREE);
   procent=(balans/100.0)*risk;

   switch((ENUM_SYMBOL_CALC_MODE)SymbolInfoInteger(symbol,SYMBOL_TRADE_CALC_MODE))
     {
      case SYMBOL_CALC_MODE_FOREX:if(sl!=0 && tv!=0) lot=procent/(sl*tv);break;
      case SYMBOL_CALC_MODE_FUTURES:if(sl!=0 && point!=0 && tv!=0 && ts!=0) lot=procent/(sl*point*(tv/ts));break;
      case SYMBOL_CALC_MODE_CFD:if(sl!=0 && point!=0 && tc!=0) lot=procent/(sl*point*tc);break;
      case SYMBOL_CALC_MODE_CFDINDEX:if(sl!=0 && point!=0 && tc!=0) lot=procent/(sl*point*tc);break;
      case SYMBOL_CALC_MODE_CFDLEVERAGE:if(sl!=0 && point!=0 && tc!=0) lot=procent/(sl*point*tc);break;
      case SYMBOL_CALC_MODE_EXCH_STOCKS:if(sl!=0 && point!=0 && tc!=0) lot=procent/(sl*point*tc);break;
      case SYMBOL_CALC_MODE_EXCH_FUTURES:if(sl!=0 && point!=0 && tv!=0 && ts!=0) lot=procent/(sl*point*(tv/ts));break;
      case SYMBOL_CALC_MODE_EXCH_FUTURES_FORTS:if(sl!=0 && point!=0 && tv!=0 && ts!=0) lot=procent/(sl*point*(tv/ts));break;
     }
   return(volume_n(symbol,lot));
  }
//+------------------------------------------------------------------+
double volume_n(string symbol,double lot)
  {
   double MinLot=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN);
   double MaxLot=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX);
   double LotStep=SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP);

   if(lot<MinLot)lot=MinLot;
   if(lot>MaxLot)lot=MaxLot;

   if(LotStep==0.001) return(NormalizeDouble(lot,3));
   if(LotStep==0.01)  return(NormalizeDouble(lot,2));
   if(LotStep==0.1)   return(NormalizeDouble(lot,1));

   return(NormalizeDouble(lot,0));
  }
//+------------------------------------------------------------------+
 
if(LotStep==0.001) return(NormalizeDouble(lot,3));
у какого брокера видели такой лот
 
Alexander Bereznyak:
у какого брокера видели такой лот

х.з. Лично я таким не пользовался, но где-то уже встречал такое, вроде-бы есть, или было у какого-то ДЦ.

Хочу предложить универсальную формулу для нормализации лота

NormalizeDouble(contractVolume, (int)MathAbs(MathLog10(MarketInfo(_Symbol, MODE_LOTSTEP))));