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

BackTrader, algorithm, buy if price decreasing



  • In result, didn't print self.dataclose[-2], can not identify it

        if self.datastatus and len(self.orderid) < 1:
            print('0 STEPPP')
            if self.dataclose[0] < self.dataclose[-1]:
                print("1 Step", self.dataclose[0], self.dataclose[-1])
                if self.dataclose[-1] < self.dataclose[-2]:
                    print("YEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEES")
                    # BUY, BUY, BUY!!! (with default parameters)
                    self.log('BUY CREATE, %.2f' % self.dataclose[0])
    
                    # Keep track of the created order to avoid a 2nd order
                    self.order = self.buy()
            else:
                print("NOOO")


  • What is your question? There isn't a print statement for dataclose[-2].



  • if self.dataclose[-1] < self.dataclose[-2]:
    self.dataclose[-2] can not identify it



  • I'd suggest to show all your code. If the example is long, try to eliminate parts that are not required to repro the error. Also, include the exact error message.



  • from future import (absolute_import, division, print_function,
    unicode_literals)

    import datetime # For datetime objects
    import os.path # To manage paths
    import sys # To find out the script name (in argv[0])

    import argparse
    import datetime
    import backtrader as bt

    from backtrader.utils import flushfile

    Create a Stratey

    class TestStrategy(bt.Strategy):
    params = dict(
        # smaperiod=5,
        trade=False,
        stake=10,
        exectype=bt.Order.Market,
        stopafter=0,
        valid=None,
    
        upper=2.1,
        cancel=0,
        donotsell=False,
    )
    
    def __init__(self):
        # Keep a reference to the "close" line in the data[0] dataseries
        self.orderid = list()
        self.order = None
        self.dataclose = self.datas[0].close
        self.upper_limit = self.p.upper
        # self.counttostop = 0
        self.datastatus = 0
        self.bar_executed = 0
    
    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_data(self, data, status, *args, **kwargs):
        print('*' * 5, 'DATA NOTIF:', data._getstatusname(status), *args)
        if status == data.LIVE:
            # self.counttostop = self.p.stopafter
            self.datastatus = 1
    
    def notify_store(self, msg, *args, **kwargs):
        print('*' * 5, 'STORE NOTIF:', msg)
    
    def notify_order(self, order):
        if order.status in [order.Completed, order.Cancelled, order.Rejected]:
            self.order = None
    
        # 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, %.2f' % order.executed.price)
        #     elif order.issell():
        #         self.log('SELL EXECUTED, %.2f' % order.executed.price)
    
        #     self.bar_executed = len(self)
        print('-' * 50, 'ORDER BEGIN', datetime.datetime.now())
        print(order)
        print('-' * 50, 'ORDER END')
    
        # elif order.status in [order.Canceled, order.Margin, order.Rejected]:
        #     self.log('Order Canceled/Margin/Rejected')
    
        # Write down: no pending order
        # self.order = None
    
    def notify_trade(self, trade):
        print('-' * 50, 'TRADE BEGIN', datetime.datetime.now())
        print(trade)
        print('-' * 50, 'TRADE END')
    
    def prenext(self):
        self.next(frompre=True)
    
    def next(self, frompre=False):
        # Simply log the closing price of the series from the reference
        txt = list()
        txt.append('%04d' % len(self))
        dtfmt = '%Y-%m-%dT%H:%M:%S.%f'
        txt.append('%s' % self.data.datetime.datetime(0).strftime(dtfmt))
        txt.append('{}'.format(self.data.close[0]))
        txt.append('{}'.format(self.data.close[-1]))
        print(', '.join(txt))
    
        # if len(self.datas) > 1:
        #     txt = list()
        #     txt.append('%04d' % len(self))
        #     dtfmt = '%Y-%m-%dT%H:%M:%S.%f'
        #     txt.append('%s' % self.data1.datetime.datetime(0).strftime(dtfmt))
        #     txt.append('{}'.format(self.data1.close[0]))
        #     # txt.append('{}'.format(self.data.close[-1]))
        #     # txt.append('{}'.format(self.data.close[-2]))
        #     txt.append('{}'.format(float('NaN')))
        #     print(', '.join(txt))
    
        # if self.counttostop:  # stop after x live lines
        #     self.counttostop -= 1
        #     if not self.counttostop:
        #         self.env.runstop()
        #         return
    
        if not self.p.trade:
            return
    
        if self.datastatus and len(self.orderid) < 1:
            print('0 STEPPP')
            if self.dataclose[0] < self.dataclose[-1]:
                print("1 Step", self.dataclose[0], self.dataclose[-1])
                if self.dataclose[-1] < self.upper_limit:
                    print("YEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEES")
                    # BUY, BUY, BUY!!! (with default parameters)
                    self.log('BUY CREATE, %.2f' % self.dataclose[0])
    
                    # Keep track of the created order to avoid a 2nd order
                    self.order = self.buy()
            else:
                print("NOOO")
    
        # elif self.position.size > 0 and not self.p.donotsell:
        #     if self.order is None:
        #         self.order = self.sell(size=self.p.stake // 2,
        #                                exectype=bt.Order.Market,
        #                                price=self.data0.close[0])
        #     self.cancel(self.order)
        # elif self.order is not None and self.p.cancel:
        #     if self.datastatus > self.p.cancel:
        #         self.cancel(self.order)
    
    
        if self.datastatus:
            self.datastatus += 1
    
    def start(self):
        if self.data0.contractdetails is not None:
            print('Timezone from ContractDetails: {}'.format(
                self.data0.contractdetails.m_timeZoneId))
        #
        # header = ['Datetime', 'Open', 'High', 'Low', 'Close', 'Volume',
        #           'OpenInterest']
        header = ['Datetime', 'Close']
        print(', '.join(header))
    
        self.done = False
    

    def runstrategy():
    args = parse_args()

    # Create a cerebro
    cerebro = bt.Cerebro()
    
    storekwargs = dict(
        host=args.host, port=args.port,
        clientId=args.clientId, timeoffset=not args.no_timeoffset,
        reconnect=args.reconnect, timeout=args.timeout,
        notifyall=args.notifyall, _debug=args.debug
    )
    
    if args.usestore:
        ibstore = bt.stores.IBStore(**storekwargs)
    
    if args.broker:
        if args.usestore:
            broker = ibstore.getbroker()
        else:
            broker = bt.brokers.IBBroker(**storekwargs)
    
        cerebro.setbroker(broker)
    
    timeframe = bt.TimeFrame.TFrame(args.timeframe)
    if args.resample or args.replay:
        datatf = bt.TimeFrame.Ticks
        datacomp = 1
    else:
        datatf = timeframe
        datacomp = args.compression
    
    fromdate = None
    if args.fromdate:
        dtformat = '%Y-%m-%d' + ('T%H:%M:%S' * ('T' in args.fromdate))
        fromdate = datetime.datetime.strptime(args.fromdate, dtformat)
    
    IBDataFactory = ibstore.getdata if args.usestore else bt.feeds.IBData
    
    datakwargs = dict(
        timeframe=datatf, compression=datacomp,
        historical=args.historical, fromdate=fromdate,
        rtbar=args.rtbar,
        qcheck=args.qcheck,
        what=args.what,
        backfill_start=not args.no_backfill_start,
        backfill=not args.no_backfill,
        latethrough=args.latethrough,
        tz=args.timezone
    )
    
    if not args.usestore and not args.broker:  # neither store nor broker
        datakwargs.update(storekwargs)  # pass the store args over the data
    
    data0 = IBDataFactory(dataname=args.data0, **datakwargs)
    
    data1 = None
    if args.data1 is not None:
        data1 = IBDataFactory(dataname=args.data1, **datakwargs)
    
    rekwargs = dict(
        timeframe=timeframe, compression=args.compression,
        bar2edge=not args.no_bar2edge,
        adjbartime=not args.no_adjbartime,
        rightedge=not args.no_rightedge,
        takelate=not args.no_takelate,
    )
    
    if args.replay:
        cerebro.replaydata(dataname=data0, **rekwargs)
    
        if data1 is not None:
            cerebro.replaydata(dataname=data1, **rekwargs)
    
    elif args.resample:
        cerebro.resampledata(dataname=data0, **rekwargs)
    
        if data1 is not None:
            cerebro.resampledata(dataname=data1, **rekwargs)
    
    else:
        cerebro.adddata(data0)
        if data1 is not None:
            cerebro.adddata(data1)
    
    if args.valid is None:
        valid = None
    else:
        datetime.timedelta(seconds=args.valid)
    # Add the strategy
    cerebro.addstrategy(TestStrategy,
                        # smaperiod=args.smaperiod,
                        trade=args.trade,
                        exectype=bt.Order.ExecType(args.exectype),
                        stake=args.stake,
                        stopafter=args.stopafter,
                        valid=valid,
                        cancel=args.cancel,
                        donotsell=args.donotsell)
    
    print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
    
    # Live data ... avoid long data accumulation by switching to "exactbars"
    cerebro.run(exactbars=args.exactbars)
    
    # Print out the final result
    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
    
    if args.plot and args.exactbars < 1:  # plot if possible
        cerebro.plot()
    

    def parse_args():
    parser = argparse.ArgumentParser(
    formatter_class=argparse.ArgumentDefaultsHelpFormatter,
    description='Test Interactive Brokers integration')

    parser.add_argument('--exactbars', default=1, type=int,
                        required=False, action='store',
                        help='exactbars level, use 0/-1/-2 to enable plotting')
    
    parser.add_argument('--plot',
                        required=False, action='store_true',
                        help='Plot if possible')
    
    parser.add_argument('--stopafter', default=0, type=int,
                        required=False, action='store',
                        help='Stop after x lines of LIVE data')
    
    parser.add_argument('--usestore',
                        required=False, action='store_true',
                        help='Use the store pattern')
    
    parser.add_argument('--notifyall',
                        required=False, action='store_true',
                        help='Notify all messages to strategy as store notifs')
    
    parser.add_argument('--debug',
                        required=False, action='store_true',
                        help='Display all info received form IB')
    
    parser.add_argument('--host', default='127.0.0.1',
                        required=False, action='store',
                        help='Host for the Interactive Brokers TWS Connection')
    
    parser.add_argument('--qcheck', default=0.5, type=float,
                        required=False, action='store',
                        help=('Timeout for periodic '
                              'notification/resampling/replaying check'))
    
    parser.add_argument('--port', default=7496, type=int,
                        required=False, action='store',
                        help='Port for the Interactive Brokers TWS Connection')
    
    parser.add_argument('--clientId', default=None, type=int,
                        required=False, action='store',
                        help='Client Id to connect to TWS (default: random)')
    
    parser.add_argument('--no-timeoffset',
                        required=False, action='store_true',
                        help=('Do not Use TWS/System time offset for non '
                              'timestamped prices and to align resampling'))
    
    parser.add_argument('--reconnect', default=3, type=int,
                        required=False, action='store',
                        help='Number of recconnection attempts to TWS')
    
    parser.add_argument('--timeout', default=3.0, type=float,
                        required=False, action='store',
                        help='Timeout between reconnection attempts to TWS')
    
    parser.add_argument('--data0', default=None,
                        required=True, action='store',
                        help='data 0 into the system')
    
    parser.add_argument('--data1', default=None,
                        required=False, action='store',
                        help='data 1 into the system')
    
    parser.add_argument('--timezone', default=None,
                        required=False, action='store',
                        help='timezone to get time output into (pytz names)')
    
    parser.add_argument('--what', default=None,
                        required=False, action='store',
                        help='specific price type for historical requests')
    
    parser.add_argument('--no-backfill_start',
                        required=False, action='store_true',
                        help='Disable backfilling at the start')
    
    parser.add_argument('--latethrough',
                        required=False, action='store_true',
                        help=('if resampling replaying, adjusting time '
                              'and disabling time offset, let late samples '
                              'through'))
    
    parser.add_argument('--no-backfill',
                        required=False, action='store_true',
                        help='Disable backfilling after a disconnection')
    
    parser.add_argument('--rtbar', default=False,
                        required=False, action='store_true',
                        help='Use 5 seconds real time bar updates if possible')
    
    parser.add_argument('--historical',
                        required=False, action='store_true',
                        help='do only historical download')
    
    parser.add_argument('--fromdate',
                        required=False, action='store',
                        help=('Starting date for historical download '
                              'with format: YYYY-MM-DD[THH:MM:SS]'))
    
    # parser.add_argument('--smaperiod', default=5, type=int,
    #                     required=False, action='store',
    #                     help='Period to apply to the Simple Moving Average')
    
    pgroup = parser.add_mutually_exclusive_group(required=False)
    
    pgroup.add_argument('--replay',
                        required=False, action='store_true',
                        help='replay to chosen timeframe')
    
    pgroup.add_argument('--resample',
                        required=False, action='store_true',
                        help='resample to chosen timeframe')
    
    parser.add_argument('--timeframe', default=bt.TimeFrame.Names[0],
                        choices=bt.TimeFrame.Names,
                        required=False, action='store',
                        help='TimeFrame for Resample/Replay')
    
    parser.add_argument('--compression', default=1, type=int,
                        required=False, action='store',
                        help='Compression for Resample/Replay')
    
    parser.add_argument('--no-takelate',
                        required=False, action='store_true',
                        help=('resample/replay, do not accept late samples '
                              'in new bar if the data source let them through '
                              '(latethrough)'))
    
    parser.add_argument('--no-bar2edge',
                        required=False, action='store_true',
                        help='no bar2edge for resample/replay')
    
    parser.add_argument('--no-adjbartime',
                        required=False, action='store_true',
                        help='no adjbartime for resample/replay')
    
    parser.add_argument('--no-rightedge',
                        required=False, action='store_true',
                        help='no rightedge for resample/replay')
    
    parser.add_argument('--broker',
                        required=False, action='store_true',
                        help='Use IB as broker')
    
    parser.add_argument('--trade',
                        required=False, action='store_true',
                        help='Do Sample Buy/Sell operations')
    
    parser.add_argument('--donotsell',
                        required=False, action='store_true',
                        help='Do not sell after a buy')
    
    parser.add_argument('--exectype', default=bt.Order.ExecTypes[0],
                        choices=bt.Order.ExecTypes,
                        required=False, action='store',
                        help='Execution to Use when opening position')
    
    parser.add_argument('--stake', default=10, type=int,
                        required=False, action='store',
                        help='Stake to use in buy operations')
    
    parser.add_argument('--valid', default=None, type=int,
                        required=False, action='store',
                        help='Seconds to keep the order alive (0 means DAY)')
    
    parser.add_argument('--cancel', default=0, type=int,
                        required=False, action='store',
                        help=('Cancel a buy order after n bars in operation,'
                              ' to be combined with orders like Limit'))
    
    return parser.parse_args()
    

    if name == 'main':
    runstrategy()



  • In the script you shown self.dataclose[-2] is not called. Also please use ``` to block the code, will be easier to review. Would be nice to see short version which generates the problem as @scottz1 proposed.



  • To check these statements, I tried to use like print("0 STEPPP"), and in result, it printed only
    0 and 1 Step, but 2 step it is correct and does not printed ("YEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEES").
    I checked when I wrote print(self.dataclose[-2]) it is printed today's value

         if self.datastatus and len(self.orderid) < 1:
            print('0 STEPPP')
            if self.dataclose[0] < self.dataclose[-1]:
                print("1 Step", self.dataclose[0], self.dataclose[-1])
                if self.dataclose[-1] < self.dataclose[-2]:
                    print("YEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEES")
                    # BUY, BUY, BUY!!! (with default parameters)
                    self.log('BUY CREATE, %.2f' % self.dataclose[0])
    
                    # Keep track of the created order to avoid a 2nd order
                    self.order = self.buy()
            else:
                print("NOOO")


  • @YELNAr you use two different scripts in this post. One of them uses dataclose[-2] and one don't. Would you be so kind and post the script which causes the problem. But removed from that script all un-necessary parts.


  • administrators

    @YELNAr said in BackTrader, algorithm, buy if price decreasing:

    def __init__(self):
        # Keep a reference to the "close" line in the data[0] dataseries
        self.orderid = list()
        self.order = None
        self.dataclose = self.datas[0].close
        self.upper_limit = self.p.upper
        # self.counttostop = 0
        self.datastatus = 0
        self.bar_executed = 0
    

    Your __init__ statement contain no indicators and no constraints to ensure buffers are filled with a minimum. This means that when you enter next the first time anything which uses [-1] and [-2] is actually broken, because the minimum period is 1, which means the code goes directly into next as soon as data is available.

    My recommendation: learn backtesting loading data from a file and not playing with InteractiveBrokers.

    In the meanwhile read this about the minimum period: https://www.backtrader.com/docu/operating/


Log in to reply
 

});