For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/
2019-10-02: The community is currently in read-only mode

A mistake when orders submitted



  • I found a mistake when the broker check submitted orders.
    For example if my cash is 10000, and here are 3 buy orders which cost 5000, 3000, 3000, 1000. Only the first two orders can be executed, because the broker will check the cash after execute after each order. The remaining cash will be -1000 after first 3 orders executed so no more order can be executed in this day.


  • administrators

    See, nobody will deny the possibility that backtrader may have bugs, but

    • Where is the code to prove the bug?
    • Where is sample data to prove the bug?
    • Where is a log showing the occurrence of the bug?

    Furthermore, see your own explanation

    @曲奇 said in A mistake when orders submitted:

    and here are 3 buy orders which cost 5000, 3000, 3000, 1000.

    You say there are 3 orders and you list 4.

    @曲奇 said in A mistake when orders submitted:

    Only the first two orders can be executed, because the broker will check the cash after execute after each order

    Indeed, your cash is 10000 and if you buy for 5000 and then 3000 (the 1st two orders apparentely), you cannot then execute an order for 3000.

    @曲奇 said in A mistake when orders submitted:

    The remaining cash will be -1000 after first 3 orders executed so no more order can be executed in this day.

    You just said above that only 2 orders can be executed, but now you say that 3 have been executed.

    See

    • I do understand that you are not a native English speaker

    but

    • Try to make sure that the information you provide is at least consistent.


  • @backtrader Sorry about that.
    The problem may be located in backtrader/brokers/bbroker.py 565-690 , which is

        def check_submitted(self):
            cash = self.cash
            positions = dict()
    
            while self.submitted:
                order = self.submitted.popleft()
    
                if self._take_children(order) is None:  # children not taken
                    continue
    
                comminfo = self.getcommissioninfo(order.data)
    
                position = positions.setdefault(
                    order.data, self.positions[order.data].clone())
    
                # pseudo-execute the order to get the remaining cash after exec
                cash = self._execute(order, cash=cash, position=position)
    
                if cash >= 0.0:
                    self.submit_accept(order)
                    continue
    
                order.margin()
                self.notify(order)
                self._ococheck(order)
                self._bracketize(order, cancel=True)
    

    When multiple stocks are tracked, and I send several buy orders in one day (which means len(self.submitted) > 1 ). This code will check these orders together by running

             cash = self._execute(order, cash=cash, position=position)
    
             if cash >= 0.0:
                    self.submit_accept(order)
                    continue
    
    • Now, suppose the cash is 10000 and there are 4 orders in self.submitted which buying 5000, 3000, 3000, 1000.
    • After each order execute the cash is 5000, 2000, -1000, -2000, so the 3rd, 4th orders is margin
    • But actually, the 1st, 2nd, 4th order can be accepted together cause they are 9000 in total.
    • The reason why 4th order be margin is the cash was not restored after the 3th order run self._execute().

    So I think this code should be

        def check_submitted(self):
            cash = self.cash
            positions = dict()
    
            while self.submitted:
                order = self.submitted.popleft()
    
                if self._take_children(order) is None:  # children not taken
                    continue
    
                comminfo = self.getcommissioninfo(order.data)
    
                position = positions.setdefault(
                    order.data, self.positions[order.data].clone())
    
                # pseudo-execute the order to get the remaining cash after exec
                cash_old = cash
                cash = self._execute(order, cash=cash, position=position)
    
                if cash >= 0.0:
                    self.submit_accept(order)
                    continue
    
                cash =  cash_old
                order.margin()
                self.notify(order)
                self._ococheck(order)
                self._bracketize(order, cancel=True)
    


  • @backtrader , here is my testing code

    import pandas as pds
    import backtrader as bt
    from datetime import datetime
    
    class testStrategy(bt.Strategy):
        def log(self, txt, dt=None):
            dt = dt or self.datas[0].datetime.date(0)
            print('%s, %s' % (dt.isoformat(), txt))
    
        def notify_order(self, order):
            if order.status in [order.Submitted, order.Accepted]:
                return
            if order.status in [order.Completed]:
                if order.isbuy():
                    self.log(
                        'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                        (order.executed.price,
                         order.executed.value,
                         order.executed.comm))
                    self.buyprice = order.executed.price
                    self.buycomm = order.executed.comm
                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)
            elif order.status in [order.Canceled, order.Rejected]:
                self.log('Order Canceled/Rejected')
            elif order.status == order.Margin:
                self.log('Order Margin')
            self.order = None
    
        def next(self):
            self.buy(size=5)
            self.buy(size=3)
            self.buy(size=3)
            self.buy(size=1)
    
            # try order [1000, 5000, 3000, 3000] will get different result
            # self.buy(size=1)
            # self.buy(size=5)
            # self.buy(size=3)
            # self.buy(size=3)
    
    
    if __name__ == '__main__':
        cerebro = bt.Cerebro()
        hs = pds.DataFrame([[1000,1000,1000,1000],[1000,1000,1000,1000]], columns=['open','close','high','close'])
        hs.index = pds.Series([datetime(2000,1,1),datetime(2000,1,2)], name='date')
        print(hs)
        print('----------------------------------------------------------')
        cerebro.adddata(bt.feeds.PandasData(dataname=hs, plot=True, name='HS300'))
        cerebro.broker.setcash(10000.0)
        cerebro.addstrategy(testStrategy)
        results = cerebro.run()
    

  • administrators

    @曲奇 said in A mistake when orders submitted:

    So I think this code should be

    Sorry but you think wrong. The broker is not there to think for you which orders can make it to the market or not. If you have 1000 send orders for a value less than 1000.

    I understand your line of reasoning, but it is the wrong reasoning. If you don't control the value on your side you are making a mistake.



  • @backtrader said in A mistake when orders submitted:

    I understand your line of reasoning, but it is the wrong reasoning. If you don't control the value on your side you are making a mistake.

    Thank you, but this part can cause some confusing problem in some cases. I just want to give a suggestion and maybe we can make it better.


Log in to reply
 

});