Брутфорс-подход к поиску закономерностей (Часть III): Новые горизонты

Evgeniy Ilin | 20 января, 2021

Содержание


Введение

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

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

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


Новые мысли и идеи

Программа находилась на стадии прототипа, и хотелось выжать из нее максимум и посмотреть на что способен даже такой простой метод поиска, как случайный перебор чисел. Основной проблемой являлось то, что для достоверного поиска глобальных закономерностей нужно брать большие участки котировок, а вместе с тем длительность одного прохода по истории была катастрофически низкой. К примеру, при анализе M5 котировки любой валютной пары длительностью 10 лет программа выдавала примерно 700-2000 вариантов в час на 1 хорошем ядре, и это еще при щадящих настройках.

Если учесть, что у вас сервер с ядрами эдак с 30, то даже здесь скорость получится не больно-то высокой, и все равно вам придется ждать днями. Единственным выходом является сокращение объема данных, которые нужно анализировать, сделать это можно, только нарезав весь промежуток на равные кусочки, и периодически пропускать некоторые из них, не проводя вычисления. Такие промежутки у нас есть, это дни, месяцы, годы, минуты, часы. Кроме того, каждый из этих условных участков характеризует определенные параметры торговли. Иначе говоря, у нас помимо ускорения еще и есть возможность детально исследовать каждый день недели, час и минуту на наличие закономерностей. Нам даже не нужно будет беспокоиться, с чем связана эта закономерность.

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


Сегментирование выборки и предсказуемость производных выборок

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

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

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

Segmenting Diagram

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

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

Вообще говоря, это дискретные функции. То есть аргументы могут принимать строго фиксированные значения. Если зафиксировать переменные, связанные с месяцами и днями недели, то можно примерно представить, как эти 2 функции будут выглядеть. Максимум измерений, которые мы можем представить себе визуально, это три, поэтому максимально информативный график можно нарисовать только с двумя свободными переменными. Я выбрал "Ts" и "Te":

3D Graph

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

Хочу еще заметить, что при исследовании рынка с помощью своего софта я заметил один очень интересный участок, где в большинстве случаев у всех валютных пар завышенные показатели предсказуемости. Этот промежуток примерно от 23:30 и до 0:30. Может быть я не совсем точно его написал, но это точно связано с точкой смены суток. Но как оказалось, при проверке на MetaTrader 5 тех стратегий, которые показывали великолепные профит факторы на MetaTrader 4, вся эта видимая прибыльность улетучивалась. Во всем виноват спред. Советую всем очень бояться того факта, что ваша закономерность окажется внутри спреда. Большинство найденных закономерностей будут внутри спреда.


Финальные модификации алгоритма поиска

Финальные модификации были призваны ускорить работу программы, а также максимально повысить вариативность поиска и его эффективность. Список изменений:

  1. Добавлена возможность поиска закономерностей в фиксированном временном интервале
  2. Добавлена возможность торговли только по отмеченным дням
  3. Добавлена возможность генерировать случайные наборы дней для каждого нового варианта, исходя из отмеченных дней
  4. Добавлена возможность генерировать случайные окна серверного времени, указав возможную минимальную и максимальную длину окна в минутах
  5. Возможно комбинирование любых из этих настроек
  6. Вторая вкладка оптимизации теперь умеет работать в многопоточном режиме
  7. Режим оптимизации доработан и теперь находит все, что можно найти

Выглядит все это теперь вот так:

Вкладка брутфорса

Вторая вкладка не изменилась, но я покажу ее все равно:

Вкладка оптимизации

Третья вкладка выглядит так:

Вкладка генерации советника

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


Оптимизация шаблонов и вспомогательного советника

Также под эту задачу пришлось модифицировать шаблоны советников. А также роботы, которые генерируют котировку в нужном формате, удобном для чтения программой. Код советника, генерирующего котировку для MetaTrader 5, стал вот таким:

string FileNameString;
uint Handle0x;
datetime Time0=0;

double Open[];
double Close[];
double High[];
double Low[];
datetime Time[];


void WriteEnd()
   {
   FileWriteString(Handle0x,"EndBars"+"\r\n");
   MqlDateTime T;
   TimeToStruct(Time[1],T);
   FileWriteString(Handle0x,IntegerToString(int(T.year))+"\r\n");
   FileWriteString(Handle0x,IntegerToString(int(T.mon))+"\r\n");
   FileWriteString(Handle0x,IntegerToString(int(T.day)));
   }

void OpenAndWriteStart()
   {
   FileDelete(FileNameString);
   Handle0x=FileOpen(FileNameString,FILE_WRITE|FILE_TXT|FILE_COMMON|FILE_ANSI,'\t',CP_UTF8);
   FileSeek(Handle0x,0,SEEK_SET);
   FileWriteString(Handle0x,"DataXXX"+" "+Symbol()+" "+IntegerToString(Period())+"\r\n");
   FileWriteString(Handle0x,DoubleToString(_Point,8)+"\r\n");
   MqlDateTime T;
   TimeToStruct(Time[1],T);
   FileWriteString(Handle0x,IntegerToString(int(T.year))+"\r\n");
   FileWriteString(Handle0x,IntegerToString(int(T.mon))+"\r\n");
   FileWriteString(Handle0x,IntegerToString(int(T.day))+"\r\n");             
   }
      
void WriteBar()
   {
   FileWriteString(Handle0x,"\r\n");
   FileWriteString(Handle0x,DoubleToString(Close[1],8)+"\r\n");
   FileWriteString(Handle0x,DoubleToString(Open[1],8)+"\r\n");
   FileWriteString(Handle0x,DoubleToString(High[1],8)+"\r\n");
   FileWriteString(Handle0x,DoubleToString(Low[1],8)+"\r\n");         
   FileWriteString(Handle0x,IntegerToString(int(Time[1]))+"\r\n");
   MqlDateTime T;
   TimeToStruct(Time[1],T);
   FileWriteString(Handle0x,IntegerToString(int(T.hour))+"\r\n");
   FileWriteString(Handle0x,IntegerToString(int(T.min))+"\r\n");
   FileWriteString(Handle0x,IntegerToString(int(T.day_of_week))+"\r\n");         
   //FileClose(Handle0x);
   }      

void CloseFile()
   {
   FileClose(Handle0x);
   }

bool bNewBar()
   {
   ArraySetAsSeries(Close,false);                        
   ArraySetAsSeries(Open,false);                           
   ArraySetAsSeries(High,false);                        
   ArraySetAsSeries(Low,false);                              
   CopyOpen(_Symbol,_Period,0,2,Open);
   CopyClose(_Symbol,_Period,0,2,Close);
   CopyHigh(_Symbol,_Period,0,2,High);
   CopyLow(_Symbol,_Period,0,2,Low);
   ArraySetAsSeries(Close,true);                        
   ArraySetAsSeries(Open,true);                           
   ArraySetAsSeries(High,true);                        
   ArraySetAsSeries(Low,true);                                 
   if ( Time0 < Time[1] )
      {
      if (Time0 != 0)
         {
         Time0=Time[1];
         return true;
         }
      else
         {
         Time0=Time[1];
         return false;
         }
      }
   else return false;
   }

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
  ArrayResize(Close,2,0);
  ArrayResize(Open,2,0);   
  ArrayResize(Time,2,0);
  ArrayResize(High,2,0);
  ArrayResize(Low,2,0);  
  FileNameString="DataHistory"+" "+Symbol()+" "+IntegerToString(Period());
  OpenAndWriteStart();
   return(INIT_SUCCEEDED);
  }

void OnDeinit(const int reason)
  {
  WriteEnd();
  CloseFile();
  }

void OnTick()
  {
  ArraySetAsSeries(Time,false);
  CopyTime(_Symbol,_Period,0,2,Time);
  ArraySetAsSeries(Time,true);
  if ( bNewBar()) WriteBar();
  }

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

Вместо того чтобы использовать временные функции, которые преобразуют datetime формат в дни недели, часы и минуты, я просто записываю их в котировку как дополнительную информацию о баре. Это все целочисленные значения, кроме ENUM_DAY_OF_WEEK. Мне оставалось лишь реализовать подобный нумерованных список внутри C# кода, что проще простого, ну и, естественно, предполагать в моих шаблонах, что данные вернутся в том же виде. Отказ от временных функций позволяет уйти от лишних ненужных вычислений на стороне C# кода. Кроме того, это позволяет избежать нестыковки во времени. Лучше такие опасные вещи обходить стороной.

Файл котировок на выходе можно открыть любым текстовым редактором, и увидеть вот такую простую и понятную структуру:

History Data File


Шапка шаблона, где прописываются переменные, и раньше содержала поля для автозаполнения в момент генерации, теперь их список пополнился переменными времени торговли и торговыми днями.

Шаблон входных настроек теперь выглядит вот так:

double C1[] = { %%%CVALUES%%% };//Brutted Values
int CNum=%%%CNUMVALUE%%%;//Bars To Equation
int DeepBruteX=%%%DEEPVALUE%%%;//Max Pow Of Polinom
int DatetimeStart=%%%DATETIMESTART%%%;//Help Datetime
input bool bInvert=%%%INVERT%%%;//Invert Trade(or sign of values as the same)
input int DaysToFuture=%%%DAYSFUTURE%%%;//Days To Future
int DaysToTrade[]={ %%%DAYS%%% };//Days To Trade
input double ValueOpenE=%%%OPTVALUE%%%;//Open Signal
input bool bUseTimeCorridorE=%%%TIMECORRIDORVALUE%%%;//Use Time Corridor
input int TradeHour=%%%HOURSTARTVALUE%%%;//Start Trading Hour
input int TradeMinute=%%%MINUTESTARTVALUE%%%;//Start Trading Minute
input int TradeHourEnd=%%%HOURENDVALUE%%%;//End Trading Hour
input int TradeMinuteEnd=%%%MINUTEENDVALUE%%%;//End Trading Minute

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

Основная функция тоже претерпела ряд изменений:

bool bDay()//Day check
   {
   MqlDateTime T;
   TimeToStruct(Time[0],T);
   for ( int i=0; i<ArraySize(DaysToTrade); i++ )
      {
      if ( T.day_of_week == DaysToTrade[i] ) return true;
      }
   return false;
   }

void Trade()//Trade Function
   {
   double Value;
   Value=PolinomTrade();
   MqlTick LastTick;
   SymbolInfoTick(Symbol(),LastTick);
   MqlDateTime tm;
   TimeToStruct(LastTick.time,tm);
   int MinuteEquivalent=tm.hour*60+tm.min;
   int BorderMinuteStartTrade=HourCorrect(TradeHour)*60+MinuteCorrect(TradeMinute);
   int BorderMinuteEndTrade=HourCorrect(TradeHourEnd)*60+MinuteCorrect(TradeMinuteEnd);
   
   if ( Value > ValueCloseE)
      {
      if ( !bInvert ) CloseBuyF();
      else CloseSellF();
      }
      
   if ( Value < -ValueCloseE)
      {
      if ( !bInvert ) CloseSellF();
      else CloseBuyF();
      }   
   
   if ( !bUseTimeCorridorE )
      {
      if ( double(TimeCurrent()-DatetimeStart)/86400.0 <= DaysToFuture && Value > ValueOpenE && Value <= ValueOpenEMax )
         {
         if ( !bInvert ) SellF();
         else BuyF();
         }
      
      if ( double(TimeCurrent()-DatetimeStart)/86400.0 <= DaysToFuture && Value < -ValueOpenE && Value >= -ValueOpenEMax )
         {
         if ( !bInvert ) BuyF();
         else SellF();
         }      
      }
   else
      {
      if ( BorderMinuteStartTrade > BorderMinuteEndTrade && bDay() )
         {
         if ( !(MinuteEquivalent>=BorderMinuteEndTrade && MinuteEquivalent<= BorderMinuteStartTrade) )
            {
            if ( double(TimeCurrent()-DatetimeStart)/86400.0 <= DaysToFuture && Value > ValueOpenE && Value <= ValueOpenEMax )
               {
               if ( !bInvert ) SellF();
               else BuyF();
               }
      
            if ( double(TimeCurrent()-DatetimeStart)/86400.0 <= DaysToFuture && Value < -ValueOpenE && Value >= -ValueOpenEMax )
               {
               if ( !bInvert ) BuyF();
               else SellF();
               }
            }        
         }
      if ( BorderMinuteStartTrade <= BorderMinuteEndTrade && bDay() )
         {
         if ( MinuteEquivalent>=BorderMinuteStartTrade && MinuteEquivalent<= BorderMinuteEndTrade )
            {
            if ( double(TimeCurrent()-DatetimeStart)/86400.0 <= DaysToFuture && Value > ValueOpenE && Value <= ValueOpenEMax )
               {
               if ( !bInvert ) SellF();
               else BuyF();
               }
      
            if ( double(TimeCurrent()-DatetimeStart)/86400.0 <= DaysToFuture && Value < -ValueOpenE && Value >= -ValueOpenEMax )
               {
               if ( !bInvert ) BuyF();
               else SellF();
               }
            }        
         }      
      }

   if ( bPrintValue ) Print("Value="+DoubleToString(Value));     
   }

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


