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. -
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 list4
.@曲奇 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 for5000
and then3000
(the 1st two orders apparentely), you cannot then execute an order for3000
.@曲奇 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 inbacktrader/brokers/bbroker.py
565-690 , which isdef 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 runningcash = self._execute(order, cash=cash, position=position) if cash >= 0.0: self.submit_accept(order) continue
- Now, suppose the
cash
is10000
and there are4
orders inself.submitted
which buying5000, 3000, 3000, 1000
. - After each order execute the
cash
is5000, 2000, -1000, -2000
, so the3rd, 4th
orders ismargin
- But actually, the
1st, 2nd, 4th
order can beaccepted
together cause they are9000
in total. - The reason why
4th
order bemargin
is thecash
was not restored after the3th
order runself._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)
- Now, suppose the
-
@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()
-
@曲奇 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 than1000
.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.