1 引言

機器學習(Machine Learning)是人工智慧(AI)的重要組成部分,目前已廣泛應用於資料探勘、自然語言處理、信用卡欺詐檢測、證券市場分析等領域。量化投資作為機器學習在投資領域內最典型的應用之一,已經越來越廣泛的出現在我們的視野中。

機器學習可簡單理解為利用統計模型或演算法擬合樣本資料並進行預測,其模型演算法根據學習任務可以分為分類、迴歸和聚類。分類方法是對離散型隨機變數建模或預測的監督學習方法。分類是按照某種標準給物件貼標籤,再根據標籤來區分歸類。而所謂的學習,其本質就是找到特徵與標籤間的關係(mapping 即對映)。換句話說,分類預測模型是一個從輸入特徵到離散的輸出標籤之間的對映函式。分類方法常用的有樸素貝葉斯、分類樹、支援向量機、KNN、整合學習(包括隨機森林)和深度學習等。

股票指數漲跌的預測本質上是一個分類問題。機器演算法有很多種,沒有最好的模型,只有更適合解決當前問題的演算法。當然,本文的目的不是尋找最優的演算法,而是機器學習在分析預測的應用。基於此,本文以Logistic 迴歸(LR)、線性判別法(LDA)、 二次判別分析(QDA)模型為例,對上證綜指的漲跌進行預測。後續推文將會應用其他可能更有效的演算法,包括SVM、整合學習和深度學習等。

【手把手教你】使用Logistic迴歸、LDA和QDA模型預測指數漲跌

2 演算法基本原理

邏輯迴歸(LR)

邏輯迴歸(Logistic Regression,LR)是廣義線性迴歸分析模型之一,其本質屬於分類問題,因此主要用於被解釋變數為分類(離散,如0,1)變數的情形。在分類問題上,邏輯迴歸要優於線性迴歸,因為線性迴歸在擬合被解釋變數為離散時會出現負機率的情況,會導致錯誤的樣本分類。而邏輯迴歸採用對數函式將預測範圍壓縮到0與1之間,從而提升預測準確率。

假設L1,L2,…Ln為預測因子,LR模型使用對數公式對上漲(U)的條件機率建立以下模型:

【手把手教你】使用Logistic迴歸、LDA和QDA模型預測指數漲跌

一般使用最大似然法來擬合上述模型。關於邏輯迴歸的詳細推導此處不再展開,想進一步瞭解的可以找本計量經濟學入門教材學習(如伍德里奇的《計量經濟學導論》)。機器學習庫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模型預測指數漲跌

#累計收益率

(data/data。iloc[0])。plot(figsize=(14,6))

plt。title(‘全球主要指數累計淨值\n (2008-2019)’,size=15)

plt。show()

【手把手教你】使用Logistic迴歸、LDA和QDA模型預測指數漲跌

#收益率相關性

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模型預測指數漲跌

上圖清晰地顯示出,自金融危機(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 結語

本文主要以邏輯迴歸(LR)、線性判別分析(LDR)、二次線性判別分析(QDR)為例,展示了機器學習演算法在股價漲跌預測上的應用。模型預測效果不甚理想,並不代表機器學習演算法的失效,主要原因可能是沒有選擇好合適的預測的因子,關於什麼樣的因子才是預測股指漲跌的最好因素留待讀者自己去挖掘(這也是量化研究者孜孜不倦在追求的)。

最後,值得關注的是,人工智慧本質上是一種非線性演算法,非線性的擬合度一般會比較高,但是策略引數可能不穩健,容易出現過擬合的現象,一般只要神經元足夠多,可以逼近任何一個函式。機器學習在消費者行為和詐騙行為的模式識別上成功率較高,主要是因為這些模式具有較長的持續期。然而,要利用這些演算法對金融市場進行預測,效果則要大打折扣。主要原因可能是,相對於可以獲取的大量相互獨立的消費者行為和信用卡資料,我們能夠獲取的在統計學意義上的相互獨立的金融資料的數量非常有限。此外,機器學習、深度學習是黑箱,但策略的改進依靠金融邏輯,最好能知道賺的誰的錢,策略為什麼能賺錢。

參考資料:

1。 scikit-learn官網:

https://

scikit-learn。org/

stable/

2。 Forecasting Financial Time Series - Part I

3。 雅虎資料API:

https://

pypi。org/project/yfinan

ce/