基于backtrader的唐奇安通道策略实现
代码实现
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. 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
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' )
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' , )
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) :
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( ) )
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)])