策略开发(多品种)

1、资产配置理论基础

1.1资产配置概述

资产配置是指投资者在投资过程中,根据自身的风险偏好、投资目标和市场情况等因素,将资金分配到不同的资产类别中,以达到最优化的风险收益比例的过程。通常资产类别包括股票、债券、房地产、黄金、现金等。

资产配置的目的是根据不同的资产类别的特征,将投资风险分散,降低整体投资组合的风险。例如,股票通常具有较高的波动性和风险,但在经济繁荣时期会有较高的回报;债券则相对较稳定,但回报率较低。通过在不同的资产类别之间进行分配,可以在一定程度上平衡投资组合的风险和回报。

资产配置需要考虑个人的风险承受能力、投资目标和市场环境等多方面因素。它是投资过程中非常重要的一步,通过合理的资产配置可以有效地控制风险、实现最大化的回报。

根据范围,资产配置可以分为全球资产配置、股票债券资产配置和行业风格资产配置;按时间跨度和风格类别,可分为战略性资产配置、战术性资产配置和资产混合配置;按资产管理人的特征、投资者的性质,可分为买入并持有策略、恒定混合策略、投资组合保险策略和战术性资产配置策略。

总之,资产配置是一种投资策略,旨在通过分散投资来降低风险并优化回报。它要求投资者根据自身的风险承受能力和投资目标,以及市场环境等因素,对各类资产进行选择和配置

1.2资产配置策略

资产配置的策略很多,有以下几种先行参考:

美林投资时钟:
美林“投资时钟”理论是一种将“资产”、“行业轮动”、“债券收益率曲线”以及“经济周期四个阶段”联系起来的方法,是一个实用的指导投资周期的工具.
美林投资时钟理论按照经济增长与通胀的不同搭配,将经济周期划分为四个阶段:
1、“经济上行,通胀下行”构成复苏阶段,此阶段由于股票对经济的弹性更大,其相对债券和现金具备明显超额收益;
2、“经济上行,通胀上行”构成过热阶段,在此阶段,通胀上升增加了持有现金的机会成本,可能出台的加息政策降低了债券的吸引力,股票的配置价值相对较强,而商品则将明显走牛;
3、“经济下行,通胀上行”构成滞胀阶段,在滞胀阶段,现金收益率提高,持有现金最明智,经济下行对企业盈利的冲击将对股票构成负面影响,债券相对股票的收益率提高;
4、“经济下行,通胀下行”构成衰退阶段,在衰退阶段,通胀压力下降,货币政策趋松,债券表现最突出,随着经济即将见底的预期逐步形成,股票的吸引力逐步增强。

生命周期法:
生命周期法是一种将资产配置与生命周期理论相结合的方法,它将投资者的生命周期阶段与资产配置策略联系起来,根据投资者的不同阶段,选择合适的资产配置策略。
生命周期法将投资者的生命周期阶段分为以下四个阶段:
年轻人:这个阶段的投资者负担小,收入有望不断提升,对新事物接受程度高,往往能接受较高的风险。适合进行积极的组合配置,也就是高风险高收益的品种。
中年人:作为中年人,收入比较稳定,但往往是上有老,下有小,对资金的安全性要求较高,适合比较稳健的投资。一半高收益高风险,一半低收益低风险式的哑铃配置。
 临近退休:这时投资者不时之需的资金就比较多,投资需要更加的保守,货币基金、纯债基金是主要投资方向,偏向配置稳健品种。
老年人:这时投资者已经完全进入“养老”阶段,对风险的承受能力已经非常低,适合进行非常保守的投资,只配置货币基金和纯债基金。

标准普尔家庭资产配置:
标准普尔家庭资产象限图”把家庭资产分成四个账户,这四个账户作用不同,所以资金的投资渠道也各不相同。只有拥有这四个账户,并且按照固定合理的比例进行分配才能保证家庭资产长期、持续、稳健的增长。
第一个账户是日常开销账户,一般占家庭资产的10%,为家庭3-6个月的生活费。这个账户保障家庭的短期开销,日常生活、旅游等都从这个账户中支出。日常开销账户适合买货币基金或短期理财工具,主要强调保本和流动性的基础上,有一定的增值。

