У меня глюк, или отныне индексация исторических данных происходит задом наперед? - страница 3

 
Renat:

Все мы смогли.

Но за найденные ошибки спасибо - проверим и все исправим.

upd: уже исправлено, будет в следующем билде.

Вы, наверное, прочитали моё "не смогли" как "ничего не смогли". Нет, почему же ничего не смогли, - смогли, но не всё.

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

Повторюсь, смогли, но не всё. То, что касается профессионализма и качества - не смогли. И я сейчас это докажу. В очередной раз.

До сих пор находятся ТАКИЕ bug'и, что можно утверждать, что процент найденных bug'ов невелик, и поэтому продукт - как был в состоянии Alpha, так там, в общем-то, там и остался.

Если изначально имевшееся количество bug'ов условно обозначить за 100%, то сейчас - хорошо, если 10% bug'ов исправлено, а 90% осталось. Почему?

Потому что bug'и и сейчас почти так же легко ищутся, а сами bug'и - простые, не отличаются особой замысловатостью условий.

Может быть проведена простая аналогия: предположим, в корзине находятся шарики, красные и белые, примерно поровну, равномерно перемешанные. Задача - удалить все красные шарики. Сначала это достаточно легко, да и шарики видны и лежат на поверхности. Но чем больше красных шариков удалено из корзины, а, точнее, чем меньше их в процентном отношении остаётся в корзине, тем труднее становится выловить очередной красный шарик, и тем глубже надо зарываться в корзину.

Так же и с bug'ами в программных продуктах - чем меньший процент их остался, тем труднее их найти, и тем замысловатее сами bug'и и сложнее условия, при которых они проявляются.

И не стоит обманываться, имея ввиду - "bug-то исправлен, проблемы нет". Да, bug исправлен, конкретно для случая этого bug'а - проблемы теперь нет, но куда важнее общая проблема: количество, точнее, процент bug'ов в продукте остался практически таким же большим. Это значит, что вероятность наткнуться на очередной bug осталась почти на прежнем уровне.

Вот когда процент bug'ов ощутимо снизится до уровня, когда уже трудно будет наткнуться на bug просто так и даже не просто так, а целенаправленно, вот тогда можно будет говорить, что - и правда, проблема огромного количества bug'ов постепенно сходит на нет, можно уже даже пользоваться продуктом. Тогда, кстати, и о релизе можно будет подумать.

А сейчас это всё больше напоминает попытку разработать часть коммерческого продукта в стиле продукта OpenSource. Этакий гибридный метод. Получается плохо. Или даже очень плохо.

А в MQL5 bug'и не просто легко ищутся, - на них просто натыкаешься, пытаясь усовершенствовать какую-нибудь простую функцию, например, функцию вычисления максимального лота, который можно использовать для открытия позиции, насколько позволяет свободная маржа.

Теперь, собственно, о самом интересном, то есть, о самих bug'ах.

Есть такая замечательная функция NormalizeDouble(). Функция от состояния торговой подсистемы, да и прочих подсистем не зависит, на состояние тех систем не влияет, да и вообще, функция - сама в себе, выполняет простейшее действие, - ну какие же тут могут быть bug'и?

Но MetaQuotes не была бы MetaQuotes, если бы и в этой функции не было бы bug'ов.

На значения второго параметра глубоко не тестировал, выяснил только, что отрицательные значения воспринимаются как 0, хотя можно было бы распространить действие функции и на уместный диапазон отрицательных значений, но главное здесь было для меня не это, а то, что bug'ов - нет, функция ведёт себя адекватно. Зато протестировал функцию на различные значения первого параметра, и вот, что получилось:

void normalize(double d)
{
  Print("d = ", d, ", NormalizeDouble(d, 0) = ", NormalizeDouble(d, 0));

  if(d != NormalizeDouble(d, 0)) // Проверка на вшивость
    Print("Шо, опять???");

  if(MathAbs(d - NormalizeDouble(d, 0)) > MathAbs(d)) // Проверка на вопиющесть вшивости
    Print("Я, конечно, извиняюсь, но этим продуктом пользоваться невозможно: глубочайшая Alpha!!!");
}

