一般理财入门书籍都会推荐采用定投方式投资,如果不知道什么是定投可以自行百度下。
现在有了quant平台,可以通过的历史数据对其进行验证核实。
本文课题:定投盈利来源归因,定投收益率和时间成正比?
如果对代码不感兴趣,可直接阅读结论部分。贴出代码主要为了让某些读者方便的进行修改回测。
获取月初价格和定投收益信息
代码:
import pandas as pdpd.set_option('display.max_columns', None)pd.set_option('display.max_rows', None)
# 获取价格信息debug = Trueprice_df = get_price('510300.XSHG', start_date='2015-01-30',end_date='2021-10-30',frequency='daily', fields=['close'])debug and print('get price_df:\n',price_df.head(5))
# 获取月初价格(模拟按月定投,此处模拟每月初,买入一次)price_df=price_df.reset_index()price_df['yestday']=price_df['index'].shift(periods=1)price_df=price_df.dropna()
# 判断是否月初(月份发生变化说明是月初)def change_month(date1,date2): return not (date1.replace('-','')[4:6]==date2.replace('-','')[4:6])debug and change_month('2015-02-02','2015-01-30')price_df['month_firstday']=price_df[['index','yestday']].apply(lambda x: change_month(str(x['index']),str(x['yestday'])),axis=1)price_df=price_df[price_df['month_firstday']].drop(columns=['yestday','month_firstday'])debug and print('keep month_firstday:\n',price_df.head())
# 每个月入金1w元price_df['incash']=10000# 入金转换为基金份额price_df['fundshare']=price_df['incash']/price_df['close']# 累计本金price_df['incash_cumsum']=price_df['incash'].cumsum()# 累计基金份额price_df['fundshare_cumsum']=price_df['fundshare'].cumsum()# 总资产=累计份额*收盘价price_df['total_asset']=price_df['fundshare_cumsum']*price_df['close']# 净利润=总资产-总入金price_df['profit']=price_df['total_asset']-price_df['incash_cumsum']# 净收益率 = 净利润/总入金=(总资产-总入金)/总入金price_df['profit_rate']=price_df['profit']/price_df['incash_cumsum']# 0轴,参考线price_df['zero']=0# 绘图,总资产,总入金,尽利润,0轴参考debug and print('price_df:\n',price_df.head(5))price_df[['total_asset','incash_cumsum','profit','zero']].plot()输出:
get price_df: close2015-01-30 3.1262015-02-02 3.0562015-02-03 3.1282015-02-04 3.0922015-02-05 3.056keep month_firstday: index close1 2015-02-02 3.05616 2015-03-02 3.27838 2015-04-01 3.74459 2015-05-04 4.35479 2015-06-01 4.633price_df: index close incash fundshare incash_cumsum fundshare_cumsum total_asset profit profit_rate zero1 2015-02-02 3.056 10000 3272.251309 10000 3272.251309 10000.000000 0.000000 0.000000 016 2015-03-02 3.278 10000 3050.640635 20000 6322.891943 20726.439791 726.439791 0.036322 038 2015-04-01 3.744 10000 2670.940171 30000 8993.832114 33672.907436 3672.907436 0.122430 059 2015-05-04 4.354 10000 2296.738631 40000 11290.570746 49159.145026 9159.145026 0.228979 079 2015-06-01 4.633 10000 2158.428664 50000 13448.999409 62309.214264 12309.214264 0.246184 0
字段解释:index:日期,每月第一个交易日加仓close:当日收盘价,incash:当日汇入现金(定投增加本金)fundshare:基金总份额incash_cumsum:累计汇入现金(定投的总投资额,定投金额*定投期数)total_asset:总资产,就是总份额*基金当前净值profit:总利润,总资产-总入金profit_rate:收益率,(总资产-总入金)/总入金总资产,总入金,总收益对比图:

先解释这张图含义:
总资产:最上面的蓝线:总资产,看起来很诱人,45度向上,诱人的主要原因是不断追加的资金(橙线的45度直线,那代表总投入资金线)。
总入金(定投资金):次之的橙线,由于不断投入的总资金,固定斜率斜线(固定资金定投)。
总盈利:红线,总资产-总投入=盈利部分。
底部0轴:盈亏平衡轴,轴上部表示盈利,下部表示亏损(主要为了上下好比对)。
结论:定投hs300,从15年到今年,整体上是盈利的。
在补充一张图,这张图是沪深300的定投收益率图
回测区间:start_date=‘2007-06-30’,end_date=‘2022-06-09’
收益率:相对当时总投入本金收益率
总体来看,正收益没毛病,但早期有高达40%的回撤幅度,哪怕在横轴右侧1/5处,相比开始定投,已经过了12年了,小孩都可以打酱油了,还位于盈亏平衡点上。意味着那几天资金和总投入资金相等(相比存银行的话,等价于亏本了)。这是相当凄惨的情况,相当于12年资金免费让人家用了。当然这是比较极端的例子,意思就是别把定投当做灵丹妙药。

定投盈亏归因分析
那么定投盈利主要来源来自哪里?得益于最近几个月行情大涨?还是几年前的某一笔英明的投资?
代码:
# 盈亏归因分析price_df['close'].plot()# 01,笔视图,各笔交易盈亏图# 每笔盈利=最新价格*购买份额-成本new_price=price_df['close'].values[-1]price_df['deal_profit']=new_price*price_df['fundshare']-10000price_df[['deal_profit','zero']].plot()
# 02,时间视图,各月份盈利图# 各月份盈亏情况=总资产.diff(1)price_df['total_asset_diff']=price_df['total_asset'].diff(1)price_df[['total_asset_diff','zero']].plot()输出:
基础行情曲线

每一笔角度的投资收益视图:从左到右的每一个点,表示当时那一笔投资到今天的收益

各个时间的投资收益视图(类似各月的涨跌情况):

结论:
第一图,从每一笔交易的视角来看,买入价格越低越好(废话,不解释),所以远期的买入价还是有点重要的。每笔交易视角:盈亏=当前点价格-买入点价格。
第二图,从时间的视角来看,越往后价值波动范围越大,由于持有份额增大了,所以同样的基础标的价格变化,导致资产变动更大。
结论:对定投组合价值影响最大的是当下价格,其次是远期价格。
宏观上看,定投类似时间加权的买入策略,时间在哪个价格(区间)停留最多,持股成本就最接近那个价格。如果把行情线看做铁丝的话,那么定投成本类似铁丝的质心。
从这个角度,我们可以大概预估定投的收益情况,不需要复杂的模拟,对一段行情的定投收益有大致预估。
显然对于不断下跌的行情,由于持续下跌,持续加仓,整体质心持续位于当前行情的上部,会持续保持亏损,且亏损额会原来越大。
收益率正比时间?
那么大家耳熟能详的,定投适用于长期投资,投资期间越长收益越高,正确吗?
不妨统计下,投资时长和定投收益是否正相关。
# 全量分析
price_df=price_df.set_index('index')profit_df=pd.DataFrame(columns=price_df.index.values)for start_date in price_df.index.values.copy(): # 累计本金 price_df['incash_cumsum']=price_df['incash'].cumsum() # 累计基金份额 price_df['fundshare_cumsum']=price_df['fundshare'].cumsum() # 总资产=累计份额*收盘价 price_df['total_asset']=price_df['fundshare_cumsum']*price_df['close'] # 净利润=总资产-总入金 price_df['profit']=price_df['total_asset']-price_df['incash_cumsum'] # 净收益率 = 净利润/总入金=(总资产-总入金)/总入金 price_df['profit_rate']=price_df['profit']/price_df['incash_cumsum'] # change01:profit=>profit_rate profit_df.loc[start_date]=price_df['profit'] # profit_df.loc[start_date]=price_df['profit_rate'] price_df.drop([start_date], axis=0,inplace=True)代码
profit_df输出:

这是一个81行81列的大表格,
纵轴:定投开始时间
横轴:定投结束时间
中间的数值:从“定投开始时间”到”定投结束时间”期间 的 总收益。
暂不关注具体盈亏金额细节,只关注盈亏是否和时间正相关,以及持有多久盈利概率最大,最长持续亏损时间等。
代码:
# 统计各个定投时间对应的收益情况periodic_profit=[set() for i in range(profit_df.shape[0])]# 将profit_df中的value填充到periodic_profit对应定投时长的set集合中for x in range(profit_df.shape[0]): for y in range(x+1,profit_df.shape[1]): periodic_profit[y-x].add(profit_df.iloc[x,y])代码
periodic_profit[1]输出:
{-1722.4260738182602, -1671.8266253869951, -1221.590909090908, -1181.929181929183, -989.3114591101184, -910.2402022756032, -883.0880311024739, -821.3820078226818, -746.6943220119283, -646.8842729970347,,,,,}为了使用seaborn绘图,对periodic_profit的数据呈现方式做转换,转成pandas的数据 比如:
periodic_profit=[set(),set(1.1,1.2,1.3),set(1.6.1,7)]=>dataframe1,1.11,1.21,1.32,1.62,1.7代码
# change01:profit=>profit_rateperiodic_profit_df=pd.DataFrame(columns=['priod','profit'])# periodic_profit_df=pd.DataFrame(columns=['priod','profit_rate'])for i in range(len(periodic_profit)): if periodic_profit[i]: for x in periodic_profit[i]: periodic_profit_df.loc[len(periodic_profit_df)] = [int(i),x]periodic_profit_df.tail()输出
priod profit3235 78.0 297937.7583693236 78.0 282156.1121833237 79.0 285189.0856263238 79.0 288350.4839113239 80.0 291429.268872代码
import seaborn as sns# change01:profit=>profit_ratesns.boxplot(x = 'priod', y= 'profit', data = periodic_profit_df)# sns.boxplot(x = 'priod', y= 'profit_rate', data = periodic_profit_df)输出:

图片说明:
x轴是定投的周期数,月为单位(1表示定投一个周期(月),20表示定投20个周期(月))
y轴是一个收益金额的分布
为何是分布,因为定投一个月的,和定投20个月的都对应了一个收益集合,比如第1月开始定投,到第21月结束会得到1个收益率数据。从第2月开始到第22月结束,也会得到1个收益率数据。所以对于每个x轴数据,都对应了一连串的收益率数据(当然,x越小对应的收益率数据越多,x越大,比如80,就只有第1个月开始和第81个月结束的收益数据,只有1份数据)这个角度来看,乍一看,好像是成立的,随着时间增加,收益均值在增大。
但是别忘了,随着时间增大,定投总金额在增加,所以总收益增大是正常的。我们需要考虑的是相对总本金的收益率,这个漏洞在change01中将修复。
而且还要留意,越长期的统计,远期由于数量不足,计算进来的权重越小,比如,70个月的,只会有:从第1月到第70个月,第2月到第71个月等这些组合。
一方面样本偏少,统计意义较低。
另一方面,由于近期表现好(且远期表现够差),所以70月,71月等都较高,使得长期统计结果退化为近期行情表现(当然是相对远期,如果远期更强势,目前依然负值,但近期表现整体占高影响力不变,毕竟总资金量是越来越大的)
change01<改为收益率>改为收益率>
profit_rate 代替profit
change01
结果图:

结论:宏观结果上看是正确的(定投时长和收益成正比),但由于标的本身是宏观上涨的标的。所以基本上持有型策略都会录得较好表现。可以说明在整体趋势向上,或者当前价格较高时,定投收益和时间成正比。但对于整体行情下跌的行情,这个大概率是不成立的。这个结果个人觉得体现不出定投的优越性。
定投本质而言,时间角度是放弃了择时(由于按时间定投的)。另一方面,由于越后期总资金越多,所以最终收益受近期行情的影响较高(这里的近期也是相对的, 比如2年的定投,可能近半年都算近期了)。我们重点是有这个概念,定投的卖出点尤为重要。这个不在解释。找到高点跑路,否则一旦跌下来,可能之前定投浮盈全部作废了。但那个才算是高点呢?所以定投也未必就是懒人策略,如果挣点小钱钱,可以设定目标,比如年化10%就跑路也行。总之有明确目标,不能持有不动,一旦牛市来临,兑现目标就立场。等下一个漫漫熊市再建仓。 本质上近似看做一个抄底型策略,低买高卖的波动策略。
结论
定投盈利来源归因:从权重来看,近期价格影响力更大,当然,远期价格也很重要。当前价格相对行情曲线质心位置,也就是说现价占了很大权重。 定投收益率和时间成正比:行情趋势向上时成立(废话对吧),如果行情水平宽幅震荡,原始的定投无法产生额外收益(后面突破了就是另一回事了)。
定投只是省事了而已(相对网格等策略,定投更简单。同时,也避免了单次买入并持有面临的买入纠结问题(本质是:买入并持有策略中,买入时点非常重要,而定投通过分散买入点解决了这个问题,或者说缓解了这个问题)),并没有传说中的那么神奇,既无法保证高额收益,也无法保证一定会盈利(行情稳定向上时可以保证,但简单的买入并持有一样能保证)。
说明定投本身适合问稳定向上的标的(听起来向废话),避免高波动标的,所以本身更适合基金等标的(不会破产倒闭)。
最大优势,在于定投符合现实中现金流,尤其打工人的。
综合结论,适合(短期不确定,但)长期看好的标的,比如2年前的黄金(当前已涨上去了,不赶趟了),当前的粮食(气候极端化)等。 选择低波动性标的,没有倒闭清盘风险的标的。
其他定投文章
定投真的有效吗?沪深300定投15年测试:https://www.joinquant.com/view/community/detail/6eba65eb8c5406580978ec040b100de4?type=1
总结
经典的傻瓜定投确实不太聪明
只买不卖是不行滴
展望
好的定投并非是无脑买的,而应该是越低越买,越高越卖(波动行情这样有额外优势,单边行情这样会完蛋,除非资金无限大并且标的不能归零)
成于时间,败于资金,量化模型揭开基金定投的真相:https://www.joinquant.com/view/community/detail/a4479781568585f70e2cf82d71d47693?type=1
基金定投理论的前提条件,一是时间足够长,二是资金足够多。
基金定投是时间给我们的承诺,但是并没有承诺这段时间有多长。如果在定投的过程中,无法再继续投入资金而中断,就可能出现上面的情况。红色箭头就是投资中断的时间点,后面价格下跌拉低成本的机会,完全享受不到,但是下跌带来的资金回撤可是一分钱没少地承受着。而这种情况下,在绿线下面的任何一个时间点投入,都是跑赢定投方式的。资金意外的出现,让定投的盈利价值大幅缩水,而且还用大幅回撤考验我们的心理承受能力。在后面的8个月时间内,遗憾地止损退场,也是有可能发生的。
因此,如果没有足够多的钱,基金定投很可能不那么美好。
基金定投完全不考虑择时问题,可以无脑定投的,并不是一个客观的观点。这个结论存在的前提还是资金,你有足够的资金,不需要考虑流动性的资金。最后,请大家体会下面几句话。
基金定投是我们和时间签下的契约,但是并没有约定期限
时间给我们盈利的承诺,时间也带给我们风险
只有足够的资金才能抵抗这个风险
基金定投的真相-收益与风险:https://www.joinquant.com/view/community/detail/d1adceb39b045a15f2ec119cfeedbb9c?type=1
结论:
定投的收益不如直接持有,这和前文的逐年结果是一致的
定投的波动和回撤都明显小于直接持有
从夏普角度看,除了13和14年外,定投的效果没有显著优于直接持有指数
定投的频率对结果影响不是特别大,以每月以下的频率定投为佳
部分信息可能已经过时