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

一、前言

承接上一回[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

三、参考


<<:  [FGL] 程序开发(4) - 查询条件输入(QBE: Query By Example)

>>:  成为工具人应有的工具包-07 IEHistoryView

[Day29] 不敢把聊天纪录上传到分析网站? 自己用Python分析LINE聊天纪录!

大家会不会很好奇跟朋友在LINE上最常讲的话是什麽? 或是跟朋友讲了几通电话呢? 前阵子很流行把LI...

系统分析师的养成之路—案例分享(1)

上周我跟大家分享了系统分析师必须具备的「观察」、「商业思维」、「聆听」共3个软实力,但在讲述下一个主...

参考监视器的非必需属性-高凝聚力(High cohesion)

-安德森报告和TCSEC 1972年,James P. Anderson&Co.在着名的Ander...

[前端暴龙机,Vue2.x 进化 Vue3 ] Day7. Vue资料的使用方式

Vue资料的使用方式 在前一篇中,我们已经会一些用来表示内容的方式了,但仅仅只是呈现~ 所以今天就会...

DAY2-必先利其器

前言: 看完了Day1的介绍後,大概可以知道接下来30天会用到的语言有:html、php、css、m...