Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
The filled price of LimitOrder

if i place a LimitOrder like this:

order_target_percent(symbol(EXPE),-1, style=LimitOrder(107.34, asset=None, exchange=None))  

this order is a short order, the limit price is 107.34, the current price is 105 which is lower than 107.34, but when the LimitOrder is filled 10 minutes later , the Unit Price (filled price ) in Transaction secotr of full backtest is 108.35. the price is better than the limit price.

In a real trade ,if i place a short order in 107.34 which is higher than the current price 105, when the order is filled sevel minutes later, the filled price must be 107.34, so the filled price 108.35 of backtest will not happen in a real live trade.
Is there any way to fix this problem?

7 responses

I think it's a basic error and it's eays to fix it . Quantopian just make the filled price of limit order equal to the limit price given by the limit order.
anyone has some way to adjus it ? thanks

The price at which an order fills, and the quantity of shares filled, is determined by the slippage model (see https://www.quantopian.com/docs/user-guide/tools/algo-api#setting-slippage-and-commissions). The current default is FixedBasisPointsSlippage which models market orders rather well. These are the types of orders placed when using order_optimal_portfolio and therefore is appropriate for contest algos. If that default doesn't model the order type or pattern one has in a specific algo, other slippage methods, or even a custom method, can be specified.

The VolumeShareSlippage model handles limit orders a bit better than the default. This will never fill worse than the limit but, if the market price is better, will fill at that better market price. This can happen in real trading but it's not typical. Therefore, this could be considered an 'optimistic' best fill model. Using this provides the best fill scenario. To use this model, put the following code into the initialize function

    set_slippage(slippage.VolumeShareSlippage(volume_limit=.1, price_impact=0.0))

There isn't a built in slippage model to provide what might be considered a 'pessimistic' worst fill model, but one can write a custom slippage model. Here is a custom slippage class that fills market orders at the bars close price but fills at the limit orders at the limit price.

class VolumeShareSlippage_Pessimistic(slippage.SlippageModel):  
    """  
    This is a simple slippage model which assumes no price impact on market orders.  
    The filled price will equal the bar's close price.  
    It fills all limit orders at the limit price to give a worst case fill price.  
    The max volume is 10% of a bar volume.  
    _________________________  
    Note this does not account for multiple orders being placed in a minute.  
    It also doesn't handle stop orders.  
    """  
    # limit the volume to 10% of a minute bar volume / no price impact  
    volume_limit = 0.1  
    price_impact = 0.0

    def process_order(self, data, order):  
        volume = data.current(order.asset, "volume")  
        price = data.current(order.asset, "close")

        # The fill amount will be the min of the  
        # volume available in the bar or the max_volume.  
        max_volume = self.volume_limit * volume  
        max_fill_volume = int(min(max_volume, abs(order.open_amount)))

        # The fill price will be the limit price for limit orders and the current price for market orders  
        if order.limit:  
            limit_reached = ((order.direction > 0 and price > order.limit) or  
                             (order.direction < 0 and price < order.limit)  
                             )  
            if limit_reached:  
                fill_price = order.limit  
                fill_volume = math.copysign(max_fill_volume, order.direction)  
            else:  
                fill_price = None  
                fill_volume = None

        else:  
            # it's a market order  
            fill_price = price  
            fill_volume = math.copysign(max_fill_volume, order.direction)  
        return (fill_price, fill_volume)

All the basic ordering logic, including the built in slippage models, are open source. I'd encourage everyone to take a look at the code to better understand how orders are filled and backtested. Check out the slippage models here. There are also some other posts on this topic you may want to look at:
https://www.quantopian.com/posts/why-is-the-fill-price-greater-than-the-limit-price-1
https://www.quantopian.com/posts/simulation-of-non-marketable-limit-orders

Attached is an algo to test the various slippage models and shows how to implement a custom slippage model.

Disclaimer

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.

How would one go about filling market orders at the bar's low or high (whichever is worse, depending on the direction of the trade) instead of its close?

(Market orders always cross the spread, while it would be reasonable to assume that half of the time the bar's close is at one side of the spread or the other.)

Btw, a note of caution to anybody using the above slippage model for limit orders to model how a strategy might perform in a retail brokerage account -- even the above fix is optimistic.

  • When you place a limit order, your order sits at the end of the queue of orders at that price. So it's possible shares will transact at that price without you ever getting a fill, because other orders are ahead of you in line.
  • Market makers can "sub-penny" you, meaning if you have a limit order sitting on the book and a matching order comes in, they can cut in line by offering $0.0001 better. On less liquid securities this is the rule rather than the exception.
  • If you are ordering in odd lots (e.g. not multiples of 100 shares), then you are not guaranteed NBBO. Matching orders crossing the spread may not go to you, but to the best round lot sitting on the quote book, even if it is worse. You'll get filled once the entire spread crosses your limit price. Since spreads are typically at least 0.4% wide, you're kind of losing 0.8% on the round trip to the spread. If trade actively, or on securities with super wide spreads, that'll add up quickly!
  • On thinly traded securities, the market will react to your order.

So while in real trading you won't get filled worse (or better) than your limit price on a non-marketable limit order, what happens instead is that often you simply don't get filled, even when shares trade at your limit price.

@Veridian Hawk
Everything you highlight above is true. Good reminder that real life may not be so rosy.

The custom slippage model simply returns two numbers - the fill price and the fill shares. It has access to all the regular data.current type methods. So to fill a long or short order at the low or high, respectively, simply use a condition to determine the order type and return the appropriate value. Something like this should work


class VolumeShareSlippage_Pessimistic(slippage.SlippageModel):  
    """  
    This is a simple slippage model which assumes lowest price on market orders.  
    The filled price will equal the bar's low or high (depending on long/short).  
    It fills all limit orders at the limit price to give a worst case fill price.  
    The max volume is 10% of a bar volume.  
    _________________________  
    Note this does not account for multiple orders being placed in a minute.  
    It also doesn't handle stop orders correctly.  
    """  
    # limit the volume to 10% of a minute bar volume / no price impact  
    volume_limit = 0.1  
    price_impact = 0.0

    def process_order(self, data, order):  
        volume = data.current(order.asset, "volume")  
        price = data.current(order.asset, "close")  
        low_price = data.current(order.asset, "low")  
        high_price = data.current(order.asset, "high")

        # The fill amount will be the min of the  
        # volume available in the bar or the max_volume.  
        max_volume = self.volume_limit * volume  
        max_fill_volume = int(min(max_volume, abs(order.open_amount)))

        # The fill price will simply be the current price for market orders  
        # For limit orders it will be the limit price  
        if order.limit:  
            limit_reached = ((order.direction > 0 and price > order.limit) or  
                             (order.direction < 0 and price < order.limit)  
                             )  
            if limit_reached:  
                fill_price = order.limit  
                fill_volume = math.copysign(max_fill_volume, order.direction)  
            else:  
                fill_price = None  
                fill_volume = None  
        else:  
           # Market order  
            if order.direction > 0:  
                # long  
                fill_price = low_price  
                fill_volume = math.copysign(max_fill_volume, order.direction)  
            else:  
                # short  
                fill_price = high_price  
                fill_volume = math.copysign(max_fill_volume, order.direction)  
        return (fill_price, fill_volume)

I didn't thoroughly test to see what happens if there aren't any trades during a minute (ie high, low, close = nan). May want to check for that before using?

Thanks, Dan. I meant flipped (buy high, sell low, as if one were crossing a spread) as so:

           # Market order  
            if order.direction > 0:  
                # long  
                fill_price = high_price  
                fill_volume = math.copysign(max_fill_volume, order.direction)  
            else:  
                # short  
                fill_price = low_price  
                fill_volume = math.copysign(max_fill_volume, order.direction)  

The results are depressingly sobering. Probably (hopefully) this is simply much too pessimistic an assumption. But it is remarkable how much execution quality matters.

@Dan Whitnable
Thank you Dan, it's very helpful