【手把手教你】使用Logistic回归、LDA和QDA模型预测指数涨跌

1

引言

机器学习(Machine Learning)是人工智能(AI)的重要组成部分,目前已广泛应用于数据挖掘、自然语言处理、信用卡欺诈检测、证券市场分析等领域。量化投资作为机器学习在投资领域内最典型的应用之一,已经越来越广泛的出现在我们的视野中。

机器学习可简单理解为利用统计模型或算法拟合样本数据并进行预测,其模型算法根据学习任务可以分为分类、回归和聚类。分类方法是对离散型随机变量建模或预测的监督学习方法。分类是按照某种标准给对象贴标签,再根据标签来区分归类。而所谓的学习,其本质就是找到特征与标签间的关系(mapping 即映射)。换句话说,分类预测模型是一个从输入特征到离散的输出标签之间的映射函数。分类方法常用的有朴素贝叶斯、分类树、支持向量机、KNN、集成学习(包括随机森林)和深度学习等。

股票指数涨跌的预测本质上是一个分类问题。机器算法有很多种,没有最好的模型,只有更适合解决当前问题的算法。当然,本文的目的不是寻找最优的算法,而是机器学习在分析预测的应用。基于此,本文以Logistic 回归(LR)、线性判别法(LDA)、 二次判别分析(QDA)模型为例,对上证综指的涨跌进行预测。后续推文将会应用其他可能更有效的算法,包括SVM、集成学习和深度学习等。

【手把手教你】使用Logistic回归、LDA和QDA模型预测指数涨跌_第1张图片

2

算法基本原理

逻辑回归(LR)

逻辑回归(Logistic Regression,LR)是广义线性回归分析模型之一,其本质属于分类问题,因此主要用于被解释变量为分类(离散,如0,1)变量的情形。在分类问题上,逻辑回归要优于线性回归,因为线性回归在拟合被解释变量为离散时会出现负概率的情况,会导致错误的样本分类。而逻辑回归采用对数函数将预测范围压缩到0与1之间,从而提升预测准确率。

假设L1,L2,…Ln为预测因子,LR模型使用对数公式对上涨(U)的条件概率建立以下模型:

一般使用最大似然法来拟合上述模型。关于逻辑回归的详细推导此处不再展开,想进一步了解的可以找本计量经济学入门教材学习(如伍德里奇的《计量经济学导论》)。机器学习库scikit-learn提供了估计模型的“黑盒子”。

线性判别法(LDA)

线性判别分析,全称Linear Discriminant Analysis (LDA)与LR的区别在于,LR使用对数函数将P(Y=U|L1,L2,...,Ln)建模为给定预测变量Li的响应Y的条件分布;在LDA中,给定Y,分别对Li变量的分布进行建模,并通过贝叶斯定理获得P(Y=U|L1,L2,...,Ln)。

本质上,LDA是通过假设预测变量来自多元高斯分布得出的。在计算出该分布的参数的估计值之后,可以将这些参数输入贝叶斯定理中,以便对观测值属于哪个类别做出预测。LDA假定所有类别共享相同的协方差矩阵。由于scikit-learn提供了拟合和预测方法,所以不用太担心对预测所需的分布或后验概率的公式不了解。

二次判别分析(QDA)

二次判别分析,英文全称是Quadratic Discriminant Analysis (QDA),与LDA密切相关。二者的区别在于,QDA的每个类别都可以拥有自己的协方差矩阵。当决策边界为非线性时,QDA通常会表现更好。LDA通常在训练观察次数较少时(即需要减少方差时)表现更好。另一方面,当训练集较大时(即,差异较小,则QDA表现良好)。一个或另一个的使用最终归结为偏差方差的权衡。与LR和LDA一样,scikit-learn提供了QDA的实现函数,因此我们只需要提供训练/测试数据即可进行参数估计和预测。

3

预测步骤

预测主要分为三个步骤:

第一,根据上证综指指数收益率的正负划分为上涨(1)和下跌(-1)两种类型,作为被解释变量。

第二,寻找合适预测因子。预测因子的选择,与预测方法的选择一样重要,对预测性能起到决定性作用。预测股市指数回报时,有很多潜在因素可供选择,并且有可用的统计检验可以证明每个因子的预测能力。但是本文的目的是为了展示预测的过程而非结果,因此在因子选择上不会作过多的分析。考虑到全球资本市场联系越来越紧密,本文使用全球几大主要指数(如道琼斯、日经225、德国DAX指数等)以及上证综指自身的滞后值作为预测因子。

第三,估计模型并对模型的预测性能进行评价。衡量预测准确率的方法有很多,包括均方差(Mean-Squared Error,MSE)、平均绝对离差(Mean Absolute Deviation,MAD)和均方根误差(Root-Mean-Squared Error,RMSE)等。本文关注的是预测准确次数的百分比,并进一步使用混淆矩阵和ROC曲线评价预测性能。

4

Python实现代码

#先引入后面可能用到的包(package)
import pandas as pd  
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns  
sns.set()  
%matplotlib inline   

#正常显示画图时出现的中文和负号
from pylab import mpl
mpl.rcParams['font.sans-serif']=['SimHei']
mpl.rcParams['axes.unicode_minus']=False

获取数据

import datetime
import yfinance as yf
def get_data(stocks,start_date,end_date):
    '''stocks为股票代码和简称字典'''
    data=pd.DataFrame()
    for code,name in stocks.items():
        dd = yf.Ticker(code)
        data[name]=dd.history(start=start_date,end=end_date)['Close']
    data=data.iloc[2:,:].fillna(method='ffill')
    return data
StockIndexs = {
    '000001.SS':'上证综指',
    '^DJI':'道琼斯',
    '^IXIC':'纳斯达克',
    '^N225':'日经225',
    '^HSI' :'香港恒生',
    '^FCHI':'法国CAC40', 
    '^GDAXI':'德国DAX'}
#获取数据
start_date='2008-01-01'
end_date='2019-10-31'
data=get_data(StockIndexs ,start_date,end_date)
data.head()

【手把手教你】使用Logistic回归、LDA和QDA模型预测指数涨跌_第2张图片

#累计收益率
(data/data.iloc[0]).plot(figsize=(14,6))
plt.title('全球主要指数累计净值\n (2008-2019)',size=15)
plt.show()

【手把手教你】使用Logistic回归、LDA和QDA模型预测指数涨跌_第3张图片

#收益率相关性
ret=data.apply(lambda x : (x/x.shift(1)-1)*100).dropna()
sns.clustermap(ret.corr())
plt.title('全球主要指数相关性',size=15)
plt.show()

【手把手教你】使用Logistic回归、LDA和QDA模型预测指数涨跌_第4张图片

上图清晰地显示出,自金融危机(2008)以来,全球股价指数中,美国纳斯达克遥遥领先,累计涨幅接近四倍,而A股则不忍直视,十年净值从1跌至0.5附近,与中国过去十年经济的快速发展形成强烈反差。不过,换一种角度看,当前的价值洼地是不是意味着未来上涨的潜力更大呢?尤其是新一轮技术革命的兴起,拭目以待。另外,再来看一下全球各大指数收益率的相关性,从图中可以看出,道琼斯和纳斯达克相关系数、法国CAC40和德国DAX指数相关系数均超过0.9,而香港恒生指数、上证综指和日经225相关系数也比较高,为了减小预测因子之间由于高度相关性带来的偏差,本文只选择上证综指滞后1、2期、道琼斯、德国DAX和日经225指数的滞后一期值作为预测因子。


def get_variables(ret):

    '''ret为上述指数收益率数据'''
    df=pd.DataFrame()
    df['lag1']=ret['上证综指'].shift(1)
    df['lag2']=ret['上证综指'].shift(2)
    df['DJ']=ret['道琼斯'].shift(1)
    df['DA']=ret['德国DAX'].shift(1)
    df['RJ']=ret['日经225'].shift(1)
    #上涨用1表示,下跌(含0)为-1
    df['direction']=np.where(ret['上证综指'].values>0,1.0,-1.0)
    df=df.dropna()
    return df
#引入机器学习库
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
from sklearn.discriminant_analysis import QuadraticDiscriminantAnalysis as QDA
from sklearn.metrics import accuracy_score,classification_report,confusion_matrix,roc_curve
def fit_model(name, model, X_train, y_train, X_test, pred):
    """使用LR, LDA 、QDA对数据进行模型拟合
    """
    # Fit and predict the model on the training, and then test, data
    model.fit(X_train, y_train)
    pred[name] = model.predict(X_test)
    # 预测准确率
    score=accuracy_score(pred['Actual'], pred[name])
    print("%s模型: %.3f" % (name, score)) 
if __name__ == "__main__":
    #获取数据
    variables = get_variables(ret).loc[:'2017']
    # 涨跌方向为响应变量, 其余变量为自变量
    X = variables.iloc[:,:-1]
    y = variables.iloc[:,-1]
    # 把原始数据的40%作为测试数据,其余为训练数据
    #X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.4)
    #以2017年以前数据为训练集,2017年数据为测试集
    start_test = datetime.datetime(2017,1,1)
    X_train = X[X.index < start_test]
    X_test = X[X.index >= start_test]
    y_train = y[y.index < start_test]
    y_test = y[y.index >= start_test]
    # 创建预测数据框
    pred = pd.DataFrame(index=y_test.index)
    pred["Actual"] = y_test
    # 分别拟合LR, LDA 和QDA三个模型  
    print ("预测准确率:")
    models = [("LR", LogisticRegression(solver='liblinear')), ("LDA", LDA()), ("QDA", QDA())]
    for m in models:
        fit_model(m[0], m[1], X_train, y_train, X_test, pred)
