Probably some bug in backtester

I got the following fill in one of my backtests:

SCOR
07-11 09:30:00
100,009.6250$

At the same day this stock was trading around the price of 22$.

This 100K filling price might probably come from some bad tick or ticks with a wrong condition.

Comments

  • ptunneyptunney Posts: 246
    edited November 2019

    a) You need to give a full date (including the year) and when there can be confusion, be clear which is the month and which is the date
    b) The order type you used would be helpful, I would assume market.
    c) Depending on the symbol, type of order, time of day, its typical current volume, it is possible that the bid/ask are extremely wide and a bad choice of order would result in a bad fill both in the real world and the simulator. There are a lot of predatory algos out there.

    If I were to assume you actually mean 2018-07-11 (July 11th 2018) and you traded at 9:30 am then you have extremely thin trading. It literally traded 100 shares in that minute and then no more until 9:36.

    The fill of the simulator is effectively warning you that you made a risky trade for that time and that symbol.

    If you have elite I would suggest you write a script to print the trades (in on_trade just print the time, the bid, the ask, the last, the exchange, the trade condition)

    something like this....

    from cloudquant.interfaces import Strategy
    
    class tradetest(Strategy):
    
        @classmethod
        def is_symbol_qualified(cls, symbol, md, service, account):
            return symbol == "SCOR"
    
        def on_trade(self, event, md, order, service, account):
            if event.timestamp > service.time(9,29) and event.timestamp < service.time(9,55):
                print "TRADE {} {} Last {:8.2f}  LastSize {}  Ask {:8.2f}  AskExch {}  AskSize {}  Bid {:8.2f}  BidExch {}  BidSize {} Trade Condition {}".format(service.time_to_string(event.timestamp),self.symbol,md.L1.last,md.L1.last_size,md.L1.ask,md.L1.ask_exchange,md.L1.ask_size,md.L1.bid,md.L1.bid_exchange,md.L1.bid_size,md.L1.trade_condition)
    
  • pp794378pp794378 Posts: 19
    edited November 2019

    Thanks.
    You are right: it happened on 2018 July 11.
    Basically, I was using TWAP trade (from CQ Elite) and it does this job automatically:

    # twap_start = md.market_open_time + service.time_interval(0,0,0,1) # start_time = 5 min before close
        # twap_end = md.market_open_time + service.time_interval(0,10) #15
        # c_twap = order.vwap(self.symbol, quantity, "buy", start_time=twap_start, end_time=twap_end, order_aggression=3)

    So, the logic of this trade is pretty straightforward: we wait until market opens (auction took place) and then fill our size in 10 minutes. I realize that it is possible to have relatively bad fills (like bid-ask spread is 1-dollar wide) but not in the 100K range.

  • ptunneyptunney Posts: 246

    Using someone else algo, be that a TWAP provided by an exchange or a software emulation of such an order type opens you up to potential weird fills. Unless you can see the code you cannot know how it will react in unusual situations.

    This is a NASDAQ symbol, as such its opening trade was bang on 9:30am and was 100 shares, just to open the stock, there was no real opening auction. It is too thin.
    When writing an algo and attempting to work out how to effectively place an order I always ask myself the question, how would I trade it manually.
    And the answer for a symbol this thin would be, I would not trade it. It takes a very particular kind of trader to trade this kind of stock and it is a very dangerous environment for agos. Maybe tighten up your symbol qualification, add in a md.stat.avol check.

Sign In or Register to comment.