Check the order status of Multiple Stocks
-
Hi there,
My strategy has a datas set with many stocks.
In the next() module, how do I check for the order status of EACH stock as I am looping through them?
I know that if I only had one stock I would use:
if self.isorder(): return
However each stock for me is referenced to as
d
ford in self.datas
.If possible, I would also appreciate advice on how to update the following
notify_order
andnotify_trade
code to work with these multiple dataframes "d
":def notify_order(self, order): # Methods (def in Class) have access to all the data contained on the instance of the object; they can access and modify anything previously set on self. 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, order.Canceled, order.Margin]: if order.isbuy(): self.buyprice = order.executed.price self.buycomm = order.executed.comm self.log('BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %(self.buyprice,order.executed.value,self.buycomm)) else: # Sell self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %(order.executed.price,order.executed.value, order.executed.comm)) self.bar_executed = len(self) # i.e. num bars elapsed. If using daily data => this is number of days stock held. self.order = None # Write down: no pending order def notify_trade(self, trade): if trade.isclosed: self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %(trade.pnl, trade.pnlcomm))
Thank you!!!!
cwe -
Could you please give a reference where
self.isorder
is shown in docs or examples?
I didn't see such check in there, maybe I missed it. -
it was in one of the doc examples or a blog post... cant remember which :D
Seems like a useful functionality, but only if I can apply it with multiple dataframes!!
-
@cwse said in Check the order status of Multiple Stocks:
if self.isorder():
returnThis is certainly unknown as pointed out by @ab_trader
order
instances carry a reference to the data feed which is targeted by the order. The attribute isdata
.Data feeds also carry an
_id
attribute holding a scalar value for ease of comparison (in case operator overloading could get in the way when looking to compare to data feed instances) -
@ab_trader and @backtrader, appologies for the typo, I meant:
if self.order: return
Nonetheless my question still stands. When I create orders I do it as follows:
self.order = self.buy(data=d)
so how can I check if a specific stock order is outstanding using
self.order
?
Can I do this?if self.order(data=d)
... to check if a specific STOCK has an order in process?
Thanks!
-
self.order
in that context is just a member attribute in a specificStrategy
subclass which is used as a sentinel and starts withNone
and is eitherNone
for not having an order active or contains anOrder
instance which evaluates toTrue
The question still stands, but the answer was already provided.
Order
instances contain thedata
for which it was created as a parameter (thanks to python overriding can also be reached as an attribute)Using object identification or the allocated
_id
as pointed out above:if self.order.data is d
orif self.order.data._id == d._id
-
Thanks @backtrader, but if I have many orders processing, that logic wouldn't check through all of them, to make sure that this particular stock doesn't have an outstanding order would it? The way the code is written it would seem that it just checks a random order to see if it equals this one, but doesnt check all orders?
-
d
stands for one of the data feeds. You would be looping over the data feeds inself.data
to check for which one this order is.There is no solution without looping if you have multiple stocks. At some point you have to go over each data feed to check for what you specifically want to know about the things surrounding that particular stock, be it price, be it indicators associated to it, be it orders.
-
Hi Backtrader, got an error on that:
File "T:/Google Drive/PyCharm/Backtrader.py", line 139, in next if self.order.data is d: #if self.order.data is d or if self.order.data._id == d._id dont re-order if have a pending order AttributeError: 'NoneType' object has no attribute 'data'
-
@cwse said in Check the order status of Multiple Stocks:
AttributeError: 'NoneType' object has no attribute 'data'
-
This is what I have in next() and that error is occuring:
def next(self): # Methods (def in Class) have access to all the data contained on the instance of the object; they can access and modify anything previously set on self. for i, d in enumerate(d for d in self.datas if len(d)): # Loop through Universe of Stocks if self.broker.getposition(d).size: self.log('Stock: %s, Close: %.2f, score: %.2f, posn: %.2f' % (d._name,d.close[0], d.lines.TOTAL_SCORE[0], self.broker.getposition(d).size)) if d.lines.TOTAL_SCORE[0] >= args.buyscore: if self.order.data is d: #if self.order.data is d or if self.order.data._id == d._id dont re-order if have a pending order return # BUY! self.log('CREATE BUY: %s, Close: %.2f, score: %.2f' %(d._name,d.close[0],d.lines.TOTAL_SCORE[0])) # Keep track of the created order to avoid a 2nd order self.order = self.buy(data=d) if d.lines.TOTAL_SCORE[-1] >= args.buyscore and d.lines.TOTAL_SCORE[0] < args.buyscore: if self.order.data is d: #if self.order.data is d or if self.order.data._id == d._id dont re-order if have a pending order return # SELL! i.e. yesterday we owned it(with all possible default parameters) self.log('CREATE SELL: %s, Close: %.2f, score: %.2f' %(d._name,d.close[0],d.lines.TOTAL_SCORE[0])) # Keep track of the created order to avoid a 2nd order self.order = self.sell(data=d)
-
@backtrader said in Check the order status of Multiple Stocks:
@cwse said in Check the order status of Multiple Stocks:
AttributeError: 'NoneType' object has no attribute 'data'
-
Sorry? Could you please elaborate?
-
NoneType
... you are checking something which isNone
and that contains no attributes.Null
pointer ... basics of Python -
Yes, because the
__init__(self)
sets it to Noneif self.order.data is d:
... and you told me to check in next() like so:
if self.order.data is d:
I am doing my best to read in between the lines here, but It would be awesome if you could just tell me what I should be coding here and save us all the time!
-
@cwse
I use the following for multiple symbolsin your init where you define self.order = None, make it a dict , self.order = {}
def __init__(self): self.order = {} for i, d in enumerate(d for d in self.datas if len(d)): self.order[d._name] = None def notify(self, order): # standard code for notification self.order[order.data._name] = None def next(self): for i, d in enumerate(d for d in self.datas if len(d)): if not self.order[d._name]: continue
-
Thank you @BRAD LLOYD, that seems like a great way to tackle it.
I implemented your code in my strategy, however I am getting a key error for one of my stock data frames in
next()
, any idea why this might be? The dictionary key should be established in INIT?
Perhaps the self.order dict needs to be defined prior to init?I have also assumed the dictionary logic applied to
self.order
, should also apply toself.buyprice
andself.buycomm
... it would be great to get your thoughts on this!I have copied my full strategy below in case you can see any obvious errors.
Thank you,
CWEclass TestStrategy(bt.Strategy): # Attributes which apply to ALL instances of a Class object are defined here, i.e. prior to __init__ args = parse_args() params = ( ('printlog', True), ) def log(self, txt, dt=None, doprint=False): ''' Logging function for this strategy''' if self.p.printlog or doprint: # NB: self.p = self.params dt = dt or self.datas[0].datetime.date(0) print('%s, %s' % (dt.isoformat(), txt)) def __init__(self): # __init__ = creates object defined by the Class. INITIALISES the object - sets all attributes # To keep track of pending orders and buy price/commission self.order = {} self.buyprice = {} self.buycomm = {} for i, d in enumerate(d for d in self.datas if len(d)): self.order[d._name] = None self.buyprice[d._name] = None self.buycomm[d._name] = None def notify_order(self, order): # Methods (def in Class) have access to all the data contained on the instance of the object; they can access and modify anything previously set on self. if order[order.data._name].status in [order[order.data._name].Submitted, order[order.data._name].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[order.data._name].status in [order[order.data._name].Completed, order[order.data._name].Canceled, order[order.data._name].Margin]: if order[order.data._name].isbuy(): self.buyprice[order.data._name] = order[order.data._name].executed.price self.buycomm[order.data._name] = order[order.data._name].executed.comm self.log('BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %(self.buyprice,order.executed.value,self.buycomm)) else: # Sell self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %(order.executed.price,order.executed.value, order.executed.comm)) self.bar_executed[order.data._name] = len(self) # i.e. num bars elapsed. If using daily data => this is number of days stock held. self.order[order.data._name] = None #self.order = None # Write down: no pending order def notify_trade(self, trade): if trade.isclosed: self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %(trade.pnl, trade.pnlcomm)) def prenext(self): ''' overrides PRENEXT() so that the "NEXT()" calculations run regardless of when each data date range starts. Typically PRENEXT would stop NEXT occuring until the minimum period of an indicator has occured, or all dataframe LINES are running. ''' self.next() def next(self): # Methods (def in Class) have access to all the data contained on the instance of the object; they can access and modify anything previously set on self. for i, d in enumerate(d for d in self.datas if len(d)): # Loop through Universe of Stocks if self.broker.getposition(d).size: self.log('Stock: %s, Close: %.2f, score: %.2f, posn: %.2f' %(d._name,d.close[0], d.lines.TOTAL_SCORE[0], self.broker.getposition(d).size)) if d.lines.TOTAL_SCORE[0] >= args.buyscore: if self.order[d._name]: return # BUY! self.log('CREATE BUY: %s, Close: %.2f, score: %.2f' %(d._name,d.close[0],d.lines.TOTAL_SCORE[0])) self.order[d._name] = self.buy(data=d) if d.lines.TOTAL_SCORE[-1] >= args.buyscore and d.lines.TOTAL_SCORE[0] < args.buyscore: if self.order[d._name]: return # SELL! i.e. yesterday we owned it(with all possible default parameters) self.log('CREATE SELL: %s, Close: %.2f, score: %.2f' %(d._name,d.close[0],d.lines.TOTAL_SCORE[0])) self.order[d._name] = self.sell(data=d) def stop(self): #Print TOTAL PORTFOLIO VALUE self.log('Ending Value %.2f' %(self.broker.getvalue()),doprint=True)
-
@cwse
I create the self.order dict in init and don't have a problem
you would have to run it in debug and figure out what key it fails onthe only thing I could see is that my loop is different as I pass all my symbols as param called portfolio and then just do loop that is
for symbol in self.p.portfolio:
-
@Brad-Lloyd sorry I think you cut the code off short there? what is the portoflio part / loop for and how does it work?
Cheers,
-
I was just saying I did the loop different using 'for' instead of enumerate
but I do something like thisparams = ('portfolio', ['AAPL', 'GOOG', 'SPY', 'IWM', 'EEM']) def next(self): for symbol in self.p.portfolio: d = self.getdatabyname(symbol) if self.order[symbol]: continue if self.signal: self.order[symbol] = self.buy(d, size=self.trade_size[symbol])