English
preview
Версионирование параметров советника в MQL5 с помощью файлов

Версионирование параметров советника в MQL5 с помощью файлов

MetaTrader 5 — Интеграция |
46 0
ALGOYIN LTD
Israel Pelumi Abioye

Содержание

  1. Введение
  2. Проектирование структуры версионирования параметров
  3. Создание версии 1 при пустом файле
  4. Проверка контрольной суммы и добавление новой версии в конец
  5. Заключение



Введение

Когда вы изменяете входные параметры советника на языке MQL5 — вручную в терминале, при оптимизации стратегии или во время работы советника в OnInit или OnTick, — запись настроек в один и тот же файл, как это часто делают, нередко приводит к перезаписи предыдущих конфигураций и удалению истории. Из-за этого становится сложно восстановить заведомо рабочую конфигурацию, сравнить разные наборы настроек или воспроизвести предыдущие результаты.

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

  • сохранять полный снимок параметров;
  • считывать последний снимок;
  • выявлять существенные изменения и проверять целостность;
  • добавлять новую версию в конец файла, не перезаписывая предыдущие.

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



Проектирование структуры версионирования параметров

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

Еще одно важное преимущество — единообразие. При записи структуры в бинарный файл расположение данных остается неизменным. Это означает, что все записи в файле будут иметь одинаковый размер и одинаковое расположение значений внутри записи. Благодаря такой единообразной структуре программа может легко просматривать файл и находить нужные записи. Это особенно полезно, когда в одном файле хранится несколько версий записей параметров.

Для поддержки версионирования параметров разделите поля структуры на три логические группы. Первая группа отвечает за версионирование. Эти поля используются для определения даты создания и конкретной версии записи параметров. Такая группа обычно включает временную метку, указывающую точный момент записи версии, и номер версии, который увеличивается при каждом новом сохранении. С помощью этой информации советник может отслеживать изменения параметров с течением времени.

Вторая группа — настраиваемые параметры. В ней содержатся собственно торговые параметры, определяющие поведение советника. К ним относятся, например, стоп-лосс, тейк-профит, размер лота и любые другие переменные, которые могут изменяться во время торговли или тестирования. Их объединение в одну группу позволяет легко сохранять и загружать все настраиваемые параметры сразу. Эти значения трейдер или система изменяет чаще всего. Третья группа — контрольная сумма. Благодаря контрольной сумме советник может проверить корректность сохраненных настроек. Когда запись загружается из файла, программа пересчитывает контрольную сумму по настраиваемым параметрам и сравнивает ее с сохраненным значением. Если значения совпадают, это означает, что параметры не изменились. Если они различаются, это может свидетельствовать о повреждении данных или ошибке записи.

Пример:
struct SessionParams
  {
   //--- Versioning & Metadata
   int               version_count;
   datetime          saved_at;

   //--- Adjustable Parameters
   int               take_profit;
   int               stop_loss;
   double            lot_multiplier;
   int               rsi_threshold;
   int               max_trades;

   //--- Performance Metrics
   double            profit_factor;
   double            win_rate;
   double            max_drawdown;
   double            avg_rrr;

   //--- Integrity
   uint              checksum;
  };
SessionParams    params;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   return(INIT_SUCCEEDED);
  }

Пояснение

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

 

Создание версии 1 при пустом файле

Теперь, когда структура успешно создана, нужно открыть бинарный файл, инициализировать параметры и сохранить первую версию. Считайте это отправной точкой для вашего советника. Без этой отправной точки в файле не будет данных, на которые можно опираться в дальнейшем. Задумайтесь над следующим вопросом. Какие значения должны иметь ваши параметры при первом сохранении? Может быть, стандартный стоп-лосс, тейк-профит по умолчанию или размер лота 0,1? Перед записью структуры заполните ее значениями, подходящими для вашей стратегии. Вы даже можете указать нулевые значения для показателей эффективности, поскольку советник еще не совершал сделок.

Открытие файла

Первый шаг — открыть бинарный файл. Необходимо выбрать имя файла и открыть его в режиме записи. Режим записи необходим, поскольку он позволяет сохранить первую запись и создать файл, если его еще нет.

Пример:

SessionParams  params;
string filename = "parameters.bin";
int handle;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Open or create the binary file
   handle = FileOpen(filename, FILE_READ|FILE_WRITE|FILE_BIN|FILE_COMMON);
   
//--- Check if file access failed
   if(handle == INVALID_HANDLE)
     {
      Print("ERROR: Cannot access file. Error: ", GetLastError());
      return;
     }
   else
      if(handle != INVALID_HANDLE)
        {
         FileClose(handle);
        }
  }

Пояснение

Первый шаг при сохранении параметров советника — открыть файл с понятным и информативным именем, например parameters.bin, и сохранить его хэндл для последующих операций чтения и записи. Для этого используется функция, которой необходимо передать имя файла и флаги доступа. В данном случае FILE_READ позволяет считывать существующие данные, FILE_WRITE — сохранять новые данные, FILE_BIN обеспечивает правильное хранение структуры в бинарном формате, а FILE_COMMON делает файл доступным для всех терминалов MetaTrader 5 на одном компьютере. После открытия файла важно проверить корректность хэндла, чтобы убедиться, что доступ получен. Если хэндл некорректен, следует изучить сообщение об ошибке, чтобы выяснить причину проблемы. После успешной проверки файл можно безопасно использовать и закрыть, убедившись, что он готов для сохранения записей параметров.

Инициализация параметров

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

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

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

Наконец, вычислите контрольную сумму по настраиваемым параметрам и присвойте ее соответствующему полю. При последующем считывании записи из файла контрольная сумма гарантирует ее корректность и отсутствие повреждений. Это повышает надежность механизма версионирования параметров. Перед инициализацией проверьте, пуст ли файл. Этот шаг предотвращает перезапись существующих данных и помогает избежать дублирования начальной записи. Если файл пуст, советник определяет, что необходимо инициализировать настройки по умолчанию и сохранить их в качестве начальной версии.

Пример:

int file_size;
handle = FileOpen(filename, FILE_READ|FILE_WRITE|FILE_BIN|FILE_COMMON);
if(handle == INVALID_HANDLE)
  {
   Print("ERROR: Cannot access file. Error: ", GetLastError());
   return;
  }
else
   if(handle != INVALID_HANDLE)
     {
//--- Get current file size
      file_size = (int)FileSize(handle);

//--- If file is empty, create first version
      if(file_size == 0)
        {
         Print("INFO: New file detected. Writing default parameters...");
        }
      FileClose(handle);
     }

Пояснение

После открытия файла следует проверить его размер. Для этого используется функция, возвращающая общее количество байтов, сохраненных в файле на данный момент. В данном примере это значение присваивается переменной file_size. По размеру файла советник может определить, пуст ли файл и готов ли он к записи начальной версии или уже содержит информацию о параметрах. Если размер файла равен 0, это означает, что в нем нет данных. Советник должен инициализировать и сохранить набор значений по умолчанию, поскольку записей параметров пока нет. Чтобы пояснить происходящее, программа выводит сообщение о создании первой записи параметров и обнаружении нового файла.

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

Пример:

//--- Input parameters (user-adjustable settings)
input int TP = 150;
input int SL = 80;
input double L_multiplier = 1.0;
input int RSI_threshold = 55;
input int Max_trades = 3;

else
   if(handle != INVALID_HANDLE)
     {
      file_size = (int)FileSize(handle);
      if(file_size == 0)
        {
         Print("INFO: New file detected. Writing default parameters...");
         params.version_count     = 1;
         params.saved_at       = TimeCurrent();
         params.take_profit    = TP;
         params.stop_loss      = SL;
         params.lot_multiplier = L_multiplier;
         params.rsi_threshold  = RSI_threshold;
         params.max_trades     = Max_trades;
         params.profit_factor  = 0.0;
         params.win_rate       = 0.0;
         params.max_drawdown   = 0.0;
         params.avg_rrr        = 0.0;
         params.checksum       = 0;
        }
      FileClose(handle);
     }

Пояснение

Убедившись, что файл пуст, определите настраиваемые параметры с помощью ключевого слова input. Это позволяет трейдерам изменять такие параметры, как тейк-профит, стоп-лосс, множитель лота, пороговое значение RSI и максимальное количество сделок, прямо в настройках советника, не внося изменений в код. Затем формируется полная запись параметров: значения входных параметров присваиваются соответствующим полям экземпляра структуры. Поля версионирования инициализируются следующим образом: номеру версии присваивается значение 1, а временной метке — текущее время. Показатели эффективности также включаются в запись, но их начальные значения равны нулю, поскольку торговых данных пока нет. На этом этапе структура содержит полный снимок конфигурации советника, готовый к сохранению в бинарном файле.

Назначение и вычисление контрольной суммы

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

Представьте себе параметры, хранящиеся в нашей структуре. Мы включаем в нее настраиваемые параметры, такие как тейк-профит, стоп-лосс, множитель лота, пороговое значение RSI и максимальное количество сделок, а также метаданные и показатели эффективности. Один из способов обнаружить изменения — сравнить каждое поле с соответствующим полем предыдущей версии, сохраненной в файле. Хотя этот подход работает, с увеличением количества полей он становится неэффективным, а его реализацию сложнее поддерживать.

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

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

Пример:
else
   if(handle != INVALID_HANDLE)
     {
      file_size = (int)FileSize(handle);
      if(file_size == 0)
        {
         Print("INFO: New file detected. Writing default parameters...");
         params.version_count     = 1;
         params.saved_at       = TimeCurrent();
         params.take_profit    = TP;
         params.stop_loss      = SL;
         params.lot_multiplier = L_multiplier;
         params.rsi_threshold  = RSI_threshold;
         params.max_trades     = Max_trades;
         params.profit_factor  = 0.0;
         params.win_rate       = 0.0;
         params.max_drawdown   = 0.0;
         params.avg_rrr        = 0.0;
         params.checksum       = 0;

         // Calculate checksum
         uint result_f = 0;
         result_f ^= (uint)params.take_profit;
         result_f ^= (uint)params.stop_loss;
         result_f ^= (uint)(params.lot_multiplier * 1000);
         result_f ^= (uint)params.rsi_threshold;
         result_f ^= (uint)params.max_trades;
         params.checksum = result_f;
        }
      FileClose(handle);
     }

Пояснение

Для хранения результата вычисления контрольной суммы сначала объявляется переменная. Поскольку переменная имеет беззнаковый целочисленный тип, в ней можно хранить только целые неотрицательные значения. Перед обработкой значений параметров переменная обнуляется, чтобы начать вычисление с нулевого значения. Затем с помощью оператора XOR (^=) значения настраиваемых параметров структуры объединяются с текущим значением переменной. Поскольку любое изменение входных значений приводит к другому результату, этот оператор часто используется для вычисления контрольных сумм. Итоговая контрольная сумма формируется последовательным объединением значения каждого настраиваемого параметра с текущим результатом.

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

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

Сохранение записи параметров в файл

После корректной инициализации структуры параметров и вычисления контрольной суммы запись сохраняется в бинарный файл. К этому моменту структура содержит все необходимые компоненты, включая показатели эффективности, сведения о версии, настраиваемые параметры и контрольную сумму, которая позволяет проверить корректность сохраненных данных.

Пример:
if(file_size == 0)
  {
   Print("INFO: New file detected. Writing default parameters...");
   params.version_count     = 1;
   params.saved_at       = TimeCurrent();
   params.take_profit    = TP;
   params.stop_loss      = SL;
   params.lot_multiplier = L_multiplier;
   params.rsi_threshold  = RSI_threshold;
   params.max_trades     = Max_trades;
   params.profit_factor  = 0.0;
   params.win_rate       = 0.0;
   params.max_drawdown   = 0.0;
   params.avg_rrr        = 0.0;
   params.checksum       = 0;

// Calculate checksum
   uint result_f = 0;
   result_f ^= (uint)params.take_profit;
   result_f ^= (uint)params.stop_loss;
   result_f ^= (uint)(params.lot_multiplier * 1000);
   result_f ^= (uint)params.rsi_threshold;
   result_f ^= (uint)params.max_trades;
   params.checksum = result_f;

// Write version 1 to file
   FileWriteStruct(handle, params);
   Print("========================================");
   Print("INFO: CURRENT PARAMETERS (Loaded)");
   Print("========================================");
   Print("Version       : ", params.version_count);
   Print("Saved At      : ", TimeToString(params.saved_at, TIME_DATE|TIME_SECONDS));
   Print("Take Profit   : ", params.take_profit);
   Print("Stop Loss     : ", params.stop_loss);
   Print("Lot Multiplier: ", params.lot_multiplier);
   Print("RSI Threshold : ", params.rsi_threshold);
   Print("Max Trades    : ", params.max_trades);
   Print("Profit Factor : ", params.profit_factor);
   Print("Win Rate      : ", params.win_rate, "%");
   Print("Max Drawdown  : ", params.max_drawdown);
   Print("Avg RRR       : ", params.avg_rrr);
   Print("Checksum      : ", params.checksum);
   Print("SUCCESS: Created parameter file with version 1.");
  }

Пояснение

После полной подготовки структуру необходимо сохранить в бинарном файле. Для этого используется функция FileWriteStruct. Эта функция принимает хэндл файла и экземпляр структуры в качестве аргументов, после чего записывает структуру целиком непосредственно в файл. Функция сохраняет набор параметров в виде одной записи фиксированного размера, поскольку структура уже объединяет все необходимые данные. Этот метод обеспечивает единообразие всех записей в файле, что упрощает их извлечение и анализ в дальнейшем. Вся запись сохраняется за одну операцию, а не по одному параметру за раз.

После записи структуры в файл сохраненные значения выводятся в терминал с помощью нескольких вызовов Print. Эти сообщения подтверждают, что параметры были правильно инициализированы и сохранены. Такой вывод очень полезен при отладке, поскольку позволяет разработчику точно увидеть, какие значения содержались в структуре на момент ее записи в файл.

Вывод:

Figure 1. Parameters Version 1

Проверка контрольной суммы и добавление новой версии в конец

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

Эта система основана на простой концепции. Советник создает запись каждый раз, когда обнаруживает изменение настраиваемых параметров. Запись содержит временную метку, указывающую момент изменения, обновленные значения параметров и новый номер версии. После этого запись добавляется в конец бинарного файла, что гарантирует сохранение всех предыдущих версий. Сначала проверьте, пуст ли файл. Если файл пуст, советник должен создать первую версию, поскольку записей пока нет. Если в файле уже есть данные, это означает, что более ранние версии были сохранены. Советник использует эту проверку, чтобы определить, следует ли создать новую версию или инициализировать первую запись.

Убедившись, что файл содержит данные, определим количество сохраненных версий. Советник может определить номер версии для нового набора параметров, зная общее количество записей в файле. Это позволяет сохранить четкую последовательность изменений, гарантируя, что каждая запись имеет уникальный идентификатор версии. После определения общего количества версий советник извлекает из файла последнюю сохраненную версию. Последняя версия содержит актуальные настройки и их контрольную сумму. Чтобы выяснить, произошли ли какие-либо изменения, советник может считать эту запись, а затем сравнить содержащиеся в ней параметры с вновь введенными. Если параметры изменились, новая версия будет создана и добавлена в конец файла; в противном случае новая запись не требуется. Эта процедура гарантирует, что в истории версий корректно отражаются только существенные изменения конфигурации советника.

Пример:

int struct_size;
int total_versions;
int last_version_offset;
else
   if(handle != INVALID_HANDLE)
     {
      file_size = (int)FileSize(handle);
      if(file_size == 0)
        {
         Print("INFO: New file detected. Writing default parameters...");
         params.version_count     = 1;
         params.saved_at       = TimeCurrent();
         params.take_profit    = TP;
         params.stop_loss      = SL;
         params.lot_multiplier = L_multiplier;
         params.rsi_threshold  = RSI_threshold;
         params.max_trades     = Max_trades;
         params.profit_factor  = 0.0;
         params.win_rate       = 0.0;
         params.max_drawdown   = 0.0;
         params.avg_rrr        = 0.0;
         params.checksum       = 0;

         // Calculate checksum
         uint result_f = 0;
         result_f ^= (uint)params.take_profit;
         result_f ^= (uint)params.stop_loss;
         result_f ^= (uint)(params.lot_multiplier * 1000);
         result_f ^= (uint)params.rsi_threshold;
         result_f ^= (uint)params.max_trades;
         params.checksum = result_f;

         // Write version 1 to file
         FileWriteStruct(handle, params);
         Print("========================================");
         Print("INFO: CURRENT PARAMETERS (Loaded)");
         Print("========================================");
         Print("Version       : ", params.version_count);
         Print("Saved At      : ", TimeToString(params.saved_at, TIME_DATE|TIME_SECONDS));
         Print("Take Profit   : ", params.take_profit);
         Print("Stop Loss     : ", params.stop_loss);
         Print("Lot Multiplier: ", params.lot_multiplier);
         Print("RSI Threshold : ", params.rsi_threshold);
         Print("Max Trades    : ", params.max_trades);
         Print("Profit Factor : ", params.profit_factor);
         Print("Win Rate      : ", params.win_rate, "%");
         Print("Max Drawdown  : ", params.max_drawdown);
         Print("Avg RRR       : ", params.avg_rrr);
         Print("Checksum      : ", params.checksum);
         Print("SUCCESS: Created parameter file with version 1.");
        }
      else
        {
         struct_size = sizeof(SessionParams);
         total_versions = file_size / struct_size;
         Print("File size: ",file_size);
         Print("Struct size: ",struct_size);
         Print("Total versions: ",total_versions);

         last_version_offset = (total_versions - 1) * struct_size;
         FileSeek(handle, last_version_offset, SEEK_SET);
         SessionParams    pre_v_params;
         FileReadStruct(handle, pre_v_params);
        }
      FileClose(handle);
     }

Пояснение

Если файл не пуст, советник переходит в блок else и использует ранее сохраненные версии параметров. Чтобы определить количество сохраненных версий, советник вычисляет размер одной записи с помощью sizeof(SessionParams) и делит общий размер файла на полученное значение. На основе этой информации советник вычисляет смещение последней записи, перемещает указатель файла в эту позицию и считывает последний сохраненный набор параметров в структуру. Затем советник извлекает контрольную сумму предыдущей версии и вычисляет новую по текущим значениям настраиваемых входных параметров.

Пример:
else
  {
   struct_size = sizeof(SessionParams);
   total_versions = file_size / struct_size;
   Print("File size: ",file_size);
   Print("Struct size: ",struct_size);
   Print("Total versions: ",total_versions);

   last_version_offset = (total_versions - 1) * struct_size;
   FileSeek(handle, last_version_offset, SEEK_SET);
   SessionParams    pre_v_params;
   FileReadStruct(handle, pre_v_params);

// Calculate last checksum
   uint result = 0;
   result ^= (uint)pre_v_params.take_profit;
   result ^= (uint)pre_v_params.stop_loss;
   result ^= (uint)(pre_v_params.lot_multiplier * 1000);
   result ^= (uint)pre_v_params.rsi_threshold;
   result ^= (uint)pre_v_params.max_trades;
   uint Prev_checksum = result;
   Print("\nPREV CHECKSUM: ",Prev_checksum);

//Calculate new checksum
   pre_v_params.take_profit    = TP;
   pre_v_params.stop_loss      = SL;
   pre_v_params.lot_multiplier = L_multiplier;
   pre_v_params.rsi_threshold  = RSI_threshold;
   pre_v_params.max_trades     = Max_trades;

   uint result_new = 0;
   result_new ^= (uint)pre_v_params.take_profit;
   result_new ^= (uint)pre_v_params.stop_loss;
   result_new ^= (uint)(pre_v_params.lot_multiplier * 1000);
   result_new ^= (uint)pre_v_params.rsi_threshold;
   result_new ^= (uint)pre_v_params.max_trades;
   pre_v_params.checksum = result_new;
   Print("NEW CHECKSUM: ",pre_v_params.checksum);
  }

Вывод:

Figure 2. Checksum

Пояснение

В начале кода вычисляется контрольная сумма последней сохраненной версии набора параметров. Чтобы получить сводное числовое представление всех полей структуры, переменная инициализируется нулем. Затем с помощью операции XOR в расчет включаются значения настраиваемых параметров из предыдущей записи, таких как take_profit, stop_loss, L_multiplier, RSI_threshold и max_trades.

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

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

Пример:

Print("NEW CHECKSUM: ",pre_v_params.checksum);

if(pre_v_params.checksum != Prev_checksum)
  {
   FileSeek(handle, 0, SEEK_END);
   pre_v_params.version_count += 1;
   pre_v_params.saved_at = TimeCurrent();

   FileWriteStruct(handle, pre_v_params);
   Print("========================================");
   Print("INFO: NEW SAVED PARAMETERS (Loaded)");
   Print("========================================");
   Print("Version       : ", pre_v_params.version_count);
   Print("Saved At      : ", TimeToString(pre_v_params.saved_at, TIME_DATE|TIME_SECONDS));
   Print("Take Profit   : ", pre_v_params.take_profit);
   Print("Stop Loss     : ", pre_v_params.stop_loss);
   Print("Lot Multiplier: ", pre_v_params.lot_multiplier);
   Print("RSI Threshold : ", pre_v_params.rsi_threshold);
   Print("Max Trades    : ", pre_v_params.max_trades);
   Print("Profit Factor : ", pre_v_params.profit_factor);
   Print("Win Rate      : ", pre_v_params.win_rate, "%");
   Print("Max Drawdown  : ", pre_v_params.max_drawdown);
   Print("Avg RRR       : ", pre_v_params.avg_rrr);
   Print("Checksum      : ", pre_v_params.checksum);
   Print("SUCCESS: Created parameter file with version ", pre_v_params.version_count);

  }

Пояснение

Суть алгоритма — выявить различия, сравнив только что вычисленную контрольную сумму с контрольной суммой последней сохраненной версии. Если советник обнаруживает изменение, он сохраняет всю структуру параметров в виде записи, увеличивает номер версии, перемещает указатель в конец файла и обновляет временную метку. Затем советник выводит подробную информацию, подтверждающую успешное сохранение. Если значения совпадают, новая версия не создается.

 

Заключение

Теперь у вас есть понятный и эффективный способ сохранять историю конфигураций с возможностью поиска и предотвращать непреднамеренную перезапись настроек советника. Результат — бинарный файл (parameters.bin), в который записи добавляются только в конец. Каждая запись представляет собой структуру фиксированного размера, содержащую:

  • номер версии и время сохранения (метаданные);
  • все входные, то есть настраиваемые, торговые параметры (значения, которые вы настраиваете);
  • необязательные показатели эффективности;
  • контрольную сумму для проверки целостности.

Правила работы и способы интеграции:

  • Создавайте версию 1 только в том случае, если файл пуст (инициализация).
  • При запуске или в регулярных контрольных точках (OnInit/OnTick) загрузите последнюю запись, вычислив смещение: offset = (total_versions - 1) * sizeof(struct).
  • Пересчитайте контрольную сумму по настраиваемым параметрам (при необходимости масштабируйте числа с плавающей запятой). Если контрольная сумма отличается от последней сохраненной, увеличьте номер версии, обновите время сохранения и с помощью FileWriteStruct сохраните запись в конце файла; в противном случае ничего не делайте.
  • Используйте контрольную сумму как показатель значимых изменений параметров.
Этот подход обеспечивает компактную историю версий, доступную для проверки, детерминированный алгоритм загрузки и добавления записей в конец файла, а также готовый к использованию модуль, который можно встроить в любой советник для надежного отслеживания, сравнения и восстановления наборов параметров.

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/21775

Прикрепленные файлы |
От новичка до эксперта: Создание индикатора-оверлея CRT для нескольких таймфреймов в MQL5 От новичка до эксперта: Создание индикатора-оверлея CRT для нескольких таймфреймов в MQL5
Диапазоны CRT на старших таймфреймах информативны, однако трейдеры часто совершают сделки на младших таймфреймах без учета этого контекста. Мы реализуем индикатор на языке MQL5, который считывает данные OHLC старшего таймфрейма, накладывает полный диапазон свечи, ее тело и тени на активный график младшего таймфрейма и отмечает уровни входа, стоп-лосса и цели. Это позволяет лучше ориентироваться в ситуации и избавляет от необходимости переключаться между окнами.
Создание системы торговой аналитики (Часть 2): Как фиксировать закрытые сделки и передавать данные в формате JSON с помощью MQL5 Создание системы торговой аналитики (Часть 2): Как фиксировать закрытые сделки и передавать данные в формате JSON с помощью MQL5
Мы создаем легковесный мост, который фиксирует закрытые сделки в MetaTrader 5 и отправляет данные о них во внешний бэкенд по HTTP в формате JSON. Он использует OnTradeTransaction для обнаружения событий, считывает сведения из истории сделок, формирует данные в формате JSON и отправляет их POST-запросом через WebRequest. Для проверки обмена данными используется локальный API Flask. Так мы получаем работающий механизм передачи торговых данных за пределы терминала.
Создание объектно-ориентированного механизма статистического арбитража на основе Z-показателя в MQL5 Создание объектно-ориентированного механизма статистического арбитража на основе Z-показателя в MQL5
В статье показано, как реализовать готовый к практическому применению модуль расчёта Z-показателя на языке MQL5 с использованием объектно-ориентированного подключаемого файла. Библиотека вычисляет скользящее среднее и стандартное отклонение генеральной совокупности, предоставляет параметр сдвига для запросов по историческим данным и позволяет избежать избыточных вычислений на тиках, выполняя расчёты при закрытии бара. Советник открывает позиции в соответствии с установленными правилами при достижении положительных/отрицательных пороговых значений сигмы и закрывает их при возврате к среднему значению; пользовательский индикатор обеспечивает визуальную проверку.
От новичка до эксперта: Автоматизация распознавания геометрических паттернов внутри базовых свечей зон ликвидности в MQL5 От новичка до эксперта: Автоматизация распознавания геометрических паттернов внутри базовых свечей зон ликвидности в MQL5
В статье описывается реализация модуля на MQL5, который анализирует бары младшего таймфрейма внутри каждой базовой свечи зоны ликвидности. Он обнаруживает свинговые точки и по объективным правилам классифицирует внутреннюю структуру: восходящий, нисходящий или симметричный треугольник, прямоугольник, M, W либо неопределенная структура. Индикатор отображает на графике метки геометрических фигур и добавляет название паттерна в алерты, сокращая необходимость ручной проверки на младшем таймфрейме.