【Python金融量化 9- 100 】九、預測股票收益方法總結
背景介紹:
量化投資有兩大主流門派:主動投資和被動投資。這也代表了不同的學術觀點:市場到底是不是有效的?
如果市場是有效的,那我們一定能找到賺錢的辦法,只要付諸足夠的努力,再加上一點點小小的天賦,我們應該是可以賺錢的,畢竟已經有那么多先賢賺了錢,那我們一定會傾向于選擇主動投資。
如果市場是無效的,那我們即使一輩子苦苦追尋,上下求索,也永遠不可能超越市場,考慮到手續費等因素,再去除市場因素,那我們處于一個負和博弈中,并且我們相比其他人并不占任何優勢,那我們不如選擇被動投資。
如果我們要選擇主動投資的道路,那就意味著我們要求自己的預測能力超過市場中基金經理的平均水平。
這是一條很艱難的道路,我們是在用科學家的手段做藝術家的事,不過,雖然作品缺點靈魂,但總歸也還能看。
本章的重點就是,如何獲得一個對股票收益的預測?
實驗過程:
導入需要用到的包
獲取股票數據
尋找并構造預測因子
利用因子給股票池中的股票打分
檢驗收益率預測的準確性
閱讀python編程規范
實驗目標:
理解預測因子的作用
掌握使用python進行編程
熟練如何定義函數
預期結果:
能正確地運行程序,得到一個預測結果
作業
獲取18年的數據,看看能不能獨立地完成同樣的分析,對比兩次實驗的結果
2.1 基礎知識回顧
什么是預測?
站在t時刻,利用t以及t之前所有時刻所獲得的信息,對標的在t+1時刻相對于t時刻的收益率進行的預測。
當我們經過單位時間,到達t+1時刻時,我們可以檢驗我們的預測是否準確。
學過概率論的同學應該知道,對于一個連續分布,有
P ( X = X 0 ) = 0 P(X = X_0) = 0 P(X=X0 )=0
而我們的收益率很接近連續分布,自然也有類似性質。
所以,討論預測是否準確,應當有更多的工具和方法。
在本章中,我們并不深入了解什么樣的工具和方法是最好的,
只是和大家一起,使用簡單的編程工具,用最簡單的方法,
預測股票未來的收益率,并簡單判斷我們的預測是否準確。
作為本書的第一個實驗,我們控制了難度,
既沒有涉及復雜的編程,也沒有涉及嚴謹的投資理論。
目的是讓讀者能夠在簡單輕松的體驗中,初識python,踏出量化交易的第一步。
2.2 操作流程
2.2.1 步驟一:導入需要用到的包
首先我們導入TuShare包,我們在上節介紹過它:
import tushare as ts
1
然后導入Pandas包:
import pandas as pd
1
Pandas包又稱Python Data Analysis Library,是基于NumPy的一種工具,該工具為了解決數據分析任務而創建。它提供了大量可以高效地操作大型數據集所需的工具。
它最早由AQR Capital Management于2008年4月開發,并于2009年底開源出來,現在屬于PyData項目的一部分。它最初就被作為金融數據分析工具而被開發出來,因此為時間序列分析提供了很好的支持。它的名字來自于panel data和data analysis。
我們具體用到Pandas提供的方法時,會更詳細的介紹Pandas的。
接下來我們導入Matplotlib包中的pyplot模塊:
import matplotlib.pyplot as plt
1
Matplotlib是一個Python的2D繪圖庫,它能生成出版質量級別的圖形。
它可以以非常簡單的代碼,畫出漂亮的線圖、直方圖、散點圖等。
我們具體用到它提供的方法時,會更詳細的介紹它的。
2.2.2 步驟二:獲取股票數據
我們把待研究的股票圈定為上證50的成分股,以2017年的數據為研究對象。
那首先,我們就需要獲取上證50的成分股列表:
stock_list = ts.get_sz50s().code print(stock_list.head())
1
2
C:\Users\huang\Anaconda3\lib\site-packages\tushare\stock\classifying.py:266: FutureWarning: the 'parse_cols' keyword is deprecated, use 'usecols' instead ct.PAGES['sz50b']), parse_cols=[0, 4, 5]) 0 600000 1 600016 2 600019 3 600028 4 600029 Name: code, dtype: object
1
2
3
4
5
6
7
8
9
10
我們把股票列表存在了變量stock_list里面,
但它是一個什么類型的變量呢?我們用type函數看一看:
type(stock_list)
1
pandas.core.series.Series
1
這說明它是Pandas提供的數據結構Series,即系列。
我們來看一看它提供了哪些方法:
print(stock_list[0]) print(stock_list[-3:])
1
2
600000 47 601988 48 601989 49 603993 Name: code, dtype: object
1
2
3
4
5
系列中常用的方法就是索引,和Python提供的常用數據結構用法相同,很容易舉一反三。
然后,選擇一只股票,試著獲取其在考察期內的數據,我們先看600000這只股票:
daily_data0 = ts.get_hist_data(stock_list[0], start = '2017-01-01', end = '2017-12-31') print(daily_data0.head())
1
2
open high close low volume price_change p_change \ date 2017-12-29 12.52 12.62 12.59 12.51 163518.27 0.05 0.40 2017-12-28 12.60 12.66 12.54 12.53 238708.23 -0.08 -0.63 2017-12-27 12.65 12.66 12.62 12.53 327318.81 -0.02 -0.16 2017-12-26 12.57 12.66 12.64 12.56 152257.22 0.05 0.40 2017-12-25 12.61 12.68 12.60 12.56 193066.62 -0.02 -0.16 ma5 ma10 ma20 v_ma5 v_ma10 v_ma20 date 2017-12-29 12.598 12.642 12.757 214973.83 203101.34 295878.94 2017-12-28 12.604 12.646 12.774 214424.05 202959.62 306991.75 2017-12-27 12.636 12.661 12.792 208787.18 195230.47 315014.51 2017-12-26 12.654 12.674 12.807 196603.11 181843.93 319571.96 2017-12-25 12.676 12.685 12.823 202950.87 196988.30 328975.06
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
我們也可以用同樣的方法,獲取上證50全體成分股在考察期內的數據,我們把它存到字典這個數據結構中:
daily_data = {} for stock in stock_list: daily_data[stock] = ts.get_hist_data(stock, start = '2017-01-01', end = '2017-12-31') print(daily_data.keys())
1
2
3
4
dict_keys(['600000', '600016', '600019', '600028', '600029', '600030', '600036', '600048', '600050', '600104', '600111', '600276', '600309', '600340', '600519', '600547', '600585', '600606', '600690', '600703', '600887', '600958', '600999', '601006', '601088', '601166', '601169', '601186', '601211', '601229', '601288', '601318', '601328', '601336', '601360', '601390', '601398', '601601', '601628', '601668', '601688', '601766', '601800', '601818', '601857', '601878', '601881', '601988', '601989', '603993'])
1
2.2.3 步驟三:尋找并構造預測因子
尋找預測因子,這需要充分發揮我們的想象力。多閱讀別人的經驗,可以給我們更多的靈感,但也有可能禁錮我們的思維。但無論如何,我們還是從書叢中找到了一個因子,作為我們的例子。
這個因子名字叫“紅三兵”,即
連續三天陽線;
第二、三天開盤價在前一天陽線實體之內;
每天收盤價接近當天最高點;
三根陽線實體部分近似等長;
出現“紅三兵”,看漲,記為1,否則記為0。
同時滿足這5條時,買入信號即觸發,即我們給它評1分,否則評0分。
我們按照上述規則,定義Python函數,來判斷是否出現“紅三兵”的形態。
首先,我們構造一個函數,用于判斷是否連續n日都是陽線:
def is_red(data, i, n): if i > len(data) - n: return False else: res = True for j in range(i, i + n): res = res and data.close[j] > data.open[j] if not res:return False return res
1
2
3
4
5
6
7
8
9
接下來,我們構造一個函數,用于判斷是否在連續n日中,每一根陽線都在前一天的陽線實體之內:
def is_open_in_last_entity(data, i, n): if i > len(data) - n - 1: return False else: res = True for j in range(i, i + n): res = res and data.open[j] > data.open[j + 1] if not res: return False return res
1
2
3
4
5
6
7
8
9
接下來,我們構造一個函數,用于判斷在連續n日中,是否每天收盤價接近當天最高點。 那么,如何定義“接近”呢?我們姑且認為,二者相差不超過1%即為接近吧。 于是這里產生了一個參數——1%,后期可能需要優化這個參數。
def is_close_near_high(data, i, n, p = 0.01): if i > len(data) - n: return False else: res = True for j in range(i, i + n): if (data.high[j] <= 0): return False res = res and (data.high[j] - data.close[j]) / data.high[j] < p return res
1
2
3
4
5
6
7
8
9
接下來,我們構造一個函數,用于判斷連續n日中,是否每天k線實體近似等長。 那么,如何定義“近似等長”呢?我們姑且認為,最長者和最短者之差,不超過均值的80%吧。 于是這里又產生一個參數——80%,后期可能需要優化它。
def is_entity_equal(data, i, n, p = 0.8): if i > len(data) - n: return False else: Max = 0 Min = 10000 Sum = 0 for j in range(i, i + n): e = abs(data['close'][j] - data['open'][j]) if e > Max: Max = e if e < Min: Min = e Sum = Sum + e if Sum > 0 and n > 0 and (Max - Min) / (Sum / n) < p: return True else: return False
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
綜合前面四點,我們可以得到“紅三兵”的判別函數。 加上“紅三兵”的“三”,該函數共有三個參數,都可以在后期優化。
def is_red_3_soldier(data, i, p1 = 3, p2 = 0.01, p3 = 0.8): if i > len(data) - p1: return False else: res1 = is_red(data, i, p1) and is_open_in_last_entity(data, i, p1 - 1) res2 = is_close_near_high(data, i, p1, p2) and is_entity_equal(data, i, p1, p3) return res1 and res2
1
2
3
4
5
6
7
2.2.4 步驟四:利用因子給股票池中的股票打分
接下來,我們循環處理股票池中的每一只股票,找出其在考察期內,每天是否出現“紅三兵”形態。
如果某日出現“紅三兵”形態,就給該股票打1分,即預測未來一天該股票的收益率為1%,否則給該股票打0分,即預測其未來一天的收益率為0%。
red_3_s = list() for stock in stock_list: data = daily_data[stock] red3s = pd.Series(0.0, index=range(len(data))) for i in range(len(data)): if is_red_3_soldier(data, i, 3, 0.01, 0.8): red3s[i] = 0.01 else: red3s[i] = 0.00 red_3_s.append(red3s)
1
2
3
4
5
6
7
8
9
10
這樣,我們就得出了收益率的預測值。
隨便選擇幾只股票,畫個圖看看:
red_3_s[4].plot() red_3_s[9].plot() red_3_s[11].plot() plt.show()
1
2
3
4
這里,我們用到了畫圖的功能。
我們知道,這里red_3_s[i]是Pandas中提供的Series對象,
它自己直接就提供畫圖的方法,即plot,可以直接使用。
畫出圖之后,再用Matplotlib提供的show來顯示圖片。
我們可以再畫幾個圖來體驗一下,先生產一個Series,注意需要用列表來生成:
test = pd.Series([1,2,3,4,5]) print(test)
1
2
0 1 1 2 2 3 3 4 4 5 dtype: int64
1
2
3
4
5
6
分成上下兩塊,然后分別畫兩種柱狀圖:
fig, axes = plt.subplots(2, 1) test.plot(kind='bar', ax=axes[0]) test.plot(kind='barh', ax=axes[1]) plt.show()
1
2
3
4
畫線圖,并加上網格:
test.plot(grid = True) plt.show()
1
2
畫虛線圖,并增加圖例和標題:
test.plot(style='--',title='test plot',label='data') plt.legend() plt.show()
1
2
3
畫直方圖:
test.hist() plt.show()
1
2
以上,我們得出了收益率的預測值,并大致了解了Series的作圖方法。
2.2.5 步驟五:檢驗收益率預測的準確性
既已得到對未來收益的預測,
那么,很自然的,下一步就是測試一下這個預測準不準了。
關于如何判斷一個預測模型好不好,
會引入很多假設和復雜的推導,我們放在下一節來討論。
在本節中,我們采用一個簡單的方法,
對預測模型的好壞,做個初步的判斷。
我們將計算我們的每日對未來的預測收益率(1%或0%)與未來20天的真實收益率的相關系數。
當然,相關系數的定義多樣,最通行的算法是:
C o r r ( X , Y ) = E ( X ? E X ) ( Y ? E Y ) σ X σ Y \mathbf{Corr}(X,Y) = \frac{\mathbf{E}(X - EX)(Y - EY)}{\sigma_X \sigma_Y} Corr(X,Y)=σX σY E(X?EX)(Y?EY)
然而我們通常會采用一個簡化算法:
C o r r ′ ( X , Y ) = E X Y E X 2 ? E Y 2 \mathbf{Corr'}(X,Y) = \frac{\mathbf{E}XY}{\sqrt{\mathbf{E}X^2 \cdot \mathbf{E}Y^2}} Corr′(X,Y)=EX2?EY2
EXY
這個算法基于一些對行情的假設:
在利用短期數據計算收益率的期望時,直接使用考察期內收益率的均值是不恰當的,應使用長期的收益率的均值。既然沒有足夠的數據,同時又只對短期進行計算,那么,一個既方便又合理的次優解就是假設該期望是0。
同理,在利用短期數據計算預測收益率的期望時,直接記為0也是一個既方便有合理的次優解。
就我們當前的情況而言,我們雖然只預測了“紅三兵”,導致預測收益率總是非負的。但“綠三兵”是真實存在的,只是沒有計算而已。故預測其實還是對稱的,所以其均值取0是不無道理的。
由于大部分時候預測收益率是0%,這為我們大幅降低了運算量。
我們現在有了預測的序列,我們只需要再找出對應的收益率,就可以完成計算了。
這里特別要注意的一點是,我們要使用整個面板數據來計算。
這是因為我們應當假定所有股票是同質的,這樣就能放在一起算相關系數了。
如果股票不同質,則應該把不同質的因子找出來,然后控制變量后再計算。
但既然我們目前只考慮“紅三兵”這個單一因子,那么我們就應當認同其同質性。
接下來,我們用和前文一樣的方法,獲得所有待研究股票的未來20日收益率:
rtn = list() for stock in stock_list: data = daily_data[stock] r = pd.Series(0.0, index=range(len(data))) for i in range(20, len(data)): if data.close[i] > 0: r[i] = data.close[i - 20] / data.close[i] - 1 rtn.append(r)
1
2
3
4
5
6
7
這樣,我們就得到了預測收益率和未來20日真實收益率。
但我們會明顯的注意到,上述算法有很多問題,比如我們假定各股票等權重交易,不計手續費,且把持有20天的收益看作在當天就實現。
如何解決這些問題呢?我們把它作為一個課堂練習,同學們就在此處,優化上面這段程序:
#在這里優化上面一段程序:
1
下面,我們一方面計算其相關系數; 另一方面,我們還可以假設每次“紅三兵”出現,我們就買入,20日之后賣出。 我們可以計算這個策略的收益和資金曲線。
我們先計算并輸出相關系數:
sum_xy = 0 sum_x2 = 0 sum_y2 = 0 daily_rtn = pd.Series(0.0, index=range(244)) for i in range(len(stock_list)): for j in range(len(red_3_s[i])): sum_xy += red_3_s[i][j] * rtn[i][j] sum_x2 += red_3_s[i][j] * red_3_s[i][j] sum_y2 += rtn[i][j] * rtn[i][j] if red_3_s[i][j] > 0 :daily_rtn[j] += rtn[i][j] / 50.0 corr = sum_xy / pow(sum_x2 * sum_y2, 0.5) print(corr)
1
2
3
4
5
6
7
8
9
10
11
12
13
0.029917573724243494
1
上面我們計算了相關系數,但我們使用的方法遠不完美,
比如,我們并不是使用的無偏估計。
而且,像Python這么優美的語言,是一定有直接計算相關系數的函數的。
請讀者自己查找Python中計算相關系數的函數,計算出對應的相關系數:
#相關系數的計算:
1
然后我們計算出總收益率,并畫出資金曲線圖:
cum_rtn = pd.Series(0.0, index=range(len(daily_rtn))) cum_rtn[0] = daily_rtn[len(daily_rtn) - 1] for i in range(1, len(cum_rtn)): cum_rtn[i] = cum_rtn[i - 1] + daily_rtn[len(daily_rtn) - 1 - i] cum_rtn.plot() plt.show() print("收益率為:",daily_rtn.sum()*100,'%',sep='')
1
2
3
4
5
6
7
收益率為:4.635919662245827%
1
Python 應用與數據集成平臺 ROMA Connect 金融專區
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。