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

Josh Readhead | 25 января, 2013

Что такое "Метод опорных векторов"?

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

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

По существу, метод опорных векторов является методом входов/выходов. Пользователь вводит входные данные, и на основе разработанной (при помощи обучения) модели получает выходные результаты. Теоретически, число входов для метода опорных векторов лежит в диапазоне от одного до бесконечности. Однако, в практическом применении, есть определенные ограничения на размер входной выборки, которые зависят от вычислительной мощности. Например, пусть для конкретного применения метода опорных векторов используются N входов (N - целое число, в диапазоне от 1 до бесконечности). Тогда задача метода опорных векторов заключается в том, чтобы сопоставить все входные данные размерности N и найти такую гиперплоскость размерности N-1, которая наилучшим образом разделяла бы обучающую выборку.

Метод входов/выходов

Рисунок 1. Метод опорных векторов - метод входов/выходов

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

Разделяющая гиперплоскость

Рисунок 2. Слева: Входные данные метода опорных векторов на двухмерном графике. Красными кружками и синими крестиками обозначаются два различных класса данных.

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

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

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

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

Нахождение оптимальной разделяющей гиперплоскости регрессионным алгоритмом метода опорных векторов

Рисунок 4. На анимации показан процесс тренировки модели методом опорных векторов. Гиперплоскость постепенно сходится к идеальной геометрической форме кривой для разделения двух различных классов данных

Большие размерности

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

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


Пример использования "Метода опорных векторов": это Шняк?

Представьте себе такой гипотетический сценарий, что вы занимаетесь исследованием редких диких животных, которые обитают только в глубинах Арктики и называются Шняками. За все время была найдена только небольшая горстка из этих животных, учитывая их удаленность (скажем около 5000). Как исследователя вас мучает вопрос - как же я могу узнать Шняка?

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

Как мы можем использовать эту информацию для того чтобы понять, что новое животное является Шняком?

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

Таким образом нам нужно определить, какие именно входные данные будут использоваться и как много их будет. Теоретически, мы можем использовать столько входов, сколько захотим, однако это может привести к длительному времени обучения (чем больше входов, тем больше времени занимает нахождение паттернов методом опорных векторов). К тому же, нужно выбирать такие входные значения, которые были бы относительно постоянными для всех Шняков. Например, рост или вес животного будут хорошими примерами параметров для входов, потому что вы предполагаете, что эти параметры относительно постоянны для всех Шняков. А вот средний возраст животного является плохим параметром для входа, потому что возраст разных животных значительно отличается.

По этим причинам были выбраны следующие параметры для входов:

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

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

Обучающая выборка рост [мм] вес [кг] N_legs N_eyes L_arm [мм] av_speed [м/с] f_call [Гц] Шняк (да/нет)
Пример 1 1030 45 8 3 420 2.1 14000 ДА
Пример 2 1010 42 8 3 450 2.2 14000 ДА
Пример 3 900 40 7 6 600 6 13000 НЕТ
Пример 4 1050 43 9 4 400 2.4 12000 ДА
Пример 5 700 35 2 8 320 21 13500 НЕТ
Пример 6 1070 42 8 3 430 2.4 12000 ДА
Пример 7 1100 40 8 3 430 2.1 11000 ДА
Пример N ... ... ... ... ... ... ... ...

Таблица 1. Таблица примеров наблюдений за Шняками

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


Работает ли "Метод опорных векторов"?

Для этой задачи мною был написан скрипт, который тестирует насколько хорошо модель, построенная методом опорных векторов, может распознать нового Шняка. Для этого скрипт использует библиотеку функций "Support Vector Machine Learning Tool", которую можно загрузить в MQL5 Маркете.

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

Параметр Нижняя граница Верхняя граница
рост [мм] 1000 1100
вес [кг] 40 50
N_legs 8 10
N_eyes 3 4
L_arm [мм] 400 450
av_speed [м/с] 2 2.5
f_call [Гц] 11000 15000

Таблица 2. Параметры для определения Шняков

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

//+------------------------------------------------------------------+
//| Эта функция получает характеристики наблюдаемого животного и на
//| основе критерия, который мы выбрали, возвращает true/false 
//| в зависимости от того, является ли животное Шняком или нет
//+------------------------------------------------------------------+
bool isItASchnick(double height,double weight,double N_legs,double N_eyes,double L_arm,double av_speed,double f_call)
  {
   if(height   < 1000  || height   > 1100)  return(false); //если рост находится за пределами отрезка > return(false)
   if(weight   < 40    || weight   > 50)    return(false); //если вес находится за пределами отрезка > return(false)
   if(N_legs   < 8     || N_legs   > 10)    return(false); //если параметр N_Legs находится за пределами отрезка > return(false)
   if(N_eyes   < 3     || N_eyes   > 4)     return(false); //если параметр the N_eyes находится за пределами отрезка > return(false)
   if(L_arm    < 400   || L_arm    > 450)   return(false); //если параметр L_arm  находится за пределами отрезка > return(false)
   if(av_speed < 2     || av_speed > 2.5)   return(false); //если параметр av_speed находится за пределами отрезка > return(false)
   if(f_call   < 11000 || f_call   > 15000) return(false); //если параметр f_call находится за пределами отрезка > return(false)
   return(true);                                           //в противном случае > return(true)
  }

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

//+------------------------------------------------------------------+
//| Эта функция получает пустые массивы типа double и bool, и
//| генерирует входные/выходные данные, используемые при обучении SVM
//+------------------------------------------------------------------+ 
void genTrainingData(double &inputs[],bool &outputs[],int N)
  {
   double in[];                    //создание пустого массива типа double для временного хранения входов
   ArrayResize(in,N_Inputs);       //изменение размера массива in[] до N_Inputs
   ArrayResize(inputs,N*N_Inputs); //изменение размера массива inputs[] до N*N_Inputs 
   ArrayResize(outputs,N);         //изменение массива outputs[] до N 
   for(int i=0;i<N;i++)
     {
      in[0]=    randBetween(980,1120);    //Создание случайного входа для роста
      in[1]=    randBetween(38,52);       //Создание случайного входа для веса
      in[2]=    randBetween(7,11);        //Создание случайного входа для N_legs
      in[3]=    randBetween(3,4.2);       //Создание случайного входа для N_eyes
      in[4]=    randBetween(380,450);     //Создание случайного входа для L_arms
      in[5]=    randBetween(2,2.6);       //Создание случайного входа для av_speed
      in[6]=    randBetween(10500,15500); //Создание случайного входа для f_call

      //--- копирование новых случайных входов в массив входов обучающей выборки
      ArrayCopy(inputs,in,i*N_Inputs,0,N_Inputs);
      //--- оценка случайных входов и определение, является ли животное Шняком
      outputs[i]=isItASchnick(in[0],in[1],in[2],in[3],in[4],in[5],in[6]);
     }
  }
//+------------------------------------------------------------------+
//| Эта функция используется для создания случайного значения между 
//| t1 и t2
//+------------------------------------------------------------------+ 
double randBetween(double t1,double t2)
  {
   return((t2-t1)*((double)MathRand()/(double)32767)+t1);
  }

Теперь у нас есть обучающая выборка и пришло время создать наш классификатор методом опорных векторов, используя библиотеку "Support Vector Machine Learning Tool", доступную в MQL5 Маркете. Создадим новый объект метода опорных векторов и передадим ему обучающую выборку для выполнения обучения.

void OnStart()
  {
   double inputs[];           //пустой массив типа double, который будет использоваться для создания входов обучающей выборки 
   bool   outputs[];          //пустой массив типа bool, который будет использоваться для создания выходов обучающей выборки 
   int N_TrainingPoints=5000; //определяет размер генерируемой обучающей выборки
   int N_TestPoints=5000;     //определяет количество элементов выборки, которые будут использованы при тестировании

   genTrainingData(inputs,outputs,N_TrainingPoints); // определяет входы и выходы, которые будут использованы для тренировки svm

   int handle1=initSVMachine();             //инициализация нового объекта метода опорных векторов и возврат хэндла
   setInputs(handle1,inputs,7);             //отправляет входные данные (без ошибок) в объект метода опорных векторов
   setOutputs(handle1,outputs);             //отправляет выходные данные (без ошибок) в объект метода опорных векторов
   setParameter(handle1,OP_TOLERANCE,0.05); //установка параметра допустимой ошибки <5%
   training(handle1);                       //тренировка объекта метода опорных векторов используя входные/выходные данные 
  }

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

//+------------------------------------------------------------------+
//| Эта функция получает хэндл обученного SVM и проверяет на сколько
//| успешно он классифицирует новые входные данные
//+------------------------------------------------------------------+ 
double testSVM(int handle,int N)
  {
   double in[];
   int atrue=0;
   int afalse=0;
   int N_correct=0;
   bool Predicted_Output;
   bool Actual_Output;
   ArrayResize(in,N_Inputs);
   for(int i=0;i<N;i++)
     {
      in[0]=    randBetween(980,1120);    //Создание случайного входа для роста
      in[1]=    randBetween(38,52);       //Создание случайного входа для веса
      in[2]=    randBetween(7,11);        //Создание случайного входа для N_legs
      in[3]=    randBetween(3,4.2);       //Создание случайного входа для N_eyes
      in[4]=    randBetween(380,450);     //Создание случайного входа для L_arms
      in[5]=    randBetween(2,2.6);       //Создание случайного входа для av_speed
      in[6]=    randBetween(10500,15500); //Создание случайного входа для f_call
      
      //--- использует функцию isItASchnick для определения фактического желаемого результата
      Actual_Output=isItASchnick(in[0],in[1],in[2],in[3],in[4],in[5],in[6]);
      //--- использование обученного SVM для возврата прогноза.
      Predicted_Output=classify(handle,in);
      if(Actual_Output==Predicted_Output)
        {
         N_correct++; //Эта переменная хранит число верных прогнозов.
        }
     }
//--- возвращает точность обученного SVM в процентах
   return(100*((double)N_correct/(double)N));
  }

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


Почему "Метод опорных векторов" так полезен?

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

Одна из лучших особенностей метода опорных векторов заключается в том, что он очень хорошо справляется с ошибками и шумами в данных. Зачастую метод в состоянии найти основной паттерн внутри данных и отфильтровать выбросы и другие сложности. Например, в задаче об исследовании можно столкнуться с множеством исследовательских работ, в которых характеристики Шняков значительно отличаются от других работ (вес Шняка - 200 кг, рост - 15 000 мм).

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

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

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

//+------------------------------------------------------------------+
//| Эта функция получает корректные входные и выходные данные 
//| обучающей выборки и вводит N случайных ошибок в них
//+------------------------------------------------------------------+ 
void insertRandomErrors(double &inputs[],bool &outputs[],int N)
  {
   int nTrainingPoints=ArraySize(outputs); //вычисляет размер обучающей выборки
   int index;                              //создание целочисленной переменной 'index'
   bool randomOutput;                      //сооздание новой переменной 'randomOutput' типа 'bool'
   double in[];                            //создание пустого массива типа double для временного хранения входов
   ArrayResize(in,N_Inputs);               //изменение размера массива in[] до N_Inputs
   for(int i=0;i<N;i++)
     {
      in[0]=    randBetween(980,1120);    //Создание случайного входа для роста
      in[1]=    randBetween(38,52);       //Создание случайного входа для веса
      in[2]=    randBetween(7,11);        //Создание случайного входа для N_legs
      in[3]=    randBetween(3,4.2);       //Создание случайного входа для N_eyes
      in[4]=    randBetween(380,450);     //Создание случайного входа для L_arms
      in[5]=    randBetween(2,2.6);       //Создание случайного входа для av_speed
      in[6]=    randBetween(10500,15500); //Создание случайного входа для f_call
      
      //--- случайным образом выбирается один из обучающих входов для добавления в него ошибки
      index=(int)MathRound(randBetween(0,nTrainingPoints-1));
      //--- генерируем случайное значение типа bool для создания ошибки
      if(randBetween(0,1)>0.5) randomOutput=true;
      else                     randomOutput=false;
      
      //--- копирование новых случайных входов в массив входов обучающей выборки
      ArrayCopy(inputs,in,index*N_Inputs,0,N_Inputs);
      //--- копирование новых случайных выходов в массив выходов обучающей выборки
      outputs[index]=randomOutput;
     }
  }

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

void OnStart()
  {
   double inputs[];           //пустой массив типа double, который будет использоваться для создания входов обучающей выборки 
   bool   outputs[];          //пустой массив типа bool, который будет использоваться для создания выходов обучающей выборки 
   int N_TrainingPoints=5000; //определяет размер генерируемой обучающей выборки
   int N_TestPoints=5000;     //определяет количество элементов выборки, которые будут использованы при тестировании

   genTrainingData(inputs,outputs,N_TrainingPoints); // определяет входы и выходы, которые будут использованы для тренировки svm

   int handle1=initSVMachine();             //инициализация нового объекта метода опорных векторов и возврат хэндла
   setInputs(handle1,inputs,7);             //отправляет входные данные (без ошибок) в объект метода опорных векторов
   setOutputs(handle1,outputs);             //отправляет выходные данные (без ошибок) в объект метода опорных векторов
   setParameter(handle1,OP_TOLERANCE,0.05); //установка параметра допустимой ошибки <5%
   training(handle1);                       //тренировка объекта метода опорных векторов используя входные/выходные данные 

   insertRandomErrors(inputs,outputs,500);  //берем оригинальные входы/выходы и добавляем в них случайные ошибки

   int handle2=initSVMachine();             //инициализация нового объекта метода опорных векторов и возврат хэндла
   setInputs(handle2,inputs,7);             //отправляет входные данные (с ошибками) в объект метода опорных векторов
   setOutputs(handle2,outputs);             //отправляет выходные данные (с ошибками) в объект метода опорных векторов
   setParameter(handle2,OP_TOLERANCE,0.05); //установка параметра допустимой ошибки <5%
   training(handle2);                       //тренировка объекта метода опорных векторов используя входные/выходные данные 

   double t1=testSVM(handle1,N_TestPoints); //тестирование на точность тренированного объекта и сохранение результатов в t1
   double t2=testSVM(handle2,N_TestPoints); //тестирование на точность тренированного объекта и сохранение результатов в t2

   Print("Точность SVM составила ",NormalizeDouble(t1,2),"% (использована обучающая выборка без ошибок)");
   Print("Точность SVM составила ",NormalizeDouble(t2,2),"% (использована обучающая выборка с ошибками)");
   deinitSVMachine(); //Очистка всей памяти, использованной при генерации SVM для избежания ее утечки
  }

В процессе выполнения скрипт выводит следующие результаты в лог эксперта. При подготовке обучающей выборки размером в 5000 элементов мы ввели в нее 500 случайных ошибок. Сравнивая результаты работы метода на зашумленных и оригинальных данных, мы получим, что качество распознавания уменьшилось менее чем на 1%. Это произошло потому что метод опорных векторов игнорирует выбросы в обучающей выборке при обучении, и по-прежнему способен строить впечатляюще точную модель. Это говорит о том, что потенциально метод может быть более полезным в извлечении сложных паттернов из зашумленных данных.

Лог Эксперта

Рисунок 5. Результаты работы скрипта "Schnick" в MetaTrader 5.


Демо-версия

Полную версию кода выше можно загрузить из Code Base, однако запустить скрипт можно будет только в своем терминале, если вы приобрели полную версию "Support Vector Machine Learning Tool" в MQL5 Маркете. Если же вы загрузили демо-версию, то использовать ее можно будет только через тестер стратегий. Для того чтобы протестировать код "Schnick", используя демо-версию, я переписал скрипт в эксперт, который можно запустить через тестер стратегий. Обе эти версии кода можно загрузить по ссылкам ниже:


Как можно использовать "Метод опорных векторов" для торговли?

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

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

Основа для значительного числа подходов к техническому анализу торговли включает в себя следующие шаги:

  1. Контроль за несколькими индикаторами
  2. Определение того, какие условия для каждого индикатора коррелируют с потенциально успешной торговлей
  3. Наблюдение за каждым из индикаторов и определение момента, когда все они (или большинство) сигнализируют об успешной торговле

Можно применить аналогичный подход с использованием метода опорных векторов, который бы подавал сигналы для начала торговли тем же образом. Библиотека "Support Vector Machine Learning Tool" была разработана с учетом этого. Подробное описание того, как ее использовать, можно найти в MQL5 Маркете, так что я дам только краткий обзор. Процесс использования этой библиотеки выглядит следующим образом:

Блок-схема

Рисунок 6. Блок-схема демонстрирует процесс применения утилиты метода опорных векторов в эксперте

Перед тем как вы сможете использовать Support Vector Machine Learning Tool важно понять, как генерируются обучающие входы и выходы.


Как генерируются входные данные для обучения?

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

Смещение задает количество баров между текущим баром и баром, с которого вы хотите начать генерировать обучающие входы. Размер обучающей выборки (будем обозначать N) устанавливает количество баров, которые нужно использовать в обучении. На рисунке ниже показано как нужно использовать эти значения. Значение отступа 4 и N равное 6 означают, что метод опорных векторов должен использовать в качестве обучающих входных данных только те бары, которые находятся внутри белого квадрата. Аналогичным образом значению сдвига 8 и N равному 8 соответствует использование в обучении только баров из синего квадрата.

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

Рисунок 7. Свечной график демонстрирует значения сдвига и N

Рисунок 7. Свечной график демонстрирует значения сдвига и N


Как генерируются выходные данные для обучения?

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

Первый параметр - это OP_TRADE. Он может принимать значения BUY или SELL, использование которых соответствует моделированию либо покупки, либо продажи. Если значение BUY, то при генерации выходов будет учитываться только успешный результат при совершении сделок на покупку. И наоборот, если значение SELL, то будет учитываться только успешный результат при совершении сделок на продажу.

Следующие два параметра - это Stop Loss и Take Profit для совершения этих сделок. Их значения указываются в пунктах и будут задавать уровни стопов и лимитов для каждой гипотетической сделки.

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


Некоторые соображения по поводу выбора входных данных

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

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

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

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

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

  2. Вы можете использовать последние пять баров индикатора MACD в методе опорных векторов как пять отдельных входов. Это можно сделать путем инициализации пяти различных объектов индикатора MACD. Каждый индикатор может быть инициализирован с различным смещением от текущего бара. Тогда в метод опорных векторов могут быть отправлены пять хэндлов отличных друг от друга индикаторов. Следует отметить, что при втором варианте ваш эксперт будет работать дольше. Чем больше входов, тем больше времени потребуется для успешного обучения.


Использование "Метода опорных векторов" в эксперте

Я подготовил пример эксперта, который вы бы могли использовать для применения метода опорных векторов в своей собственной торговой стратегии (копию эксперта вы можете загрузить по этой ссылке https://www.mql5.com/ru/code/1229). Эксперт дает возможность немного поэкспериментировать с методом опорных векторов, я рекомендую вам копировать/изменять/модифицировать в соответствии с вашей торговой стратегией. Эксперт работает следующим образом:

  1. Создаются два новых объекта метода опорных векторов при помощи библиотеки svMachineTool. Один из них настроен на отправку сигналов на покупку, а другой на продажу.

  2. Инициализируются семь стандартных индикаторов, и их хэндлы сохраняются в массиве целых чисел (Примечание: может быть использована любая комбинация индикаторов для входов, они просто должны быть переданы в объект SVM в одном целочисленном массиве).

  3. Массив хэндлов индикаторов передается в объект метода опорных векторов.

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

  5. После того, как все входные и выходные данные сгенерированы, происходит обучение обоих объектов метода опорных векторов.

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

Инициализация и обучение методом опорных векторов выполняется в функции onInit(). Для справки, этот сегмент svTrader EA приведен ниже:

#property copyright "Copyright 2011, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#property indicator_buffers 7

//+---------Функции Support Vector Machine Learning Tool -----------+
//| Следующий оператор #import импортирует все функции
//| обучающей утилиты метода опорных векторов в эксперт. Пожалуйста 
//| обратите внимание на то, что если вы не импортируете функции, 
//| то компилятор не позволит вам использовать данные функции
//+------------------------------------------------------------------+
#import "svMachineTool.ex5"
enum ENUM_TRADE {BUY,SELL};
enum ENUM_OPTION {OP_MEMORY,OP_MAXCYCLES,OP_TOLERANCE};
int  initSVMachine(void);
void setIndicatorHandles(int handle,int &indicatorHandles[],int offset,int N);
void setParameter(int handle,ENUM_OPTION option,double value);
bool genOutputs(int handle,ENUM_TRADE trade,int StopLoss,int TakeProfit,double duration);
bool genInputs(int handle);
bool setInputs(int handle,double &Inputs[],int nInputs);
bool setOutputs(int handle,bool &Outputs[]);
bool training(int handle);
bool classify(int handle);
bool classify(int handle,int offset);
bool classify(int handle,double &iput[]);
void  deinitSVMachine(void);
#import

#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\HistoryOrderInfo.mqh>

//+-----------------------Входные параметры----------------------------+
input int            takeProfit=100;      // тейкпрофит в пунктах
input int            stopLoss=150;        // стоплосс в пунктах
input double         hours=6;             // Максимальная продолжительность торговли для вычисления обучающих выходов.
input double         risk_exp=5;          // Максимальное количество ордеров, которые могут быть выставлены одновременно
input double         Tolerance_Value=0.1; // Уровень допустимой ошибки для обучения SVM (по умолчанию 10%)
input int            N_DataPoints=100;    // Размер обучающей выборки для генерации и использования.

//+---------------------Параметры индикатора--------------------------+
//| Здесь используются только параметры индикаторов по умолчанию. Я
//| рекомендую вам поиграть с этими значениями, чтобы увидеть когда 
//| ваш эксперт работает лучше всего.                    
//+------------------------------------------------------------------+
int bears_period=13;
int bulls_period=13;
int ATR_period=13;
int mom_period=13;
int MACD_fast_period=12;
int MACD_slow_period=26;
int MACD_signal_period=9;
int Stoch_Kperiod=5;
int Stoch_Dperiod=3;
int Stoch_slowing=3;
int Force_period=13;

//+------------------Параметры эксперта------------------------+
int         tickets[];
bool        Opn_B,Opn_S;
datetime    New_Time;
int         handleB,handleS;
double      Vol=1;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
   New_Time=0;
   int handles[];ArrayResize(handles,7);
//+------------------------------------------------------------------+
//| Следующие операторы используются для инициализации индикаторов,
//| которые будут использоваться в методе опорных векторов. 
//| Возвращаемые хэндлы сохраняются в массив типа int. Я использовал
//| стандартные индикаторы, однако вы также можете использовать и 
//| желаемые для вас пользовательские индикаторы
//+------------------------------------------------------------------+
   handles[0]=iBearsPower(Symbol(),0,bears_period);
   handles[1]=iBullsPower(Symbol(),0,bulls_period);
   handles[2]=iATR(Symbol(),0,ATR_period);
   handles[3]=iMomentum(Symbol(),0,mom_period,PRICE_TYPICAL);
   handles[4]=iMACD(Symbol(),0,MACD_fast_period,MACD_slow_period,MACD_signal_period,PRICE_TYPICAL);
   handles[5]=iStochastic(Symbol(),0,Stoch_Kperiod,Stoch_Dperiod,Stoch_slowing,MODE_SMA,STO_LOWHIGH);
   handles[6]=iForce(Symbol(),0,Force_period,MODE_SMA,VOLUME_TICK);

//----------Инициализация, настройка и обучение методом опорных векторов для сигналов на покупку----------
   handleB=initSVMachine();                               // инициализация нового SVM и сохранение его хэндла в 'handleB'
   setIndicatorHandles(handleB,handles,0,N_DataPoints); // отправка инициализированных индикаторов в SVM с желаемым отступом 
                                                                // и размером обучающей выборки
   setParameter(handleB,OP_TOLERANCE,Tolerance_Value);  // установка максимально допустимой ошибки для обучения SVM
   genInputs(handleB);                                     // Генерация входов с использованием инициализированных индикаторов
   genOutputs(handleB,BUY,stopLoss,takeProfit,hours);   // Генерация выходов на основе желаемых параметров для получения гипотетических сделок

//----------Инициализация, настройка и обучение методом опорных векторов для сигналов на продажу----------
   handleS=initSVMachine();                               // инициализация нового SVM и сохранение его хэндла в 'handleS'
   setIndicatorHandles(handleS,handles,0,N_DataPoints); // отправка инициализированных индикаторов в SVM с желаемым отступом 
                                                                // и размером обучающей выборки
   setParameter(handleS,OP_TOLERANCE,Tolerance_Value);  // установка максимально допустимой ошибки для обучения SVM
   genInputs(handleS);                                  // Генерация входов с использованием инициализированных индикаторов
   genOutputs(handleS,SELL,stopLoss,takeProfit,hours);  // Генерация выходов на основе желаемых параметрах для получения гипотетических сделок
//----------
   training(handleB);   // Выполнение обучения методом опорных векторов для сигналов на покупку
   training(handleS);   // Выполнение обучения методом опорных векторов для сигналов на продажу   
   return(0);
  }


Дополнительная возможность

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

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

Входные данные: Входные данные нужно отправлять в SVM как одномерный массив типа double. Обратите внимание, что любое входное значение, которое вы создали, должно быть передано как число типа double. Переменные типа bool, int и т.д. - все они должны быть конвертированы в значения типа double, до того как они будут отправлены в метод опорных векторов. Входные данные нужно передавать в следующей форме. Например, предположим, что мы передаем входные данные с 3 входами и размером выборки равным 5. Для этого наш массив типа double должен включать в себя 15 элементов в следующем формате:

| A1 | B1 | C1 | A2 | B2 | C2 | A3 | B3 | C3 | A4 | B4 | C4 | A5 | B5 | C5 |

Также необходимо отправить количество входов. В нашем случае, N_Inputs=3.

Выходные данные: выходные данные передаются как массив типа bool. Эти желаемые выходные значения для SVM должны соответствовать каждому элементу из входных данных обучающей выборки. В приведенном выше примере у нас есть пять элементов в обучающей выборке. В этом случае, мы передадим массив выходов типа bool c 5 значениями.

Общие замечания: