Эксперты на основе популярных торговых систем и алхимия оптимизации торгового робота (Часть 4)

Nikolay Kositsin | 11 апреля, 2008

Введение

В своих двух последних статьях 2 и 3 я занимался основами бэктестинга. На мой взгляд, самой основной задачей процедуры бэктестинга оказывается качественный анализ поведения эксперта в течении какого-то периода времени при условии, что параметры эксперта периодически заменяются. Стратегией использования результатов оптимизаций в этом процессе будет какое-то нами жёстко устанавливаемое правило выборки параметров из всех вариантов, полученных в результате каждой оптимизации. Например, максимально профицитный вариант с минимальной просадкой.

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

Это, конечно, здорово - получить такой результат, а как быть, ежели эксперт с, казалось бы, очень рациональной логикой работы оказывается безнадёжно убыточным в половине бэктестинговых случаев? Собственно говоря, на стратегиях, основанных на выборках параметров эксперта внутри объёма каждой оптимизации и без сравнения с параметрами, полученными в других оптимизациях свет клином не сошёлся и гораздо интереснее производить статистический анализ и сравнение параметров эксперта, полученных из различных оптимизаций вместе.

Вдобавок, сами оптимизации в бэктестинге занимают достаточно много времени, и поэтому было бы не совсем целесообразным производить эти оптимизации только для самого бэктестинга. Например, попутно при оптимизациях можно было бы результаты всех прибыльных оптимизационных проходов сохранять в один файл в виде таблицы, которую было бы удобно обрабатывать средствами статистического анализа таблиц таких развитых программ, как Microsoft Exсel. В самом терминале MetaTrader 4 есть возможность сохранить результат оптимизации в виде HTML таблицы -

которую без проблем можно загрузить в тот же Microsoft Exсel. Но для обработки в таблице представлены только окончательные отчеты каждого из прогонов - без табличного представления внешних параметров эксперта:

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

Запись результатов оптимизаций эксперта в виде HTML таблицы в один файл

Для осуществления такой записи в файл мной была написана функция:

void Write_Param_htm
(
bool ToResolveWrite_Up, int Timeframe_Up, string ExtrVarblsNemes_Up, string ExtrVarblsVelues_Up, 
bool ToResolveWrite_Dn, int Timeframe_Dn, string ExtrVarblsNemes_Dn, string ExtrVarblsVelues_Dn
)

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

int deinit()
{
//----+
//---- Сброс параметров эксперта в текстовый файл
if (IsOptimization())
{ 
//---- +------------------------------------------+
//Здесь помещён код инициализации внешних переменных функции Write_Param_htm()
//---- +------------------------------------------+
//---- ЗАПИСЬ СТРИНГОВ В HTML ФАЙЛ
Write_Param_htm(Test_Up, Timeframe_Up, ExtVarNemes_Up, ExtVarVelues_Up, Test_Dn, Timeframe_Dn, ExtVarNemes_Dn, 
ExtVarVelues_Dn);
//----+ +-------------------------------------------------------+
}
//---- Завершение деинициализации эксперта
return(0);
//----+ 
}

Эта функция пишет в директории C:\ три файла: два файла результатов оптимизаций (для лонгов и шортов соответственно) и логфайл (MetaTraderTester.log), в который записаны пути и имена этих двух файлов. Имена этих двух файлов выглядят следующим образом (OptReport_Exp_5_2_GBPUSD_240_Long.htm):

Функция Write_Param_htm() включается в состав файла директивой,

#include <TestReport.mqh>

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

//----+ Выполнение условий бэктестинга 
if (!IsBackTestingTime())
return(0);

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

Рассмотрим одну группу, для другой всё аналогично. Параметр ToResolveWrite_Up передаёт в функцию разрешение или запрет на запись в файл. Параметр Timeframe_Up передаёт таймфрейм, на котором работает лонговый алгоритм эксперта. Параметр ExtrVarblsNemes_Up передаёт в функцию стринговую строку, состоящую из имён внешних параметров эксперта для лонгов. Аналогично параметр ExtrVarblsVelues_Up служит для передачи стринговой строки, состоящей из значенний внешних параметров эксперта для лонгов.

На инициализации двух последних переменных мне следовало бы остановиться по подробнее. Допустим, у нас имеется следущие внешние параметры эксперта для длинных позиций:

n_ExternParam_1, n_ExternParam_2, d_ExternParam_3, n_ExternParam_4, b_ExternParam_5, d_ExternParam_6, d_ExternParam_7, 
n_ExternParam_8

Приставка "n_" обозначает, что параметр является типом int, "d_" обозначает, что параметр является типом double, а "b_" обозначает, что параметр является типом bool. Прежде чем делать инициализацию переменных ExtrVarblsNemes_Up и ExtrVarblsVelues_Up, следует объявить и проинициализировать пару дополнительных стринговых переменных кусками html кода:

string n_Width = "</td><td><center>";
string d_Width = "</td><td class=mspt><center>";

После этого можно произвести сборку необходимых стрингов. Для внешней переменной ExtrVarblsNemes_Up этот код будет выглядеть следующим образом:

ExtVarNemes_Up =
StringConcatenate
(
n_Width, "n_ExternParam_1", 
n_Width, "n_ExternParam_2", 
n_Width, "d_ExternParam_3", 
n_Width, "n_ExternParam_4", 
n_Width, "b_ExternParam_5", 
n_Width, "d_ExternParam_6", 
n_Width, "d_ExternParam_7", 
n_Width, "n_ExternParam_8"
);

Инициализация внешней переменной ExtVarVelues_Up выглядит немного посложнее:

ExtVarVelues_Up = 
StringConcatenate
(
n_Width, n_ExternParam_1, 
n_Width, n_ExternParam_2, 
d_Width, d_ExternParam_3, 
n_Width, n_ExternParam_4, 
n_Width, b_ExternParam_5, 
d_Width, d_ExternParam_6, 
d_Width, d_ExternParam_7, 
n_Width, n_ExternParam_8
);

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

DoubleToStr( double value, int digits)

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

Вот вариант реализации записи в HTML файл, сделанный на примере эксперта Exp_5_2.mq4:

//+==================================================================+
//| expert deinitialization function                                 |
//+==================================================================+ 
int deinit()
{
//----+
//---- Сброс параметров эксперта в файл
if (IsOptimization())
{
string ExtVarNemes_Up, ExtVarVelues_Up;
string ExtVarNemes_Dn, ExtVarVelues_Dn, n_Width, d_Width;

//---- ИНИЦИАЛИЗАЦИЯ СТРИНГОВ ДЛЯ ФУНКЦИИ Write_Param_htm 
//----+ +-------------------------------------------------------+ 
n_Width = "</td><td><center>";
d_Width = "</td><td class=mspt><center>";
//---- 
ExtVarNemes_Up =
StringConcatenate( 
n_Width, "IndLevel_Up",
n_Width, "FastEMA_Up",
n_Width, "SlowEMA_Up",
n_Width, "SignalSMA_Up",
n_Width, "STOPLOSS_Up",
n_Width, "TAKEPROFIT_Up",
n_Width, "TRAILINGSTOP_Up",
n_Width, "PriceLevel_Up",
n_Width, "ClosePos_Up");

ExtVarVelues_Up = 
StringConcatenate(
d_Width, DoubleToStr(IndLevel_Up, Digits), // 9
n_Width, FastEMA_Up, // 10
n_Width, SlowEMA_Up, // 11
n_Width, SignalSMA_Up, // 12
n_Width, STOPLOSS_Up, // 13
n_Width, TAKEPROFIT_Up, // 14
n_Width, TRAILINGSTOP_Up, // 15
n_Width, PriceLevel_Up, // 16
n_Width, ClosePos_Up); // 17 
//----+ +-------------------------------------------------------+
ExtVarNemes_Dn =
StringConcatenate( 
n_Width, "IndLevel_Dn",
n_Width, "FastEMA_Dn",
n_Width, "SlowEMA_Dn",
n_Width, "SignalSMA_Dn",
n_Width, "STOPLOSS_Dn",
n_Width, "TAKEPROFIT_Dn",
n_Width, "TRAILINGSTOP_Dn",
n_Width, "PriceLevel_Dn",
n_Width, "ClosePos_Dn"); 

ExtVarVelues_Dn = 
StringConcatenate(
d_Width, DoubleToStr(IndLevel_Dn, Digits), // 9
n_Width, FastEMA_Dn, // 10
n_Width, SlowEMA_Dn, // 11
n_Width, SignalSMA_Dn, // 12
n_Width, STOPLOSS_Dn, // 13
n_Width, TAKEPROFIT_Dn, // 14
n_Width, TRAILINGSTOP_Dn, // 15
n_Width, PriceLevel_Dn, // 16
n_Width, ClosePos_Dn); // 17 

//---- ЗАПИСЬ СТРИНГОВ В HTML ФАЙЛ
Write_Param_htm
(Test_Up, Timeframe_Up, ExtVarNemes_Up, ExtVarVelues_Up, 
Test_Dn, Timeframe_Dn, ExtVarNemes_Dn, ExtVarVelues_Dn);
//----+ +-------------------------------------------------------+
}
//---- Завершение деинициализации эксперта
return(0);
//----+ 
}

Вот полученная HTML таблица параметров для этого эксперта, открытая в Microsoft Exсel:


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

Тут следует добавить, что параметры Drawdown $ и Drawdown % считаются только по закрытым сделкам и поэтому будут несколько отличаться от тех, что мы будем иметь в тестере стратегий. Так же необходимо учитывать тот факт, что параметр Profit factor имеет смысл, если в оптимизационном проходе была хотябы одна убыточная сделка, а в случае их отсутствия он теряет смысл. Так как в таблице изначально отсутствуют убыточные проходы, то для ситуаций с отсутствием убыточных сделок я сделал этот параметр равным нулю.

За торговыми результатами эксперта справа идут оптимизированные параметры эксперта:

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

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

</table>
</body></html>

открыв файл в каком-нибудь текстовом редакторе.

Некоторые пояснения с содержанию файла TestReport.mqh

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

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

Директива

#include <Read_Write_File.mqh>

включает в состав файла TestReport.mqh содержимое файла Read_Write_File.mqh: импорт из модулей операционной системы Windows функций записи и чтения файлов в любой директории:

#include <WinUser32.mqh>
#import "kernel32.dll"
int _lopen (string path, int of);
int _lcreat (string path, int attrib);
int _llseek (int handle, int offset, int origin);
int _lread (int handle, int& buffer[], int bytes);
int _lwrite (int handle, string buffer, int bytes);
int _lclose (int handle);
#import

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

int _FileSize
(
string path
)

bool WriteFile 
(
string path, string buffer
) 

bool ReadFile
(
string path, string&amp;amp; StrBuffer[]
)

SpellFile
(
string path, string&amp; SpellBuffer[]
)

В этих функциях имеется переменная path, которая представляет стринговую строку, содержащую путь к текстовому файлу и его имя.

Функция _FileSize() возвращает значение размера файла в байтах.

Функция WriteFile() пишет в конец файла содержимое стринговой строки buffer. Если необходимо писать в текстовый файл каждый стринг с новой строки то к стрингу buffer необходимо добавить стринг "\n":

buffer = buffer + "\n";

Функция ReadFile() изменяет размер стрингового буфера StrBuffer[] и грузит в него содержимое файла стрингами по четыре буквы в одну ячейку буфера.

Функция SpellFile() изменяет размер стрингового буфера SpellBuffer[] и грузит в него содержимое файла по буквам.

После директивы #include <Read_Write_File.mqh> в файле TestReport.mqh следует группа универсальных пользовательских функций, посредством коих производится расчёт так называемых параметров опимизаций,

CountProfit(int cmd) 
CountMaxDrawdownPrs(int cmd) 
CountMaxDrawdown(int cmd) 
CountAbsDrawdown(int cmd) 
CountProfitFactor(int cmd) 
CountProfitTrades(int cmd) 
CountTotalTrades(int cmd) 
CountExpectedPayoff(int cmd)

аналогичных тем, что получаются на закладке "Результаты оптимизации" Тестера стратегий. Тут следует добавить, что эти функции производят расчёт только на основе данных по закрытым сделкам, и поэтому функции CountMaxDrawdownPrs(), CountMaxDrawdown(), CountAbsDrawdown() возвращают свои значения, которые будут отличаться от аналогичных значений, полученных в тестере стратегий. Если переменная cmd принимает значение OP_BUY, то расчёт во всех функциях происходит только по закрытым длинным позициям, если OP_SELL, то только по закрытым коротким позициям. Любые другие значения этой переменной обеспечивают расчёт по всем закрытым торговым операциям.

После этих функций в файле объявляются ещё четыре функции для записи результатов оптимизаций и оптимизированных параметров в файлы:

void Write_Param_htm
(
bool ToResolveWrite_Up, int Timeframe_Up, string ExtrVarblsNemes_Up, string ExtrVarblsVelues_Up, 
bool ToResolveWrite_Dn, int Timeframe_Dn, string ExtrVarblsNemes_Dn, string ExtrVarblsVelues_Dn
)

void Write_Param_1htm
(
int Timeframe, string ExtrVarblsNemes, string ExtrVarblsVelues
)

void Write_Param_txt
(
bool ToResolveWrite_Up, int Timeframe_Up, string ExtrVarblsVelues_Up,
bool ToResolveWrite_Dn, int Timeframe_Dn, string ExtrVarblsVelues_Dn
)

void Write_Param_1txt
(
int Timeframe, string ExtrVarblsVelues
)

Всё, что касается функции Write_Param_htm(), я уже сказал выше.

Функция Write_Param_1htm(), является аналогом предыдущей функции, но она сделана для экспертов, у которых нет разделения внешних параметров эксперта для коротких и длинных позиций. Из этой функции за ненадобностью удалена переменная ToResolveWrite.

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

Width = " ";

Каждый новый стринг пишется в файл с новой строки, в конце каждого стринга в текстовом файле пишется ";" . Между значениями внешних переменных одного стринга в текстовом файле делаются пробелы.

Функция Write_Param_1txt() - это полный аналог функции Write_Param_txt(), но её следует использовать в экспертах, в которых нет разделения внешних параметров эксперта для коротких и длинных позиций.

И самые две последние функции

void Write_Param_htm_B
(
bool ToResolveWrite_Up, int Timeframe_Up, string ExtrVarblsNemes_Up, string ExtrVarblsVelues_Up, 
bool ToResolveWrite_Dn, int Timeframe_Dn, string ExtrVarblsNemes_Dn, string ExtrVarblsVelues_Dn
)

void Write_Param_1htm_B
(
int Timeframe, string ExtrVarblsNemes, string ExtrVarblsVelues
)

это полные аналоги функций Write_Param_htm() и Write_Param_1htm(), но полученные с их помощью html файлы имеют инверсную окраску, вот фрагмент Exel таблицы, сделанной такой функцией:

Может кому-то понравится работать с такими файлами гораздо больше! Я так, например, сторонник восприятия информации именно в такой форме!

Параболическая торговая система


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

Я здесь представлю МТС с отложенными ордерами типа BuyLimit и SellLimit.

Вот вариант реализации алгоритма для ордеров BuyLimit:


Вариант этого алгоритма для ордеров типа SellLimit будет выглядеть абсолютно аналогично:

Реализация программного кода для этого эксперта по уже отработанной схеме при некоторой предварительной тренировке занимает считанные минуты времени:

//+==================================================================+
//|                                                        Exp_8.mq4 |
//|                             Copyright © 2008,   Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+==================================================================+
#property copyright "Copyright © 2008, Nikolay Kositsin"
#property link "farria@mail.redcom.ru"
//----+ +------------------------------------------------------------+
//---- ВХОДНЫЕ ПАРАМЕТРЫ ЭКСПЕРТА ДЛЯ BUY СДЕЛОК 
extern bool   Test_Up = true;//фильтр направления расчётов сделок
extern int    Timeframe_Up = 240;
extern double Money_Management_Up = 0.1;
extern double Step_Up = 0.02; 
extern double Maximum_Up = 0.2;
extern int    STOPLOSS_Up = 50;  // стоплосс
extern int    TAKEPROFIT_Up = 100; // тейкпрофит
extern int    TRAILINGSTOP_Up = 0; // трейлинстоп
extern int    PriceLevel_Up =40; // разница между текущей ценой и 
                          // ценой срабатывания отложенного ордера
extern bool   ClosePos_Up = true; // разрешение принудительного 
                                              //закрывания позиции
//----+ +------------------------------------------------------------+
//---- ВХОДНЫЕ ПАРАМЕТРЫ ЭКСПЕРТА ДЛЯ SELL СДЕЛОК 
extern bool   Test_Dn = true;//фильтр направления расчётов сделок
extern int    Timeframe_Dn = 240;
extern double Money_Management_Dn = 0.1;
extern double Step_Dn = 0.02; 
extern double Maximum_Dn = 0.2;
extern int    STOPLOSS_Dn = 50;  // стоплосс
extern int    TAKEPROFIT_Dn = 100; // тейкпрофит
extern int    TRAILINGSTOP_Dn = 0; // трейлинстоп
extern int    PriceLevel_Dn = 40; // разница между текущей ценой и 
                          // ценой срабатывания отложенного ордера
extern bool   ClosePos_Dn = true; // разрешение принудительного 
                                              //закрывания позиции
//----+ +------------------------------------------------------------+
//---- Целые переменные для минимума расчётных баров
int MinBar_Up, MinBar_Dn;
//+==================================================================+
//| TimeframeCheck() functions                                       |
//+==================================================================+
void TimeframeCheck(string Name, int Timeframe)
  {
//----+
   //---- Проверка значения переменной Timeframe на корректность
   if (Timeframe != 1)
    if (Timeframe != 5)
     if (Timeframe != 15)
      if (Timeframe != 30)
       if (Timeframe != 60)
        if (Timeframe != 240)
         if (Timeframe != 1440)
           Print(StringConcatenate("Параметр ",Name,
                     " не может ", "быть равным ", Timeframe, "!!!"));    
//----+ 
  }
//+==================================================================+
//| Custom Expert functions                                          |
//+==================================================================+
#include <Lite_EXPERT1.mqh>
//+==================================================================+
//| Custom Expert initialization function                            |
//+==================================================================+
int init()
  {
//---- Проверка значения переменной Timeframe_Up на корректность
   TimeframeCheck("Timeframe_Up", Timeframe_Up);                                                          
//---- Проверка значения переменной Timeframe_Dn на корректность 
   TimeframeCheck("Timeframe_Dn", Timeframe_Dn);  
//---- Инициализация переменных             
   MinBar_Up  = 5;
   MinBar_Dn  = 5;                               
//---- завершение инициализации
   return(0);
  }
//+==================================================================+
//| expert deinitialization function                                 |
//+==================================================================+  
int deinit()
  {
//----+
   
    //---- Завершение деинициализации эксперта
    return(0);
//----+ 
  }
//+==================================================================+
//| Custom Expert iteration function                                 |
//+==================================================================+
int start()
  {
   //----+ Объявление локальных переменных
   double SAR1, SAR2, CLOSE1, CLOSE2;
   //----+ Объявление статических переменных
   //----+ +---------------------------------------------------------------+
   static datetime StopTime_Up, StopTime_Dn; 
   static int LastBars_Up, LastBars_Dn;
   static bool BUY_Sign, BUY_Stop, SELL_Sign, SELL_Stop;
   //----+ +---------------------------------------------------------------+
   //----++ КОД ДЛЯ ДЛИННЫХ ПОЗИЦИЙ 1
   if (Test_Up) 
    {
      int IBARS_Up = iBars(NULL, Timeframe_Up);
      
      if (IBARS_Up >= MinBar_Up)
       {
         if (LastBars_Up != IBARS_Up)
          {
           //----+ Иницмализация переменных 
           BUY_Sign = false;
           BUY_Stop = false;
           LastBars_Up = IBARS_Up;
           StopTime_Up = iTime(NULL, Timeframe_Up, 0)
                                           + 60 * Timeframe_Up;
           
           //----+ ВЫЧИСЛЕНИЕ ИНДИКАТОРНЫХ ЗНАЧЕНИЙ И ЗАГРУЗКА ИХ В БУФЕРЫ                                        
           SAR1 = iSAR(NULL, Timeframe_Up, Step_Up, Maximum_Up, 1);         
           SAR2 = iSAR(NULL, Timeframe_Up, Step_Up, Maximum_Up, 2);
           //---
           CLOSE1 = iClose(NULL, Timeframe_Up, 1);
           CLOSE2 = iClose(NULL, Timeframe_Up, 2);
           
           //----+ ОПРЕДЕЛЕНИЕ СИГНАЛОВ ДЛЯ СДЕЛОК                                           
           if (SAR2 > CLOSE2)
             if (SAR1 < CLOSE1)
                          BUY_Sign = true;
                          
           if (SAR1 > CLOSE1)
                          BUY_Stop = true;                                           
          }
          //----+ СОВЕРШЕНИЕ СДЕЛОК
          if (PriceLevel_Up == 0)
           {           
            if (!OpenBuyOrder1(BUY_Sign, 1, 
                Money_Management_Up, STOPLOSS_Up, TAKEPROFIT_Up))
                                                                return(-1);
           }
          else
           {           
            if (!OpenBuyLimitOrder1(BUY_Sign, 1, 
                Money_Management_Up, STOPLOSS_Up, TAKEPROFIT_Up,
                                            PriceLevel_Up, StopTime_Up))
                                                                return(-1);
           }
           
          if (ClosePos_Up)
                if (!CloseOrder1(BUY_Stop, 1))
                                        return(-1);
                                        
          if (!Make_TreilingStop(1, TRAILINGSTOP_Up))
                                                  return(-1);
        }
     }
   //----+ +---------------------------------------------------------------+
   //----++ КОД ДЛЯ КОРОТКИХ ПОЗИЦИЙ 1
   if (Test_Dn) 
    {
      int IBARS_Dn = iBars(NULL, Timeframe_Dn);
      
      if (IBARS_Dn >= MinBar_Dn)
       {
         if (LastBars_Dn != IBARS_Dn)
          {
           //----+ Иницмализация переменных 
           SELL_Sign = false;
           SELL_Stop = false;
           LastBars_Dn = IBARS_Dn;
           StopTime_Dn = iTime(NULL, Timeframe_Dn, 0)
                                           + 60 * Timeframe_Dn;
           
           //----+ ВЫЧИСЛЕНИЕ ИНДИКАТОРНЫХ ЗНАЧЕНИЙ И ЗАГРУЗКА ИХ В БУФЕРЫ                                        
           SAR1 = iSAR(NULL, Timeframe_Dn, Step_Dn, Maximum_Dn, 1);         
           SAR2 = iSAR(NULL, Timeframe_Dn, Step_Dn, Maximum_Dn, 2);
           //---
           CLOSE1 = iClose(NULL, Timeframe_Dn, 1);
           CLOSE2 = iClose(NULL, Timeframe_Dn, 2);
           
           //----+ ОПРЕДЕЛЕНИЕ СИГНАЛОВ ДЛЯ СДЕЛОК                                           
           if (SAR2 < CLOSE2)
             if (SAR1 > CLOSE1)
                          SELL_Sign = true;
                          
           if (SAR1 < CLOSE1)
                          SELL_Stop = true;                                           
          }
          //----+ СОВЕРШЕНИЕ СДЕЛОК
          if (PriceLevel_Dn == 0)
           {           
            if (!OpenSellOrder1(SELL_Sign, 2, 
                Money_Management_Dn, STOPLOSS_Dn, TAKEPROFIT_Dn))
                                                                return(-1);
           }
          else
           {           
            if (!OpenSellLimitOrder1(SELL_Sign, 2, 
                Money_Management_Dn, STOPLOSS_Dn, TAKEPROFIT_Dn,
                                            PriceLevel_Dn, StopTime_Dn))
                                                                return(-1);
           }
           
          if (ClosePos_Dn)
                if (!CloseOrder1(SELL_Stop, 2))
                                        return(-1);
                                        
          if (!Make_TreilingStop(2, TRAILINGSTOP_Dn))
                                                  return(-1);
        }
     }
   //----+ +---------------------------------------------------------------+
//----+ 
    
    return(0);
  }
