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

next() beginning before minimum period



  • Hi I have the following code

    class FirstStrat (bt.Strategy):
    
        params = (
            ('system1EntryPeriod', 20),
            ('system1ExitPeriod', 10),
            ('system2EntryPeriod', 55),
            ('system2ExitPeriod', 20),
            ('printlog', True),
        )
    
        def __init__(self):
            # self.dataclose = self.datas[0].close
            # To keep track of pending orders
            self.order = None
            self.buyprice = None
            self.buyComm = None
    
            # Tried with just self.data
            # It uses CLOSE instead of high
            self.S1HEntry = bt.indicators.Highest(self.data.high,
                                             period=self.p.system1EntryPeriod)
            self.S2HEntry = bt.indicators.Highest(self.data.high,
                                             period=self.p.system2EntryPeriod)
            self.S1LExit = bt.indicators.Lowest(self.data.low,
                                             period=self.p.system1ExitPeriod)
            self.S2LExit = bt.indicators.Lowest(self.data.low,
                                             period=self.p.system2ExitPeriod)
            
            self.S1LEntry = bt.indicators.Lowest(self.data.low,
                                            period=self.p.system1EntryPeriod)
            self.S2LEntry = bt.indicators.Lowest(self.data.low,
                                            period=self.p.system2EntryPeriod)
            self.S1HExit = bt.indicators.Highest(self.data.high,
                                             period=self.p.system1ExitPeriod)
            self.S2HExit = bt.indicators.Highest(self.data.high,
                                             period=self.p.system2ExitPeriod)
    

    I'm using one data feed -- YahooFinanceData. The problem I'm facing is that next() should be called when we have 55 data points, but for me it's called right from the first data point. After some debugging I see that self._minperiod = 55 while self._minperiods = [1].

    What am I not getting?


  • administrators

    You have a lot more code, which probably is the root of your problem and some more information which would be needed, like timeframes you use, how you load the data ...

    Posting a complete working sample is what helps.



  • Understood. Here's the full code.

    Again, the problem is that next() runs on the first candle with all the indicators as nan instead of running after the expected 55 candles.

    import backtrader as bt
    import pdb
    
    from enum import Enum, auto
    
    class TurtleSystem(Enum):
        SYSTEM_1 = auto()
        SYSTEM_2 = auto()
        NO_POSITION = auto()
    
    
    class FirstStrat (bt.Strategy):
    
        params = (
            ('system1EntryPeriod', 20),
            ('system1ExitPeriod', 10),
            ('system2EntryPeriod', 55),
            ('system2ExitPeriod', 20),
            ('printlog', True),
        )
    
        def __init__(self):
            # self.dataclose = self.datas[0].close
            # To keep track of pending orders
            self.order = None
            self.buyprice = None
            self.buyComm = None
    
            # Tried with just self.data
            # It uses CLOSE instead of high
            self.S1HEntry = bt.indicators.Highest(self.data.high,
                                             period=self.p.system1EntryPeriod)
            self.S2HEntry = bt.indicators.Highest(self.data.high,
                                             period=self.p.system2EntryPeriod)
            self.S1LExit = bt.indicators.Lowest(self.data.low,
                                             period=self.p.system1ExitPeriod)
            self.S2LExit = bt.indicators.Lowest(self.data.low,
                                             period=self.p.system2ExitPeriod)
            
            self.S1LEntry = bt.indicators.Lowest(self.data.low,
                                            period=self.p.system1EntryPeriod)
            self.S2LEntry = bt.indicators.Lowest(self.data.low,
                                            period=self.p.system2EntryPeriod)
            self.S1HExit = bt.indicators.Highest(self.data.high,
                                             period=self.p.system1ExitPeriod)
            self.S2HExit = bt.indicators.Highest(self.data.high,
                                             period=self.p.system2ExitPeriod)
    
    
            self.currentPositionSystem = TurtleSystem.NO_POSITION
    
        def log(self, txt, dt=None, doprint=False):
            ''' Logging function fot this strategy'''
            if self.params.printlog or doprint:
                dt = dt or self.datas[0].datetime.date(0)
                print('%s, %s' % (dt.isoformat(), txt))
    
        def next(self):
            self.log('highest %.2f' % self.S1HEntry[0])
    
            # Check if an order is pending ... if yes, we cannot send a 2nd one
            if self.order:
                return
    
            # We are already in the market
            if self.position:
    
                # pdb.set_trace()
                
                # If this is a short position
                if self.position.size < 0:
    
                    # Check if exit criteria is reached
                    if self.currentPositionSystem == TurtleSystem.SYSTEM_1 and \
                        self.data.close[0] > self.S1HExit[-1]:
    
                        self.order = self.buy()
    
                    elif self.currentPositionSystem == TurtleSystem.SYSTEM_2 and \
                            self.data.close[0] > self.S2HExit[-1]:
    
                        self.order = self.buy()
    
                # This is a long position
                elif self.position.size > 0:
                    if self.currentPositionSystem == TurtleSystem.SYSTEM_1 and \
                        self.data.close[0] < self.S1LExit[-1]:
    
                        self.order = self.sell()
    
                    elif self.currentPositionSystem == TurtleSystem.SYSTEM_2 and \
                        self.data.close[0] < self.S2LExit[-1]:
    
                        self.order = self.sell()
                else:
                    assert False, "Position size zero. Should not happen"
                
    
            # We're not in the market yet
            else:
                # pdb.set_trace()
                if self.data.close[0] > self.S1HEntry[-1]:
                    # Need to do other checks before entering position
    
                    self.order = self.buy()
                    self.currentPositionSystem = TurtleSystem.SYSTEM_1
                # Breaking 55H
                elif self.data.close[0] > self.S2HEntry[-1]:
    
                    # Always take this entry
                    self.order = self.buy()
                    self.currentPositionSystem = TurtleSystem.SYSTEM_2
    
                elif self.data.close[0] < self.S1LEntry[-1]:
                    # Need to do other checks before entering this position
    
                    self.order = self.sell()
                    self.currentPositionSystem = TurtleSystem.SYSTEM_1
    
                # Breaking 55L
                elif self.data.close[0] < self.S2LEntry[-1]:
                    # Always take this entry
                    self.order = self.sell()
                    self.currentPositionSystem = TurtleSystem.SYSTEM_2
    
                else:
                    self.log("Do nothing")
    
        def notify_trade(self, trade):
            if not trade.isclosed:
                return
            pdb.set_trace()
            self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
                     (trade.pnl, trade.pnlcomm))
    
        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))
                elif order.issell():
                    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.currentPositionSystem = TurtleSystem.NO_POSITION
    
            # Write down: no pending order
            self.order = None
    
    
    
    INSTRUMENT = 'BTC-USD'
    
    if __name__ == '__main__':
    
        cerebro = bt.Cerebro()
    
        data = bt.feeds.YahooFinanceData(dataname=INSTRUMENT,
                                         period='d',
                                         fromdate=datetime.datetime(2013, 1, 1),
                                         todate=datetime.datetime(2018, 9, 20),
                                         )
    
        cerebro.adddata(data)
        cerebro.addstrategy(FirstStrat)
    
        cerebro.broker.setcommission(commission=0.001)
        cerebro.addsizer(bt.sizers.PercentSizer, percents=2)
    
        startingValue = cerebro.broker.getvalue()
        print('Starting Portfolio Value: %.2f' % startingValue)
        cerebro.broker.set_cash(100000)
        cerebro.run()
    
        finalValue = cerebro.broker.getvalue()
        print('Final Portfolio Value: %.2f' % finalValue)
    
    

  • administrators

    A working sample doesn't mean your full code ... just a working sample, for example with a single indicator and no logic.

    @xenide said in next() beginning before minimum period:

        # Tried with just self.data
        # It uses CLOSE instead of high
        self.S1HEntry = bt.indicators.Highest(self.data.high,
    

    Of course. If you apply self.data it gives you the default which everybody uses, i.e.: the close price. Highest calculates the highest price of what you give to it and not the highest high, unless you pass the high, like in your case.

    @xenide said in next() beginning before minimum period:

    What am I not getting?

    Nothing, in a good sense, because you have uncovered an unexpected behavior. You are probably the first to not use self.data (or any of the data feeds in multiple data feeds scenarios), because you have chosen the individual lines. Which generates this behavior. To be checked.


  • administrators

    See Community - Release 1.9.67.122

    And for a test sample

    import argparse
    import backtrader as bt
    
    
    class St(bt.Strategy):
        params = dict(usedata=False)
    
        def __init__(self):
            d = self.data if self.p.usedata else self.data.high
            bt.indicators.Highest(d, period=20)
    
        def nextstart(self):
            print('NEXTSTART len(self):', len(self), 'ok:', len(self) == 20)
    
    
    def runstrat(args=None):
        args = parse_args(args)
        cerebro = bt.Cerebro()
    
        cerebro.adddata(bt.feeds.BacktraderCSVData(
            dataname='../../datas/2006-day-001.txt'))
        cerebro.addstrategy(St, usedata=args.data)
        cerebro.run(stdstats=False)
    
    
    def parse_args(pargs=None):
        parser = argparse.ArgumentParser(
            formatter_class=argparse.ArgumentDefaultsHelpFormatter,
            description=('Sample Skeleton')
        )
    
        parser.add_argument('--data', action='store_true', help='Do not use high')
        return parser.parse_args(pargs)
    
    
    if __name__ == '__main__':
        runstrat()