#
# Copyright 2016 Quantopian, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from abc import abstractmethod
from collections import defaultdict
from toolz import merge
from zipline.assets import Equity, Future
from zipline.finance.constants import FUTURE_EXCHANGE_FEES_BY_SYMBOL
from zipline.finance.shared import AllowedAssetMarker, FinancialModelMeta
from zipline.utils.dummy import DummyMapping
DEFAULT_PER_SHARE_COST = 0.001 # 0.1 cents per share
DEFAULT_PER_CONTRACT_COST = 0.85 # $0.85 per future contract
DEFAULT_PER_DOLLAR_COST = 0.0015 # 0.15 cents per dollar
DEFAULT_MINIMUM_COST_PER_EQUITY_TRADE = 0.0 # $0 per trade
DEFAULT_MINIMUM_COST_PER_FUTURE_TRADE = 0.0 # $0 per trade
[docs]class CommissionModel(metaclass=FinancialModelMeta):
"""Abstract base class for commission models.
Commission models are responsible for accepting order/transaction pairs and
calculating how much commission should be charged to an algorithm's account
on each transaction.
To implement a new commission model, create a subclass of
:class:`~zipline.finance.commission.CommissionModel` and implement
:meth:`calculate`.
"""
# Asset types that are compatible with the given model.
allowed_asset_types = (Equity, Future)
[docs] @abstractmethod
def calculate(self, order, transaction):
"""
Calculate the amount of commission to charge on ``order`` as a result
of ``transaction``.
Parameters
----------
order : zipline.finance.order.Order
The order being processed.
The ``commission`` field of ``order`` is a float indicating the
amount of commission already charged on this order.
transaction : zipline.finance.transaction.Transaction
The transaction being processed. A single order may generate
multiple transactions if there isn't enough volume in a given bar
to fill the full amount requested in the order.
Returns
-------
amount_charged : float
The additional commission, in dollars, that we should attribute to
this order.
"""
raise NotImplementedError("calculate")
class NoCommission(CommissionModel):
"""Model commissions as free.
Notes
-----
This is primarily used for testing.
"""
@staticmethod
def calculate(order, transaction):
return 0.0
# todo: update to Python3
class EquityCommissionModel(CommissionModel, metaclass=AllowedAssetMarker):
"""
Base class for commission models which only support equities.
"""
allowed_asset_types = (Equity,)
# todo: update to Python3
class FutureCommissionModel(CommissionModel, metaclass=AllowedAssetMarker):
"""
Base class for commission models which only support futures.
"""
allowed_asset_types = (Future,)
def calculate_per_unit_commission(
order, transaction, cost_per_unit, initial_commission, min_trade_cost
):
"""
If there is a minimum commission:
If the order hasn't had a commission paid yet, pay the minimum
commission.
If the order has paid a commission, start paying additional
commission once the minimum commission has been reached.
If there is no minimum commission:
Pay commission based on number of units in the transaction.
"""
additional_commission = abs(transaction.amount * cost_per_unit)
if order.commission == 0:
# no commission paid yet, pay at least the minimum plus a one-time
# exchange fee.
return max(min_trade_cost, additional_commission + initial_commission)
else:
# we've already paid some commission, so figure out how much we
# would be paying if we only counted per unit.
per_unit_total = (
abs(order.filled * cost_per_unit)
+ additional_commission
+ initial_commission
)
if per_unit_total < min_trade_cost:
# if we haven't hit the minimum threshold yet, don't pay
# additional commission
return 0
else:
# we've exceeded the threshold, so pay more commission.
return per_unit_total - order.commission
[docs]class PerShare(EquityCommissionModel):
"""
Calculates a commission for a transaction based on a per share cost with
an optional minimum cost per trade.
Parameters
----------
cost : float, optional
The amount of commissions paid per share traded. Default is one tenth
of a cent per share.
min_trade_cost : float, optional
The minimum amount of commissions paid per trade. Default is no
minimum.
Notes
-----
This is zipline's default commission model for equities.
"""
def __init__(
self,
cost=DEFAULT_PER_SHARE_COST,
min_trade_cost=DEFAULT_MINIMUM_COST_PER_EQUITY_TRADE,
):
self.cost_per_share = float(cost)
self.min_trade_cost = min_trade_cost or 0
def __repr__(self):
return (
"{class_name}(cost_per_share={cost_per_share}, "
"min_trade_cost={min_trade_cost})".format(
class_name=self.__class__.__name__,
cost_per_share=self.cost_per_share,
min_trade_cost=self.min_trade_cost,
)
)
def calculate(self, order, transaction):
return calculate_per_unit_commission(
order=order,
transaction=transaction,
cost_per_unit=self.cost_per_share,
initial_commission=0,
min_trade_cost=self.min_trade_cost,
)
class PerContract(FutureCommissionModel):
"""
Calculates a commission for a transaction based on a per contract cost with
an optional minimum cost per trade.
Parameters
----------
cost : float or dict
The amount of commissions paid per contract traded. If given a float,
the commission for all futures contracts is the same. If given a
dictionary, it must map root symbols to the commission cost for
contracts of that symbol.
exchange_fee : float or dict
A flat-rate fee charged by the exchange per trade. This value is a
constant, one-time charge no matter how many contracts are being
traded. If given a float, the fee for all contracts is the same. If
given a dictionary, it must map root symbols to the fee for contracts
of that symbol.
min_trade_cost : float, optional
The minimum amount of commissions paid per trade.
"""
def __init__(
self,
cost,
exchange_fee,
min_trade_cost=DEFAULT_MINIMUM_COST_PER_FUTURE_TRADE,
):
# If 'cost' or 'exchange fee' are constants, use a dummy mapping to
# treat them as a dictionary that always returns the same value.
# NOTE: These dictionary does not handle unknown root symbols, so it
# may be worth revisiting this behavior.
if isinstance(cost, (int, float)):
self._cost_per_contract = DummyMapping(float(cost))
else:
# Cost per contract is a dictionary. If the user's dictionary does
# not provide a commission cost for a certain contract, fall back
# on the pre-defined cost values per root symbol.
self._cost_per_contract = defaultdict(
lambda: DEFAULT_PER_CONTRACT_COST, **cost
)
if isinstance(exchange_fee, (int, float)):
self._exchange_fee = DummyMapping(float(exchange_fee))
else:
# Exchange fee is a dictionary. If the user's dictionary does not
# provide an exchange fee for a certain contract, fall back on the
# pre-defined exchange fees per root symbol.
self._exchange_fee = merge(
FUTURE_EXCHANGE_FEES_BY_SYMBOL,
exchange_fee,
)
self.min_trade_cost = min_trade_cost or 0
def __repr__(self):
if isinstance(self._cost_per_contract, DummyMapping):
# Cost per contract is a constant, so extract it.
cost_per_contract = self._cost_per_contract["dummy key"]
else:
cost_per_contract = "<varies>"
if isinstance(self._exchange_fee, DummyMapping):
# Exchange fee is a constant, so extract it.
exchange_fee = self._exchange_fee["dummy key"]
else:
exchange_fee = "<varies>"
return (
"{class_name}(cost_per_contract={cost_per_contract}, "
"exchange_fee={exchange_fee}, min_trade_cost={min_trade_cost})".format(
class_name=self.__class__.__name__,
cost_per_contract=cost_per_contract,
exchange_fee=exchange_fee,
min_trade_cost=self.min_trade_cost,
)
)
def calculate(self, order, transaction):
root_symbol = order.asset.root_symbol
cost_per_contract = self._cost_per_contract[root_symbol]
exchange_fee = self._exchange_fee[root_symbol]
return calculate_per_unit_commission(
order=order,
transaction=transaction,
cost_per_unit=cost_per_contract,
initial_commission=exchange_fee,
min_trade_cost=self.min_trade_cost,
)
[docs]class PerTrade(CommissionModel):
"""
Calculates a commission for a transaction based on a per trade cost.
For orders that require multiple fills, the full commission is charged to
the first fill.
Parameters
----------
cost : float, optional
The flat amount of commissions paid per equity trade.
"""
def __init__(self, cost=DEFAULT_MINIMUM_COST_PER_EQUITY_TRADE):
"""
Cost parameter is the cost of a trade, regardless of share count.
$5.00 per trade is fairly typical of discount brokers.
"""
# Cost needs to be floating point so that calculation using division
# logic does not floor to an integer.
self.cost = float(cost)
def __repr__(self):
return "{class_name}(cost_per_trade={cost})".format(
class_name=self.__class__.__name__,
cost=self.cost,
)
def calculate(self, order, transaction):
"""
If the order hasn't had a commission paid yet, pay the fixed
commission.
"""
if order.commission == 0:
# if the order hasn't had a commission attributed to it yet,
# that's what we need to pay.
return self.cost
else:
# order has already had commission attributed, so no more
# commission.
return 0.0
class PerFutureTrade(PerContract):
"""
Calculates a commission for a transaction based on a per trade cost.
Parameters
----------
cost : float or dict
The flat amount of commissions paid per trade, regardless of the number
of contracts being traded. If given a float, the commission for all
futures contracts is the same. If given a dictionary, it must map root
symbols to the commission cost for trading contracts of that symbol.
"""
def __init__(self, cost=DEFAULT_MINIMUM_COST_PER_FUTURE_TRADE):
# The per-trade cost can be represented as the exchange fee in a
# per-contract model because the exchange fee is just a one time cost
# incurred on the first fill.
super(PerFutureTrade, self).__init__(
cost=0,
exchange_fee=cost,
min_trade_cost=0,
)
self._cost_per_trade = self._exchange_fee
def __repr__(self):
if isinstance(self._cost_per_trade, DummyMapping):
# Cost per trade is a constant, so extract it.
cost_per_trade = self._cost_per_trade["dummy key"]
else:
cost_per_trade = "<varies>"
return "{class_name}(cost_per_trade={cost_per_trade})".format(
class_name=self.__class__.__name__,
cost_per_trade=cost_per_trade,
)
[docs]class PerDollar(EquityCommissionModel):
"""
Model commissions by applying a fixed cost per dollar transacted.
Parameters
----------
cost : float, optional
The flat amount of commissions paid per dollar of equities
traded. Default is a commission of $0.0015 per dollar transacted.
"""
def __init__(self, cost=DEFAULT_PER_DOLLAR_COST):
"""
Cost parameter is the cost of a trade per-dollar. 0.0015
on $1 million means $1,500 commission (=1M * 0.0015)
"""
self.cost_per_dollar = float(cost)
def __repr__(self):
return "{class_name}(cost_per_dollar={cost})".format(
class_name=self.__class__.__name__, cost=self.cost_per_dollar
)
def calculate(self, order, transaction):
"""
Pay commission based on dollar value of shares.
"""
cost_per_share = transaction.price * self.cost_per_dollar
return abs(transaction.amount) * cost_per_share