用Python 超簡單選股與回測 – 讓你投資少繳點學費!

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

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

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

先寫好策略框架

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

pseudo code

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

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

前三年股票的狀況

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

lookback three years

# 拿取近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

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

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

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

回測

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

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]

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

完整的範例

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種技術指標,找一個你喜歡的吧!

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

FinLab - 韓承佑

嗨大家好,我是韓承佑,FinLab創辦人,畢業於巴黎第十一大學資工博士,目前擔任臺灣量化交易協會 學術顧問、台北商業大學 創新育成中心 創業技術顧問與上市科技公司 量化交易顧問。當初,我喜歡寫程式、無意間因為軟體比賽接觸Fintech,從此開始了財經跟程式的學習之路。我們成立 FinLab 量化投資部落格,用自己研發的軟體,對台灣股市做大量快速的實驗。希望可以在量化投資的路上,當大家的「武器製造商」!