For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

Incorrect datetime execution or time zone handlind



  • I noticed that accepted orders and executed orders differ in time for more than 6 hours.

    Could you please tell me what can be wrong with my example?
    Backtrader version is the latest, installed yesterday.
    Datasample is 2006-min-005.txt downloaded from here

    from __future__ import (absolute_import, division, print_function, )
    #                        unicode_literals)
    
    import argparse
    import datetime
    
    import backtrader as bt
    import backtrader.feeds as btfeeds
    import pytz
    
    mytimezone = pytz.timezone('US/Eastern')
    mytimezone2 = pytz.timezone('Europe/Kiev')
    
    
    class St(bt.Strategy):
        def __init__(self):
            self.curdate = datetime.date.min
            self.last_order_status_name = 'None'
            self.last_position_size = 0
            self.elapsed = 0
            self.order = None
    
        def notify_order(self, order):
            curdtstr = self.data.datetime.datetime().strftime('%a %Y-%m-%d %H:%M:%S')
            if order.status in [order.Completed] or True:
    
                if order.isbuy():
                    if order.executed.dt is not None:
                        dtstr = bt.num2date(order.executed.dt).strftime('%a %Y-%m-%d %H:%M:%S')
                        print('{}: BUY  EXECUTED, on: {}, dt {}'.format(curdtstr, dtstr, order.executed.dt))
                        self.order = None
                    else:
                        dtstr = bt.num2date(order.created.dt).strftime('%a %Y-%m-%d %H:%M:%S')
                        print('{}: BUY {}, on: {}, dt {}'.format(curdtstr, order.getstatusname(), dtstr, order.created.dt))
                else:
                    if order.executed.dt is not None:  # Sell
                        dtstr = bt.num2date(order.executed.dt).strftime('%a %Y-%m-%d %H:%M:%S')
                        print('{}: SELL  EXECUTED, on: {}, dt {}'.format(curdtstr, dtstr, order.executed.dt))
    
        def next(self):
            curdate = self.data.datetime.datetime()
            if self.curdate == datetime.date.min:
                self.curdate = curdate
                return
    
            if curdate.weekday() != 0:
                self.elapsed = 0
                return
    
            is_order_or_position_changed = self.last_position_size != self.position.size
            if not is_order_or_position_changed and self.order:
                is_order_or_position_changed = self.order.getstatusname() != self.last_order_status_name
    
            dtstr = self.data.datetime.datetime().strftime('%a %Y-%m-%d %H:%M:%S')
            difference: datetime = curdate - self.curdate
            seconds = difference.total_seconds()
            hours = seconds // 3600
            minutes = (seconds % 3600) // 60
            seconds = seconds % 60
            # if curdate > self.curdate:
            #     self.elapsed += 1
            #     self.curdate = curdate
            if minutes:
                self.elapsed += minutes
    
            order_status = 'None'
            if self.order:
                order_status = self.order.getstatusname()
    
            if is_order_or_position_changed:
                print('{}: elapsedh: {:.2f}, current hour: {}, self.order.status {}, position {}'.format(dtstr,
                                                                                                         self.elapsed / 60,
                                                                                                         curdate.hour,
                                                                                                         order_status,
                                                                                                         self.position.size))
    
            dtstr = self.data.datetime.datetime().strftime('%a %Y-%m-%d %H:%M:%S')
            if self.position.size > 0 and self.order is None and int(self.elapsed / 60) > 6:
                print('%s: SELL CREATED' % dtstr)
                self.close(exectype=bt.Order.Close)
                self.elapsed = 0
            elif self.order is None and curdate.hour < 12 and self.position.size == 0:  # no pending order
                print('%s: BUY  CREATED' % dtstr)
                self.order = self.buy(exectype=bt.Order.Close)
                self.elapsed = 0
            self.curdate = curdate
            self.last_position_size = self.position.size
            if self.order:
                self.last_order_status_name = self.order.getstatusname()
            else:
                self.last_order_status_name = 'None'
    
    
    def runstrat():
        args = parse_args()
    
        cerebro = bt.Cerebro()
        data = getdata(args)
        cerebro.resampledata(data, timeframe=bt.TimeFrame.Minutes, compression=30)
    
        cerebro.addstrategy(St)
        if args.eosbar:
            cerebro.broker.seteosbar(True)
    
        cerebro.run()
        cerebro.plot(style='candlestick', volume=False)
    
    
    def getdata(args):
        dataformat = dict(
            bt=btfeeds.BacktraderCSVData,
            visualchart=btfeeds.VChartCSVData,
            sierrachart=btfeeds.SierraChartCSVData,
            yahoo=btfeeds.YahooFinanceCSVData,
            yahoo_unreversed=btfeeds.YahooFinanceCSVData,
            tz=mytimezone
        )
    
        dfkwargs = dict()
        if args.csvformat == 'yahoo_unreversed':
            dfkwargs['reverse'] = True
    
        if args.fromdate:
            fromdate = datetime.datetime.strptime(args.fromdate, '%Y-%m-%d')
            dfkwargs['fromdate'] = fromdate
    
        if args.todate:
            todate = datetime.datetime.strptime(args.todate, '%Y-%m-%d')
            dfkwargs['todate'] = todate
    
        if args.tend is not None:
            # internally only the "time" part is used
            dfkwargs['sessionend'] = datetime.datetime.strptime(args.tend, '%H:%M')
    
        dfkwargs['dataname'] = args.infile
        dfcls = dataformat[args.csvformat]
    
        data = dfcls(**dfkwargs)
    
        return data
    
    
    def parse_args():
        parser = argparse.ArgumentParser(
            formatter_class=argparse.ArgumentDefaultsHelpFormatter,
            description='Sample for Close Orders with daily data')
    
        parser.add_argument('--infile', '-i', required=False,
                            default='2006-min-005.txt',
                            help='File to be read in')
    
        parser.add_argument('--csvformat', '-c', required=False, default='bt',
                            choices=['bt', 'visualchart', 'sierrachart',
                                     'yahoo', 'yahoo_unreversed'],
                            help='CSV Format')
    
        parser.add_argument('--fromdate', '-f', required=False, default=None,
                            help='Starting date in YYYY-MM-DD format')
    
        parser.add_argument('--todate', '-t', required=False, default=None,
                            help='Ending date in YYYY-MM-DD format')
    
        parser.add_argument('--eosbar', required=False, action='store_true',
                            help=('Consider a bar with the end of session time to'
                                  'be the end of the session'))
    
        parser.add_argument('--tend', '-te',
                            default=None, required=False,
                            help='End time for the Session Filter (HH:MM)')
    
        return parser.parse_args()
    
    
    if __name__ == '__main__':
        runstrat()
    
    

    App output:

    Mon 2006-01-02 10:00:00: BUY  CREATED
    Mon 2006-01-02 10:30:00: BUY Submitted, on: Mon 2006-01-02 10:00:00, dt 732313.4166666666
    Mon 2006-01-02 10:30:00: BUY Accepted, on: Mon 2006-01-02 10:00:00, dt 732313.4166666666
    Mon 2006-01-02 10:30:00: elapsedh: 0.50, current hour: 10, self.order.status Accepted, position 0
    Tue 2006-01-03 09:30:00: BUY  EXECUTED, on: Mon 2006-01-02 17:30:00, dt 732313.7291666666
    Mon 2006-01-09 09:30:00: elapsedh: 0.00, current hour: 9, self.order.status None, position 1
    Mon 2006-01-09 16:30:00: SELL CREATED
    Tue 2006-01-10 09:30:00: SELL  EXECUTED, on: Mon 2006-01-09 17:30:00, dt 732320.7291666666
    Mon 2006-01-16 09:30:00: elapsedh: 0.00, current hour: 9, self.order.status None, position 0
    Mon 2006-01-16 09:30:00: BUY  CREATED
    Mon 2006-01-16 10:00:00: BUY Submitted, on: Mon 2006-01-16 09:30:00, dt 732327.3958333334
    Mon 2006-01-16 10:00:00: BUY Accepted, on: Mon 2006-01-16 09:30:00, dt 732327.3958333334
    Mon 2006-01-16 10:00:00: elapsedh: 0.50, current hour: 10, self.order.status Accepted, position 0
    Tue 2006-01-17 09:30:00: BUY  EXECUTED, on: Mon 2006-01-16 17:30:00, dt 732327.7291666666
    Mon 2006-01-23 09:30:00: elapsedh: 0.00, current hour: 9, self.order.status None, position 1
    Mon 2006-01-23 16:30:00: SELL CREATED
    Tue 2006-01-24 09:30:00: SELL  EXECUTED, on: Mon 2006-01-23 17:30:00, dt 732334.7291666666
    Mon 2006-01-30 09:30:00: elapsedh: 0.00, current hour: 9, self.order.status None, position 0
    Mon 2006-01-30 09:30:00: BUY  CREATED
    Mon 2006-01-30 10:00:00: BUY Submitted, on: Mon 2006-01-30 09:30:00, dt 732341.3958333334
    Mon 2006-01-30 10:00:00: BUY Accepted, on: Mon 2006-01-30 09:30:00, dt 732341.3958333334
    Mon 2006-01-30 10:00:00: elapsedh: 0.50, current hour: 10, self.order.status Accepted, position 0
    
    


  • May be unrelated, but I don't see where you specify the timeframe and compression of the original CSV data (no resampled) in getdata(). The CSV seems to have a 5 min bars and if timeframe and compression are not set, the default is assumed, which is daily bars AFAIK.


Log in to reply
 

});