机器学习:支持向量机如何应用于交易

Josh Readhead | 4 三月, 2014

何为“支持向量机”?

支持向量机是一种机器学习的方法,尝试将输入的数据分为两类。要使支持向量机生效,首先要使用一系列训练输入和输出数据来建立支持向量机的模型(用于新数据分类)。

支持向量机模型是这样建立的:获取训练输入,将其映射至多维空间,使用回归算法找到可最佳分离两类输入数据的超平面(超平面是一个在 n 维空间中将空间划分为两个半空间的一个平面)。一旦支持向量机完成了受训,它就可以评估有关划分超平面的新输入数据,并将划分为其中一类。

支持向量机本质上是一个输入/输出型机器。用户可以输入一个数据,以训练建立的模型为基础,返回一个输出结果。理论上,任何给定支持向量机可支持的输入数据的数量范围为 1 到无穷大;然而在实际应用中,运算能力确实成为了输入数据使用量的瓶颈。举例说,有 N 个输入数据用于某一特定支持向量机(N 取 1 至无穷大之间的自然数),支持向量机就必须将每一个输入数据组映射至 N 维空间,并找到最能分离训练数据的 N-1 维超平面。

输入/输出型机器

图1. 支持向量机是输入/输出型机器

将支持向量机工作方式概念化的最好办法就是以二维空间为例。假设我们想创建这样一个支持向量机:有两个输入参数,返回一个单一的输出结果(将数据点归类为属于两种类别之一)。我们可以通过在下方所示的 2 维图表上进行标绘来展现这一点:

分割超平面

图 2. 左: 支持向量机的输入数据映射至 2D 图表。红圈和蓝叉用以表示两类输入参数。

图 3. 右:支持向量机的输入数据映射至 2D 图表。红圈和蓝叉用以表示两类输入参数,黑色线表示划分超平面。

在这个例子中,蓝叉表示属于类型 1 的数据点,红圈表示属于类型 2 的数据点。每一个数据点拥有唯一的输入 1 值(对应于 x 轴坐标)和输入 2 值(对应于 y 轴坐标),所有的数据点均映射到 2D 空间中。

支持向量机能够通过在 2D 空间创建数据点模型的方式对数据进行分类。通过观察 2D 空间的数据,支持向量机使用回归算法找到一个一维超平面(也称为线),该平面能最精确地将数据分为两种类型。然后支持向量机用这条分割线把新数据点分为 1 类或 2 类。

下面的动态图展示了一个新的支持向量机“受训”的过程。算法将随机寻找一个分割超平面,然后反复地改进超平面的精确度。如您所见,一开始算法波动非常大,然后逐渐放慢速度以趋向所期望的解决方案。

支持向量机回归算法正在寻找最优分割超平面

图 4. 一个动态图,显示了支持向量机的受训过程。超平面逐步收敛以达到几何式区分两类不同的数据

更高维度

上文展示的二维空间的情形让我们得以将支持向量机的处理过程可视化,然而它只能使用两个输入数据来对数据点分类。如果要使用更多的输入数据呢?庆幸的是,支持向量机算法可以让我们在更高维度做同样的事情,尽管在概念上更难解释一些。

思考一下:你希望创建获取 20 个输入数据并且能够使用这些输入把任何数据点划分为类型 1 或类型 2。要做到这一点,支持向量机要将数据在 20 维空间建模,同时要用一个回归算法找到一个将数据点区分为两类的 19 维超平面。要对上述情形进行可视化是极其困难的,因为我们很难理解三维以上空间。不管怎样,您只需知道它的工作原理与二维空间完全相同。


支持向量机的工作原理示例:这是 Schnick 吗?

想象一个场景,您是一个研究人员,正在调查一种稀有的动物叫做 Schnicks,该动物只在北极深处出没。由于这研究对象罕见,只有很少一部分被发现过(假设是 5000 只左右)。作为一个研究人员,您被这样一个问题所困惑:我怎么去认定这是一个 Schnick 呢?

您手头上只有一些见过该动物的其他研究员发表的论文。而这些研究论文中,作者对他们发现的 Schnicks 的某些特征进行了描述,包括身高、体重、肢体数量等等。不过每一篇论文的特征描述都各不相同,而且没有明显的模式……

我们如何用这些数据来确定一只新的动物是 schnick 呢?

支持向量机就有可能成为解决该问题的方案之一。利用支持向量机区分数据的模式,创建一个框架模型以区分动物是 Schnick 还是非 Schnick。第一步就是创建一系列的数据,用于训练你的支持向量机分辨 schnick。训练数据是一系列的输入参数,为支持向量机匹配输出结果,以分析并从中提取模式。

因此,我们必须决定哪些输入参数要用到,以及其数量。理论上说,我们可以获取所希望数量的输入参数,然而这将经常减缓训练的速度(参数越多,支持向量机提取模式的时间越长)。同时,你希望选取在所有 schnick 中较为一致的输入值。比如,动物的身高或体重将是一个很好的输入选择,因为您期望所有的 schnick 都在这些数值上保持相对一致。然而,平均年龄将不是个好选择,因为您知道,所辨认的动物年龄可能差距非常大。

因此,以下的输入参数是我们选择的:

选择了以上的输入参数,我们开始编译熟练数据。有效的训练数据对于支持向量机来说必须满足以下特定条件:

在这个案例中,我们获得了科学家的研究论文,这些论文已经成功地辨认出 schnick 并列出了它的相关属性。因此我们可以阅读研究论文并提取每一输入的数据,然后对每一案例分配真或假的输出结果。本例中的训练数据可能与下表类似。

训练采样 身高 [mm] 体重 [kg] N_legs N_eyes L_arm [mm] av_speed [m/s] f_call [Hz] Schnick (true/false)
示例 1 1030 45 8 3 420 2.1 14000 TRUE
示例 2 1010 42 8 3 450 2.2 14000 TRUE
示例 3 900 40 7 6 600 6 13000 FALSE
示例 4 1050 43 9 4 400 2.4 12000 TRUE
示例 5 700 35 2 8 320 21 13500 FALSE
示例 6 1070 42 8 3 430 2.4 12000 TRUE
示例 7 1100 40 8 3 430 2.1 11000 TRUE
示例 N ... ... ... ... ... ... ... ...

表 1. schnick 数据例表

一旦我们收集了所有的训练输入和输出数据,我们就可以用它训练我们的支持向量机。在训练过程中,支持向量机会创建一个七维模型,用以将各种训练示例分类为 ture 或 false。支持向量机会不断创建,直到获得精确表示训练数据(在指定误差容许范围之内)的模型。一旦这项训练结束,该模型就可以用来处理新的数据点,按照 true 或 false 归类。


支持向量机到底工作了吗?

借助 Schnick 案例,我写了一个脚本,测试支持向量机实际上是如何很好地处理新的 schnick 的。为了完成这个测试,我使用了“支持向量机学习工具”函数库(可以在“应用市场”中下载)。

为了针对这个案例有效建模,我们首先决定哪些是 Schnick 的实际属性。我假定的这些属性已经列在了下表中。如果一只动物满足以下所有标准,那么它就是 Schnick……

参数 下限 上限
身高 [mm] 1000 1100
体重 [kg] 40 50
N_legs 8 10
N_eyes 3 4
L_arm [mm] 400 450
av_speed [m/s] 2 2.5
f_call [Hz] 11000 15000

表 2. 定义 schnick 的参数汇总

现在我们定义了 Schnick,我们可以用这种定义来试验支持向量机。第一步是创建一个函数:能获取指定动物的 7 个输入,然后返回是否为 schnick 的动物实际分类结果。这个函数将用来生成支持向量机的训练数据,同时在结束的时候评估支持向量机的性能。可以用以下的函数完成:

//+------------------------------------------------------------------+
//| This function takes the observation properties of the observed 
//| animal and based on the criteria we have chosen, returns true/false whether it is a schnick
//+------------------------------------------------------------------+
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);   // If the height is outside the parameters > return(false)
   if(weight   < 40    || weight   > 50)    return(false);   // If the weight is outside the parameters > return(false)
   if(N_legs   < 8     || N_legs   > 10)    return(false);   // If the N_Legs is outside the parameters > return(false)
   if(N_eyes   < 3     || N_eyes   > 4)     return(false);   // If the N_eyes is outside the parameters > return(false)
   if(L_arm    < 400   || L_arm    > 450)   return(false);   // If the L_arm  is outside the parameters > return(false)
   if(av_speed < 2     || av_speed > 2.5)   return(false);   // If the av_speed is outside the parameters > return(false)
   if(f_call   < 11000 || f_call   > 15000) return(false);   // If the f_call is outside the parameters > return(false)
   return(true);                                             // Otherwise > return(true)
  }

下一步就是创建一个可生成训练输入和输出的函数。本例中的输入,将通过在 7 个输入值的各自设置范围内创建随机数字的方式生成。然后针对每组随机生成的输入,再使用上面的 isItASchnick() 函数生成希望的输出结果。这在下述函数中完成: 

//+------------------------------------------------------------------+
//| This function takes an empty double array and an empty boolean array,
//| and generates the inputs/outputs to be used for training the SVM
//+------------------------------------------------------------------+ 
void genTrainingData(double &inputs[],bool &outputs[],int N)
  {
   double in[];                    // Creates an empty double array to be used for temporarily storing the inputs generated
   ArrayResize(in,N_Inputs);       // Resize the in[] array to N_Inputs
   ArrayResize(inputs,N*N_Inputs); // Resize the inputs[] array to have a size of N*N_Inputs 
   ArrayResize(outputs,N);         // Resize the outputs[] array to have a size of N 
   for(int i=0;i<N;i++)
     {
      in[0]=    randBetween(980,1120);      // Random input generated for height
      in[1]=    randBetween(38,52);         // Random input generated for weight
      in[2]=    randBetween(7,11);          // Random input generated for N_legs
      in[3]=    randBetween(3,4.2);         // Random input generated for N_eyes
      in[4]=    randBetween(380,450);       // Random input generated for L_arms
      in[5]=    randBetween(2,2.6);         // Random input generated for av_speed
      in[6]=    randBetween(10500,15500);   // Random input generated for f_call
      ArrayCopy(inputs,in,i*N_Inputs,0,N_Inputs);                         // Copy the new random inputs generated into the training input array
      outputs[i]=isItASchnick(in[0],in[1],in[2],in[3],in[4],in[5],in[6]); // Assess the random inputs and determine if it is a schnick
     }
  }
//+------------------------------------------------------------------+
//| This function is used to create a random value between t1 and t2
//+------------------------------------------------------------------+ 
double randBetween(double t1,double t2)
  {
   return((t2-t1)*((double)MathRand()/(double)32767)+t1);
  }

我们现在有了一系列的输入和输出参数,现在到了利用“应用市场”的“支持向量机学习工具”创建支持向量机的时候了。只要新的支持向量机创建好,就必须要将训练输入输出参数传递给它,并执行训练。

void OnStart()
  {
   double inputs[];              // Empty double array to be used for creating training inputs
   bool   outputs[];             // Empty bool array to be used for creating training inputs
   int    N_TrainingPoints=5000; // Defines the number of training samples to be generated
   int    N_TestPoints=5000;     // Defines the number of samples to be used when testing

   genTrainingData(inputs,outputs,N_TrainingPoints); //Generates the inputs and outputs to be used for training the SVM

   int handle1=initSVMachine();             // Initializes a new support vector machine and returns a handle
   setInputs(handle1,inputs,7);             // Passes the inputs (without errors) to the support vector machine
   setOutputs(handle1,outputs);             // Passes the outputs (without errors) to the support vector machine
   setParameter(handle1,OP_TOLERANCE,0.05); // Sets the error tolerance parameter to <5%
   training(handle1);                       // Trains the support vector machine using the inputs/outputs passed
  }

现在,我们有了一个支持向量机并完成了良好的训练,它可以成功分辨 Schnick 了。为了确认这一点,我们可以测试最终支持向量机,让它对新的数据点进行分类。首先可以生成随机输入,然后使用 isItASchnick() 函数来判断这些输入是否对应实际的 Schnick,然后使用支持向量机去划分输入,并判断所预测的结果是否与实际结果相符。这在下述函数中完成: 

//+------------------------------------------------------------------+
//| This function takes the handle for the trained SVM and tests how
//| successful it is at classifying new random inputs
//+------------------------------------------------------------------+ 
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);      // Random input generated for height
      in[1]=    randBetween(38,52);         // Random input generated for weight
      in[2]=    randBetween(7,11);          // Random input generated for N_legs
      in[3]=    randBetween(3,4.2);         // Random input generated for N_eyes
      in[4]=    randBetween(380,450);       // Random input generated for L_arms
      in[5]=    randBetween(2,2.6);         // Random input generated for av_speed
      in[6]=    randBetween(10500,15500);   // Random input generated for f_call
      Actual_Output=isItASchnick(in[0],in[1],in[2],in[3],in[4],in[5],in[6]); // Uses the isItASchnick fcn to determine the actual desired output
      Predicted_Output=classify(handle,in);                                  // Uses the trained SVM to return the predicted output.
      if(Actual_Output==Predicted_Output)
        {
         N_correct++;   // This statement keeps count of the number of times the predicted output is correct.
        }
     }

   return(100*((double)N_correct/(double)N));   // Returns the accuracy of the trained SVM as a percentage
  }

我建议用上述函数中的值来检验支持向量机在不同条件下的工作方式。


为何支持向量机如此有用?

使用支持向量机从数据中提取复杂模式的好处是,并不需要对数据的行为进行事先了解。一个支持向量机可以分析数据,仅提取其自身的发现和关系。函数处理方式类似一个获取输入和生成输出的黑匣子,输出可证实对找寻数据模式大有裨益,而那些数据通常非常复杂又并且极不明了。

支持向量机最佳功能之一是在于可以极好地处理数据误差和噪音。它们经常能够发现数据内部的潜在模式、过滤数据异常值和其它复杂的情况。假设有下面的场景,在研究 Schnick 的过程中,您拿到了多篇研究论文,他们的特征描述迥然不同(比如 schnick 是 200 公斤、有 15 米高)。

这样的误差会扭曲关于 Schnick 的模型,有可能导致在对新发现的 Schnick 分类时出错。支持向量机的好处是它可以建立一种模型:它符合与满足所有训练数据点的模型相反的基本模式。它是这样做到的:模型获得一定的容错能力,使支持向量机可以忽略数据中的任何错误。

这个 Schnick 支持向量机案例中,如果我们的容错率是 5%,那么训练将尝试建立一个符合 95% 受训数据的模型。因为训练能忽略小部分的数据异常,这一点十分有用。

我们可以修改 Schnick 脚本,以进一步查明支持向量机的这项属性。已添加下列函数,以在训练数据集中引入人为产生的随机误差。此函数将随机选取训练点,并用随机变量替换输入和相应输出。

