Perhaps the FIFO order queue is maintained for each stock? And because you have more than one stock, the order of execution is defined by the list of stocks and not by the placement of buy and sell orders (the FIFO queue is valid only for 1 stock, not for a list of stocks)?
Posts made by Batman
RE: Buy and Sell Orders Execution order
RE: Repeated days in multi symbol strategy
Many thanks for the explanation! It is now clear!
I'm realizing a multi symbol strategy can create a complex synchronization scenario. It is not enough to just copy a “single symbol” strategy, you need to pay attention to the many details and situations that can occur.
Thank you very much!
Repeated days in multi symbol strategy
I implemented a multi-symbol mean reversion strategy and the results were much different than the strategy with only one symbol. I was suspicious about the results and found that the system was repeating days in the simulation.
The multi-symbol strategy processes multiple feeds, in the format:
def next(self): … for data in self.datas: # Operations
While reviewing the CSV files of the feeds, I discovered two issues:
- Some days were repeated
- Some days were absent in some feeds (for example: in 100 stocks, there were some days when 1 stock had no trading)
I corrected the feeds with repeated days, but cannot do the same for the problem of absent data.
This lack of synchronization in the dates of the feeds can make a lot of difference in the result, because Backtrader needs to be synchronized for every day in the multi symbol simulation.
I did not understand it well how Backtrader "repeats" days if the feeds do not have da same days for all feeds, but it seems like that in cerebro.py ‘datas’ is synched early in the "_runonce (self, runstrats ) ":
while True: # Check next incoming date in the datas dts = [d.advance_peek() for d in datas] dt0 = min(dts)
See that ‘dt0’ receives the "minimum feed". But it looks like this causes a day that has already been processed to be processed again if any item in 'datas' faild to 'advance'.
I tried putting a test at the beginning of the strategy to check if the days are repeated:
def __init__(self): ... self.last_day = None self.current_day = None def next(self): self.last_day = self.current_day self.current_day = self.data.datetime.datetime(0) if self.current_day == self.last_day: print("Repeated day" + str(self.current_day)) return
But that does not solve the problem.
RE: Don't start next() until specific date
I had a similar problem and resolved following way.
First, there is a base strategy class that can verify if date is within interval:
class StrategyBase(bt.Strategy): params = ( ('start_date', None), ('end_date', None), ) def within_range(self): date = self.data.datetime.datetime(0) if date >= self.p.start_date and date <= self.p.end_date: return True else: return False
The methold within_range() is called in self() of derived class:
def next(self): if not self.within_range(): return
Then I have a function to adjust the dates that will be loaded from the feed:
def adjust_start_date(date, *params): maximum = 0 for p in params: if isinstance(p, int): if p > maximum: maximum = p # For every week there are only 5 trading days (stocks) days = math.ceil(maximum * 7/5) adjusted = date - datetime.timedelta(days = days) return adjusted
This function is used to adjust the date of the feed. So there are two start dates: one for the feed, other for the strategy:
feed_start_date = adjust_start_date(start_date, param1, param2, param3) cerebro.adddata(bt.feeds.GenericCSVData(fromdate = feed_start_date, todate=end_date)) cerebro.addstrategy(Strategy, param1 = param1, param2 = param2, param3 = param3, start_date = feed_start_date, end_date = end_date )
RE: Limit order with a day valid never gets executed? #220
I had a similar problem and resolved by canceling the pending order every and then recreating it. This way there's no "weekend problem" when the order for next day is more than one day ahead.
def next(self): self.cancel(self.order) ... if not self.position: self.order = self.buy(price = buy_price, exectype = bt.Order.Limit) else: self.order = self.close(price = sell_price, exectype = bt.Order.Limit)