Limit orders

When doing a limit order with order.algo_buy() does the order only execute once the limit price is touched meaning the possibility of getting a better than limit price is not possible? I'm debugging with a portfolio of 60 changing each month and am noticing only price slippage against me and none helping me when doing limit orders but doing the same strategy with market orders gives me a mix of price slippage for and against me. Any thoughts on why this is?

Comments

  • ptunneyptunney Posts: 246
    edited May 2019

    That is a complicated question to answer for many reasons.

    In order to answer specifically I would need to know if you are running CQ Lite or CQ Elite (as the behaviour is different) and if you are using a specific Limit order type or just "limit".

    Filling an order in a simulation is never going to be close to accurate, even a market order. The only order you can rely upon with a modicum of confidence is a Market on Open or a Market on Close order and even then it is dependent upon the order size you are requesting.

    I have run many models using purely "market order simulation" and upon running live had slippage because of my order's impact on the market. Even running a backtest on a day when my model was actually trading and the same code placed trades into the market does not mean that I match with my backtest. The backtest has parameters for Latency for Market Data, Ack and Fill which are entered by the user but in real life vary dramatically depending up on time of day and market conditions. So any simulated fill, even of a market order in a liquid stock with a tiny spread should be taken with a pinch of salt.

    Now, with regard to your specific question:

    The documentation has details of the logic used for fills for order types for Lite and Elite.

    For a plain Limit order on **Lite **it defines the default logic as follows...
    If the order is marketable, it is filled at the limit if the limit crosses the bid (selling) or ask (buying). If the order is not marketable, it is compared to the minute-bar highest bid (if selling) or the minute-bar lowest ask (if buying). If the high bid or low ask crosses our limit, the order is filled at the limit. The number of shares filled is the symbol's bid_size * lot_size (for selling) or ask_size * lot_size (for buying).

    For a plain Limit order on **Elite **it defines the logic as follow...
    The order is filled at the limit if the bid (selling) or ask (buying) locks or crosses the limit. The number of shares filled is the symbol's bid_size * lot_size (for selling) or ask_size * lot_size (for buying).

    Hopefully this helps.

  • aj165602aj165602 Posts: 105

    On a related theme, if someone builds models using MOO or MOC orders, would they eventually need to re-write the code to place orders during regular hours? Put another way, do MOO and MOC orders limit the amount of capital that can support a strategy?

  • ptunneyptunney Posts: 246
    edited May 2019

    MOO and MOC orders are submitted in the 15 mins prior to the open and close. As orders come in the anticipated opening/closing price is adjusted.

    If your order is small compared to the overall opening or closing print ('O' in md.L1.trade_condition, '6' in md.L1.trade_condition in on_trade) then you will have no real impact on the closing price. But you do not know the opening/closing print size until it occurs. Catch 22. You can identify the typical opening/closing size. Or you can base it on a percentage of the average volume or average opening/closing minute volume.

    I had a colleague who argued that you should never submit an order of larger than 10% of the typical opening/closing print volume.

    But there is a reason all the hedge/pension funds trade on the closing print.. it absolves them of responsibility for the quality of the order execution.. No-one can tell them they could have executed the order better and got a better price. So if the big guys do it that way then why shouldn't you?

    Also, you can then backtest your strategies using nothing more than daily opening closing prices - which can be super fast. This is how a lot of people can say "I wrote my own backtesting system", they just download daily prices for all symbols they are interested in from Yahoo and boom.

    So, sizing up depends on which symbols you are trading and how much of their typical size you are trying to take.

    All model sizing is as much art as it is science. You can never know exactly how it will play out until you are in the market and even then you dont know how it would have played out if you had NOT been there!

    But if you are planning to trade a thin stock and you want more size than the open/close will give you without causing the price to move then you could always spread your order out, as you say, in market hours using a VWAP or TWAP order.

    I have had many models that originally had a MOC exit which I have switched to a Market/TWAP/VWAP order in the run up to 3:45pm EST which have then performed better!

  • aj165602aj165602 Posts: 105

    Thanks! That's a very useful answer!

  • tf591404tf591404 Posts: 10

    Hello,
    Thanks for the in depth response. I have a few questions. First, is Elite Cloud Quants premium subscription and if so what are the advantages of getting it? Also it seems with what you've described both lite and elite limit orders execute once the limit price is touched and not at price advantages (say when I'm buying the current price is lower than my limit order in which I would like to buy at the better price than my limit price), am I reading this correctly? I'm trying to avoid slippage but am getting considerably worse results using limit orders when compared to market orders. I think if what you are saying is correct this makes sense in that the possibility of getting slippage going in my favor is not possible with limit orders.

  • ptunneyptunney Posts: 246

    You will get filled on the price you pass and no better.

    Elite is not subscription, if you have run some backtests then you will have received an invite via E-mail to upgrade to Elite.
    I would suggest you check your emails for April 7 or May 13, follow the link, fill in the form and we can upgrade your account.

    Once you have Elite you can run this and you will see everything in the console...

    from cloudquant.interfaces import Strategy, Event
    import ktgfunc
    
    class FillLogic(Strategy):
        @classmethod
    
        def is_symbol_qualified(cls, symbol, md, service, account):
            return md.symbol == "AMZN"
    
        def on_start(self, md, order, service, account):
            service.clear_event_triggers()
            service.add_event_trigger([md.symbol], [Event.TRADE, Event.FILL])
            self.status = 0
    
        def on_trade(self, event, md, order, service, account):
            if account[self.symbol].position.shares == 0 and self.status == 0 :
                order.algo_buy( md.symbol, 'limit', 'init', order_quantity=100, price=round(md.L1.bid-0.01,2) )
                mybar = md.bar.minute(-1)
                print "{} Buy  Limit Order: Entered At {:8.2f}  Last {:8.2f}  Bid {:8.2f}  Ask {:8.2f}".format(service.time_to_string(event.timestamp,format("%H:%M:%S.%f")),md.L1.bid-0.01,md.L1.last,md.L1.bid,md.L1.ask)
                self.status = 1
    
        def on_fill(self, event, md, order, service, account):
            if event.state == 'F' and not account[self.symbol].position.shares and self.status==2:
                print "{} Sell Limit Fill : Fill Price {:8.2f}  Last {:8.2f}  Bid {:8.2f}  Ask {:8.2f}".format(service.time_to_string(event.timestamp,format("%H:%M:%S.%f")),event.price,md.L1.last, md.L1.bid,md.L1.ask)
                print
                self.status = 0
    
            if event.state == "F" and account[self.symbol].position.shares > 0 and self.status == 1:
                print "{} Buy  Limit Fill : Fill Price {:8.2f}  Last {:8.2f}  Bid {:8.2f}  Ask {:8.2f}".format(service.time_to_string(event.timestamp,format("%H:%M:%S.%f")),event.price,md.L1.last, md.L1.bid,md.L1.ask)
                print
                order.algo_sell( md.symbol, 'limit', 'exit', price=round(md.L1.ask+0.01,2) )
                print "{} Sell Limit Order: Entered At {:8.2f}  Last {:8.2f}  Bid {:8.2f}  Ask {:8.2f}".format(service.time_to_string(event.timestamp,format("%H:%M:%S.%f")),md.L1.ask+0.01,md.L1.last,md.L1.bid,md.L1.ask)
                self.status = 2
    
Sign In or Register to comment.