第二个账户是杠杆账户,一般占家庭资产的20%,为的是以小博大,专门解决突发的大额开支。这个账户主要是保险,保障突发的大额开销。

第三个账户是投资收益账户,一般占家庭资产的30%,为家庭创造收益,用有风险的投资创造高回报,包括投资的股票、基金、房产等。这个账户关键在于合理的占比,无论盈亏对家庭不能有致命性的打击,这样才能从容的抉择。

第四个账户是长期收益账户,一般占家庭资产的40%,为保障家庭成员的养老金、子女教育金、留给子女的钱等。一定要用,并需要提前准备的钱。

按这个定律安排资产,既可满足家庭生活的日常需要,又可通过投资保值增值,还能为家庭提供基本保险保障。由于这一套配置比例是按照40%30%20%10%进行分配。所以又被人称作4321配置法。

股债再平衡策略:
股债平衡策略最开始是由巴菲特的老师格雷厄姆写在他的一本非常出名的著作《聪明的投资者》里。大概的意思就是把自己三年甚至更长时间不用的闲钱分成平均的两部分,50%买股票,50%买债券,然后每年年底进行动态平衡一次。

股债平衡策略,本质上是一种根据市值进行仓位调整的策略,也即股债各投一半,定期进行平衡,股市涨得多就卖掉一些,买人债券,使得两类资产的市值比例回归到1:1。

两类资产的市值比例可以依据风险偏好主动调整,如果风险偏好低,债券的比例就可以多一点。这种策略虽然简单,却大道至简、充满智慧。

我们可以根据行情变化自己动态调整股债比例,当然也有更简单的办法就是直接配置股债平衡策略的基金。

优秀的股债平衡型基金,可以达到“涨时像股票基金,跌时像债券基金”的效果。平衡混合型基金多以四六开、五五开的股债配比为主,但基金公司也开始探索“量身定制”的路子,推出股债配置比例更加多样化的平衡混合型基金,以更好地满足投资者的需求。

总体来说,平衡混合型基金收益可观,且回撤控制表现优异。在牛市,平衡混合型基金可以有望捕捉到较高收益,而在熊市则可一定程度上规避市场调整的风险,可以说是股票与债券的双赢。

从上述策略理论可以看出,不同资产配置策略手法不一,
例如美林时钟根据经济周期辅助择时调整配置,出发点是经济理论。
家庭标准普尔家庭资产配置出发点是家庭资产,生命周期出发点是人生周期,股债平衡是品种协同。但核心思想都是通过投资组合的优化,实现资产的长期、持续、稳健的增长
所以资产配置的策略无高低之分,只有适用与否的差别。
比如你让一个25岁的年轻单身小伙子,按照标准普尔方法去配置资产,首先面临的问题不是他会不会,而是无账可管。
所以任何资产配置策略,都是基于需求才存在的。

2、经典股债量化配置策略构建

本节我们以股债平衡策略为例,构建一个资产配置策略。并基于Python进行量化回测。

2.1策略思路

1.股债4-6动态平衡策略,债券占60%仓位,股票占40%。股票仓位是以沪深300指数基金来替代,债券仓位则是信用债基金来替代。
2.每个交易日检查一次持仓,若股票仓位占比小于36%或大于44%时触发平衡,将两则比例再次调整为4:6!思路比较简单,也很容易落地。

2.2数据获取及整理

#导入相关库,为了避免重复,这里一次性导入本内容所需要的所有库
import pandas as pd 
import numpy as np      
import matplotlib.pyplot as plt
import akshare as ak
import warnings
import backtrader as bt
import datetime
import statsmodels.api as sm
warnings.filterwarnings('ignore')
plt.rcParams['font.sans-serif'] = ['Source Han Sans CN']#和鲸平台可用的中文字体
plt.rcParams['axes.unicode_minus'] = False




#导入数据
df=pd.read_excel('/home/mw/input/0017037/博时沪深300指数_时序数据.xlsx')
df1=pd.read_excel('/home/mw/input/0017037/信用债.xlsx' )



#查看两个基金的走势
fig, ax1 = plt.subplots(figsize=(10,6),dpi=100)#创建画布
ax1.plot(df.datetime, df['close'].values,label='博时深300指数C')
ax1.plot(df1.datetime, df1['close'].values,label='富国信用债C')
plt.yticks(fontsize=10)
plt.xticks(fontsize=10)
plt.legend(loc='upper left',fontsize=10)

那我们就按照上述要求回测一下,其中股票仓位为沪深300指数基金,债券仓位为富国信用债。回测时间为2016年1月-2023年6月30日:

2.3策略回测

#导入数据
cerebro = bt.Cerebro()
#获取行情
df['openinterest']=0#添加一列数据
df1['openinterest']=0#添加一列数据
data=df.loc[:,['open','high','low','close','volume','openinterest','datetime']]#选择数据
data=data.set_index(pd.to_datetime(data['datetime'].astype('str'))).sort_index()#排序
data1=df1.loc[:,['open','high','low','close','volume','openinterest','datetime']]#选择数据
data1=data1.set_index(pd.to_datetime(data1['datetime'].astype('str'))).sort_index()#排序
datafeed = bt.feeds.PandasData(dataname=data,
                            fromdate=datetime.datetime(2016,1,1),
                            todate=datetime.datetime(2023,6,30))
cerebro.adddata(datafeed, name='000300.SH') # 通过 name 实现数据集与股票的一一对应
print('000300.SH读取成功')
datafeed = bt.feeds.PandasData(dataname=data1,
                            fromdate=datetime.datetime(2016,1,1),
                            todate=datetime.datetime(2023,6,30)) 
cerebro.adddata(datafeed, name='000192.OF') # 通过 name 实现数据集与股票的一一对应
print('000192.OF读取成功')

这里面特别说明一下费用问题,基金每年是有一定费用支出的,一般在1%-2%,这里选择C份额,费用少一些。费用参考如下:
沪深300指数c管理费率0.98%,销售服务费0.4%,托管费0.2%,最高赎回费1.5%
富国信用债c管理费率0.5%,销售服务费0.4%,托管费0.1%,最高赎回费1.5%
这里假设赎回费0.5%!那么两个基金的年综合费率在1.5%-2.08%之间,说高不高,说低不低,但对收益率肯定是有影响的!因为用的是累计净值数据回测,所以不考虑管理和托管费,只需要剔除掉销售及赎回费用即可。

