Repository: quantopian/zipline
File: zipline/finance/slippage.py (Python)

Code before:

    order: zipline.finance.order.Order
        The order whose limit price to check.

    Returns
    -------
    bool: Whether the fill price is above the limit price (for a buy) or below
    the limit price (for a sell).
    """
    if order.limit:
        # this is tricky! if an order with a limit price has reached
        # the limit price, we will try to fill the order. do not fill
        # these shares if the impacted price is worse than the limit
        # price. return early to avoid creating the transaction.

        # buy order is worse if the impacted price is greater than
        # the limit price. sell order is worse if the impacted price
        # is less than the limit price
        if (order.direction > 0 and fill_price > order.limit) or \
                (order.direction < 0 and fill_price < order.limit):
            return True

    return False


class SlippageModelMeta(ABCMeta):
    """
    This metaclass allows users to create custom slippage models that support
    both equities and futures by subclassing EquityFutureModel and
    FutureSlippageModel together.

    Take for example the following custom model:

        class MyCustomSlippage(EquitySlippageModel, FutureSlippageModel):
            def process_order(self, data, order):
                ...

    Ordinarily the first parent class in the MRO ('EquitySlippageModel' in this
    example) would override the 'allowed_asset_types' class attribute to only
    include equities. However, this is probably not the expected behavior for a
    reasonable user, so the metaclass will handle this specific case by
    manually allowing both equities and futures for the class being created.
    """

    def __new__(mcls, name, bases, dict_):
        if 'allowed_asset_types' not in dict_:
            allowed_asset_types = tuple(
                chain.from_iterable(
                    base_class.allowed_asset_types
                    for base_class in bases
                    if isinstance(base_class, AssetSlippageMarker)

Review comment (suggestion):
@richafrank Thanks for the feedback, I updated based on your comments. I also decided to call this `SlippageModelMeta` instead of `AssetSlippageMeta` since we are now using it as the metaclass for the core `SlippageModel` class, but let me know if you have feelings against this.

Code after:

    order: zipline.finance.order.Order
        The order whose limit price to check.

    Returns
    -------
    bool: Whether the fill price is above the limit price (for a buy) or below
    the limit price (for a sell).
    """
    if order.limit:
        # this is tricky! if an order with a limit price has reached
        # the limit price, we will try to fill the order. do not fill
        # these shares if the impacted price is worse than the limit
        # price. return early to avoid creating the transaction.

        # buy order is worse if the impacted price is greater than
        # the limit price. sell order is worse if the impacted price
        # is less than the limit price
        if (order.direction > 0 and fill_price > order.limit) or \
                (order.direction < 0 and fill_price < order.limit):
            return True

    return False


class SlippageModelMeta(ABCMeta):
    """
    This metaclass allows users to create custom slippage models that support
    both equities and futures by subclassing EquityFutureModel and
    FutureSlippageModel together.

    Take for example the following custom model:

        class MyCustomSlippage(EquitySlippageModel, FutureSlippageModel):
            def process_order(self, data, order):
                ...

    Ordinarily the first parent class in the MRO ('EquitySlippageModel' in this
    example) would override the 'allowed_asset_types' class attribute to only
    include equities. However, this is probably not the expected behavior for a
    reasonable user, so the metaclass will handle this specific case by
    manually allowing both equities and futures for the class being created.
    """

    def __new__(mcls, name, bases, dict_):
        if 'allowed_asset_types' not in dict_:
            allowed_asset_types = tuple(
                chain.from_iterable(
                    marker.allowed_asset_types
                    for marker in bases
                    if isinstance(marker, AssetSlippageMarker)