Вы упускаете торговые возможности:
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Регистрация
Вход
Вы принимаете политику сайта и условия использования
Если у вас нет учетной записи, зарегистрируйтесь
Я начал с базовой предпосылки о равенстве цен (скорее, просто равенстве парных цен).
(P1). Предполагая y = 1.50000: x == y, если x - любое действительное число, которое (i) больше или равно 1.499995 и (ii) меньше 1.500005.
Основываясь на P1, я пришел к выводу, что -
(P2). При условии y = 1.50000: a == y, b == y и a == b, если a и b - действительные числа, которые (i) больше или равны 1.499995 и (ii) меньше 1.500005.
Примеры: 1.500055 == 1.50006, 1.500055 == 1.500064, 1.500051 != 1.500059 и 1.500054 != 1.500056.
Используя вышесказанное, я создал функцию (см. ниже), которая (1) принимает две цены в качестве аргументов, (2) округляет эти цены до ближайшего точечного эквивалента и (3) определяет, равны ли эти две цены.
Эта функция проста и понятна, но я должен немного прокомментировать часть "Округление цены". Как многие из нас знают, удвоенные (т.е, Я обнаружил, что когда я округлил 1.5000551 и 1.5000550 до ближайшей точки и сравнил результат (1.50006 и 1.50005, соответственно), они оказались не равны, хотя, согласно P1 и P2 выше, они должны быть равны. Я пришел к выводу (после проведения пары тестов), что литерал 1.5000550 хранится в переменной как ~1.5000549999. Чтобы исправить это, я решил, что если цена находится в пределах 15 десятитысячных пункта от точки половины пути (x.xxxxx5), то я буду считать, что цена достигла минимального порога для округления до ближайшего пункта. Соответственно, я добавляю 15 десятитысячных пункта к каждой цене перед округлением до ближайшего пункта. На данный момент я не считаю, что это добавление имеет какие-либо непредвиденные последствия. Более того, эти значения могут быть скорректированы для увеличения или уменьшения предположения для округления до ближайшего пункта.
RaptorUK и WHRoeder(и другие):Используя вышесказанное в качестве образца, я создал следующую функцию ComparePrices(), которая основана на предыдущем сообщении RaptorUK:
Как всегда, приветствуются поучительные/конструктивные комментарии. :)
Я сам немного поиграл с этим - пытался достичь приемлемого компромисса между читабельностью и производительностью.
Я остановился на отдельных функциях eq(a,b), ne(a,b), lt(a,b) и т.д....
Например, .
if (eq(a,b)) { ...}
Что касается производительности на моей медленной виртуальной машине для 4999999 итераций я получаю следующие базовые измерения:
Пустой цикл : 370 мс
inline MathAbs(a-b) < gHalfPoint (global) : 2482 мс
Пустая bool-функция: 4266 мс <-- Я стремлюсь приблизиться к этой цифре как можно ближе.
Ниже приведены самые быстрые реализации eq(), которые мне удалось найти.
Они примерно в 2.3 раза медленнее, чем вызов inline MathsAbs() и в 1.3 раза медленнее, чем вызов пустой функции boolean, которая просто возвращает true.
Также в качестве дополнения я обнаружил, что MQL не замыкает булевы выражения.
5558 мс
Или если вы предпочитаете статику глобалам (чтобы держать весь код в одном месте):
за 5718 мс
lt(), gt() и т.д. должны быть быстрее, поскольку eq() и ne() сложнее.
Никак. Плавающая точка НИКОГДА не является точной для некоторых чисел.
https://en.wikipedia.org/wiki/Floating_point
Числа с плавающей точкой - это рациональные числа, потому что они могут быть представлены как одно целое число, деленное на другое. Например, 1,45×103 - это (145/100)*1000 или 145000/100. Однако основание определяет, какие дроби могут быть представлены. Например, 1/5 не может быть точно представлена как число с плавающей точкой с двоичным основанием, но может быть точно представлена с десятичным основанием (0,2, или 2×10-1). Однако 1/3 не может быть точно представлена ни в двоичной (0.010101...), ни в десятичной (0.333....) системе счисления, но в базе 3 это тривиально (0.1 или 1×3-1).Двойное значение от брокера может быть где угодно от 1.2345750000000000 до 1.23458499999999999 и все еще считаться той же самой ценой 1.23458.
И все же ваша функция говорит, что 1.2345750000000000 НЕ является GToE 1.23458499999999999.
И все же ваша функция говорит, что 1.23458499999999999999 НЕ ЯВЛЯЕТСЯ LToE 1.2345750000000000.
Вы должны использовать точку/2 в сравнениях https://www.mql5.com/en/forum/136997/page3#780837.
Я сам немного поиграл с этим - пытался достичь приемлемого компромисса между читабельностью и производительностью.
Двойное значение от брокера может быть где угодно от 1,2345750000000000 до 1,234584999999999 и все равно считаться той же самой ценой 1,23458.
В целом я согласен. См. мои P1 и P2 в моем сообщении выше.
WHRoeder:
Тем не менее, ваша функция говорит, что 1.2345750000000000 НЕ является GToE 1.23458499999999999.
И все же ваша функция говорит, что 1.23458499999999999999 НЕ является LToE 1.234575000000000000.
Проблема возникает из-за того, как MT4/MQL хранит значения с плавающей точкой в переменных. Например:
выводит эти две переменные в журнал/журнал:
Как вы можете видеть, p2 больше не является 1.23458499999999999, а вместо этого становится 1.23458500 - из-за, я полагаю, округления. Именно по этой причине моя функция говорит, что p1 не равно p2; и как вы можете видеть в приведенном ниже коде, ваш код также предполагает то же самое - то есть, что p1 не равно p2 и что p1 не равно p2.
Вы должны использовать точку/2 в сравнениях
Существует вероятность того, что Point/2 - это слишком маленькое максимальное отклонение. Например:
Если предполагается, что 1,234575 равно 1,234580, то почему #2 показывает NEQ? Более того, если мы предположим, что 1.23458 - это цена, которая может означать цену у брокера, находящуюся в любом диапазоне от 1.2345750000000000 до 1.234584999999999, то почему #1 должен показывать NEQ? Разве они не должны быть равны, если у них одна и та же ценовая точка (отсюда моя предпосылка №2 в моем сообщении выше)?
@Thirteen,
В вашем коде вы рассматриваете преднамеренные ошибки округления из-за логики приложения, а не непреднамеренные ошибки округления из-за ошибок плавающей точки, отсюда и разница:
Два типа "округления":
a) ошибки внутреннего округления из-за двоичных дробей в формате IEEE. - Эти числа должны быть абсолютно одинаковыми, но это не так из-за двоичного представления десятичных дробей. Они округляются за счет MQ4-представления десятичных дробей.
б) Явное округление до некоторого числа или десятичных знаков. (например, при печати или отправке цен брокеру). - Эти значения на самом деле не должны быть одинаковыми, вместо этого они округляются логикой приложения для чьего-то удобства.
На самом деле это не является ошибкой. Ошибки, связанные исключительно с представлением с плавающей запятой, вряд ли будут настолько большими (если только не вычислять серию плохо). Но вы можете захотеть сделать такой тип сравнения в вашем приложении в соответствии с вашей собственной логикой.
Внутренние ошибки округления[a] обычно очень малы (на порядки меньше, чем Point) и непреднамеренны. Приложение не в состоянии округлить эти числа до точного значения, используя тип данных double .
Явные различия в округлении[b] являются преднамеренными и намного больше (+/- 0.5 пункта). (в данном случае). Поэтому два числа, округленные логикой приложения до одного и того же значения, могут изначально отличаться почти на целый балл.
В идеале я бы сначала округлил числа [b] (только если округление необходимо) , а затем сравнил их [a], в этот момент ошибка очень мала из-за ограничений удвоения. (например, < 0.0000001).
Но в вашем коде сравнение выполняется перед округлением, и в этом случае вам приходится иметь дело с гораздо большими возможными различиями. Однако округление требуется не всегда. Я бы использовал его только при отправке цен брокеру.
Подумайте об этом с другой стороны (Если бы MQ4 использовал двоично-кодированную десятичную систему счисления - которая позволяет точно представлять десятичные дроби - тогда все вопросы относительно Price != Price исчезли бы,
Но вам все равно придется округлять и сравнивать числа в вашем приложении до ближайшей точки для определенных операций. (В основном это функции OrderXXX).
>> "если мы предположим, что 1.23458 - это цена, то это может означать цену у брокера, которая находится в диапазоне от 1.2345750000000000 до 1.23458499999999999".
Я могу ошибаться (не знаю, как работают брокеры), но я думаю, что цена от брокера 1.23458 - именно такая. Особенно при размере лота $100,000 и больше. В противном случае можно заработать много денег (брокеру), используя разницу в опубликованных ценах.
Я понимаю, что на самом деле округлять нужно только при отправке брокеру, а не по всему приложению. В этом случае сравнения для небольшой погрешности должно быть достаточно.
Неточность плавающей точки отделена от округления для брокерских цен. Но если вы хотите иметь дело с ними обоими одновременно, я думаю, это личное предпочтение (хотя это может запутать?).
Вот моя полная версия, (надеюсь, без ошибок)...
Это обеспечивает 6 функций:
eq(a,b) = ne(a,b) != gt(a,b) > lt(a,b) < ge(a,b) >= le(a,b) <= if (ge(Bid,target)) sell sell sell...
Рациональным является сохранение читабельности кода (IMO) и снижение вероятности ошибок при вводе, без слишком большого падения производительности.Для всех намерений и целей эти функции должны быть настолько быстрыми, насколько это может быть сделано с помощью пользовательских функций MQ4,
(для сравнения производительности с MathAbs(a-b) < HalfPoint смотрите https://www.mql5.com/en/forum/136997/page5#822505, хотя в реальном советнике (в отличие от бенчмарка) я подозреваю, что разница незначительна.
Вот моя полная версия (надеюсь, без ошибок)...
...
Часто цитируемая предпосылка такова:
Учитывая эту предпосылку и используя ваш код в качестве фона, не могли бы вы объяснить мне, почему вы говорите, что (a) 1.234576 и 1.234584 считаются не равными, (b) 1.234577 и 1.234583 считаются не равными, но (c) 1.234578 и 1.234582 считаются равными? Почему (и каким образом) пример (b) является менее равным, чем пример (c)?
Как я уже говорил выше, я считаю все эти цены равными, потому что все они имеют одну и ту же ценовую точку - а именно 1,23458. Этот пример иллюстрирует, почему я считаю (и говорил об этом выше), что Point/2 может быть слишком маленьким максимальным отклонением.
@Thirteen, мой ответ на ваши наблюдения остается тем же 3 постами выше https://www.mql5.com/en/forum/136997/page5#822672 ссылка. Я повторю ту часть, которая может привести к озарению в понимании моей точки зрения: (с небольшими изменениями и добавленными акцентами)
Think of it another way (If MQ4 had used Binary Coded Decimal - which allows exact representation of Decimal fractions - then most of the original issues regarding Price != Price would go away, (and is often used on financial platforms for that very reason )
но вам все равно придется округлять и сравнивать числа в вашем приложении до ближайшей точки для определенных операций. (В основном это функции OrderXXX).
Это зависит от того, как вы пишете код, и хотите ли вы различать округление в приложении (когда два разных числа концептуально/логически рассматриваются как одно и то же для простоты/удобства),
и ошибками с плавающей запятой. Здесь нет правильного и неправильного, но я думаю, что один подход более запутанный, чем другой.....
Кроме того, я лично немного скептически отношусь к нецитируемой предпосылке (но могу поправить!), снова упомянутой в предыдущем сообщении.