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

Can get backtrader to buy on the 1st of the month



  • I am trying to implement the BuyAndHold_More strategy listed here:
    https://www.backtrader.com/blog/2019-06-13-buy-and-hold/buy-and-hold/

    I am using the exact same code. I then try to run it via this:

    if __name__ == '__main__':
        cerebro = bt.Cerebro()
        cerebro.addstrategy(BuyAndHold_More)
    
        class PandasData(bt.feeds.DataBase):
          params = (
              ('datetime', 'Date'),
              ('open','Open'),
              ('high','High'),
              ('low','Low'),
              ('close','Close'),
              ('volume','Volume'),
              ('openinterest',None),
              #('adj_close','Adj Close'),
            )
            
        adj_data = bt.feeds.PandasData(dataname=df3)
        cerebro.adddata(adj_data)
        print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
        cerebro.run()
        print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
    

    But the staring and ending portfolio values are unchanged. What am I missing please? I feel like it should be something simple but I am lost. Thanks in advance.



  • Having the same problem! Sharing whole code snippet below.

    import datetime
    import backtrader as bt
    
    class BuyAndHold_More(bt.Strategy):
        params = dict(
            monthly_cash=1000.0,  # amount of cash to buy every month
        )
    
        def start(self):
            self.cash_start = self.broker.get_cash()
            self.val_start = 100.0
    
            # Add a timer which will be called on the 1st trading day of the month
            self.add_timer(
                bt.timer.SESSION_END,  # when it will be called
                monthdays=[1],  # called on the 1st day of the month
                monthcarry=True,  # called on the 2nd day if the 1st is holiday
            )
    
        def notify_timer(self, timer, when, *args, **kwargs):
            # Add the influx of monthly cash to the broker
            self.broker.add_cash(self.p.monthly_cash)
            print('Cash Added!')
            # buy available cash
            target_value = self.broker.get_value() + self.p.monthly_cash
            self.order_target_value(target=target_value)
    
        def stop(self):
            # calculate the actual returns
            self.roi = (self.broker.get_value() / self.cash_start) - 1.0
            print('ROI:        {:.2f}%'.format(self.roi))
    
    
    if __name__ == '__main__':
        cerebro = bt.Cerebro()
    
       # Get a pandas dataframe
    
        class PandasData(bt.feeds.DataBase):
          #lines = ('adj_close')
          params = (
              ('datetime', 'Date'),
              ('open','Open'),
              ('high','High'),
              ('low','Low'),
              ('close','Close'),
              ('volume','Volume'),
              ('openinterest',None),
              #('adj_close','Adj Close'),
            )
            
        # import data
        print(df_new)
        adj_data = bt.feeds.PandasData(dataname=df_new)
        
        #add data to cerebro
        cerebro.adddata(adj_data)
    
        cerebro.addstrategy(BuyAndHold_More)
    
        # Execute
        cerebro.run()
    
    

    Output when run. Note that my "Cash Added!" never prints, so it looks like the timer is never getting run.

                Open      High       Low       Close     Volume
    

    Date
    2000-01-03 101.3166 101.3166 98.3267 99.394493 8164300
    2000-01-04 98.0917 98.4548 95.4328 95.507530 8089800
    2000-01-05 95.6357 96.7249 93.7990 95.678391 12177900
    2000-01-06 95.4221 96.7035 94.1407 94.140717 6227200
    2000-01-07 95.8920 99.6081 95.7211 99.608055 8066500
    ... ... ... ... ... ...
    2020-04-20 282.6100 286.7900 281.3500 281.589996 100109300
    2020-04-21 276.7300 278.0400 272.0200 273.040009 126385700
    2020-04-22 278.3500 281.0000 276.9100 279.100006 93524600
    2020-04-23 280.4900 283.9400 278.7500 279.079987 104709700
    2020-04-24 280.7300 283.7000 278.5000 282.970001 85063200

    [5110 rows x 5 columns]
    ROI: 0.00%



  • Is it possible that this is due to the date format of the pandas date? i.e. there is no time in my data source, vs in the examples on

    https://www.backtrader.com/docu/timers/timers/

    they all have times attached to their dates.



  • Modified data to have EOD time stamp attached. Still doesn't work. Output:

                         Open      High       Low       Close     Volume
    

    Date
    2000-01-03 17:30:00 101.3166 101.3166 98.3267 99.394493 8164300
    2000-01-04 17:30:00 98.0917 98.4548 95.4328 95.507530 8089800
    2000-01-05 17:30:00 95.6357 96.7249 93.7990 95.678391 12177900
    2000-01-06 17:30:00 95.4221 96.7035 94.1407 94.140717 6227200
    2000-01-07 17:30:00 95.8920 99.6081 95.7211 99.608055 8066500
    ... ... ... ... ... ...
    2020-04-20 17:30:00 282.6100 286.7900 281.3500 281.589996 100109300
    2020-04-21 17:30:00 276.7300 278.0400 272.0200 273.040009 126385700
    2020-04-22 17:30:00 278.3500 281.0000 276.9100 279.100006 93524600
    2020-04-23 17:30:00 280.4900 283.9400 278.7500 279.079987 104709700
    2020-04-24 17:30:00 280.7300 283.7000 278.5000 282.970001 85063200

    [5110 rows x 5 columns]
    ROI: 0.00%



  • @Ajay-Nainani said in Can get backtrader to buy on the 1st of the month:

    s it possible that this is due to the date format of the pandas date?

    Looks like this is correct guess. Possibly the date is not in the datetime format, but is imported as text.



  • @ab_trader
    Hello, I too facing the timer issue, it doesn't trigger not only for this simple buy and hold but anywhere I tried. The pandas feed I am using has datetime index, I also tried tz_localize('UTC')

    prices.index = prices.index.tz_localize('UTC')
    prices.info()
    DatetimeIndex: 2792 entries, 2009-01-02 00:00:00+00:00 to 2020-05-19 00:00:00+00:00
    Data columns (total 5 columns):
     #   Column  Non-Null Count  Dtype  
    ---  ------  --------------  -----  
     0   open    2792 non-null   float64
     1   high    2792 non-null   float64
     2   low     2792 non-null   float64
     3   close   2792 non-null   float64
     4   volume  2792 non-null   float64
    
    prices.head()
    	open	high	low	close	volume
    date					
    2009-01-02 00:00:00+00:00	145.625000	145.625000	140.475006	50.516472	9012080.0
    2009-01-05 00:00:00+00:00	142.500000	148.087006	142.462006	52.332775	11241232.0
    2009-01-06 00:00:00+00:00	147.500000	148.750000	143.169006	52.143211	11563864.0
    2009-01-07 00:00:00+00:00	146.393997	149.694000	143.199997	52.991051	22538352.0
    2009-01-09 00:00:00+00:00	148.261993	158.324997	146.250000	53.698215	40182160.0
    
    

    I appreciate any help!!



  • You have the notify_timer, but you didn't initiate the timer. You also need to add in a monthdays parameter.

    I've simplified your example just to get the cash added.

    class BuyAndHold_More(bt.Strategy):
        params = dict(
            monthly_cash=1000.0,  # amount of cash to buy every month
            when=bt.timer.SESSION_START,
            timer=True,
            monthdays=[1],
        )
    
        def __init__(self):
            self.add_timer(
                when=self.p.when,
                monthdays=self.p.monthdays,
            )
    
        def log(self, txt, dt=None):
            """ Logging function fot this strategy"""
            dt = dt or self.data.datetime[0]
            if isinstance(dt, float):
                dt = bt.num2date(dt)
            print("%s, %s" % (dt.isoformat(), txt))
    
        def start(self):
            self.cash_start = self.broker.get_cash()
            self.val_start = 100.0
    
            # Add a timer which will be called on the 1st trading day of the month
            self.add_timer(
                bt.timer.SESSION_END,  # when it will be called
                monthdays=[1],  # called on the 1st day of the month
                monthcarry=True,  # called on the 2nd day if the 1st is holiday
            )
    
        def notify_timer(self, timer, when, *args, **kwargs):
            # Add the influx of monthly cash to the broker
            self.broker.add_cash(self.p.monthly_cash)
            print("Cash Added!")
            
        def next(self):
            self.log("Cash {} Value {}".format(self.broker.cash, self.broker.get_value()))
    
        def stop(self):
            # calculate the actual returns
            self.roi = (self.broker.get_value() / self.cash_start) - 1.0
            print("ROI:        {:.2f}%".format(self.roi))
    

    This yields:

    2005-01-03T00:00:00, Cash 10000.0 Value 10000.0
    2005-01-04T00:00:00, Cash 11000.0 Value 11000.0
    2005-01-05T00:00:00, Cash 11000.0 Value 11000.0
    2005-01-06T00:00:00, Cash 11000.0 Value 11000.0
    2005-01-07T00:00:00, Cash 11000.0 Value 11000.0
    2005-01-10T00:00:00, Cash 11000.0 Value 11000.0
    2005-01-11T00:00:00, Cash 11000.0 Value 11000.0
    2005-01-12T00:00:00, Cash 11000.0 Value 11000.0
    2005-01-13T00:00:00, Cash 11000.0 Value 11000.0
    2005-01-14T00:00:00, Cash 11000.0 Value 11000.0
    2005-01-18T00:00:00, Cash 11000.0 Value 11000.0
    2005-01-19T00:00:00, Cash 11000.0 Value 11000.0
    2005-01-20T00:00:00, Cash 11000.0 Value 11000.0
    2005-01-21T00:00:00, Cash 11000.0 Value 11000.0
    2005-01-24T00:00:00, Cash 11000.0 Value 11000.0
    2005-01-25T00:00:00, Cash 11000.0 Value 11000.0
    2005-01-26T00:00:00, Cash 11000.0 Value 11000.0
    2005-01-27T00:00:00, Cash 11000.0 Value 11000.0
    2005-01-28T00:00:00, Cash 11000.0 Value 11000.0
    2005-01-31T00:00:00, Cash 11000.0 Value 11000.0
    Cash Added!
    2005-02-01T00:00:00, Cash 11000.0 Value 11000.0
    2005-02-02T00:00:00, Cash 12000.0 Value 12000.0
    2005-02-03T00:00:00, Cash 12000.0 Value 12000.0
    2005-02-04T00:00:00, Cash 12000.0 Value 12000.0
    2005-02-07T00:00:00, Cash 12000.0 Value 12000.0
    2005-02-08T00:00:00, Cash 12000.0 Value 12000.0
    2005-02-09T00:00:00, Cash 12000.0 Value 12000.0
    2005-02-10T00:00:00, Cash 12000.0 Value 12000.0
    2005-02-11T00:00:00, Cash 12000.0 Value 12000.0
    2005-02-14T00:00:00, Cash 12000.0 Value 12000.0
    2005-02-15T00:00:00, Cash 12000.0 Value 12000.0
    2005-02-16T00:00:00, Cash 12000.0 Value 12000.0
    2005-02-17T00:00:00, Cash 12000.0 Value 12000.0
    2005-02-18T00:00:00, Cash 12000.0 Value 12000.0
    2005-02-22T00:00:00, Cash 12000.0 Value 12000.0
    2005-02-23T00:00:00, Cash 12000.0 Value 12000.0
    2005-02-24T00:00:00, Cash 12000.0 Value 12000.0
    2005-02-25T00:00:00, Cash 12000.0 Value 12000.0
    2005-02-28T00:00:00, Cash 12000.0 Value 12000.0
    Cash Added!
    2005-03-01T00:00:00, Cash 12000.0 Value 12000.0
    2005-03-02T00:00:00, Cash 13000.0 Value 13000.0
    2005-03-03T00:00:00, Cash 13000.0 Value 13000.0
    2005-03-04T00:00:00, Cash 13000.0 Value 13000.0
    2005-03-07T00:00:00, Cash 13000.0 Value 13000.0
    2005-03-08T00:00:00, Cash 13000.0 Value 13000.0
    2005-03-09T00:00:00, Cash 13000.0 Value 13000.0
    2005-03-10T00:00:00, Cash 13000.0 Value 13000.0
    2005-03-11T00:00:00, Cash 13000.0 Value 13000.0
    2005-03-14T00:00:00, Cash 13000.0 Value 13000.0
    2005-03-15T00:00:00, Cash 13000.0 Value 13000.0
    2005-03-16T00:00:00, Cash 13000.0 Value 13000.0
    2005-03-17T00:00:00, Cash 13000.0 Value 13000.0
    2005-03-18T00:00:00, Cash 13000.0 Value 13000.0
    2005-03-21T00:00:00, Cash 13000.0 Value 13000.0
    2005-03-22T00:00:00, Cash 13000.0 Value 13000.0
    2005-03-23T00:00:00, Cash 13000.0 Value 13000.0
    2005-03-24T00:00:00, Cash 13000.0 Value 13000.0
    2005-03-28T00:00:00, Cash 13000.0 Value 13000.0
    2005-03-29T00:00:00, Cash 13000.0 Value 13000.0
    2005-03-30T00:00:00, Cash 13000.0 Value 13000.0
    2005-03-31T00:00:00, Cash 13000.0 Value 13000.0
    Cash Added!
    2005-04-01T00:00:00, Cash 13000.0 Value 13000.0
    2005-04-04T00:00:00, Cash 14000.0 Value 14000.0
    2005-04-05T00:00:00, Cash 14000.0 Value 14000.0
    2005-04-06T00:00:00, Cash 14000.0 Value 14000.0
    2005-04-07T00:00:00, Cash 14000.0 Value 14000.0
    2005-04-08T00:00:00, Cash 14000.0 Value 14000.0
    2005-04-11T00:00:00, Cash 14000.0 Value 14000.0
    2005-04-12T00:00:00, Cash 14000.0 Value 14000.0
    2005-04-13T00:00:00, Cash 14000.0 Value 14000.0
    2005-04-14T00:00:00, Cash 14000.0 Value 14000.0
    2005-04-15T00:00:00, Cash 14000.0 Value 14000.0
    2005-04-18T00:00:00, Cash 14000.0 Value 14000.0
    2005-04-19T00:00:00, Cash 14000.0 Value 14000.0
    2005-04-20T00:00:00, Cash 14000.0 Value 14000.0
    2005-04-21T00:00:00, Cash 14000.0 Value 14000.0
    2005-04-22T00:00:00, Cash 14000.0 Value 14000.0
    2005-04-25T00:00:00, Cash 14000.0 Value 14000.0
    2005-04-26T00:00:00, Cash 14000.0 Value 14000.0
    2005-04-27T00:00:00, Cash 14000.0 Value 14000.0
    2005-04-28T00:00:00, Cash 14000.0 Value 14000.0
    2005-04-29T00:00:00, Cash 14000.0 Value 14000.0
    Cash Added!
    2005-05-02T00:00:00, Cash 14000.0 Value 14000.0
    2005-05-03T00:00:00, Cash 15000.0 Value 15000.0
    2005-05-04T00:00:00, Cash 15000.0 Value 15000.0
    2005-05-05T00:00:00, Cash 15000.0 Value 15000.0
    


  • @run-out Thank you!!! understood the mistake at my end and got it now. I highly appreciate your on-time help. Many thanks again.
    -SJ



  • If you want to operate only at the end of the month, you can resample to monthly bars, add cash in the next(), and remove timers from the script. Might be simpler.



  • @ab_trader Yes, Thank you!!


Log in to reply
 

});