void OnStart()
{
  normalize(10);
  normalize(1e10);
  normalize(1e20);
}

/* Вывод в лог (хронология сверху вниз:
HG      0       1 (EURUSD,M15)  02:29:49        d = 10, NormalizeDouble(d, 0) = 10
JO      0       1 (EURUSD,M15)  02:29:49        d = 10000000000, NormalizeDouble(d, 0) = 10000000000
JG      0       1 (EURUSD,M15)  02:29:49        d = 1e+020, NormalizeDouble(d, 0) = -1.844674407370955e+019
HK      0       1 (EURUSD,M15)  02:29:49        Шо, опять???
II      0       1 (EURUSD,M15)  02:29:49        Я, конечно, извиняюсь, но этим продуктом пользоваться невозможно: глубочайшая Alpha!!!
*/

Для чисел, начиная где-то в диапазоне между 1e10 и 1e20, функция возвращает не то, что не близкое значение, - вообще другого знака!

А ведь диапазон double простирается аж до 1.8e308. Ну, и как работать с такими функциями?

Также понадобилось мне применить механизм неявного приведения типов, который имеет место в выражении "u * d", где u - значение типа ulong, а d - типа double. Значение типа ulong, согласно механизму неявного приведения типа, сначала приводится к типу double, потом происходит умножение, и результат также имеет тип double. Выяснилось, что этот механизм работает через пень-колоду: в некоторых случаях, зависящих как от значений типа ulong, так и от "происхождения" этих значений, результат вычислений абсолютно неверен. Тестовый пример, демонстрирующий найденные "особенности":

void test(double parameter)
{
  double local_par = parameter;
  double local_imm = 0.1;

  ulong  ulong_mid = 12;
  ulong  ulong_max = ULONG_MAX;

  Print("parameter = ", parameter);
  Print("local_par = ", local_par);
  Print("local_imm = ", local_imm);
  Print("immediate = ", 0.1);
  Print("");
  Print("ulong_mid = ", ulong_mid);
  Print("ulong_mid * parameter = ", ulong_mid * parameter);
  Print("ulong_mid * local_par = ", ulong_mid * local_par);
  Print("ulong_mid * local_imm = ", ulong_mid * local_imm);
  Print("ulong_mid * immediate = ", ulong_mid * 0.1);
  Print("");
  Print("ulong_max = ", ulong_max);
  Print("ulong_max * parameter = ", ulong_max * parameter);
  Print("ulong_max * local_par = ", ulong_max * local_par);
  Print("ulong_max * local_imm = ", ulong_max * local_imm);
  Print("ulong_max * immediate = ", ulong_max * 0.1);
  Print("");
  Print("ULONG_MAX = ", ULONG_MAX);
  Print("ULONG_MAX * parameter = ", ULONG_MAX * parameter);
  Print("ULONG_MAX * local_par = ", ULONG_MAX * local_par);
  Print("ULONG_MAX * local_imm = ", ULONG_MAX * local_imm);
  Print("ULONG_MAX * immediate = ", ULONG_MAX * 0.1);
}

void OnStart()
{
  test(0.1);
}

