[英]Running backtest on backtrader I get many BUY ORDERS CANCELLED/MARGIN/REJECTED when they should not
Many buy orders are cancelled in the backtests and I cannot find why.许多买单在回测中被取消,我找不到原因。 Looking at the Writefile log it seems the buy orders are created at the next day's close.
查看 Writefile 日志,似乎购买订单是在第二天收盘时创建的。 In most cases the buy price is in the day's range but still not executed.
在大多数情况下,买入价在当天的范围内,但仍未执行。
I have tried on different assets, different sizes of the data feed, different strategies, with the same result.我尝试了不同的资产、不同大小的数据源、不同的策略,但结果相同。
Running on Jupyter notebook.在 Jupyter 笔记本上运行。 I include the code and the log.
我包括代码和日志。
Finally I changed the default parameter ('100') in AllInSizerInt() below 100 and it worked.最后,我将 AllInSizerInt() 中的默认参数 ('100') 更改为低于 100 并且它起作用了。 I do not really understand why, I thought the sizer would get the cash position from the broker and adjust the order.
我真的不明白为什么,我以为sizer会从经纪人那里得到现金position并调整订单。
Here is the fix:这是修复:
''''
python
#Add the sizer. We plan to go 'all in' every time
cerebro.addsizer(bt.sizers.AllInSizerInt, percents = 95)
''''
And here is the original code:这是原始代码:
''''
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)
###
''''
Here is the log:这是日志:
>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
The reason your adjustment from 100% size trade to 95% size trade corrected the problem is that trying to by all in 100% on a portfolio will always result in some margins along the way.您从 100% 规模交易调整到 95% 规模交易纠正了问题的原因是,在投资组合中尝试全部 100% 总是会在此过程中产生一些利润。 This can be seen here in your logs:
这可以在您的日志中看到:
>2019-06-19, Order Canceled/Margin/Rejected
The problem is you are calculating the number of shares to trade off the previous close.问题是您正在计算与前一收盘价交易的股票数量。 If the market price gaps up on the next bar, you won't have enough cash to buy.
如果市场价格在下一根柱线上跳空,您将没有足够的现金购买。 There is an option called
cheat-on-open
that allows a peak at the next open price to permit sizing of the shares.有一种称为
cheat-on-open
的选项,它允许在下一个开盘价达到峰值以允许调整股票规模。 This helps.这有帮助。
But in reality, it's best to trade below 100%.但实际上,最好在 100% 以下进行交易。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.