繼使用SVM預測大盤漲跌, 使用決策樹預測大盤漲跌後的第三個預測大盤漲跌的模型。包括調參的過程以及模型穩健性驗證。
- 經過調參之後,預測準确率可以達到平均90%,上下波動範圍約10%。
- 看到預測的準确率還不錯,我提取出了特征的權重值,來思考決策樹為什麼預測準确率還不錯。然後做了政策不加預測大盤和加上預測大盤的對比。可以發現,一般的政策(例如純随機,小于4購買)在加上了對大盤預測的系數後,收益不一定得到了明顯提高,但是風險得到了明顯的降低。
- 經過提取特征值比對,發現對于未來30個交易日後的漲跌預測,最重要的是前30個交易日的最小值,最大值,以及日變化,可以簡單了解為市場震蕩程度。
- 可以認為決策樹預測大盤是有效的,對量化交易有一定的積極作用。
import numpy as np
import pandas as pd
from CAL.PyCAL import Date
from CAL.PyCAL import Calendar
from CAL.PyCAL import BizDayConvention
from sklearn.tree import DecisionTreeClassifier
start = '2014-01-01' # 回測起始時間
end = '2016-12-01' # 回測結束時間
benchmark = 'HS300' # 政策參考标準
universe = set_universe('HS300') # 證券池,支援股票和基金
capital_base = 100000 # 起始資金
freq = 'd' # 政策類型,'d'表示日間政策使用日dw線回測,'m'表示日内政策使用分鐘線回測
refresh_rate = 1 # 調倉頻率,表示執行handle_data的時間間隔,若freq = 'd'時間間隔的機關為交易日,若freq = 'm'時間間隔為分鐘
處理資料
×
1
fields = ['tradeDate','closeIndex', 'highestIndex','lowestIndex', 'turnoverVol','CHG','CHGPct']
2
stock = '000300'
3
#tradeDate是交易日、closeIndex是收盤指數、highestIndex是當日最大指數,lowestIndex是當日最小指數,CHG是漲跌
4
index_raw = DataAPI.MktIdxdGet(ticker=stock,beginDate=u"2006-03-01",endDate=u"2015-03-01",field=fields,pandas="1")
5
#擷取2006年3月1日到2015年3月1日,上一行代碼設定的所有索引的相關資訊。
6
7
index_date = index_raw.set_index('tradeDate')
8
index_date = index_date.dropna()
9
index_date['max_difference'] = index_date['highestIndex'] - index_date['lowestIndex']
10
11
index_date['max_of_30day'] = None
12
index_date['min_of_30day'] = None
13
index_date['max_difference_of_30day'] = None
14
index_date['closeIndex_after30days'] = None
15
#預設需要處理的值為None,友善之後直接用dropna函數去掉無效資料
16
17
for i in xrange(len(index_date)-30):
18
#對資料進行處理
19
index_date['max_of_30day'][i+30] = max(index_date['highestIndex'][i:i+30])
20
#找出前30天最大值。
21
index_date['min_of_30day'][i+30] = min(index_date['lowestIndex'][i:i+30])
22
#找出前30天最小值
23
index_date['max_difference_of_30day'][i+30] = max(index_date['max_difference'][i:i+30])
24
#找出前30天最大日波動
25
index_date['closeIndex_after30days'][i]=index_date['closeIndex'][i+30]
26
#找出30天後的收盤價。
27
28
index_date = index_date.dropna() #去掉前30個和後30個無效的資料。
29
lables_raw = index_date['closeIndex_after30days'] #提取出需要預測的資料
30
lables = index_date['closeIndex_after30days'] > index_date['closeIndex'] #為分類處理資料,判斷30天後的收盤價是否大于今日收盤價
31
lables_ud = lables.replace({True:'up',False:'down'}) #友善他人閱讀,将True和False改為up和down,意味着30天後收盤價漲了還是跌了
32
features = index_date.drop(['closeIndex_after30days'],axis = 1) #在特征值中去掉我們要預測的資料。
在未調參之前,我們先擷取一次準确率:得到0.88
from sklearn import cross_validation
from sklearn import preprocessing
scaler = preprocessing.StandardScaler().fit(features)
features_scaler = scaler.transform(features)
#上面兩行代碼用來标準化資料
X_train,X_test, y_train, y_test = cross_validation.train_test_split(features_scaler, lables, test_size = 0.2, random_state = 0)
clf_tree = DecisionTreeClassifier(random_state=0)
clf_tree.fit(X_train, y_train)
print "預測準确率為:%0.2f" % (clf_tree.score(X_test, y_test))
然後調C值,這裡我是先讓max_depth在1~100的range跑,然後作圖
×
1
i_list = []
2
score_list = []
3
for i in range(1,100,1):
4
i=i/1.
5
clf_tree = DecisionTreeClassifier(max_depth =i ) #使用SVM分類器來判斷漲跌
6
clf_tree.fit(X_train, y_train)
7
i_list.append(i)
8
score_list.append(clf_tree.score(X_test, y_test))
9
10
score_list_df = pd.DataFrame({'max_depth':i_list,'score_list':score_list})
11
score_list_df.plot(x='max_depth' ,y='score_list',title='score change with max_depth')
然後是min_samples_leaf值,同理。這裡是從0.1到10變動範圍
i_list = []
score_list = []
for i in range(1,100,1):
i=i/10.
clf_tree = DecisionTreeClassifier(min_samples_leaf = i )
clf_tree.fit(X_train, y_train)
i_list.append(i)
score_list.append(clf_tree.score(X_test, y_test))
score_list_df = pd.DataFrame({'min_samples_leaf':i_list,'score_list':score_list})
score_list_df.plot(x='min_samples_leaf' ,y='score_list',title='score change with min_samples_leaf')
然後是min_samples,這裡選用2~50
i_list = []
score_list = []
min_samples = range(2,50,1)
for i in min_samples :
clf_tree = DecisionTreeClassifier(min_samples_split = i )
clf_tree.fit(X_train, y_train)
i_list.append(i)
score_list.append(clf_tree.score(X_test, y_test))
score_list_df = pd.DataFrame({'min_samples_split':i_list,'score_list':score_list})
score_list_df.plot(x='min_samples_split' ,y='score_list',title='score change with min_samples_split')
然後是min_weight_fraction_leaf……自己看圖。發現調整這個參數對模型影響意義并不大。
i_list = []
score_list = []
min_weight = [x /1000. for x in range(1,500,1)]
for i in min_weight :
clf_tree = DecisionTreeClassifier(min_weight_fraction_leaf = i )
clf_tree.fit(X_train, y_train)
i_list.append(i)
score_list.append(clf_tree.score(X_test, y_test))
score_list_df = pd.DataFrame({'min_weight_fraction_leaf':i_list,'score_list':score_list})
score_list_df.plot(x='min_weight_fraction_leaf' ,y='score_list',title='score change with min_weight')
知道了大緻參數最優範圍以後,我們使用grisearchCV在這個範圍内找到最優解。
from sklearn.grid_search import GridSearchCV
from sklearn.cross_validation import ShuffleSplit
params = {'max_depth':range(1,50,1),'min_samples_split':range(2,50,1)}
X_train,X_test, y_train, y_test = cross_validation.train_test_split(features_scaler, lables, test_size = 0.2, random_state = 0)
clf_tree = DecisionTreeClassifier()
# cv_sets = ShuffleSplit(X_train.shape[0], n_iter = 10, test_size = 0.20, random_state = 0)
grid = GridSearchCV(clf_tree, params)
grid = grid.fit(X_train, y_train)
print grid.best_estimator_
然後在最優解的基礎上再次計算一次準确率
from sklearn import cross_validation
from sklearn import preprocessing
scaler = preprocessing.StandardScaler().fit(features)
features_scaler = scaler.transform(features)
#上面兩行代碼用來标準化資料
X_train,X_test, y_train, y_test = cross_validation.train_test_split(features_scaler, lables, test_size = 0.2, random_state = 0)
clf_tree = DecisionTreeClassifier(max_depth = 32 , min_samples_split=3 )
clf_tree.fit(X_train, y_train)
print "預測準确率為:%0.2f" % (clf_tree.score(X_test, y_test))
然後我們通過傳回資料重要性,來看看對我們決策樹哪些特征影響最大。
通過下方的表格(和直方圖)發現影響最大的特征主要是30日的最小值,30日的最大值以及30日的最大日波動,然後是當日交易量。可以簡單認為,前30日的波動幅度,最大震蕩能一定程度上反映出未來30日後的市場漲跌趨勢。
為了判斷模型是否穩健,我們讓訓練集合處于變化中,然後觀察随着訓練集合的變化,準确率的波動範圍圖。這裡采取的是1000~2500資料每10個變化一次。
從圖的表現可以看出,波動範圍0.1左右,平均線在0.88左右,大緻還算穩健……吧。
num_list = []
score_list = []
for i in xrange((len(features_scaler)-1000)/2):
num_now = len(features_scaler)%2 + 2*i +1000
X_train,X_test, y_train, y_test = cross_validation.train_test_split(features_scaler[:num_now], lables[:num_now], test_size = 0.2, random_state = 0)
clf_tree = DecisionTreeClassifier( max_depth = 32 , min_samples_split=3 ) #使用SVM分類器來判斷漲跌
clf_tree.fit(X_train, y_train)
num_list.append(num_now)
score_list.append(clf_tree.score(X_test, y_test))
score_list_df = pd.DataFrame({'sets_num':num_list,'accuracy':score_list})
score_list_df.plot(x='sets_num' ,y='accuracy',title='Accuracy with sets')
接下來是比對用的空白組,純随機政策(不控制風險,隻是随機買,1.20倍賣出)
import random
start = '2016-01-01' # 回測起始時間
end = '2016-12-01' # 回測結束時間
benchmark = 'HS300' # 政策參考标準
universe = set_universe('HS300') # 證券池,支援股票和基金
capital_base = 100000 # 起始資金
freq = 'd' # 政策類型,'d'表示日間政策使用日線回測,'m'表示日内政策使用分鐘線回測
refresh_rate = 1 # 調倉頻率,表示執行handle_data的時間間隔,若freq = 'd'時間間隔的機關為交易日,若freq = 'm'時間間隔為分鐘
def initialize(account): # 初始化虛拟賬戶狀态
pass
features_list = []
def handle_data(account):
random.shuffle(account.universe) # 随機化股票池一遍随機政策
for stock in account.universe: # 股票是股票池中的股票,并且優礦幫你自動剔除了當天停牌退市的股票
p = account.reference_price[stock] # 股票前一天的收盤價
cost = account.security_cost.get(stock) # 股票的平均持倉成本
if not cost: # 判斷目前沒有買入該股票
order_pct_to(stock, 0.10) # 将滿足條件的股票買入,總價值占虛拟帳戶的10%
elif cost and p >= cost * 1.20: # 賣出條件,當p這個價格漲幅到買入價的1.20倍;
order_to(stock, 0) # 将滿足條件的股票賣到剩餘0股,即全部賣出
然後是純随機政策基礎上,隻增加一個預測盤指的漲跌,如果預測漲,則随機買入,否則不買。和純随機政策比,的确好了一丢丢。
import random
2
start = '2016-01-01' # 回測起始時間
3
end = '2016-12-15' # 回測結束時間
4
benchmark = 'HS300' # 政策參考标準
5
universe = set_universe('HS300') # 證券池,支援股票和基金
6
capital_base = 100000 # 起始資金
7
freq = 'd' # 政策類型,'d'表示日間政策使用日線回測,'m'表示日内政策使用分鐘線回測
8
refresh_rate = 1 # 調倉頻率,表示執行handle_data的時間間隔,若freq = 'd'時間間隔的機關為交易日,若freq = 'm'時間間隔為分鐘
9
stock = '000300' #預測的指數,滬深300指數。和政策參考池一緻。
10
fields = ['tradeDate','closeIndex', 'highestIndex','lowestIndex', 'turnoverVol','CHG','CHGPct']
11
#tradeDate是交易日、closeIndex是收盤指數、highestIndex是當日最大指數,lowestIndex是當日最小指數,CHG是漲跌
12
13
def initialize(account): # 初始化虛拟賬戶狀态
14
pass
15
16
features_list = []
17
def handle_data(account):
18
# 生成買入清單
19
last_date = account.previous_date.strftime("%Y-%m-%d") #擷取上一個交易日日期并格式化
20
begin_date = pd.date_range(end=last_date,periods=60)[0] #擷取60日之前的交易日日期
21
begin_date = begin_date.strftime("%Y-%m-%d") #格式化這個日期
22
to_class = DataAPI.MktIdxdGet(ticker='000300',beginDate=begin_date,endDate=last_date,field=fields,pandas="1")
23
to_class = to_class.dropna()
24
to_class = to_class[-30:] #擷取我們要的30天的指數資訊
25
to_class_date = to_class.set_index('tradeDate')
26
to_class_date['max_difference'] = to_class_date['highestIndex'] - to_class_date['lowestIndex']
27
28
to_class_date_max_of_30day = max(to_class_date['highestIndex'])
29
#找出前30天最大值。
30
to_class_date_min_of_30day = min(to_class_date['lowestIndex'])
31
#找出前30天最小值
32
to_class_date_max_difference_of_30day = max(to_class_date['max_difference'])
33
#找出前30天最大日波動
34
35
features_for_predict = to_class_date[-1:]
36
features_for_predict['max_of_30day'] = to_class_date_max_of_30day
37
features_for_predict['min_of_30day'] = to_class_date_min_of_30day
38
features_for_predict['max_difference_of_30day'] = to_class_date_max_difference_of_30day
39
40
features_fp_scaler = scaler.transform(features_for_predict)
41
predict_up = clf_tree.predict(features_fp_scaler)
42
43
#預測30天後的收盤是漲還是跌。
44
random.shuffle(account.universe)
45
for stock in account.universe: # 股票是股票池中的股票,并且優礦幫你自動剔除了當天停牌退市的股票
46
p = account.reference_price[stock] # 股票前一天的收盤價
47
cost = account.security_cost.get(stock) # 股票的平均持倉成本
48
if predict_up and not cost: # 判斷目前沒有買入該股票
49
order_pct_to(stock, 0.10) # 将滿足條件的股票買入,總價值占虛拟帳戶的10%
50
elif cost and p >= cost * 1.20: # 賣出條件,當p這個價格漲幅到買入價的1.20倍;
51
order_to(stock, 0) # 将滿足條件的股票賣到剩餘0股,即全部賣出
×
1
import random
2
start = '2015-01-01' # 回測起始時間
3
end = '2016-12-15' # 回測結束時間
4
benchmark = 'HS300' # 政策參考标準
5
universe = set_universe('HS300') # 證券池,支援股票和基金
6
capital_base = 100000 # 起始資金
7
freq = 'd' # 政策類型,'d'表示日間政策使用日線回測,'m'表示日内政策使用分鐘線回測
8
refresh_rate = 1 # 調倉頻率,表示執行handle_data的時間間隔,若freq = 'd'時間間隔的機關為交易日,若freq = 'm'時間間隔為分鐘
9
stock = '000300' #預測的指數,滬深300指數。和政策參考池一緻。
10
fields = ['tradeDate','closeIndex', 'highestIndex','lowestIndex', 'turnoverVol','CHG','CHGPct']
11
#tradeDate是交易日、closeIndex是收盤指數、highestIndex是當日最大指數,lowestIndex是當日最小指數,CHG是漲跌
12
13
def initialize(account): # 初始化虛拟賬戶狀态
14
pass
15
16
features_list = []
17
def handle_data(account):
18
# 生成買入清單
19
last_date = account.previous_date.strftime("%Y-%m-%d") #擷取上一個交易日日期并格式化
20
begin_date = pd.date_range(end=last_date,periods=60)[0] #擷取60日之前的交易日日期
21
begin_date = begin_date.strftime("%Y-%m-%d") #格式化這個日期
22
to_class = DataAPI.MktIdxdGet(ticker='000300',beginDate=begin_date,endDate=last_date,field=fields,pandas="1")
23
to_class = to_class.dropna()
24
to_class = to_class[-30:] #擷取我們要的30天的指數資訊
25
to_class_date = to_class.set_index('tradeDate')
26
to_class_date['max_difference'] = to_class_date['highestIndex'] - to_class_date['lowestIndex']
27
28
to_class_date_max_of_30day = max(to_class_date['highestIndex'])
29
#找出前30天最大值。
30
to_class_date_min_of_30day = min(to_class_date['lowestIndex'])
31
#找出前30天最小值
32
to_class_date_max_difference_of_30day = max(to_class_date['max_difference'])
33
#找出前30天最大日波動
34
35
features_for_predict = to_class_date[-1:]
36
features_for_predict['max_of_30day'] = to_class_date_max_of_30day
37
features_for_predict['min_of_30day'] = to_class_date_min_of_30day
38
features_for_predict['max_difference_of_30day'] = to_class_date_max_difference_of_30day
39
40
features_fp_scaler = scaler.transform(features_for_predict)
41
predict_up = clf_tree.predict(features_fp_scaler)
42
43
#預測30天後的收盤是漲還是跌。
44
random.shuffle(account.universe)
45
for stock in account.universe: # 股票是股票池中的股票,并且優礦幫你自動剔除了當天停牌退市的股票
46
p = account.reference_price[stock] # 股票前一天的收盤價
47
cost = account.security_cost.get(stock) # 股票的平均持倉成本
48
if p<4 and not cost: # 判斷目前沒有買入該股票
49
order_pct_to(stock, 0.10) # 将滿足條件的股票買入,總價值占虛拟帳戶的10%
50
elif cost and p >= cost * 1.20: # 賣出條件,當p這個價格漲幅到買入價的1.20倍;
51
order_to(stock, 0) # 将滿足條件的股票賣到剩餘0股,即全部賣出