From b55d4bd4238ca89b4108783cc996e9c0cd4c95f9 Mon Sep 17 00:00:00 2001 From: Jean Bredeche Date: Wed, 26 Apr 2017 09:55:05 -0400 Subject: [PATCH] BUG: Add backwards compatibility for position lookup by int. --- tests/test_api_shim.py | 46 ++++++++++++++++++++++++++++++++++++++++++ zipline/protocol.py | 41 ++++++++++++++++++++++++++++++++++--- 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/tests/test_api_shim.py b/tests/test_api_shim.py index f79416d2..b90ed2b7 100644 --- a/tests/test_api_shim.py +++ b/tests/test_api_shim.py @@ -113,6 +113,22 @@ def handle_data(context, data): assert iter_list == items_list """ +reference_missing_position_by_int_algo = """ +def initialize(context): + pass + +def handle_data(context, data): + context.portfolio.positions[24] +""" + +reference_missing_position_by_unexpected_type_algo = """ +def initialize(context): + pass + +def handle_data(context, data): + context.portfolio.positions["foobar"] +""" + class TestAPIShim(WithCreateBarData, WithDataPortal, @@ -513,3 +529,33 @@ class TestAPIShim(WithCreateBarData, self.assertEqual("Iterating over the assets in `data` is " "deprecated.", str(warning.message)) + + def test_reference_empty_position_by_int(self): + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("default", ZiplineDeprecationWarning) + + algo = self.create_algo(reference_missing_position_by_int_algo) + algo.run(self.data_portal) + + self.assertEqual(1, len(w)) + self.assertEqual( + w[0].message.message, + "Referencing positions by integer is deprecated. Use an asset " + "instead." + ) + + def test_reference_empty_position_by_unexpected_type(self): + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("default", ZiplineDeprecationWarning) + + algo = self.create_algo( + reference_missing_position_by_unexpected_type_algo + ) + algo.run(self.data_portal) + + self.assertEqual(1, len(w)) + self.assertEqual( + w[0].message.message, + "Position lookup expected a value of type Asset but got str" + " instead." + ) diff --git a/zipline/protocol.py b/zipline/protocol.py index 07f814e4..5d944347 100644 --- a/zipline/protocol.py +++ b/zipline/protocol.py @@ -257,8 +257,43 @@ class Position(object): ) -class Positions(dict): +# Copied from Position and renamed. This is used to handle cases where a user +# does something like `context.portfolio.positions[100]` instead of +# `context.portfolio.positions[sid(100)]`. +class _DeprecatedSidLookupPosition(object): + def __init__(self, sid): + self.sid = sid + self.amount = 0 + self.cost_basis = 0.0 # per share + self.last_sale_price = 0.0 + self.last_sale_date = None + def __repr__(self): + return "_DeprecatedSidLookupPosition({0})".format(self.__dict__) + + # If you are adding new attributes, don't update this set. This method + # is deprecated to normal attribute access so we don't want to encourage + # new usages. + __getitem__ = _deprecated_getitem_method( + 'position', { + 'sid', + 'amount', + 'cost_basis', + 'last_sale_price', + 'last_sale_date', + }, + ) + + +class Positions(dict): def __missing__(self, key): - pos = Position(key) - return pos + if isinstance(key, Asset): + return Position(key) + elif isinstance(key, int): + warn("Referencing positions by integer is deprecated." + " Use an asset instead.") + else: + warn("Position lookup expected a value of type Asset but got {0}" + " instead.".format(type(key).__name__)) + + return _DeprecatedSidLookupPosition(key)