//+------------------------------------------------------------------+
//| This function takes the correct training inputs and outputs generated
//| and inserts N random errors into the data
//+------------------------------------------------------------------+ 
void insertRandomErrors(double &inputs[],bool &outputs[],int N)
  {
   int    nTrainingPoints=ArraySize(outputs); // Calculates the number of training points
   int    index;                              // Creates new integer 'index'
   bool   randomOutput;                       // Creates new bool 'randomOutput'
   double in[];                               // Creates an empty double array to be used for temporarily storing the inputs generated
   ArrayResize(in,N_Inputs);                  // Resize the in[] array to N_Inputs
   for(int i=0;i<N;i++)
     {
      in[0]=    randBetween(980,1120);        // Random input generated for height
      in[1]=    randBetween(38,52);           // Random input generated for weight
      in[2]=    randBetween(7,11);            // Random input generated for N_legs
      in[3]=    randBetween(3,4.2);           // Random input generated for N_eyes
      in[4]=    randBetween(380,450);         // Random input generated for L_arms
      in[5]=    randBetween(2,2.6);           // Random input generated for av_speed
      in[6]=    randBetween(10500,15500);     // Random input generated for f_call

      index=(int)MathRound(randBetween(0,nTrainingPoints-1)); // Randomly chooses one of the training inputs to insert an error
      if(randBetween(0,1)>0.5) randomOutput=true;             // Generates a random boolean output to be used to create an error
      else                     randomOutput=false;

      ArrayCopy(inputs,in,index*N_Inputs,0,N_Inputs);         // Copy the new random inputs generated into the training input array
      outputs[index]=randomOutput;                            // Copy the new random output generated into the training output array
     }
  }

此函数让我们在训练数据中人为引入误差。使用此带有误差的数据,我们可以创建并训练一个新的支持向量机,并将其性能与最初的向量机进行对比。

void OnStart()
  {
   double inputs[];              // Empty double array to be used for creating training inputs
   bool   outputs[];             // Empty bool array to be used for creating training inputs
   int    N_TrainingPoints=5000; // Defines the number of training samples to be generated
   int    N_TestPoints=5000;     // Defines the number of samples to be used when testing

   genTrainingData(inputs,outputs,N_TrainingPoints); // Generates the inputs and outputs to be used for training the svm

   int handle1=initSVMachine();             // Initializes a new support vector machine and returns a handle
   setInputs(handle1,inputs,7);             // Passes the inputs (without errors) to the support vector machine
   setOutputs(handle1,outputs);             // Passes the outputs (without errors) to the support vector machine
   setParameter(handle1,OP_TOLERANCE,0.05); // Sets the error tolerance parameter to <5%
   training(handle1);                       // Trains the support vector machine using the inputs/outputs passed

   insertRandomErrors(inputs,outputs,500);  // Takes the original inputs/outputs generated and adds random errors to the data

   int handle2=initSVMachine();             // Initializes a new support vector machine and returns a handle
   setInputs(handle2,inputs,7);             // Passes the inputs (with errors) to the support vector machine
   setOutputs(handle2,outputs);             // Passes the outputs (with errors) to the support vector machine
   setParameter(handle2,OP_TOLERANCE,0.05); // Sets the error tolerance parameter to <5%
   training(handle2);                       // Trains the support vector machine using the inputs/outputs passed

   double t1=testSVM(handle1,N_TestPoints); // Tests the accuracy of the trained support vector machine and saves it to t1
   double t2=testSVM(handle2,N_TestPoints); // Tests the accuracy of the trained support vector machine and saves it to t2

   Print("The SVM accuracy is ",NormalizeDouble(t1,2),"% (using training inputs/outputs without errors)");
   Print("The SVM accuracy is ",NormalizeDouble(t2,2),"% (using training inputs/outputs with errors)");
   deinitSVMachine();                       // Cleans up all of the memory used in generating the SVM to avoid memory leak
  }

当脚本运行的时候,将在“EA 日志”中生成下列结果。在拥有 5000 个训练点的训练数据集中,我们可以引入 500 个随机误差。对比这台带有误差的支持向量机和原本的支持向量机的性能,性能仅降低<1%。这是因为支持向量机在训练的过程中,可以忽略数据集中的异常值,并且有能力生成真实数据精确的令人吃惊的模型。这表示,在从带噪音的数据集中提取复杂模式和发现方面,支持向量机有潜力成为更加有用的工具。

EA 日志

图 5. "Schnick" 脚本在 MetaTrader 5 中运行后生成的 EA 日志。


演示版本

“代码库”中可下载上述代码的完整版本,但是只有在您从“应用商店”购买了完整版本的“支持向量机学习工具”的情况下,才能在您的终端运行此脚本。如果您只是下载了此工具的演示版,则仅限于通过策略测试程序使用。为了能用此工具的演示版测试 "Schnick" 代码,我将此脚本改写成了可利用策略测试程序配置的“EA 交易”。两种版本的代码,都可以使用下述链接下载:


支持向量机如何应用于“应用市场”?

应该承认,上面的讨论 Schnick 例子是非常简单的;然而,本例与用于技术市场分析的支持向量机使用有着许多的共同点。

技术分析基本上就是使用历史市场数据,预测未来的价格走势。这与 schnick 例子是同一个道理,我们使用前辈科学家的研究发现,来判断一只新动物是否为 schnick。而且,市场充斥着噪音、误差和统计异常值,而这些让支持向量机应用成为了一个有趣的概念。

大量技术分析交易方法的基础包括下述步骤:

  1. 监控数个指标
  2. 找出每一个指标与潜在成功交易相关联的条件
  3. 观察每一个指标,并在他们全部(或大部分)产生交易信号时进行评估

这种相似之处,让应用支持向量机以相似方法产生新的交易信号成为了可能。支持向量机学习工具的开发理念就在于此。工具使用的详细说明可在“应用商店”下载,这里仅作概述。使用工具的过程如下:

方框图

图 6. 此方框图显示了在 EA 交易中实施支持向量机工具的过程

在开始使用“支持向量机学习工具”之前,重要的是首先要了解训练输入与输出的生成方式。

如何生成训练输入?

如此一来,您想要作为输入使用的指标已经完成初始化,您的新支持向量机也一样。下一步就是将指标句柄传递到您的新支持向量机,并就其如何生成训练数据给予指示。这通过调用 setIndicatorHandles() 函数来完成。此函数允许您将初始化指标的句柄传递到支持向量机中。这通过传递包含句柄的整数数组来完成。而偏移值和数据点数量,是此函数的另两个输入。

偏移值表示当前柱与要用于生成训练输入的起始柱之间的偏移,而训练数据点的数量(以 N 表示)则会设置训练数据的大小。下图阐明了如何使用这些值。如果偏移值为 4、N 值为 6,则会告诉支持向量机仅使用白色方框中捕获的柱来生成训练输入和输出。与之类似,如果偏移值为 8、N 值为 8,则是告诉支持向量机仅使用蓝色方框中捕获的柱来生成训练输入和输出。

一旦 setIndicatorHandles() 函数被调用,则有可能调用 genInputs() 函数。此函数会使用待传递的指标句柄来生成一组待训练使用的输入数据。

图 7. 显示偏移值与 N 值的烛形图

图 7. 显示偏移值与 N 值的烛形图


如何生成训练输出?

训练输出是通过模拟基于历史价格数据的假设交易,并确定此交易是否会成功的方式生成的。为此,也有几个用于指导支持向量机学习工具如何评估假设交易成败的参数。

第一个变量为 OP_TRADE。其值可以是 BUY (买入)或 SELL (卖出),且会对应于假设的买入或卖出交易。如果此值为 BUY,那么,在生成输出时,它只会关注假设买入交易的成功可能性。或者,如果此值为 SELL,那么,在生成输出时,它只会关注假设卖出交易的成功可能性。

针对这些假设交易,接下来使用的值是 Stop Loss (止损)与 Take Profit (获利)。此类值以点计算,会为每一个假设交易设定止损位和限制位。

最后一个参数是交易期限。此变量按小时计算,可确保只有在此最大期限内完成的交易才被视为成功。之所以加入此变量,就是为了避免支持向量机在某个慢速移动的横向市场中产生交易信号。


选择输入时要考虑的事项

在您的交易中实施支持向量机时,在输入选择中加入一些自己的想法是很重要的。与 Schnick 的例子类似,选择一个预计有类似不同率的输入非常重要。比如说,您可能忍不住将移动平均线用作一个输入,但是,由于长期平均价格很容易随着时间的推移大幅变动,被隔离的移动平均线可能并非最佳的待用输入。这是因为,如今的移动平均线值与 6 个月前的移动平均线值之间不会有任何重大的相似性。

假设我们要执行 EURUSD 交易,并利用一个带移动平均线输入的支持向量机来生成“买入”交易信号。比如当前价格为 1.10,但它在 6 个月前的价格尚为 0.55 时,就在生成训练数据。训练支持向量机时,它发现的模式,只有在价格为 0.55 左右时才可能生成交易信号,因为它只知道此数据。因此,在价格回落到 0.55 之前,您的支持向量机永远都不会生成交易信号。

反之,更适合支持向量机使用的输入可能是一个 MACD 或类似的动量指标,因为 MACD 的值独立于平均价格水平,而且只相对于移动生成信号。我建议您自己试验一下,看看怎样才能得到最佳结果。

选择输入时,还要考虑确保支持向量机拥有足量的生成新的交易信号的指标快照。根据自己的交易经验,您可能会发现:MACD 仅在您有过去的 5 个柱可待查看时才有用(因为这样才会显示趋势)。除非您能说明其为上升还是下降,否则 MACD 单独的一个柱可能没什么用。因此,可能有必要将 MACD 指标过去的几个柱传递到支持向量机。有两种方式可能实现:

  1. 您可以创建一个新的、使用 MACD 指标过去 5 个柱的自定义指标,以将趋势作为一个单一值计算。之后,可将这个自定义指标作为一个单一输入传递给支持向量机,或者

  2. 您可以将支持向量机中 MACD 指标的过去 5 个柱作为 5 个独立的输入使用。方法则是初始化 MACD 指标的 5 个不同实例。每个指标均可利用于当前柱的某个不同偏移进行初始化。之后,就可以将来自不同指标的 5 个句柄传递给支持向量机。要注意的是,第 2 种选项很容易导致 EA 交易执行时间的延长。您的输入越多,成功训练所需的时间就越长。


EA 交易中实施支持向量机

我准备了一个 EA 交易示例,展示了在个人交易过程中怎样使用支持向量机(从下述链接可下载一份示例复本 https://www.mql5.com/zh/code/1229)。希望这个 EA 交易可以让您多少体验支持向量机。我建议您复制/更改/修改此 EA 交易,以适应您自己的交易风格。此 EA 的工作方式如下:

  1. 创建两个使用 svMachineTool 库的新的支持向量机。一个设置用于生成新“买入”交易信号,另一个则设置用于生成新“卖出”交易信号。

  2. 有 7 个标准指标完成初始化,而每一个指标的句柄均存储于一个整数数组中(注意:上述指标的任何组合均可用作输入,只是需要将其以单一整数数组的形式传递给支持向量机。)

  3. 指标句柄的数组被传递至新的支持向量机。

  4. 利用指标句柄数组及其它参数,历史价格数据被用于生成准确的输入与输出,以供训练支持向量机使用。

  5. 一旦所有的输入与输出均生成完毕,则两台支持向量机亦训练完成。

  6. EA 中的训练支持向量机用于生成新的“买入”与“卖出”交易信号。生成新的“买入”或“卖出”交易信号后,交易会随着手动止损与获利订单建仓。

支持向量机的初始化和训练,均在 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 Functions-----------+
//| The following #import statement imports all of the support vector
//| machine learning tool functions into the EA for use. Please note, if
//| you do not import the functions here, the compiler will not let you
//| use any of the functions
//+------------------------------------------------------------------+
#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 Variables----------------------------+
input int            takeProfit=100;      // TakeProfit level measured in pips
input int            stopLoss=150;        // StopLoss level measured in pips
input double         hours=6;             // The maximum hypothetical trade duration for calculating training outputs.
input double         risk_exp=5;          // Maximum simultaneous order exposure to the market
input double         Tolerance_Value=0.1; // Error Tolerance value for training the SVM (default is 10%)
input int            N_DataPoints=100;    // The number of training points to generate and use.

//+---------------------Indicator Variables--------------------------+
//| Only the default indicator variables have been used here. I
//| recommend you play with these values to see if you get any 
//| better performance with your EA.                    
//+------------------------------------------------------------------+
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;

//+------------------Expert Advisor Variables------------------------+
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);
//+------------------------------------------------------------------+
//| The following statements are used to initialize the indicators to be used for the support 
//| vector machine. The handles returned are stored to an int[] array. I have used standard 
//| indicators in this case however, you can also you custom indicators if desired
//+------------------------------------------------------------------+
   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);

