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 thedatetime
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!!
-
Hey everyone,
I am also trying out this sample code from https://www.backtrader.com/blog/2019-06-13-buy-and-hold/buy-and-hold/ , however I don't understand what fundmode/fundstartval means. Documentation also doesn't have alot of information either. How does the example code track "tax lots"? Can someone please explain this to me?
Thanks!
-