From 5b1aa5ec55c70660103206e99e5f13fa6ed8f332 Mon Sep 17 00:00:00 2001 From: John Ricklefs Date: Thu, 1 Sep 2016 14:32:45 -0400 Subject: [PATCH 1/2] BUG: Capital change deltas rely on cash, not portfolio_value The value of holdings is irrelevant when altering the capital base of the current perf period. --- tests/test_algorithm.py | 19 ++++++++++--------- zipline/algorithm.py | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/tests/test_algorithm.py b/tests/test_algorithm.py index 3735d2ab..55db97ec 100644 --- a/tests/test_algorithm.py +++ b/tests/test_algorithm.py @@ -2167,7 +2167,7 @@ class TestCapitalChanges(WithLogger, ) @parameterized.expand([ - ('target', 153000.0), ('delta', 50000.0) + ('target', 127000.0), ('delta', 50000.0) ]) def test_capital_changes_daily_mode(self, change_type, value): sim_params = factory.create_simulation_parameters( @@ -2215,8 +2215,9 @@ def order_stuff(context, data): capital_change_packets[0], {'date': pd.Timestamp('2006-01-06', tz='UTC'), 'type': 'cash', - 'target': 153000.0 if change_type == 'target' else None, - 'delta': 50000.0}) + 'target': value if change_type == 'target' else None, + 'delta': 50000.0 + }) # 1/03: price = 10, place orders # 1/04: orders execute at price = 11, place orders @@ -2320,10 +2321,10 @@ def order_stuff(context, data): ) @parameterized.expand([ - ('interday_target', [('2006-01-04', 2388.0)]), + ('interday_target', [('2006-01-04', 1899.0)]), ('interday_delta', [('2006-01-04', 1000.0)]), - ('intraday_target', [('2006-01-04 17:00', 2186.0), - ('2006-01-04 18:00', 2806.0)]), + ('intraday_target', [('2006-01-04 17:00', 908.0), + ('2006-01-04 18:00', 1408.0)]), ('intraday_delta', [('2006-01-04 17:00', 500.0), ('2006-01-04 18:00', 500.0)]), ]) @@ -2486,10 +2487,10 @@ def order_stuff(context, data): ) @parameterized.expand([ - ('interday_target', [('2006-01-04', 2388.0)]), + ('interday_target', [('2006-01-04', 1899.0)]), ('interday_delta', [('2006-01-04', 1000.0)]), - ('intraday_target', [('2006-01-04 17:00', 2186.0), - ('2006-01-04 18:00', 2806.0)]), + ('intraday_target', [('2006-01-04 17:00', 908.0), + ('2006-01-04 18:00', 1408.0)]), ('intraday_delta', [('2006-01-04 17:00', 500.0), ('2006-01-04 18:00', 500.0)]), ]) diff --git a/zipline/algorithm.py b/zipline/algorithm.py index 0f134b1c..7e731c38 100644 --- a/zipline/algorithm.py +++ b/zipline/algorithm.py @@ -858,7 +858,7 @@ class TradingAlgorithm(object): if capital_change['type'] == 'target': target = capital_change['value'] capital_change_amount = target - \ - self.updated_portfolio().portfolio_value + self.updated_portfolio().cash self.portfolio_needs_update = True log.info('Processing capital change to target %s at %s. Capital ' From 311284475acdce84d8c09f1443aabcc436c51469 Mon Sep 17 00:00:00 2001 From: John Ricklefs Date: Thu, 1 Sep 2016 14:36:30 -0400 Subject: [PATCH 2/2] ENH: Allow passing additional adjustments to calculate_capital_changes If subclasses have additional capital change information that is required to correctly calculate the target values for cash capital changes, it can now be provided via "portfolio_value_adjustment". --- tests/test_algorithm.py | 2 +- zipline/algorithm.py | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/test_algorithm.py b/tests/test_algorithm.py index 55db97ec..5ff15f40 100644 --- a/tests/test_algorithm.py +++ b/tests/test_algorithm.py @@ -2217,7 +2217,7 @@ def order_stuff(context, data): 'type': 'cash', 'target': value if change_type == 'target' else None, 'delta': 50000.0 - }) + }) # 1/03: price = 10, place orders # 1/04: orders execute at price = 11, place orders diff --git a/zipline/algorithm.py b/zipline/algorithm.py index 7e731c38..097ff2c6 100644 --- a/zipline/algorithm.py +++ b/zipline/algorithm.py @@ -825,12 +825,17 @@ class TradingAlgorithm(object): return daily_stats - def calculate_capital_changes(self, dt, emission_rate, is_interday): + def calculate_capital_changes(self, dt, emission_rate, is_interday, + portfolio_value_adjustment=0.0): """ If there is a capital change for a given dt, this means the the change occurs before `handle_data` on the given dt. In the case of the change being a target value, the change will be computed on the portfolio value according to prices at the given dt + + `portfolio_value_adjustment`, if specified, will be removed from the + portfolio_value of the cumulative performance when calculating deltas + from target capital changes. """ try: capital_change = self.capital_changes[dt] @@ -852,13 +857,12 @@ class TradingAlgorithm(object): False, self.data_portal ) - self.perf_tracker.prepare_capital_change(is_interday) if capital_change['type'] == 'target': target = capital_change['value'] capital_change_amount = target - \ - self.updated_portfolio().cash + (self.updated_portfolio().cash - portfolio_value_adjustment) self.portfolio_needs_update = True log.info('Processing capital change to target %s at %s. Capital '