[Обсуждение] Тестирование/оптимизация советников: общие вопросы
Небольшая разработка для тестирования мультивалютных советников.
Сразу оговорюсь, это не панацея, требуется понимать код советника чтоб подружить его с этим механизмом тестирования.
Как это работает:
Для тестирования эксперта на нескольких валютах потребуется подготовить несколько терминалов, то есть например для тестирования корзины USD советника BacketFX Mod потребуется 7 отдельных копий МТ4. Так же потребуется модифицировать код советника что бы обеспечить передачу общих данных между всеми копиями терминала, таких как глобальные переменные, количество ордеров, профит по всем ордерам, баланс, экваити и т.д. - в общем все что необходимо для полноценно работы советника. Дальше технические детали как это реализовать.
Содержимое архива:
SmartDev.MultiCurrencyTester.Connect.dll - дллка обеспечивающая взаимодействие
SmartDev.MultiCurrencyTester.Connect.mqh - заголовочный файл к этой длл
SmartDev.MultiCurrencyTester.ExpertExample.mq4 - пример мультивалютного експерта
EquityInTesting.mq4 - пример индикатора отображающего баланс и экваити сумарно по всем валютам тестируемого експерта
Как установить:
Для работы потребуется MS .NET 4.0 http://www.microsoft.com/en-us/download/details.aspx?id=17851
Распаковываем архив в корень папки МТ4.
Описание функций из заголовочного файла SmartDev.MultiCurrencyTester.Connect.mqh
void InitializeTestAPI(int instanceId, int instancesCount, int syncSeconds, string logFilePath);
Функция инициализации API. Должна вызываться один раз из функции init() эксперта, параметры:
instanceId - номер экземпляра тестируемого советника.
должен быть уникальным для каждой тестируемой валютной пары.
должен быть в диапазоне от 0 до instancesCount-1
например если вы тестируете советник на трех валютных парах то в тестере первой пары указываете instanceId = 0, второй = 1 и третьей соответственно 2
instancesCount - количество экземпляров советника в тесте, в примере выше соответственно instancesCount = 3
syncSeconds - количество секунд по истории, на которое разрешается разным экземплярам советника обгонять друг друга при тестировании. 0 - соответственно тестирование тик в тик без опережения. не-нулевые значения существенно ускоряют тест при уменьшении достоверности
logFilePath - полный путь к файлу куда писать информацию о балансе и экваити каждого отдельно взятого экземпляра тестируемого советника. должен быть разным для каждого экземпляра. если вывод в файл не нужен - передаем пустую строку ""
void DeinitializeTestAPI();
Функция деинициализации, должна быть вызвана из функции deinit() эксперта
void NextTick(int tick, double balance, double equity);
Функция должна вызываться на каждый тик тестируемого советника, параметры:
tick - время текущего тика (для синхронизации тестируемых экземпляров), передавать TimeCurrent()
balance и equity - исключительно для вывода в файл истории изменения баланса и экваити. если logFilePath в InitializeTestAPI не задан - не используются
void DeclareVariable(string variableName, int variableOperation);
Функция декларации переменной, доступной во всех экземплярах эксперта. По умолчанию переменная не нуждается в декларации. При первом использовании переменная декларируется автоматически. Ручная декларация требуеться если необходимо задать математическую операцию над переменной. Примеры будут ниже при разборе тестового эксперта. Параметры:
variableName - имя переменной
variableOperation - математическая операция (пока доступно две опции VariableOperation_Nothing = 0 и VariableOperation_Sum = 1)
double GetVariable(string variableName);
Функция получения значения общедоступной переменной, параметры:
variableName - имя переменной
void SetVariable(string variableName, double variableValue);
Функция установки значения общедоступной переменной. Параметры:
variableName - имя переменной
variableValue - значение переменной
Типы операция над переменными:
int VariableOperation_Nothing = 0;
int VariableOperation_Sum = 1;
Удобные обертки подменяющие в тестинге операции с глобальными переменными на операции с API
Просто
double GlobalVariableGetExt(string name)
double GlobalVariableSetExt(string name, double value)
Как пользоваться:
Разберем пример эксперта из архива:
InitializeTestAPI(0, 1, 60, "");
Инициализируем тестирование советника из одной валюты, погрешность синхронизации 60 секунд (одна минутная свеча), лог файл не пишем.
Другой пример:
InitializeTestAPI(2, 5, 0, "d:\testlogs\balance_2.log");
Инициализируем третий из пяти экземпляров советника, без погрешности синхронизации (тик в тик), выводим баланс и экваити в лог файл. Соответсвенно функции инициализации для всех пяти валют этого советника будут выглядеть так:
InitializeTestAPI(0, 5, 0, "d:\testlogs\balance_0.log");
InitializeTestAPI(1, 5, 0, "d:\testlogs\balance_1.log");
InitializeTestAPI(2, 5, 0, "d:\testlogs\balance_2.log");
InitializeTestAPI(3, 5, 0, "d:\testlogs\balance_3.log");
InitializeTestAPI(4, 5, 0, "d:\testlogs\balance_4.log");
Кому не привычна нумерация с нуля - простите, так заведено в практически всех языках программирования в том числе и в MQL
Функция InitializeTestAPI поставит терминал в ожидание инициализации остальных экземпляров советника. Тест начнется только после того как все экземпляры будут проиницализированны. Выглядеть это будет как подвисший МТ4. После того как стартанет последний экземпляр - все остальные тоже начнут тест.
Лог файл баланса пишется в csv-формате: tickTime;balance;equaity;
В будущем придумаем покошернее аналитику :)
Идем дальше:
DeclareVariable("TotalProfitVariableName", VariableOperation_Sum);
DeclareVariable("OrdersCountVariableName", VariableOperation_Sum);
DeclareVariable("vGrafBalance", VariableOperation_Sum);
DeclareVariable("vGrafEquity", VariableOperation_Sum);
Декларируем несколько переменных для передачи между экспертами. Несколько слов о VariableOperation:
VariableOperation_Nothing - значение по умолчанию и в ручной декларации нет необходимости, GetVariable возвращает последние значение которое установили любой из экземпляров для переменной функцией SetVariable.
VariableOperation_Sum - в этом случае GetVariable возвращает сумму значений установленных каждым экземпляром. Например если нам нужно получить суммарный экваити или баланс по всему советнику по всем валютам.
Идем дальше: в функции старт
NextTick(TimeCurrent(), AccountBalance(), AccountEquity());
передаем информацию о текущем времени экземпляра, балансе и экваити. Без вызова этой фунции каждый тик не будут корректно работать GetVariable и SetVariable
SetVariable("vGrafBalance", AccountBalance());
SetVariable("vGrafEquity", AccountEquity());
Устанавливаем текущее значение баланса и экваити в общедоступных переменных
GlobalVariableSet("vGrafBalance", GetVariable("vGrafBalance"));
GlobalVariableSet("vGrafEquity", GetVariable("vGrafEquity"));
Получаем суммарные значения баланса и экваити и запихуем в глобальную переменную - там их поиспользует индикатор EquityInTesting.mq4. Суммарные значения по переменным возвращаются поскольку выше мы их задекларировали как VariableOperation_Sum.
double varValue = GlobalVariableGetExt("VarName");
GlobalVariableSetExt("VarName", 10);
Устанавливаем и получаем значение общедоступной переменной. Суммирования происходить не будет так как переменная задекларирована автоматически как VariableOperation_Nothing
Функции SetVariable и GetVariable являються синхронизируемыми. Тоесть к примеру если какой либо терминал отстал от остальных на значение секунд более чем syncSeconds из InitializeTestAPI - остальные терминалы при попытке прочитать или установить общую переменную будут ждать отставшего. Таким образом обеспечивается мультивалютная консистентность тестирования по всем парам.
Ниже примеры реализации функций, возвращающих общий профит по всем валютам эксперта и общее количество ордеров. double GetTotalProfit() и int GetOrdersCount()
Для того что бы следить за изменением баланса и экваити запускаем тестирование в визуальном режиме и кидаем на график индикатор EquityInTesting.mq4. не забываем перенести из примера работу с глобальными переменными vGrafBalance и vGrafEquity.
Проблемы использования:
syncSeconds = 0 сильно снижает быстродействие, предлагаеться использовать только для советников которым критична синхронизация тик в тик, например для мультивалютных хеджеров-скальперов работающих на несоответсвии спредов разных ДЦ. Для остальных можно делать быстрый проход с ненулевым значением и отдельно тестировать спорные участки с syncSeconds = 0.
В случае исключительных ситуаций в корне папки МТ4 появиться файл SmartDev.MultiCurrencyTester.Connect.log
Его можно почитать самому и если не понятно - прислать мне, все починим :)
Если есть вопросы - велкам.
