Блог им. elektroyar

Ошибки в MQL5

Как-то раз решил перенести свой советник с МТ4 на МТ5. И все было по началу хорошо, пока не обнаружил странное поведение в коде. При помощи записи состояния всех переменных стратегий в лог я выяснил, что некоторые условия срабатывали тогда, когда не надо. И наоборот, некоторые условия не срабатывали.

Самый простой пример:

double a = 5;
double b = 10;

if (a > b) {

}

В любом языке программирования такое условие не выполнится. Но в MQL5 подобные условия иногда выполняются. И от чего это зависит — не понятно.

Тогда мне помогло лишь одно: я переехал с терминала МТ5 от брокера Альпари (он не хотел обновляться до свежей версии) на оригинальный МТ5 последней (на тот момент) версии. И это помогло, подобные баги исчезли. Менять что либо в коде было бесполезно.

И вот недавно я решился сделать заказ для знакомого. По началу все шло хорошо, но вот захотелось добавить асинхронное открытие и закрытие сделок. Казалось бы, что может быть проще?

Опустим такую деталь, что в очень удобном МТ5 нет специальной переменной в запросе, чтобы можно было однозначно судить о том, ответ на какой именно запрос открыть позицию пришел от сервера. Для этих целей есть только магик и комментарий к ордеру. Поэтому уже на этом этапе делаем костыль в виде записи специального числа в комментарии к сделке. Но это ладно, жить можно. Подумаешь, что почти в любом API есть обычно специальная переменная для передачи пользовательских данных. Ну решили в МТ5 такое не делать. 

И вот значит встает вопрос, что иногда сделки не открываются с первого раза. Да и не всегда закрываются. Значит, нужно добавить повторные попытки открыть/закрыть позицию. Казалось бы, что может быть проще. Но MT5 не был бы MT5, если бы все было просто. Я бы лучше на C++ писал все, дайте мне REST API, поток котировок, чем вот это вот все.

Потому что имеем следующее:

pos_info.error++;
const int close_error = pos_info.error;
Print("-close-error ", symbol, " magic ", m_magic, " ticket ", ticket, " uid ", unique_id, " num ", close_error, " attempts ", m_open_attempts);
if (close_error > m_open_attempts) {
	Print("-1-close-error; errors ", close_error);
	Print("-1-close-error; attempts ", m_open_attempts);
	break;
}
if (close_error == m_open_attempts) {
	Print("-2-close-error; errors ", close_error);
	Print("-2-close-error; attempts ", m_open_attempts);
}
if (close_error != m_open_attempts) {
	Print("-3-close-error; errors ", close_error);
	Print("-3-close-error; attempts ", m_open_attempts);
}
if (close_error < m_open_attempts) {
	Print("-4-close-error; errors ", close_error);
	Print("-4-close-error; attempts ", m_open_attempts);
}
if (close_error > m_open_attempts) {
	pos_info.state = TradePositionState::POSITION_CLOSE_ERROR;
	on_close_position_error(m_magic);
	on_close_position(m_magic, symbol, pos_info.type, result);
	remove_position(symbol, unique_id);
	Print("-4-remove ", symbol, " magic ", m_magic, " ticket ", ticket, " uid ", unique_id);
	break;
}

Переменная close_error содержит 1, так как ошибка при закрытии позиции возникла 1 раз. Это видно в терминале в соответствующем сообщении. Переменная m_open_attempts содержит 5. Внимание, вопрос: какие условия сработают в этом коде?

Правильный ответ

Условия:

if (close_error > m_open_attempts) {
	Print("-1-close-error; errors ", close_error);
	Print("-1-close-error; attempts ", m_open_attempts);
	break;
}
if (close_error == m_open_attempts) {
	Print("-2-close-error; errors ", close_error);
	Print("-2-close-error; attempts ", m_open_attempts);
}
Не сработают, что логично.

Условия:

if (close_error != m_open_attempts) {
	Print("-3-close-error; errors ", close_error);
	Print("-3-close-error; attempts ", m_open_attempts);
}
if (close_error < m_open_attempts) {
	Print("-4-close-error; errors ", close_error);
	Print("-4-close-error; attempts ", m_open_attempts);
}

Сработают. И условие:

if (close_error > m_open_attempts) {
	pos_info.state = TradePositionState::POSITION_CLOSE_ERROR;
	on_close_position_error(m_magic);
	on_close_position(m_magic, symbol, pos_info.type, result);
	remove_position(symbol, unique_id);
	Print("-4-remove ", symbol, " magic ", m_magic, " ticket ", ticket, " uid ", unique_id);
	break;
}

Тоже сработает!

Тут кто-то заметит, что есть 

break;

Потому что сами условия находятся в switch. Возможно, это запретная магия, и для одного case условия должен быть один break в конце, и его нельзя писать внутри условий if {}. Я не знаю, буду проверять, хотя как это может влиять на поведение условия… Отключение оптимизации — не помогает.

Попробовал поискать информацию о таких ошибках в интернетах. Нашел такое упоминание. И по сути, это все.

P.S.

Сегодня (15.05.2023) вышло обновление МТ5 (билд 3730), после чего баг исчез.

  • обсудить на форуме:
  • MQL5
15 комментариев
Про MQL лучше писать на форуме MQL. На СЛ толку от этого никакого.
На MQL тоже, но ближе к телу.)
Про АПИ на MQL лучше не писать.))
avatar
double a = 5;
double b = 10;

if (a > b) {

}
Никогда и не выполнится.
Что-то вы не договариваете. Хотя если терминал был не оригинальный, а доработанный брокером, то это брокер мог накосячить.
Вот уж почти 10 лет работаю с MT5, подобных ошибок от компилятора никогда не встречал.
Когда встречалась «магия», каждый раз вскоре убеждался, что из-за собственной криворукости.
(А вот в API и библиотеках баги есть, это да.)
avatar

Jame Bonds, вы видимо плохо прочитали текст. Я и сам написал, что такой код никогда не должен выполняться. Но в MQL5 от чего-то зависит. Одно и то же условие, видимо, в зависимости от содержимого внутри условия, может срабатывать. 

Это еще не все чудеса. В одном из билдов MQL5 у меня пропускался элемент массива в цикле. Т.е. буквально его просто бац и нет. И причем, это носило случайный характер. 

Помогло только обновление. И вот опять странные баги.

avatar
elektroyar, добавьте внутрь блока if
Print( «a=» + DoubleToString( a ) + " b=" + DoubleToString( b ) );
Уверен на 100%, что это докажет, что if работает правильно.
А еще дебаггер есть.
avatar

Jame Bonds, недавно встречал баг, где советник открыл сделку которой вообще нет в логах. Причем логировал я не только открытие сделки, но и расчет лота, наличие сигнала на сделку и т.д. Нигде в логах нет сделки) Была сделка по EURGBP на лот 0.1, но открылось в итоге две, вторая с лотом 0.08. 

И логика там тоже криво работала. У меня код, который открывает сделки, был сделан по принципу конечного автомата. Когда состояние переключается на «открытие сделок», то пока сделки не откроются, это состояние при повторном вызове функции просто делает return, и мы тут же выходим. Однако, return не срабатывал, в итоге функция доходила до конца, где состояние менялось обратно на начальное, так как сигнала уже не было.

Лечится это все отключением оптимизации у компилятора. Либо выходом новой версии обновления МТ5, но иногда обновление собственно и возвращает баг.

Я не хочу сказать что вот в моем коде багов не бывает. Просто такое поведение у МТ5 увы, встречается. Это вынуждает меня добавлять логирование везде, где можно, чтобы понимать что происходит, когда встречу странное поведение.

avatar
Видимо у вас факт асинхрощины, о которой вы написали, шибко влияет.

Быть может m_open_attempts вы изменяете асинхронно показному примеру, и пока исполнение кода добегает до последнего условия, он там уже другой. Вы как раз его не принтуете.

Асинхронщина она вообще такая, если ее программировать только изредка, она по башке часто будет бить. Я бы рыл в этом направлении
avatar
Андрей К, m_open_attempts является объектом класса, который инициализируется 1 раз в конструкторе класса и больше не меняется.
avatar
По началу все шло хорошо, но вот захотелось добавить асинхронное открытие и закрытие сделок. 
Зачем интересно.

Дмитрий Овчинников, одновременно открывается несколько сделок на одном и том же символе, и сигнал бывает тоже сразу на нескольких символах. Позиция разбивается на несколько частей, т.к. бывает ситуация когда на символе повышенная маржа и из-за чего сделка может не открыться. Заказчик попросил сделать разбивку на части, т.к. лучше если откроется часть сделок, чем ноль. 

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

avatar
elektroyar, 
никакой разницы в скорости открытия синхронного и асинхронного ордеров нет. разница в том, что синхронный приказ ждет подтверждения от сервера.
в итоге в логе терминала у асинхронного приказа фиксируется время установки ордера (placed for execution), а у синхронного время исполнения (done) или иного статуса.
естественно время установки меньше времени исполнения, но это все равно, что сравнивать мягкое с теплым. 

Дмитрий Овчинников, разница появляется, если открывать сделки последовательно. Если скажем, был сигнал на 5-ти символах сразу, то 5 раз подряд будет вызов синхронной функции, и 4-5-тая сделка откроется с значительной задержкой.

 

Баг кстати исчез. Сегодня вышло обновление, обновил терминал и ошибка больше не появляется.

avatar
elektroyar, 
я сильно сомневаюсь, что у вас появятся одновременные сделки на 5 символах в интервале допустим 50 мсек. 
также сомневаюсь, что задержка в 50 мсек в такой маловероятной ситуации серьезно повлияет на любую из метрик алгоритма :)
но программистам обычно важен сам процесс, поэтому не буду убеждать. успехов!
Дмитрий Овчинников, Брокеры делают задержку на вход в сделку порой до 2-4 секунд. Видимо борются таким образом с арбитражниками. Этот бот скальпировать должен с горизонтом прогноза на несколько минут. Поэтому тут нежелательно открывать сделки с задержками в несколько секунд. Если сигналов возникло несколько (советник мультивалютный), то они открываются с значительной разницей во времени. 
avatar
Видимо в MQL есть/били баги с мультипоточностью, поэтому и значения в переменных скачут непредсказуемо, надо или переменную пометить как volatile либо блокировки добавить(но это может не прокатить если какие-то значения меняет сам терминал)
avatar
А как там числа с плавающей точкой сравниваются, выясняли? В C++ точно, возможно и в C#, надо писать свою процедуру для этого.
Вы объявляете double, а значения берутся integer, может такие и стоило объявлять? С integer проблема не возникнет.
avatar

теги блога elektroyar

....все тэги



UPDONW
Новый дизайн