mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-07 18:19:42 +08:00
ENH: Add basis for minute rate emission of performance.
- Create different benchmark containers in performance depending on emission rate. - Add a minute close method which updates algorithm and benchmark returns, and calculates the risk metrics depending on those methods. - Provide fake 0.0 values for annualized metrics like sharpe, sortino, and information, until we figure out how they should be treated in the context of minutely calculation. *NOTE* This does not fully work without the changes to the simulation loop by @fawce
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
#
|
||||
# Copyright 2013 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import unittest
|
||||
import datetime
|
||||
import pytz
|
||||
|
||||
from zipline.finance.trading import SimulationParameters
|
||||
from zipline.finance import risk
|
||||
|
||||
|
||||
class TestMinuteRisk(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
start_date = datetime.datetime(
|
||||
year=2006,
|
||||
month=1,
|
||||
day=3,
|
||||
hour=0,
|
||||
minute=0,
|
||||
tzinfo=pytz.utc)
|
||||
end_date = datetime.datetime(
|
||||
year=2006, month=1, day=3, tzinfo=pytz.utc)
|
||||
|
||||
self.sim_params = SimulationParameters(
|
||||
period_start=start_date,
|
||||
period_end=end_date
|
||||
)
|
||||
self.sim_params.emission_rate = 'minute'
|
||||
|
||||
def test_minute_risk(self):
|
||||
|
||||
risk_metrics = risk.RiskMetricsIterative(self.sim_params)
|
||||
|
||||
first_dt = self.sim_params.first_open
|
||||
second_dt = self.sim_params.first_open + datetime.timedelta(minutes=1)
|
||||
|
||||
risk_metrics.update(first_dt, 1.0, 2.0)
|
||||
|
||||
self.assertEquals(1, len(risk_metrics.alpha))
|
||||
|
||||
risk_metrics.update(second_dt, 3.0, 4.0)
|
||||
|
||||
self.assertEquals(2, len(risk_metrics.alpha))
|
||||
@@ -1047,19 +1047,31 @@ class TestPerformanceTracker(unittest.TestCase):
|
||||
dt=foo_event_1.dt,
|
||||
price=10.0,
|
||||
commission=0.50)
|
||||
benchmark_event_1 = Event({
|
||||
'dt': start_dt,
|
||||
'returns': 1.0,
|
||||
'type': DATASOURCE_TYPE.BENCHMARK
|
||||
})
|
||||
|
||||
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))
|
||||
benchmark_event_2 = Event({
|
||||
'dt': start_dt + datetime.timedelta(minutes=1),
|
||||
'returns': 2.0,
|
||||
'type': DATASOURCE_TYPE.BENCHMARK
|
||||
})
|
||||
|
||||
events = [
|
||||
foo_event_1,
|
||||
order_event_1,
|
||||
benchmark_event_1,
|
||||
txn_event_1,
|
||||
bar_event_1,
|
||||
foo_event_2,
|
||||
bar_event_2
|
||||
benchmark_event_2,
|
||||
bar_event_2,
|
||||
]
|
||||
|
||||
messages = {date: snapshot[-1].perf_messages[0] for date, snapshot in
|
||||
|
||||
@@ -166,9 +166,13 @@ class PerformanceTracker(object):
|
||||
risk.RiskMetricsIterative(self.sim_params)
|
||||
self.emission_rate = sim_params.emission_rate
|
||||
|
||||
# Temporarily hold these here as we work on streaming benchmarks.
|
||||
self.all_benchmark_returns = pd.Series(
|
||||
index=trading.environment.trading_days)
|
||||
if self.emission_rate == 'daily':
|
||||
self.all_benchmark_returns = pd.Series(
|
||||
index=trading.environment.trading_days)
|
||||
elif self.emission_rate == 'minute':
|
||||
self.all_benchmark_returns = pd.Series(index=pd.date_range(
|
||||
self.sim_params.first_open, self.sim_params.last_close,
|
||||
freq='Min'))
|
||||
|
||||
# this performance period will span the entire simulation.
|
||||
self.cumulative_performance = PerformancePeriod(
|
||||
@@ -244,6 +248,9 @@ class PerformanceTracker(object):
|
||||
event.portfolio = None
|
||||
new_snapshot.append(event)
|
||||
|
||||
|
||||
self.handle_minute_close(date)
|
||||
|
||||
if new_snapshot:
|
||||
new_snapshot[-1].perf_messages = [self.to_dict()]
|
||||
new_snapshot[-1].portfolio = self.get_portfolio()
|
||||
@@ -342,6 +349,15 @@ class PerformanceTracker(object):
|
||||
|
||||
return messages
|
||||
|
||||
def handle_minute_close(self, dt):
|
||||
#update risk metrics for cumulative performance
|
||||
algorithm_returns = pd.Series({dt: self.todays_performance.returns})
|
||||
benchmark_returns = pd.Series({dt: self.all_benchmark_returns[dt]})
|
||||
|
||||
self.cumulative_risk_metrics.update(dt,
|
||||
algorithm_returns,
|
||||
benchmark_returns)
|
||||
|
||||
def handle_market_close(self):
|
||||
# add the return results from today to the list of DailyReturn objects.
|
||||
todays_date = self.market_close.replace(hour=0, minute=0, second=0,
|
||||
|
||||
+25
-5
@@ -537,8 +537,20 @@ class RiskMetricsIterative(RiskMetricsBase):
|
||||
|
||||
self.trading_days = all_trading_days[mask]
|
||||
|
||||
self.algorithm_returns_cont = pd.Series(index=self.trading_days)
|
||||
self.benchmark_returns_cont = pd.Series(index=self.trading_days)
|
||||
self.sim_params = sim_params
|
||||
|
||||
if sim_params.emission_rate == 'daily':
|
||||
self.algorithm_returns_cont = pd.Series(index=self.trading_days)
|
||||
self.benchmark_returns_cont = pd.Series(index=self.trading_days)
|
||||
|
||||
elif sim_params.emission_rate == 'minute':
|
||||
|
||||
self.algorithm_returns_cont = pd.Series(index=pd.date_range(
|
||||
sim_params.first_open, sim_params.last_close,
|
||||
freq="Min"))
|
||||
self.benchmark_returns_cont = pd.Series(index=pd.date_range(
|
||||
sim_params.first_open, sim_params.last_close,
|
||||
freq="Min"))
|
||||
|
||||
self.algorithm_returns = None
|
||||
self.benchmark_returns = None
|
||||
@@ -629,9 +641,6 @@ algorithm_returns ({algo_count}) in range {start} : {end} on {dt}"
|
||||
'treasury_period_return': self.treasury_period_return,
|
||||
'algorithm_period_return': self.algorithm_period_returns[-1],
|
||||
'benchmark_period_return': self.benchmark_period_returns[-1],
|
||||
'sharpe': self.sharpe[-1],
|
||||
'sortino': self.sortino[-1],
|
||||
'information': self.information[-1],
|
||||
'beta': self.beta[-1],
|
||||
'alpha': self.alpha[-1],
|
||||
'excess_return': self.excess_returns[-1],
|
||||
@@ -639,6 +648,17 @@ algorithm_returns ({algo_count}) in range {start} : {end} on {dt}"
|
||||
'period_label': period_label
|
||||
}
|
||||
|
||||
if self.sim_params.emission_rate == 'daily':
|
||||
# Some risk metrics only make sense in a context of daily
|
||||
# risk calculations.
|
||||
rval['sharpe'] = self.sharpe[-1]
|
||||
rval['sortino'] = self.sortino[-1]
|
||||
rval['information'] = self.information[-1]
|
||||
elif self.sim_params.emission_rate == 'minute':
|
||||
rval['sharpe'] = 0.0
|
||||
rval['sortino'] = 0.0
|
||||
rval['information'] = 0.0
|
||||
|
||||
# check if a field in rval is nan, and replace it with
|
||||
# None.
|
||||
def check_entry(key, value):
|
||||
|
||||
Reference in New Issue
Block a user