//+------------------------------------------------------------------+

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

Пробойная торговая система

Что касается торговых систем на основе пробойных уровней, то их сушествует просто бесчисленное множество и при некоторой изобретательности всегда можно придумать ещё какую-нибудь пробойную торговую систему. Я в этой статье подробно остановлюсь на системе, в которой пробойные уровни определяются границами канала, построенного на базе мувинга:

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

И то же сакое для ордеров SellLimit:

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

dMovLevel_Up = MovLevel_Up * Point;
dMovLevel_Dn = MovLevel_Dn * Point;

которые используются для расчёта значений уровней UpMovLeve1 и DownMovLevel1.

Торговля на новостях.

На рынке часто возникает ситуация, когда стабильно в одно и то же время происходят сильные движения. Нельзя сказать заранее, в какую сторону будет происходить это движение, но то, что оно будет, можно утверждать с большой степенью вероятности заранее. Например, к таким движениям приводит выход данных по занятости в США, который происходит обычно в первую пятницу каждого месяца (Non-farm Payrolls). Существует немало любителей ловить такие движения рынку, и поэтому было бы интересно проверить - а есть ли смысл в такой торговой системе. Сама идея такой МТС заключается в том, чтобы в нужное время выставить два отложенных ордера типа BuyStop и SellStop и ждать, пока при возникновении сильного движения один из них, открыв сделку, не закроется по тейкпрофиту:



Это ещё один вариант пробойной системы. Реализация в программном коде подобной торговой системы в простейшем виде не вызывает никаких серьёзных недоразумений:

//+==================================================================+
//|                                                       Exp_10.mq4 |
//|                             Copyright © 2008,   Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+==================================================================+
#property copyright "Copyright © 2008, Nikolay Kositsin"
#property link "farria@mail.redcom.ru"
//----+ +------------------------------------------------------------+
//---- ВХОДНЫЕ ПАРАМЕТРЫ ЭКСПЕРТА ДЛЯ УСТАНОВКИ ОТЛОЖЕННЫХ ОРДЕРОВ
extern int    NewsWeekDay = 5;  // День недели для выхода новости
// если параметр меньше единицы или больше пяти, то используется любой рабочий день
extern int    NewsHour = 15;  // час выхода новости
extern int    NewsMinute = 0;  // минута выхода новости
extern int    OrderLife = 30;  // Количество минут жизни отложенного ордера
//----+ +------------------------------------------------------------+
//---- ВХОДНЫЕ ПАРАМЕТРЫ ЭКСПЕРТА ДЛЯ ЗАКРЫВАНИЯ ПОЗИЦИЙ
extern int    PosLife = 120;  // Количество минут жизни открытой позиции 
                                              //от времени выхода новости
//----+ +------------------------------------------------------------+
//---- ВХОДНЫЕ ПАРАМЕТРЫ ЭКСПЕРТА ДЛЯ BUY СДЕЛОК 
extern bool   Test_Up = true;//фильтр направления расчётов сделок
extern double Money_Management_Up = 0.1;
extern int    STOPLOSS_Up = 50;  // стоплосс
extern int    TAKEPROFIT_Up = 100; // тейкпрофит
extern int    TRAILINGSTOP_Up = 0; // трейлинстоп
extern int    PriceLevel_Up =40; // разница между текущей ценой и 
                          // ценой срабатывания отложенного ордера
extern bool   ClosePos_Up = true; // разрешение принудительного 
                                              //закрывания позиции
//----+ +------------------------------------------------------------+
//---- ВХОДНЫЕ ПАРАМЕТРЫ ЭКСПЕРТА ДЛЯ SELL СДЕЛОК 
extern bool   Test_Dn = true;//фильтр направления расчётов сделок
extern double Money_Management_Dn = 0.1;
extern int    STOPLOSS_Dn = 50;  // стоплосс
extern int    TAKEPROFIT_Dn = 100; // тейкпрофит
extern int    TRAILINGSTOP_Dn = 0; // трейлинстоп
extern int    PriceLevel_Dn = 40; // разница между текущей ценой и 
                          // ценой срабатывания отложенного ордера
extern bool   ClosePos_Dn = true; // разрешение принудительного 
                                              //закрывания позиции
//+==================================================================+
//| Custom Expert functions                                          |
//+==================================================================+
#include <Lite_EXPERT1.mqh>
//+==================================================================+
//| Custom Expert initialization function                            |
//+==================================================================+
int init()
  {
//---- 
    if (NewsHour > 23) 
             NewsHour = 23;
    if (NewsMinute > 59)
           NewsMinute = 59;                               
//---- завершение инициализации
   return(0);
  }
//+==================================================================+
//| expert deinitialization function                                 |
//+==================================================================+  
int deinit()
  {
//----+
   
    //---- Завершение деинициализации эксперта
    return(0);
//----+ 
  }
//+==================================================================+
//| Custom Expert iteration function                                 |
//+==================================================================+
int start()
  {
   //----+ Объявление статических переменных
   //----+ +---------------------------------------------------------------+
   static datetime StopTime, PosStopTime; 
   static bool BUY_Sign, SELL_Sign, BUY_Stop, SELL_Stop;
   //----+ +---------------------------------------------------------------+
   BUY_Stop = true;
   SELL_Stop = true;
   
   //----+ ОПРЕДЕЛЕНИЕ СИГНАЛОВ ДЛЯ СДЕЛОК 
   if (DayOfWeek() == NewsWeekDay 
     || NewsWeekDay < 1 
       || NewsWeekDay > 5)
         if (Hour() == NewsHour)
          if (Minute() == NewsMinute)
    {
     StopTime = TimeCurrent() + OrderLife * 60;
     PosStopTime = TimeCurrent() + PosLife * 60;
     BUY_Sign = true;
     SELL_Sign = true;
    }
   
   //---- Удаление сигналов для совершения сделок после
                           // времени истечения отложенных ордеров
   if (TimeCurrent() >= StopTime)
    {
     BUY_Sign = false;
     SELL_Sign = false;
    }

   //----+ +---------------------------------------------------------------+ 
   //----++ КОД ДЛЯ ДЛИННЫХ ПОЗИЦИЙ 1
   if (Test_Up) 
    {
      //----+ СОВЕРШЕНИЕ СДЕЛОК          
      if (!OpenBuyStopOrder1(BUY_Sign, 1, 
                Money_Management_Up, STOPLOSS_Up, TAKEPROFIT_Up,
                                            PriceLevel_Up, StopTime))
                                                                return(-1);
      if (ClosePos_Up)  
        if (TimeCurrent() >= PosStopTime)                                                                  
                if (!CloseOrder1(BUY_Stop, 1))
                                           return(-1);
                                         
      if (!Make_TreilingStop(1, TRAILINGSTOP_Up))
                                           return(-1);
     }
   //----+ +---------------------------------------------------------------+
   //----++ КОД ДЛЯ КОРОТКИХ ПОЗИЦИЙ 1
   if (Test_Dn) 
     {
      //----+ СОВЕРШЕНИЕ СДЕЛОК
      if (!OpenSellStopOrder1(SELL_Sign, 2, 
                Money_Management_Dn, STOPLOSS_Dn, TAKEPROFIT_Dn,
                                            PriceLevel_Dn, StopTime))
                                                                return(-1);
      if (ClosePos_Dn)  
        if (TimeCurrent() >= PosStopTime)            
                if (!CloseOrder1(SELL_Stop, 2))
                                           return(-1);
                                        
      if (!Make_TreilingStop(2, TRAILINGSTOP_Dn))
                                           return(-1);
     }
   //----+ +---------------------------------------------------------------+
//----+ 
    
    return(0);
  }
//+------------------------------------------------------------------+

Заключение

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