这段脚本是为了配合在MQL5网站上发布的《机器学习:支持向量机如何在交易中应用》文章而编写的。
该版本的代码专门设计用于与支持向量机学习工具的演示版本配合使用,您可以在MQL5市场免费获取。
想象一下一个假设的场景,您是一名研究员,正在调查一种只在北极深处发现的稀有动物,称为Shnicks。考虑到这些动物的偏远性,至今只发现了少量(大约5000只)。作为研究者,您面临的问题是……我该如何识别一只Shnick?
您手头只有少数研究者发表的研究论文,这些研究者曾见过Shnick。在这些研究论文中,作者们描述了一些关于Shnick的特征,比如身高、体重、腿的数量等,但这些特征在不同论文中有很大的差异,没有明显的模式。
我们如何利用这些数据来识别新的动物是否为Shnick?
解决这个问题的一个可能方案是使用支持向量机来识别数据中的模式,并创建一个框架,用于对动物进行分类,判断它们是Shnick还是非Shnick。第一步是创建一组数据,用于训练您的支持向量机以识别Shnick。训练数据是一组输入和匹配输出,供支持向量机分析并提取模式。
这段脚本旨在演示使用支持向量机解决分类问题的强大能力,利用MQL5市场中的支持向量机学习工具。有关此假设问题和脚本的完整描述,请参见文章《机器学习:支持向量机如何在交易中应用》。文章中包括了如何使用该脚本的详细说明,以及该问题如何为使用机器学习评估市场趋势提供洞见。
代码:
//+------------------------------------------------------------------+ //| Schnick_Demo.mq5 | //| Copyright 2011, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "http://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| 这个脚本演示了支持向量机学习工具的能力 //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| 以下语句导入支持向量机工具'svMachineTool.ex5'中的所有函数 //+------------------------------------------------------------------+ #import "svMachineTool_demo.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 &input[]); void deinitSVMachine(void); #import //--- 我们将使用的输入数量 int N_Inputs=7; //+------------------------------------------------------------------+ //| 机器人初始化函数 | //+------------------------------------------------------------------+ int OnInit() { 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.01); //将误差容忍度参数设置为<5% training(handle1); //使用传递的输入/输出训练支持向量机 insertRandomErrors(inputs,outputs,500); //对生成的原始输入/输出添加随机错误 int handle2=initSVMachine(); //初始化新的支持向量机并返回一个句柄 setInputs(handle2,inputs,7); //将输入(有误差)传递给支持向量机 setOutputs(handle2,outputs); //将输出(有误差)传递给支持向量机 setParameter(handle2,OP_TOLERANCE,0.01); //将误差容忍度参数设置为<5% training(handle2); //使用传递的输入/输出训练支持向量机 double t1=testSVM(handle1,N_TestPoints); //测试训练好的支持向量机的准确性并保存到t1 double t2=testSVM(handle2,N_TestPoints); //测试训练好的支持向量机的准确性并保存到t2 Print("支持向量机的准确度为 ",NormalizeDouble(t1,2),"% (使用无误差的训练输入/输出)"); Print("支持向量机的准确度为 ",NormalizeDouble(t2,2),"% (使用有误差的训练输入/输出)"); deinitSVMachine(); //清理生成SVM时使用的所有内存,以避免内存泄漏 return(0); } //+------------------------------------------------------------------+ //| 机器人去初始化函数 | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- 在OnDeinit()中没有执行的函数 } //+------------------------------------------------------------------+ //| 机器人每次价格变动时触发的函数 | //+------------------------------------------------------------------+ void OnTick() { //--- 在OnTick()中没有执行的函数 } //+------------------------------------------------------------------+ //| 这个函数根据我们选择的标准判断观察到的动物是否为Shnick,并返回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); //如果身高超出参数则返回(false) if(weight < 40 || weight > 50) return(false); //如果体重超出参数则返回(false) if(N_legs < 8 || N_legs > 10) return(false); //如果腿的数量超出参数则返回(false) if(N_eyes < 3 || N_eyes > 4) return(false); //如果眼睛数量超出参数则返回(false) if(L_arm < 400 || L_arm > 450) return(false); //如果手臂长度超出参数则返回(false) if(av_speed < 2 || av_speed > 2.5) return(false); //如果平均速度超出参数则返回(false) if(f_call < 11000 || f_call > 15000) return(false); //如果f_call超出参数则返回(false) return(true); //否则返回(true) } //+------------------------------------------------------------------+ //| 该函数接收空的double数组和空的boolean数组 //| 生成输入/输出用于训练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); //为腿的数量生成随机输入 in[3]= randBetween(3,4.2); //为眼睛数量生成随机输入 in[4]= randBetween(380,450); //为手臂长度生成随机输入 in[5]= randBetween(2,2.6); //为平均速度生成随机输入 in[6]= randBetween(10500,15500); //为f_call生成随机输入 //--- 将新生成的随机输入复制到训练输入数组中 ArrayCopy(inputs,in,i*N_Inputs,0,N_Inputs); //--- 评估随机输入并确定它是否为Shnick outputs[i]=isItASchnick(in[0],in[1],in[2],in[3],in[4],in[5],in[6]); } } //+------------------------------------------------------------------+ //| 该函数接受训练好的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); //为腿的数量生成随机输入 in[3]= randBetween(3,4.2); //为眼睛数量生成随机输入 in[4]= randBetween(380,450); //为手臂长度生成随机输入 in[5]= randBetween(2,2.6); //为平均速度生成随机输入 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)); } //+------------------------------------------------------------------+ //| 此函数接收生成的正确训练输入和输出,并插入N个随机错误 //+------------------------------------------------------------------+ void insertRandomErrors(double &inputs[],bool &outputs[],int N) { int nTrainingPoints=ArraySize(outputs); //计算训练样本数量 int index; //创建新整数'index' bool randomOutput; //创建新布尔'randomOutput' 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); //为腿的数量生成随机输入 in[3]= randBetween(3,4.2); //为眼睛数量生成随机输入 in[4]= randBetween(380,450); //为手臂长度生成随机输入 in[5]= randBetween(2,2.6); //为平均速度生成随机输入 in[6]= randBetween(10500,15500); //为f_call生成随机输入 //--- 随机选择一个训练输入以插入错误 index=(int)MathRound(randBetween(0,nTrainingPoints-1)); //--- 生成一个随机布尔输出以用于创建错误 if(randBetween(0,1)>0.5) randomOutput=true; else randomOutput=false; //--- 将新生成的随机输入复制到训练输入数组中 ArrayCopy(inputs,in,index*N_Inputs,0,N_Inputs); //--- 将新生成的随机输出复制到训练输出数组中 outputs[index]=randomOutput; } } //+------------------------------------------------------------------+ //| 此函数用于创建t1和t2之间的随机值 //+------------------------------------------------------------------+ double randBetween(double t1,double t2) { return((t2-t1)*((double)MathRand()/(double)32767)+t1); } //+------------------------------------------------------------------+