预测准确率:
LR模型: 0.557
LDA模型: 0.566
QDA模型: 0.590

从模型的预测准确率来看,并不是很理想,三个模型中使用QDA的效果要好一些(可能因为训练数据集样本较多,而测试集分配较少),准确率也只有0.59,只比投掷硬币好一丁点。下面以QDA模型为例,使用混淆矩阵和ROC曲线对模型的预测性能作进一步评价,混淆矩阵图没有给出,ROC曲线图很直观,此处不过多分析。


model=QDA()

model.fit(X_train, y_train)
# 模型在测试数据集上的预测
pred = model.predict(X_test)
# 构建混淆矩阵
cm = pd.crosstab(y_test,pred)
cm
# 绘制混淆矩阵图
#sns.heatmap(cm, annot = True, cmap = 'GnBu', fmt = 'd')
print('模型的准确率为:\n',accuracy_score(y_test, pred))
print('模型的评估报告:\n',classification_report(y_test, pred))
模型的准确率为:
 0.59016
模型的评估报告:
               precision    recall  f1-score   support

        -1.0       0.60      0.03      0.06       101
         1.0       0.59      0.99      0.74       143

    accuracy                           0.59       244
   macro avg       0.59      0.51      0.40       244
weighted avg       0.59      0.59      0.46       244


# 计算正例的预测概率,而非实际的预测值,用于生成ROC曲线的数据

y_score = model.predict_proba(X_test)[:,1]
#fpr表示1-Specificity,tpr表示Sensitivity
fpr,tpr,threshold = roc_curve(y_test, y_score)
# 计算AUC的值
roc_auc = metrics.auc(fpr,tpr)
# 绘制面积图
plt.figure(figsize=(8,6))
plt.stackplot(fpr, tpr, color='steelblue', alpha = 0.5, edgecolor = 'black')
plt.plot(fpr, tpr, color='black', lw = 1)
# 添加对角线
plt.plot([0,1],[0,1], color = 'red', linestyle = '--')
# 添加文本信息
plt.text(0.5,0.3,'ROC曲线 (area = %0.2f)' % roc_auc)
# 添加x轴与y轴标签
plt.title('QDA模型预测指数涨跌的ROC曲线',size=15)
plt.xlabel('1-Specificity')
plt.ylabel('Sensitivity')
plt.show()

【手把手教你】使用Logistic回归、LDA和QDA模型预测指数涨跌_第5张图片

5

结语

本文主要以逻辑回归(LR)、线性判别分析(LDR)、二次线性判别分析(QDR)为例,展示了机器学习算法在股价涨跌预测上的应用。模型预测效果不甚理想,并不代表机器学习算法的失效,主要原因可能是没有选择好合适的预测的因子,关于什么样的因子才是预测股指涨跌的最好因素留待读者自己去挖掘(这也是量化研究者孜孜不倦在追求的)。

最后,值得关注的是,人工智能本质上是一种非线性算法,非线性的拟合度一般会比较高,但是策略参数可能不稳健,容易出现过拟合的现象,一般只要神经元足够多,可以逼近任何一个函数。机器学习在消费者行为和诈骗行为的模式识别上成功率较高,主要是因为这些模式具有较长的持续期。然而,要利用这些算法对金融市场进行预测,效果则要大打折扣。主要原因可能是,相对于可以获取的大量相互独立的消费者行为和信用卡数据,我们能够获取的在统计学意义上的相互独立的金融数据的数量非常有限。此外,机器学习、深度学习是黑箱,但策略的改进依靠金融逻辑,最好能知道赚的谁的钱,策略为什么能赚钱

参考资料:

1. scikit-learn官网:https://scikit-learn.org/stable/

2. Forecasting Financial Time Series - Part I:https://www.quantstart.com/articles/Forecasting-Financial-Time-Series-Part-1

3. 雅虎数据API:https://pypi.org/project/yfinance/

关于Python金融量化

专注于分享Python在金融量化领域的应用。加入知识星球,可以免费获取量化投资视频资料、量化金融相关PDF资料、公众号文章Python完整源码、量化投资前沿分析框架,与博主直接交流、结识圈内朋友等。

你可能感兴趣的:(【手把手教你】使用Logistic回归、LDA和QDA模型预测指数涨跌)