The following ValueError exception may be raised in case the specific fromdate parameter is used to initialize the IBStore:
Traceback (most recent call last):
File "C:/Users/Vlad/PycharmProjects/test/test_ib_error.py", line 35, in <module>
cerebro.run()
File "W:\backtrader\backtrader\cerebro.py", line 1177, in run
runstrat = self.runstrategies(iterstrat)
File "W:\backtrader\backtrader\cerebro.py", line 1263, in runstrategies
data._start()
File "W:\backtrader\backtrader\feed.py", line 203, in _start
self.start()
File "W:\backtrader\backtrader\feeds\ibdata.py", line 406, in start
self._st_start()
File "W:\backtrader\backtrader\feeds\ibdata.py", line 650, in _st_start
sessionend=self.p.sessionend)
File "W:\backtrader\backtrader\stores\ibstore.py", line 720, in reqHistoricalDataEx
intdate = self.dt_plus_duration(begindate, dur)
File "W:\backtrader\backtrader\stores\ibstore.py", line 1191, in dt_plus_duration
return dt.replace(year=dt.year + years, month=month + 1)
ValueError: day is out of range for month
Sample user code:
import backtrader as bt
import datetime
class IntraTrendStrategy(bt.Strategy):
def next(self):
pass
cerebro = bt.Cerebro()
store = bt.stores.IBStore(host="127.0.0.1", port=7497, clientId= 4)
cerebro.addstrategy(IntraTrendStrategy)
stockkwargs = dict(
timeframe=bt.TimeFrame.Minutes,
compression=5,
rtbar=False, # use RealTime 5 seconds bars
historical=True, # only historical download
qcheck=0.5, # timeout in seconds (float) to check for events
fromdate=datetime.datetime(2020, 1, 1), # get data from..
todate=datetime.datetime(2020, 9, 20), # get data from..
latethrough=False, # let late samples through
tradename=None, # use a different asset as order target
tz="Asia/Kolkata"
)
data0 = store.getdata(dataname="TCS-STK-NSE-INR", **stockkwargs)
cerebro.replaydata(data0, timeframe=bt.TimeFrame.Days, compression=1)
cerebro.broker.setcash(100000.0)
cerebro.broker.setcommission(commission=0.001)
cerebro.run()
Analysis:
The problem is with the following code in IBStore.py:
def dt_plus_duration(self, dt, duration):
size, dim = duration.split()
size = int(size)
if dim == 'S':
return dt + timedelta(seconds=size)
if dim == 'D':
return dt + timedelta(days=size)
if dim == 'W':
return dt + timedelta(days=size * 7)
if dim == 'M':
month = dt.month - 1 + size # -1 to make it 0 based, readd below
years, month = divmod(month, 12)
return dt.replace(year=dt.year + years, month=month + 1) # <=---- the bug is here
if dim == 'Y':
return dt.replace(year=dt.year + size)
return dt # could do nothing with it ... return it intact
It's plain wrong to replace the month of the date without taking care of the day of the month. In our case the date '2019.12.31' was replaced with '2020.02.31' which is definitely wrong.
Reduced test case:
import backtrader as bt
import datetime
store = bt.IBStore()
store.dt_plus_duration(datetime(2019,12,31,), '2M')
Community discussion: https://community.backtrader.com/topic/3030/exception-has-occurred-valueerror-day-is-out-of-range-for-month-file-f-ibbacktradelivedataex-py-line-165-in-module-cerebro-run
Problem description:
The following ValueError exception may be raised in case the specific
fromdate
parameter is used to initialize the IBStore:Sample user code:
Analysis:
The problem is with the following code in IBStore.py:
It's plain wrong to replace the month of the date without taking care of the day of the month. In our case the date '2019.12.31' was replaced with '2020.02.31' which is definitely wrong.
Reduced test case: