LifeHack для трейдера: один бэк-тест хорошо, а четыре – лучше
Перед каждым трейдером при первом одиночном тестировании встает один и тот же вопрос — "Какой же из четырех режимов использовать?" Каждый из предлагаемых режимов имеет свои преимущества и особенности, поэтому сделаем проще — запустим сразу все режимы одной кнопкой! В статье показано, как с помощью Win API и небольшой магии увидеть одновременно все четыре графика тестирования.
Оглавление- 2.1. Связь между папкой установки и каталогом данных в папке AppData
- 3.1. Секрет №1
- 3.3. Пример использования FindFirstFileW, FindNextFileW
- 3.4. Заглядываем внутрь папок терминалов
- 3.6. Работаем с файлом "origin.txt"
- 3.7. Последний штрих
- 5.1. Копирование советника в папки Подчинённых терминалов
- 5.3. Запуск терминалов
Основная цель данной статьи — показать, как из одного терминала (назовём его Мастер-терминал) запустить одиночное тестирование (не оптимизацию, а именно тестирование!) советника сразу в четырёх терминалах (назовём их Подчинённые терминалы и присвоим им обозначения #1, #2, #3 и #4). При этом тестеры стратегий в Подчиненных терминалах будут запущены в разных режимах генерации тиков:
- в терминале #1 — "Каждый тик на основе реальных тиков";
- в терминале #2 — "Все тики";
- в терминале #3 — "OHLC на M1";
- в терминале #4 — "Только цены открытия".
- Мастер-терминал должен запускаться без ключа /portable.
- Нужно иметь как минимум пять установленных терминалов MetaTrader 5.
- Торговый счёт в Мастер-терминале — назовём его Мастер-счёт — должен быть хотя бы один раз запущен в каждом из Подчинённых терминалов. Это нужно, потому что эксперт из данной статьи не передаёт пароль от торгового счёта через ini-файлы в Подчинённые терминалы. Вместо пароля передаётся только номер торгового счёта, на котором нужно запустить тестер стратегий, и этот номер всегда совпадает с номером Мастер-счёта. Такое поведение видится логичным, поскольку тестирование советника в разных режимах генерации тиков нужно проводить на одном и том же торговом счёте.
- Перед запуском эксперта максимально разгрузите центральный процессор: выключите онлайн-игры, медиаплеер и другие ресурсоемкие программы. В противном случае, одно из ядер процессора может быть заблокировано, и на этом ядре не запустится тестирование.
Що занадто, то не здраво (польская пословица).
Я всегда предпочитаю использование стандартных возможностей программного обеспечения. Применительно к торговым терминалам MetaTrader 5 это правило звучит так: "никогда не запускать терминал с ключом /portable, никогда не отключать в операционной системе контроль учётных записей пользователей (UAC)". Исходя из этого, работу с файлами описываемый советник будет проводить в папке AppData.
Все приведенные в статье скриншоты демонстрируют работу в Windows 10, поскольку это новейшая и полностью лицензионная система. Именно в применении к ней будет рассматриваться весь код, описанный в этой работе.
Рассматриваемый советник, наряду с возможностями MQL5, широко применяет dll:
Рис. 1. Зависимости
В частности, использованы вызовы таких Windows API функций:
-
— копирует файлы в "песочницу" и из "песочницы" MQL5. — закрывает хэндлы поиска. — ищет каталог файла или подкаталог, название которого соответствует указанному имени файла. — продолжает поиск файла из предыдущего вызова функции FindFirstFile. — вызывает системный диалог открытия файла:
Рис. 2. Открытие файла
2. Входные параметрыРис. 3. Входные параметры
Пути folder of the MetaTrader#Х installation — это пути к папкам установки Подчинённых терминалов. При задании пути в mq5 коде нужно прописывать двойной слэш . Также очень важно ставить двойной обратный слэш в конце пути:
Название терминала "terminal64.exe" приведено для 64-битной операционной системы.
Связь между папкой установки и каталогом данных в папке AppData
При запуске терминала обычным способом или с ключом /portable терминал будет выдавать разные пути для переменной TERMINAL_DATA_PATH. Рассмотрим эту ситуацию на примере Мастер-терминала, который установлен в папку "C:\Program Files\MetaTrader 5 1".
Когда Мастер-терминал запущен с ключом /portable, из этого терминала MQL будет выдавать такие результаты:
А вот ответ этого же терминала без ключа /portable:
Но это нам пригодится, только если мы будем получать параметры из текущего терминала. А как поступать в случае с Подчинёнными терминалами, в которых мы будем запускать тестирование советника? Как связать пути установки Подчинённых терминалов с путями их каталогов данных?
Здесь нужно пояснить, почему так важно знать путь к каталогам данных в папке AppData (цитата из справки):
Начиная с MS Windows Vista, по умолчанию программам, установленным в каталог Program Files, запрещено сохранять данные в каталоге установки. Все данные должны храниться в отдельном каталоге данных пользователя Windows.
Другими словами, наш эксперт может свободно создавать и изменять файлы в папке, вроде этой: C:\Users\имя пользователя\AppData\Roaming\MetaQuotes\Terminal\идентификатор терминала\MQL5\Files. Здесь "идентификатор терминала" — это идентификатор Мастер-терминала.
3. Сопоставляем папку установки и папку AppData Подчинённых терминаловЭксперт выполняет запуск Подчинённых терминалов путём указания конфигурационного файла. При этом для каждого терминала используется свой конфигурационный файл. В каждом конфигурационном файле есть указание на то, чтобы при запуске терминала сразу начинать тестирование заданного советника. Соответствующие команды размещаются в секции [Tester] конфигурационного файла:
Как видите, путь не указывается, а это означает, что тестируемый советник может находиться исключительно в "песочнице" MQL5. На примере Подчинённого терминала 1 это могут быть два пути:
- C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\038C9E8FAFF9EA373522ECC6D5159962\MQL5\Experts
- или C:\Program Files\MetaTrader 5 1\MQL5\Experts
Вариант №2 отбрасываем, так как, согласно политике безопасности, начиная с Windows Vista, писать в папку "Program Files" нам запрещено. Остаётся вариант №1 — а это означает, что для всех Подчинённых терминалов мы должны провести сопоставление папок установки и папок в AppData.
3.1. Секрет №1
В каждом каталоге данных есть файл "origin.txt". На примере Подчинённого терминала 1:
Рис. 4. Файл origin.txt
и содержание файла origin.txt:
Данная запись в файле указывает, что папку "C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\038C9E8FAFF9EA373522ECC6D5159962" создал терминал, установленный в "C:\Program Files\MetaTrader 5 1".
3.2. FindFirstFileW, FindNextFileW
FindFirstFileW — осуществляет поиск директории для файла или подкаталога с именем, которое совпадает с определенным именем (или частью имени, если используются специальные символы).
[in] Директория или путь и имя файла, который может включать в себя символы подстановки, например, звездочка (*) или знак вопроса (?).
[in][out] Указатель на структуру WIN32_FIND_DATA, которая получает информацию о найденном файле или директории.
Возвращаемое значение
Если функция завершается успешно, возвращаемым значением будет хэндл поиска, используемый в последующем вызове FindNextFile or FindClose, а lpFindFileData параметр содержит информацию о первом файле или найденной папке.
Если функция терпит неудачу или не может найти файлы из строки поиска в параметре lpFileName, возвращается значение INVALID_HANDLE_VALUE, и содержимое lpFindFileData будет неопределенным. Чтобы получить дополнительную информацию об ошибке, вызовите функцию GetLastError.
Если функция не срабатывает, потому что не могут быть найдены соответствующие файлы, функция GetLastError возвращает ERROR_FILE_NOT_FOUND.
FindNextFileW — п родолжает поиск файла из предыдущего вызова функции FindFirstFile , FindFirstFileEx , или FindFirstFileTransacted .
[in][out] Указатель на структуру WIN32_FIND_DATA, которая получает информацию о найденном файле или директории.
Возвращаемое значение
Если функция завершается успешно, то возвращаемое значение не равно нулю, а параметр lpFindFileData будет содержать информацию о следующем файле или найденном каталоге.
Если функция завершается ошибкой, возвращаемое значение равно нулю, а содержание lpFindFileData будет неопределённым. Чтобы получить дополнительную информацию об ошибке, вызовите функцию GetLastError .
Если функция терпит неудачу, потому что не может больше найти файлы, функция GetLastError возвращает ERROR_NO_MORE_FILES.
Пример объявления Win API функций FindFirstFileW и FindNextFileW (код взят из включаемого файла ListingFilesDirectory.mqh):
3.3. Пример использования FindFirstFileW, FindNextFileW
Скрипт "ListingFilesDirectory.mq5" — одновременно и пример, и практически полная копия рабочего кода советника. Иными словами, этот код максимально приближен к реальности.
Задача: получить имена всех папок для пути TERMINAL_COMMONDATA_PATH — "Common".
На примере моего компьютера путь TERMINAL_COMMONDATA_PATH возвращает значение "C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\Common". Значит, если отсечь от этого пути "Common", то получим искомый путь "C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\":
Рис. 5. Find First
Обычно для поиска всех файлов применяется маска поиска "*.*". Значит, нам нужно провести две операции со следующими строками: отсечь слово "Common", а затем добавить маску "*.*":
Проверим, какой путь в итоге получился. Для этого установим точку останова и запустим отладку
Пока всё верно: подготовлена маска поиска ВСЕХ файлов и папок в директории "C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\".
Идём дальше: инициализируем поисковый хендл "hFind" (в моем случае это вызвано скорее привычкой) и вызываем Win API функцию FindFirstFileW:
Если вызов FindFirstFileW закончится неудачей, то поисковый хендл "hFind" будет равен "INVALID_HANDLE" и выполнение скрипта закончится.
В случае удачного вызова FindFirstFileW организуем цикл do while, в котором будем получать имя файла или папки , а в конце цикла будет вызвана Win API функция FindNextFileW :
Цикл do while будет продолжаться, пока вызов Win API функции FindNextFileW возвращает ненулевое значение. Если вызов Win API функции FindNextFileW возвращает ноль и ошибка не равна "ERROR_NO_MORE_FILES" — значит, произошла критическая ошибка.
В конце работы скрипта закрываем поисковый хендл .
Результат работы скрипта "ListingFilesDirectory.mq5":
3.4. Заглядываем внутрь папок терминалов
Вышеописанный пример продемонстрировал нам работу на верхнем уровне — в папке "C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\". Но мы с вами помним о разделе 3.1. Секрет №1, согласно которому, нам нужно заглянуть во все вложенные папки.
Для этого организуем двухуровневый поиск, причем для поиска во вложенных папках нужно использовать такую маску первичного поиска для Win API функции FindFirstFileW:
"C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\" + имя найденной папки верхнего уровня + "origin.txt".
Таким образом, первичный поиск FindFirstFileW во вложенной папке будет искать только один файл — "origin.txt".
Привожу полный листинг функции FindDataPath():
Функция FindDataPath() распечатывает примерно такую информацию:
Пояснения к первым строкам распечатки: сначала создаётся фильтр "filter_0" первичного поиска (фильтр равен "C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\*.*") и получаем хэндл первичного поиска "hFirstFind_0", равный 1901014212592. Раз значение "hFirstFind_0" не равно "INVALID_HANDLE" — значит, фильтр "filter_0" первичного поиска, переданный в Win API функцию FindFirstFileW(filter_0,ffd), правильный. После удачного вызова FindFirstFileW(filter_0,ffd) получим имя первой попавшейся папки: это папка "038C9E8FAFF9EA373522ECC6D5159962".
Далее нужно провести поиск файла origin.txt" внутри папки 038C9E8FAFF9EA373522ECC6D5159962. Для этого формируем маску-фильтр. Например, для папки 038C9E8FAFF9EA373522ECC6D5159962, эта маска будет такой: "C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\038C9E8FAFF9EA373522ECC6D5159962\origin.txt". Если хэндл "hFirstFind_1" при этом не будет равен "INVALID_HANDLE" — значит, в указанной папке (038C9E8FAFF9EA373522ECC6D5159962) есть искомый файл (origin.txt).
В распечатке хорошо видно, что первичный поиск во вложенных папках иногда возвращает "INVALID_HANDLE". Это означает, что в указанных папкках нет файла "origin.txt".
Остановимся подробнее на том, что мы будем делать, когда файл "origin.txt" будет найден во вложенной папке.
3.5. CopyFileW
CopyFileW — копирует существующий файл в новый файл.
[in] Имя существующего файла.
Здесь принудительно принято ограничение по длине имени — MAX_PATH символов, этого всегда хватит для нашего примера.
Если файл с именем lpExistingFileName не существует, то функция терпит неудачу, и GetLastError возвращает ERROR_FILE_NOT_FOUND.
[in] Имя нового файла.
Здесь принудительно принято ограничение по длине имени — MAX_PATH символов, этого всегда хватит для нашего примера.
Возвращаемое значение
Если функция завершается успешно, то возвращаемое значение не равно нулю.
Если функция завершается ошибкой, возвращаемое значение равно нулю. Чтобы получить дополнительную информацию об ошибке, вызовите функцию GetLastError.
Пример объявления Win API функции CopyFileW (код взят из включаемого файла ListingFilesDirectory.mqh):
3.6. Работаем с файлом "origin.txt"
Описание работы функции ListingFilesDirectory.mqh::CopiedAndReadFile(string full_file_name).
Во входном параметре функция получает полное имя файла "origin.txt", который был найден в одной из вложенных папок. Это может быть примерно такой путь: "C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\038C9E8FAFF9EA373522ECC6D5159962\origin.txt". Открывать файл "origin.txt" и читать его содержимое будем при помощи MQL5, а это означает, что файл должен быть в "песочнице". Значит, нам нужно скопировать файл "origin.txt" из вложенной папки в песочницу (в данном случае в "песочницу" в общих файлах всех терминалов). Такое копирование выполним при помощи вызова Win API функции CopyFileW.
Запишем в переменную "new_path" путь к файлу "origin.txt" в песочнице:
и вызовем Win API функцию CopyFileW с третьим параметром, равным false — разрешим перезапись файла "origin.txt" в песочнице:
Средствами MQL5 открываем файл "origin.txt" для чтения, при этом не забываем указать флаг FILE_COMMON, ведь файл находится в папке общих файлов:
Читаем только один раз — одну строку, дописываем к ней в конце "\\" и возвращаем полученный результат.
3.7. Последний штрих
Во входных параметрах советника задаются пути к папкам установки для четырёх терминалов:
Эти пути прописываются жёстко, и они должны корректно указывать на папки установки терминалов.
Также ниже, на глобальном уровне, объявлены ещё четыре строковых переменных и один массив:
В эти переменные нужно будет записать пути к папкам терминалов в AppData, а двумерный массив поможет в этом. Теперь можно составить общую схему того, как сопоставить папки установки Подчинённых терминалов с их папками в AppData:
GetStatsFromAccounts_EA.mq5::OnInit() >вызов> GetStatsFromAccounts_EA.mq5::FindDataFolders(arr_path) >вызов> ListingFilesDirectory.mqh::FindDataPath(string &array[][2]) >вызов> CopiedAndReadFile(string full_file_name)
В функции ListingFilesDirectory.mqh::FindDataPath(string &array[][2]), когда во вложенных папках терминалов обнаруживается файл "origin.txt", вызывается функция CopiedAndReadFile(string full_file_name), и после её вызова проводится запись в двумерный массив. В измерение массива "0" пишется путь к папке терминала в AppData, а в измерение массива "1" пишется путь к папке установки (этот путь, напомню, получаем из найденного файла "origin.txt").
>возвращаем управление в> GetStatsFromAccounts_EA.mq5::FindDataFolders(arr_path):
здесь, путём простого обхода по двумерному массиву, заполняются переменные slaveTerminalDataPath1, slaveTerminalDataPath2, slaveTerminalDataPath3 и slaveTerminalDataPath4:
Если мы дошли до этого этапа, значит, советник сопоставил пути установки терминалов и пути их папок в AppData. В случае, если хотя бы один путь к папке терминала в AppData не найден (то есть он будет равен NULL), то все пути распечатываются в последних строках и советник завершит работу ошибкой.
4. Выбор советника для тестированияПеред запуском четырёх Подчинённых терминалов сначала нужно выбрать файл тестируемого эксперта. Этот эксперт должен быть предварительно скомпилирован и расположен в каталоге данных Мастер-терминала.
4.1. GetOpenFileName
GetOpenFileName — создаёт диалоговое окно "Открыть", позволяющее пользователю указать диск, папку и имя файла или набора файлов, которые будут открыты. Объявление и реализация диалогового окна "Открыть" полностью представлено во включаемом файле GetOpenFileNameW.mqh.
4.2. Выбираем советник при помощи системного диалога "Открыть файл"
Системное диалоговое окно "Открыть" вызывается из OnInit() советника:
где происходит вызов GetOpenFileNameW.mqh::OpenFileName(void)
Переменная "path", при удачном вызове Win API функции GetOpenFileName, будет содержать полное имя выбранного файла, наподобие этого: "C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Experts\Examples\MACD\MACD Sample.ex5".
Переменная "filter" отвечает за текст ① рис. 2. Строка "\0*.ex5\0" отвечает за фильтр по типам файлов (② рис. 2). Строка "TerminalInfoString(TERMINAL_DATA_PATH)+"\\MQL5\\Experts\\"" задаёт путь к папке, которая будет открыта в системном диалоге "Открыть".
4.3. Конфигурационный ini-файл
Для запуска терминала на тестирование советника из командной строки (или при помощи Win API), нужно иметь конфигурационный ini-файл в котором обязательно должен присутствовать раздел [Tester] и необходимые указания:
Забегая наперёд, скажу, что этот раздел [Tester] мы будем самостоятельно добавлять в файл.
За основу решено было взять ini-файл Мастер-терминала. Этот файл (common.ini) располагается в каталоге данных терминала, в папке "config ". Для моего терминала путь к нему выглядит так: "C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\config\common.ini".
Схема работы с ini-файлом такая:
- Получить полный путь к "common.ini" Мастер-терминала. Полный путь — это строка вида "C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\config\common.ini". (MQL5)
- Получить новый путь к ini-файлу в песочнице "\Files". Новый путь — это строка вида: "C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Files\myconfiguration.ini" Мастер-терминала. (MQL5)
- Копирование файла "common.ini" в "myconfiguration.ini". (WIn API функция CopyFileW).
- Редактирование файла "myconfiguration.ini". (MQL5).
- Получить новый путь к ini-файлу в песочнице Подчинённого терминала. Это строка вида (на примере моего Подчинённого терминала №1) "C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\038C9E8FAFF9EA373522ECC6D5159962\MQL5\Files\myconfiguration.ini". (MQL5)
- Копировать отредактированный ini-файл "myconfiguration.ini" из песочницы Мастер-терминала в песочницу Подчинённого терминала. (WIn API функция CopyFileW).
- Удаление файла "myconfiguration.ini" из песочницы Мастер-терминала. (MQL5)
Для каждого Подчинённого терминала эту схему нужно повторять. Хотя здесь есть место для оптимизации, но цель описать этот процесс в настоящей статье мною не ставилась.
К редактированию конфигурационных ini-файлов приступаем после того, как уже выбран советник для тестирования, GetStatsFromAccounts_EA.mq5::OnInit():
Схема работы с ini-файлом, на примере Подчинённого терминала №1, GetStatsFromAccounts_EA.mq5::CopyCommonIni():
В вызов функции EditCommonIniFile(common_file_name,3000,4) передаётся:
common_file_name — имя ini-файла, который нужно редактировать;
3000 — номер порта агента тестирования. Каждый терминал должен запускаться на своём агенте тестирования. Нумерация агентов производится начиная с 3000. Увидеть номера портов агентов тестирования можно так: в терминале MetaTrader 5 перейти в тестер стратегий и выполнить правый клик мышки во вкладке "Журнал" тестера стратегий. При этом в выпадающем меню можно будет увидеть нумерацию портов агентов тестирования:
Рис. 7. Агенты тестирования
4 - тип тестирования:
- 0 — "Все тики",
- 1 — "OHLC на M1",
- 2 — "Только цены открытия",
- 3 — "Математические вычисления",
- 4 — "Каждый тик на основе реальных тиков"
Редактирование конфигурационного commom.ini файла выполняется в функции GetStatsFromAccounts_EA.mq5::EditCommonIniFile(string name,const int port,const int model) — операции открытия файла, чтения из файла и записи в файл выполняются средствами MQL5:
4.4. Секрет №2
Перед завершением работы терминал MetaTrader 5 сохраняет расположение окон и панелей, а также их размеры в файл "terminal.ini". Сам файл находится в каталоге данных терминала, в подпапке "config". Например, для моего Подчинённого терминала №1 полный путь к "terminal.ini" будет таким:
"C:\Users\KVN\AppData\Roaming\MetaQuotes\Terminal\038C9E8FAFF9EA373522ECC6D5159962\config\terminal.ini".
В самом файле "terminal.ini" нас будет интересовать только блок "[Window]". Свернём в окно терминал MetaTrader 5. Терминал примет примерно такие размеры:
Рис. 8. Терминал свёрнутый в окно
Если этот терминал закрыть, то в файле terminal.ini блок [Window] будет иметь такой вид:
То есть, блок [Window] хранит координаты терминала и его состояние.
4.5. Задаём размер терминала (ширину, высоту). Вставка строк в середину файла
Изменять координаты в файлах terminal.ini Подчинённых терминалов нужно для того, чтобы все четыре Подчинённых терминала при запуске выстроились так:
Рис. 9. Расположение терминалов
Как было сказано выше, нужно редактировать файл "terminal.imi" для каждого Подчинённого терминала. Здесь нужно обратить внимание на то, что строки придется вставлять не в конец, а в середину файла "terminal.ini". Ниже показаны особенности этой процедуры.
Поясню на таком примере: есть файл "test.txt" расположенный в "песочнице" терминала. Содержимое файла "test.txt":
Нужно изменить информацию во второй и третьей строке , чтобы получилось так:
На первый взгляд, следует поступить так:
- открыть файл для чтения и записи, прочитать первую строку (эта операция переместит файловый указатель на начало второй строки);
- записать во вторую строку новое значение "df=1256";
- записать в третью строку новое значение "asf=5";
- закрыть файл.
Получаем неожиданный результат — в четвёртой строке пропали символы " g= ":
Было Стало
Почему так произошло? Представьте себе, что файл состоит из множества ячеек, которые идут друг за другом. В каждой ячейке помещается один символ. Поэтому когда мы пишем в файл, начиная с его середины, то, по сути, мы просто перезаписываем ячейки. Если добавить больше символов, чем было на этом месте изначально (как в примере выше: было "df=12", а мы записали на два символа больше - "df=1256"), то лишние символы просто повредят дальнейший код. Вот как это выглядит:
Рис. 10. Повреждение информации.
Чтобы избежать повреждения информации при вставке строк в середину файла, поступим следующим образом.
- Копируем из Подчинённого терминала файл "terminal.ini" в песочницу Мастер-терминала в файл с именем "terminal_ext.ini" (Win API CopyFileW).
- Создаём в песочнице Мастер-терминала файл "terminal.ini", открываем его на запись (MQL5).
- Открываем в песочнице Мастер-терминала файл "terminal_ext.ini" на чтение (MQL5).
- В песочнице Мастер терминала: считываем строки из "terminal_ext.ini" и записываем их в файл "terminal.ini" (MQL5).
- Как только считанная строка равна "[Window]" - записываем в файл "terminal.ini" новые координаты (это шесть строк), а в файле "terminal_ext.ini" перемещаем файловый указатель тоже на шесть строк (MQL5).
- В песочнице Мастер терминала: считываем строки из "terminal_ext.ini" и записываем их в файл "terminal.ini", пока не обнаружим конец файла "terminal_ext.ini" (MQL5).
- В песочнице Мастер терминала: закрываем файлы "terminal.ini" и "terminal_ext.ini" (MQL5).
- Копируем из песочницы Мастер-терминала файл "terminal.ini" в Подчинённый терминал, в файл "terminal.ini" (Win API CopyFileW).
- В песочнице Мастер терминала: удаляем файлы "terminal.ini" и "terminal_ext.ini" (MQL5).
Порядок вызовов функций:
GetStatsFromAccounts_EA.mq5::OnInit() >вызов> GetStatsFromAccounts_EA.mq5::CopyTerminalIni()
>вызов> GetStatsFromAccounts_EA.mq5::EditTerminalIniFile
Таким образом, редактируются файлы "terminal.ini" в Подчинённых терминалах, что позволяет запустить их так, как на Рис. 9. При этом можно наблюдать за графиками тестирования и сравнивать точность тестирования в разных режимах.
5. Запуск Подчинённых терминалов на тестированиеК настоящему моменту у нас всё готово для запуска Подчинённых терминалов в режиме тестирования советника:
- мы подготовили конфигурационные файлы "myconfiguration.ini" для всех Подчинённых терминалов;
- мы отредактировали файлы "terminal.ini" всех Подчинённых терминалов;
- мы знаем имя советника, который будет проходить тестирование.
5.1. Копирование советника в папки Подчинённых терминалов
Копирование выбранного ранее советника (его название сохранено в переменной "expert_name") производится в OnInit():
5.2. ShellExecuteW
ShellExecuteW — выполняет операцию на указанном файле.
[in] Хэндл родительского окна, используемого для отображения пользовательского интерфейса и сообщения об ошибках. Это значение должно быть NULL, если операция не связана с окнами.
[in] Строка с именем команды, определяющей действие, которое будет выполняться. Набор доступных команд зависит от конкретного файла или папки. Как правило, это действия, доступные из контекстного меню объекта. Обычно используются следующие команды:
"edit"
Запускает редактор и открывает документ для редактирования. Если lpFile не является файлом документа, то функция не будет выполнена.
Открывает папку, которая указана в lpFile.
Инициирует поиск, начинающийся в каталоге, указанном в lpDirectory.
Открывает элемент, заданный параметром lpFile. Этим элементом может быть файл или папка.
Печатает файл, указанный lpFile. Если lpFile не является файлом документа, функция завершается ошибкой.
Используется имя команды по умолчанию, если такая имеется. Если такой команды нет, то будет использовано команда "open". Если не используется ни одна команда, то система использует первую команду, которая указана в реестре.
[in] Строка, задающая файл или объект, на котором можно исполнить команду. Передаётся полное имя (включающее не только имя файла, но и путь к нему). Обратите внимание, что объект может поддерживать не все команды. Например, не все документы поддерживают команду "print". Если относительный путь используется для lpDirectory параметра, то не используйте относительный путь для lpFile.
[in] Если lpFile указывает на исполняемый файл, этот параметр - строка, которая определяет параметры, которые будут переданы приложению. Формат этой строки определяется именем команды, которая должна быть выполнена. Если lpFile указывает на файл документа, lpParameters должен быть NULL.
[in] Строка, которая определяет рабочий каталог. Если это значение NULL, используется текущий рабочий каталог. Если относительный путь задан в lpFile, тогда не используйте относительный путь для lpDirectory.
[in] Флаги, которые определяют, как приложение должно отображаться при его открытии. Если lpFile определяет файл документа, флаг просто передается соответствующему приложению. Используемые флаги:
Возвращаемое значение
Если функция завершается успешно, она возвращает значение большее, чем 32.
Пример объявления Win API функции ShellExecuteW:
5.3. Запуск терминалов
Запускаются подчинённые терминалы из OnInit():
при этом между запусками советник ожидает "sleeping" миллисекунд. По умолчанию параметр "sleeping" равен 9000 (то есть, 9 секунд). Если происходят ошибки авторизации агентов в Подчинённых терминалах, увеличьте этот параметр.
Параметры, передаваемые в Win API функцию (на примере моего Подчинённого терминала №1) выглядят так:
6. Возможные ошибкиМожет возникнуть ситуация, когда один из Подчинённых терминалов запускается, но тестер не может подключится к агенту тестирования.
В тестере, во вкладке "Журнал" будет примерно такая запись:
В логах агента записи такие:
В таких случаях рекомендуется увеличить паузу между запусками терминалов (переменная "sleeping"), а также выгрузить все ресурсоёмкие приложения, которые могут захватить в своё пользование ядра процессора.
ЗаключениеЗадача по запуску тестирования выбранного советника сразу в четырёх режимах тестирования выполнена. После запуска советника можно практически одновременно наблюдать, как проходит тестирование сразу в четырёх терминалах.