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

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)為例,展示了機器學習算法在股價漲跌預測上的應用。模型預測效果不甚理想,並不代表機器學習算法的失效,主要原因可能是沒有選擇好合適的預測的因子,關於什麼樣的因子才是預測股指漲跌的最好因素留待讀者自己去挖掘(這也是量化研究者孜孜不倦在追求的)。

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

關於Python金融量化

專注於分享Python在金融量化領域的應用。加入知識星球,可以免費獲取量化投資視頻資料、量化金融相關PDF資料、公眾號文章Python完整源碼、量化投資前沿分析框架,與博主直接交流、結識圈內朋友等。

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

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/


分享到:


相關文章: