diff --git a/tests/test_perf_tracking.py b/tests/test_perf_tracking.py index d8d13820..076ec252 100644 --- a/tests/test_perf_tracking.py +++ b/tests/test_perf_tracking.py @@ -2264,12 +2264,11 @@ shares in position" pt.execute_transaction(txn) pp.handle_execution(txn) - # sync prices and calculate performance before we introduce a capital - # change + # sync prices before we introduce a capital change pt.sync_last_sale_prices(trades[2].dt, False, data_portal) - pp.calculate_performance() - pp.subdivide_period(1000.0) + pp.initialize_subperiod_divider() + pp.set_current_subperiod_starting_values(1000.0) pt.sync_last_sale_prices(trades[-1].dt, False, data_portal) pp.calculate_performance() diff --git a/zipline/algorithm.py b/zipline/algorithm.py index c8f68078..bc9b5c4c 100644 --- a/zipline/algorithm.py +++ b/zipline/algorithm.py @@ -821,25 +821,12 @@ class TradingAlgorithm(object): self.data_portal ) - # Calculate performance before we sync prices price for the current dt - self.perf_tracker.cumulative_performance.calculate_performance() - self.perf_tracker.todays_performance.calculate_performance() + self.perf_tracker.prepare_capital_change(is_interday) if capital_change['type'] == 'target': - # Get an updated portfolio value as of this dt, but do it in a way - # so that the performance is not recalculated. This is done so - # that `process_capital_change` can find the performance values - # for the end of the subperiod, which is the previous dt - self.perf_tracker.position_tracker.sync_last_sale_prices( - dt, - self._in_before_trading_start, - self.data_portal - ) - portfolio_value = \ - self.perf_tracker.position_tracker.stats().net_value + \ - self.perf_tracker.cumulative_performance.ending_cash - - capital_change_amount = capital_change['value'] - portfolio_value + capital_change_amount = capital_change['value'] - \ + self.updated_portfolio().portfolio_value + self.portfolio_needs_update = True log.info('Processing capital change to target %s at %s. Capital ' 'change delta is %s' % (capital_change['value'], dt, diff --git a/zipline/finance/performance/period.py b/zipline/finance/performance/period.py index 2aeab406..cc17f374 100644 --- a/zipline/finance/performance/period.py +++ b/zipline/finance/performance/period.py @@ -241,14 +241,12 @@ class PerformancePeriod(object): else: del self._payout_last_sale_prices[asset] - def subdivide_period(self, capital_change): - # Apply the capital change to the ending cash - self.ending_cash += capital_change + def initialize_subperiod_divider(self): + self.calculate_performance() - # Increment the total capital change occurred within the period - self._total_intraperiod_capital_change += capital_change - - # Divide the period into subperiods + # Initialize a subperiod divider to stash the current performance + # values. Current period starting values are set to equal ending values + # of the previous subperiod self.subperiod_divider = SubPeriodDivider( prev_returns=self.returns, prev_pnl=self.pnl, @@ -257,6 +255,20 @@ class PerformancePeriod(object): curr_starting_cash=self.ending_cash ) + def set_current_subperiod_starting_values(self, capital_change): + # Apply the capital change to the ending cash + self.ending_cash += capital_change + + # Increment the total capital change occurred within the period + self._total_intraperiod_capital_change += capital_change + + # Update the current subperiod starting cash to reflect the capital + # change + starting_value = self.subperiod_divider.curr_subperiod.starting_value + self.subperiod_divider.curr_subperiod = CurrSubPeriodStats( + starting_value=starting_value, + starting_cash=self.ending_cash) + def handle_dividends_paid(self, net_cash_payment): if net_cash_payment: self.handle_cash_payment(net_cash_payment) diff --git a/zipline/finance/performance/tracker.py b/zipline/finance/performance/tracker.py index 017bfea5..617a44bb 100644 --- a/zipline/finance/performance/tracker.py +++ b/zipline/finance/performance/tracker.py @@ -238,8 +238,16 @@ class PerformanceTracker(object): return _dict + def prepare_capital_change(self, is_interday): + self.cumulative_performance.initialize_subperiod_divider() + + if not is_interday: + # Change comes in the middle of day + self.todays_performance.initialize_subperiod_divider() + def process_capital_change(self, capital_change_amount, is_interday): - self.cumulative_performance.subdivide_period(capital_change_amount) + self.cumulative_performance.set_current_subperiod_starting_values( + capital_change_amount) if is_interday: # Change comes between days @@ -247,7 +255,8 @@ class PerformanceTracker(object): capital_change_amount) else: # Change comes in the middle of day - self.todays_performance.subdivide_period(capital_change_amount) + self.todays_performance.set_current_subperiod_starting_values( + capital_change_amount) def process_transaction(self, transaction): self.txn_count += 1 diff --git a/zipline/gens/tradesimulation.py b/zipline/gens/tradesimulation.py index e44e1196..4aa1c2fa 100644 --- a/zipline/gens/tradesimulation.py +++ b/zipline/gens/tradesimulation.py @@ -100,11 +100,11 @@ class AlgorithmSimulator(object): def every_bar(dt_to_use, current_data=self.current_data, handle_data=algo.event_manager.handle_data): # called every tick (minute or day). + algo.on_dt_changed(dt_to_use) calculate_minute_capital_changes(dt_to_use) self.simulation_dt = dt_to_use - algo.on_dt_changed(dt_to_use) blotter = algo.blotter perf_tracker = algo.perf_tracker @@ -149,10 +149,6 @@ class AlgorithmSimulator(object): perf_tracker = algo.perf_tracker - # process any capital changes that came overnight - algo.calculate_capital_changes( - midnight_dt, emission_rate=emission_rate, is_interday=True) - # Get the positions before updating the date so that prices are # fetched for trading close instead of midnight positions = algo.perf_tracker.position_tracker.positions @@ -162,6 +158,10 @@ class AlgorithmSimulator(object): self.simulation_dt = midnight_dt algo.on_dt_changed(midnight_dt) + # process any capital changes that came overnight + algo.calculate_capital_changes( + midnight_dt, emission_rate=emission_rate, is_interday=True) + # we want to wait until the clock rolls over to the next day # before cleaning up expired assets. self._cleanup_expired_assets(midnight_dt, position_assets)