Скачать MetaTrader 5

Сохранение важных текущих значений в программе на случай сбоя

Авторизуйтесь или зарегистрируйтесь, чтобы добавить комментарий
Нашел хорошего программиста? Ставь адресную заявку!
Artyom Kuraev
867
Artyom Kuraev 2016.08.07 14:08 

Здравствуйте, коллеги-программисты. Заинтересовал меня следующий вопрос, прошу поделиться опытом.

Предположим, имеется программа, которая имеет ряд флагов состояний и требует знания информации о том, какие сделки и в каком статусе находились на предыдущем тике. Одним словом, часть важной информации сохраняется во внутренней памяти программы. Однако если есть требование, чтобы в случае технической неисправности (сбоя терминала, перезагрузки компьютера и т.д.) программу можно было запустить с того места, на котором она остановилась, нужно, чтобы все эти сведения хранились где-то в ином месте. И здесь возникает вопрос, что можно в таких случаях использовать. Конечно, самый простой способ - отправлять эти сведения в csv или ini файлы, однако когда это делаешь на каждом тике, скорость исполнения программы сильно снижается, так как нужно не просто записать сведения, но и открыть/закрыть файл на каждом тике, иначе в случае сбоя несохраненные сведения "улетят".

Мне приходит решение сохранять данные во внутренней памяти и перезаписывать сведения только если происходит изменение состояния (содержания внешнего файла).

Поделитесь опытом, кто как решает подобные проблемы?

Vitalie Postolache
12145
Vitalie Postolache 2016.08.07 18:15  

Все сведения о сделках хранятся на сервере и они никуда не исчезают после сбоя клиента. Надо код строить так, чтобы состояния считывались из доступных источников, флаги - костыли. 

Artyom Kuraev
867
Artyom Kuraev 2016.08.07 19:30  
evillive:

Все сведения о сделках хранятся на сервере и они никуда не исчезают после сбоя клиента. Надо код строить так, чтобы состояния считывались из доступных источников, флаги - костыли. 

Оно конечно понятно, что нужно стремиться к минимуму флагов, но все-таки если флаги - костыли, то как бы Вы порекомендовали без них реализовать такую стратегию:

имеется перерисовывающийся индикатор, который рисует канал из трех линий - верхняя, средняя и нижняя. И мы должны выполнять стратегию А с того момента, как цена уходит вниз нижнего канала и до тех пор, пока не пробьет вверх среднюю линию, и, соответственно, стратегию Б с того момента, как цена уходит вверх верхнего канала до момента, как цена не пробьет вниз уровень средней линии. Подчеркну - советник перерисовывающийся. Реализовать такую стратегию без флагов я не представляю возможным. Поделитесь, пожалуйста, опытом, как это можно сделать без флагов?


И сам вопрос по сохранению значений флагов для меня остался актуальным.

Ilya Prozumentov
275
Ilya Prozumentov 2016.08.08 04:38  
Поскольку открытые сделки могут зарыться по стопу или тейку даже при выключенном терминале, то робот просто обязан перед началом работы убедиться какие сделки всё ещё находятся в рынке и из этого сделать вывод, в каком состоянии он должен находится. А флаги ещё можно в глобальные переменные терминала писать.
Vitalie Postolache
12145
Vitalie Postolache 2016.08.08 07:44  
delfik71091:

Оно конечно понятно, что нужно стремиться к минимуму флагов, но все-таки если флаги - костыли, то как быВы порекомендовали без них реализовать такую стратегию:

имеетсяперерисовывающийся индикатор, который рисует канал из трех линий -верхняя, средняя и нижняя. И мы должны выполнять стратегию А с тогомомента, как цена уходит вниз нижнего канала и до тех пор, пока непробьет вверх среднюю линию, и, соответственно, стратегию Б с тогомомента, как цена уходит вверх верхнего канала до момента, как цена непробьет вниз уровень средней линии. Подчеркну - советникперерисовывающийся. Реализовать такую стратегию без флагов я не представляю возможным. Поделитесь, пожалуйста, опытом, как это можно сделать без флагов?


И сам вопрос по сохранению значений флагов для меня остался актуальным.

Я бы не связывался с перерисовывающим на каждом тике индикатором, каким бы он красивым ни казался, это всё обман. Но если очень надо именно такое, записывайте в файл критичную информацию, это меньше секунды времени занимает. Либо в глобальные переменные терминала, они тоже в файл скидываются по команде советника.
Artyom Kuraev
867
Artyom Kuraev 2016.08.08 15:40  
Я бы тоже не связывался, но какие есть ТЗ, по таким и работаю. Спасибо, Evilive, навели меня еще на одну мысль, как реализовать одну стратегию, над которой сейчас работаю и уйти от записи инфы в файл.
Vladimir
329
Vladimir 2016.08.09 09:52  
delfik71091:

...Поделитесь опытом, кто как решает подобные проблемы?

Насколько проблемы подобны, не знаю. Я ловлю изменение комплекта последних тиков по 25 парам с опросом каждые 16 миллисекунд, сейчас работает такой код выдачи строки изменений S в файл (iErr извлекает последнюю ошибку и сбрасывает соответствующую системную переменную в ноль), работало это и для 60 терминалов без ощутимых последствий для процессоров и дисков, реально ведь идет обмен с памятью (не с магнитными пластинами):

 k=StringLen(S);
  if (k!=0) {
    FileWriteString(fiRes, S, k); k = iErr();
    // FileFlush(fiRes); 25.05.2014 узнал, что FileFlush работает дольше 25 мс. А Close,Open,Seek исполняются 0.1 мс
    FileClose(fiRes); k1=iErr(); // Закрываем файл тиков, данные ушли в буфер контроллера диска
    for (kFi=0;kFi<=10;kFi++) { // пытаемся открыть 10 раз с шагом 0.05 сек
      fiRes = FileOpen(ResFName, FILE_BIN|FILE_READ|FILE_WRITE|FILE_ANSI|FILE_SHARE_READ,0,CP_ACP); // если не было, создаст
      k1=iErr();
      if (fiRes>=0) break; else Sleep (50);
      }
    if (fiRes<1) {ToProt(StringConcatenate ("^99 Не открывается файл ",ResFName," для записи, err=",k1)); return;}
    
    FileSeek(fiRes,0,SEEK_END); k1=iErr(); // В конец файла
    // Новые тики записаны, размер файла изменился, он снова открыт    .......
Artyom Kuraev
867
Artyom Kuraev 2016.08.12 12:23  

Влад, это опять-таки идет запись в файл.

А что касается флагов - ведь по сути, любая переменная класса памяти static или переменная, объявленная на глобальном уровне mql-программы является флагом, так как сохраняет в себя то, что было раньше и передает на новую итерацию.

И при этом без статической переменной невозможно корректно обнаружить новый бар (по крайней мере, я таких функций не встречал), но ведь если во время текущего бара произойдет сбой и программа перезапустится, она вновь обнаружит ""новый" бар. Таким образом получается, что для надежной работы программы нам необходимо все static переменные и переменные, объявленные на глобальном уровне программы куда-то скидывать, либо писать такой код, чтобы абсолютно каждую переменную на каждом тике можно было вычислить "с нуля". Но как этого можно достичь?

Vladimir
329
Vladimir 2016.08.14 18:44  
delfik71091:

Влад, это опять-таки идет запись в файл.

А что касается флагов - ведь по сути, любая переменная класса памяти static или переменная, объявленная на глобальном уровне mql-программы является флагом, так как сохраняет в себя то, что было раньше и передает на новую итерацию.

И при этом без статической переменной невозможно корректно обнаружить новый бар (по крайней мере, я таких функций не встречал), но ведь если во время текущего бара произойдет сбой и программа перезапустится, она вновь обнаружит ""новый" бар. Таким образом получается, что для надежной работы программы нам необходимо все static переменные и переменные, объявленные на глобальном уровне программы куда-то скидывать, либо писать такой код, чтобы абсолютно каждую переменную на каждом тике можно было вычислить "с нуля". Но как этого можно достичь?

Говоря о сбоях, надо ограничить их круг. Пока что я понял, что речь идет о ситуациях, когда прежнее содержимое памяти исчезает. Так естественно и сохранять его на диске. Однако сам по себе советник валиться не должен, это Ваша забота. Случаи внешнего вмешательства возможны, случайно закрыли терминал крестиком в верхнем правом углу и др. Бывают и более серъезные причины. По моему опыту, самая частая - отказ электропитания.

Насколько я догадываюсь, в Ваших ТЗ нет требования обеспечить взрывозащищенную работу советника. Защищать надо не от взрыва газового баллона в соседней комнате, а от отказа электропитания. В первую очередь установкой устройства бесперебойного питания. И, например, запуском скидывания всего нужного на диск по сигналу от этого устройства о том, что емкость аккумулятор скоро будет исчерпана. Или жестче, как Вы пишете, записью на диск на каждый тик. Даже в отсутствие бесперебойника данные, переданные в буфер контроллера диска, успеют при отказе питания записаться на магнитные пластины (для этого используются накопленные механическая энергия вращения и электрическая энергия на обкладках конденсаторов).

Общий подход к ответственному хранению данных хорошо развит в универсальных системах управления базами данных (СУБД). Непрерывно ведется журнал транзакций (на диске). И вообще любая операция считается исполненной после ее отражения на диске, не в памяти, поэтому и быстродействие меряется лишь сотнями-тысячами транзакций в секунду. По этому журналу можно восстановить состояние БД в любой предыдущий момент, начиная с создания БД. Важность этого дела приводит к тому, что СУБД при разворачивании отнимает под себя у диска нужную часть с секторами, идущими подряд, а затем внутри этой области ведет свою файловую систему. Ничего более подходящего предложить не могу. Диск - место энергонезависимого хранения данных, а гораздо более быстрая по сравнению с ним память, к сожалению, забывает данные сразу с отключением питания.

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

Artyom Kuraev
867
Artyom Kuraev 2016.08.15 04:17  
Спасибо, Влад! Чувствуется ответ компетентного человека. За время обдумывания вопроса в эти дни, да и Ваш ответ подтвердил, что это правильное решение, - придумал следующий вариант: из последовательности переменных составляется текстовая строка через определенный разделитель, далее мы отслеживаем,  не изменилась ли эта текстовая строка. Как только замечено отличие хотя бы на 1 знак, сбрасываем строку на жесткий диск вместо старой записи (до полноценной БД, скорее всего, в таких программах нет смысла разрастаться, кроме того, в случае, если с момента сбоя прошло значительное время, даже последние восстановленные данные, скорее всего, уже будут не актуальны, а вот ). В OnInit'e делаем проверку на наличие такой строки на жестком диске. Если есть, то все значения переменных восстанавливаются из нее и советник продолжает работу с последней точки. Тогда мы и данные сохраняем, и не особо нагружаем систему.
Vladimir
329
Vladimir 2016.08.16 01:41  
delfik71091:
Спасибо, Влад! Чувствуется ответ компетентного человека. За время обдумывания вопроса в эти дни, да и Ваш ответ подтвердил, что это правильное решение, - придумал следующий вариант: из последовательности переменных составляется текстовая строка через определенный разделитель, далее мы отслеживаем,  не изменилась ли эта текстовая строка. Как только замечено отличие хотя бы на 1 знак, сбрасываем строку на жесткий диск вместо старой записи (до полноценной БД, скорее всего, в таких программах нет смысла разрастаться, кроме того, в случае, если с момента сбоя прошло значительное время, даже последние восстановленные данные, скорее всего, уже будут не актуальны, а вот ). В OnInit'e делаем проверку на наличие такой строки на жестком диске. Если есть, то все значения переменных восстанавливаются из нее и советник продолжает работу с последней точки. Тогда мы и данные сохраняем, и не особо нагружаем систему.

Если это подходит, хорошо. Особо нагрузить систему очень сложно, чтобы убедиться, просто замеряйте время исполнения операций, исполняя их, например, по 100 тысяч раз. Но вот складывать все в одну строку для проверки, по-моему, лишнее. И сами преобразования значений в строки, и, особенно, сложение подстрок мне кажутся лишними операциями. Лучше завести структуру, в которой содержатся только элементы, которые надо сравнивать. Один экземпляр со значениями, с которыми надо сравнивать (последними записанными на диск), другой с текущими. Одна функция их сравнит (LastV.a != OperV.a || LastV.b != OperV.b || LastV.c != OperV.c || ...) и вернет необходимость записи на диск. Логические выражения в MQL подсчитываются по короткой схеме, и в случае первого же обнаруженного отличия остальные делаться не будут.

Структура того, что лучше писать на диск - посмотрите файл \config\terminal.ini, ведь он предназначен тоже для возобновления работы после перерыва. А также справку к терминалу "Конфигурация при старте". С разделителями трудно разбираться, лучше дать имя каждому из записываемых значений. Можно будет наглядно просматривать редактором текстов. И считывать проще, особенно с учетом последующего изменения состава хранимых на диске значений. И, в целях отладки, лучше было бы опустошать файл, куда пишете, не каждый раз, а, например, после достижения размера в 10 Мб или 1 Гб. В этих же целях добавил бы в записываемые сведения локальное время. Собственно, время дополнит проверку наличия на диске, ведь прошлогодние сведения вряд ли нужны.

12
Авторизуйтесь или зарегистрируйтесь, чтобы добавить комментарий