Order executed price mismatch!
-
When I try the sample multidata-strategy.py from the source distribution of backtrader under the samples/multidata-strategy directory, an Order executed price mismatching phenomenon happens.
As such:
on 2003-02-11:
BUY CREATE , 9.14 => yahoo close 9.145000 --- OK
on 2003-02-12:
BUY COMPLETE, 10.51 => orcl no data match ?
2003-02-12,11.820000,11.950000,11.490000,11.500000,10.229136,39650300
2003-02-12T23:59:59.999989, BUY COMPLETE, 9.10 => yahoo open 9.100000 ---OKIt seems that, when it goes for Data0-orcl, the Order executed price matching will always go wrong!
Some details:
(backtrader37) D:\Python\BackTrader\Test>python ./multidata-strategy.py --plot ... Self len: 28 Data0 len: 28 Data1 len: 28 Data0 len == Data1 len: True Data0 dt: 2003-02-11 23:59:59.999989 Data1 dt: 2003-02-11 23:59:59.999989 2003-02-11T23:59:59.999989, BUY CREATE , 9.14 2003-02-12T23:59:59.999989, BUY COMPLETE, 10.51 2003-02-12T23:59:59.999989, BUY COMPLETE, 9.10 Self len: 29 Data0 len: 29 Data1 len: 29 Data0 len == Data1 len: True Data0 dt: 2003-02-12 23:59:59.999989 Data1 dt: 2003-02-12 23:59:59.999989 2003-02-12T23:59:59.999989, SELL CREATE , 9.09 2003-02-13T23:59:59.999989, SELL COMPLETE, 10.28 2003-02-13T23:59:59.999989, SELL COMPLETE, 9.35 ...
And the corresponding data feeds:
Data0 - orcl-1995-2014.txt,Date,Open,High,Low,Close,Adj Close,Volume ... 2003-02-10,11.410000,11.800000,11.380000,11.750000,10.451510,33763500 2003-02-11,11.840000,12.140000,11.720000,11.910000,10.593827,46126300 2003-02-12,11.820000,11.950000,11.490000,11.500000,10.229136,39650300 2003-02-13,11.560000,11.690000,11.350000,11.540000,10.264715,34557500 2003-02-14,11.560000,11.920000,11.350000,11.700000,10.407034,41473100 ...
Data1 - yhoo-1996-2014.txt,
Date,Open,High,Low,Close,Adj Close,Volume ... 2003-02-10,8.785000,8.985000,8.730000,8.960000,8.960000,10486400 2003-02-11,9.000000,9.245000,8.990000,9.145000,9.145000,14895800 2003-02-12,9.100000,9.300000,9.000000,9.085000,9.085000,12368000 2003-02-13,9.345000,9.365000,8.780000,9.025000,9.025000,26697800 2003-02-14,9.155000,9.455000,9.045000,9.450000,9.450000,15901600 ...
Appendix:
multidata-strategy.py#!/usr/bin/env python # -*- coding: utf-8; py-indent-offset:4 -*- ############################################################################### # # Copyright (C) 2015, 2016 Daniel Rodriguez # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # ############################################################################### from __future__ import (absolute_import, division, print_function, unicode_literals) import argparse import datetime # The above could be sent to an independent module import backtrader as bt import backtrader.feeds as btfeeds import backtrader.indicators as btind class MultiDataStrategy(bt.Strategy): ''' This strategy operates on 2 datas. The expectation is that the 2 datas are correlated and the 2nd data is used to generate signals on the 1st - Buy/Sell Operationss will be executed on the 1st data - The signals are generated using a Simple Moving Average on the 2nd data when the close price crosses upwwards/downwards The strategy is a long-only strategy ''' params = dict( period=15, stake=10, printout=True, ) def log(self, txt, dt=None): if self.p.printout: dt = dt or self.data.datetime[0] dt = bt.num2date(dt) print('%s, %s' % (dt.isoformat(), txt)) def notify_order(self, order): if order.status in [bt.Order.Submitted, bt.Order.Accepted]: return # Await further notifications if order.status == order.Completed: if order.isbuy(): buytxt = 'BUY COMPLETE, %.2f' % order.executed.price self.log(buytxt, order.executed.dt) else: selltxt = 'SELL COMPLETE, %.2f' % order.executed.price self.log(selltxt, order.executed.dt) elif order.status in [order.Expired, order.Canceled, order.Margin]: self.log('%s ,' % order.Status[order.status]) pass # Simply log # Allow new orders self.orderid = None def __init__(self): # To control operation entries self.orderid = None # Create SMA on 2nd data sma = btind.MovAv.SMA(self.data1, period=self.p.period) # Create a CrossOver Signal from close an moving average self.signal = btind.CrossOver(self.data1.close, sma) def next(self): if self.orderid: return # if an order is active, no new orders are allowed if self.p.printout: print('Self len:', len(self)) print('Data0 len:', len(self.data0)) print('Data1 len:', len(self.data1)) print('Data0 len == Data1 len:', len(self.data0) == len(self.data1)) print('Data0 dt:', self.data0.datetime.datetime()) print('Data1 dt:', self.data1.datetime.datetime()) if not self.position: # not yet in market if self.signal > 0.0: # cross upwards self.log('BUY CREATE , %.2f' % self.data1.close[0]) self.buy(size=self.p.stake) self.buy(data=self.data1, size=self.p.stake) else: # in the market if self.signal < 0.0: # crosss downwards self.log('SELL CREATE , %.2f' % self.data1.close[0]) self.sell(size=self.p.stake) self.sell(data=self.data1, size=self.p.stake) def stop(self): print('==================================================') print('Starting Value - %.2f' % self.broker.startingcash) print('Ending Value - %.2f' % self.broker.getvalue()) print('==================================================') def runstrategy(): args = parse_args() # Create a cerebro cerebro = bt.Cerebro() # Get the dates from the args fromdate = datetime.datetime.strptime(args.fromdate, '%Y-%m-%d') todate = datetime.datetime.strptime(args.todate, '%Y-%m-%d') # Create the 1st data data0 = btfeeds.YahooFinanceCSVData( dataname=args.data0, fromdate=fromdate, todate=todate) # Add the 1st data to cerebro cerebro.adddata(data0) # Create the 2nd data data1 = btfeeds.YahooFinanceCSVData( dataname=args.data1, fromdate=fromdate, todate=todate) # Add the 2nd data to cerebro cerebro.adddata(data1) # Add the strategy cerebro.addstrategy(MultiDataStrategy, period=args.period, stake=args.stake) # Add the commission - only stocks like a for each operation cerebro.broker.setcash(args.cash) # Add the commission - only stocks like a for each operation cerebro.broker.setcommission(commission=args.commperc) # And run it cerebro.run(runonce=not args.runnext, preload=not args.nopreload, oldsync=args.oldsync) # Plot if requested if args.plot: cerebro.plot(numfigs=args.numfigs, volume=False, zdown=False) def parse_args(): parser = argparse.ArgumentParser(description='MultiData Strategy') parser.add_argument('--data0', '-d0', default='../../datas/orcl-1995-2014.txt', help='1st data into the system') parser.add_argument('--data1', '-d1', default='../../datas/yhoo-1996-2014.txt', help='2nd data into the system') parser.add_argument('--fromdate', '-f', default='2003-01-01', help='Starting date in YYYY-MM-DD format') parser.add_argument('--todate', '-t', default='2005-12-31', help='Starting date in YYYY-MM-DD format') parser.add_argument('--period', default=15, type=int, help='Period to apply to the Simple Moving Average') parser.add_argument('--cash', default=100000, type=int, help='Starting Cash') parser.add_argument('--runnext', action='store_true', help='Use next by next instead of runonce') parser.add_argument('--nopreload', action='store_true', help='Do not preload the data') parser.add_argument('--oldsync', action='store_true', help='Use old data synchronization method') parser.add_argument('--commperc', default=0.005, type=float, help='Percentage commission (0.005 is 0.5%%') parser.add_argument('--stake', default=10, type=int, help='Stake to apply in each operation') parser.add_argument('--plot', '-p', action='store_true', help='Plot the read data') parser.add_argument('--numfigs', '-n', default=1, help='Plot using numfigs figures') return parser.parse_args() if __name__ == '__main__': runstrategy()
-
Check the next bar prices, they will match.
For example you place order on 1st, you see price "x", however, the order will execute on the next daily bar with its prices. -
There are two data feeds, ocrl and yhoo, the 1st day orders are created on both datas, when the 2nd day, the orders are completed. However, the executed price for yhoo, is right, exactly the open price of the next daily bar, but the executed price for ocrl, I can not find out where does it come from... CAN NOT MATCH PRICES FROM ANY DAY!!!
-
YahooFinanceCSVData
data feed provides adjusted prices tobt
with the default settings. For ORCL on 2/12/2003 it will be the following price:10.229 / 11.5 * 11.82 = 10.514
which is what
bt
logged. -
Yes, that's right now~