首页 > 解决方案 > 在 backtrader 上运行回测,我收到了许多订单取消/保证金/拒绝,而他们不应该这样做

问题描述

许多买单在回测中被取消,我找不到原因。查看 Writefile 日志,似乎购买订单是在第二天收盘时创建的。在大多数情况下,买入价在当天的范围内,但仍未执行。

我尝试了不同的资产、不同大小的数据源、不同的策略,但结果相同。

在 Jupyter 笔记本上运行。我包括代码和日志。

最后,我将 AllInSizerInt() 中的默认参数 ('100') 更改为低于 100 并且它起作用了。我真的不明白为什么,我以为sizer会从经纪人那里得到现金头寸并调整订单。

这是修复:

''''
python
#Add the sizer.  We plan to go 'all in' every time  
cerebro.addsizer(bt.sizers.AllInSizerInt, percents = 95)  

''''

这是原始代码:

''''
python
#### Import databases
from datetime import datetime
import backtrader as bt
import backtrader.indicators as btind

abspath = '/mypath/'
logfile = 'abs_momentum.csv'
# Create a Strategy

class Abs_momentum(bt.Strategy):
    alias = ('Abs_momentum',)
    params = (
    # period for momentum calculation
    ('lookback', 252),
    ('range', 100))
    

def __init__(self):
    # keep track of close price in the series
    self.data_close = self.datas[0].close

    # keep track of pending orders/buy price/buy commission
    self.order  = None
    self.price  = None
    self.comm   = None

    # add a momentum indicator
    self.mom    = btind.MomentumOsc(self.data_close, \
                                    period = self.p.lookback, \
                                    band = self.p.range)

    self.buysig = self.mom
    
    
def log(self, txt, dt=None):
    ''' Logging function fot this strategy'''
    dt = dt or self.datas[0].datetime.date(0)
    print('%s, %s' % (dt.isoformat(), txt))


def notify_order(self, order):
    if order.status in [order.Submitted, order.Accepted]:
        # Buy/Sell order submitted/accepted to/by broker - Nothing to do
        return

    # Check if an order has been completed
    # Attention: broker could reject order if not enough cash
    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:  # Sell
            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 notify_trade(self, trade):
    if not trade.isclosed:
        return

    self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
             (trade.pnl, trade.pnlcomm))
def next(self):
    
    # do nothing if an order is pending
    if self.order:
        return

    # check if there is already a position
    if not self.position:
        # buy condition
        if self.buysig > 100:
            self.log(f'BUY CREATED --- Price: {self.data_close[0]:.2f}')
            self.order = self.buy(size = None)
    else:
        # sell condition
        if self.buysig < 100:
            self.log(f'SELL CREATED --- Price: {self.data_close[0]:.2f}')
            self.order = self.sell(Size = None)
###
#### Download data from Yahoo Finance
data = bt.feeds.YahooFinanceData(dataname= 'SPY', \
                             fromdate=datetime(2018, 6, 15),\
                             todate=datetime(2021, 3, 17),\
                               reverse = False)
####

# create a Cerebro entity
cerebro = bt.Cerebro(stdstats = False)

### Set up the backtest
# Add the Data Feed to Cerebro
cerebro.adddata(data)

# Set our desired cash start
cerebro.broker.setcash(100000.0)

# Set the commission - 0.1% ... divide by 100 to remove the %
cerebro.broker.setcommission(commission=0.001)

#Add the sizer.  We plan to go 'all in' every time
cerebro.addsizer(bt.sizers.AllInSizerInt)

#Add the strategy
cerebro.addstrategy(Abs_momentum)

#Add the observers to the plot
cerebro.addobserver(bt.observers.BuySell)
cerebro.addobserver(bt.observers.Value)

#Write to a csv file
cerebro.addwriter(bt.WriterFile, out = (abspath + logfile), csv=True,\ data(csv) = False)

# Print out the starting conditions
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())

# Run over everything
cerebro.run()

# Print out the final result
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
###

### Plot the results
cerebro.plot(iplot=True, volume=False)
###

''''

这是日志:

>Starting Portfolio Value: 100000.00
>2019-06-18, BUY CREATED --- Price: 282.95
>2019-06-19, Order Canceled/Margin/Rejected
>2019-06-19, BUY CREATED --- Price: 283.58
>2019-06-20, Order Canceled/Margin/Rejected
>2019-06-20, BUY CREATED --- Price: 286.29
>2019-06-21, Order Canceled/Margin/Rejected
>2019-06-21, BUY CREATED --- Price: 285.88
>2019-06-24, BUY EXECUTED, Price: 286.10, Cost: 99848.90, Comm 99.85
>2020-03-12, SELL CREATED --- Price: 243.56
>2020-03-13, SELL EXECUTED, Price: 258.27, Cost: 99848.90, Comm 90.14
>2020-03-13, OPERATION PROFIT, GROSS -9712.67, NET -9902.66
>2020-04-17, BUY CREATED --- Price: 283.04
>2020-04-20, BUY EXECUTED, Price: 279.06, Cost: 88741.08, Comm 88.74
>2020-04-20, SELL CREATED --- Price: 278.05
>2020-04-21, SELL EXECUTED, Price: 273.25, Cost: 88741.08, Comm 86.89
>2020-04-21, OPERATION PROFIT, GROSS -1847.58, NET -2023.21
>2020-04-29, BUY CREATED --- Price: 289.53
>2020-04-30, Order Canceled/Margin/Rejected
>2020-04-30, BUY CREATED --- Price: 286.83
>2020-05-01, Order Canceled/Margin/Rejected
>2020-05-06, BUY CREATED --- Price: 280.68
>2020-05-07, Order Canceled/Margin/Rejected
>2020-05-07, BUY CREATED --- Price: 284.07
>2020-05-08, Order Canceled/Margin/Rejected
>2020-05-08, BUY CREATED --- Price: 288.77
>2020-05-11, BUY EXECUTED, Price: 286.69, Cost: 87153.76, Comm 87.15
>Final Portfolio Value: 121189.86

标签: pythonjupyter-notebookback-testingbacktrader

解决方案


您从 100% 规模交易调整到 95% 规模交易纠正问题的原因是,在投资组合中尝试全部 100% 总是会在此过程中产生一些利润。这可以在您的日志中看到:

>2019-06-19, Order Canceled/Margin/Rejected

问题是您正在计算与前一收盘价交易的股票数量。如果市场价格在下一根柱线上跳空,您将没有足够的现金购买。有一个称为cheat-on-open允许在下一个开盘价达到峰值以允许调整股票规模的选项。这有帮助。

但实际上,最好在 100% 以下进行交易。


推荐阅读