ENH: Add trading controls to zipline API.

Adds four new methods to the Zipline API that can be used as circuit-breakers
to interrupt the execution of an algorithm.  The API methods are:

`set_max_position_size`
`set_max_order_size`
`set_max_order_count`
`set_long_only`

Internally, these methods are implemented by each registering a TradingControl
callback object with the TradingAlgorithm.  During
TradingAlgorithm.__validate_order_params (and thus before any side-effects of
the order call occur), each callback's `validate` method is called with
information about the order to be placed and the algorithm's current state,
raising an exception if the callback detects that an error condition has been breached.
This commit is contained in:
Scott Sanderson
2014-05-03 01:25:19 -04:00
parent 9953c7ea28
commit 644486e6da
6 changed files with 635 additions and 9 deletions
+86 -4
View File
@@ -26,16 +26,23 @@ from six import iteritems, exec_
from operator import attrgetter
from zipline.errors import (
UnsupportedSlippageModel,
OverrideSlippagePostInit,
UnsupportedCommissionModel,
OverrideCommissionPostInit,
UnsupportedOrderParameters
OverrideSlippagePostInit,
RegisterTradingControlPostInit,
UnsupportedCommissionModel,
UnsupportedOrderParameters,
UnsupportedSlippageModel,
)
from zipline.finance import trading
from zipline.finance.blotter import Blotter
from zipline.finance.commission import PerShare, PerTrade, PerDollar
from zipline.finance.controls import (
LongOnly,
MaxOrderCount,
MaxOrderSize,
MaxPositionSize,
)
from zipline.finance.constants import ANNUALIZER
from zipline.finance.execution import (
LimitOrder,
@@ -125,6 +132,9 @@ class TradingAlgorithm(object):
self.transforms = []
self.sources = []
# List of trading controls to be used to validate orders.
self.trading_controls = []
self._recorded_vars = {}
self.namespace = kwargs.get('namespace', {})
@@ -525,6 +535,13 @@ class TradingAlgorithm(object):
msg="Passing both stop_price and style is not supported."
)
for control in self.trading_controls:
control.validate(sid,
amount,
self.updated_portfolio(),
self.get_datetime(),
self.trading_client.current_data)
@staticmethod
def __convert_order_params_for_blotter(limit_price, stop_price, style):
"""
@@ -799,3 +816,68 @@ class TradingAlgorithm(object):
bar_count, frequency, field, ffill)
history_spec = self.history_specs[spec_key_str]
return self.history_container.get_history(history_spec, self.datetime)
####################
# Trading Controls #
####################
def register_trading_control(self, control):
"""
Register a new TradingControl to be checked prior to order calls.
"""
if self.initialized:
raise RegisterTradingControlPostInit()
self.trading_controls.append(control)
@api_method
def set_max_position_size(self,
sid=None,
max_shares=None,
max_notional=None):
"""
Set a limit on the number of shares and/or dollar value held for the
given sid. Limits are treated as absolute values and are enforced at
the time that the algo attempts to place an order for sid. This means
that it's possible to end up with more than the max number of shares
due to splits/dividends, and more than the max notional due to price
improvement.
If an algorithm attempts to place an order that would result in
increasing the absolute value of shares/dollar value exceeding one of
these limits, raise a TradingControlException.
"""
control = MaxPositionSize(sid=sid,
max_shares=max_shares,
max_notional=max_notional)
self.register_trading_control(control)
@api_method
def set_max_order_size(self, sid=None, max_shares=None, max_notional=None):
"""
Set a limit on the number of shares and/or dollar value of any single
order placed for sid. Limits are treated as absolute values and are
enforced at the time that the algo attempts to place an order for sid.
If an algorithm attempts to place an order that would result in
exceeding one of these limits, raise a TradingControlException.
"""
control = MaxOrderSize(sid=sid,
max_shares=max_shares,
max_notional=max_notional)
self.register_trading_control(control)
@api_method
def set_max_order_count(self, max_count):
"""
Set a limit on the number of orders that can be placed within the given
time interval.
"""
control = MaxOrderCount(max_count)
self.register_trading_control(control)
@api_method
def set_long_only(self):
"""
Set a rule specifying that this algorithm cannot take short positions.
"""
self.register_trading_control(LongOnly())