Анализ глобальных закономерностей

Все представленные здесь результаты получены на основе обучения на участке 2010.01.01-2020.01.01, как и в предыдущей статье. Сделано это намеренно для того, чтобы оставить текущий год как форвард период для проверки результатов на котировках из будущего. Форвард период был выбран 2020.01-2020.12.01.

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

Начнем с валютной пары EURCHF H1. Предшествующий анализ показал неплохую предсказуемость данного инструмента, поэтому я решил начать с него. Мне удалось создать 3 советника по результатам оптимизации. Пару тестов я приведу с версии для MetaTrader 4, для того чтобы показать, насколько выросли показатели найденных закономерностей. Посмотрим на первый вариант:

First Bot EURCHF H1 2010.01.01-2020.01.01 MetaTrader 4

Лот при тестировании был выставлен 0.1. Исходя из математического ожидания, полученного при тесте, выходит, что математическое ожидание 54 доллара против 8 долларов, которые я получил в предыдущей версии программы. Профит-фактор тоже повысился значительно, минимум на 0.5, а может и выше. Я помню, что в прошлой статье было в районе 1.14. Это был максимальный профит-фактор, который удалось получить. Надеюсь, всем видно, насколько разительно отличаются результаты, стоило лишь структурировать выборку по дням недели и времени работы.

Хочу заметить, что это далеко не самый лучший результат. Ведь тесты проводились весьма ограниченное время, в виду специфики задачи. Приходилось укладываться в очень жесткие временные рамки. В общей сложности на все тесты ушло порядка 2-3 суток чистого времени. Тесты проводились на 2 ядрах довольно слабого процессора по сегодняшним меркам, даже не серверного процессора. На каждый вариант ушло около 5-6 часов примерно, если подробнее рассмотреть процесс поиска закономерностей.

Еще в процессе тестирования нашлись ошибки в алгоритме программы, которые довольно часто выдавали ложные результаты. Конечно, я уже исправил эти ошибки и их больше нет, но это очень негативно сказалось на количестве найденных мной закономерностей. Тем не менее, мне хватило этого времени для нахождения приемлемых вариантов. Теперь поглядим, как этот тест выглядит в MetaTrader 5:

First Bot EURCHF H1 2010.01.01-2020.01.01 MetaTrader 5

Сделок так мало потому, что я ограничивал спред при тестировании для того, чтобы получить более менее стабильный тест, в погоне за большими показателями прибыльности. Но это делать совсем необязательно, как показала практика. Почему — объясню в конце статьи. Несмотря на очень маленькое количество сделок, которые остались от итогового теста, мы все равно можем положиться на эти данные, так как за ними стоит максимально продолжительная выборка (ее я получил, когда брутил коэффициенты формулы на первой вкладке). Фактически, на первой вкладке мы просчитываем все бары, которые есть в загруженном отрезке, поэтому это идеальный фундамент для надстройки (оптимизации). Когда мы вычленяем часть результатов большой выборки как более маленькую выборку, то сила этой выборки тем больше ,чем больше данных содержит первая выборка(ордера).

Теперь можно взглянуть на форвард период:

Future 1 year

Всего 2 сделки за 1 год, очень мало но результат плюсовой.

Посмотрим на следующий вариант, он с того же участка, что и предыдущий:

Second Bot EURCHF H1 2010.01.01-2020.01.01 MetaTrader 4

В MetaTrader 5 без требований к спредам данная формула убыточна, но если его ограничить, исходя из полученного математического ожидания, то окажется, что в этих сигналах есть и хорошие, хоть график и выглядит косо очень:

Second Bot EURCHF H1 2010.01.01-2020.01.01 MetaTrader 5

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

Future 1 year

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

Еще хочу сказать, что тестов для MetaTrader 4 больше не будет. Двух полных вариантов, я думаю, будет вполне достаточно для сравнения. Да и нагромождать статью лишними данными тоже не вижу смысла. Третий вариант:

Third Bot EURCHF H1 2010.01.01-2020.01.01 MetaTrader 5

График в целом нормальный, по крайней мере, небезобразный. Теперь посмотрим в будущее:

Future 1 year

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

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

Переходим на EURUSD H4

Как я уже сказал, тесты с MetaTrader 4 я не буду больше выкладывать, но тесты на этой валютной паре выглядят так же ровно и красиво как и на прошлой, просто знайте это. На данном таймфрейме я нашел 2 разных робота. Начнем с первого:

