diff --git a/tests/test_algorithm.py b/tests/test_algorithm.py index 1c25fbfba..4c89a9b82 100644 --- a/tests/test_algorithm.py +++ b/tests/test_algorithm.py @@ -127,7 +127,6 @@ TestTargetAlgorithm, TestTargetPercentAlgorithm, TestTargetValueAlgorithm, - TestBatchTargetPercentAlgorithm, SetLongOnlyAlgorithm, SetAssetDateBoundsAlgorithm, SetMaxPositionSizeAlgorithm, @@ -963,7 +962,6 @@ def test_order_rounding(self): ('order_percent', TestOrderPercentAlgorithm,), ('order_target_percent', TestTargetPercentAlgorithm,), ('order_target_value', TestTargetValueAlgorithm,), - ('batch_order_target_percent', TestBatchTargetPercentAlgorithm,), ]) def test_order_methods(self, test_name, algo_class): algo = algo_class( @@ -1809,8 +1807,8 @@ def test_order_methods(self): ) test_algo.run(self.data_portal) - def test_batch_order_target_percent_matches_multi_order(self): - weights = pd.Series([.3, .7]) + def test_batch_market_order_matches_multiple_manual_orders(self): + share_counts = pd.Series([50, 100]) multi_blotter = RecordBatchBlotter(self.SIM_PARAMS_DATA_FREQUENCY) multi_test_algo = TradingAlgorithm( @@ -1818,7 +1816,7 @@ def test_batch_order_target_percent_matches_multi_order(self): from collections import OrderedDict from six import iteritems - from zipline.api import sid, order_target_percent + from zipline.api import sid, order def initialize(context): @@ -1827,14 +1825,14 @@ def initialize(context): def handle_data(context, data): if not context.placed: - for asset, weight in iteritems(OrderedDict(zip( - context.assets, {weights} + for asset, shares in iteritems(OrderedDict(zip( + context.assets, {share_counts} ))): - order_target_percent(asset, weight) + order(asset, shares) context.placed = True - """).format(weights=list(weights)), + """).format(share_counts=list(share_counts)), blotter=multi_blotter, env=self.env, ) @@ -1844,9 +1842,9 @@ def handle_data(context, data): batch_blotter = RecordBatchBlotter(self.SIM_PARAMS_DATA_FREQUENCY) batch_test_algo = TradingAlgorithm( script=dedent("""\ - from collections import OrderedDict + import pandas as pd - from zipline.api import sid, batch_order_target_percent + from zipline.api import sid, batch_market_order def initialize(context): @@ -1855,9 +1853,9 @@ def initialize(context): def handle_data(context, data): if not context.placed: - orders = batch_order_target_percent(OrderedDict(zip( - context.assets, {weights} - ))) + orders = batch_market_order(pd.Series( + index=context.assets, data={share_counts} + )) assert len(orders) == 2, \ "len(orders) was %s but expected 2" % len(orders) for o in orders: @@ -1865,7 +1863,7 @@ def handle_data(context, data): context.placed = True - """).format(weights=list(weights)), + """).format(share_counts=list(share_counts)), blotter=batch_blotter, env=self.env, ) @@ -1881,16 +1879,15 @@ def handle_data(context, data): ) assert_equal(multi_stats, batch_stats) - def test_batch_order_target_percent_filters_null_orders(self): - weights = pd.Series([1, 0]) + def test_batch_market_order_filters_null_orders(self): + share_counts = [50, 0] batch_blotter = RecordBatchBlotter(self.SIM_PARAMS_DATA_FREQUENCY) batch_test_algo = TradingAlgorithm( script=dedent("""\ - from collections import OrderedDict - - from zipline.api import sid, batch_order_target_percent + import pandas as pd + from zipline.api import sid, batch_market_order def initialize(context): context.assets = [sid(0), sid(3)] @@ -1898,9 +1895,9 @@ def initialize(context): def handle_data(context, data): if not context.placed: - orders = batch_order_target_percent(OrderedDict(zip( - context.assets, {weights} - ))) + orders = batch_market_order(pd.Series( + index=context.assets, data={share_counts} + )) assert len(orders) == 1, \ "len(orders) was %s but expected 1" % len(orders) for o in orders: @@ -1908,7 +1905,7 @@ def handle_data(context, data): context.placed = True - """).format(weights=list(weights)), + """).format(share_counts=share_counts), blotter=batch_blotter, env=self.env, ) diff --git a/zipline/algorithm.py b/zipline/algorithm.py index 5341c87d8..d67300899 100644 --- a/zipline/algorithm.py +++ b/zipline/algorithm.py @@ -13,11 +13,6 @@ # 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 @@ -38,7 +33,6 @@ itervalues, string_types, viewkeys, - viewvalues, ) from zipline._protocol import handle_non_market_minutes @@ -107,9 +101,11 @@ coerce_string, ensure_upper_case, error_keywords, + expect_dtypes, expect_types, optional, ) +from zipline.utils.numpy_utils import int64_dtype from zipline.utils.calendars.trading_calendar import days_at_time from zipline.utils.cache import CachedObject, Expired from zipline.utils.calendars import get_calendar @@ -2039,35 +2035,28 @@ def _calculate_order_target_percent_amount(self, 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. + @expect_types(share_counts=pd.Series) + @expect_dtypes(share_counts=int64_dtype) + def batch_market_order(self, share_counts): + """Place a batch market order for multiple assets. Parameters ---------- - weights : collections.Mapping[Asset -> float] + share_counts : pd.Series[Asset -> int] + Map from asset to number of shares to order for that asset. 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)) - order_ids = pd.Series(data=order_ids, index=order_args) - return order_ids[~order_ids.isnull()] + order_ids : pd.Index[str] + Index of ids for newly-created orders. + """ + style = MarketOrder() + order_args = [ + (asset, amount, style) + for (asset, amount) in iteritems(share_counts) + if amount + ] + return self.blotter.batch_order(order_args) @error_keywords(sid='Keyword argument `sid` is no longer supported for ' 'get_open_orders. Use `asset` instead.') diff --git a/zipline/api.pyi b/zipline/api.pyi index 15c8710ae..724297959 100644 --- a/zipline/api.pyi +++ b/zipline/api.pyi @@ -34,21 +34,18 @@ def attach_pipeline(pipeline, name, chunks=None): :func:`zipline.api.pipeline_output` """ -def batch_order_target_percent(weights): - """Place orders towards a given portfolio of weights. +def batch_market_order(share_counts): + """Place a batch market order for multiple assets. Parameters ---------- - weights : collections.Mapping[Asset -> float] + share_counts : pd.Series[Asset -> int] + Map from asset to number of shares to order for that asset. 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_ids : pd.Index[str] + Index of ids for newly-created orders. """ def cancel_order(order_param): diff --git a/zipline/test_algorithms.py b/zipline/test_algorithms.py index aeb9279cb..22acb5dd8 100644 --- a/zipline/test_algorithms.py +++ b/zipline/test_algorithms.py @@ -436,11 +436,6 @@ 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): self.set_slippage(FixedSlippage())