/* Вывод в лог (хронология сверху вниз):
LI      0       1 (EURUSD,M15)  02:38:03        parameter = 0.1
HD      0       1 (EURUSD,M15)  02:38:03        local_par = 0.1
FO      0       1 (EURUSD,M15)  02:38:03        local_imm = 0.1
PK      0       1 (EURUSD,M15)  02:38:03        immediate = 0.1
EO      0       1 (EURUSD,M15)  02:38:03        
ML      0       1 (EURUSD,M15)  02:38:03        ulong_mid = 12
LG      0       1 (EURUSD,M15)  02:38:03        ulong_mid * parameter = 1.2
PL      0       1 (EURUSD,M15)  02:38:03        ulong_mid * local_par = 1.2
FE      0       1 (EURUSD,M15)  02:38:03        ulong_mid * local_imm = 1.2
HH      0       1 (EURUSD,M15)  02:38:03        ulong_mid * immediate = 1.2
MQ      0       1 (EURUSD,M15)  02:38:03        
GK      0       1 (EURUSD,M15)  02:38:03        ulong_max = 18446744073709551615
IO      0       1 (EURUSD,M15)  02:38:03        ulong_max * parameter = -0.1
KG      0       1 (EURUSD,M15)  02:38:03        ulong_max * local_par = -0.1
DR      0       1 (EURUSD,M15)  02:38:03        ulong_max * local_imm = 1.844674407370955e+018
DK      0       1 (EURUSD,M15)  02:38:03        ulong_max * immediate = 1.844674407370955e+018
QS      0       1 (EURUSD,M15)  02:38:03        
KI      0       1 (EURUSD,M15)  02:38:03        ULONG_MAX = 18446744073709551615
MM      0       1 (EURUSD,M15)  02:38:03        ULONG_MAX * parameter = -0.1
OQ      0       1 (EURUSD,M15)  02:38:03        ULONG_MAX * local_par = -0.1
HD      0       1 (EURUSD,M15)  02:38:03        ULONG_MAX * local_imm = 1.844674407370955e+018
HI      0       1 (EURUSD,M15)  02:38:03        ULONG_MAX * immediate = 1.844674407370955e+018
*/

Ну, невозможно же пользоваться продуктом! Не работает!

В любой момент неправильный результат может получиться, и не защититься от этого никак.

Сначала надо все подобные bug'и выловить, а уж потом пытаться пользоваться, потому что, собственно, написание чего-то превращается в отлов bug'ов.

Правда, дизайн программной части оставляет желать лучшего, но - хоть как-то работать можно. А с такими bug'ами - нельзя. Просто не получится. Работать не будет, да и всё.

Нет, простые учебные примеры, может, и будут работать, но это же - несерьёзно.

Поэтому, ещё раз повторюсь, - смогли, смогли, но - не всё. А именно - не смогли выполнить профессиональный дизайн программной части терминала, включая сам язык MQL5 и качественно его запрограммировать. Не смогли.

 
simpleton:

Вы, наверное, прочитали моё "не смогли" как "ничего не смогли". Нет, почему же ничего не смогли, - смогли, но не всё.

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

Повторюсь, смогли, но не всё. То, что касается профессионализма и качества - не смогли. И я сейчас это докажу. В очередной раз.

До сих пор находятся ТАКИЕ bug'и, что можно утверждать, что процент найденных bug'ов невелик, и поэтому продукт - как был в состоянии Alpha, так там, в общем-то, там и остался.

Если изначально имевшееся количество bug'ов условно обозначить за 100%, то сейчас - хорошо, если 10% bug'ов исправлено, а 90% осталось. Почему?

Потому что bug'и и сейчас почти так же легко ищутся, а сами bug'и - простые, не отличаются особой замысловатостью условий.

Может быть проведена простая аналогия: предположим, в корзине находятся шарики, красные и белые, примерно поровну, равномерно перемешанные. Задача - удалить все красные шарики. Сначала это достаточно легко, да и шарики видны и лежат на поверхности. Но чем больше красных шариков удалено из корзины, а, точнее, чем меньше их в процентном отношении остаётся в корзине, тем труднее становится выловить очередной красный шарик, и тем глубже надо зарываться в корзину.

Так же и с bug'ами в программных продуктах - чем меньший процент их остался, тем труднее их найти, и тем замысловатее сами bug'и и сложнее условия, при которых они проявляются.

И не стоит обманываться, имея ввиду - "bug-то исправлен, проблемы нет". Да, bug исправлен, конкретно для случая этого bug'а - проблемы теперь нет, но куда важнее общая проблема: количество, точнее, процент bug'ов в продукте остался практически таким же большим. Это значит, что вероятность наткнуться на очередной bug осталась почти на прежнем уровне.

Вот когда процент bug'ов ощутимо снизится до уровня, когда уже трудно будет наткнуться на bug просто так и даже не просто так, а целенаправленно, вот тогда можно будет говорить, что - и правда, проблема огромного количества bug'ов постепенно сходит на нет, можно уже даже пользоваться продуктом. Тогда, кстати, и о релизе можно будет подумать.

А сейчас это всё больше напоминает попытку разработать часть коммерческого продукта в стиле продукта OpenSource. Этакий гибридный метод. Получается плохо. Или даже очень плохо.

А в MQL5 bug'и не просто легко ищутся, - на них просто натыкаешься, пытаясь усовершенствовать какую-нибудь простую функцию, например, функцию вычисления максимального лота, который можно использовать для открытия позиции, насколько позволяет свободная маржа.

Теперь, собственно, о самом интересном, то есть, о самих bug'ах.

Есть такая замечательная функция NormalizeDouble(). Функция от состояния торговой подсистемы, да и прочих подсистем не зависит, на состояние тех систем не влияет, да и вообще, функция - сама в себе, выполняет простейшее действие, - ну какие же тут могут быть bug'и?

Но MetaQuotes не была бы MetaQuotes, если бы и в этой функции не было бы bug'ов.

На значения второго параметра глубоко не тестировал, выяснил только, что отрицательные значения воспринимаются как 0, хотя можно было бы распространить действие функции и на уместный диапазон отрицательных значений, но главное здесь было для меня не это, а то, что bug'ов - нет, функция ведёт себя адекватно. Зато протестировал функцию на различные значения первого параметра, и вот, что получилось:

Для чисел, начиная где-то в диапазоне между 1e10 и 1e20, функция возвращает не то, что не близкое значение, - вообще другого знака!

А ведь диапазон double простирается аж до 1.8e308. Ну, и как работать с такими функциями?

Также понадобилось мне применить механизм неявного приведения типов, который имеет место в выражении "u * d", где u - значение типа ulong, а d - типа double. Значение типа ulong, согласно механизму неявного приведения типа, сначала приводится к типу double, потом происходит умножение, и результат также имеет тип double. Выяснилось, что этот механизм работает через пень-колоду: в некоторых случаях, зависящих как от значений типа ulong, так и от "происхождения" этих значений, результат вычислений абсолютно неверен. Тестовый пример, демонстрирующий найденные "особенности":

Ну, невозможно же пользоваться продуктом! Не работает!

В любой момент неправильный результат может получиться и не защититься от этого никак.

Сначала надо все подобные bug'и выловить, а уж потом пытаться пользоваться, потому что, собственно, написание чего-то превращается в отлов bug'ов.

Правда, дизайн программной части оставляет желать лучшего, но - хоть как-то работать можно. А с такими bug'ами - нельзя. Просто не получится. Работать не будет, да и всё.

Нет, простые учебные примеры, может, и будут работать, но это же - несерьёзно.

Поэтому, ещё раз повторюсь, - смогли, смогли, но - не всё. А именно - не смогли выполнить профессиональный дизайн программной части терминала, включая сам язык MQL5 и качественно его запрограммировать. Не смогли.

1.

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

Дело в том, что при вызове normalize() передаваемый в нее параметр сравнивается, но перед этим не нормализуется.

На мой взгляд нормализация должна была выглядеть примерно так

//normalize
void normalize(double d)
{
//------------------------------------------------------------------------------------//
double NormalParam; //Нормализованный параметр
//------------------------------------------------------------------------------------//

//Нормализуем переданный параметр
NormalParam = NormalizeDouble(d, 0);

Print("d = ",d,", NormalizeDouble(d, 0) = ",NormalParam);

// Проверка на вшивость
  if(NormalParam!= NormalizeDouble(d, 0)) Print("Шо, опять???");

  if(MathAbs(d - NormalParam) > MathAbs(d))
    Print("Я, конечно, извиняюсь, но этим продуктом пользоваться невозможно: глубочайшая Alpha!!!");
//------------------------------------------------------------------------------------//
}
2. При проверки по тому образцу вызова который задали Вы оказалось что работа ведется успешно за исключением тех случаев когда для нормализации передается значение больше чем  1e15 (т.е 1e16, 1e17  и т.д. приведут к появлению надписи про Альфу)...
 

Спасибо за найденные ошибки - обязательно все исправим.

В первом случае происходило переполнение в вычислении больших значений, а во втором случае похоже на ошибку оптимизатора - вскрытие покажет.

Не беспокойтесь - с проектом все будет хорошо.

 
Interesting:

1.

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

Дело в том, что при вызове normalize() передаваемый в нее параметр сравнивается, но перед этим не нормализуется.

На мой взгляд нормализация должна была выглядеть примерно так

2. При проверки по тому образцу вызова который задали Вы оказалось что работа ведется успешно за исключением тех случаев когда для нормализации передается значение больше чем  1e15 (т.е 1e16, 1e17  и т.д. приведут к появлению надписи про Альфу)...

1. Да, в этом тестовом примере это специально так сделано. При этом в функцию специально передаются такие значения, которые уже нормализованы с точки зрения NormalizeDouble() (при заданном втором параметре). В данном случае - вообще для любого значения второго параметра (так получилось), но суть теста состояла в том, что передаётся уже нормализованное значение (нормализованное не функцией, а по факту, то есть, передаваемое значение имеет нулевую дробную часть), и, соответственно, функция NormalizeDouble() обязана возвращать то же самое значение, какое ей и передали в первом параметре. Потому и первая проверка "на вшивость" - проверка на равенство.

//normalize
void normalize(double d)
{
//------------------------------------------------------------------------------------//
double NormalParam; //Нормализованный параметр
//------------------------------------------------------------------------------------//

//Нормализуем переданный параметр
NormalParam = NormalizeDouble(d, 0); // Вот здесь получим в некоторых случаях неправильное значение

Print("d = ",d,", NormalizeDouble(d, 0) = ",NormalParam);

// Проверка на вшивость
  if(NormalParam!= NormalizeDouble(d, 0)) Print("Шо, опять???"); // А здесь прежде полученное неправильное сравним с только что сгенерённым, но точно таким же по значению неправильным: смысл?

  if(MathAbs(d - NormalParam) > MathAbs(d)) // Здесь - аналогичная потеря смысла
    Print("Я, конечно, извиняюсь, но этим продуктом пользоваться невозможно: глубочайшая Alpha!!!");
//------------------------------------------------------------------------------------//
}

2. Если бы была в наличии не глючная NormalizeDouble(), а неглючная PerfectNormalizeDouble(), и именно она была бы использована для присвоения значения переменной NormalParam, я бы согласился.

А так - смотри добавленные комментарии. И вообще - код прогонять надо перед публикацией, это правило хорошего тона. :)

 
Renat:

Спасибо за найденные ошибки - обязательно все исправим.

В первом случае происходило переполнение в вычислении больших значений, а во втором случае похоже на ошибку оптимизатора - вскрытие покажет.

Не беспокойтесь - с проектом все будет хорошо.

С проектом, может, и будет всё хорошо в том смысле, что жить - будет, не умрёт. :)

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

Зато, если будет достаточно времени на это, смогу найти и вскрыть более серьёзные ошибки проектирования. Никто же ещё не ковырял серьёзно все эти Ваши системы защиты и безопасности, да и вообще внутреннее устройство не смотрел. А это, если там что-то не по мелочи обнаружится, быстренько не исправишь. Но будет ли у меня время на это - пока не знаю.

