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 for d in self.datas.

    If possible, I would also appreciate advice on how to update the following notify_order and notify_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!!


  • administrators

    @cwse said in Check the order status of Multiple Stocks:

    if self.isorder():
    return

    This 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 is data.

    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!


  • administrators

    self.order in that context is just a member attribute in a specific Strategy subclass which is used as a sentinel and starts with None and is either None for not having an order active or contains an Order instance which evaluates to True

    The question still stands, but the answer was already provided. Order instances contain the data 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 or if 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?


  • administrators

    d stands for one of the data feeds. You would be looping over the data feeds in self.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'
    

  • administrators

    @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)
    

  • administrators

    @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?


  • administrators

    NoneType... you are checking something which is None and that contains no attributes. Null pointer ... basics of Python



  • Yes, because the __init__(self) sets it to None if 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 symbols

    in 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 to self.buyprice and self.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,
    CWE

    class 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 on

    the 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 this

    params = ('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])

Log in to reply
 

Looks like your connection to Backtrader Community was lost, please wait while we try to reconnect.