#创建策略
class beta_balance(bt.Strategy):
    def notify_order(self, order):  # 当订单状态变化时触发 
        if order.status in [order.Submitted, order.Accepted]:  # 接受订单交易,正常情况
            return
        if order.status in [order.Completed]:
            if order.isbuy():
                print(self.datetime.date(0),'已买入, 购入价格 {:.2f}, 金额 {:.2f} ,手续费{:.2f}'.format(order.executed.price, order.executed.value, order.executed.comm))             
            elif order.issell():
                print(self.datetime.date(0),'已卖出, 卖出价格 {:.2f}, 金额{:.2f} ,手续费{:.2f}'.format(order.executed.price, order.executed.value, order.executed.comm))
        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            print('订单取消、保证金不足、金额不足拒绝交易')
    def __init__(self):
        self.stock=0.4
        self.bond=0.6
        self.day_num=1
    def next(self):
        if self.getposition(self.data0).size==0:#空仓时
            self.buy_stock=int(self.broker.get_cash()*self.stock/self.data0.close[0])#计算买入股票指数数量
            self.buy_bond=int(self.broker.get_cash()*self.bond/self.data1.close[0])#计算买入债基数量
            self.buy(data=self.data0,size=self.buy_stock)
            self.buy(data=self.data1,size=self.buy_bond)          
        elif self.getposition(self.data0).size>0:#持仓时
            stock_ccsl=self.getposition(self.data0).size#股指etf数量            
            if stock_ccsl*self.data0.close[0]/self.broker.getvalue()>0.44: #股票仓位大于44%时
                print('股票占比较大,股票占比',self.datetime.date(0),stock_ccsl*self.data0.close[0]/self.broker.getvalue())        
                bond_ccsl=self.getposition(self.data1).size#债基数量
                sell_stock=int(stock_ccsl-self.broker.getvalue()*0.4/self.data0.close[0])#需要卖的股指数量
                self.close(data=self.data0,size=sell_stock)
                buy_bond=int(self.broker.getvalue()*0.599/self.data1.close[0]-bond_ccsl)
                self.buy(data=self.data1,sizi=buy_bond)
            elif stock_ccsl *self.data0.close[0]/self.broker.getvalue()<0.36:  #股票仓位小于36%时
                print('债券占比较大,股票占比',self.datetime.date(0),stock_ccsl*self.data0.close[0]/self.broker.getvalue())              
                bond_ccsl=self.getposition(self.data1).size
                sell_bond=int(bond_ccsl-self.broker.getvalue()*0.6/self.data1.close[0])
                self.close(data=self.data1,sizi=sell_bond)
                buy_stock=int(self.broker.getvalue()*0.399/self.data.close[0]-stock_ccsl)
                self.buy(data=self.data,size=buy_stock)
            


# 添加分析指标
# 返回年初至年末的年度收益率
cerebro.addanalyzer(bt.analyzers.AnnualReturn,_name='_AnnualReturn')
# 计算最大回撤相关指标
cerebro.addanalyzer(bt.analyzers.DrawDown,_name='_DrawDown')
# 计算年化收益
cerebro.addanalyzer(bt.analyzers.Returns,_name='_Returns',tann=252)
# 计算年化夏普比率
cerebro.addanalyzer(bt.analyzers.SharpeRatio_A,_name='_SharpeRatio_A')
# 返回收益率时
cerebro.addanalyzer(bt.analyzers.TimeReturn,_name='_TimeReturn')
            
cerebro.addstrategy(beta_balance)
# 初始资金 1,000,000
cerebro.broker.setcash(1000000.0)
# 因为购买的是基金,所以不算交易费用,但通过interest设置利息费用充当上述所说的费用。
cerebro.broker.setcommission(commission=0,percabs=True,stocklike=True,interest=0.01,interest_long=True,name='000300.SH')
cerebro.broker.setcommission(commission=0,percabs=True,stocklike=True,interest=0.01,interest_long=True,name='000192.OF')
# 滑点:双边各 0.0001
cerebro.broker.set_slippage_perc(perc=0.001)
cerebro.broker.set_coc(True)
# 启动回测
result=cerebro.run()





#绘制收益率走势图
ret = pd.Series(result[0].analyzers._TimeReturn.get_analysis())#获得收益时序图
#画图
fig, ax1 = plt.subplots(figsize=(10,6),dpi=100)#创建画布
ax1.plot((ret+1).cumprod().index,(ret+1).cumprod().values,'r-',label = "策略走势")
ax1.plot(pd.to_datetime(data.index),(data['close'].diff(1)/data['close'].shift(1)+1).cumprod().values,'b-',label = "沪深300收益走势")
ax1.plot(pd.to_datetime(data1.index),(data1['close'].diff(1)/data1['close'].shift(1)+1).cumprod().values,'g-',label = "信用债基金收益走势")
ax1.set_title('策略收益与基准走势对比',fontsize=10)
plt.legend(loc='upper left',fontsize=10)
plt.grid(True,linestyle='--')
plt.show()

对比一下纯股和纯债的收益率走势,股债4-6平衡的优势可以用一句话来形容——比股基波动小,比债基收益高!适合稳健偏好的投资者。但年化收益率较低,存在巨大的优化空间。

2.4本节动手题

请你结合上述案例,对股债4-6平衡策略进行优化,以夏普比率为评判标准,寻求理论上夏普比例最高的股债配置比例。
提示:这是一个参数寻优题,虽然在实战中有过拟合的可能,但是技能还是要会的。backtrader框架有自带的参数寻优模型,这里自行学习印象更深刻。

3、资产配置策略的风控及优化

3.1优化思路

股债平衡这种策略优化有两个方向,其一优化股或债的比例,其二做择时。
例如上述股债比是4:6,那么可以调整为5:5或者8:2,但没有必要精确到小数点位后两位,因为容易陷入参数拟合的陷阱,所以重点优化方向放在择时上。
目前策略采用的是被动调仓,也就是涨多了就卖点,跌多了就买点的方式,那如果我们加上主动择时对股债仓位比例调整,结果如何?
首先我们要解决的是指数择时的问题,这里可选的方式很多,技术指标类的可以参考我之前写的文章。这里我们采用股债利差指标来择时——沪深300指数市盈率的倒数减去十年期国债收益率!
这个模型非常有名,且实战效果极好,唯一的缺点就是周期偏长。

3.2择时指标的构建

#导入数据
gzb=pd.read_excel(r'/home/mw/input/0017037/中债国债到期收益率_10年.xls')
gzb.fillna(method='pad', inplace=True)
gzb.set_index(pd.to_datetime(gzb['指标名称']),inplace=True)



#计算股债利差
gzb['沪深300指数PE倒数']=1/gzb['沪深300:滚动市盈率(TTM)']#计算市盈率倒数
gzb['股债利差']=gzb['沪深300指数PE倒数']*100-gzb['中债国债到期收益率:10年']#计算利差



#可视化利差及沪深300指数
fig, ax1 = plt.subplots(figsize=(20,12),dpi=200)#创建画布
ax2 = ax1.twinx()#共享x轴
ax1.plot(gzb.index,gzb['股债利差'].values,'r-',label = "利差走势")
ax2.plot(gzb.index,gzb['收盘价:沪深300指数'].values,'b-',label = "沪深300指数走势")
ax1.set_title('利差与沪深300走势对比',fontsize=30)
ax1.legend(loc='upper left',fontsize=15)
ax2.legend(loc='upper right',fontsize=15)
plt.grid(True,linestyle='--')
#fig.legend(loc='upper left',fontsize=15)
plt.show()

市盈率的倒数可以间接反映潜在回报率——10倍市盈率可以看做10年收回本金,潜在回报率就是10%。实体投资项目不能这么简单计算,但是用在指数做估值对比是可以的。
那从图形上来看,两者走势基本相反,利差高点的时候指数位置较低,利差低点的时候,指数位置较高。但每个高点和低点并不绝对,所以我们在这里加上利差通道系统。

#利差标准差线
gzb['750天均线']=gzb['股债利差'].rolling(750).mean()
gzb['750天标准差']=gzb['股债利差'].rolling(750).std()
gzb['标准差p1']=gzb['750天均线']+gzb['750天标准差']
gzb['标准差n1']=gzb['750天均线']-gzb['750天标准差']
gzb['标准差p2']=gzb['750天均线']+gzb['750天标准差']*2
gzb['标准差n2']=gzb['750天均线']-gzb['750天标准差']*2
# 将日期列转换为datetime类型
gzb['日期'] = pd.to_datetime(gzb['指标名称'])
# 按月划分并为每个日期分配每日序号
gzb['day_number'] = gzb.groupby(pd.Grouper(key='日期', freq='M')).cumcount() + 1




#可视化利差及沪深300指数
import numpy as np
gzb=gzb.iloc[750:,:]
fig, ax1 = plt.subplots(figsize=(20,12),dpi=200)#创建画布
ax2 = ax1.twinx()#共享x轴
ax1.plot(gzb.index,gzb['股债利差'].values,'r-',label = "利差走势")
ax1.plot(gzb.index,gzb['750天均线'].values,'y-',label = "750天均值走势")
ax1.plot(gzb.index,gzb['标准差p1'].values,'c-',label = "+1标准差")
ax1.plot(gzb.index,gzb['标准差p2'].values,'b-',label = "+2标准差")
ax1.plot(gzb.index,gzb['标准差n1'].values,'c-',label = "-1标准差")
ax1.plot(gzb.index,gzb['标准差n2'].values,'b-',label = "-2标准差")
ax2.plot(gzb.index,gzb['收盘价:沪深300指数'].values,color=(0,0,0,0),label = "沪深300指数走势")
ax2.fill_between(gzb.index, np.zeros(len(gzb)), gzb['收盘价:沪深300指数'].values, color='lightgray',alpha=0.6)
ax1.set_title('利差走势通道',fontsize=30)
ax1.legend(loc='upper left',fontsize=15)
ax2.legend(loc='upper right',fontsize=15)
plt.grid(True,linestyle='--')
plt.show()

通过利差通道系统我们可以判断利差所处的相对位置,这样就可以利用利差所处的相对位置调整股票指数的仓位!
原先我们的策略的股债4:6,那么我们定制一个新的规则:
1.若利差在+1标准差之上,可以有理由认为指数相对低估,因此在此区间股债比例为8:2。
2.若利差在-1标准差之下,可以有理由认为指数相对高估,因此在此区间股债比例为2:8。
3.若利差在-1至+1区间,则认为估值相对合理,因此股债比例为5:5!
4.持有期间同样动态调整,每个交易日检查一次持仓,若股票仓位占比小于或大于上述要求的比例,则将两者再次调整为要求的比例,若中间仓位变动超过10%,自动止盈或止损。

3.3新版策略回测

cerebro = bt.Cerebro()
#定义数据读取
class PandasData_more(bt.feeds.PandasData):
    lines = ('股债利差','标准差p1','标准差p2','标准差n1','标准差n2','day_number') # 要添加的线
    # 设置 line 在数据源上的列位置
    params=(
        ('股债利差', -1),('标准差p1', -1),('标准差p2', -1),('标准差n1', -1),('标准差n2', -1),('day_number',-1),)
    # -1表示自动按列明匹配数据,也可以设置为线在数据源中列的位置索引 
#获取行情
df=pd.read_excel('/home/mw/input/0017037/博时沪深300指数_时序数据.xlsx')
df1=pd.read_excel('/home/mw/input/0017037/信用债.xlsx' )
df['openinterest']=0#添加一列数据
df1['openinterest']=0#添加一列数据
data=df.loc[:,['open','high','low','close','volume','openinterest','datetime']]#选择数据
data=data.set_index(pd.to_datetime(data['datetime'].astype('str'))).sort_index()#排序
data1=df1.loc[:,['open','high','low','close','volume','openinterest','datetime']]#选择数据
data1=data1.set_index(pd.to_datetime(data1['datetime'].astype('str'))).sort_index()#排序
datafeed = bt.feeds.PandasData(dataname=data,
                            fromdate=datetime.datetime(2016,1,27),
                            todate=datetime.datetime(2023,6,30))
cerebro.adddata(datafeed, name='000300.SH') # 通过 name 实现数据集与股票的一一对应
print('000300.SH读取成功')
datafeed = bt.feeds.PandasData(dataname=data1,
                            fromdate=datetime.datetime(2016,1,27),
                            todate=datetime.datetime(2023,6,30)) 
cerebro.adddata(datafeed, name='000192.OF') # 通过 name 实现数据集与股票的一一对应
print('000192.OF读取成功')
datafeed=PandasData_more(dataname=gzb,
                            fromdate=datetime.datetime(2016,1,27),
                            todate=datetime.datetime(2023,6,30)) 
cerebro.adddata(datafeed, name='gzb') # 通过 name 实现数据集与股票的一一对应)
print('gzb读取成功')






#回测主程序
#创建策略
class beta_balance(bt.Strategy):
    def notify_order(self, order):  # 当订单状态变化时触发 
        if order.status in [order.Submitted, order.Accepted]:  # 接受订单交易,正常情况
            return
        if order.status in [order.Completed]:
            if order.isbuy():
                print(self.datetime.date(0),'已买入, 购入价格 {:.2f}, 金额 {:.2f} ,手续费{:.2f}'.format(order.executed.price, order.executed.value, order.executed.comm))             
            elif order.issell():
                print(self.datetime.date(0),'已卖出, 卖出价格 {:.2f}, 金额{:.2f} ,手续费{:.2f}'.format(order.executed.price, order.executed.value, order.executed.comm))
        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            print('订单取消、保证金不足、金额不足拒绝交易')
    def __init__(self):
        self.stock=0.49#股票起始默认仓位,留点空余给手续费,下同
        self.bond=0.49#债券起始默认仓位
        self.day_num=1
    def next(self):
        if self.getposition(self.data0).size==0:#空仓时,初始建仓使用股债5:5组合。
            self.order = self.order_target_percent(data=self.data0,target=self.stock)          
            self.order = self.order_target_percent(data=self.data1,target=self.bond)   
        elif self.getposition(self.data0).size>0:#持有仓位时 
            if self.data2.day_number[0]==1 or self.data2.day_number[0]==11: 
                if self.data2.股债利差[0]>self.data2.标准差n1[0] and self.data2.股债利差[0]<self.data2.标准差p1[0]:#
                    self.stock=0.495#股票起始默认仓位
                    self.bond=0.495#债券起始默认仓位
                    stock_ccsl=self.getposition(self.data0).size#获得股票的持仓数量
                    if stock_ccsl*self.data0.close[0]/self.broker.getvalue()>self.stock*1.1: #股票持仓占比大于self.stock*1.1
                        print('股票占比较大,股票占比',self.datetime.date(0),stock_ccsl*self.data0.close[0]/self.broker.getvalue())      
                        self.order = self.order_target_percent(data=self.data0,target=self.stock)          
                        self.order = self.order_target_percent(data=self.data1,target=self.bond)  
                    elif stock_ccsl *self.data0.close[0]/self.broker.getvalue()<self.stock*0.9:  
                        print('债券占比较大,股票占比',self.datetime.date(0),stock_ccsl*self.data0.close[0]/self.broker.getvalue())   
                        self.order = self.order_target_percent(data=self.data1,target=self.bond)           
                        self.order = self.order_target_percent(data=self.data0,target=self.stock)          
                elif self.data2.股债利差[0]>self.data2.标准差p1[0]:#股票低估时,利差较大
                    self.stock=0.795#股票低估默认仓位
                    self.bond=0.195#债券高估默认仓位
                    stock_ccsl=self.getposition(self.data0).size#获得股票的持仓数量
                    if stock_ccsl*self.data0.close[0]/self.broker.getvalue()>self.stock*1.1: #股票持仓占比大于self.stock*1.1
                        print('股票占比较大,股票占比',self.datetime.date(0),stock_ccsl*self.data0.close[0]/self.broker.getvalue())      
                        self.order = self.order_target_percent(data=self.data0,target=self.stock)          
                        self.order = self.order_target_percent(data=self.data1,target=self.bond)  
                    elif stock_ccsl *self.data0.close[0]/self.broker.getvalue()<self.stock*0.9:  
                        print('债券占比较大,股票占比',self.datetime.date(0),stock_ccsl*self.data0.close[0]/self.broker.getvalue())    
                        self.order = self.order_target_percent(data=self.data1,target=self.bond)         
                        self.order = self.order_target_percent(data=self.data0,target=self.stock)          
                elif self.data2.股债利差[0]<self.data2.标准差n1[0]:
                    self.stock=0.195#股票高估默认仓位
                    self.bond=0.795#债券低估默认仓位
                    stock_ccsl=self.getposition(self.data0).size#获得股票的持仓数量
                    if stock_ccsl*self.data0.close[0]/self.broker.getvalue()>self.stock*1.1: #股票持仓占比大于self.stock*1.1
                        print('股票占比较大,股票占比',self.datetime.date(0),stock_ccsl*self.data0.close[0]/self.broker.getvalue())      
                        self.order = self.order_target_percent(data=self.data0,target=self.stock)          
                        self.order = self.order_target_percent(data=self.data1,target=self.bond)  
                    elif stock_ccsl *self.data0.close[0]/self.broker.getvalue()<self.stock*0.9:  
                        print('债券占比较大,股票占比',self.datetime.date(0),stock_ccsl*self.data0.close[0]/self.broker.getvalue())              
                        self.order = self.order_target_percent(data=self.data1,target=self.bond) 
                        self.order = self.order_target_percent(data=self.data0,target=self.stock)          
            


# 添加分析指标
# 返回年初至年末的年度收益率
cerebro.addanalyzer(bt.analyzers.AnnualReturn,_name='_AnnualReturn')
# 计算最大回撤相关指标
cerebro.addanalyzer(bt.analyzers.DrawDown,_name='_DrawDown')
# 计算年化收益
cerebro.addanalyzer(bt.analyzers.Returns,_name='_Returns',tann=252)
# 计算年化夏普比率
cerebro.addanalyzer(bt.analyzers.SharpeRatio_A,_name='_SharpeRatio_A')
# 返回收益率时
cerebro.addanalyzer(bt.analyzers.TimeReturn,_name='_TimeReturn')
           
cerebro.addstrategy(beta_balance)
# 初始资金 100,000,000
cerebro.broker.setcash(100000000.0)
# 佣金,双边各 0.0003
cerebro.broker.setcommission(commission=0,percabs=True,stocklike=True,interest=0.01,interest_long=True,name='000300.SH')
cerebro.broker.setcommission(commission=0,percabs=True,stocklike=True,interest=0.01,interest_long=True,name='000192.OF')
# 滑点:双边各 0.0001
cerebro.broker.set_slippage_perc(perc=0.001)
cerebro.broker.set_coc(True)
# 启动回测
result=cerebro.run()






#绘制收益率走势图
ret = pd.Series(result[0].analyzers._TimeReturn.get_analysis())#获得收益时序图
#画图
fig, ax1 = plt.subplots(figsize=(20,12),dpi=200)#创建画布
ax1.plot((ret+1).cumprod().index,(ret+1).cumprod().values,'r-',label = "策略走势")
ax1.plot(pd.to_datetime(data.index),(data['close'].diff(1)/data['close'].shift(1)+1).cumprod().values,'b-',label = "沪深300收益走势")
ax1.plot(pd.to_datetime(data1.index),(data1['close'].diff(1)/data1['close']+1).shift(1).cumprod().values,'g-',label = "信用债基金收益走势")
ax1.set_title('股债动态平衡策略收益与基准走势对比',fontsize=30)
plt.legend(loc='upper left',fontsize=15)
plt.grid(True,linestyle='--')
plt.show()




result[0].analyzers._DrawDown.get_analysis()



result[0].analyzers._Returns.get_analysis()

3.4本节小结

累计收益率53.2%,年化收益率5.93%,收益率得到了极大的提升!而最大回撤-16%!相比较收益率的提升,在可接受的范围内,至少比与纯指数的回撤幅度小!
到这里基本上策略已经成型了,别小看这收益率,这个水平至少可以打败70%的普通投资者!最关键是这个策略难度并不高,可以落地执行!一部分股债混合基金大致就是这个思路,区别就是细分标的和比例的不同。

3.5本节动手题(选做)

1.修改策略标的,沪深300换成中证500指数基金,同样以上述标准的买入,回测结果如何?
2.股票仓位以沪深300+中证500指数(沪深300与中证500之间仓位配比由自己决定),债券仓位标的不变,三者按照上述策略构建股债混合,回测结果如何?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值