今天分享一個簡單的XGBoost選股模型。
導入包
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 解決坐標軸刻度負號亂碼
plt.rcParams['axes.unicode_minus'] = False
# 解決中文亂碼問題
plt.rcParams['font.sans-serif'] = ['Simhei']
一、讀入數(shù)據(jù)
我們的數(shù)據(jù)是滬深300成分股2013-2016年每個季度的包括盤面信息、基本面信息的17個因子特征。
data = pd.read_csv("300Data.csv")
data.head()

源數(shù)據(jù)
可以看一下我們的17個因子:
data['factor'].unique()

因子特征
二、特征工程
我們首先要定義label:
下個季度股價漲跌幅超過滬深300指數(shù)的漲跌幅,則我們認為本季度該股票應該買入,label設為1;否則為0。
首先找出滬深300每個季度的漲跌幅作為benchmark,計算出每只成分股每個季度相對于基準的漲跌幅。
# 獲取每只個股每個季度的漲跌幅
p_chg_df=data[data['factor']=='漲跌幅']
# 找到滬深300的季度漲跌幅
p_chg300=p_chg_df[p_chg_df['stock']=='滬深300']
p_chg300

滬深300季度漲跌幅
# 計算每只成分股每個季度相對于基準的漲跌幅
for col in p_chg_df.columns[2:]:
p_chg_df[col]=p_chg_df[col] - p_chg300.loc[563,col] # 滬深300的索引是563,這一步計算每只股票對于滬深300的相對漲幅
p_chg_df.head()

季度相對漲跌幅
這里需要注意,我們計算的是本季度相對于基準的漲跌幅,但我們本季度【是否買入】應該根據(jù)下一季度相對漲跌幅的情況而定。
# 獲取下一季度的超漲跌幅
p_chg_df.iloc[:,2:]=p_chg_df.iloc[:,2:].shift(-1,axis=1)
p_chg_df.head()

下一季度相對漲跌幅
接來下我們需要將每只股票每個季度的17個因子提出來,作為一條記錄。
# 定義數(shù)據(jù)轉換函數(shù),行名為股票名,列名為特征名
def transform_data(df, value):
result = (df.pivot_table(index=df['stock'], columns=[
df['factor']], values=value).reset_index())
return(result)
header=[i for i in data.columns if 'Q' in i] # 存儲每個季度的名字
column=list(data['factor'].unique()) # 存儲特征名
column.append('超漲幅')
stock_df = pd.DataFrame(columns = column)
for col in header:
df=transform_data(data, col)
df=df.merge(p_chg_df[['stock',col]],on='stock',how='left')
df.rename(columns={col:'超漲幅'},inplace=True)
stock_df=stock_df.append(df,ignore_index=True)
stock_df['是否買入']=np.where(stock_df['超漲幅']>0,1,0)
stock_df.head()

特征
# 空值處理:由于如果用填充法需要行業(yè)數(shù)據(jù),我們這里直接簡單粗暴刪除空值行
stock_df1=stock_df.dropna(axis=0)
stock=stock_df1.copy()
y=stock['是否買入'].values
del stock['stock']
del stock['超漲幅']
del stock['是否買入']
X=stock.values
print('特征維度是{}\n標簽維度是{}'.format(X.shape,y.shape))

image.png
pd.Series(y).value_counts()

標簽分布
可以看到,漲跌記錄數(shù)量相差不大,不存在數(shù)據(jù)不平衡的問題。
三、建立XGBoost模型
# 劃分訓練集和測試集
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.3,random_state=420)
import xgboost as xgb
from sklearn import metrics
dtrain=xgb.DMatrix(X_train,label=y_train)
dtest=xgb.DMatrix(X_test)
# 給定模型參數(shù)
params={'booster':'gbtree',
'objective': 'binary:logistic',
'eval_metric': 'auc',
'max_depth':8,
'gamma':0,
'lambda':2,
'subsample':0.7,
'colsample_bytree':0.8,
'min_child_weight':3,
'eta': 0.2,
'nthread':8,
'silent':1}
# 用于觀察弱分類器訓練過程
watchlist = [(dtrain,'train')]
# 建立xgboost模型
bst=xgb.train(params,dtrain,num_boost_round=100,evals=watchlist)
# 漲跌概率
y_prob=bst.predict(dtest)
# 設置閾值, 輸出一些評價指標
y_pred = (ypred >= 0.5)*1
# 獲取真陽率、偽陽率、閾值
fpr_xgb,tpr_xgb,threshold_xgb = metrics.roc_curve(y_test,y_prob)
auc_xgb = metrics.auc(fpr_xgb,tpr_xgb) # AUC得分
score_xgb = metrics.accuracy_score(y_test,y_pred) # 模型準確率
print([score_xgb,auc_xgb])
最終預測準確率為57.62%,AUC值為0.6096。
可以看到,這個結果并不高。當然,一是因為股市預測中擾動因素太多,很難有非常高的預測準確率,二是本例僅僅是一個簡單的建模過程,大家有興趣,可以通過選取其他因子、重新定義標簽、對xgboost模型進一步調參(小白這里模型參數(shù)都是直接從我別的案例里拷過來的并未調參)等方式改進,相信會有一個更佳的結果。