diff --git a/tests/test_algorithm.py b/tests/test_algorithm.py index fbfda6ab..8faa31ba 100644 --- a/tests/test_algorithm.py +++ b/tests/test_algorithm.py @@ -2571,7 +2571,7 @@ class TestOrderCancelation(WithDataPortal, ) def prep_algo(self, cancelation_string, data_frequency="minute", - amount=1000): + amount=1000, minute_emission=False): code = self.code.format(cancelation_string, amount) algo = TradingAlgorithm( script=code, @@ -2580,21 +2580,28 @@ class TestOrderCancelation(WithDataPortal, period_start=self.sim_params.period_start, period_end=self.sim_params.period_end, env=self.env, - data_frequency=data_frequency + data_frequency=data_frequency, + emission_rate='minute' if minute_emission else 'daily' ) ) return algo - @parameterized.expand([ - (1,), (-1,), - ]) - def test_eod_order_cancel_minute(self, direction): + @parameter_space( + direction=[1, -1], + minute_emission=[True, False] + ) + def test_eod_order_cancel_minute(self, direction, minute_emission): + """ + Test that EOD order cancel works in minute mode for both shorts and + longs, and both daily emission and minute emission + """ # order 1000 shares of asset1. the volume is only 1 share per bar, # so the order should be cancelled at the end of the day. algo = self.prep_algo( "set_cancel_policy(cancel_policy.EODCancel())", amount=np.copysign(1000, direction), + minute_emission=minute_emission ) log_catcher = TestHandler() diff --git a/tests/test_perf_tracking.py b/tests/test_perf_tracking.py index b77b9c0c..383fb7f7 100644 --- a/tests/test_perf_tracking.py +++ b/tests/test_perf_tracking.py @@ -198,7 +198,7 @@ def calculate_results(sim_params, except KeyError: pass - msg = perf_tracker.handle_market_close_daily(date, data_portal) + msg = perf_tracker.handle_market_close(date, data_portal) perf_tracker.position_tracker.sync_last_sale_prices( date, False, data_portal, ) diff --git a/zipline/finance/performance/tracker.py b/zipline/finance/performance/tracker.py index ff98ebb1..3c3be3dc 100644 --- a/zipline/finance/performance/tracker.py +++ b/zipline/finance/performance/tracker.py @@ -303,9 +303,7 @@ class PerformanceTracker(object): def handle_minute_close(self, dt, data_portal): """ - Handles the close of the given minute. This includes handling - market-close functions if the given minute is the end of the market - day. + Handles the close of the given minute in minute emission. Parameters __________ @@ -314,9 +312,7 @@ class PerformanceTracker(object): Returns _______ - (dict, dict/None) - A tuple of the minute perf packet and daily perf packet. - If the market day has not ended, the daily perf packet is None. + A minute perf packet. """ self.position_tracker.sync_last_sale_prices(dt, False, data_portal) self.update_performance() @@ -333,40 +329,40 @@ class PerformanceTracker(object): account.leverage) minute_packet = self.to_dict(emission_type='minute') + return minute_packet - # if this is the close, update dividends for the next day. - # Return the performance tuple - if dt == self.market_close: - return minute_packet, self._handle_market_close( - todays_date, data_portal._adjustment_reader, - ) - else: - return minute_packet, None + def handle_market_close(self, dt, data_portal): + """ + Handles the close of the given day, in both minute and daily emission. + In daily emission, also updates performance, benchmark and risk metrics + as it would in handle_minute_close if it were minute emission. - def handle_market_close_daily(self, dt, data_portal): + Parameters + __________ + dt : Timestamp + The minute that is ending + + Returns + _______ + A daily perf packet. """ - Function called after handle_data when running with daily emission - rate. - """ - self.position_tracker.sync_last_sale_prices(dt, False, data_portal) - self.update_performance() completed_date = self.day - account = self.get_account(False) - benchmark_value = self.all_benchmark_returns[completed_date] + if self.emission_rate == 'daily': + # this method is called for both minutely and daily emissions, but + # this chunk of code here only applies for daily emissions. (since + # it's done every minute, elsewhere, for minutely emission). + self.position_tracker.sync_last_sale_prices(dt, False, data_portal) + self.update_performance() + account = self.get_account(False) - self.cumulative_risk_metrics.update( - completed_date, - self.todays_performance.returns, - benchmark_value, - account.leverage) + benchmark_value = self.all_benchmark_returns[completed_date] - daily_packet = self._handle_market_close( - completed_date, data_portal._adjustment_reader, - ) - return daily_packet - - def _handle_market_close(self, completed_date, adjustment_reader): + self.cumulative_risk_metrics.update( + completed_date, + self.todays_performance.returns, + benchmark_value, + account.leverage) # increment the day counter before we move markers forward. self.day_count += 1.0 @@ -400,8 +396,11 @@ class PerformanceTracker(object): return daily_update # Check for any dividends, then return the daily perf packet - self.check_upcoming_dividends(next_trading_day=next_trading_day, - adjustment_reader=adjustment_reader) + self.check_upcoming_dividends( + next_trading_day=next_trading_day, + adjustment_reader=data_portal._adjustment_reader + ) + return daily_update def handle_simulation_end(self): diff --git a/zipline/gens/sim_engine.pyx b/zipline/gens/sim_engine.pyx index c357f899..abc7d8b1 100644 --- a/zipline/gens/sim_engine.pyx +++ b/zipline/gens/sim_engine.pyx @@ -81,8 +81,8 @@ cdef class MinuteSimulationClock: if minute_emission: yield minute, MINUTE_END - if not minute_emission: - yield minutes[-1], DAY_END + yield minutes[-1], DAY_END + cdef class DailySimulationClock: diff --git a/zipline/gens/tradesimulation.py b/zipline/gens/tradesimulation.py index 33a9c432..431350a2 100644 --- a/zipline/gens/tradesimulation.py +++ b/zipline/gens/tradesimulation.py @@ -199,20 +199,18 @@ class AlgorithmSimulator(object): once_a_day(dt) elif action == DAY_END: # End of the day. + if algo.perf_tracker.emission_rate == 'daily': + handle_benchmark(normalize_date(dt)) execute_order_cancellation_policy() - handle_benchmark(normalize_date(dt)) yield self._get_daily_message(dt, algo, algo.perf_tracker) elif action == MINUTE_END: handle_benchmark(dt) - minute_msg, daily_msg = \ + minute_msg = \ self._get_minute_message(dt, algo, algo.perf_tracker) yield minute_msg - if daily_msg: - yield daily_msg - risk_message = algo.perf_tracker.handle_simulation_end() yield risk_message @@ -256,7 +254,7 @@ class AlgorithmSimulator(object): """ Get a perf message for the given datetime. """ - perf_message = perf_tracker.handle_market_close_daily( + perf_message = perf_tracker.handle_market_close( dt, self.data_portal, ) perf_message['daily_perf']['recorded_vars'] = algo.recorded_vars @@ -268,12 +266,9 @@ class AlgorithmSimulator(object): """ rvars = algo.recorded_vars - minute_message, daily_message = perf_tracker.handle_minute_close( + minute_message = perf_tracker.handle_minute_close( dt, self.data_portal, ) + minute_message['minute_perf']['recorded_vars'] = rvars - - if daily_message: - daily_message["daily_perf"]["recorded_vars"] = rvars - - return minute_message, daily_message + return minute_message