小波分析 + 支持向量机(SVM)预测股票涨跌幅的实现

作者:k474905973

刚刚开始进入量化投资领域,最近在做金融数据方面的预测,用到了数据挖掘的知识,花了大概半个月的时间搞懂研报的思想,学习研报中提到的数据处理和机器学习算法,并实现了这个量化投资的核心模型部分,虽然结果显示这个方法没有多好,但是在这期间,我遇到了很多问题,也t通过各种渠道把他们解决了,因此,在这里分享一下我在做这一次策略中用到的方法,希望能够给刚开始接触量化投资或者时间序列挖掘的人们提供一些参考,当然,我也是第一次真正地实现一个策略,文中有纰漏之处,若大家发现,也请指出,小胖感激不尽!

做的这个策略来源于国信证券2010年中期策略会专题报告一篇名为《基于小波分析和支持向量机的指数预测模型》的研报(以下简称研报)。

策略思想:

1.先用小波分析对用于预测的数据集进行消噪处理

2.归一化消噪后的时间序列,求出新的股票指标,以备预测用

3.选择适当的核函数,对股票的涨跌情况Label进行预测,并求出支持向量机中的拉格朗日乘数alpha(有i个)和截距bias(一个)

4.根据alpha,bias和涨跌情况Label,结合核函数和支持向量机的高斯径向基函数,预测涨跌值z

5.用预测的涨跌值还原预测股票价格


如果我没有理解错,大致步骤应该就是这样的了。而且绝大多数策略思想和时间序列数据挖掘的思想都和这差不多。

下面详细讲解每一个步骤:

1.小波消噪

MATLAB自带小波分析工具箱,网上也有讲解,但是我在做的过程中也犯过迷糊,因此拿出来和大家分享一下。

假设我们要对成交量,收盘价,成交金额,最高价和最低价这五个指标进行消噪处理,并且用db4小波进行4层分解,那么代码应该是下面这样的:

%% 对每一个指标用db4小波执行4层小波分解
%对总成交量进行分解
[C_Volume, L_Volume] = wavedec(TotalVolumeIndex, 4, 'db4');
%对收盘价进行分解
[C_ClosePrice, L_ClosePrice] = wavedec(CloseIndex, 4, 'db4');
%对总成交金额进行分解
[C_Amount, L_Amount] = wavedec(TotalAmountIndex, 4, 'db4');
%对最高价进行分解
[C_HighPrice, L_HighPrice] = wavedec(HighIndex, 4, 'db4');
%对最低价进行分解
[C_LowPrice, L_LowPrice] = wavedec(LowIndex, 4, 'db4');

小波分析把原始的信号分解为两个部分:近似(Approximation)和细节(Detail),近似就是我们通常所说的信号的低频部分,而细节就是高频部分。信号的噪声通常包含在高频部分,而近似部分和原始的信号非常相似,但是我们又不能只保留低频部分对高频不管不问,因此我们可以只需要对高频部分进行消噪处理,然后再还原信号,代码如下(以收盘价为例,其他指标类似):

%% 从系数中4层近似(低频)和1,2,3,4层细节(高频)

%收盘价的4层近似和1,2,3,4层细节
A4_ClosePrice = wrcoef('a',C_ClosePrice, L_ClosePrice, 'db4', 4);
D4_ClosePrice = wrcoef('d',C_ClosePrice, L_ClosePrice, 'db4', 4);
D3_ClosePrice = wrcoef('d',C_ClosePrice, L_ClosePrice, 'db4', 3);
D2_ClosePrice = wrcoef('d',C_ClosePrice, L_ClosePrice, 'db4', 2);
D1_ClosePrice = wrcoef('d',C_ClosePrice, L_ClosePrice, 'db4', 1);

%% 对高频信息进行消噪处理

ClosePriceThreshold1 = thselect(D1_ClosePrice, 'sqtwolog');
ClosePriceThreshold2 = thselect(D2_ClosePrice, 'sqtwolog');
ClosePriceThreshold3 = thselect(D3_ClosePrice, 'sqtwolog');
ClosePriceThreshold4 = thselect(D4_ClosePrice, 'sqtwolog');
ClosePriceTR = [ClosePriceThreshold1,ClosePriceThreshold2,ClosePriceThreshold3,ClosePriceThreshold4];

可以从上面看到,近似只有第4层有,而细节在一二三四层都有,这是因为,小波消噪的原理图是这样的:


意思就是说,原始的信号被分解为了近似和细节两个部分,这个时候是在第一层,到了第二层,分解的就是第一层中的近似,而将第一层中的细节保留,到了第三层,同样,分解的是上一层的近似,保留上一层的细节,这样就会发现,细节和我们分解的层数一样多,而近似永远只有一个,而原始信号的大部分信息,都被保留在近似中。

消噪之后,就是恢复信号,函数和我所用到的属性及相应含义在下面的注释中都写清楚了,当然这个函数还有很多的属性选项,如需了解,敬请help MATLAB,作为简单的消噪处理,这些选项差不多基本够了。

%% 恢复去噪后的各项指标

%SORH是阈值选项,为s时选择软阈值,为h时选择硬阈值
SORH = 's';
%PERF0是恢复和压缩的范数百分比
%'lvd'为允许设置各层的阈值
%'gbl'为固定阈值
% 4为阈值的长度

[ClosePrice,CXC,LXC,PERF0,PERF2] = wdencmp('lvd',CloseIndex,'db4',4 ,ClosePriceTR ,SORH);

注意,小波分析工具箱里还有一组对应的函数,不记得叫什么名字了,反正可以实现和wrcoef一样的功能--从近似系数中提取近似和细节,因为当时是看了别人的博客,我以为所有的代码都需要,就写上去了,后来发现,实现的是一样的功能,只是略微有点差别,等我找到了那个函数再继续补充。

最后衡量一下去噪的效果,这段代码比较死,可以copy用,用多了就熟悉了。

%% 去噪效果衡量
%SNR越大效果越好,MSE越小越好
%获取各个部分的长度:理论上应该是一样长的
VolumeLength = length(TotalVolumeIndex);
ClosePriceLength = length(CloseIndex);
AmountLength = length(TotalAmountIndex);
HighPriceLength = length(HighIndex);
LowPriceLength = length(LowIndex);
x = CloseIndex;
y = ClosePrice;
SqureError = (CloseIndex-ClosePrice).^2;
SqureClosePrice = ClosePrice.^2;
ClosePriceToError = SqureClosePrice./SqureError;
RatioSum = sum(ClosePriceToError);
ErrorSum = sum(SqureError);
SNR =  10*log10(RatioSum);
MSE = ErrorSum/ClosePriceLength;
display(['SNR is:',num2str(SNR),';      MSE is:',num2str(MSE)])

最后,对比一下去噪前后的信号:

figure(2);
% subplot(2,1,1);
% plot(CloseIndex);
% title('原始收盘价曲线')
% subplot(2,1,2);
% plot(ClosePrice);
% title('消噪后的收盘价曲线')


2.归一化消噪后的时间序列,求出新的股票指标

归一化时间序列是都要做的,不管用什么方法,都不能让大的值比如成交量、成交金额在决策中抢了收盘价、开盘价等的风头,除非有这样的需求,不然就最好让这些指标都在同一起跑线上。

在这里,我用的是均值方差归一化方法,公式如下:

代码贴上,同样以收盘价为例,其他指标类似:

%对收盘价进行归一化
MeanClose = mean(ClosePrice);
VarClose = std(ClosePrice);
MeanTemp = zeros(size(MeanClose));
VarTemp = zeros(size(VarClose));
MeanTemp(:) = MeanClose;
VarTemp(:) = VarClose;
%根据归一化公式对收盘价进行归一化
ClosePriceNorm = (ClosePrice-MeanTemp)./VarTemp;

然后是准备支持向量机的输入,偷了个懒,和研报中用了相同的指标:


代码贴上:

%% 获取重要指标
for i = M+1:length(TotalVolume)
    VolumeIndex(i) = TotalVolume(i)/mean(TotalVolume(i-M:i-1));
    CloseIndex(i) = ClosePrice(i)/mean(ClosePrice(i-M:i-1));
    TradeAmountIndex(i) = TotalAmount(i)/mean(TotalAmount(i-M:i-1));
    MeanIndex(i) = mean(ClosePrice(i-M:i-1));
    RateChangeIndex(i) = (ClosePrice(i-1)-ClosePrice(i-M))/ClosePrice(i-M);
    HighIndex(i) = max(HighPrice(i-M:i-1));
    LowIndex(i) = min(LowPrice(i-M:i-1));
end

3.选择适当的核函数,对股票的涨跌情况Label进行预测,并求出alpha和bias

在做这一次策略之前,我一直非常不明白怎么对时间序列进行预测,我想还有很多刚刚接触机器学习的人和我有着同样的疑问,所以我简单说一下吧。

首先,我们要知道我们需要预测什么指标,需要用什么指标去预测,在这个例子里,我所用到的指标就是上面所说的CloseIndex,TradeAmountIndex等等,这些在教科书上,就体现为点的第N维坐标,把这些作为输入,再预测我们想要的值,例如明天的涨跌情况,就是这样的,非常简单。

在这里,我们计算的是M天以后的涨跌幅,涨跌幅的正负号值,就是涨跌情况啦,即Label!

%获取价格的涨跌情况
%yii为第M天之后的收盘价与当前i天的收盘价之差
y = zeros(size(ClosePrice));

for ii = 1: length(ClosePrice)-M
    y(ii) = ClosePrice(ii+M)-ClosePrice(ii);
end

%标记label
%价格上涨或持平记为1
%价格下跌记为-1
Label = zeros(size(y));
Label(find(y >= 0)) = 1;
Label(find(y < 0 )) = -1;

下面,我们将用MATLAB自带的svmtrain函数来预测股票的涨跌情况,即新的predictLabel。

在这里,训练的策略是这样的:训练N天的数据,预测N+1天到N+M天的数据的Label,然后窗口向前移动M个数据,直到全部数据都使用完毕。

for ii=1:M:(length(ClosePrice) - M - N - 1)
    %选取训练集和预测集
    trainData = DataMatrix(ii:ii+N-1,:);
    trainLabel = Label(ii:ii+N-1);
    predictData = DataMatrix(ii + N: ii + M + N -1,:); 
    %训练
    %用高斯径向基函数rbf进行训练
    struct = svmtrain(trainData, trainLabel, 'kernel_Function', 'rbf','rbf_sigma', sigma);%sigma 默认为1

    %从svm的结构体中提取alpha和bias
    alpha = struct.Alpha;
    bias = struct.Bias;
    SupportVectorIndex = struct.SupportVectorIndices;
    SupportVectors = struct.SupportVectors;
    %预测涨跌情况
    predictLabel = svmclassify(struct, predictData);
    %预测涨跌幅
    SupportLabel = trainLabel(SupportVectorIndex);
    z = cal_z(SupportVectors, SupportLabel, predictData, alpha, bias, sigma);
    z_predict(ii+N:ii+N+M-1) = z(:);
    %% 计算实际涨跌幅
    for jj = (ii+N):(ii+N+M-1)
        %预测的股票价格
        predictPrice (jj + M) = ClosePrice(jj) + z_predict(jj);    
        if jj == N + ii
            delta(jj + M) = ClosePrice(jj) + y(jj) - ClosePrice(jj -1);
            delta_predict(jj + M) = predictPrice (jj) - ClosePrice(jj - 1);
        elseif ((jj > N +ii) && (jj < N + M +ii) )
            delta(jj + M) = ClosePrice(jj) + y(jj) - (ClosePrice(jj - 1) + y(jj -1));
            delta_predict(jj + M) = predictPrice (jj) - predictPrice (jj - 1); 
        end        
    end
end

训练过程中,遇到了一个问题,就是如果我把训练集设置得过小或者预测集设置得过大,MATLAB都会报错,告诉我groups必须包含两个类别,我思考良久,发现并不是我的数据的问题,经过MATLAB群里一个同僚的提示,终于知道了答案,因为如果训练集过小,里面包含的Label就会只剩下一种,特别是股票数据,在短期内没有太大的变化,可能一直是1或者-1,这个时候,支持向量机就没法训练和预测,因此,如果下次有人还遇到这种情况,千万不要怀疑自己的数据有问题,把训练集改大一点就好了。

至此,这一步所需的工作已经完成,我们得到了我们想要的Label,alpha和bias。

4.根据alpha,bias和涨跌情况Label,结合核函数和支持向量机的高斯径向基函数,预测涨跌值z

研报告诉我们,svm预测出来的分类值代表了股票的涨跌情况,而没有被sign之前的函数值大概就是涨跌幅。

百度上说高斯径向基核函数的公式是这样的:

 k(||x-xc||)=exp{- ||x-xc||^2/(2*σ)^2) } 其中xc为核函数中心,σ为函数的宽度参数 , 控制了函数的径向作用范围。

