iT邦幫忙

2021 iThome 鐵人賽

DAY 17
0
AI & Data

從零開始的套牢生活 - AI股票預測系統系列 第 17

[Day 17] 有人提到股利嗎?

  • 分享至 

  • xImage
  •  

一、前言

承接上一回[Day 16] 保守型投資 - 「只買不賣」策略真的賺?
上次提到「只買不賣」策略的報酬率和定存差不多,
然而可能會有人拿著存摺打我臉:「我明明靠這招存到退休金啊? 在唬爛啊!」,
先別急,容我解釋一番,理由很很複雜,這邊先不提了,簡單來說就是我忘了考慮股利

如果要把股利的因素放進回測裡,最直觀的想法可能是:
「在除息日當天,若有持股,則會在付息日給本金相應的股息」
然而很可惜這招會讓策略忽略的機會成本的概念,打個比方

台積電在2019年6月24日進行了除息,當天股價為241,股息為每股8元
而在上一個交易日6月21日股價為248.5,
乍看之下在6/21買進的股價較高,然而事實上6/24買進雖省下了248.5-241 = 7.5元,
但是卻少賺了股息8元,整體來說還倒賠0.5元。

因此現在的回測分析普遍使用「調整過去股價」的方法,具體作法為:
6/24日股價維持241元,6/21日股價會減去股息,調整後的價格為240.5。
該作法的另一個好處即是讓股價正確反映股票價值,
以更極端的拆分為例:
如果今天公司以0.5的比例拆分股票,
則股價的兩日價差甚至會直接砍半(因為股票數量加倍,但是股本不變),
然而該公司的價值並不會因為股票拆分而砍半,砍半的僅僅是每張的股票價值而已,
如果是用調整股價進行分析,則拆分股票的行為並不會對調整股價有所影響。

二、調整股價

股利政策表

url = "https://api.finmindtrade.com/api/v4/data"
parameter = {
    "dataset": "TaiwanStockDividend",
    "start_date": datetime.datetime(2020, 1, 1, 0, 0).strftime("%Y-%m-%d"),
    "end_date": datetime.datetime(2021, 12, 31, 0, 0).strftime("%Y-%m-%d"),
    "data_id": stock_index,
}

data = requests.get(url, params=parameter)
data = data.json()

div_df = pd.DataFrame(data["data"])
div_df = div_df[["CashExDividendTradingDate", "CashEarningsDistribution"]]
div_df = div_df.rename({"CashExDividendTradingDate": "date"}, axis=1)
div_df = div_df.rename({"CashEarningsDistribution": "Dividends"}, axis=1)
div_df = div_df.set_index("date")
div_df

date Dividends
2020-03-19 2.50000
2020-06-18 2.50000
2020-09-17 2.50000
2020-12-17 2.50000
2021-03-17 2.50000
2021-06-17 2.50000
2021-09-16 2.75000
2021-12-16 2.75000

調整股價

調整公式為:
第一次調息因數 = 1 + (股息 / 除息日前一天收盤價)
第一次調整股價 = 除息日原始收盤價 / 第一次調息因數

第二次調息因數 = 第一次調息因數 * (1 + (股息 / 除息日前一天收盤價))
第二次調整股價 = 除息日原始收盤價 / 第二次調息因數

第三次之後的調整以此類推,公式從yahoo finance資料反推而來,

  • 為什麼不用yahoo finance的Adj close資料?
    • 因為台股資料有誤,有一半以上的股息調整都是錯的(少算除息日)。
  • 為什麼算出來的資料和yahoo finance不同?
    • 因為公式是近似值,由於小數點問題會有些微差異,如果要求精確值還要繼續推,但我今天已經花兩小時算數學了。
adj_df = etf50[stock_index].join(div_df)

adj_df["F"] = (1 + adj_df["Dividends"] / adj_df["Close"]).shift(-1)
adj_df["Factor"] = adj_df.iloc[::-1]["F"].cumprod().iloc[::-1].bfill().fillna(1)
adj_df["Adj Close"] = adj_df["Close"] / adj_df["Factor"]
adj_df = adj_df.drop(columns=["Dividends", "F", "Factor"]).dropna()
adj_df

三、參考


上一篇
[Day 16] 保守型投資 - 「只買不賣」策略真的賺?
下一篇
[Day 18] 資料整理
系列文
從零開始的套牢生活 - AI股票預測系統30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言