一般理财入门书籍都会推荐采用定投方式投资,如果不知道什么是定投可以自行百度下。 现在有了quant平台,可以通过的历史数据对其进行验证核实。 本文课题:定投盈利来源归因,定投收益率和时间成正比? 如果对代码不感兴趣,可直接阅读结论部分 。贴出代码主要为了让某些读者方便的进行修改回测。
获取月初价格和定投收益信息 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import pandas as pd pd.set_option('display.max_columns', None) pd.set_option('display.max_rows', None) # 获取价格信息 debug = True price_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()
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 get price_df: close 2015-01-30 3.126 2015-02-02 3.056 2015-02-03 3.128 2015-02-04 3.092 2015-02-05 3.056 keep month_firstday: index close 1 2015-02-02 3.056 16 2015-03-02 3.278 38 2015-04-01 3.744 59 2015-05-04 4.354 79 2015-06-01 4.633 price_df: index close incash fundshare incash_cumsum fundshare_cumsum total_asset profit profit_rate zero 1 2015-02-02 3.056 10000 3272.251309 10000 3272.251309 10000.000000 0.000000 0.000000 0 16 2015-03-02 3.278 10000 3050.640635 20000 6322.891943 20726.439791 726.439791 0.036322 0 38 2015-04-01 3.744 10000 2670.940171 30000 8993.832114 33672.907436 3672.907436 0.122430 0 59 2015-05-04 4.354 10000 2296.738631 40000 11290.570746 49159.145026 9159.145026 0.228979 0 79 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年资金免费让人家用了。当然这是比较极端的例子,意思就是别把定投当做灵丹妙药 。
定投盈亏归因分析 那么定投盈利主要来源来自哪里?得益于最近几个月行情大涨?还是几年前的某一笔英明的投资 ? 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 # 盈亏归因分析 price_df['close'].plot() # 01,笔视图,各笔交易盈亏图 # 每笔盈利=最新价格*购买份额-成本 new_price=price_df['close'].values[-1] price_df['deal_profit']=new_price*price_df['fundshare']-10000 price_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()
输出: 基础行情曲线
每一笔角度的投资收益视图:从左到右的每一个点,表示当时那一笔投资到今天的收益
各个时间的投资收益视图(类似各月的涨跌情况):
结论: 第一图,从每一笔交易的视角来看,买入价格越低越好(废话,不解释) ,所以远期的买入价还是有点重要的 。每笔交易视角:盈亏=当前点价格-买入点价格。 第二图,从时间的视角来看,越往后价值波动范围越大,由于持有份额增大了,所以同样的基础标的价格变化,导致资产变动更大 。 结论:对定投组合价值影响最大的是当下价格,其次是远期价格 。
宏观上看,定投类似时间加权的买入策略 ,时间在哪个价格(区间)停留最多,持股成本就最接近那个价格 。如果把行情线看做铁丝的话,那么定投成本类似铁丝的质心 。 从这个角度,我们可以大概预估定投的收益情况 ,不需要复杂的模拟,对一段行情的定投收益有大致预估。 显然对于不断下跌的行情 ,由于持续下跌,持续加仓,整体质心持续位于当前行情的上部,会持续保持亏损,且亏损额会原来越大 。
收益率正比时间? 那么大家耳熟能详的,定投适用于长期投资,投资期间越长收益越高,正确吗 ? 不妨统计下,投资时长和定投收益是否正相关。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # 全量分析 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)
代码
输出:
这是一个81行81列的大表格, 纵轴:定投开始 时间 横轴:定投结束 时间 中间的数值:从“定投开始时间”到”定投结束时间”期间 的 总收益 。暂不关注具体盈亏金额细节,只关注盈亏是否和时间正相关 ,以及持有多久盈利概率最大,最长持续亏损时间 等。 代码:
1 2 3 4 5 6 # 统计各个定投时间对应的收益情况 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])
代码
输出:
1 2 3 4 5 6 7 8 9 10 {-1722.4260738182602, -1671.8266253869951, -1221.590909090908, -1181.929181929183, -989.3114591101184, -910.2402022756032, -883.0880311024739, -821.3820078226818, -746.6943220119283, -646.8842729970347,,,,,}
为了使用seaborn绘图,对periodic_profit的数据呈现方式做转换,转成pandas的数据 比如:
1 2 3 4 5 6 7 8 periodic_profit=[set(),set(1.1,1.2,1.3),set(1.6.1,7)] => dataframe 1,1.1 1,1.2 1,1.3 2,1.6 2,1.7
代码
1 2 3 4 5 6 7 8 # change01:profit=>profit_rate periodic_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()
输出
1 2 3 4 5 6 priod profit 3235 78.0 297937.758369 3236 78.0 282156.112183 3237 79.0 285189.085626 3238 79.0 288350.483911 3239 80.0 291429.268872
代码
1 2 3 4 import seaborn as sns # change01:profit=>profit_rate sns.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轴是一个收益金额的分布
1 为何是分布,因为定投一个月的,和定投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:profit=>profit_rate 结果图:
结论:宏观结果上看是正确的(定投时长和收益成正比) ,但由于标的本身是宏观上涨的标的。所以基本上持有型策略都会录得较好表现。可以说明在整体趋势向上,或者当前价格较高时,定投收益和时间成正比。但对于整体行情下跌的行情,这个大概率是不成立的。这个结果个人觉得体现不出定投的优越性 。 定投本质而言,时间角度是放弃了择时(由于按时间定投的)。另一方面,由于越后期总资金越多,所以最终收益受近期行情的影响较高(这里的近期也是相对的, 比如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年外,定投的效果没有显著优于直接持有指数 定投的频率对结果影响不是特别大,以每月以下的频率定投为佳