2020年2月19日 星期三

[Pandas 練習/投資] 一筆錢無腦買進 0050,賺錢的機率是多少?


投資理財經典《漫步華爾街》作者推崇長期持有一籃子股票的指數型投資。
買進並持有大盤,只要大盤長期來說有向上的趨勢,加上持有的時間「夠久」,幾乎一定能獲利。夠久是多久呢?以美國股票為例,在 1950-2013 年間任何時刻一次買入,持有 15 年,就能保證獲利了。
台股應該也有類似的現象。我試著以台灣 50 (0050) 為例,練習用 pandas 來回答以下問題:
持有 0050 不同時間 (一個月、一年、三年、五年) 的獲利機會多少?平均年化利率為多少?
先上答案:
從 2007/12/31 開始,於任何一天買入,持有 0050 不同時間的獲利機會與年化報酬如下表:
持有時間 1 個月 1年 3 年 5 年
獲利機會 59% 69.9% 89.7% 93%
平均年化獲利 7.35% 8.26% 7.5% 7.3%
因為容易找得到的資料從 2007 年底開始,數據不像美國股市統計起來這麼有代表性。但同樣可以看見,長期持有的獲利機會比起持有短時間大幅提高。

事前準備

1. 找資料

我選擇在 Yahoo! finance 下載 0050 的資料。在歷史資料內,時間範圍選擇 maximum ,可以下載所有年度的價格資訊,可惜 0050 的資料只從 2007 年底開始。我們將使用「還原價格」幫助我們計算。

2. 看資料

import datetime
import pandas as pd
import matplotlib.pyplot as plt

data = pd.read_csv("0050.TW.csv")
data.head()
使用 pandas 的 read_csv 函數,可以讀取 csv 表格。看一下資料內容如下:
我們計算獲利最重要的還原收盤價 Adj Close 已包含在內,這讓我們不用去處理除權息。

3. 建立方便計算的表格

我們想回答的問題是,持有 n 天後的獲利是多少。若 n 天後是假日,表格內是沒有資料的。為了方便計算,我們需要做一張含有所有日期的表格,並在假日填入下一開盤日的價格。也就是說,如果預計賣出那天是假日,就以下一開盤日的價格當做賣出價。
# 以資料最早的那天做為開始日,並以今天當做結束日,建立一個只有日期的表格
start_date = data["Date"][0]
end_date = datetime.date.today().isoformat()

date_rng = pd.date_range(start=start_date, end=end_date, freq='D')
df = pd.DataFrame(date_rng,columns=['Date'])
再一行,就能把空表格和價格表以日期為索引,合併成一張,此時假日的價格仍是空的。
df = df.set_index('Date').join(data.set_index('Date'))
使用 bfill() 函數,把數值往回填入無數值的格子。這樣就把下一營業日的價格回填了。
df = df.bfill()
表格最末可能是假日,無更多資料可以回填。直接捨棄這部分。
# 去除 N/A
df = df.dropna()
以上三行可以串接起來,一次搞定:
df = df.set_index('Date').join(data.set_index('Date')).bfill().dropna()
表格現在如下,多出了 1/1,並已填入 1/2 的價格了。至此,每一列之間都是固定一天的差距了。

輕鬆得到想要的資料

接下來就是 pandas 操作表格方便的地方!把表格平移 30 列 (30 天) ,除以原表格的值,就變成任一天買進,持有 30 天的獲利率了。
# 一行得到時間範圍內,持有 30 天的報酬
df_30d_rate=(df["Adj Close"].shift(-30)/df["Adj Close"]).dropna()
df_30d_rate.describe()
結果如下:
count    4399.000000
mean        1.006127
std         0.053622
min         0.631027
25%         0.978523
50%         1.011056
75%         1.038577
max         1.247586
Name: Adj Close, dtype: float64
在這 4399 個日子中,持有 30 天賣掉的平均獲利是 0.61%,一年大約是 0.61% * 12,約 7.3%。把曲線畫出來看看,振動的相當厲害呀!
df_30d_rate.plot()

持有 0050 一個月的總獲利 (期末/期初)
如果是持有五年呢?除了振動較小外,只在 2008 年出現虧損 (期末/期初<1),其他時間都是賺錢的。這也意味只要分批買入,例如以定期定額的方式進行,就可以避免買在最貴的時候,得到平均的報酬。

持有 0050 五年的總獲利 (期末/期初)
勝率是多少呢?這裡,用 dataframe/series 的篩選功能.

# filter 會是一個 true/false 的 series, >1 的日子也就是賺錢的日子會是 true
filter = df_30d_rate > 1

# 可以用 filter 來篩選資料,算出賺錢機會
print(df_30d_rate[filter].count()/df_30d_rate.count())

# 簡寫如下
print(df_30d_rate[df_30d_rate>1].count()/df_30d_rate.count())
約有 59% 機會賺錢。
0.5917253921345761
重覆用以上的方法,就能回答一開始的問題了。記得轉為年化報酬時,要把複利的效應考慮進來。

沒有留言

張貼留言