ENH: Wires minutely emission of data from performance tracker.

Wires up performance tracker so that when `emission_rate` is set
to `minute`, the performance packets are sent out every minute,
instead of once per day.

Please note, the performance packets that are generated are not
ready for prime time consumption, this patch is merely a step towards
hooking up the ability to inspect minute data.

Known issues:
- The packets do not currently include risk information.
  Since we need to consider how this affects the denominators
  of the risk calculations.
This commit is contained in:
Eddie Hebert
2013-03-27 16:58:56 -04:00
parent 20d50450b6
commit 7679e5a581
3 changed files with 77 additions and 6 deletions
+40
View File
@@ -1030,3 +1030,43 @@ class TestPerformanceTracker(unittest.TestCase):
event['TRANSACTION'] = txn
return event
def test_minute_tracker(self):
""" Tests minute performance tracking."""
exc_tz = pytz.timezone('US/Eastern')
start_dt = trading.exchange_dt_in_utc(
datetime.datetime(2013, 3, 1, 9, 30, tzinfo=exc_tz))
end_dt = trading.exchange_dt_in_utc(
datetime.datetime(2013, 3, 1, 16, 0, tzinfo=exc_tz))
sim_params = SimulationParameters(
period_start=start_dt,
period_end=end_dt,
emission_rate='minute'
)
tracker = perf.PerformanceTracker(sim_params)
foo_event_1 = factory.create_trade('foo', 10.0, 20, start_dt)
bar_event_1 = factory.create_trade('bar', 100.0, 200, start_dt)
txn = Transaction(sid=foo_event_1.sid,
amount=-25,
dt=foo_event_1.dt,
price=10.0,
commission=0.50)
foo_event_1.TRANSACTION = txn
foo_event_2 = factory.create_trade(
'foo', 11.0, 20, start_dt + datetime.timedelta(minutes=1))
bar_event_2 = factory.create_trade(
'bar', 11.0, 20, start_dt + datetime.timedelta(minutes=1))
foo_event_3 = factory.create_trade(
'foo', 12.0, 30, start_dt + datetime.timedelta(minutes=2))
tracker.process_event(foo_event_1)
tracker.process_event(bar_event_1)
messages = tracker.process_event(foo_event_2)
tracker.process_event(bar_event_2)
messages += tracker.process_event(foo_event_3)
self.assertEquals(2, len(messages))
+28 -5
View File
@@ -161,6 +161,7 @@ class PerformanceTracker(object):
self.capital_base = self.sim_params.capital_base
self.cumulative_risk_metrics = \
risk.RiskMetricsIterative(self.period_start)
self.emission_rate = sim_params.emission_rate
# this performance period will span the entire simulation.
self.cumulative_performance = PerformancePeriod(
@@ -187,6 +188,7 @@ class PerformanceTracker(object):
serialize_positions=True
)
self.saved_dt = self.period_start
self.returns = []
# one indexed so that we reach 100%
self.day_count = 0.0
@@ -225,15 +227,27 @@ class PerformanceTracker(object):
Creates a dictionary representing the state of this tracker.
Returns a dict object of the form described in header comments.
"""
return {
_dict = {
'period_start': self.period_start,
'period_end': self.period_end,
'progress': self.progress,
'capital_base': self.capital_base,
'cumulative_perf': self.cumulative_performance.to_dict(),
'daily_perf': self.todays_performance.to_dict(),
'cumulative_risk_metrics': self.cumulative_risk_metrics.to_dict()
}
if self.emission_rate == 'daily':
_dict.update({'cumulative_risk_metrics':
self.cumulative_risk_metrics.to_dict(),
'daily_perf':
self.todays_performance.to_dict()})
if self.emission_rate == 'minute':
# Currently reusing 'todays_performance' for intraday trading
# result, should be analogous, but has the potential for needing
# its own configuration down the line.
# Naming as intraday to make clear that these results are
# being updated per minute
_dict['intraday_perf'] = self.todays_performance.to_dict()
return _dict
def process_event(self, event):
@@ -242,8 +256,17 @@ class PerformanceTracker(object):
if event.type == zp.DATASOURCE_TYPE.TRADE:
messages = []
while event.dt > self.market_close and event.dt < self.last_close:
messages.append(self.handle_market_close())
# This switch could also be handled by an inheritance
# with a DailyPerformanceTracker and a MinutePerformanceTracker
if self.emission_rate == 'daily':
while (event.dt > self.market_close and
event.dt < self.last_close):
messages.append(self.handle_market_close())
elif self.emission_rate == 'minute':
if event.dt > self.saved_dt:
messages.append(self.to_dict())
self.saved_dt = event.dt
if event.TRANSACTION:
self.txn_count += 1
+9 -1
View File
@@ -74,6 +74,11 @@ log = logbook.Logger('Transaction Simulator')
environment = None
def exchange_dt_in_utc(dt):
delorean = Delorean(dt, dt.tzinfo)
return delorean.shift(pytz.utc.zone).datetime
class TransactionSimulator(object):
def __init__(self):
@@ -253,7 +258,8 @@ Last successful date: %s" % self.market_open)
class SimulationParameters(object):
def __init__(self, period_start, period_end,
capital_base=10e3):
capital_base=10e3,
emission_rate='daily'):
global environment
if not environment:
@@ -264,6 +270,8 @@ class SimulationParameters(object):
self.period_end = period_end
self.capital_base = capital_base
self.emission_rate = emission_rate
assert self.period_start <= self.period_end, \
"Period start falls after period end."