Python 簡單選股和回測

最近在看一些比較古典而學術的策略文章,雖然已經眾所皆知,但是好像很少人用,所以今天來介紹一下,如何利用 python 實做 portfolio trading。

動手做

Portfolio Trading 的意思就是選擇一籃子股票,並且照著一定的比例買入,今天我就不介紹太詳細,先用最簡單的篩選法,選出一些股票,並觀察買入一兩年後的變化。

首先,大家必須要把之前的 Python-時間序列實作! 那一篇複習下,我們會用到當中的 close 這個成品,最好是跑五年以上的資料,會比較有可靠性。

先寫好策略框架

在開始寫策略時,先用簡單的兩三句話,來闡述所有的code在做什麼,並且逐段擴寫:

pseudo code
1
2
3
4
for 每年:
1. 先看前三年每個股票的表現
2. 篩選股票
3. 回測當年的狀況

於是接下來我們就依照這三點來建構回測。

前三年股票的狀況

假設我們目前在第year年,,我們把最近lookback_period年所有股票的股價選取出來

lookback three years
1
2
3
4
5
6
7
8
9
10
11
# 拿取近n年股票
c = close.truncate(str(year-lookback_period), str(year))
# 計算近n年最大下跌幅度
dropdown = (c.cummax() - c).max()/c.max()*100
# 計算近n年報酬率
profit = (c.iloc[-1] / c.iloc[0] - 1) * 100
# 計算近n年標準差(波動率)
std = (c/c.shift()).std() * 200

第5、8、11行是計算三個指標,當然你也可以建構自己的一些指標,我這邊就先舉這三個例子,這邊的每一行都很值得玩味,假如你都看不懂,建議你先去看 pandas 的新手教學

篩選股票

這邊我們就用剛剛計算的三個指標來選股:

select stocks
1
2
3
constraint = (std[std < 2].index &
profit[profit > 10].index &
dropdown[dropdown < 50].index)

這邊顧名思義,我們希望選取:

  • 波動率 < 2%:波動率越大代表股價變化幅度越大,我們只選波動率小的股票
  • 獲利 > 10%:近三年報酬率大於10的股票
  • 最大下跌幅度 < 50%:下跌幅度也不能太大

回測

接著就是回測,這邊的回測只求簡單算算,跟實際情況一定不一樣,這邊我們將資產均勻分佈於選出來的股票,不計算手續費,也不計算除權息、減資等等。建議有餘力的人可以用 adjust close取代普通的 close price,結果會比較準確。

backtest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 取出今年的股價
c2 = close.truncate(str(year), str(year + 1))
# 依照剛剛的條件選取股票
selected_stocks = constraint & c2.columns
print(year, '年買了',len(selected_stocks),'支股票')
# 回測
equality = c2[selected_stocks].dropna(axis=1).mean(axis=1)
total_equality = (equality / equality[0] * start_capital)
total_equality.plot(color='blue')
# 今年底的資產,變成明年初的資產
start_capital = total_equality[-1]

什麼!回測竟然只要這麼少行!!是的因為我們是平均分散所有要買的股票,所以只要把選出來的股價做平均,買入這個平均指數就可以了。由於有了前面的假設,結果會是一樣的。

完整的範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import pandas as pd
%matplotlib inline
lookback_period = 3
start_capital = 1
for year in range(2010, 2018):
# calculate performance of stocks
# -------------------------------
# 拿取近n年股票
c = close.truncate(str(year-lookback_period), str(year))
# 計算近n年最大下跌幅度
dropdown = (c.cummax() - c).max()/c.max()*100
# 計算近n年報酬率
profit = (c.iloc[-1] / c.iloc[0] - 1) * 100
# 計算近n年標準差(波動率)
std = (c/c.shift()).std()
# constraint
# ----------
constraint = (std[std < 0.02].index &
profit[profit > 10].index &
dropdown[dropdown < 50].index)
# backtest
# --------
# 取出今年的股價
c2 = close.truncate(str(year), str(year + 1))
# 依照剛剛的條件選取股票
selected_stocks = constraint & c2.columns
print(year, '年買了',len(selected_stocks),'支股票')
# 回測
equality = c2[selected_stocks].dropna(axis=1).mean(axis=1)
total_equality = (equality / equality[0] * start_capital)
total_equality.plot(color='blue')
# 今年底的資產,變成明年初的資產
start_capital = total_equality[-1]
權益曲線

這邊已知的問題是,假如你當年沒有任何股票的話,回測會有點問題喔!盡量讓每一年都持有一些股票吧!(或者debug一下XD)
可以看出前幾年獲利滿好的,近年來獲利普普,感覺還得加入其它的條件吧?
大家可以新增一些指標,並且用類似的方法做回測喔!

另外,假如想要實做更多有用的指標,可以參考:超簡單108種技術指標,找一個你喜歡的吧!

除了開高低收,想要有更多數據做回測嗎?也歡迎到
財報爬蟲
月營收爬蟲
來找找喔!

假如覺得文章不錯,那更不能錯過我們的影音課程喔!
或我們按個 鼓勵一下吧!