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

Code before:
        return self.__dict__


class EquityAndFutureMeta(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_):
        try:
            if EquitySlippageModel in bases and FutureSlippageModel in bases:
                dict_['allowed_asset_types'] = (Equity, Future)
        except NameError:
            # Ignore this check upon creation of the EquitySlippageModel and
            # FutureSlippageModel classes.
            pass
        return super(EquityAndFutureMeta, mcls).__new__(
            mcls, name, bases, dict_,
        )


class EquitySlippageModel(with_metaclass(EquityAndFutureMeta, SlippageModel)):
    """
    Base class for slippage models which only support equities.
    """
    allowed_asset_types = (Equity,)


class FutureSlippageModel(with_metaclass(EquityAndFutureMeta, SlippageModel)):
    """
    Base class for slippage models which only support futures.
    """
    allowed_asset_types = (Future,)


class VolumeShareSlippage(SlippageModel):
    """
    Model slippage as a function of the volume of contracts traded.

Review comment (bug):
The tested behavior looks good to me! 👍 

Implementation-wise I'd probably make this a bit more generic and potentially more robust by de-coupling the metaclass from its current marker instances (`EquitySlippageModel`, `FutureSlippageModel`). The `NameError->pass` makes me nervous that we might mask some unexpected failure.

We can pull out a marker metaclass from this base metaclass and make the base metaclass handle any marker instance, instead of just the two. Then we can add more markers without renaming the metaclass and changing its implementation.

```python
class AssetSlippageMeta(ABCMeta):
    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))
            )
            if allowed_asset_types:
                dict_['allowed_asset_types'] = allowed_asset_types

        return super(AssetSlippageMeta, mcls).__new__(
            mcls, name, bases, dict_,
        )


class AssetSlippageMarker(AssetSlippageMeta):
    pass


class SlippageModel(with_metaclass(AssetSlippageMeta)):
...
class EquitySlippageModel(with_metaclass(AssetSlippageMarker, SlippageModel)):
...
class FutureSlippageModel(with_metaclass(AssetSlippageMarker, SlippageModel)):
...
```

Code after:
        for order in orders_for_asset:
            if order.open_amount == 0:
                continue

            order.check_triggers(price, dt)
            if not order.triggered:
                continue

            txn = None

            try:
                execution_price, execution_volume = \
                    self.process_order(data, order)

                if execution_price is not None:
                    txn = create_transaction(
                        order,
                        data.current_dt,
                        execution_price,
                        execution_volume
                    )

            except LiquidityExceeded:
                break

            if txn:
                self._volume_for_bar += abs(txn.amount)
                yield order, txn

    def __eq__(self, other):
        return self.asdict() == other.asdict()

    def __hash__(self):
        return hash((
            type(self),
            tuple(sorted(iteritems(self.asdict())))
        ))

    def asdict(self):
        return self.__dict__


class EquitySlippageModel(with_metaclass(AssetSlippageMarker, SlippageModel)):
    """
    Base class for slippage models which only support equities.
    """
    allowed_asset_types = (Equity,)


class FutureSlippageModel(with_metaclass(AssetSlippageMarker, SlippageModel)):
    """