基于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'
def get_data ( code, start, end) :
df= ts. get_k_data( code, autype= 'qfq' , start= start, end= end)
df. index= pd. to_datetime( df. date)
df[ 'openinterest' ] = 0
df= df[ [ 'open' , 'high' , 'low' , 'close' , 'volume' , 'openinterest' ] ]
return df
dataframe= get_data( code= 'sh' , start= '2010-01-01' , end= '2020-07-17' )
dataframe. head( )
本接口即将停止更新,请尽快使用Pro版接口:https://waditu.com/document/2
open high low close volume openinterest date 2010-01-04 3289.750 3295.28 3243.32 3243.760 109447927.0 0 2010-01-05 3254.468 3290.51 3221.46 3282.179 126115066.0 0 2010-01-06 3277.517 3295.87 3253.04 3254.215 123651384.0 0 2010-01-07 3253.991 3268.82 3176.71 3192.776 128652827.0 0 2010-01-08 3177.259 3198.92 3149.02 3195.997 98377147.0 0
def plot_stock ( code, title, start, end) :
dd= ts. get_k_data( code, autype= 'qfq' , start= start, end= end)
dd. index= pd. to_datetime( dd. date)
dd. close. plot( figsize= ( 14 , 6 ) , color= 'r' )
plt. title( title+ '价格走势\n' + start+ ':' + end, size= 15 )
plt. annotate( f'期间累计涨幅: { ( dd. close[ - 1 ] / dd. close[ 0 ] - 1 ) * 100 : .2f } %' , xy= ( dd. index[ - 150 ] , dd. close. mean( ) ) ,
xytext= ( dd. index[ - 500 ] , dd. close. min ( ) ) , bbox = dict ( boxstyle = 'round,pad=0.5' ,
fc = 'yellow' , alpha = 0.5 ) ,
arrowprops= dict ( facecolor= 'green' , shrink= 0.05 ) , fontsize= 12 )
plt. show( )
plot_stock( code= 'sh' , title= '上证综指' , start= '2010-01-01' , end= '2022-07-17' )
class TurtleStrategy ( bt. Strategy) :
params = ( ( 'long_period' , 20 ) ,
( 'short_period' , 10 ) ,
( 'printlog' , False ) , )
def __init__ ( self) :
self. order = None
self. buyprice = 0
self. buycomm = 0
self. buy_size = 0
self. buy_count = 0
self. H_line = bt. indicators. Highest( self. data. high( - 1 ) , period= self. p. long_period)
self. L_line = bt. indicators. Lowest( self. data. low( - 1 ) , period= self. p. short_period)
self. M_line = ( self. H_line+ self. L_line) / 2
self. TR = bt. indicators. Max( ( self. data. high( 0 ) - self. data. low( 0 ) ) , \
abs ( self. data. close( - 1 ) - self. data. high( 0 ) ) , \
abs ( self. data. close( - 1 ) - self. data. low( 0 ) ) )
self. ATR = bt. indicators. SimpleMovingAverage( self. TR, period= 14 )
self. buy_signal = bt. ind. CrossOver( self. data. close( 0 ) , self. H_line)
self. sell_signal = bt. ind. CrossOver( self. data. close( 0 ) , self. L_line)
def next ( self) :
if self. order:
return
if self. buy_signal > 0 and self. buy_count == 0 :
self. buy_size = self. broker. getvalue( ) * 0.01 / self. ATR
self. buy_size = int ( self. buy_size / 100 ) * 100
self. sizer. p. stake = self. buy_size
self. buy_count = 1
self. order = self. buy( )
elif self. data. close > self. buyprice+ 0.5 * self. ATR[ 0 ] and self. buy_count > 0 and self. buy_count <= 4 :
self. buy_size = self. broker. getvalue( ) * 0.01 / self. ATR
self. buy_size = int ( self. buy_size / 100 ) * 100
self. sizer. p. stake = self. buy_size
self. order = self. buy( )
self. buy_count += 1
elif self. sell_signal < 0 and self. buy_count > 0 :
self. order = self. sell( )
self. buy_count = 0
elif self. data. close < ( self. buyprice - 2 * self. ATR[ 0 ] ) and self. buy_count > 0 :
self. order = self. sell( )
self. buy_count = 0
def log ( self, txt, dt= None , doprint= False ) :
if self. params. printlog or doprint:
dt = dt or self. datas[ 0 ] . datetime. date( 0 )
print ( f' { dt. isoformat( ) } , { txt} ' )
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( f'买入: \n价格: { order. executed. price} , \
成本: { order. executed. value} , \
手续费: { order. executed. comm} ')
self. buyprice = order. executed. price
self. buycomm = order. executed. comm
else :
self. log( f'卖出: \n价格:{ 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( '交易失败' )
self. order = None
def notify_trade ( self, trade) :
if not trade. isclosed:
return
self. log( f'策略收益:\n毛收益 { trade. pnl: .2f } , 净收益 { trade. pnlcomm: .2f } ' )
def stop ( self) :
self. log( f'( 组合线:{ self. p. long_period} , { self. p. short_period} ) ; \
期末总资金: { self. broker. getvalue( ) : . 2f} ', doprint= True )
class TradeSizer ( bt. Sizer) :
params = ( ( 'stake' , 1 ) , )
def _getsizing ( self, comminfo, cash, data, isbuy) :
if isbuy:
return self. p. stake
position = self. broker. getposition( data)
if not position. size:
return 0
else :
return position. size
return self. p. stake
def main ( df, long_list, short_list, best_long, best_short, startcash= 1000000 , com= 0.001 ) :
cerebro = bt. Cerebro( )
if long_list:
cerebro. optstrategy( TurtleStrategy, long_period= long_list, short_period= short_list)
data = bt. feeds. PandasData( dataname= df)
cerebro. adddata( data)
cerebro. broker. setcash( startcash)
cerebro. broker. setcommission( commission= com)
cerebro. addsizer( TradeSizer)
print ( '期初总资金: %.2f' % cerebro. broker. getvalue( ) )
cerebro. run( maxcpus= 1 )
else :
cerebro. addstrategy( TurtleStrategy, long_period= best_long, short_period= best_short)
data = bt. feeds. PandasData( dataname= df)
cerebro. adddata( data)
cerebro. broker. setcash( startcash)
cerebro. broker. setcommission( commission= com)
cerebro. addsizer( TradeSizer)
print ( '期初总资金: %.2f' % cerebro. broker. getvalue( ) )
cerebro. run( )
cerebro. plot( iplot= False )
long_list= range ( 20 , 70 , 5 )
short_list= range ( 5 , 20 , 5 )
main( dataframe, long_list= long_list, short_list= short_list, best_long= None , best_short= None )
期初总资金: 1000000.00
2020-07-17,(组合线:20,5); 期末总资金: 1390120.23
2020-07-17,(组合线:20,10); 期末总资金: 1277599.38
2020-07-17,(组合线:20,15); 期末总资金: 1364670.21
2020-07-17,(组合线:25,5); 期末总资金: 1382881.56
2020-07-17,(组合线:25,10); 期末总资金: 1243270.56
2020-07-17,(组合线:25,15); 期末总资金: 1252566.38
2020-07-17,(组合线:30,5); 期末总资金: 1320310.90
2020-07-17,(组合线:30,10); 期末总资金: 1401899.81
2020-07-17,(组合线:30,15); 期末总资金: 1356889.13
2020-07-17,(组合线:35,5); 期末总资金: 1289133.30
2020-07-17,(组合线:35,10); 期末总资金: 1257212.45
2020-07-17,(组合线:35,15); 期末总资金: 1281212.74
2020-07-17,(组合线:40,5); 期末总资金: 1309812.05
2020-07-17,(组合线:40,10); 期末总资金: 1297384.29
2020-07-17,(组合线:40,15); 期末总资金: 1384260.64
2020-07-17,(组合线:45,5); 期末总资金: 1291239.64
2020-07-17,(组合线:45,10); 期末总资金: 1283014.10
2020-07-17,(组合线:45,15); 期末总资金: 1402853.31
2020-07-17,(组合线:50,5); 期末总资金: 1541612.17
2020-07-17,(组合线:50,10); 期末总资金: 1428670.67
2020-07-17,(组合线:50,15); 期末总资金: 1428341.67
2020-07-17,(组合线:55,5); 期末总资金: 1421742.45
2020-07-17,(组合线:55,10); 期末总资金: 1401355.21
2020-07-17,(组合线:55,15); 期末总资金: 1303585.14
2020-07-17,(组合线:60,5); 期末总资金: 1453711.37
2020-07-17,(组合线:60,10); 期末总资金: 1441592.85
2020-07-17,(组合线:60,15); 期末总资金: 1352939.66
2020-07-17,(组合线:65,5); 期末总资金: 1527890.38
2020-07-17,(组合线:65,10); 期末总资金: 1470179.85
2020-07-17,(组合线:65,15); 期末总资金: 1417375.85
main( dataframe, long_list= None , short_list= None , best_long= 50 , best_short= 5 )
期初总资金: 1000000.00
2020-07-17,(组合线:50,5); 期末总资金: 1541612.17
import requests
import json
import pandas as pd
import datetime as dt
def get_binance_bars ( symbol, interval, startTime, endTime) :
url = "https://api.binance.com/api/v3/klines"
startTime = str ( int ( startTime. timestamp( ) * 1000 ) )
endTime = str ( int ( endTime. timestamp( ) * 1000 ) )
limit = '1000'
req_params = { "symbol" : symbol, 'interval' : interval, 'startTime' : startTime, 'endTime' : endTime, 'limit' : limit}
df = pd. DataFrame( json. loads( requests. get( url, params = req_params) . text) )
if ( len ( df. index) == 0 ) :
return None
df = df. iloc[ : , 0 : 6 ]
df. columns = [ 'datetime' , 'open' , 'high' , 'low' , 'close' , 'volume' ]
df. open = df. open . astype( "float" )
df. high = df. high. astype( "float" )
df. low = df. low. astype( "float" )
df. close = df. close. astype( "float" )
df. volume = df. volume. astype( "float" )
df[ 'adj_close' ] = df[ 'close' ]
df. index = [ dt. datetime. fromtimestamp( x / 1000.0 ) for x in df. datetime]
return df
df_list = [ ]
last_datetime = dt. datetime( 2020 , 9 , 1 )
while True :
new_df = get_binance_bars( 'ETHUSDT' , '1h' , last_datetime, dt. datetime. now( ) )
if new_df is None :
break
df_list. append( new_df)
last_datetime = max ( new_df. index) + dt. timedelta( 0 , 5 )
dataframe= pd. concat( df_list)
dataframe. shape
(15125, 7)
long_list= range ( 20 , 70 , 5 )
short_list= range ( 5 , 20 , 5 )
main( dataframe, long_list= long_list, short_list= short_list, best_long= None , best_short= None )
期初总资金: 1000000.00
2022-05-29,(组合线:20,5); 期末总资金: 896779.29
2022-05-29,(组合线:20,10); 期末总资金: 2637197.56
2022-05-29,(组合线:20,15); 期末总资金: 2367248.05
2022-05-29,(组合线:25,5); 期末总资金: 896309.42
2022-05-29,(组合线:25,10); 期末总资金: 2462265.85
2022-05-29,(组合线:25,15); 期末总资金: 2644703.91
2022-05-29,(组合线:30,5); 期末总资金: 839576.26
2022-05-29,(组合线:30,10); 期末总资金: 2643388.52
2022-05-29,(组合线:30,15); 期末总资金: 3128941.58
2022-05-29,(组合线:35,5); 期末总资金: 816821.35
2022-05-29,(组合线:35,10); 期末总资金: 1999473.33
2022-05-29,(组合线:35,15); 期末总资金: 1786466.61
2022-05-29,(组合线:40,5); 期末总资金: 773224.01
2022-05-29,(组合线:40,10); 期末总资金: 2225776.12
2022-05-29,(组合线:40,15); 期末总资金: 2481419.84
2022-05-29,(组合线:45,5); 期末总资金: 812148.94
2022-05-29,(组合线:45,10); 期末总资金: 2131451.55
2022-05-29,(组合线:45,15); 期末总资金: 2151300.13
2022-05-29,(组合线:50,5); 期末总资金: 810831.18
2022-05-29,(组合线:50,10); 期末总资金: 1846377.21
2022-05-29,(组合线:50,15); 期末总资金: 2128097.56
2022-05-29,(组合线:55,5); 期末总资金: 714907.27
2022-05-29,(组合线:55,10); 期末总资金: 1837638.19
2022-05-29,(组合线:55,15); 期末总资金: 1789916.05
2022-05-29,(组合线:60,5); 期末总资金: 833934.77
2022-05-29,(组合线:60,10); 期末总资金: 1890589.11
2022-05-29,(组合线:60,15); 期末总资金: 1826466.57
2022-05-29,(组合线:65,5); 期末总资金: 781731.77
2022-05-29,(组合线:65,10); 期末总资金: 1656774.05
2022-05-29,(组合线:65,15); 期末总资金: 1447570.31
plt. rcParams[ 'figure.figsize' ] = [ 18 , 16 ]
plt. rcParams[ 'figure.dpi' ] = 200
main( dataframe, long_list= None , short_list= None , best_long= 30 , best_short= 15 )
期初总资金: 1000000.00
2022-05-29,(组合线:30,15); 期末总资金: 3128941.58