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

 
Event:

Функция написана на С

На mql5 действительно выдает 0.

А на C# выдает 1.

Может mql кривой?

 

 

С другими аргументами на C# что? Странная какая-функция.
 
extern double  LotsFor1000  = 0.08;
extern int     TakeProfit   = 140;    //Параметры советника
extern int     StopLoss     = 50;
extern int     Magic        = 111;
extern int     Slippage     = 3;
extern double  Risk = 4;
//-------------------------------------------------------------------------------------------------------------------------------------
Comment(DoubleToStr(LotsByRisk(OP_SELL, Risk, StopLoss),2));  //---Вывод на экран.

//--------------------------------------------------------------------------------------------------------------------------------------

//---Сама функция.
double GetLots()
 {
    double clots = AccountBalance() / 1000 * LotsFor1000;
    clots = MathMax(clots, MarketInfo(Symbol(), MODE_MINLOT));
    clots = MathMin(clots, MarketInfo(Symbol(), MODE_MAXLOT));
    
    clots = NormalizeDouble(clots, 2);
    return(clots);
 }
 
 
 
 double LotsByRisk()
{
   double clots = AccountBalance() / 1000 * LotsFor1000;
   clots = MathMax(clots, MarketInfo(Symbol(), MODE_MINLOT));
   clots = MathMin(clots, MarketInfo(Symbol(), MODE_MAXLOT));//Вычисления.
   
   clots = NormalizeDouble(clots, 2); //Два знака после запятой.
   return(clots);
   
}

double LotsByRisk (int op_type, double risk, int SL)
{
   double lot_min  = MarketInfo(Symbol(), MODE_MINLOT);
   double lot_max  = MarketInfo(Symbol(), MODE_MAXLOT);
   double lot_step = MarketInfo(Symbol(), MODE_LOTSTEP);
   double lotcost  = MarketInfo(Symbol(), MODE_TICKVALUE);
   
   double lot = 0;
   double UsdPerPip = 0;
   
   lot = AccountBalance() * risk/100;
   UsdPerPip = lot/SL;
   
   lot = NormalizeDouble(UsdPerPip/lotcost, 2);
   lot = NormalizeDouble(lot/lot_step, 0) * lot_step;
   
   if (lot < lot_min) lot = lot_min;
   if (lot > lot_max) lot = lot_max;
   
   if (AccountFreeMarginCheck(Symbol(), op_type, lot) < 10 || GetLastError() == ERR_NOT_ENOUGH_MONEY)
   {
      Alert("Невозможно открыть позицию с обьёмом = " + DoubleToStr(lot, 2), "Недостаточно средств!");
      return(-1);
   }
   
   return(lot);
//---------------------------------------------------------------------------------------------------------------------   
   Вот нашёл. Правда эта функция, выводит расчет лота и изменения в % только на экран. Не подскажете как в код советника вставить?
 
Dmitry Fedoseev:
С другими аргументами на C# что? Странная какая-функция.
Да, похоже, что функция неправильная.
 

А вот и функция которая считает количество знаков после запятой у любого числа:

uchar fnDigits(double num)
   {
      uchar i=0;
      for(i=0;i<=DBL_DIG;i++) if (MathAbs(MathAbs(NormalizeDouble(num,i))-MathAbs(num))<=DBL_EPSILON) break;
      return(i);
   }

Нюанс который не нравиться чисто эстетически -  3 раза взятие модуля, как бы это уменьшить.

 
Alexey Oreshkin:

А вот и функция которая считает количество знаков после запятой у любого числа:

Нюанс который не нравиться чисто эстетически -  3 раза взятие модуля, как бы это уменьшить.

Перед началом цикла сделать num=MathAbs(num), внутри цикла будет достаточно одного MathAbs
 
Dmitry Fedoseev:
Перед началом цикла сделать num=MathAbs(num), внутри цикла будет достаточно одного MathAbs
нет, так раньше и было и возникают проблемы с отрицательными числами. Мы не знаем что получим после округления, большее или меньшее число, поэтому неизвестно что от чего отнимать, следовательно оба числа надо брать по модулю и из-за этого знак разницы тоже не знаем, поэтому и его по модулю, но готов поверить конкретному коду, а не просто словам.
 
uchar fnDigits2(double num){
      num=MathAbs(num);
      uchar i=0;
      for(i=0;i<=DBL_DIG;i++) if (MathAbs(NormalizeDouble(num,i)-num)<=DBL_EPSILON) break;
      return(i);
}

В каких случаях эта функция будет неправильно работать?

 

Всё догнал. Думал не догоню. ))) Всем спасибо за помощь. Тему можно в архив.

 

Могли бы и подсказать. Что такие *****то? ) Ответ то банальный.  Ппц.

 
Denis Tishkin:

...

Могли бы и подсказать. Что такие *****то? )

...

Сначала надо было бы спросить.
 
Alexey Oreshkin:

А вот и функция которая считает количество знаков после запятой у любого числа:

uchar fnDigits(double num)
   {
      uchar i=0;
      for(i=0;i<=DBL_DIG;i++) if (MathAbs(MathAbs(NormalizeDouble(num,i))-MathAbs(num))<=DBL_EPSILON) break;
      return(i);
   }

Нюанс который не нравиться чисто эстетически -  3 раза взятие модуля, как бы это уменьшить.

Я не знаю, разобрались ли вы самостоятельно в том, что ваша функция - не для любых чисел типа double.

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


Недавно я решила всё-таки немного обновить опубликованный вариант своей функции (обновлённый привожу в конце поста). 

Попутно, позднее, решила всё-таки написать здесь своими, простыми словами о следующем (некоторое, как минимум с первого по третий пункт, прозвучит банально, но привожу ниже всё для отображения своей точки зрения: в целях взаимоувязки, бо́льшей смысловой полноты, но без углубления в какие-то детали, усложняющие восприятие):


1. Числа типа double имеют особенности в целом. Т.е., не с привязкой именно к языкам программирования MQL5 и MQL4.

При работе с этими числами нам, естественно, может быть требоваться учитывать их особенности.

2. При трейдинге может требоваться различная точность по десятичным знакам (в т.ч., на одних графиках - пять знаков, на других графиках - три знака, и т.д., и т.д., и т.д. в разном и в одном и том же по разному).

3. Языки программирования MQL5 и MQL4 позволяют работать с числами типа double, имеющими различное значимое количество десятичных знаков. И разными способами.

В этих языках есть:

  • обширная библиотека различных готовых функций (их можно посмотреть, например, в папках MetaEditor (сама я ещё не познакомилась подробно со всеми имеющимися там функциями, их очень и очень много): Include => Math и других);
  • различные функции, с которыми можно познакомиться через F1 в Справке к  MetaEditor
Кроме того, эти языки позволяют конструировать какие-то свои варианты, в том числе, "изобретать велосипеды". Программирование - творческий процесс.

4. Одна из функций в Справке - это NormalizeDouble().

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

 

Но её возможности (удобные "побочные" эффекты), позволили применять её не только для этого.


5. Функция NormalizeDouble() имеет ограничение по digits: от 0 до 8 знаков.

 

Убедиться в этом можно по разному.

Вот тест из самых простейших (я привожу здесь и далее упрощённые данные, чтобы при пояснении не вдаваться/не отвлекаться в различные детали):

#define TEST_PRINT_TWO(v1,v2)            Print(__LINE__,", ",__FUNCTION__,", ",(#v1)," = ",(v1),", ",(#v2)," = ",(v2))
//---
input int i_digits=15;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
TEST_PRINT_TWO(NormalizeDouble(1.123456789,i_digits),DoubleToString(1.123456789,i_digits));
  }
//+------------------------------------------------------------------+

Результат:

19, OnStart, NormalizeDouble(1.123456789,i_digits) = 1.12345679, DoubleToString(1.123456789,i_digits) = 1.123456789000000

Т.е., при нормализации в 15 знаков, функция NormalizeDouble выводит число, имеющее девять десятичных знаков, с количеством в восемь и округляя при этом: 1.123456789 => 1.12345679

Дополнено: При этом само нормализуемое число не округляется. Округлённым будет значение, присваиваемое этой функцией какой-либо переменной. Если кратко примером, что подразумеваю, то как-то так примерно:

   double a=1.12859;
   double c=NormalizeDouble(a,4);
//---
   TEST_PRINT_TWO(DoubleToString(a),DoubleToString(c));
   TEST_PRINT_ONE(DoubleToString(NormalizeDouble(a,4)));
   TEST_PRINT_ONE(DoubleToString(a));

Результат:

21, OnStart, DoubleToString(a) = 1.12859000, DoubleToString(c) = 1.12860000
22, OnStart, DoubleToString(NormalizeDouble(a,4)) = 1.12860000
23, OnStart, DoubleToString(a) = 1.12859000

Т.е., значение a - осталось прежним, побывав в NormalizeDouble(), нормализованное значение у c
 

Если для работы требуются значения больше восьми знаков, а эффект нужен подобный NormalizeDouble(), то, естественно, можно применять не её, а другие способы. Не её можно применять и для чисел с до восьми десятичных.

Кроме того, учитывая то, что максимальная точность на графиках сейчас, насколько знаю, это пять десятичных знаков, то значимая точность в 8 знаков - это, имхо, более чем достаточно для работы с ордерами, объектами и другой "повседневкой".


Попутно задам следующий вопрос:

Если кто-либо считает, что, например, для графических объектов и иной повседневки при трейдинге этого недостаточно и нужна значимая точность значений больше восьми знаков после запятой, то приведите, пожалуйста, реальные практические (не теоретические) примеры.


В своей формуле, Alexey, вы применяете NormalizeDouble().

Где ставите:

  • digits, равный DBL_DIG. Согласно Справке, DBL_DIG = 15. Оно у вас стоит в цикле for со знаком <=, т.е., вырастает до 16.
  • DBL_EPSILON - согласно Справке, это наименьшее число для которого выполняется условие 1.0+DBL_EPSILON != 1.0 (2.2204460492503131e-016)
К чему это может приводить:

Для чисел, наподобие 0.1020, ваша функция, как и моя, будет показывать количество десятичных знаков, равное трём.

Но для чисел, например, 1.098665432 (здесь по факту девять десятичных знаков), ваша функция показывает наличие 16-ти десятичных знаков. Что ведёт к тому, что, например, можно считать числа, ненормализованными до интересующего, в то время как они нормализованы до интересующего.

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

Вы говорили, что готовы поверить конкретному коду, а не  просто словам.

Вот код:

#define TEST_PRINT_THREE(v1,v2,v3)       Print(__LINE__,", ",__FUNCTION__,", ",(#v1)," = ",(v1),\
                                              
