基于backtrader的唐奇安通道策略实现

代码实现

##导入相关包 优化jupyter画图设置
from datetime import datetime,timedelta
import backtrader as bt
import tushare as ts
import pandas as pd
import talib as ta
import numpy as np
import matplotlib.pyplot as plt
import mplfinance as mpf
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False
plt.rcParams['figure.figsize']=[6, 3]
plt.rcParams['figure.dpi']=200
plt.rcParams['figure.facecolor']='w'
plt.rcParams['figure.edgecolor']='k'
# ts获取数据测试
ts.get_k_data('601398',autype='qfq',start='2020-01-01',end='2022-05-25')
date open close high low volume code
0 2020-01-02 5.391 5.441 5.501 5.381 2349493.0 601398
1 2020-01-03 5.441 5.461 5.491 5.431 1522130.0 601398
2 2020-01-06 5.431 5.441 5.521 5.421 2265097.0 601398
3 2020-01-07 5.451 5.481 5.511 5.451 1168043.0 601398
4 2020-01-08 5.431 5.381 5.441 5.371 1585590.0 601398
... ... ... ... ... ... ... ...
573 2022-05-19 4.610 4.610 4.620 4.590 1691556.0 601398
574 2022-05-20 4.620 4.630 4.630 4.590 2406916.0 601398
575 2022-05-23 4.620 4.620 4.640 4.620 1732244.0 601398
576 2022-05-24 4.630 4.670 4.680 4.620 2437749.0 601398
577 2022-05-25 4.670 4.660 4.680 4.650 1523157.0 601398

578 rows × 7 columns

# 获取数据函数编写
def get_data(code,start_date,end_date,period):
    df=ts.get_k_data(code,autype='qfq',start=start_date,end=end_date)
    df['ret']=df.close.pct_change()
    close=df.close
    high=df.high
    low=df.low
    up=pd.Series(0.0,index=close.index)
    down=pd.Series(0.0,index=close.index)
    middle=pd.Series(0.0,index=close.index)
    for i in range(period,len(close)):
        up[i]=max(high[(i-period):i])
        down[i]=min(low[(i-period):i])
        middle[i]=(up[i]+down[i])/2
    df['up']=up
    df['down']=down
    df['middle']=middle
    df=df[20:]
    df['atr']= ta.ATR(high, low, close, timeperiod=14)
    df=df[20:]
    df['openinterest']=0
    df.index=pd.to_datetime(df.date)
    df=df[['open','high','low','close','volume','openinterest','ret','up','down','middle','atr']] 
    return df
# 获取数据函数测试
stock_df=get_data('601398','2020-01-01','2022-05-25',20)
stock_df.head()
open high low close volume openinterest ret up down middle atr
date
2020-03-06 4.891 4.901 4.861 4.881 1959498.0 0 -0.010140 4.991 4.751 4.871 0.071731
2020-03-09 4.811 4.821 4.771 4.781 2880620.0 0 -0.020488 4.991 4.751 4.871 0.074465
2020-03-10 4.761 4.841 4.761 4.831 2508207.0 0 0.010458 4.991 4.751 4.871 0.074860
2020-03-11 4.821 4.841 4.791 4.791 1694070.0 0 -0.008280 4.991 4.751 4.871 0.073084
2020-03-12 4.771 4.791 4.741 4.761 2462965.0 0 -0.006262 4.991 4.751 4.871 0.071436
#  唐奇安通道画图
style = mpf.make_mpf_style(base_mpl_style="ggplot")
add_plot=[
    mpf.make_addplot(stock_df.up),
    mpf.make_addplot(stock_df.middle),
    mpf.make_addplot(stock_df.down)]
mpf.plot(data=stock_df,
    type="candle",
    title="Candlestick for fhgx",
         addplot=add_plot,
    ylabel="price",
    style=style,
    volume=True,
    figratio=(100,50))

在这里插入图片描述

# 编写唐奇安通道策略
def strategy(data,start,end):
    df=data
    x1=data.close>data.up
    x2=data.close.shift(1)<data.up.shift(1)
    x=x1&x2
    y1=data.close<data.down
    y2=data.close.shift(1)>data.down.shift(1)
    y=y1&y2
    data.loc[x,'收盘信号']=1
    data.loc[y,'收盘信号']=0
    data=data.fillna(0)
    df['当天仓位']=df['收盘信号'].shift(1)
    df['当天仓位'].fillna(method='ffill',inplace=True)
    d=df[df['当天仓位']==1].index[0]-timedelta(days=1)
    df1=df.loc[d:].copy()
    df1['ret'][0]=0
    df1['当天仓位'][0]=0
    #当仓位为1时,买入持仓,当仓位为-1时,空仓,计算资金净值
    df1['策略净值']=(df1.ret.values*df1['当天仓位'].values+1.0).cumprod()
    df1['指数净值']=(df1.ret.values+1.0).cumprod()
    df1['策略收益率']=df1['策略净值']/df1['策略净值'].shift(1)-1
    df1['指数收益率']=df1.ret
    total_ret=df1[['策略净值','指数净值']].iloc[-1]-1
    annual_ret=pow(1+total_ret,250/len(df1))-1
    dd=(df1[['策略净值','指数净值']].cummax()-df1[['策略净值','指数净值']])/df1[['策略净值','指数净值']].cummax()
    d=dd.max()
    beta=df1[['策略收益率','指数收益率']].cov().iat[0,1]/df1['指数收益率'].var()
    alpha=(annual_ret['策略净值']-annual_ret['指数净值']*beta)
    exReturn=df1['策略收益率']-0.03/250
    sharper_atio=np.sqrt(len(exReturn))*exReturn.mean()/exReturn.std()
    TA1=round(total_ret['策略净值']*100,2)
    TA2=round(total_ret['指数净值']*100,2)
    AR1=round(annual_ret['策略净值']*100,2)
    AR2=round(annual_ret['指数净值']*100,2)
    MD1=round(d['策略净值']*100,2)
    MD2=round(d['指数净值']*100,2)
    S=round(sharper_atio,2)
    df1[['策略净值','指数净值']].plot(figsize=(15,7))
    plt.title('海龟交易策略简单回测',size=15)
    bbox = dict(boxstyle="round", fc="w", ec="0.5", alpha=0.9)
    plt.text(df1.index[int(len(df1)/5)], df1['指数净值'].max()/1.5, f'累计收益率:\
策略{TA1}%,指数{TA2}%;\n年化收益率:策略{AR1}%,指数{AR2}%;\n最大回撤:  策略{MD1}%,指数{MD2}%;\n\
策略alpha: {round(alpha,2)},策略beta:{round(beta,2)}; \n夏普比率:  {S}',size=13,bbox=bbox)  
    plt.xlabel('')
    ax=plt.gca()
    ax.spines['right'].set_color('none')
    ax.spines['top'].set_color('none')
    plt.show()
strategy(data=stock_df,start='2020-01-01',end='2022-05-25')

在这里插入图片描述

## 编写bt回测策略
import backtrader.analyzers as btanalyzers
import backtrader.feeds as btfeeds
import backtrader.strategies as btstrats
from backtrader.feeds import PandasData
class PandasData(PandasData):
# 新增两条数据线
    lines = ('up', 'down',)   
    
   # 新增数据在dataframe中的位置
    params = (('up',8), ('down',9), )
class TestStrategy(bt.Strategy):
    
    def log(self,txt,dt=None):
        dt=dt or self.datas[0].datetime.date(0)
        print('%s,%s'%(dt.isoformat(),txt))
        
    def __init__(self):
        self.dataclose = self.datas[0].close
        self.dataup = self.datas[0].up
        self.datadown = self.datas[0].down
        self.datatime=self.datas[0].datetime.date(0)
        self.order=None  #跟踪挂单
        self.buyprice = None
        self.buycomm = None #加入手续费
        
    def notify_order(self,order):
        if order.status in [order.Submitted,order.Accepted]: #经纪商提交/接受/接受的买入/卖出订单 
            return
        if order.status in [order.Completed]: ## 检查订单是否完成
                                              # 注意:如果没有足够的现金,经纪人可能会拒绝订单
            
            if order.isbuy():
                self.log(
                    'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                    (order.executed.price,
                     order.executed.value,
                     order.executed.comm))
                self.buyprice = order.executed.price
                self.buycomm = order.executed.comm
                         
            else:
                 self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                          (order.executed.price,order.executed.value,order.executed.comm))
                      
            self.bar_executed=len(self)
            
        elif order.status in [order.Canceled,order.Margin,order.Rejected]:
            self.log('Order Canceled/Margin/Rejected')
            
        self.order=None #写下:无挂单
    def next(self):
#         self.log('Close,%.2f'%self.dataclose[0])
        if self.order: ## 检查订单是否处于待处理状态...如果是,我们不能发送第二个
            return
        if not self.position: #检查我们是否在市场上
            
            if self.dataclose[0]>self.dataup[0]:
                if self.dataclose[-1]<self.dataup[-1]:
                    self.log('BUY CREATE,%.2f'%self.dataclose[0])
                    self.log('BUY CREATE,%.2f'%self.dataup[0])
                    self.order=self.buy() #跟踪创建的订单以避免第二个订单
        else:
            
            if self.dataclose[0]<self.datadown[0]:
                if self.dataclose[-1]>self.datadown[-1]:
                    self.log('SELL CREATE,%.2f'%self.dataclose[0])
                    self.log('SELL CREATE,%.2f'%self.datadown[0])
                    self.order=self.sell()
#回测
data = PandasData(dataname=stock_df)
cerebro=bt.Cerebro()
cerebro.addstrategy(TestStrategy)
cerebro.adddata(data)
cerebro.addanalyzer(btanalyzers.SharpeRatio, _name='mysharpe')

cerebro.broker.setcash(50000.0)
cerebro.addsizer(bt.sizers.FixedSize, stake=5000) 
cerebro.broker.setcommission(commission=0.002) 
print(f'组合初始价值:%.2f'%cerebro.broker.getvalue())
#  运行broker
thestrats = cerebro.run()
print(f'组合期末价值:%.2f'%cerebro.broker.getvalue())
cerebro.plot(iplot=False)
thestrat = thestrats[0]
print('Sharpe Ratio:', thestrat.analyzers.mysharpe.get_analysis())

组合初始价值:50000.00
2020-03-16,BUY CREATE,4.64
2020-03-16,BUY CREATE,4.60
2020-03-17,BUY EXECUTED, Price: 4.63, Cost: 23155.00, Comm 46.31
2020-03-31,SELL CREATE,4.62
2020-03-31,SELL CREATE,4.67
2020-04-01,SELL EXECUTED, Price: 4.62, Cost: 23155.00, Comm 46.21
2020-09-04,BUY CREATE,4.63
2020-09-04,BUY CREATE,4.62
2020-09-07,BUY EXECUTED, Price: 4.62, Cost: 23120.00, Comm 46.24
2020-09-22,SELL CREATE,4.66
2020-09-22,SELL CREATE,4.68
2020-09-23,SELL EXECUTED, Price: 4.66, Cost: 23120.00, Comm 46.64
2020-12-23,BUY CREATE,4.69
2020-12-23,BUY CREATE,4.65
2020-12-24,BUY EXECUTED, Price: 4.69, Cost: 23470.00, Comm 46.94
2021-01-22,SELL CREATE,4.77
2021-01-22,SELL CREATE,4.83
2021-01-25,SELL EXECUTED, Price: 4.76, Cost: 23470.00, Comm 47.64
2021-04-29,BUY CREATE,4.95
2021-04-29,BUY CREATE,4.91
2021-04-30,BUY EXECUTED, Price: 4.94, Cost: 24720.00, Comm 49.44
2021-06-01,SELL CREATE,4.91
2021-06-01,SELL CREATE,4.92
2021-06-02,SELL EXECUTED, Price: 4.91, Cost: 24720.00, Comm 49.14
2021-07-15,BUY CREATE,4.75
2021-07-15,BUY CREATE,4.68
2021-07-16,BUY EXECUTED, Price: 4.75, Cost: 23750.00, Comm 47.50
2021-08-12,SELL CREATE,4.63
2021-08-12,SELL CREATE,4.64
2021-08-13,SELL EXECUTED, Price: 4.63, Cost: 23750.00, Comm 46.30
2021-11-11,BUY CREATE,4.66
2021-11-11,BUY CREATE,4.61
2021-11-12,BUY EXECUTED, Price: 4.66, Cost: 23300.00, Comm 46.60
2021-12-15,SELL CREATE,4.61
2021-12-15,SELL CREATE,4.62
2021-12-16,SELL EXECUTED, Price: 4.61, Cost: 23300.00, Comm 46.10
2022-03-10,BUY CREATE,4.56
2022-03-10,BUY CREATE,4.50
2022-03-11,BUY EXECUTED, Price: 4.55, Cost: 22750.00, Comm 45.50
2022-04-27,SELL CREATE,4.71
2022-04-27,SELL CREATE,4.73
2022-04-28,SELL EXECUTED, Price: 4.71, Cost: 22750.00, Comm 47.10
2022-05-13,BUY CREATE,4.70
2022-05-13,BUY CREATE,4.64
2022-05-16,BUY EXECUTED, Price: 4.70, Cost: 23500.00, Comm 47.00
组合期末价值:49395.34

在这里插入图片描述

Sharpe Ratio: OrderedDict([('sharperatio', -1.0256629031574036)])
Logo

加入社区!打开量化的大门,首批课程上线啦!

更多推荐