SVM机器学习交易实战入门
用支持向量机从指标中提取交易信号
什么是支持向量机(SVM)
支持向量机(Support Vector Machine,简称SVM)是一种机器学习方法,其核心目标是将输入数据划分为两类。要让SVM生效,必须先用一组带有已知分类结果的训练数据来建立模型。模型建立后,面对新的未知数据,SVM就能依据已学到的边界将其自动归类为其中一类。
从数学角度看,SVM的工作方式是将训练输入映射到多维空间,然后使用回归算法寻找一个能最佳分离两类数据的超平面。所谓超平面,是在N维空间中将空间一分为二的平面:在二维空间里它是一条线(一维超平面),在三维空间里是一个面(二维超平面),在更高维度中则是N-1维结构。SVM本质上是一台输入/输出机器:你喂入一组特征,它返回类别判断。
理论上SVM可接受的输入特征数量从1到无穷,但实际运算能力是瓶颈。若有N个输入特征,算法就要把每组数据映射至N维空间,并找出N-1维分割超平面。这也是为何在MT5中应用SVM时,特征数量要克制,否则训练时间会急剧拉长。
从二维到高维:SVM如何扩展
上文的二维示例只是为了可视化。真实问题中我们往往使用更多特征。例如你希望用20个输入参数判断某样本属于类型1还是类型2,SVM会在20维空间建模,并寻找19维超平面来分割。人类难以想象三维以上的形态,但算法逻辑与二维完全一致:在对应维度中求解最优分离边界。
高维空间带来的好处是能表达极其复杂的非线性关系,代价是计算量随维度上升。在交易中若直接把多个指标的几十根历史柱作为独立输入,维度会爆炸,因此原文作者建议通过自定义指标压缩,或控制偏移与数量。
案例:用SVM识别Schnick动物
为说明SVM的建模流程,原文虚构了稀有动物Schnick的研究场景。研究员只有少量论文描述,特征杂乱无模式。我们可采集身高、体重、肢体数、眼睛数、前肢长、奔跑速度、鸣叫频率共7个输入,并为每个样本标注是真Schnick还是假。训练数据需同时包含正例与反例,如下表概念所示。
选定输入后,编写函数判定真实属性:若动物同时满足身高1000-1100mm、体重40-50kg、肢8-10、眼3-4、臂400-450mm、速2-2.5m/s、鸣11000-15000Hz,则为真。此函数既用来造训练集,也用来检验SVM准确率。
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(weight < 40 || weight > 50) return(false);
if(N_legs < 8 || N_legs > 10) return(false);
if(N_eyes < 3 || N_eyes > 4) return(false);
if(L_arm < 400 || L_arm > 450) return(false);
if(av_speed < 2 || av_speed > 2.5) return(false);
if(f_call < 11000 || f_call > 15000) return(false);
return(true);
}
随后生成随机输入并调用上述函数得到输出,填充训练数组。以下代码展示生成过程:在各自范围内随机取值,拷贝进inputs,并用isItASchnick赋outputs。
void genTrainingData(double &inputs[],bool &outputs[],int N)
{
double in[];
ArrayResize(in,N_Inputs);
ArrayResize(inputs,N*N_Inputs);
ArrayResize(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);
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]);
}
}
double randBetween(double t1,double t2)
{
return((t2-t1)*((double)MathRand()/(double)32767)+t1);
}
训练与测试SVM模型
利用SVM学习工具库(应用市场下载),初始化机器、设输入/输出、设容错率、执行训练。容错率OP_TOLERANCE设为0.05表示允许5%训练点不符。训练完用新随机点测试,对比预测与真实标签计算准确率。
void OnStart()
{
double inputs[]; bool outputs[];
int N_TrainingPoints=5000;
int N_TestPoints=5000;
genTrainingData(inputs,outputs,N_TrainingPoints);
int handle1=initSVMachine();
setInputs(handle1,inputs,7);
setOutputs(handle1,outputs);
setParameter(handle1,OP_TOLERANCE,0.05);
training(handle1);
}
测试函数循环生成样本,调用classify获取预测,与isItASchnick真实值比对,返回正确率百分比。
double testSVM(int handle,int N)
{
double in[]; int N_correct=0; bool Predicted_Output,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);
Actual_Output=isItASchnick(in[0],in[1],in[2],in[3],in[4],in[5],in[6]);
Predicted_Output=classify(handle,in);
if(Actual_Output==Predicted_Output) N_correct++;
}
return(100*((double)N_correct/(double)N));
}
SVM的容错优势与噪音处理
SVM最大优势之一是对数据误差和噪音鲁棒。它不要求事先理解数据行为,而是自行提取模式,并可通过容错率忽略异常值。在Schnick案例中,人为向5000点训练集注入500个随机错误后,准确率下降不足1%。
这是因为训练时SVM寻求符合大部分(如95%)数据的模型,而非强行拟合全部点。以下代码展示如何插入随机误差:随机选索引,用随机输入和随机布尔覆盖原数据。
void insertRandomErrors(double &inputs[],bool &outputs[],int N)
{
int nTrainingPoints=ArraySize(outputs); int index; bool randomOutput; double in[];
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);
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;
}
}
SVM在MT5交易中的应用逻辑
技术分析与Schnick案例类似:用历史数据预测未来。市场充满噪音,SVM正适合提取复杂模式。典型流程为:监控多指标→找各自触发条件→综合评估信号。SVM学习工具把该过程自动化:用指标句柄生成输入,用模拟历史交易生成输出。
生成训练输入需调用setIndicatorHandles传递句柄数组、偏移offset与点数N。offset是当前柱到起始柱的距离,N是训练点规模。例如offset=4、N=6只用白框柱;offset=8、N=8用蓝框柱。随后genInputs()据此提取特征。
训练输出通过模拟假设交易产生:OP_TRADE选BUY/SELL;止损止盈以点计;duration以小时限定时长,避免横盘误判。只有期限内平仓盈利才记成功。
输入特征选择的关键原则
选输入特征比选模型更关键。与Schnick类似,要挑各类别间差异稳定、跨时间一致的指标。例如裸用移动平均线值作输入很差:价格从0.55涨到1.10后,旧均值模式失效,SVM只认0.55附近信号。反之MACD等动量指标与绝对价位无关,更适合。
还要注意特征快照长度。MACD常需近5柱看趋势,可写自定义指标压成单值传入,或初始化5个MACD实例以不同偏移传5个句柄。后者输入多、训练慢,需权衡。
在EA中部署SVM:svTrader示例
原文提供svTrader EA(下载见https://www.mql5.com/zh/code/1229),其初始化两个SVM分别抓买卖信号,将7个标准指标句柄数组传入,生成历史输入输出并训练,之后实时分类发信号并手动止损止盈。OnInit内核心片段如下(因原文截断,仅示前置):
#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,double &iput[]);
void deinitSVMachine(void);
#import
int OnInit()
{
int handles[];ArrayResize(handles,7);
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();
setIndicatorHandles(handleB,handles,0,N_DataPoints);
setParameter(handleB,OP_TOLERANCE,Tolerance_Value);
genInputs(handleB);
genOutputs(handleB,BUY,stopLoss,takeProfit,hours);
高级用法:自定义输入输出
SVM工具允许高级用户绕开指标,直接传自定义双精度输入数组与布尔输出数组。输入格式为扁平长数组:若3特征×5点,则长15,顺序A1B1C1A2B2C2…并声明N_Inputs=3。输出为等长布尔数组。任何类型须先转double。
约束:数组长度须为N_Inputs整数倍;输入组数须等于输出数;训练集真假比应均衡。除生成信号,还可用于仓位管理、成交概率等。但需自担过拟合风险。