elektroyar
elektroyar личный блог
15 мая 2023, 19:14

Ошибки в 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), после чего баг исчез.

15 Комментариев
  • 3Qu
    15 мая 2023, 19:25
    Про MQL лучше писать на форуме MQL. На СЛ толку от этого никакого.
    На MQL тоже, но ближе к телу.)
    Про АПИ на MQL лучше не писать.))
  • Jame Bonds
    15 мая 2023, 19:53
    double a = 5;
    double b = 10;
    
    if (a > b) {
    
    }
    Никогда и не выполнится.
    Что-то вы не договариваете. Хотя если терминал был не оригинальный, а доработанный брокером, то это брокер мог накосячить.
    Вот уж почти 10 лет работаю с MT5, подобных ошибок от компилятора никогда не встречал.
    Когда встречалась «магия», каждый раз вскоре убеждался, что из-за собственной криворукости.
    (А вот в API и библиотеках баги есть, это да.)
      • Jame Bonds
        15 мая 2023, 21:42
        elektroyar, добавьте внутрь блока if
        Print( «a=» + DoubleToString( a ) + " b=" + DoubleToString( b ) );
        Уверен на 100%, что это докажет, что if работает правильно.
        А еще дебаггер есть.
  • Андрей К
    15 мая 2023, 19:56
    Видимо у вас факт асинхрощины, о которой вы написали, шибко влияет.

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

    Асинхронщина она вообще такая, если ее программировать только изредка, она по башке часто будет бить. Я бы рыл в этом направлении
  • По началу все шло хорошо, но вот захотелось добавить асинхронное открытие и закрытие сделок. 
    Зачем интересно.
      • elektroyar, 
        никакой разницы в скорости открытия синхронного и асинхронного ордеров нет. разница в том, что синхронный приказ ждет подтверждения от сервера.
        в итоге в логе терминала у асинхронного приказа фиксируется время установки ордера (placed for execution), а у синхронного время исполнения (done) или иного статуса.
        естественно время установки меньше времени исполнения, но это все равно, что сравнивать мягкое с теплым. 
          • elektroyar, 
            я сильно сомневаюсь, что у вас появятся одновременные сделки на 5 символах в интервале допустим 50 мсек. 
            также сомневаюсь, что задержка в 50 мсек в такой маловероятной ситуации серьезно повлияет на любую из метрик алгоритма :)
            но программистам обычно важен сам процесс, поэтому не буду убеждать. успехов!
  • Beach Bunny
    16 мая 2023, 01:57
    Видимо в MQL есть/били баги с мультипоточностью, поэтому и значения в переменных скачут непредсказуемо, надо или переменную пометить как volatile либо блокировки добавить(но это может не прокатить если какие-то значения меняет сам терминал)
  • svgr
    16 мая 2023, 11:24
    А как там числа с плавающей точкой сравниваются, выясняли? В C++ точно, возможно и в C#, надо писать свою процедуру для этого.
    Вы объявляете double, а значения берутся integer, может такие и стоило объявлять? С integer проблема не возникнет.

Активные форумы
Что сейчас обсуждают

Старый дизайн
Старый
дизайн