", ",(#v2)," = ",(v2),", ",(#v3)," = ",(v3))
//+------------------------------------------------------------------+
void OnStart()
  {
//--- 
   double array[]=
     {
      0.0,              //actual 0  => function = 0
      1.0,              //actual 0  => function = 0
      1.09,             //actual 2  => function = 2
      1.09801,          //actual 5  => function = 5
      1.095,            //actual 3  => function = 3
      1.09826,          //actual 5  => function = 5
      1.098365,         //actual 6  => function = 6
      1.0985658,        //actual 7  => function = 7
      1.09816543,       //actual 8  => function = 8
      1.098665432,      //actual 9  => function = 16
      1.0989654321,     //actual 10 => function = 16
      5.895,            //actual 3  => function = 3
      0.1020,           //actual 3  => function = 3
      9.10208,          //actual 5  => function = 5
      1.0560,           //actual 3  => function = 3
      3.050608,         //actual 6  => function = 6
      1.090905,         //actual 6  => function = 6
      8.000000001,      //actual 9  => function = 16
      86.68100000000001 //actual 14 => function = 16
     };
   for(int i=ArraySize(array)-1;i>=0;i--)
     {TEST_PRINT_THREE(i,DoubleToString(array[i],15),fnDigits(array[i]));}
  }
//+------------------------------------------------------------------+
//|               |
//+------------------------------------------------------------------+
uchar fnDigits(double num)
   {
      uchar i=0;
      for(i=0;i<=DBL_DIG;i++) if (MathAbs(MathAbs(NormalizeDouble(num,i))-MathAbs(num))<=DBL_EPSILON) break;
      return(i);
   }
//+------------------------------------------------------------------+

Как видите, я не вносила никаких изменений в вашу функцию.

Результат (он же, выше в коде комментариями):

39, OnStart, i = 18, DoubleToString(array[i],15) = 86.681000000000012, fnDigits(array[i]) = 16
39, OnStart, i = 17, DoubleToString(array[i],15) = 8.000000001000000, fnDigits(array[i]) = 16
39, OnStart, i = 16, DoubleToString(array[i],15) = 1.090905000000000, fnDigits(array[i]) = 6
39, OnStart, i = 15, DoubleToString(array[i],15) = 3.050608000000000, fnDigits(array[i]) = 6
39, OnStart, i = 14, DoubleToString(array[i],15) = 1.056000000000000, fnDigits(array[i]) = 3
39, OnStart, i = 13, DoubleToString(array[i],15) = 9.102080000000001, fnDigits(array[i]) = 5
39, OnStart, i = 12, DoubleToString(array[i],15) = 0.102000000000000, fnDigits(array[i]) = 3
39, OnStart, i = 11, DoubleToString(array[i],15) = 5.895000000000000, fnDigits(array[i]) = 3
39, OnStart, i = 10, DoubleToString(array[i],15) = 1.098965432100000, fnDigits(array[i]) = 16
39, OnStart, i = 9, DoubleToString(array[i],15) = 1.098665432000000, fnDigits(array[i]) = 16
39, OnStart, i = 8, DoubleToString(array[i],15) = 1.098165430000000, fnDigits(array[i]) = 8
39, OnStart, i = 7, DoubleToString(array[i],15) = 1.098565800000000, fnDigits(array[i]) = 7
39, OnStart, i = 6, DoubleToString(array[i],15) = 1.098365000000000, fnDigits(array[i]) = 6
39, OnStart, i = 5, DoubleToString(array[i],15) = 1.098260000000000, fnDigits(array[i]) = 5
39, OnStart, i = 4, DoubleToString(array[i],15) = 1.095000000000000, fnDigits(array[i]) = 3
39, OnStart, i = 3, DoubleToString(array[i],15) = 1.098010000000000, fnDigits(array[i]) = 5
39, OnStart, i = 2, DoubleToString(array[i],15) = 1.090000000000000, fnDigits(array[i]) = 2
39, OnStart, i = 1, DoubleToString(array[i],15) = 1.000000000000000, fnDigits(array[i]) = 0
39, OnStart, i = 0, DoubleToString(array[i],15) = 0.000000000000000, fnDigits(array[i]) = 0

 Более детально (по значениям и оборотам внутри своей функции) вы можете провести тестирование самостоятельно.


Можете потом посмотреть работу моей:

//+------------------------------------------------------------------+
//|NumberOfDigitsGet - number of digits after decimal point          |
//+------------------------------------------------------------------+
//|2015, Dina Paches (Updated: 2016.12.12)                           |
//|dipaches.blogspot.com                                             |
//|https://login.mql5.com/ru/users/dipach                            |
//+------------------------------------------------------------------+
int NumberOfDigitsGet(const double value,
                      const int   digits=8)//<= F1: NormalizeDouble() to 8
  {
   int dig=(digits<1 || digits>8) ? 8 : digits;
//---
   if(NormalizeDouble(value,dig)==0){return(0);}
//---
   double val=value;
//---
   if(NormalizeDouble(val,dig)<0){val=MathAbs(val);}
//---
   double int_value=MathFloor(val);
//---
   val=NormalizeDouble(val-int_value,dig);
//---
   if(NormalizeDouble(val,dig)==0){return(0);}
//---
   int variable =(int)MathPow(10,dig);
   variable     =(int)(NormalizeDouble(val*variable,0));
//---
   string text  =IntegerToString(variable);
   variable     =StringLen(text);
//---
   for(int i=variable-1;i>=0;i--)
     {
      if(StringFind(text,"0",i)==i){dig--;}
      else {return(dig);}
     }
//---
   return(dig);
  }
//+------------------------------------------------------------------+
Причина обращения: