Python | 怎么在基金定投上实现收益最大化

论坛 期权论坛 期权     
Python爱好者社区   2019-6-9 21:26   3635   0


作者:Samshare
公众号:SAMshare


个人兴趣专栏  
本期对于股市基金定投进行进一步分析,试图找到投资赚钱的规律哈哈哈~

[h1] Index[/h1]
  • 分析思路阐述
  • 是否存在最合适的定投周期?
  • 设置多少止盈点较为合适?
  • 其他策略
  • 本文总结

[h1] 分析思路阐述[/h1]继上一篇文章《Python在基金定投上的验证》,我们今天讨论一下更加深入的Ideas,总结来说主要有两点:
1) 是否存在最合适的定投周期?
2) 我们到底要设置多少止盈点较为合适?
什么意思呢?我们展开来说,
对于第一点:是否存在最合适的定投周期?
基金定投作为"懒人理财"的头号玩家,当然得贯彻其"懒人"思维,我们想知道到底定投多久,才能说大概率地获得较为满意的收益。
这里有几个点需要理一下:
  • 入场的随机性:因为涉及到实际操作,每个人的入场时间不尽相同,所以这里探讨最合适的定投周期,要考虑入场时机的随机性。
  • 指数的异质性:我们讨论的是指数基金,但不同的指数之间,也是可能会存在不一样的性质,这里也要考虑。
  • 人性的弱点:因为是定投,实际操作的还是人,所以当定投期结束时,是否会因为当前是所谓的"上涨"or"下跌"行情而纠结?
  • 何为"满意的收益”:如何判断“满意”呢,不同人对满意的态度不同,如何定义一个基准。
针对以上的几点,我在自己思考后给出了下面的验证方式:
在指定的定投周期内(定投周期为0.5年的整数倍),遍历所有可能的入场点,卖出点取离场点当月的指数均值,如果收益率大于余额宝定存相同周期时间内的收益的,即为之"满意",同时验证多几个指数。
简单解释一下,我们验证的定投周期,均是0.5年的整数倍,比如定投1年、1.5年、2年等等,买入日期,选择月初,而卖出点,不会是最后的那个月的月初,而是取其当月的指数均值,比如2019年1月1日开始定投,定投1年,那么卖出点就是2020年1月的指数均值,就是为了防止有的人因为当时股价变化而出现的纠结情绪。
当指数基金定投收益率大于余额宝定存收益率的,记为1,为我们的正样本,反之则记为0,为我们的负样本。我们就来统计一下,不同的定投周期,正负样本的比例关系,看下怎么样的定投周期,具备较大概率的"满意"结果!
对于第二点:我们到底要设置多少止盈点较为合适?
关于这点,我们就是为了找出多少的止盈点较为合适。这个结合我们上篇文章的内容,我们知道如果我们定投的周期越长,收益率当然就是越大的,所以,不同定投周期之间的收益率没有可比性。所以我们需要指定一个定投周期(姑且我们用第一点得到的"最优"定投周期N来作为我们的观测周期)。
我们的验证方式:
在指定的定投周期内,遍历所有可能的入场点,卖出点取离场点当月的指数均值,记录此时的收益率情况,统计收益率的大概率集中区间,即为我们的参考止盈点。
接下来,我们就针对以上的2个内容,来用python代码验证一波。(:show time~)

[h1] 是否存在最合适的定投周期?[/h1][h2]1. 导入相关库[/h2]
  1. 1# -*- coding: UTF-8 -*- 2import matplotlib as mpl 3import matplotlib.pyplot as plt 4import pandas as pd 5import numpy as np 6import tushare as ts 7from datetime import datetime 8 9#解决中文显示问题,Mac10%matplotlib inline11from matplotlib.font_manager import FontProperties1213plt.rcParams['figure.figsize'] = (10.0, 4.0) # 设置figure_size尺寸14plt.rcParams['image.interpolation'] = 'nearest' # 设置 interpolation style15plt.rcParams['image.cmap'] = 'gray' # 设置 颜色 style16plt.rcParams['savefig.dpi'] = 100 #图片像素17plt.rcParams['figure.dpi'] = 100 #分辨率1819# 利用plotly绘图20import plotly.offline as of21import plotly.graph_objs as go
复制代码
[h2]2. 数据获取[/h2]这里还是适用Tushare的接口来获取数据,具体代码如下:
  1. 1# 设置token 2ts.set_token('your token') 3 4# 初始化接口 5pro = ts.pro_api() 6 7# 获取基本信息 8sh_basic = pro.index_basic(market='SSE') # 上交所 9sz_basic = pro.index_basic(market='SZSE') # 深交所101112# 000001.SH 上证综指13# 399001.SZ 深证成指14data_sh = pro.index_daily(ts_code='000001.SH', start_date='19901219', end_date='20190401')15data_sz = pro.index_daily(ts_code='399001.SZ', start_date='19901219', end_date='20190401')16data_sh.head()
复制代码
[h2]3. 代码封装[/h2]对上面提及的验证方式进行逻辑封装:
  1. 1def Log_of_AIP(data, n, money): 2    ''' 3    data:测试数据集,即指数历史走势数据 4    n:定投周期,6个月的整数倍,如n=1代表定投6个月 5    money:定投金额 6    ''' 7 8    data = data.loc[:,['open','trade_date']] 9    data['trade_month'] = data['trade_date'].apply(lambda x:str(x)[0:6])10    data['date'] = data['trade_date'].apply(lambda x:datetime.strptime(x,'%Y%m%d'))11    data = data.set_index('date').sort_index()1213    # 假设余额宝的年化收益率为 4%14    data['余额宝利率'] = (4.0/100+1)**(1.0/250)-1  15    data['理财收益_净值'] = (data['余额宝利率']+1).cumprod()1617    # 选择每个月的第一个交易日进行定投18    trading_day = data.resample('M', kind='date').first()1920    # 确定循环次数,因为得保证满足定投周期21    try:22        All_Sales = pd.DataFrame()23        for i in range(len(trading_day) - (6*n)):24            # 在定投周期结束后一个月卖出25            trading_cycle = trading_day.iloc[i:i+6*n+1] 2627            # 计算卖出点 下个月的指数均值28            in_month = data[data['trade_month']==list(trading_cycle['trade_month'][-1:])[0]]29            sales_point = in_month.pivot_table(values='open', index='trade_month').mean().values[0]303132            # 定投指数基金33            AIP = pd.DataFrame(index=trading_cycle.index)34            AIP['定投金额'] = int(money)3536            # 以基金当天的开盘价作为当天买入的价格37            AIP['基金价格'] = trading_cycle['open']38            AIP['购买基金份额'] = AIP['定投金额']/AIP['基金价格']39            AIP['累计基金份额'] = AIP['购买基金份额'].cumsum()4041            # 定期购买理财产品42            AIP['购买理财产品份额'] = AIP['定投金额']/trading_cycle['理财收益_净值']43            AIP['累计理财产品份额'] = AIP['购买理财产品份额'].cumsum()4445            # 累计投入本金46            AIP['累计定投本金'] = AIP['定投金额'].cumsum()4748            # 计算每个交易日的本息(即本金+利息,公式=当天的份额 X 当天的基金价格)49            result = pd.concat([trading_cycle, AIP], axis=1)50            result['基金本息'] = (result['open'] * result['累计基金份额']).astype('int')51            result['理财本息'] = (result['理财收益_净值'] * result['累计理财产品份额']).astype('int')5253            # 买入点 result['trade_date'][0]54            # 定投周期(月) 6*n55            # 定投投入本金 result['累计定投本金'][-2:-1][0]56            # 基金卖出后本息 result['累计基金份额'][-2:-1][0] * sales_point57            # 余额宝卖出后本息 result['理财本息'][-2:-1][0]585960            Each_Sales = pd.DataFrame([[result['trade_date'][0], 61                                       6*n, 62                                       result['累计定投本金'][-2:-1][0],63                                       result['累计基金份额'][-2:-1][0] * sales_point, 64                                       result['理财本息'][-2:-1][0]]], 65                                     columns=['买入点','定投周期(月)', '累计定投本金', '基金卖出后本息', '余额宝卖出后本息'])66            Each_Sales['基金收益率%'] = 100*(Each_Sales['基金卖出后本息'][0]/Each_Sales['累计定投本金'][0] - 1)67            Each_Sales['余额宝收益率%'] = 100*(Each_Sales['余额宝卖出后本息'][0]/Each_Sales['累计定投本金'][0] - 1)68            Each_Sales['LikeOrNot'] = Each_Sales['基金卖出后本息'] > Each_Sales['余额宝卖出后本息'] 69            All_Sales = All_Sales.append(Each_Sales)7071        return All_Sales7273    except:74        print("定投周期大于历史股价走势!请重新设置定投周期。")757677def Rate_of_Like(data, money):78    data= data79    money= money8081    Rate_of_like = pd.DataFrame()82    for i in range(int(len(trading_day)/6)):83        tt = Log_of_AIP(data_sh, i+1, 2000)84        rate = pd.DataFrame([[(i+1)*6, (tt['LikeOrNot'].value_counts()/len(tt))[True]]], 85                            columns=['定投周期(月)','定投基金满意占比'])86        Rate_of_like = Rate_of_like.append(rate)87    return Rate_of_like
复制代码
[h2]4. 函数调用&猜想验证[/h2]
  1. 1rate_of_sh = Rate_of_Like(data_sh, 2000)2rate_of_sh.head()
复制代码
  1. 1# 绘制定投满意概率图 2of.offline.init_notebook_mode(connected=True) 3 4trace1 = go.Scatter( 5    x=rate_of_sh['定投周期(月)'], 6    y=rate_of_sh['定投基金满意占比'], 7    mode = 'lines+markers', 8    name = '定投基金满意的次数占比' 9)10data = go.Data([trace1])1112layout = dict(title = '是否存在最合适的定投周期?',13              yaxis = dict(showgrid=True,  #网格14                           zeroline=False,  #是否显示基线,即沿着(0,0)画出x轴和y轴15                           nticks=20,16                           showline=True,17                           title='定投基金满意的次数占比'),1819              xaxis = dict(showgrid=True,  #网格20                           zeroline=False,  #是否显示基线,即沿着(0,0)画出x轴和y轴21                           nticks=20,22                           showline=True,23                           title='定投周期(月)')24             )25fig = dict(data=data, layout=layout)26of.plot(fig, filename='rate_of_like')
复制代码


上面,我只是验证了上证综指,从数据中可以看出,当定投周期超出240个月,即20年,我们获得满意(即定投收益跑赢余额宝定存收益)的基金定投的概率超出80%!
此外,我们看到,好像定投周期在300个月以上,获得满意的概率竟然得到了100%,但是我们知道这不太符合实际,原因是因为这基本是从上证开盘就开始定投,也不太满足我们的实际操作。
综上所述:(针对上证综指)
  • 定投周期在50个月之内的,获得满意的概率是一半一半,定投的威力还没体现出来;
  • 当定投周期在60个月左右的满意概率与定投60~120个月的满意概率差不多;
  • 定投240个月左右的满意概率可以达到80%,定投的威力终于来了!

[h1] 设置多少止盈点较为合适?[/h1][h2]1. 函数调用[/h2]
  1. 1log = Log_of_AIP(data_sh, 40, 2000) 2log = log.sort_values(['基金收益率%']) 3 4# 转化为密度函数,以10%为一个单位 5log['density'] = log['基金收益率%'].apply(lambda x:int(x/10)) 6log2 = log.pivot_table(values='LikeOrNot', index='density', aggfunc=len).reset_index() 7log2['density2'] = log2['density'].apply(lambda x:str(10*x) +'%~'+str(10*(x+1)) + '%') 8 9# 帕累托图10log2['density3'] = log2['density'].cumsum()11log2['density4'] = log2['density3']/(log2.tail(1).reset_index()['density3'][0])12log2
复制代码
[h2]2. 猜想绘制[/h2]这里只是贴了其中一个绘制代码:
  1. 1# 绘制收益分布图 2of.offline.init_notebook_mode(connected=True) 3 4trace1 = go.Scatter( 5    x = pd.Series(range(len(log))), 6    y = log['基金收益率%'], 7    mode = 'markers', 8    name = '基金 收益率分布图' 9)1011trace2 = go.Scatter(12    x = pd.Series(range(len(log))),13    y = log['余额宝收益率%'],14    mode = 'markers',15    name = '余额宝 收益率分布图'16)1718data = go.Data([trace1,trace2])1920layout = dict(title = '设置多少止盈点较为合适?',21              yaxis = dict(showgrid=True,  #网格22                           zeroline=False,  #是否显示基线,即沿着(0,0)画出x轴和y轴23                           nticks=20,24                           showline=True,25                           title='投资收益率%'),2627              xaxis = dict(showgrid=True,  #网格28                           zeroline=False,  #是否显示基线,即沿着(0,0)画出x轴和y轴29                           nticks=20,30                           showline=True,31                           title='迭代次数'32                           )33             )34fig = dict(data=data, layout=layout)35of.plot(fig,filename='log')
复制代码


我们按照定投最优的周期(240个月)来进行止盈点的计算,从上图可以看出,余额宝在定投240个月后,收益率大概维持在50%左右,而基金大多数的收益率都是大于余额宝的。




根据基金的收益率,我进行了密度转换,可以看出,我们的收益率,大致都是集中在60~70%之间。
综上所述:(针对上证综指)
  • 定投20年基金,大概率(80%)水平上会获得比余额宝要好得多的收益;
  • 定投20年基金,止盈点定在80~90%,发生的可能性为80%;
  • 定投20年基金,止盈点定在130~140%,发生的可能性为50%;
  • 定投20年基金,止盈点定在200~210%,发生的可能性为20%;

[h1] 其他策略[/h1]此外,我也从网上看到了其他更佳复杂的投资策略,这里就先不验证,先放出来给大家参考,下次一起验证:
方法一:高抛低吸降成本
熊市中,不要相信某一次上涨行情可能是新一轮牛市的开端,牛市的到来远远比你想象的要遥远。对于部分套牢的基金,建议在反弹10%以上时,逐步卖出,保留一部分底仓。然后遇到下跌,再买回来。这样做,大概率上会降低成本。
要牢牢记住,反弹多了就减仓,就算你很不幸,卖在了牛市的起步阶段,你也可以在牛市行情确立的时候再买回来。
这样高抛低吸的来回交易,对主动管理型基金来说,成本费用太高。建议买入创业板ETF或者中小板ETF,手续费更便宜。
方法二:10%补仓法
不断进行补仓,使亏损比例控制在10%以内,然后一个10%的反弹就解套了。是不是很简单?缺点就是要有足够的资金。

[h1] 本文总结[/h1]我们通过上述的验证,大致可以得到下面的结论,当然这个结论是针对上证综指的,按道理我应该看过几个指数,但是授之以鱼不如授之以渔,方法都在代码里,大家不妨自己动手试试?自己得到一些结论不是来得更有feel不
  • 定投周期在50个月之内的,定投跑赢余额宝的概率是50%;
  • 当定投周期在60个月(5年)左右的满意概率与定投60~120个月的满意概率差不多,大概是65%;
  • 定投240个月(20年)左右的满意概率可以达到80%,大概率水平上会获得比余额宝要好得多的收益!
  • 定投20年基金,止盈点定在80~90%,发生的可能性为80%;
  • 定投20年基金,止盈点定在130~140%,发生的可能性为50%;
  • 定投20年基金,止盈点定在200~210%,发生的可能性为20%;
感谢阅读
推荐阅读:
1:漫话:程序员要失业了??!机器人开始在GitHub上修Bug了。
2:非科系大学生如何学编程?
3:阿里入职的第一年,这是你想要的职场生活吗?
4:2018年Python爱好者社区历史文章合集(作者篇)
5:2018年终精心整理|Python爱好者社区历史文章合集(类型篇)




分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:155
帖子:32
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP