基于backtrader的唐奇安结合ADX策略实现(自动多参数调优和回测)

基于backtrader的唐奇安结合ADX策略实现(自动多参数调优和回测)


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(2022,1,1)
while True:
    new_df = get_binance_bars('ETHUSDT', '4h', last_datetime, dt.datetime(2022,7,10))
    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()
(1099, 6)
                        open     high      low    close       volume  \
2022-07-09 08:00:00  1214.03  1221.79  1204.67  1217.01  147773.1735
2022-07-09 12:00:00  1217.01  1227.40  1210.69  1225.02   79680.3095
2022-07-09 16:00:00  1225.01  1228.25  1205.27  1210.28   80972.9123
2022-07-09 20:00:00  1210.28  1222.61  1206.52  1215.24   83406.7132
2022-07-10 00:00:00  1215.23  1235.40  1209.33  1218.61   89320.9760

                     openinterest
2022-07-09 08:00:00             0
2022-07-09 12:00:00             0
2022-07-09 16:00:00             0
2022-07-09 20:00:00             0
2022-07-10 00:00:00             0

openhighlowclosevolumeopeninterest2022-01-01 00:00:003784.643788.453623.003626.2754604.902702022-01-01 04:00:003626.213712.503622.293676.2339496.599302022-01-01 08:00:003676.223748.453676.223723.9626592.769302022-01-01 12:00:003723.963765.273701.003715.3127251.140102022-01-01 16:00:003715.323733.843673.463693.3723727.99490


class D_ADX_TradingStrategy(bt.Strategy):
    params = dict(
        N1= 40,
        N2=30,
        N3=30,
        N4=20,
        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.buy_count = 0
        self.last_price = 0
        self.close = self.datas[0].close
        self.high = self.datas[0].high
        self.low = self.datas[0].low

        self.DonchianH = bt.ind.Highest(self.high(-1), period=int(self.p.N1), subplot=True)

        self.DonchianL = bt.ind.Lowest(self.low(-1), period=int(self.p.N2), subplot=True)

        self.DonchianM= (self.DonchianH+self.DonchianL)/2

        self.CrossoverH = bt.ind.CrossOver(self.close(0), self.DonchianH, subplot=False)

        self.CrossoverL = bt.ind.CrossOver(self.close(0), self.DonchianL, subplot=False)

        self.CrossoverM = bt.ind.CrossOver(self.close(0), self.DonchianM, subplot=False)

        self.ADX = bt.talib.ADX(self.high, self.low, self.close,timeperiod=int(self.p.N4),subplot=True)

    def next(self):

        if self.order:
            return

        if self.position.size > 0 :

            if self.CrossoverM<0 or self.ADX[0]<self.ADX[-1]:
                self.order = self.sell(size=abs(self.position.size))
                self.buy_count = 0

        elif self.position.size < 0 :

            if self.CrossoverM>0 or self.ADX[0]<self.ADX[-1]:
                self.order = self.buy(size=abs(self.position.size))
                self.buy_count = 0

        else:

            if self.CrossoverH > 0 and self.buy_count == 0 and self.ADX[0]>self.ADX[-1] and self.ADX[-1]>int(self.p.N3):
                self.buy_unit = int(self.broker.getvalue()/self.close[0]/4)

                self.order = self.buy(size=self.buy_unit)
                self.last_price = self.position.price
                self.buy_count = 1

            elif self.CrossoverL < 0 and self.buy_count == 0 and self.ADX[0]>self.ADX[-1] and self.ADX[-1]>int(self.p.N3):
                self.buy_unit = int(self.broker.getvalue()/self.close[0]/4)
                self.buy_unit=500
                self.order = self.sell(size=self.buy_unit)
                self.last_price = self.position.price
                self.buy_count = 1

    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 notify_trade(self, trade):

        if trade.justopened:
            self.log('Trade Opened, name: %s, Size: %.2f,Price: %.2f' % (
                    trade.getdataname(), trade.size, trade.price))

        elif trade.isclosed:
            self.log('Trade Closed, name: %s, GROSS %.2f, NET %.2f, Comm %.2f' %(
            trade.getdataname(), trade.pnl, trade.pnlcomm, trade.commission))

        else:
            self.log('Trade Updated, name: %s, Size: %.2f,Price: %.2f' % (
                    trade.getdataname(), trade.size, trade.price))
    def stop(self):

        self.log(f'(组合线:{self.p.N1},{self.p.N2},{self.p.N3},{self.p.N4}); 期末总资金: {self.broker.getvalue():.2f}', doprint=False)


def main(N1,N2,N3,N4,para_opt=True,startcash=100000,com=0.02):
    if para_opt==True:
        cerebro = bt.Cerebro()
        cerebro.addstrategy(D_ADX_TradingStrategy,N1=N1,N2=N2,N3=N3,N4=N4)
        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(D_ADX_TradingStrategy,N1=N1,N2=N2,N3=N3,N4=N4)
        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)
        cerebro.plot(iplot=False)
        result = results[0]
        pyfolio = result.analyzers.pyfolio

        returns, positions, transactions, gross_lev = pyfolio.get_pf_items()
        pf.create_full_tear_sheet(returns)

'''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',
    N1=[40,100],
    N2=[40,100],
    N3=[20,50],
    N4=[8,20]
    )

optimal_pars, details, _ = opt
print(optimal_pars)
{'N1': 76.64719168590987, 'N2': 83.65052762407552, 'N3': 25.247355935345496, 'N4': 15.344248021285086}

main(N1=optimal_pars['N1'],N2=optimal_pars['N2'],N3=optimal_pars['N3'],N4=optimal_pars['N4'],para_opt=False)
&#x671F;&#x521D;&#x603B;&#x8D44;&#x91D1;: 100000.00

基于backtrader的唐奇安结合ADX策略实现(自动多参数调优和回测)

Start date2022-01-01End date2022-07-10Total months9BacktestAnnual return276.335%Cumulative returns173.054%Annual volatility3386.631%Sharpe ratio-0.12Calmar ratio2.35StabilityNaNMax drawdown-117.486%Omega ratio0.90Sortino ratio-0.15Skew-3.82Kurtosis92.83Tail ratio0.60Daily value at risk-428.304% Worst drawdown periodsNet drawdown in %Peak dateValley dateRecovery dateDuration0117.492022-01-222022-05-042022-06-13101125.222022-01-202022-01-212022-01-222223.402022-06-152022-06-19NaTNaN33.432022-06-132022-06-142022-06-1534NaNNaTNaTNaTNaN

基于backtrader的唐奇安结合ADX策略实现(自动多参数调优和回测)

Original: https://blog.csdn.net/jsyzliuyu/article/details/125717295
Author: 飘逸高铁侠
Title: 基于backtrader的唐奇安结合ADX策略实现(自动多参数调优和回测)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/678341/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球