今天我用Python做了定投回测实验,想从历史数据上看,什么时候定投获得的收益最大。

先上结论:

  1. 对于沪深300/中证500,周定投的话,周5定投收益最高,但由于周5申购下周一才确认,因此会多占用2天时间,考虑到资金的时间价值,周5投也不一定最合适,另外从回测结果上看,周2、周3收益率垫底,因此周定投的朋友,周1、周4、周5选哪天投都ok,差不多

  2. 对于沪深300/中证500,月定投的朋友,尽量避免月中,至于为什么月中定投收益低,我也没想明白,期待高手解惑。

以上结论基于10年定投&2年无脑定投(定时定额)回测结果,历史不一定代表未来,谨慎参考~

以下为啰里啰嗦的分析过程。


这几年定投很火,大家都知道,简单说就是分批买入基金

定投的好处有很多

  1. 每月拿出工资的一部分买基金,相当于强制储蓄,避免乱花钱;

  2. 对于我们这种非专业投资者,不太会择时,既然不知道什么时候该买,那就佛系一点,定时自动扣款,不用盯盘,省时省力

  3. 一次性买入有可能会买在高点被套,而定投可以摊平成本,避免这种情况。

使用工具——Baostock

Baostock是一个免费、开源的证券数据平台,我们可以用它来遍历沪深市选股,监测股票行情,进行量化分析和定投回测。

Baostock的安装方法和其他Python包一样,pip install baostock就行。

类似的包还有Dtshare、Tushare,这两个都是免费的Python金融数据接口库,可以自行选择,不过Tushare现在取数需要注册和积分,稍有点麻烦。

  1. Baostock官方说明文档:baostock.com

  2. Tushare官方说明文档:tushare.pro/document/1

  3. Dtshare官方说明文档:dt-share.com

回测思路

一些问题

  1. 定投哪些基金?

  2. 定投频率和日期?

  3. 定投金额?

  4. 定投日遇到节假日(不开盘)如何操作?

  5. 收益率如何计算?

第一个问题,我们定投基金一般选指数基金(债券基金不适合定投,这里不展开说了)。

“指数”很好理解,就是选取一组股票,把他们的价格进行加权平均。也就是说,你买股票买的是单个股票,买指数买的是多个股票。

从代表性来看,指数可以分为宽基指数和窄基指数。

宽基指数包含了不同行业,不同类型的指数,能够避免单个股票、单个行业出现的黑天鹅事件,风险相对较小。在中国,最合适新手的宽基指数,就是沪深300指数、中证500指数(这2个可以说是定投入门经典组合了)。

窄基指数中比较有代表性的是行业指数,行业指数要比宽基指数投资风险高,当然,出现利好时,获得超额收益的可能性也更大。我也会投行业指数,例如消费指数、医疗指数,这个以后再说。

今天就先看沪深300、中证500这2个指数。

定投周期通常有周定投、月定投,这两都可以测一下。

周定投:测周一到周五,每次定投1单位金额(不设置具体金额了,毕竟最后计算的收益率是一个相对值的概念,这里1单位你可以理解为1千元,1万元之类)。

月定投:测每月5日、10日、15日、20日、25日,每次定投4单位金额。

节假日:为了方(偷)便(懒),遇到节假日就直接跳过不投。

收益计算逻辑:累计收益率=(市值-本金)/本金

简便起见,这里不考虑资金效率,因为本文主要涉及周定投、月定投的不同定投日的横向比较,也是一个相对值的概念,所以在计算收益率上没那么严谨(其实还是我懒得计算IRR>.<)。

过程及代码

我是从2010年5月31日(周一)开始取数,看近10年的定投情况(我们一般认为中国股市的牛熊周期是7-8年,因此年限太短的话覆盖不了一个周期)。

上图是在果仁网上拉的近20年上证指数(大盘)的走势,我简单标注了一下,差不多2001年到2008年是一个熊牛周期,7年,2008年到2015年又是一个7年周期(粗略地看)。

整体上来讲中国股市的熊市时间很长,所以长期定投真的很考验人的耐心啊(当然了,没有遇到大牛市,也可以做短线策略,吃一些短期波动的收益)。

import baostock as bs
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime, date# 登陆系统
lg = bs.login()
code = 'sh. 000300'
start = '2010-05-31'
end = '2020-06-01'# 获取指数基金指数历史数据
hs300_price = bs.query_history_k_data_plus(code, "date,code,open,high,low,close,preclose,pctChg",start_date=start, end_date=end, frequency="d")
# 整合为DataFrame格式
data_list = []
while (hs300_price.error_code == '0') & hs300_price.next():data_list.append(hs300_price.get_row_data())
hs300 = pd.DataFrame(data_list, columns=hs300_price.fields)# 最后一行对应的日期执行卖出操作
sell_index = hs300[hs300['date']==end].index[0]
hs300_buy = hs300.drop(sell_index, axis=0)
sell_price = float(hs300.loc[sell_index, 'close'])

场外定投都是按照当日收盘价格计算,因此hs300表里close列是我们要用到的,如下图,最后一行2020年6月1日执行卖出。

参数说明

date-交易所行情日期,code-证券代码,open-今开盘价格 ,high-最高价,low-最低价,close-今收盘价,preclose-上个交易日收盘价,pctChg-涨跌幅。

# 周定投
total = [0] * 5
count = [0] * 5
for index,row in hs300_buy.iterrows():# 将日期转化成星期,周一到周五用数字0到4来表示weekday = int(datetime.strptime(row['date'], '%Y-%m-%d').weekday())# 1单位金额除以收盘价格得到购买基金份额,并计算累计份额total[weekday] = total[weekday] + 1/float(row['close'])count[weekday] += 1for index, unit in enumerate(total):print("每周{}定投,累计投入{}单位金额,最终卖出{}单位金额,收益率{}%;".format(index+1, count[index],\round(sell_price*unit,2), round(100*(sell_price*unit-count[index])/count[index],2)))# 月定投
date = ['05', '10', '15', '20', '25']
hs300_buy['day'] = hs300_buy['date'].map(lambda x: x[-2:])for i in date:close_series = hs300_buy[hs300_buy['day']==i]['close']buy = close_series.shape[0]unit = close_series.map(lambda x: 4/float(x)).sum()print("每月{}日定投,累计投入{}单位金额,最终卖出{}单位,收益率{}%;".format(i, 4*buy,\round(sell_price*unit, 2), round(100*(sell_price*unit-4*buy)/(4*buy),2)))

结果

每周1定投,累计投入473单位金额,最终卖出618.48单位金额,收益率30.76%;
每周2定投,累计投入489单位金额,最终卖出637.93单位金额,收益率30.46%;
每周3定投,累计投入493单位金额,最终卖出642.89单位金额,收益率30.4%;
每周4定投,累计投入491单位金额,最终卖出642.0单位金额,收益率30.75%;
每周5定投,累计投入484单位金额,最终卖出634.12单位金额,收益率31.02%

每月05日定投,累计投入284单位金额,最终卖出371.59单位,收益率30.84%;
每月10日定投,累计投入328单位金额,最终卖出429.2单位,收益率30.85%;
每月15日定投,累计投入324单位金额,最终卖出419.69单位,收益率29.53%
每月20日定投,累计投入340单位金额,最终卖出444.31单位,收益率30.68%;
每月25日定投,累计投入340单位金额,最终卖出444.6单位,收益率30.76%;

可以看到,如果是定时定额的定投方式,周定投和月定投最终的累计收益率差别不算大,按周投的话周五的收益率最高,周三收益率最低,按月投的话月中的收益率最低

中证500的回测思路也一样,把股票代码改成000905就可以,回测结果:

每周1定投,累计投入473单位金额,最终卖出552.1单位金额,收益率16.72%;
每周2定投,累计投入489单位金额,最终卖出567.74单位金额,收益率16.1%;
每周3定投,累计投入493单位金额,最终卖出572.07单位金额,收益率16.04%;
每周4定投,累计投入491单位金额,最终卖出572.81单位金额,收益率16.66%;
每周5定投,累计投入484单位金额,最终卖出566.01单位金额,收益率16.94%;

每月05日定投,累计投入284单位金额,最终卖出332.63单位,收益率17.12%;
每月10日定投,累计投入328单位金额,最终卖出381.31单位,收益率16.25%;
每月15日定投,累计投入324单位金额,最终卖出373.2单位,收益率15.18%;
每月20日定投,累计投入340单位金额,最终卖出397.25单位,收益率16.84%;
每月25日定投,累计投入340单位金额,最终卖出394.91单位,收益率16.15%;

中证500的结果和沪深300相似,按周投的话也是周五的收益率最高,周三收益率最低,按月投的话月中的收益率最低

单测一个时间段,可能不具备代表性,而且基本上很少有人坚持十年买入,一朝卖出。

那么我再实验一下定投2年的结果,也就是在2010.05.31-2018.05.31这个时间段,随机取100个日期,并计算从这100个日期开始定投2年的平均收益。

代码如下

import random
# 随机生成100个索引
end_index = hs300[hs300['date']=='2018-05-31'].index[0]
random_index = random.sample(range(0, end_index), 100)
random_index.sort()start_list = []
week_day = []
buy_amt = []
sell_amt = []
return_rate = []
for i in random_index:# 生成每个随机数对应的定投区间和卖出价random_start = hs300.loc[i]['date']random_end = random_start[:2] + str(int(random_start[2:4])+2) + random_start[4:]random_buy = hs300[(hs300['date']>=random_start) & (hs300['date']<=random_end)]random_end_index = int(random_buy.index[-1])+1sell_price = float(hs300.loc[random_end_index, 'close'])# 周定投total = [0] * 5  count = [0] * 5for index,row in random_buy.iterrows():# 将日期转化成星期,周一到周五用数字0到4来表示weekday = int(datetime.strptime(row['date'], '%Y-%m-%d').weekday())# 1单位金额除以收盘价格得到购买基金份额,并计算累计份额total[weekday] = total[weekday] + 1/float(row['close'])count[weekday] += 1for index, unit in enumerate(total):start_list.append(random_start)week_day.append(index+1)buy_amt.append(count[index])sell_amt.append(round(sell_price*unit,2))return_rate.append(round(100*(sell_price*unit-count[index])/count[index],2))df = pd.DataFrame({'定投开始日期':start_list,'每周几':week_day,'累计投入单位金额':buy_amt,'最终卖出单位金额':sell_amt,'收益率(%)':return_rate})

沪深300指数2年周定投结果

相应的,我们可以测100次月定投结果,也会得到上方形式的表格,但是这么看肯定不够直观,还是得画个图,那就画个箱线图吧~

沪深300周定投结果:周5>周4>周1>周3>周2。

沪深300月定投结果:20号>25号>10号>5号>15号。

中证500周定投结果:周5>周1>周4>周2>周3。

中证500月定投结果:20号>25号>5号>10号>15号。

图中紫色数字为100个随机样本的收益率均值,可以看到2年定投,不论是沪深300,还是中证500,都依旧是周五定投收益最高,月中定投收益最低

各位可以把代码拿去,自行实验不同时期,不同定投年限的收益结果~

思考一:为什么周五收益最高?

周五定投收益率高,其实比较符合经验,因为通常节假日前的那个交易日股市跌的可能性大于涨的可能性,股市跌就意味着基金价格可能更便宜了,买的便宜自然赚得多。

从近十年的沪深300收盘价数据上看也是如此,这10年共有512周有交易数据,我看了下每一周的最低价出现在周几,发现一周最低价出现在周五的次数占比最高,其次是周一。

中证500的结果稍微有点不一样,周一出现最低价的频次最高,其次才是周五(不过周一分母较小,应该是很多假日都遇上了周一)。

虽然周五定投收益率最高,但是因为场外定投的规则是:当日15点以前买入,次日确认份额,也就意味着如果周五申购,要到下周一确认,会平白多出2天资金占用时间(如果你其他理财渠道在周末有收益,周五定投就会造成你这两天的收益损失哟)。

结合前面的实验结果,周二和周三的收益率基本都是垫底,因此周定投沪深300、中证500,可以避开周2、3,选周1、4、5。

不过为什么每月15号定投收益率低,我一直没想明白?

思考二:十年才赚这么点?

大家有没有发现,就是,我测试了10年的数据,说好的“定投十年赚十倍”(这也是个公众号名称,想学定投的话挺值得关注的),实际上定投沪深300十年,收益才30%左右(中证500才16%)?

问题在哪呢,第一个就是统计口径的问题了,我计算的是累计收益率,如果计算内部收益率肯定要比这个值高。

第二个问题就在于,这10年跨越了一个牛熊周期,而傻傻的定投会导致买入在牛市高点,徒增成本。因此定投要想多赚,需要有比较好的择时卖出策略。

上图是中证500近十年走势,站在我们现在这个时点往回看,我们知道该指数在2015年6月11日冲顶到11366.29。

好,我们马后炮地回测一下2010.05.31-2015.06.10的定投结果:

每周1定投,累计投入238单位金额,最终卖出661.06单位金额,收益率177.76%;
每周2定投,累计投入245单位金额,最终卖出676.76单位金额,收益率176.23%;
每周3定投,累计投入246单位金额,最终卖出678.82单位金额,收益率175.94%;
每周4定投,累计投入246单位金额,最终卖出683.81单位金额,收益率177.97%;
每周5定投,累计投入245单位金额,最终卖出681.71单位金额,收益率178.25%;

定投5年赚176%,真香(醒醒,实际上你根本猜不准哪儿是顶)。

不过,我又算了下2010年5月31日一次性申购,到顶后卖出,收益率是(11366.29-4104.36)/4104.36=177%,也就是说分批买入和一次性买入差别不大。

这种是基金长期卧倒装死,然后突然起跳的行情,这样的场景下定投优势无法显现(因为底部波动小,分批投,跟一次性投,成本都差不多)。

那什么样的场景下适合定投?这就要说到定投达人常提起的“微笑曲线”。

还是看上面的走势图,2015年6月11日后,中证500经历了一个月的单边暴跌, 接着小幅上涨,后续有一段上下波动。

假如有个人三傻,他前期没有累计筹码,到了6月份看见基金涨势喜人,想要追涨一波,结果没想到后面跌跌不休,心态崩了,后面有点上涨就赶紧趁势卖了。也就是他2015.06.11在顶部买入,走完上图那个深V(绿色的行情)后卖出,此时收益率是(8551.99-11366.29)/11366.29=-25%。

如果有个二蛋,他虽然也有点二,但是比三傻好点,也比较有钱,买在顶部后,发现后期跌了,“越跌越买”嘛,就继续定投,他的收益将会是:

每周1定投,累计投入5单位金额,最终卖出5.11单位金额,收益率2.19%;
每周2定投,累计投入6单位金额,最终卖出5.99单位金额,收益率-0.16%;
每周3定投,累计投入6单位金额,最终卖出6.1单位金额,收益率1.63%;
每周4定投,累计投入6单位金额,最终卖出5.87单位金额,收益率-2.12%;
每周5定投,累计投入6单位金额,最终卖出5.95单位金额,收益率-0.8%;

二蛋明显比三傻亏得少,如果时间选的好,还能略微有点收益!

后面那个黄色的对勾行情也一样,时间范围为15.08.20-15.11.25。

一次性买入:(7922.38-8049.51)/8049.51=-1.58%

周定投:

每周1定投,累计投入13单位金额,最终卖出15.12单位金额,收益率16.32%;
每周2定投,累计投入13单位金额,最终卖出15.3单位金额,收益率17.71%;
每周3定投,累计投入12单位金额,最终卖出14.3单位金额,收益率19.15%;
每周4定投,累计投入12单位金额,最终卖出13.81单位金额,收益率15.07%;
每周5定投,累计投入12单位金额,最终卖出13.73单位金额,收益率14.42%;

这结果也不用多说了吧~

以上都是定时定额策略的结果,如果是均线法、价值平均法等智能定投策略,还能有更高的收益。

思考三:定投心态

定投虽然稳,但是也考验人。虽然大家都知道定投需要耐心,但是真正能一直坚持的人其实很少。牛市拿不住早早卖了止盈的人有很多,熊市扛不住早早割肉离场的人也有很多,前者还好只是赚的少点,后者就是白折腾半天的赔钱货了。

假设我们回到过去,从11年开始定投,我们在当时是不知道未来的情况的,如果你和我一样是个有耐心但是偏保守的人,那么,会发生如下情况:

  1. 11年开始,指数一直在下跌,越跌越买啊,累计筹码,等待时机;

  2. 又一年过去,12年一直在跌,这时候很多人会很焦虑,但你一方面有持续的现金流(工资),一方面又是耐得住寂寞扛得住跌的人,所以还是会继续定投(甚至加大投入金额);

  3. 13年起,指数开始缓缓稳步上涨了,虽然涨得不多,但是渐渐回本,心里也不慌了;

  4. 到14年,指数一直在涨,定投的思想是涨得多买的少嘛,所以你开始减少投入金额;

  5. 15年上半年你每天都在关注走势,因为大家都说大牛市来了,很多以前不投资的人都开始进场追涨,天天涨那么多,激动的同时你也有点害怕,万一将来爆跌那前面的功夫不就白费了?而且现在市场过热,感觉心里有点不踏实,所以开始分批逐步卖出;

  6. 4月中旬,指数涨到了8000,可怕,你觉得也赚够了(翻了一番),就全卖了,并且还有点沾沾自喜,还想着后面指数暴跌;

  7. 当然后面没有暴跌,一路飙升到6月的11000多……md……

设想上面这种场景,是想展示我作为一个普通投资者的定投心态(不一定最赚钱,也不一定适合你,仅供参考):

  1. 只要指数一直处于低估状态(可以参考支付宝的指数红绿灯,或者螺丝钉估值、果仁估值、且慢估值之类),就要坚持持有;

  2. 现金流充裕的情况下,一定要在底部多累积筹码(但千万不要梭哈),有计划地买入,打长期有准备之仗;

  3. 市场不可预测,不要贪心,不要想着赚最后一个铜板(虽然事后肯定会有遗憾,人之常情嘛),达到预期收益后收手就好,切勿恋战。

注意

  1. 买卖基金是有手续费等费用的,本文未考虑这部分费用。

  2. 本文的收益率是累计收益率,未考虑资金的时间占用成本。

  3. 本文测的沪深300指数(用的收盘点数),而实际上我们购买基金买的是各个基金公司的产品(按净值计算),这些产品跟踪沪深300指数,例如易方达沪深300ETF联接A(代码:110020),天弘沪深300ETF联接C(代码:005918),虽然都叫沪深300,但还是有差异的,例如跟踪误差不一样,收费不一样等等。此外还会有一些增强型,也就是加入了跟多的主观人为操作的指数,不完全被动跟踪,例如我比较喜欢买的兴全沪深300增强A(代码:163407)。

  4. 历史不一定代表未来,谨慎参考。

  5. 本文所测定投为无脑定时定额投资,后续计划:回测智能定投,回测窄基指数。

Python应用之回测基金定投,选周几收益最高?相关推荐

  1. python 量化策略回测_在python中创建和回测对交易策略

    python 量化策略回测 Pairs trading is one of the many mean-reversion strategies. It is considered non-direc ...

  2. 量化交易中用到的回测评估指标(策略收益、基准收益、Alpha比率、Beta比率、夏普比率、索提诺比率)详解

    前言 近日在做A股的过程中接触到了量化交易.通过一个月时间的了解发现并非全自动印钞机,也有可能有全自动接盘侠的潜质.故现阶段以学习量化交易的知识为主,多学多问总是没错的嘛~ 现阶段使用Python爬取 ...

  3. Python笔记-最大回测计算

    例子 初始资金是1块钱的话,资金连续一周的变化如下: 周一:1.01 周二:1.02 周三:0.98 周四:1.0 周五:0.97 那么我们要计算的样本为:[1, 1.01, 1.02, 0.98, ...

  4. 【交易回顾】没用Python做量化回测,少赚100w | 股指期货妙用案例

    最近发现自己做了一件很傻逼的事情. 是年初做的一笔投资.当时对这笔交易思考的不够全面,没用历史数据做详细的回测. 导致现在让我至少少赚了100万. 这种事情大家肯定都喜闻乐见,比如之前一篇文章< ...

  5. 5、Python量化交易-回测收益计算

    目录 前言 一.回测的主方法 二.回测实现 1 - 获取回测数据ticks 2 - 运行回测 3 - 为回测数据添加生成方法 4 - sell中添加订单的pnl收益计算 5 - 策略执行中调整买卖ma ...

  6. python基金预测分析_基金定投选星期几更划算?[python统计分析]

    基金定投常见的一种方式是定期定额投资,即每周或每月固定的时间段,向基金公司申购固定份额的基金.基金定投可以平均成本.分散风险,实现自动投资,所以基金定投又称为"懒人投资术".今天主 ...

  7. 手把手教你用Python搭建自己的量化回测框架【均值回归策略】

    1 引言 大部分量化策略都可以归类为均值回归与动量策略.事实上,只有当股票价格是均值回归或趋势的,交易策略才能盈利.否则,价格是随机游走的,交易将无利可图.均值回归是金融学的一个重要概念,指股票价格无 ...

  8. 手把手教你python实现量价形态选股知乎_【手把手教你】Python实现基于事件驱动的量化回测...

    01引言 使用矢量化方法(pandas)建立的基于研究的量化回测框架,不考虑交易的委托成交行为,与真实市场情况差距比较大.今天为大家介绍的是基于事件驱动的回测框架,这是一种十分复杂的回测系统,力图模拟 ...

  9. 发明者本地回测平台python版[博]

    原创博客地址:发明者本地回测平台python版[博] 网页版策略开发问题 1,没有自动提示 2,tab自动4个空格,问题是编译器无法识别4个空格,只能再把自动填充4空格改成tab 另一方面,官方提供安 ...

最新文章

  1. “用手机就能访问卫星” 软件定义升级卫星智能
  2. stm32串口通信(初学者对于串口通信的理解)
  3. aspect spring_使用Aspect和Spring Profile进行电子邮件过滤
  4. 杭电HDUacm2098
  5. Linux版Flash亮相,但64位版需时间
  6. 技术交流群和CSDN免费下载服务
  7. 【软件-ACDSee】图像合并为TIFF
  8. 苹果电脑怎样禁用首字母自动大写?
  9. canvas图片合成模糊变清晰的方法
  10. 远程IT运维的升级,“团队协作”
  11. 【wordpress】Elementor插件图标显示错误:显示为空方格
  12. 京东商品详情页前端开发宝典
  13. 黑客入侵自我保护手册
  14. facsum (线性筛 积性函数)
  15. c#窗体编辑个人简历_编辑个人简历求职简历
  16. java pdf文件下载_Java后台返回PDF文件预览下载
  17. 流星雨直播:都市安魂新曲
  18. 计算机组成原理5章课件,计算机组成原理第5章存储器课件.ppt
  19. 【错题记录】JavaScript专项练习(篇四)
  20. C# 用 iTextSharp 将 PDF 转成文本的代码

热门文章

  1. 终于找到了老游戏digger
  2. 2020年中国汽车试验场行业市场现状分析,新技术、新模式发展带动行业深刻变革「图」
  3. 优化大师修复IE右键
  4. 自主设计的滑移转向机器人同步带传动stm32f103的can伺服电机控制
  5. ds6708 symbol 驱动_SymbolDS6708
  6. Kendo-UI学习 下拉列表kendoDropDownList 三级联动demo
  7. wordpress 占用内容过高的解决方案
  8. 世界时钟的C语言编码,世界时钟官方下载 世界时钟(Sharp World Clock) 显示多个不同时区当前准确时间 v9.3.5 安装版 下载-脚本之家...
  9. 蓝牙HC-05上电自动互联(最详细教程)
  10. Kerberos 简介——教你做个好人