First Bot EURUSD H4 2010.01.01-2020.01.01 MetaTrader 5

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

Future 1 year

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

Осталось закрепить наши выводы и посмотреть, что нам покажет второй советник:

Second Bot EURUSD H4 2010.01.01-2020.01.01 MetaTrader 5

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

Future 1 year

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

Переходим на EURJPY M5.

EURJPY M5 2010.01.01-2020.01.01 MetaTrader 5

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

Future 1 year

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

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


Делаем выводы

Считаю, что данных тестов достаточно чтобы сделать несколько важных выводов относительно поиска и тестирования рабочих советников в терминалах MetaTrader 4 и MetaTrader 5. Дело в том, что тестеры обоих терминалов работают немного по-разному. Котировки для MetaTrader 4 хранят историю без информации о спредах, тем самым давая возможность пользователю выставлять спред "1", что почти равно "0". Для подавляющего большинства советников этот спред вообще не имеет никакого значения, тем самым позволяя находить зачатки любых закономерностей и развивать их, чего не дает MetaTrader 5. Но у MetaTrader 5 котировок сохраняется спред, что позволяет оценить реальную прибыльность системы.

Как показала моя практика, если система, которая была написана для MetaTrader 4, не была так же переведена на пятый терминал, то у такой системы очень мало шансов показать прибыль, потому что большинство закономерностей, которые мы способны найти, лежат внутри спреда, и не имеют никакой практической пользы. Правда, в некоторых случаях среди всех сигналов можно выделить сигналы, которые были с минимальными спредами, и тем самым останется хотя-бы часть сигналов, но опыт показывает, что так бывает далеко не всегда.

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

Development Cycle

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

  1. Использование простейших математических функций для обработки данных баров
  2. Добавление механизмов вариации лота, повышающих профит-фактор для роботов, реализующих глобальные закономерности
  3. Добавление и отработка механизмов мартингейла и обратного мартингейла для работы на предельно коротких участках
  4. Добавление механизма объединения хороших роботов в один робот с максимально возможной частотой трейдов
  5. Включение спредов в котировку и избавление пользователя от ручного подбора (полная автоматизация всего цикла разработки)
  6. Исправление выявленных ошибок оптимизатора

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

В конечном итоге цель данного софта — это автоматизировать весь процесс разработки, тестирования и оптимизации советников для обеих платформ, уходя от привычной рутины. Те манипуляции, что я сейчас проделываю в ручном режиме, с легкостью может делать и машина, причем конечно гораздо быстрее меня. Может быть качество у меня будет повыше, чем у машины, но в итоге машина все равно найдет лучше и быстрее за счет своей вычислительной мощи, которой ни у одного человека нет. Да и, если честно, я уже настолько обленился, что мне даже крутить такие элементарные вещи, как величину спреда, не хочется. Вроде бы кажется мелочь, но каждая такая мелочь в разы повышает длительность полного цикла разработки системы. Не так интересен сам процесс разработки, сколько количество и качество полученных результатов в единицу времени, ведь от этого и будет зависеть прибыль в случае, если вы хотите использовать эти системы для торговли. Да и время тикает, пока вы доведете до ума одну систему, она уже может быть неактуальной. Большинство работающих советников будут работать очень ограниченное время, и в данной связи важно делать все очень быстро и качественно. Куй железо пока горячо. Либо используйте оптимизатор каждый месяц, чего я своему врагу не посоветую даже.

Возможно, если я увижу потенциал в дальнейшем развитии идеи, то продолжу развивать направление. Если идея будет успешной, то буду делать аналогичную программу по градиентному бустингу, ну или просто добавлю новый режим в виде третьей ступени анализа данных. Дело в том, что сам метод очень даже неплох как поисковик предикторов для задачи градиентного бустинга. Вместо того, чтобы самому придумывать предикторы и их испытывать, можно с успехом использовать предикторы, найденные этим методом. Но, конечно, это уже гораздо более серьезная и глубокая модификация программы, поэтому пока рано говорить об этом. Время покажет. Но пара-тройка модификаций, которые я перечислил в этом списке, уже реализованы, и сейчас я занимаюсь их тестированием и коплю данные для максимально развернутой статьи с результатами изменений и примерами применения. " TO BE CONTINUED", как говорится.

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

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

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


Заключение

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

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

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

Ссылки

  1. Брутфорс-подход к поиску закономерностей
  2. Брутфорс-подход к поиску закономерностей (Часть II): Погружение