Как найти прибыльную торговую стратегию

Автор: lexy Среда, Сентябрь 10th, 2014 Нет комментариев

Рубрика: Разное

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

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

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

А что если попробовать весь этот процесс полностью переложить на плечи компьютера?

В данной статье будет рассмотрено использование простейшей однослойной нейронной сети для идентификации будущего направления движения котировок по показаниям осциллятора Acceleration/Deceleration (AC).

Нейронная сеть

Что такое нейронная сеть или Perceptron? Это алгоритм, использующий уравнение линейного неравенства (линейного фильтра), с помощью которого можно причислить исследуемый объект к тому или иному классу или, наоборот, исключить его из этого самого класса объектов. Само неравенство выглядит так:
      w1 * a1 + w2 * a2 + … wn * an > d,
где:wi — весовой коэффициент с индексом i,

ai — численное значение признака с индексом i исследуемого объекта,

d — пороговое значение, чаще всего равное 0.

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

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

Дело в том, что геометрически плоскость описывается линейным уравнением. Например, в трехмерном пространстве уравнение плоскости относительно координат X, Y и Z имеет вид:

A * X + B * Y + C * Z + D = 0
Координаты всех точек, расположенных по одну сторону от плоскости в этом пространстве, удовлетворяют неравенству:
A * X + B * Y + C * Z + D > 0

А координаты всех точек, лежащих по другую сторону от плоскости, удовлетворяют неравенству:

A * X + B * Y + C * Z + D < 0
Таким образом, если нам известно уравнение некой плоскости и координаты любых точек, то мы можем разделить множество всех точек пространства на два множества точек, разделяемых этой плоскостью.Соответственно, весовые коэффициенты в неравенстве нейронной сети — это константы, которые задают уравнение некой плоскости в многомерном пространстве признаков объектов. А с помощью самого неравенства мы можем точно определить, лежат ли эти объекты по одну или по другую сторону заданной плоскости. Достаточно лишь найти точечные координаты самих объектов и, подставив их в уравнение плоскости, сравнить с нулем.

Постановка задачи

Если мы разделим объекты на два класса: открываемые длинные позиции и короткие позиции, а в качестве признаков возьмем значения индикаторов или осцилляторов технического анализа, то остается лишь выяснить уравнение плоскости и попытаться с ее помощью произвести идентификацию. Постановка задачи ясна.Но есть одна проблема, которая присуща нейронным сетям. Возьмем, например, двумерное пространство признаков, описываемое координатами X и Y. Расставим в этом пространстве некие объекты, имеющие точечные координаты.

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

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

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

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

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

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

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

  1. Расстановки стоп-ордеров — takeprofit и stoploss;
  2. Разворотом позиции в противоположном направлении при получении сигнала о изменении направления тенденции рынка.

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

  • Котировки вероятнее всего будут двигаться в сторону повышения;
  • Котировки вероятнее всего будут двигаться в сторону понижения.

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

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

Решение задачи

Ниже приведен исходный код советника, реализующего данную торговую стратегию:

//+------------------------------------------------------------------+
//|                                       ArtificialIntelligence.mq4 |
//|                               Copyright й 2006, Yury V. Reshetov |
//|                                         http://reshetov.xnet.uz/       |
//+------------------------------------------------------------------+
#property copyright "Copyright й 2006, Yury V. Reshetov ICQ:282715499  http://reshetov.xnet.uz/"
#property link      "http://reshetov.xnet.uz/"
//---- input parameters
extern int    x1 = 120;
extern int    x2 = 172;
extern int    x3 = 39;
extern int    x4 = 172;
// StopLoss level
extern double sl = 50;
extern double lots = 0.1;
extern int    MagicNumber = 888;
static int prevtime = 0;
static int spread = 3;
//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
  {
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
  {
   if(Time[0] == prevtime)
       return(0);
   prevtime = Time[0];
//----
   if(IsTradeAllowed())
     {
       spread = MarketInfo(Symbol(), MODE_SPREAD);
     }
   else
     {
       prevtime = Time[1];
       return(0);
     }
   int ticket = -1;
   // check for opened position
   int total = OrdersTotal();
   for(int i = 0; i < total; i++)
     {
       OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
       // check for symbol & magic number
       if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
         {
           int prevticket = OrderTicket();
           // long position is opened
           if(OrderType() == OP_BUY)
             {
               // check profit
               if(Bid > (OrderStopLoss() + (sl * 2  + spread) * Point))
                 {
                   if(perceptron() < 0)
                     {
                       // reverse
                       ticket = OrderSend(Symbol(), OP_SELL, lots * 2, Bid, 3,
                                          Ask + sl * Point, 0, "AI", MagicNumber,
                                          0, Red);
                       Sleep(30000);
                       if(ticket < 0)
                         {
                           prevtime = Time[1];
                         }
                       else
                         {
                           OrderCloseBy(ticket, prevticket, Blue);
                         }
                     }
                   else
                     {
                       // trailing stop
                       if(!OrderModify(OrderTicket(), OrderOpenPrice(),
                          Bid - sl * Point, 0, 0, Blue))
                         {
                           Sleep(30000);
                           prevtime = Time[1];
                         }
                     }
                 }
               // short position is opened
             }
           else
             {
               // check profit
               if(Ask < (OrderStopLoss() - (sl * 2 + spread) * Point))
                 {
                   if(perceptron() > 0)
                     {
                       // reverse
                       ticket = OrderSend(Symbol(), OP_BUY, lots * 2, Ask, 3,
                                          Bid - sl * Point, 0, "AI", MagicNumber,
                                          0, Blue);
                       Sleep(30000);
                       if(ticket < 0)
                         {
                           prevtime = Time[1];
                         }
                       else
                         {
                           OrderCloseBy(ticket, prevticket, Blue);
                         }
                     }
                   else
                     {
                       // trailing stop
                       if(!OrderModify(OrderTicket(), OrderOpenPrice(),
                          Ask + sl * Point, 0, 0, Blue))
                         {
                           Sleep(30000);
                           prevtime = Time[1];
                         }
                     }
                 }
             }
           // exit
           return(0);
         }
     }
   // check for long or short position possibility
   if(perceptron() > 0)
     {
       //long
       ticket = OrderSend(Symbol(), OP_BUY, lots, Ask, 3, Bid - sl * Point, 0,
                      "AI", MagicNumber, 0, Blue);
       if(ticket < 0)
         {
           Sleep(30000);
           prevtime = Time[1];
         }
     }
   else
     {
       // short
       ticket = OrderSend(Symbol(), OP_SELL, lots, Bid, 3, Ask + sl * Point, 0,
                      "AI", MagicNumber, 0, Red);
       if(ticket < 0)
         {
           Sleep(30000);
           prevtime = Time[1];
         }
     }
//--- exit
   return(0);
  }
//+------------------------------------------------------------------+
//|  The PERCEPRRON - a perceiving and recognizing function          |
//+------------------------------------------------------------------+
double perceptron()
  {
   double w1 = x1 - 100.0;
   double w2 = x2 - 100.0;
   double w3 = x3 - 100.0;
   double w4 = x4 - 100.0;
   double a1 = iAC(Symbol(), 0, 0);
   double a2 = iAC(Symbol(), 0, 7);
   double a3 = iAC(Symbol(), 0, 14);
   double a4 = iAC(Symbol(), 0, 21);
   return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4);
  }
//+------------------------------------------------------------------+}

Осталось только подобрать весовые коэффициенты линейного уравнения разделительной плоскости для нейросети. Запустим тестер стратегий нажатием на клавиши Ctrl + R:

Во вкладке Settings выберем модель эмуляции рынка быстрым методом только по ценам открытия (ведь у нас в советнике сигналы считываются только по сформировавшимся барам). Поставим галочки на Recalculate и Optimization. После чего нажмем кнопку Expert properties.

Вкладка Testing:

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

Вкладка Inputs:

Поставим галочки на подбираемых генетическим алгоритмом входных параметрах: x1, x2, x3 и x4 для весовых коэффициентов нейросети, а также нам понадобится подобрать приемлемое значение sl — уровень stoploss. Количество лотов примем за 1, а магический номер оставим по умолчанию.

Вкладка Optimization:

Чтобы ускорить процесс оптимизации, максимальную просадку поставим на уровень 35%. Для уточнения приемлемого уровня максимальной просадки, приходится сначала запускать процесс оптимизации без всяких ограничений. И лишь после того, как появится первый результат оптимизации, необходимо взять из него значение, округлить его в большую сторону и остановив процесс, ввести ограничение. Запущенная снова оптимизация пойдет гораздо быстрее.

Нажмем кнопку OK, чтобы закрыть вкладку настроек эксперта. Теперь можно запускать процесс оптимизации нажатием на кнопку Start. Также не помешает отключить вывод бесполезных результатов:

      

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

      

На Pentium III весь процесс оптимизации проходит примерно чуть более часа. Время зависит от финансового инструмента.

Осталось только кликнуть правой клавишей по верхней строчке и, выбрав во всплывшем меню пункт Set Input Parameters, запустить тестирование на исторических данных.

Можно не сомневаться, что результаты тестирования будут совпадать с тем, что выдал оптимизатор.

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

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

Запустим процесс оптимизации. Получим результаты для входных параметров: x1 = 146, x2 = 25, x3 = 154, x4 = 121, sl = 45.


А теперь проверим полученную стратегию за тот период, на котором она не оптимизировалась и не могла ничего предполагать, то есть три месяца после периода оптимизации. Просто уберем галочку с «Use date» и посмотрим на то, как стратегия продолжит торговать:


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

Выводы

Хорошо, а как же тогда относиться к выводам относительно нейросетей сделанным Д. Катцем и Д. Маккормиком в их книге «Энциклопедия торговых стратегий»?

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

  • Постановка задачи Д. Катца и Д. Маккормика заключалась в том, чтобы создать некий в природе несуществующий индикатор, а точнее обращенный во времени стохастик с медленным %K, который, по сути, представляет из себя машину времени, то есть берет информацию на 10 баров вперед и по ним выдает свои показания на 10 баров назад. Будь у меня такой индикатор, Билл Гейтс и Джордж Сорос вряд ли бы могли со мной конкурировать;
  • Следующий шаг заключался в том, чтобы взять некие данные и получить по ним предсказания этого самого стохастика с телепатическими способностями. Проще говоря, они ставили уже задачу аппроксимации, то  есть зная аргументы функции, получать ее значение. Аппроксимация — это самая что ни на есть подгонка, про которую Катц и Маккормик столь многозначительно с умным видом рассуждают на страницах своего «фолианта»;
  • Не суть важно, как была получена аппроксимация, сколь важно то, что для этой цели нейронные сети в основе своей не пригодны. Гораздо проще было бы выполнить ту же самую задачу, например, через спектральный анализ;
  • Еще хуже нейронные сети ведут себя в задачах интерполяции и экстраполяции, а если брать данные вне репрезентативной выборки, то экстраполяция налицо, когда пытаются вычислить значение, а не принадлежность объекта к какому либо классу;
  • Получив некую аппроксимацию телепатического стохастика, в реализации которой имелись явные погрешности, Катц и Маккормик пошли дальше и по показаниям этого самого «кривого» прибора создали «торговую стратегию», которая должна была еще и интерпретировать  показания заведомо неисправного прибора,, а именно, если %K зашкаливает за некие пределы, то вероятно котировки достигли максимума или минимума. После чего вся эта мишура была загнана в механическую торговую систему и, получив на ней статистику и скороспелые выводы, авторы предложили ознакомиться с ними читателей.

Результаты не заставили себя ждать. Если в репрезентативных выборках еще кое что получалось, то вне их вероятность извлечения прибыли сводилась к извлечению только убытков.

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

Впрочем, провал задуманных экспериментов с нейронными сетями был не только у Катца и Маккормика. Первый проект нейронной сети под названием «Перцептрон» не оправдал надежд, которые на него возлагались. У Франкенштейна результат, как и первый блин, вышел комом. Более поздний объективный анализ, того, что могут нейросети  и того, что им явно не по зубам, был сделан М. Минским и С. Пейпертом [1]. Поэтому, прежде, чем приступать к решению тех или иных задач на базе нейросетей, постарайтесь не наступать на уже известные грабли:

  1. Постановка задачи не должна содержать телепатических прогнозов на будущее с целью получить точный ответ на вопрос о том, когда и сколько. Следует ограничить решение в виде идентификации ситуации по текущим признакам в виде разделения всего на несколько взаимоисключающих потенциальных ситуаций. Например, если задача о погоде, то не старайтесь узнать, когда с точностью до секунды пойдет дождь и каков будет уровень осадков в миллиметрах. Ограничьтесь прогнозом  о потенциальной ситуации в виде перемены погоды в сторону солнечной либо  дождливой;
  2. Вырезайте все лишнее «Бритвой Оккама» .  Некоторые экспериментаторы полагают, что чем больше слоев у нейросети и чем загогулистей ее функции активации, тем лучше результат. На самом деле, конечно, же таким образом можно более точно провести границу, разделяющую идентифицируемые объекты по их признакам. С этим никто не спорит. Но зачем? Ведь такой подход равносилен построению замков из песка. Если бы граница имела  жесткие очертания, неизменные во времени и не зависимые от других обстоятельств, тогда усложнение с целью максимального уточнения  имело бы смысл. Но основная часть, задач решаемых с помощью нейросетей, к такому разряду не относится. Финансовые инструменты тоже не стоят на месте. А значит простейшая нейронная сеть с небольшим количеством входов и одним слоем может оказаться более приемлемой, нежели сложнейшая по конструкции «хламида» с одноразовой результативностью.

Вот собственно и все, что я хотел сказать в этой статье.

Литература

  1. Minsky, M. and Papert, S. (1969) PERCEPTRON; an Introduction to Computational Geometry, MIT Press, Massachussetts (имеется перевод: Минский М., Пейперт С. Персептроны: Пер. с англ. — М.: Мир, 1971 — с. 261).
Прикрепленные файлы:
 ArtificialIntelligence.mq4 (6.0 Kb)
Источник: mql4.com

Оставить комментарий

Чтобы оставлять комментарии Вы должны быть авторизованы.

Похожие посты