И - ещё большой вопрос, где продукт окажется по прошествии времени относительно других продуктов. А жить, пожалуй, будет. Запас старых заслуг, по крайней мере, позволяет, - люди пока прощают Вам ужасное качество.

Но мне все равно интересно - к чему в конце концов приведёт такой подход, какой Вы используете, и как на это будет влиять качество и  дизайн программной части. Как на это всё будет реагировать сообщество, и во что это всё трансформируется в результате действия всех этих сил в сложившихся условиях.

 
simpleton:

2. Если бы была в наличии не глючная NormalizeDouble(), а неглючная PerfectNormalizeDouble(), и именно она была бы использована для присвоения значения переменной NormalParam, я бы согласился.

А так - смотри добавленные комментарии. И вообще - код прогонять надо перед публикацией, это правило хорошего тона. :)

Да вроде тестил оба варианта, в моем первой ошибки не было, а вот вторая появлялась.
 
Interesting:
Да вроде тестил оба варианта, в моем первой ошибки не было, а вот вторая появлялась.

Сделал более подробной распечатку различных значений:

//normalize
void normalize(double d)
{
//------------------------------------------------------------------------------------//
double NormalParam; //Нормализованный параметр
//------------------------------------------------------------------------------------//

//Нормализуем переданный параметр
NormalParam = NormalizeDouble(d, 0); // Вот здесь получим в некоторых случаях неправильное значение

Print("d = ", d, ", NormalParam = ", NormalParam, ", NormalizeDouble(d, 0) = ", NormalizeDouble(d, 0), ", (d - NormalParam) = ", d - NormalParam);

// Проверка на вшивость
  if(NormalParam!= NormalizeDouble(d, 0)) Print("Шо, опять???"); // А здесь прежде полученное неправильное сравним с только что сгенерённым, но точно таким же по значению неправильным: смысл?

  if(MathAbs(d - NormalParam) > MathAbs(d)) // Здесь - аналогичная потеря смысла
    Print("Я, конечно, извиняюсь, но этим продуктом пользоваться невозможно: глубочайшая Alpha!!!");
//------------------------------------------------------------------------------------//
}

void OnStart()
{
  normalize(10);
  normalize(1e10);
  normalize(1e20);
}

/* Вывод в лог (хронология сверху вниз):
LE      0       1 (EURUSD,M15)  16:22:13        d = 10, NormalParam = 10, NormalizeDouble(d, 0) = 10, (d - NormalParam) = 0
PQ      0       1 (EURUSD,M15)  16:22:13        d = 10000000000, NormalParam = 10000000000, NormalizeDouble(d, 0) = 10000000000, (d - NormalParam) = 0
OQ      0       1 (EURUSD,M15)  16:22:13        d = 1e+020, NormalParam = -1.844674407370955e+019, NormalizeDouble(d, 0) = -1.844674407370955e+019, (d - NormalParam) = 1.184467440737096e+020
LK      0       1 (EURUSD,M15)  16:22:13        Я, конечно, извиняюсь, но этим продуктом пользоваться невозможно: глубочайшая Alpha!!!
*/
Внимательно просмотрите третью строчку лога, помеченную символами OQ: значения NormalParam и NormalizeDouble(d, 0) равны для всех случаев входных параметров. И равны они потому, что значение NormalParam было получено через NormalizeDouble(d, 0), а не гипотетическую правильную PerfectNormalizeDouble(). Ещё бы они были не равны - тогда бы MetaQuotes превзошли сами себя! :)

А для второй проверки по сути всё осталось, как в моём первоначальном варианте. И, поскольку NormalizeDouble() меняет знак в последнем случае - для параметра 1e20, - то разность (d - NormalParam) становится больше, чем изначальное значение параметра. Поэтому и срабатывает условие второго if() - оно именно такое - если разность оказалась больше, чем было изначальное число.

Причина обращения: