diff --git a/tests/test_algorithm.py b/tests/test_algorithm.py index e5c4e2ed..85a7a65f 100644 --- a/tests/test_algorithm.py +++ b/tests/test_algorithm.py @@ -123,6 +123,7 @@ from zipline.test_algorithms import ( TestTargetAlgorithm, TestTargetPercentAlgorithm, TestTargetValueAlgorithm, + TestBatchTargetPercentAlgorithm, SetLongOnlyAlgorithm, SetAssetDateBoundsAlgorithm, SetMaxPositionSizeAlgorithm, @@ -905,14 +906,15 @@ def before_trading_start(context, data): self.assertEqual(algo.sim_params.data_frequency, 'minute') @parameterized.expand([ - (TestOrderAlgorithm,), - (TestOrderValueAlgorithm,), - (TestTargetAlgorithm,), - (TestOrderPercentAlgorithm,), - (TestTargetPercentAlgorithm,), - (TestTargetValueAlgorithm,), + ('order', TestOrderAlgorithm,), + ('order_value', TestOrderValueAlgorithm,), + ('order_target', TestTargetAlgorithm,), + ('order_percent', TestOrderPercentAlgorithm,), + ('order_target_percent', TestTargetPercentAlgorithm,), + ('order_target_value', TestTargetValueAlgorithm,), + ('batch_order_target_percent', TestBatchTargetPercentAlgorithm,), ]) - def test_order_methods(self, algo_class): + def test_order_methods(self, test_name, algo_class): algo = algo_class( sim_params=self.sim_params, env=self.env, diff --git a/tests/test_blotter.py b/tests/test_blotter.py index deb0bbdb..21260954 100644 --- a/tests/test_blotter.py +++ b/tests/test_blotter.py @@ -330,7 +330,7 @@ class BlotterTestCase(WithCreateBarData, blotter.prune_orders([other_order]) - def test_order_batch_matches_multi_order(self): + def test_batch_order_matches_multiple_orders(self): """ Ensure the effect of order_batch is the same as multiple calls to order. @@ -345,7 +345,7 @@ class BlotterTestCase(WithCreateBarData, (self.asset_25, i * 100, LimitOrder(i * 100 + 1)), ] - order_batch_ids = blotter1.order_batch(order_arg_lists) + order_batch_ids = blotter1.batch_order(order_arg_lists) order_ids = [] for order_args in order_arg_lists: order_ids.append(blotter2.order(*order_args)) diff --git a/zipline/algorithm.py b/zipline/algorithm.py index 2cfd4bf9..b076e71a 100644 --- a/zipline/algorithm.py +++ b/zipline/algorithm.py @@ -13,6 +13,11 @@ # See the License for the specific language governing permissions and # limitations under the License. from collections import Iterable +try: + # optional cython based OrderedDict + from cyordereddict import OrderedDict +except ImportError: + from collections import OrderedDict from copy import copy import operator as op import warnings @@ -33,6 +38,7 @@ from six import ( itervalues, string_types, viewkeys, + viewvalues, ) from zipline._protocol import handle_non_market_minutes @@ -1916,7 +1922,7 @@ class TradingAlgorithm(object): ---------- asset : Asset The asset that this order is for. - percent : float + target : float The desired percentage of the porfolio value to allocate to ``asset``. This is specified as a decimal, for example: 0.50 means 50%. @@ -1969,6 +1975,36 @@ class TradingAlgorithm(object): target_amount = self._calculate_order_percent_amount(asset, target) return self._calculate_order_target_amount(asset, target_amount) + @api_method + @disallowed_in_before_trading_start(OrderInBeforeTradingStart()) + def batch_order_target_percent(self, weights): + """Place orders towards a given portfolio of weights. + + Parameters + ---------- + weights : collections.Mapping[Asset -> float] + + Returns + ------- + order_ids : pd.Series[Asset -> str] + The unique identifiers for the orders that were placed. + + See Also + -------- + :func:`zipline.api.order_target_percent` + """ + order_args = OrderedDict() + for asset, target in iteritems(weights): + if self._can_order_asset(asset): + amount = self._calculate_order_target_percent_amount( + asset, target, + ) + amount, style = self._calculate_order(asset, amount) + order_args[asset] = (asset, amount, style) + + order_ids = self.blotter.batch_order(viewvalues(order_args)) + return pd.Series(data=order_ids, index=order_args) + @error_keywords(sid='Keyword argument `sid` is no longer supported for ' 'get_open_orders. Use `asset` instead.') @api_method diff --git a/zipline/finance/blotter.py b/zipline/finance/blotter.py index fc1b3558..a73dda0d 100644 --- a/zipline/finance/blotter.py +++ b/zipline/finance/blotter.py @@ -135,7 +135,7 @@ class Blotter(object): return order.id - def order_batch(self, order_arg_lists): + def batch_order(self, order_arg_lists): """Place a batch of orders. Parameters diff --git a/zipline/test_algorithms.py b/zipline/test_algorithms.py index 5d112f12..7fe1713d 100644 --- a/zipline/test_algorithms.py +++ b/zipline/test_algorithms.py @@ -429,9 +429,17 @@ class TestTargetPercentAlgorithm(TradingAlgorithm): "Orders not filled at current price." self.sale_price = data.current(sid(0), "price") - self.order_target_percent(self.sid(0), .002) + self._order(self.sid(0), .002) self.ordered = True + def _order(self, asset, target): + return self.order_target_percent(asset, target) + + +class TestBatchTargetPercentAlgorithm(TestTargetPercentAlgorithm): + def _order(self, asset, target): + return self.batch_order_target_percent({asset: target}) + class TestTargetValueAlgorithm(TradingAlgorithm): def initialize(self):