@backtrader said in 'ItemCollection' object has no attribute 'broker':
erebro_wf = Cerebro(stdstats=False)
Amazing! Thanks so much for your quick reply!! That has indeed solved the issue.
@backtrader said in 'ItemCollection' object has no attribute 'broker':
erebro_wf = Cerebro(stdstats=False)
Amazing! Thanks so much for your quick reply!! That has indeed solved the issue.
I have gone into "annualreturn.py" and changed:
value_cur = self.strategy.stats.broker.value[-i]
to:
try:
value_cur = self.strategy.stats.broker.value[-i]
except:
value_cur = self.strategy.stats.acctvalue[-i]
and it now seems to work - but surely I should not be changing the backtrader source code!
Does anyone have any idea?
I am trying to run a Cerebro instance backtest and am getting the following error:
File "<ipython-input-120-b8cab2f96cd8>", line 1, in <module>
runfile('C:/Users/Google Drive/PPH/walkforward_opt/main_optim.py', wdir='C:/Users/Google Drive/PPH/walkforward_opt')
File "C:\Users\Anaconda3\lib\site-packages\spyder_kernels\customize\spydercustomize.py", line 678, in runfile
execfile(filename, namespace)
File "C:\Users\Anaconda3\lib\site-packages\spyder_kernels\customize\spydercustomize.py", line 106, in execfile
exec(compile(f.read(), filename, 'exec'), namespace)
File "C:/Users/Google Drive/PPH/Marten/Jamieson2/walkforward_opt/main_optim.py", line 457, in <module>
results = cerebro_wf.run()
File "C:\Users\Anaconda3\lib\site-packages\backtrader\cerebro.py", line 1127, in run
runstrat = self.runstrategies(iterstrat)
File "C:\Users\Anaconda3\lib\site-packages\backtrader\cerebro.py", line 1298, in runstrategies
strat._stop()
File "C:\Users\Anaconda3\lib\site-packages\backtrader\strategy.py", line 462, in _stop
analyzer._stop()
File "C:\Users\Anaconda3\lib\site-packages\backtrader\analyzer.py", line 200, in _stop
self.stop()
File "C:\Users\Anaconda3\lib\site-packages\backtrader\analyzers\annualreturn.py", line 64, in stop
value_cur = self.strategy.stats.broker.value[-i]
AttributeError: 'ItemCollection' object has no attribute 'broker'
I am especially confused by this error as I believe the code that is causing the error is part of the built in "annualreturn" analyzer code which I have not changed.
Why would my strategy be creating an "ItemCollection" object where it is not supposed to?
Has anyone come across this before?
My full strategy class and other code is below if needed:
class SMACWalkForward(bt.Strategy):
"""The SMAC strategy but in a walk-forward analysis context"""
params = {"start_dates": None, # Starting days for trading periods (a list)
"end_dates": None, # Ending day for trading periods (a list)
"fast": None, # List of fast moving average windows, corresponding to start dates (a list)
"slow": None} # Like fast, but for slow moving average window (a list)
# All the above lists must be of the same length, and they all line up
def __init__(self):
"""Initialize the strategy"""
print("STARTING WALKFORWARD STRATEGY")
self.fastma = dict()
self.slowma = dict()
self.regime = dict()
self.date_combos = [c for c in zip(self.p.start_dates, self.p.end_dates)]
# Error checking
if type(self.p.start_dates) is not list or type(self.p.end_dates) is not list or \
type(self.p.fast) is not list or type(self.p.slow) is not list:
raise ValueError("Must past lists filled with numbers to params start_dates, end_dates, fast, slow.")
elif len(self.p.start_dates) != len(self.p.end_dates) or \
len(self.p.fast) != len(self.p.start_dates) or len(self.p.slow) != len(self.p.start_dates):
raise ValueError("All lists passed to params must have same length.")
for d in self.getdatanames():
self.fastma[d] = dict()
self.slowma[d] = dict()
self.regime[d] = dict()
# Additional indexing, allowing for differing start/end dates
for sd, ed, f, s in zip(self.p.start_dates, self.p.end_dates, self.p.fast, self.p.slow):
# More error checking
if type(f) is not int or type(s) is not int:
raise ValueError("Must include only integers in fast, slow.")
elif f > s:
raise ValueError("Elements in fast cannot exceed elements in slow.")
elif f <= 0 or s <= 0:
raise ValueError("Moving average windows must be positive.")
if type(sd) is not dt.date or type(ed) is not dt.date:
raise ValueError("Only datetime dates allowed in start_dates, end_dates.")
elif ed - sd < dt.timedelta(0):
raise ValueError("Start dates must always be before end dates.")
# The moving averages
# Notice that different moving averages are obtained for different combinations of
# start/end dates
self.fastma[d][(sd, ed)] = btind.SimpleMovingAverage(self.getdatabyname(d),
period=f,
plot=False)
self.slowma[d][(sd, ed)] = btind.SimpleMovingAverage(self.getdatabyname(d),
period=s,
plot=False)
# Get the regime
self.regime[d][(sd, ed)] = self.fastma[d][(sd, ed)] - self.slowma[d][(sd, ed)]
# In the future, use the backtrader indicator btind.CrossOver()
def next(self):
"""Define what will be done in a single step, including creating and closing trades"""
# Determine which set of moving averages to use
curdate = self.datetime.date(0)
dtidx = None # Will be index
# Determine which period (if any) we are in
for sd, ed in self.date_combos:
# Debug output
#print('{}: {} < {}: {}, {} < {}: {}'.format(
# len(self), sd, curdate, (sd <= curdate), curdate, ed, (curdate <= ed)))
if sd <= curdate and curdate <= ed:
dtidx = (sd, ed)
# Debug output
#print('{}: the dtixdx is {}, and curdate is {};'.format(len(self), dtidx, curdate))
for d in self.getdatanames(): # Looping through all symbols
pos = self.getpositionbyname(d).size or 0
if dtidx is None: # Not in any window
break # Don't engage in trades
if pos == 0: # Are we out of the market?
# Consider the possibility of entrance
# Notice the indexing; [0] always mens the present bar, and [-1] the bar immediately preceding
# Thus, the condition below translates to: "If today the regime is bullish (greater than
# 0) and yesterday the regime was not bullish"
if self.regime[d][dtidx][0] > 0 and self.regime[d][dtidx][-1] <= 0: # A buy signal
self.buy(data=self.getdatabyname(d))
else: # We have an open position
if self.regime[d][dtidx][0] <= 0 and self.regime[d][dtidx][-1] > 0: # A sell signal
self.sell(data=self.getdatabyname(d))
datafile = 'GETI-B.ST_daily.csv'
market = datafile.split('-')[0]
ohlc = pd.read_csv(datafile, index_col='date', parse_dates=True,dayfirst=True)
ohlc = ohlc[ohlc['1. open']!= 0]
ohlc.columns = ['open', 'high', 'low', 'close', 'volume']
ohlc = ohlc[['open', 'high', 'low', 'close']]
ohlc= ohlc[ohlc.index > datetime.datetime(2000, 12, 31)]
cerebro_wf = Cerebro(stdstats=False)
cerebro_wf.broker.setcash(1000000)
cerebro_wf.broker.setcommission(0.02)
cerebro_wf.addstrategy(SMACWalkForward,
# Give the results of the above optimization to SMACWalkForward (NOT OPTIONAL)
fast=[int(f) for f in wfdf.fast],
slow=[int(s) for s in wfdf.slow],
start_dates=[sd.date() for sd in wfdf.start_date],
end_dates=[ed.date() for ed in wfdf.end_date])
cerebro_wf.adddata(data)
cerebro_wf.addobserver(AcctValue)
cerebro_wf.addobservermulti(bt.observers.BuySell) # Plots up/down arrows
cerebro_wf.addsizer(PropSizer)
cerebro_wf.addanalyzer(AcctStats)
results = cerebro_wf.run()