Back to Community
Trade at the open slippage model

For those of you who may want to trade at the open, or a fraction of the open to close range:



# Slippage model to trade at the open or at a fraction of the open - close range.  
class TradeAtTheOpenSlippageModel(slippage.SlippageModel):  
    '''Class for slippage model to allow trading at the open  
       or at a fraction of the open to close range.  
    # Constructor, self and fraction of the open to close range to add (subtract)  
    #   from the open to model executions more optimistically  
    def __init__(self, fractionOfOpenCloseRange):

        # Store the percent of open - close range to take as the execution price  
        self.fractionOfOpenCloseRange = fractionOfOpenCloseRange

    def process_order(self, trade_bar, order):  
        openPrice = trade_bar.open_price  
        closePrice = trade_bar.price  
        ocRange = closePrice - openPrice  
        ocRange = ocRange * self.fractionOfOpenCloseRange  
        if (ocRange != 0.0):  
            targetExecutionPrice = openPrice + ocRange  
            targetExecutionPrice = openPrice '\nOrder:{0} open:{1} close:{2} exec:{3} side:{4}'.format(  
            trade_bar.sid.symbol, openPrice, closePrice, targetExecutionPrice, order.direction))

        # Create the transaction using the new price we've calculated.  
        return slippage.create_transaction(  

Example trades:

Order:BND open: 76.30 close: 76.35 exec: 76.305 side: 1.0  
Order:BND open: 76.27 close: 76.10 exec: 76.253 side: 1.0  
Order:BND open: 77.95 close: 77.97 exec: 77.952 side: 1.0  
Order:SPY open:137.24 close:136.99 exec:137.215 side: 1.0  
Order:BND open: 77.34 close: 77.63 exec: 77.369 side:-1.0  
Order:SPY open:132.23 close:132.92 exec:132.299 side: 1.0  
Order:SPY open:131.29 close:131.94 exec:131.355 side:-1.0  
Order:BND open: 78.38 close: 77.85 exec: 78.327 side: 1.0  
Order:BND open: 77.16 close: 76.90 exec: 77.134 side:-1.0  
Order:BND open: 77.52 close: 77.35 exec: 77.503 side: 1.0  
Order:SPY open:139.40 close:139.07 exec:139.367 side: 1.0  
Order:BND open: 77.17 close: 77.10 exec: 77.163 side:-1.0  
Order:SPY open:142.33 close:141.34 exec:142.231 side: 1.0  
Order:SPY open:128.28 close:127.80 exec:128.232 side:-1.0

21 responses

very nice!


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.

Could also just set the fraction to be order.amount/trade_bar.volume, maybe even penalize towards the high/low

Hello Anony,

Perhaps you would be willing to elaborate on the point of this. Does the built-in Quantopian slippage model not work in some cases?


I think the original point was to abuse the slippage model system in order to get better executions by filling them at the open price of the next bar, not the close price :)

NOTE: this OrderAtTodaysClose method doesn't work properly, unfortunately :(

@Anony: yeah for me i want to trade at today's close (to validate my algorithms without additional randomization)

to that effect, here's a slippage model to do that. Please note that you need to use the OrderAtTodaysClose.order_target_percent() method otherwise the price will be the opening of the following day.

class OrderAtTodaysClose:  
    wrapper for ordering, lets the TradeAtTodaysCloseSlippageModel know what today's price was.  
    def __init__(this):  
        this.closePrices = {}  
    def order_target_percent(this,data,sid,percent, limitPrice=None, stopPrice=None):  
        return order_target_percent(sid,percent,limitPrice,stopPrice)  
global orderAtTodaysClose  
orderAtTodaysClose = OrderAtTodaysClose()

class TradeAtTodaysCloseSlippageModel(slippage.SlippageModel):  
    ''' simulate selling immediatly at the close (hack to let interday pretend to be intraday)  
    YOU MUST USE OrderAtTodaysClose.order_target_percent() for this to take effect.  if not, we use the opening price for the following timeslice  
    def __init__(self):  

    def process_order(self, trade_bar, order):          

        #doesn't work with daily  
        #price_history = history(bar_count=2, frequency='1d', field='price')  
        #price = price_history[trade_bar.sid][-2]  
        if orderAtTodaysClose.closePrices.has_key(trade_bar.sid):  
            price = orderAtTodaysClose.closePrices[trade_bar.sid]  
            price = trade_bar.open_price

        return slippage.create_transaction(  

Thanks for the discussion on this item. Have there been any improvements made to Quantopian since the last post to allow someone to set which price is used when executing an order? The system I'm converting from, and all that I've used, allow you to specify the close of the current period, to the open of the next, the close of the next, or often something in between. While I understand the desire to simulate "real trading", the first step in building a system is too test on known data, then start to introduce randomness one step at a time. Further, waiting until the next period's close is not how "real trading" works.

not that I know of, but be aware that my "Trade at today's close" never seemed to work properly. I didn't figure out the issue though, as I just moved to minute-mode instead.

Thanks for the quick reply Jason! I am using minute mode, but for me, a minute makes a huge difference. My average trade is 22 minutes long. Thanks again. It's unfortunate that there is no work around.

Hello Andrew,

Regardless of the data frequency (minute/daily), a custom slippage model should work for you. No time now, but I think that Anony's code above could be tweaked.


@Grant: that's what I was trying to do with the Trade at Today's close code. The problem is you can't transmit any state along with the order, so the slippage model can't know what the previous bar's price was.

I tried working around that with my helper function for ordering, but as I mentioned, it didn't seem to work right.

Jason and Andrew,

Here's an example of how one could adjust the execution price with extrapolation outside the open-close range (e.g. backward into the current period, based on the price trend of the next minute). Presumably, a more sophisticated model could be constructed using the high, low and volume data, as well.


Clone Algorithm
Backtest from to with initial capital
Total Returns
Max Drawdown
Benchmark Returns
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 53a220bc92ccfd0714705693
There was a runtime error.

Grant & Jason,

Thank you very much! I really appreciate your help. As much as I am enjoying Quantopian, as trader and user of technical analysis software for the last 20 years, there are some things that just make me scratch my head. In system development I always start out with only what is known, then introduce variable and randomness one item at a time. Utilizing a random slippage model on a new strategy, as is the Quantopian default, makes no sense to me. Neither does not having an option of what price to use for the backtesting execution. I understand that you don't want newbies to be over excited, but really?

I rappreciate the code and will try it out this evening.

To my knowledge, there is no randomness in the default Quantopian slippage model. Here's the one in zipline (presumably equivalent to the one in the online backtester):

Glancing at it, I think I understand why a custom slippage model can't be made more flexible until some internal changes are made. Based on the way Quantopian works in live trading, it seems overly conservative, since orders are filled at Interactive Brokers asynchronously (i.e. ASAP), rather than after a 1-2 minute delay.


Jason & Andrew,

It's an ugly hack, but it appears one could code the fill price into the number of shares field, when placing the order (if I follow the slippage model). For example:


So, the N's would represent the number of shares (e.g. 100) and the P's would represent the fill price (e.g. $32.5931), with a zero separating them. So, parsing would be done from right to left. When the zero is encountered, it indicates the end of the price field and the start of the number of shares field (including a sign, for long/short).

Then, in the custom slippage model, after parsing, create_transaction would get called using with price = PP.PPPP and amount = NNN.


Hi Jason (and Andrew),

I thought I should point out that ordering using the closing price available during the execution of handle_data is effectively traveling back in time in a backtest simulation (for discussion and some relevant code, see While executing handle_data, only trailing bar data is available. So, when the order is filled, it accesses the OHLCV recent bar data that become available after the execution of handle_data is complete. So, I'm wondering if Anony's slippage model would work for you, by just using the open price? There would still be a slight bias, since in live trading, the order will be filled slightly later by IB (hence Anony's factor of 0.1).

Or am I missing something in your motivation to code your OrderAtTodaysClose slippage model above?


So I gather that the trick is to use:

global priorCloses  

Then you can feed the custom slippage model whatever data you want, correct?

I'm kinda lost regarding the use cases here for minutely bars (I think I understand the daily MOC order simulation). For minutely trading, wouldn't one expect the fill price to be between the open and close of the current (incomplete) bar (i.e. the bar that is being accumulated as handle_data is executed)?


@Grant: I personally like the idea of using "LastClose" for daily mode, but for minute mode, you are right that I'd not use it (but I probably wouldn't bother with trade at the open either)

Here is a slippage model that can be configured to do close/open/lastOpen/ohlc:

class DeluxeSlippage(slippage.SlippageModel):  
    ''' allows customizing slippage if desired, though mostly used for logging your order details to the console  
    priceModel = close/open/ohlc/worst/lastClose  
         lastClose:  uses the closing price of the bar the order is placed.  Usually only useful in daily mode.  to use, you must call updateLastPrices() from your handle_data() at the END of every timestep  
         worst: only useful when running in minute-mode, as in daily mode uses the high/low of the day, which is usually too excessive a slippage for practical purposes'''  
    def __init__(this, volume_limit=.25, price_impact=0.1, priceModel = "close",  logTrades=True):  
        this.volume_limit = volume_limit  
        this.price_impact = price_impact  
        this.priceModel = priceModel  
        this.lastPrices = None  
        this.logTrades = logTrades  
        logger.debug("USING DELUXESLIPPAGE,  PRICEMODEL= {0}".format(this.priceModel));  
    def __processVolumeShareSlippage(this,event,order, targetPrice):  
        '''coppied implementation from VolumeShareSlippage.process_order(), found here:  
        modification:  we return the final (price,volume) tuple for our main .process_order() to use, instead of executing the order  
        RETURNS: final (price,volume) tuple'''  
        max_volume = this.volume_limit * event.volume

        # price impact accounts for the total volume of transactions  
        # created against the current minute bar  
        remaining_volume = max_volume - this.volume_for_bar  
        if remaining_volume < 1:  
            # we can't fill any more transactions  
            return (0.0,0)  
        # the current order amount will be the min of the  
        # volume available in the bar or the open amount.  
        cur_volume = int(min(remaining_volume, abs(order.open_amount)))

        if cur_volume < 1:  
            return (0.0,0)

        # tally the current amount into our total amount ordered.  
        # total amount will be used to calculate price impact  
        total_volume = this.volume_for_bar + cur_volume

        volume_share = min(total_volume / event.volume,  

        simulated_impact = volume_share ** 2 * math.copysign(this.price_impact, order.direction) * targetPrice  
        return (targetPrice + simulated_impact,int(math.copysign(cur_volume, order.direction)))

    def __processPriceModel(this,trade_bar,order):  
        '''sets the price of the trade based on the priceModel picked in the constructor'''

        if this.priceModel == "close":  
            targetPrice = trade_bar.close_price  
        elif this.priceModel == "open":  
            targetPrice = trade_bar.open_price  
        elif this.priceModel == "ohlc":  
            #midpoint, ohlc weighted  
            targetPrice = (targetPrice + trade_bar.open_price + trade_bar.high + trade_bar.low + trade_bar.close_price) / 5  
        elif this.priceModel == "worst":  
            if order.amount < 0:  
                targetPrice = trade_bar.low  
                targetPrice = trade_bar.high  
        elif this.priceModel == "lastClose":  
            sid = trade_bar.sid.sid  
            if this.lastPrices == None:  
                raise Exception("To use the 'lastClose' priceModel, you must call this.updateLastPrices(data) from your handle_data() at the END of every timestep")  
            targetPrice = this.lastPrices[sid]  
            raise Exception("invalid price strategy");

        return targetPrice

    def process_order(this,trade_bar,order):  
        modelPrice = this.__processPriceModel(trade_bar,order)  
        price, volume = this.__processVolumeShareSlippage(trade_bar,order,modelPrice)

        if this.priceModel == "worst":  
            priceSlippage = trade_bar.close_price - price  
            priceSlippage = modelPrice - price  
        volumeSlippage = order.amount - volume    

        if price == 0.0 or volume == 0:  

        #construct our pnl once this transaction is comitted (logged below)  
        pnl = _g.context.portfolio.pnl + (price * order.amount) - (trade_bar.close_price * order.amount)  
        returns = (pnl / _g.context.portfolio.starting_cash ) * 100

        if this.logTrades:  
  "ORDER_COMMITTED: {0} shares {1} @ {2} \n\tv={8} o={4} h={5} l={6} c={7}\n\tSlippage: vol= -{9} price= {3:.3f})\n\tpnl=${10:.0f} ({11:.2f}%)"  
                    .format(volume,trade_bar.sid.symbol,price,priceSlippage, trade_bar.open_price, trade_bar.high, trade_bar.low, trade_bar.close_price, trade_bar.volume,volumeSlippage, pnl,returns))  
        return slippage.create_transaction(trade_bar,  

    def updateLastPrices(this,data):  
        '''To use the "lastClose" priceModel, you must call updateLastPrices() from your handle_data() at the END of every timestep'''  
        if this.lastPrices == None:  
            this.lastPrices = {}  

        for qsec in data:  
            sid = qsec.sid  
            closePrice = data[sid].close_price  

I verified it works for LastClose, though you will need to invoke the .updateLastPrices() method from your handle_data() in order for "lastClose" to work.

Does anyone know if there is a way to force fill's in the research environment. I tried the above example, but was not successful.

@Grant @Anony Mole
When i cloned and run source code .It gives me following error,Anybody can help me?

AttributeError: 'zipline.protocol.BarData' object has no attribute 'openprice'

You are life saver.Thanks