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
import pyfolio as pf
import optunity
import optunity.metrics
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False
plt.rcParams['figure.figsize']=[10, 8]
plt.rcParams['figure.dpi']=200
plt.rcParams['figure.facecolor']='w'
plt.rcParams['figure.edgecolor']='k'
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 = '50'
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.index = [dt.datetime.fromtimestamp(x / 1000.0) for x in df.datetime]
return df
df_list = []
last_datetime = dt.datetime(2021,6,1)
while True:
new_df = get_binance_bars('ETHUSDT', '4h', last_datetime, dt.datetime(2022,7,15))
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['openinterest']=0
dataframe=dataframe[['open','high','low','close','volume','openinterest']]
print(dataframe.shape)
print(dataframe.tail())
dataframe.head()
(2361, 6)
open high low close volume \
2022-07-14 08:00:00 1115.01 1127.19 1109.00 1112.71 200647.5134
2022-07-14 12:00:00 1112.72 1114.90 1093.30 1097.58 161471.2202
2022-07-14 16:00:00 1097.57 1100.71 1072.11 1081.27 196371.8355
2022-07-14 20:00:00 1081.26 1147.97 1072.80 1140.95 403737.3239
2022-07-15 00:00:00 1140.96 1214.65 1133.14 1194.04 490261.6674
openinterest
2022-07-14 08:00:00 0
2022-07-14 12:00:00 0
2022-07-14 16:00:00 0
2022-07-14 20:00:00 0
2022-07-15 00:00:00 0
|
open |
high |
low |
close |
volume |
openinterest |
2021-06-01 00:00:00 |
2637.47 |
2677.70 |
2572.65 |
2627.20 |
192289.89830 |
0 |
2021-06-01 04:00:00 |
2627.20 |
2720.00 |
2593.20 |
2706.15 |
142944.38327 |
0 |
2021-06-01 08:00:00 |
2706.15 |
2740.00 |
2612.63 |
2630.79 |
190770.24350 |
0 |
2021-06-01 12:00:00 |
2630.66 |
2716.33 |
2615.27 |
2652.53 |
166661.30183 |
0 |
2021-06-01 16:00:00 |
2652.37 |
2672.60 |
2547.12 |
2624.09 |
263021.10974 |
0 |
class three_moving_average(bt.Strategy):
params = dict(
short_period=5,
median_period=20,
long_period=60,
printlog=False)
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 __init__(self):
self.order = None
self.close = self.datas[0].close
self.s_ma = bt.ind.SMA(period=int(self.p.short_period))
self.m_ma = bt.ind.SMA(period=int(self.p.median_period))
self.l_ma = bt.ind.SMA(period=int(self.p.long_period))
self.signal1 = bt.And(self.m_ma>self.l_ma, self.s_ma>self.m_ma)
self.long_signal = bt.If((self.signal1-self.signal1(-1))>0, 1, 0)
self.close_long_signal = bt.ind.CrossDown(self.s_ma, self.m_ma)
self.signal2 = bt.And(self.m_ma<self.l_ma, self.s_ma<self.m_ma)
self.short_signal = bt.If((self.signal2-self.signal2(-1))>0, 1, 0)
self.close_short_signal = bt.ind.CrossUp(self.s_ma, self.m_ma)
def next(self):
if self.position.size>0:
if self.close_long_signal ==1:
self.order = self.sell(size=abs(self.position.size))
elif self.position.size < 0 :
if self.close_short_signal ==1:
self.order = self.buy(size=abs(self.position.size))
else:
if self.long_signal ==1 :
self.buy_unit = int(self.broker.getvalue()/self.close[0]/4)
self.order = self.buy(size=self.buy_unit)
elif self.short_signal==1:
self.sell_unit = int(self.broker.getvalue()/self.close[0]/4)
self.order = self.sell(size=self.sell_unit)
def notify_order(self, order):
order_status = ['Created','Submitted','Accepted','Partial',
'Completed','Canceled','Expired','Margin','Rejected']
if order.status in [order.Submitted, order.Accepted]:
self.log('ref:%.0f, name: %s, Order: %s'% (order.ref,
order.data._name,
order_status[order.status]))
return
if order.status in [order.Partial, order.Completed]:
if order.isbuy():
self.log(
'BUY EXECUTED, status: %s, ref:%.0f, name: %s, Size: %.2f, Price: %.2f, Cost: %.2f, Comm %.2f' %
(order_status[order.status],
order.ref,
order.data._name,
order.executed.size,
order.executed.price,
order.executed.value,
order.executed.comm))
else:
self.log('SELL EXECUTED, status: %s, ref:%.0f, name: %s, Size: %.2f, Price: %.2f, Cost: %.2f, Comm %.2f' %
(order_status[order.status],
order.ref,
order.data._name,
order.executed.size,
order.executed.price,
order.executed.value,
order.executed.comm))
elif order.status in [order.Canceled, order.Margin, order.Rejected, order.Expired]:
self.log('ref:%.0f, name: %s, status: %s'% (
order.ref, order.data._name, order_status[order.status]))
self.order = None
def stop(self):
self.log(f'(组合线:{self.p.short_period},{self.p.median_period},{self.p.long_period}); 期末总资金: {self.broker.getvalue():.2f}', doprint=False)
def main(short_period,median_period,long_period,para_opt=True,startcash=100000,com=0.0005,printlog=False):
if para_opt==True:
cerebro = bt.Cerebro()
cerebro.addstrategy(three_moving_average,short_period=short_period,
median_period=median_period,long_period=long_period,printlog=printlog)
data = bt.feeds.PandasData(dataname=dataframe)
cerebro.adddata(data)
cerebro.broker.setcash(startcash)
cerebro.broker.setcommission(commission=com)
cerebro.run(maxcpus=2)
value = cerebro.broker.getvalue()
return value
else:
cerebro = bt.Cerebro()
cerebro.addstrategy(three_moving_average,short_period=short_period,
median_period=median_period,long_period=long_period,printlog=printlog)
data = bt.feeds.PandasData(dataname=dataframe)
cerebro.adddata(data)
cerebro.broker.setcash(startcash)
cerebro.broker.setcommission(commission=com)
cerebro.addanalyzer(bt.analyzers.PyFolio, _name='pyfolio')
print('期初总资金: %.2f' % cerebro.broker.getvalue())
results=cerebro.run(maxcpus=2)
print('期末总资金: %.2f' % cerebro.broker.getvalue())
cerebro.plot(iplot=False)
'''backtrader内置的策略参数优化方法是权利搜索方法,也就是遍历每个参数组合值。在参数很多,每个参数取值变化范围大的情况下,优化效率是很低的。
可以采用智能优化算法,比如粒子群优化等进行大规模参数优化。下面,我们用python开源算法库optunity来对backtrader策略参数进行优化。 '''
'''
执行10次回测,设置两个参数sma1、sma2的取值范围
num_evals: 执行次数
Available solvers: particle swarm, tpe, sobol, nelder-mead, random search, cma-es, grid search
sma1、sma2 确定参数的取值范围
'''
opt = optunity.maximize(
f=main,
num_evals=100,
solver_name='particle swarm',
short_period=[5,30],
median_period=[30,60],
long_period=[60,100]
)
optimal_pars, details, _ = opt
print(optimal_pars)
{'short_period': 11.27713202190711, 'median_period': 57.050084350679064, 'long_period': 72.96828600731862}
main(short_period=optimal_pars['short_period'],median_period=optimal_pars['median_period'],long_period=optimal_pars['long_period'],
para_opt=False,com=0.0005,printlog=False)
期初总资金: 100000.00
期末总资金: 137124.16