//----------Initialize, Setup and Training of the Buy-Signal support vector machine----------
   handleB=initSVMachine();                             // Initializes a new SVM and stores the handle to 'handleB'
   setIndicatorHandles(handleB,handles,0,N_DataPoints); // Passes the initialized indicators to the SVM with the desired offset 
                                                        // and number of data points
   setParameter(handleB,OP_TOLERANCE,Tolerance_Value);  // Sets the maximum error tolerance for SVM training
   genInputs(handleB);                                  // Generates inputs using the initialized indicators
   genOutputs(handleB,BUY,stopLoss,takeProfit,hours);   // Generates the outputs based on the desired parameters for taking hypothetical trades

//----------Initialize, Setup and Training of the Sell-Signal support vector machine----------
   handleS=initSVMachine();                             // Initializes a new SVM and stores the handle to 'handleS'
   setIndicatorHandles(handleS,handles,0,N_DataPoints); // Passes the initialized indicators to the SVM with the desired offset 
                                                        // and number of data points
   setParameter(handleS,OP_TOLERANCE,Tolerance_Value);  // Sets the maximum error tolerance for SVM training
   genInputs(handleS);                                  // Generates inputs using the initialized indicators
   genOutputs(handleS,SELL,stopLoss,takeProfit,hours);  // Generates the outputs based on the desired parameters for taking hypothetical trades
//----------
   training(handleB);   // Executes training on the Buy-Signal support vector machine
   training(handleS);   // Executes training on the Sell-Signal support vector machine   
   return(0);
  }


高级支持向量机交易

支持向量机学习工具中还内置了更多功能,以供更高级的用户使用。此工具允许用户在自己的自定义输入数据和输出数据中传递(如 Schnick 示例)。如此一来,您就可以为支持向量机输入与输出自定义设计自己的标准,并手动传入此数据进行训练。这样就为您交易的方方面面都使用支持向量机创造了机会。

它不仅可能利用支持向量机生成新的交易信号,还可以用来生成成交、资金管理、新的高级指标等信号。但是,为确保不出错,了解这些输入与输出的构造方式是很重要的。

输入: 输入作为一个一维双精度值数组被传递到支持向量机。请注意:您创建的任何输入,都必须作为一个双精度值传入。布尔、整数等类型的值,都必须转换为双精度值之后,才能被传入支持向量机。输入有下述格式要求。示例:假设我们要传入带有 3 个输入 x 5 个训练点的输入。为此,我们的双精度数组必须是 15 位长的格式:

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

还有必要传入一个输入数量值。本例中,N_Inputs=3。

输出: 输出是作为一个布尔值数组被传入。这些布尔值是支持向量机相对每组被传入输入的需要输出。按照上例,则为 5 个训练点。在此情形下,我们会传入一个 5 单位长度的布尔型输出值数组。

一般性说明: