From f1fafc34ce610e50c095f6bef2fb9b42fa596740 Mon Sep 17 00:00:00 2001 From: Scott Sanderson Date: Thu, 17 Apr 2014 14:26:36 -0400 Subject: [PATCH] ENH: Add style parameters to order API helper methods. Add `style` parameter to order_value, order_percent, order_target, order_target_percent, and order_target_value methods. The style parameter is forwarded to the underlying call to `order`. --- tests/test_algorithm.py | 19 ++++++++++++++ zipline/algorithm.py | 52 ++++++++++++++++++++++++++++---------- zipline/test_algorithms.py | 32 +++++++++++++++++++++++ 3 files changed, 89 insertions(+), 14 deletions(-) diff --git a/tests/test_algorithm.py b/tests/test_algorithm.py index bca840b9..a9799a33 100644 --- a/tests/test_algorithm.py +++ b/tests/test_algorithm.py @@ -26,6 +26,7 @@ import zipline.utils.simfactory as simfactory from zipline.test_algorithms import (TestRegisterTransformAlgorithm, RecordAlgorithm, TestOrderAlgorithm, + TestOrderStyleForwardingAlgorithm, TestOrderInstantAlgorithm, TestOrderValueAlgorithm, TestTargetAlgorithm, @@ -211,6 +212,24 @@ class TestTransformAlgorithm(TestCase): ) algo.run(self.df) + def test_order_method_style_forwarding(self): + + method_names_to_test = ['order', + 'order_value', + 'order_percent', + 'order_target', + 'order_target_percent', + 'order_target_value'] + + for name in method_names_to_test: + algo = TestOrderStyleForwardingAlgorithm( + sim_params=self.sim_params, + data_frequency='daily', + instant_fill=False, + method_name=name + ) + algo.run(self.df) + def test_order_instant(self): algo = TestOrderInstantAlgorithm(sim_params=self.sim_params, data_frequency='daily', diff --git a/zipline/algorithm.py b/zipline/algorithm.py index ea0165b5..2107ab43 100644 --- a/zipline/algorithm.py +++ b/zipline/algorithm.py @@ -537,7 +537,8 @@ class TradingAlgorithm(object): return MarketOrder() @api_method - def order_value(self, sid, value, limit_price=None, stop_price=None): + def order_value(self, sid, value, + limit_price=None, stop_price=None, style=None): """ Place an order by desired value rather than desired number of shares. If the requested sid is found in the universe, the requested value is @@ -561,7 +562,10 @@ class TradingAlgorithm(object): return else: amount = value / last_price - return self.order(sid, amount, limit_price, stop_price) + return self.order(sid, amount, + limit_price=limit_price, + stop_price=stop_price, + style=style) @property def recorded_vars(self): @@ -639,7 +643,8 @@ class TradingAlgorithm(object): self.annualizer = ANNUALIZER[self.data_frequency] @api_method - def order_percent(self, sid, percent, limit_price=None, stop_price=None): + def order_percent(self, sid, percent, + limit_price=None, stop_price=None, style=None): """ Place an order in the specified security corresponding to the given percent of the current portfolio value. @@ -647,10 +652,14 @@ class TradingAlgorithm(object): Note that percent must expressed as a decimal (0.50 means 50\%). """ value = self.portfolio.portfolio_value * percent - return self.order_value(sid, value, limit_price, stop_price) + return self.order_value(sid, value, + limit_price=limit_price, + stop_price=stop_price, + style=style) @api_method - def order_target(self, sid, target, limit_price=None, stop_price=None): + def order_target(self, sid, target, + limit_price=None, stop_price=None, style=None): """ Place an order to adjust a position to a target number of shares. If the position doesn't already exist, this is equivalent to placing a new @@ -661,13 +670,19 @@ class TradingAlgorithm(object): if sid in self.portfolio.positions: current_position = self.portfolio.positions[sid].amount req_shares = target - current_position - return self.order(sid, req_shares, limit_price, stop_price) + return self.order(sid, req_shares, + limit_price=limit_price, + stop_price=stop_price, + style=style) else: - return self.order(sid, target, limit_price, stop_price) + return self.order(sid, target, + limit_price=limit_price, + stop_price=stop_price, + style=style) @api_method - def order_target_value(self, sid, target, limit_price=None, - stop_price=None): + def order_target_value(self, sid, target, + limit_price=None, stop_price=None, style=None): """ Place an order to adjust a position to a target value. If the position doesn't already exist, this is equivalent to placing a new @@ -680,13 +695,19 @@ class TradingAlgorithm(object): current_price = self.trading_client.current_data[sid].price current_value = current_position * current_price req_value = target - current_value - return self.order_value(sid, req_value, limit_price, stop_price) + return self.order_value(sid, req_value, + limit_price=limit_price, + stop_price=stop_price, + style=style) else: - return self.order_value(sid, target, limit_price, stop_price) + return self.order_value(sid, target, + limit_price=limit_price, + stop_price=stop_price, + style=style) @api_method - def order_target_percent(self, sid, target, limit_price=None, - stop_price=None): + def order_target_percent(self, sid, target, + limit_price=None, stop_price=None, style=None): """ Place an order to adjust a position to a target percent of the current portfolio value. If the position doesn't already exist, this is @@ -705,7 +726,10 @@ class TradingAlgorithm(object): target_value = self.portfolio.portfolio_value * target req_value = target_value - current_value - return self.order_value(sid, req_value, limit_price, stop_price) + return self.order_value(sid, req_value, + limit_price=limit_price, + stop_price=stop_price, + style=style) @api_method def get_open_orders(self, sid=None): diff --git a/zipline/test_algorithms.py b/zipline/test_algorithms.py index 31f8818d..0d3dbb68 100644 --- a/zipline/test_algorithms.py +++ b/zipline/test_algorithms.py @@ -79,6 +79,7 @@ from six import itervalues from zipline.algorithm import TradingAlgorithm from zipline.api import FixedSlippage +from zipline.finance.execution import StopLimitOrder class TestAlgorithm(TradingAlgorithm): @@ -261,6 +262,37 @@ class TestOrderInstantAlgorithm(TradingAlgorithm): self.last_price = data[0].price +class TestOrderStyleForwardingAlgorithm(TradingAlgorithm): + """ + Test Algorithm for verifying that ExecutionStyles are properly forwarded by + order API helper methods. Pass the name of the method to be tested as a + string parameter to this algorithm's constructor. + """ + + def __init__(self, *args, **kwargs): + self.method_name = kwargs.pop('method_name') + super(TestOrderStyleForwardingAlgorithm, self)\ + .__init__(*args, **kwargs) + + def initialize(self): + self.incr = 0 + self.last_price = None + + def handle_data(self, data): + if self.incr == 0: + assert len(self.portfolio.positions.keys()) == 0 + + method_to_check = getattr(self, self.method_name) + method_to_check(0, data[0].price, style=StopLimitOrder(10, 10)) + + assert len(self.blotter.open_orders[0]) == 1 + result = self.blotter.open_orders[0][0] + assert result.limit == 10 + assert result.stop == 10 + + self.incr += 1 + + class TestOrderValueAlgorithm(TradingAlgorithm): def initialize(self): self.incr = 0