updated factory to load from msgpack files, added tests for risk, parameterized treasury and benchmark data.

This commit is contained in:
fawce
2012-03-07 01:48:03 -05:00
parent d648899d5f
commit fec5e98a8d
3 changed files with 338 additions and 50 deletions
+35 -46
View File
@@ -4,7 +4,6 @@ import pytz
import numpy as np import numpy as np
import numpy.linalg as la import numpy.linalg as la
import zipline.util as qutil import zipline.util as qutil
import zipline.db as db
import zipline.protocol as zp import zipline.protocol as zp
from pymongo import ASCENDING, DESCENDING from pymongo import ASCENDING, DESCENDING
@@ -13,10 +12,17 @@ class daily_return():
def __init__(self, date, returns): def __init__(self, date, returns):
self.date = date self.date = date
self.returns = returns self.returns = returns
def __repr__(self):
return str(self.date) + " - " + str(self.returns)
class periodmetrics(): class periodmetrics():
def __init__(self, start_date, end_date, returns, benchmark_returns): def __init__(self, start_date, end_date, returns, benchmark_returns, treasury_curves, trading_calendar):
self.db = db.DbConnection.get()[1] """
:param treasury_curves: {datetime in utc -> {duration label -> interest rate}}
"""
self.treasury_curves = treasury_curves
self.start_date = start_date self.start_date = start_date
self.end_date = end_date self.end_date = end_date
self.trading_calendar = trading_calendar self.trading_calendar = trading_calendar
@@ -134,13 +140,18 @@ class periodmetrics():
else: else:
self.treasury_duration = '30year' self.treasury_duration = '30year'
treasuryQS = self.db.treasury_curves.find(
spec={"date" : {"$lte" : self.end_date}}, one_day = datetime.timedelta(days=1)
sort=[("date",DESCENDING)],
limit=3, curve = None
slave_ok=True) #in case end date is not a trading day, search for the next market day for an interest rate
for i in range(7):
for curve in treasuryQS: if(self.treasury_curves.has_key(self.end_date + i * one_day)):
#qutil.LOGGER.info(self.treasury_curves[self.end_date + i * one_day])
curve = self.treasury_curves[self.end_date + i * one_day]
break
if curve:
self.treasury_curve = curve self.treasury_curve = curve
rate = self.treasury_curve[self.treasury_duration] rate = self.treasury_curve[self.treasury_duration]
#1month note data begins in 8/2001, so we can use 3month instead. #1month note data begins in 8/2001, so we can use 3month instead.
@@ -149,17 +160,18 @@ class periodmetrics():
if rate != None: if rate != None:
return rate * (td.days + 1) / 365 return rate * (td.days + 1) / 365
raise Exception("no rate for end date = {dt} and term = {term}, from {curve}. Using zero.".format(dt=self.end_date, raise Exception("no rate for end date = {dt} and term = {term}. Using zero.".format(dt=self.end_date,
term=self.treasury_duration, term=self.treasury_duration))
curve=self.treasury_curve['date']))
class riskmetrics(): class riskmetrics():
def __init__(self, algorithm_returns): def __init__(self, algorithm_returns, benchmark_returns, treasury_curves, trading_calendar):
"""algorithm_returns needs to be a list of daily_return objects sorted in date ascending order""" """algorithm_returns needs to be a list of daily_return objects sorted in date ascending order"""
self.db = db.DbConnection.get()[1]
self.algorithm_returns = algorithm_returns self.algorithm_returns = algorithm_returns
self.bm_returns = [x for x in benchmark_returns if x.date >= self.algorithm_returns[0].date and x.date <= self.algorithm_returns[-1].date] self.bm_returns = [x for x in benchmark_returns if x.date >= self.algorithm_returns[0].date and x.date <= self.algorithm_returns[-1].date]
self.treasury_curves = treasury_curves
self.trading_calendar = trading_calendar
qutil.LOGGER.debug("#### {start} thru {end} with {count} trading_days of {total} possible".format(start=self.algorithm_returns[0].date, qutil.LOGGER.debug("#### {start} thru {end} with {count} trading_days of {total} possible".format(start=self.algorithm_returns[0].date,
end=self.algorithm_returns[-1].date, end=self.algorithm_returns[-1].date,
@@ -191,23 +203,16 @@ class riskmetrics():
if(cur_end > the_end): if(cur_end > the_end):
break break
#qutil.LOGGER.debug("start: {start}, end: {end}".format(start=cur_start, end=cur_end)) #qutil.LOGGER.debug("start: {start}, end: {end}".format(start=cur_start, end=cur_end))
cur_period_metrics = periodmetrics(start_date=cur_start, end_date=cur_end, returns=self.algorithm_returns, benchmark_returns=self.bm_returns) cur_period_metrics = periodmetrics(start_date=cur_start,
end_date=cur_end,
returns=self.algorithm_returns,
benchmark_returns=self.bm_returns,
treasury_curves=self.treasury_curves,
trading_calendar=self.trading_calendar)
ends.append(cur_period_metrics) ends.append(cur_period_metrics)
cur_start = advance_by_months(cur_start, 1) cur_start = advance_by_months(cur_start, 1)
return ends return ends
def store_to_db(self, back_test_run_id):
col = self.db.risk_metrics
for period in self.month_periods:
for metric in ["algorithm_period_returns", "benchmark_period_returns", "excess_return", "trading_days", "benchmark_volatility", "algorithm_volatility", "sharpe", "beta", "alpha", "max_drawdown"]:
record = {'back_test_run_id':back_test_run_id}
record['ending_on'] = period.end_date
record['metric_name'] = metric
for dur in ["month", "three_month", "six_month", "year", "three_year", "five_year"]:
record[dur] = self.find_metric_by_end(period.end_date, dur, metric)
#qutil.LOGGER.debug("storing {val} for {metric} and {dur}".format(val=record[dur], metric=metric, dur=dur))
col.insert(record, safe=True)
def find_metric_by_end(self, end_date, duration, metric): def find_metric_by_end(self, end_date, duration, metric):
col = getattr(self, duration + "_periods") col = getattr(self, duration + "_periods")
@@ -233,9 +238,10 @@ def advance_by_months(dt, jump_in_months):
class TradingCalendar(object): class TradingCalendar(object):
def __init__(self, benchmark_returns): def __init__(self, benchmark_returns, treasury_curves):
self.trading_days = [] self.trading_days = []
self.trading_day_map = {} self.trading_day_map = {}
self.treasury_curves = treasury_curves
for bm in benchmark_returns: for bm in benchmark_returns:
self.trading_days.append(bm.date) self.trading_days.append(bm.date)
self.trading_day_map[bm.date] = bm self.trading_day_map[bm.date] = bm
@@ -253,21 +259,4 @@ class TradingCalendar(object):
return self.trading_day_map[date].returns return self.trading_day_map[date].returns
else: else:
return 0.0 return 0.0
def get_benchmark_data():
bmQS = db.DbConnection.get()[1].bench_marks.find(
spec={"symbol" : "GSPC"},
sort=[("date",ASCENDING)],
slave_ok=True,
as_class=zp.namedict)
bm_returns = []
for bm in bmQS:
bm_r = daily_return(date=bm.date.replace(tzinfo=pytz.utc), returns=bm.returns)
bm_returns.append(bm_r)
cal = TradingCalendar(bm_returns)
return bm_returns, cal
benchmark_returns, trading_calendar = get_benchmark_data()
+65 -4
View File
@@ -1,9 +1,26 @@
import datetime import datetime
import pytz import pytz
import msgpack
import random
import zipline.util as qutil import zipline.util as qutil
import zipline.finance.risk as risk import zipline.finance.risk as risk
import zipline.protocol as zp import zipline.protocol as zp
def load_market_data():
fp_bm = open("./etc/benchmark.msgpack", "rb")
bm_map = msgpack.loads(fp_bm.read())
bm_returns = []
for epoch, returns in bm_map.iteritems():
bm_returns.append(risk.daily_return(date=datetime.datetime.fromtimestamp(epoch).replace(hour=0, minute=0, second=0, tzinfo=pytz.utc), returns=returns))
bm_returns = sorted(bm_returns, key=lambda(x): x.date)
fp_tr = open("./etc/treasury_curves.msgpack", "rb")
tr_map = msgpack.loads(fp_tr.read())
tr_curves = {}
for epoch, curve in tr_map.iteritems():
tr_curves[datetime.datetime.fromtimestamp(epoch).replace(hour=0, minute=0, second=0, tzinfo=pytz.utc)] = curve
return bm_returns, tr_curves
def create_trade(sid, price, amount, datetime): def create_trade(sid, price, amount, datetime):
row = { row = {
@@ -16,14 +33,14 @@ def create_trade(sid, price, amount, datetime):
} }
return row return row
def create_trade_history(sid, prices, amounts, start_time, interval): def create_trade_history(sid, prices, amounts, start_time, interval, trading_calendar):
i = 0 i = 0
trades = [] trades = []
current = start_time.replace(tzinfo = pytz.utc) current = start_time.replace(tzinfo = pytz.utc)
for price, amount in zip(prices, amounts): for price, amount in zip(prices, amounts):
if(risk.trading_calendar.is_trading_day(current)): if(trading_calendar.is_trading_day(current)):
trade = create_trade(sid, price, amount, current) trade = create_trade(sid, price, amount, current)
trades.append(trade) trades.append(trade)
@@ -38,13 +55,13 @@ def createTxn(sid, price, amount, datetime, btrid=None):
price=price, transaction_cost=-1*price*amount) price=price, transaction_cost=-1*price*amount)
return txn return txn
def createTxnHistory(sid, priceList, amtList, startTime, interval): def create_transaction_history(sid, priceList, amtList, startTime, interval, trading_calendar):
txns = [] txns = []
current = startTime current = startTime
for price, amount in zip(priceList, amtList): for price, amount in zip(priceList, amtList):
if risk.trading_calendar.is_trading_day(current): if trading_calendar.is_trading_day(current):
txns.append(createTxn(sid, price, amount, current)) txns.append(createTxn(sid, price, amount, current))
current = current + interval current = current + interval
@@ -52,3 +69,47 @@ def createTxnHistory(sid, priceList, amtList, startTime, interval):
current = current + datetime.timedelta(days=1) current = current + datetime.timedelta(days=1)
return txns return txns
def create_returns(daycount, start, trading_calendar):
i = 0
test_range = []
current = start.replace(tzinfo=pytz.utc)
one_day = datetime.timedelta(days = 1)
while i < daycount:
i += 1
r = risk.daily_return(current, random.random())
test_range.append(r)
current = current + one_day
return [ x for x in test_range if(trading_calendar.is_trading_day(x.date)) ]
def create_returns_from_range(start, end, trading_calendar):
current = start.replace(tzinfo=pytz.utc)
end = end.replace(tzinfo=pytz.utc)
one_day = datetime.timedelta(days = 1)
test_range = []
i = 0
while current <= end:
current = current + one_day
if(not trading_calendar.is_trading_day(current)):
continue
r = risk.daily_return(current, random.random())
i += 1
test_range.append(r)
return test_range
def create_returns_from_list(returns, start, trading_calendar):
current = start.replace(tzinfo=pytz.utc)
one_day = datetime.timedelta(days = 1)
test_range = []
i = 0
while len(test_range) < len(returns):
if(trading_calendar.is_trading_day(current)):
r = risk.daily_return(current, returns[i])
i += 1
test_range.append(r)
current = current + one_day
return sorted(test_range, key=lambda(x):x.date)
+238
View File
@@ -0,0 +1,238 @@
import unittest
import copy
import datetime
import calendar
import pytz
import zipline.finance.risk as risk
import zipline.test.factory as factory
import zipline.util as qutil
class Risk(unittest.TestCase):
def setUp(self):
qutil.configure_logging()
self.benchmark_returns, self.treasury_curves = factory.load_market_data()
self.trading_calendar = risk.TradingCalendar(self.benchmark_returns, self.treasury_curves)
self.onesec = datetime.timedelta(seconds=1)
self.oneday = datetime.timedelta(days=1)
self.tradingday = datetime.timedelta(hours=6, minutes=30)
self.dt = datetime.datetime.utcnow()
returns = [0.0093,-0.0193,0.0351,0.0396,0.0338,-0.0211,0.0389,0.0326,-0.0137,-0.0411,-0.0032,0.0149,0.0133,0.0348,0.042,-0.0455,0.0262,-0.0461,0.0021,-0.0273,-0.0429,0.0427,-0.0104,0.0346,-0.0311,0.0003,0.0211,0.0248,-0.0215,0.004,0.0267,0.0029,-0.0369,0.0057,0.0298,-0.0179,-0.0361,-0.0401,-0.0123,-0.005,0.0203,-0.041,0.0011,0.0118,0.0103,-0.0184,-0.0437,0.0411,-0.0242,-0.0054,-0.0039,-0.0273,-0.0075,0.0064,-0.0376,0.0424,0.0399,0.019,0.0236,-0.0284,-0.0341,0.0266,0.05,0.0069,-0.0442,-0.016,0.0173,0.0348,-0.0404,-0.0068,-0.0376,0.0356,0.0043,-0.0481,-0.0134,0.0257,0.0442,0.0234,0.0394,0.0376,-0.0147,-0.0098,0.0474,-0.0102,0.0138,0.0286,0.0347,0.0279,-0.0067,0.0462,-0.0432,0.0247,0.0174,-0.0305,-0.0317,-0.0068,0.0264,-0.0257,-0.0328,0.0092,0.0288,-0.002,0.0288,0.028,-0.0093,0.0178,-0.0365,-0.0086,-0.0133,-0.0309,0.0473,-0.0149,0.0378,-0.0316,-0.0292,-0.0453,-0.0451,0.0093,0.0397,-0.0361,-0.0168,-0.0494,-0.0143,-0.0405,-0.0349,0.0069,0.0378,-0.0233,-0.0492,0.018,-0.0386,0.0339,0.0119,0.0454,0.0118,-0.011,-0.0254,0.0266,-0.0366,-0.0211,0.0399,0.0307,0.035,-0.0402,0.0304,-0.0031,0.0256,0.0134,-0.0019,-0.0235,-0.0058,-0.0117,0.0051,-0.0451,-0.0466,-0.0124,0.0283,-0.0499,0.0318,-0.0028,0.0203,0.005,0.0085,0.0048,0.0277,0.0159,-0.0149,0.035,0.0404,-0.01,0.0377,0.0302,0.0046,-0.0328,-0.0469,0.0071,-0.0382,-0.0214,0.0429,0.0145,-0.0279,-0.0172,0.0423,0.041,-0.0183,0.0137,-0.0412,-0.0348,0.0302,0.0248,0.0051,-0.0298,-0.0103,-0.0333,-0.0399,0.0485,-0.0166,0.0384,0.0259,-0.0163,0.0357,0.0308,-0.0386,0.0481,-0.0446,-0.0282,-0.0037,0.0202,0.0216,0.0113,0.0194,0.0392,0.0016,0.0268,-0.0155,-0.027,0.02,0.0216,-0.0009,0.022,0,0.041,0.0133,-0.0382,0.0495,-0.0221,-0.0329,-0.0033,-0.0089,-0.0129,-0.0252,0.048,-0.0307,-0.0357,0.0033,-0.0412,-0.0407,0.0455,0.0159,-0.0051,-0.0274,-0.0213,0.0361,0.0051,-0.0378,0.0084,0.0066,-0.0103,-0.0037,0.0478,-0.0278]
start_date = datetime.datetime(year=2006, month=1, day=1, tzinfo=pytz.utc)
self.algo_returns_06 = factory.create_returns_from_list(returns, start_date, self.trading_calendar)
end_date = datetime.datetime(year=2006, month=12, day=31, tzinfo=pytz.utc)
self.metrics_06 = risk.riskmetrics(self.algo_returns_06, self.benchmark_returns, self.treasury_curves, self.trading_calendar)
def tearDown(self):
return
def test_factory(self):
returns = [0.1] * 100
start_date = datetime.datetime(year=2006, month=1, day=1, tzinfo=pytz.utc)
r_objects = factory.create_returns_from_list(returns, start_date, self.trading_calendar)
self.assertTrue(r_objects[-1].date <= datetime.datetime(year=2006, month=12, day=31, tzinfo=pytz.utc))
def test_drawdown(self):
start_date = datetime.datetime(year=2006, month=1, day=1)
returns = factory.create_returns_from_list([1.0,-0.5,0.8,.17,1.0,-0.1,-0.45], start_date, self.trading_calendar)
#200, 100, 180, 210.6, 421.2, 379.8, 208.494
metrics = risk.periodmetrics(returns[0].date, returns[-1].date, returns, self.benchmark_returns, self.treasury_curves, self.trading_calendar)
self.assertEqual(metrics.max_drawdown, 0.505)
def test_benchmark_returns_06(self):
start_date = datetime.datetime(year=2006, month=1, day=1)
end_date = datetime.datetime(year=2006, month=12, day=31)
returns = factory.create_returns_from_range(start_date, end_date, self.trading_calendar)
metrics = risk.riskmetrics(returns, self.benchmark_returns, self.treasury_curves, self.trading_calendar)
self.assertEqual([round(x.benchmark_period_returns, 4) for x in metrics.month_periods],
[0.0255,0.0005,0.0111,0.0122,-0.0309,0.0001,0.0051,0.0213,0.0246,0.0315,0.0165,0.0126])
self.assertEqual([round(x.benchmark_period_returns, 4) for x in metrics.three_month_periods],
[0.0373,0.0239,-0.0083,-0.0191,-0.0259,0.0266,0.0517,0.0793,0.0743,0.0617])
self.assertEqual([round(x.benchmark_period_returns, 4) for x in metrics.six_month_periods],
[0.0176,-0.0027,0.0181,0.0316,0.0514,0.1028,0.1166])
self.assertEqual([round(x.benchmark_period_returns,4) for x in metrics.year_periods],[0.1362])
def test_trading_days_06(self):
start_date = datetime.datetime(year=2006, month=1, day=1)
end_date = datetime.datetime(year=2006, month=12, day=31)
returns = factory.create_returns_from_range(start_date, end_date, self.trading_calendar)
metrics = risk.riskmetrics(returns, self.benchmark_returns, self.treasury_curves, self.trading_calendar)
self.assertEqual([x.trading_days for x in metrics.year_periods],[251])
self.assertEqual([x.trading_days for x in metrics.month_periods],[20,19,23,19,22,22,20,23,20,22,21,20])
def test_benchmark_volatility_06(self):
start_date = datetime.datetime(year=2006, month=1, day=1)
end_date = datetime.datetime(year=2006, month=12, day=31)
returns = factory.create_returns_from_range(start_date, end_date, self.trading_calendar)
metrics = risk.riskmetrics(returns, self.benchmark_returns, self.treasury_curves, self.trading_calendar)
self.assertEqual([round(x.benchmark_volatility, 3) for x in metrics.month_periods],
[0.031,0.026,0.024,0.025,0.037,0.047,0.039,0.022,0.023,0.021,0.025,0.019])
self.assertEqual([round(x.benchmark_volatility, 3) for x in metrics.three_month_periods],
[0.047,0.042,0.050,0.064,0.070,0.064,0.049,0.037,0.039,0.037])
self.assertEqual([round(x.benchmark_volatility, 3) for x in metrics.six_month_periods],
[0.079,0.082,0.081,0.081,0.08,0.074,0.061])
self.assertEqual([round(x.benchmark_volatility, 3) for x in metrics.year_periods],[0.100])
def test_algorithm_returns_06(self):
self.assertEqual([round(x.algorithm_period_returns, 3) for x in self.metrics_06.month_periods],[0.101,-0.062,-0.041,0.092,0.135,-0.25,0.076,-0.003,-0.024,0.072,0.063,-0.071])
self.assertEqual([round(x.algorithm_period_returns, 3) for x in self.metrics_06.three_month_periods],[-0.009,-0.017,0.188,-0.071,-0.085,-0.196,0.047,0.043,0.112,0.058])
self.assertEqual([round(x.algorithm_period_returns, 3) for x in self.metrics_06.six_month_periods],[-0.08,-0.101,-0.044,-0.027,-0.045,-0.106,0.108])
self.assertEqual([round(x.algorithm_period_returns, 3) for x in self.metrics_06.year_periods],[0.02])
def test_algorithm_volatility_06(self):
self.assertEqual([round(x.algorithm_volatility, 3) for x in self.metrics_06.month_periods],[0.137,0.12,0.13,0.142,0.128,0.14,0.141,0.118,0.143,0.144,0.117,0.135])
self.assertEqual([round(x.algorithm_volatility, 3) for x in self.metrics_06.three_month_periods],[0.222,0.224,0.229,0.243,0.243,0.235,0.23,0.231,0.231,0.227])
self.assertEqual([round(x.algorithm_volatility, 3) for x in self.metrics_06.six_month_periods],[0.328,0.329,0.329,0.333,0.334,0.329,0.321])
self.assertEqual([round(x.algorithm_volatility, 3) for x in self.metrics_06.year_periods],[0.458])
def test_algorithm_sharpe_06(self):
self.assertEqual([round(x.sharpe, 3) for x in self.metrics_06.month_periods],[0.711,-0.541,-0.348,0.625,1.017,-1.809,0.508,-0.062,-0.193,0.467,0.502,-0.557])
self.assertEqual([round(x.sharpe, 3) for x in self.metrics_06.three_month_periods],[-0.094,-0.129,0.769,-0.342,-0.402,-0.888,0.153,0.131,0.432,0.2])
self.assertEqual([round(x.sharpe, 3) for x in self.metrics_06.six_month_periods],[-0.322,-0.383,-0.213,-0.156,-0.213,-0.398,0.257])
self.assertEqual([round(x.sharpe, 3) for x in self.metrics_06.year_periods],[-0.066])
def dtest_algorithm_beta_06(self):
self.assertEqual([round(x.beta, 3) for x in self.metrics_06.month_periods],[0.553,0.583,-2.168,-0.548,1.463,-0.322,-1.38,1.473,-1.315,-0.7,0.352,-2.002])
self.assertEqual([round(x.beta, 3) for x in self.metrics_06.three_month_periods],[-0.075,-0.637,0.124,0.186,-0.204,-0.497,-0.867,-0.173,-0.499,-0.563])
self.assertEqual([round(x.beta, 3) for x in self.metrics_06.six_month_periods],[-0.075,-0.637,0.124,0.186,-0.204,-0.497,-0.867,-0.173,-0.499,-0.563])
self.assertEqual([round(x.beta, 3) for x in self.metrics_06.year_periods],[-0.219])
def dtest_algorithm_alpha_06(self):
self.assertEqual([round(x.alpha, 3) for x in self.metrics_06.month_periods],[0.085,-0.063,-0.03,0.093,0.182,-0.255,0.073,-0.032,0,0.086,0.054,-0.058])
self.assertEqual([round(x.alpha, 3) for x in self.metrics_06.three_month_periods],[-0.051,-0.021,0.179,-0.077,-0.106,-0.202,0.069,0.042,0.13,0.073])
self.assertEqual([round(x.alpha, 3) for x in self.metrics_06.six_month_periods],[-0.105,-0.135,-0.072,-0.051,-0.066,-0.094,0.152])
self.assertEqual([round(x.alpha, 3) for x in self.metrics_06.year_periods],[-0.011])
#FIXME: Covariance is not matching excel precisely enough to run the test. Month 4 seems to be the problem. Variance is disabled
#just to avoid distraction - it is much closer than covariance and can probably pass with 6 significant digits instead of 7.
#re-enable variance, alpha, and beta tests once this is resolved
def dtest_algorithm_covariance_06(self):
metric = self.metrics_06.month_periods[3]
print repr(metric)
print "----"
self.assertEqual([round(x.algorithm_covariance, 7) for x in self.metrics_06.month_periods],[0.0000289,0.0000222,-0.0000554,-0.0000192,0.0000954,-0.0000333,-0.0001111,0.0000322,-0.0000349,-0.0000143,0.0000108,-0.0000386])
self.assertEqual([round(x.algorithm_covariance, 7) for x in self.metrics_06.three_month_periods],[-0.0000026,-0.0000189,0.0000049,0.0000121,-0.0000158,-0.000031,-0.0000336,-0.0000036,-0.0000119,-0.0000122])
self.assertEqual([round(x.algorithm_covariance, 7) for x in self.metrics_06.six_month_periods],[0.000005,-0.0000172,-0.0000142,-0.0000102,-0.0000089,-0.0000207,-0.0000229])
self.assertEqual([round(x.algorithm_covariance, 7) for x in self.metrics_06.year_periods],[-8.75273E-06])
def dtest_benchmark_variance_06(self):
self.assertEqual([round(x.benchmark_variance, 7) for x in self.metrics_06.month_periods],[0.0000496,0.000036,0.0000244,0.0000332,0.0000623,0.0000989,0.0000765,0.0000209,0.0000252,0.0000194,0.0000292,0.0000183])
self.assertEqual([round(x.benchmark_variance, 7) for x in self.metrics_06.three_month_periods],[0.0000351,0.0000298,0.0000395,0.0000648,0.0000773,0.0000625,0.0000387,0.0000211,0.0000238,0.0000217])
self.assertEqual([round(x.benchmark_variance, 7) for x in self.metrics_06.six_month_periods],[0.0000499,0.0000538,0.0000508,0.0000517,0.0000492,0.0000432,0.00003])
self.assertEqual([round(x.benchmark_variance, 7) for x in self.metrics_06.year_periods],[0.0000399])
def test_benchmark_returns_08(self):
start_date = datetime.datetime(year=2008, month=1, day=1)
end_date = datetime.datetime(year=2008, month=12, day=31)
returns = factory.create_returns_from_range(start_date, end_date, self.trading_calendar)
metrics = risk.riskmetrics(returns, self.benchmark_returns, self.treasury_curves, self.trading_calendar)
self.assertEqual([round(x.benchmark_period_returns, 3) for x in metrics.month_periods],
[-0.061,-0.035,-0.006,0.048,0.011,-0.086,-0.01,0.012,-0.091,-0.169,-0.075,0.008])
self.assertEqual([round(x.benchmark_period_returns, 3) for x in metrics.three_month_periods],
[-0.099,0.005,0.052,-0.032,-0.085,-0.084,-0.089,-0.236,-0.301,-0.226])
self.assertEqual([round(x.benchmark_period_returns, 3) for x in metrics.six_month_periods],
[-0.128,-0.081,-0.036,-0.118,-0.301,-0.360,-0.294])
self.assertEqual([round(x.benchmark_period_returns,3) for x in metrics.year_periods],[-0.385])
def test_trading_days_08(self):
start_date = datetime.datetime(year=2008, month=1, day=1)
end_date = datetime.datetime(year=2008, month=12, day=31)
returns = factory.create_returns_from_range(start_date, end_date, self.trading_calendar)
metrics = risk.riskmetrics(returns, self.benchmark_returns, self.treasury_curves, self.trading_calendar)
self.assertEqual([x.trading_days for x in metrics.year_periods],[253])
self.assertEqual([x.trading_days for x in metrics.month_periods],[21,20,20,22,21,21,22,21,21,23,19,22])
def test_benchmark_volatility_08(self):
start_date = datetime.datetime(year=2008, month=1, day=1)
end_date = datetime.datetime(year=2008, month=12, day=31)
returns = factory.create_returns_from_range(start_date, end_date, self.trading_calendar)
metrics = risk.riskmetrics(returns, self.benchmark_returns, self.treasury_curves, self.trading_calendar)
self.assertEqual([round(x.benchmark_volatility, 3) for x in metrics.month_periods],
[0.07,0.058,0.082,0.054,0.041,0.057,0.068,0.06,0.157,0.244,0.195,0.145])
self.assertEqual([round(x.benchmark_volatility, 3) for x in metrics.three_month_periods],
[0.120,0.113,0.105,0.09,0.098,0.107,0.179,0.293,0.344,0.340])
self.assertEqual([round(x.benchmark_volatility, 3) for x in metrics.six_month_periods],
[0.15,0.149,0.15,0.2,0.308,0.36,0.383])
#TODO: ugly, but I can't get the rounded float to match. maybe we need a different test that checks the difference between the numbers
self.assertEqual([round(x.benchmark_volatility, 3) for x in metrics.year_periods],[0.41099999999999998])
def test_treasury_returns_06(self):
start_date = datetime.datetime(year=2006, month=1, day=1)
end_date = datetime.datetime(year=2006, month=12, day=31)
returns = factory.create_returns_from_range(start_date, end_date, self.trading_calendar)
metrics = risk.riskmetrics(returns, self.benchmark_returns, self.treasury_curves, self.trading_calendar)
self.assertEqual([round(x.treasury_period_return, 4) for x in metrics.month_periods],
[0.0037,0.0034,0.0039,0.0038,0.0040,0.0037,0.0043,0.0043,0.0038,0.0044,0.0043,0.0041])
self.assertEqual([round(x.treasury_period_return, 4) for x in metrics.three_month_periods],
[0.0114,0.0118,0.0122,0.0125,0.0129,0.0127,0.0123,0.0128,0.0125,0.0128])
self.assertEqual([round(x.treasury_period_return, 4) for x in metrics.six_month_periods],
[0.0260,0.0257,0.0258,0.0252,0.0259,0.0256,0.0258])
self.assertEqual([round(x.treasury_period_return, 4) for x in metrics.year_periods],
[0.0500])
def test_benchmarkrange(self):
self.check_year_range(datetime.datetime(year=2008,month=1,day=1), 2)
def test_partial_month(self):
start_date = datetime.datetime(year=1991, month=1, day=1)
returns = factory.create_returns(365 * 5 + 2, start_date, self.trading_calendar) #1992 and 1996 were leap years
returns = returns[:-10] #truncate the returns series to end mid-month
metrics = risk.riskmetrics(returns, self.benchmark_returns, self.treasury_curves, self.trading_calendar)
total_months = 60
self.check_metrics(metrics, total_months, start_date)
def check_year_range(self, start_date, years):
if(start_date.month <= 2):
ld = calendar.leapdays(start_date.year, start_date.year + years)
else:
#because we may catch the leap of the last year, and i think this func is [start,end)
ld = calendar.leapdays(start_date.year, start_date.year + years + 1)
returns = factory.create_returns(365 * years + ld, start_date, self.trading_calendar)
metrics = risk.riskmetrics(returns, self.benchmark_returns, self.treasury_curves, self.trading_calendar)
total_months = years * 12
self.check_metrics(metrics, total_months, start_date)
def check_metrics(self, metrics, total_months, start_date):
self.assert_range_length(metrics.month_periods, total_months, 1, start_date)
self.assert_range_length(metrics.three_month_periods, total_months, 3, start_date)
self.assert_range_length(metrics.six_month_periods, total_months, 6, start_date)
self.assert_range_length(metrics.year_periods, total_months, 12, start_date)
self.assert_range_length(metrics.three_year_periods, total_months, 36, start_date)
self.assert_range_length(metrics.five_year_periods, total_months, 60, start_date)
def assert_last_day(self, period_end):
#30 days has september, april, june and november
if(period_end.month in [9,4,6,11]):
self.assertEqual(period_end.day, 30)
#all the rest have 31, except for february
elif(period_end.month != 2):
self.assertEqual(period_end.day, 31)
else:
if calendar.isleap(period_end.year):
self.assertEqual(period_end.day, 29)
else:
self.assertEqual(period_end.day, 28)
def assert_month(self, start_month, actual_end_month):
if start_month == 1:
expected_end_month = 12
else:
expected_end_month = start_month - 1
self.assertEqual(expected_end_month, actual_end_month)
def assert_range_length(self, col, total_months, period_length, start_date):
if(period_length > total_months):
self.assertEqual(len(col), 0)
else:
self.assertEqual(len(col), total_months - (period_length - 1), "mismatch for total months - expected:{total_months}/actual:{actual}, period:{period_length}, start:{start_date}, calculated end:{end}".format(
total_months=total_months,
period_length=period_length,
start_date=start_date,
end=col[-1].end_date,
actual=len(col)
))
self.assert_month(start_date.month, col[-1].end_date.month)
self.assert_last_day(col[-1].end_date)