mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-25 13:10:33 +08:00
BUG: Aligns performance packet generation between minute and daily modes
This commit is contained in:
@@ -1949,8 +1949,7 @@ class TestPerformanceTracker(unittest.TestCase):
|
||||
tracker.process_order(event)
|
||||
elif event.type == zp.DATASOURCE_TYPE.TRANSACTION:
|
||||
tracker.process_transaction(event)
|
||||
tracker.handle_minute_close(date)
|
||||
msg = tracker.to_dict()
|
||||
msg, _ = tracker.handle_minute_close(date)
|
||||
messages[date] = msg
|
||||
|
||||
self.assertEquals(2, len(messages))
|
||||
|
||||
@@ -279,8 +279,11 @@ class PerformanceTracker(object):
|
||||
Creates a dictionary representing the state of this tracker.
|
||||
Returns a dict object of the form described in header comments.
|
||||
"""
|
||||
if not emission_type:
|
||||
|
||||
# Default to the emission rate of this tracker if no type is provided
|
||||
if emission_type is None:
|
||||
emission_type = self.emission_rate
|
||||
|
||||
_dict = {
|
||||
'period_start': self.period_start,
|
||||
'period_end': self.period_end,
|
||||
@@ -294,6 +297,8 @@ class PerformanceTracker(object):
|
||||
elif emission_type == 'minute':
|
||||
_dict['minute_perf'] = self.todays_performance.to_dict(
|
||||
self.saved_dt)
|
||||
else:
|
||||
raise BaseException("Invalid emission type: %s" % emission_type)
|
||||
|
||||
return _dict
|
||||
|
||||
@@ -409,6 +414,22 @@ class PerformanceTracker(object):
|
||||
period.handle_dividends_paid(net_cash_payment)
|
||||
|
||||
def handle_minute_close(self, dt):
|
||||
"""
|
||||
Handles the close of the given minute. This includes handling
|
||||
market-close functions if the given minute is the end of the market
|
||||
day.
|
||||
|
||||
Parameters
|
||||
__________
|
||||
dt : Timestamp
|
||||
The minute that is ending
|
||||
|
||||
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.
|
||||
"""
|
||||
self.update_performance()
|
||||
todays_date = normalize_date(dt)
|
||||
account = self.get_account(False)
|
||||
@@ -424,19 +445,14 @@ class PerformanceTracker(object):
|
||||
bench_since_open,
|
||||
account)
|
||||
|
||||
# if this is the close, update dividends for the next day.
|
||||
if dt == self.market_close:
|
||||
self.check_upcoming_dividends(todays_date)
|
||||
minute_packet = self.to_dict(emission_type='minute')
|
||||
|
||||
def handle_intraday_market_close(self, new_mkt_open, new_mkt_close):
|
||||
"""
|
||||
Function called at market close only when emitting at minutely
|
||||
frequency.
|
||||
"""
|
||||
# increment the day counter before we move markers forward.
|
||||
self.day_count += 1.0
|
||||
self.market_open = new_mkt_open
|
||||
self.market_close = new_mkt_close
|
||||
# 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))
|
||||
else:
|
||||
return (minute_packet, None)
|
||||
|
||||
def handle_market_close_daily(self):
|
||||
"""
|
||||
@@ -454,12 +470,16 @@ class PerformanceTracker(object):
|
||||
self.all_benchmark_returns[completed_date],
|
||||
account)
|
||||
|
||||
return self._handle_market_close(completed_date)
|
||||
|
||||
def _handle_market_close(self, completed_date):
|
||||
|
||||
# increment the day counter before we move markers forward.
|
||||
self.day_count += 1.0
|
||||
|
||||
# Take a snapshot of our current performance to return to the
|
||||
# browser.
|
||||
daily_update = self.to_dict()
|
||||
daily_update = self.to_dict(emission_type='daily')
|
||||
|
||||
# On the last day of the test, don't create tomorrow's performance
|
||||
# period. We may not be able to find the next trading day if we're at
|
||||
|
||||
@@ -114,29 +114,20 @@ class AlgorithmSimulator(object):
|
||||
self.update_universe(event)
|
||||
|
||||
else:
|
||||
message = self._process_snapshot(
|
||||
messages = self._process_snapshot(
|
||||
date,
|
||||
snapshot,
|
||||
self.algo.instant_fill,
|
||||
)
|
||||
# Perf messages are only emitted if the snapshot contained
|
||||
# a benchmark event.
|
||||
if message is not None:
|
||||
yield message
|
||||
if messages is not None:
|
||||
for message in messages:
|
||||
yield message
|
||||
|
||||
# When emitting minutely, we re-iterate the day as a
|
||||
# packet with the entire days performance rolled up.
|
||||
# When emitting minutely, we need to call
|
||||
# before_trading_start before the next trading day begins
|
||||
if date == mkt_close:
|
||||
if self.algo.perf_tracker.emission_rate == 'minute':
|
||||
daily_rollup = self.algo.perf_tracker.to_dict(
|
||||
emission_type='daily'
|
||||
)
|
||||
daily_rollup['daily_perf']['recorded_vars'] = \
|
||||
self.algo.recorded_vars
|
||||
yield daily_rollup
|
||||
tp = self.algo.perf_tracker.todays_performance
|
||||
tp.rollover()
|
||||
|
||||
if mkt_close <= self.algo.perf_tracker.last_close:
|
||||
before_last_close = \
|
||||
mkt_close < self.algo.perf_tracker.last_close
|
||||
@@ -149,12 +140,6 @@ class AlgorithmSimulator(object):
|
||||
# If at the end of backtest history,
|
||||
# skip advancing market close.
|
||||
pass
|
||||
if self.algo.perf_tracker.emission_rate == \
|
||||
'minute':
|
||||
self.algo.perf_tracker\
|
||||
.handle_intraday_market_close(
|
||||
mkt_open,
|
||||
mkt_close)
|
||||
|
||||
if before_last_close:
|
||||
self._call_before_trading_start(mkt_open)
|
||||
@@ -328,7 +313,7 @@ class AlgorithmSimulator(object):
|
||||
perf_process_trade(trade)
|
||||
|
||||
if benchmark_event_occurred:
|
||||
return self.get_message(dt)
|
||||
return self.generate_messages(dt)
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -356,9 +341,9 @@ class AlgorithmSimulator(object):
|
||||
if self.algo.datetime != dt:
|
||||
self.algo.on_dt_changed(dt)
|
||||
|
||||
def get_message(self, dt):
|
||||
def generate_messages(self, dt):
|
||||
"""
|
||||
Get a perf message for the given datetime.
|
||||
Generator that yields perf messages for the given datetime.
|
||||
"""
|
||||
# Ensure that updated_portfolio has been called at least once for this
|
||||
# dt before we emit a perf message. This is a no-op if
|
||||
@@ -371,13 +356,22 @@ class AlgorithmSimulator(object):
|
||||
perf_message = \
|
||||
self.algo.perf_tracker.handle_market_close_daily()
|
||||
perf_message['daily_perf']['recorded_vars'] = rvars
|
||||
return perf_message
|
||||
yield perf_message
|
||||
|
||||
elif self.algo.perf_tracker.emission_rate == 'minute':
|
||||
self.algo.perf_tracker.handle_minute_close(dt)
|
||||
perf_message = self.algo.perf_tracker.to_dict()
|
||||
perf_message['minute_perf']['recorded_vars'] = rvars
|
||||
return perf_message
|
||||
# close the minute in the tracker, and collect the daily message if
|
||||
# the minute is the close of the trading day
|
||||
minute_message, daily_message = \
|
||||
self.algo.perf_tracker.handle_minute_close(dt)
|
||||
|
||||
# collect and yield the minute's perf message
|
||||
minute_message['minute_perf']['recorded_vars'] = rvars
|
||||
yield minute_message
|
||||
|
||||
# if there was a daily perf message, collect and yield it
|
||||
if daily_message:
|
||||
daily_message['daily_perf']['recorded_vars'] = rvars
|
||||
yield daily_message
|
||||
|
||||
def update_universe(self, event):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user