这个宽度参数,就是上面这段代码中,svmtrain的最后一个参数,sigma。

这个过程相当于还原svmtrain的工作过程。

%% 用高斯径向基核函数计算z
[m1,n1] = size(testData);
[m2,n2] = size(SupportVectors);
z = zeros(m1,1);
for  ii = 1 : m1
    K = zeros(m1,1);
    for jj = 1: m2
        deltaRow = testData(ii , :) - SupportVectors(jj , :);
    end
    K(ii) = deltaRow * deltaRow';
    K = exp( K /(-(1*sigma)^2));
    save K.mat K
    save SupportLabel.mat SupportLabel
    save alpha.mat alpha
    z(ii) = (SupportLabel' * alpha) * K(ii) + bias;
    %注意运算技巧
end

然后,通过这个函数,我们连股票的涨跌值都预测出来了,用到的输入,都是第3步中的输出,MATLAB真心非常强大!

5.还原预测的股票价格

记得我们在之前定义的y(ii)是M天相对于ii天的股票涨跌幅,所以我们预测出来的z,也是M天之后的,要还原为当天的股价,加上M天之前的收盘价就好了,其实这部分的代码和第3部分的训练是挨在一起的,我再提出来让大家看清楚一点。

%预测涨跌幅
    SupportLabel = trainLabel(SupportVectorIndex);
    z = cal_z(SupportVectors, SupportLabel, predictData, alpha, bias, sigma);
    z_predict(ii+N:ii+N+M-1) = z(:);
    %% 计算实际涨跌幅
    for jj = (ii+N):(ii+N+M-1)
        %预测的股票价格
        predictPrice (jj + M) = ClosePrice(jj) + z_predict(jj);    
        if jj == N + ii
            delta(jj + M) = ClosePrice(jj) + y(jj) - ClosePrice(jj -1);
            delta_predict(jj + M) = predictPrice (jj) - ClosePrice(jj - 1);
        elseif ((jj > N +ii) && (jj < N + M +ii) )
            delta(jj + M) = ClosePrice(jj) + y(jj) - (ClosePrice(jj - 1) + y(jj -1));
            delta_predict(jj + M) = predictPrice (jj) - predictPrice (jj - 1); 
        end        
    end


然后我们的工作就都做完了,研报中作者说有延迟,但是预测得很准确,我做这个策略之前,对这个策略充满了信心。现在看做出来的结果也和研报中相似:

可是我做完之后,才发现问题还挺严重,因为在还原的时候,本来就是以M天以前的收盘价为基础加上来的,如果不能预测出和当天一样的价格或趋势,就说明Z没有预测到。我打算换神经网络做,下一篇博文发表用神经网络预测的结果。

这篇文章是MATLAB群里一个学弟要我写出来和大家分享的,在这过程中,遇到了很多新手都会遇到的问题,所以贴出来,让大家都能共同进步,也顺便纪念一下我第一次实现策略的时光。


对了,原文是用沪深300指数进行分析和预测的,由于我没有那个数据,我用的是SP500中随机选的一只股票的数据,而且这个数据里没有成交金额,所以成交金额也是用成交量代替的,也就是说我的策略里用了两次成交量,这也许也是策略不准的原因之一吧。

策略用到的数据:

http://download.csdn.net/detail/k474905973/7490371

策略源代码:(MATLAB)

http://download.csdn.net/detail/k474905973/7539409



发表评论

2个评论

  • yes_2009

    你好,文中的代码中设计的变量在附件的data中并没有注明?附件的代码貌似不能下载?能否改进一下相关问题?

    2016-10-28 15:03:07回复

  • ben46

    这位哥们, 可以加个QQ: 843880805一起讨论吗? 你的这个方法我试过的, 不给力, 我也研究神经网络, 不知道你用的是哪个网络?

    2015-09-15 12:06:47回复

我要留言×

技术领域:

我要留言×

留言成功,我们将在审核后加至投票列表中!

提示x

人工智能机器学习知识库已成功保存至我的图谱现在你可以用它来管理自己的知识内容了

删除图谱提示×

你保存在该图谱下的知识内容也会被删除,建议你先将内容移到其他图谱中。你确定要删除知识图谱及其内容吗?

删除节点提示×

无法删除该知识节点,因该节点下仍保存有相关知识内容!

删除节点提示×

你确定要删除该知识节点吗?