From 77af1ca632d04ea5b5c25a66d680062087c0658c Mon Sep 17 00:00:00 2001 From: Eddie Hebert Date: Thu, 4 Oct 2012 16:05:13 -0400 Subject: [PATCH] Applies PEP-8 and pyflakes style to `tests` and `zipline`. Mostly whitespace, line width and other spacing changes. Also, removes use of deprecated has_key in favor of `in` Going forward new patches should pass running `flake8` before submission. --- tests/test_algorithm.py | 9 +- tests/test_delayed_signals.py | 5 +- tests/test_exception_handling.py | 23 +- tests/test_finance.py | 170 ++++--- tests/test_ndict.py | 27 +- tests/test_optimize.py | 39 +- tests/test_risk.py | 794 ++++++++++++++++++++++++------- tests/test_sources.py | 3 +- tests/test_transforms.py | 80 ++-- zipline/finance/commission.py | 2 +- zipline/finance/performance.py | 171 ++++--- zipline/finance/risk.py | 122 ++--- zipline/finance/slippage.py | 29 +- zipline/finance/trading.py | 46 +- zipline/gens/composites.py | 3 +- zipline/gens/mavg.py | 2 +- zipline/gens/merge.py | 19 +- zipline/gens/sort.py | 13 +- zipline/gens/stddev.py | 35 +- zipline/gens/tradegens.py | 58 ++- zipline/gens/tradesimulation.py | 29 +- zipline/gens/transform.py | 46 +- zipline/gens/utils.py | 4 +- zipline/optimize/example.py | 26 +- zipline/optimize/factory.py | 30 +- zipline/protocol.py | 8 +- zipline/test_algorithms.py | 53 ++- zipline/utils/date_utils.py | 64 ++- zipline/utils/delayed_signals.py | 3 +- zipline/utils/factory.py | 62 ++- zipline/utils/log_utils.py | 10 +- zipline/utils/protocol_utils.py | 16 +- zipline/utils/simfactory.py | 185 +++---- zipline/utils/timeout.py | 38 +- zipline/utils/tradingcalendar.py | 102 ++-- zipline/version.py | 3 +- 36 files changed, 1461 insertions(+), 868 deletions(-) diff --git a/tests/test_algorithm.py b/tests/test_algorithm.py index 84f94008..1928cd08 100644 --- a/tests/test_algorithm.py +++ b/tests/test_algorithm.py @@ -7,6 +7,7 @@ from zipline.test_algorithms import TestRegisterTransformAlgorithm from zipline.gens.tradegens import SpecificEquityTrades, DataFrameSource from zipline.gens.mavg import MovingAverage + class TestTransformAlgorithm(TestCase): def setUp(self): setup_logger(self) @@ -37,7 +38,8 @@ class TestTransformAlgorithm(TestCase): def test_multi_source_as_input(self): algo = TestRegisterTransformAlgorithm(sids=[0, 1, 133]) - algo.run([self.source, self.df_source], start=self.df.index[0], end=self.df.index[-1]) + algo.run([self.source, self.df_source], + start=self.df.index[0], end=self.df.index[-1]) self.assertEqual(len(algo.sources), 2) def test_df_as_input(self): @@ -51,7 +53,6 @@ class TestTransformAlgorithm(TestCase): assert algo.get_sid_filter() == algo.sids == [133] assert 'mavg' in algo.registered_transforms assert algo.registered_transforms['mavg']['args'] == (['price'],) - assert algo.registered_transforms['mavg']['kwargs'] == {'days': 2, 'market_aware': True} + assert algo.registered_transforms['mavg']['kwargs'] == \ + {'days': 2, 'market_aware': True} assert algo.registered_transforms['mavg']['class'] is MovingAverage - - diff --git a/tests/test_delayed_signals.py b/tests/test_delayed_signals.py index d840bb0a..d698bfb8 100644 --- a/tests/test_delayed_signals.py +++ b/tests/test_delayed_signals.py @@ -1,11 +1,12 @@ import os -from signal import signal, SIGHUP, SIGINT +from signal import signal, SIGHUP, SIGINT import time -from types import FrameType +from types import FrameType import unittest from zipline.utils.delayed_signals import delayed_signals + class DelayedSignals(unittest.TestCase): def handler(self, signum, frame): print "Got signal " + str(signum) diff --git a/tests/test_exception_handling.py b/tests/test_exception_handling.py index f950dd07..62306306 100644 --- a/tests/test_exception_handling.py +++ b/tests/test_exception_handling.py @@ -6,7 +6,6 @@ import zipline.utils.simfactory as simfactory from zipline.test_algorithms import ExceptionAlgorithm, DivByZeroAlgorithm, \ InitializeTimeoutAlgorithm, TooMuchProcessingAlgorithm from zipline.finance.slippage import FixedSlippage -from zipline.lines import SimulatedTrading from zipline.gens.transform import StatefulTransform from zipline.utils.timeout import TimeoutException @@ -19,7 +18,7 @@ from zipline.utils.test_utils import ( ExceptionTransform ) -DEFAULT_TIMEOUT = 15 # seconds +DEFAULT_TIMEOUT = 15 # seconds EXTENDED_TIMEOUT = 90 @@ -29,8 +28,8 @@ class ExceptionTestCase(TestCase): def setUp(self): self.zipline_test_config = { - 'sid' : 133, - 'slippage' : FixedSlippage() + 'sid': 133, + 'slippage': FixedSlippage() } setup_logger(self) @@ -51,7 +50,6 @@ class ExceptionTestCase(TestCase): 'integer division or modulo by zero' ) - def test_tranform_exception(self): exc_tnfm = StatefulTransform(ExceptionTransform) self.zipline_test_config['transforms'] = [exc_tnfm] @@ -63,7 +61,8 @@ class ExceptionTestCase(TestCase): with self.assertRaises(AssertionError) as ctx: output, _ = drain_zipline(self, zipline) - self.assertEqual(ctx.exception.message,'An assertion message') + self.assertEqual(ctx.exception.message, + 'An assertion message') def test_exception_in_init(self): # Simulation @@ -81,7 +80,8 @@ class ExceptionTestCase(TestCase): with self.assertRaises(Exception) as ctx: output, _ = drain_zipline(self, zipline) - self.assertEqual(ctx.exception.message,'Algo exception in initialize') + self.assertEqual(ctx.exception.message, + 'Algo exception in initialize') def test_exception_in_handle_data(self): # Simulation @@ -99,7 +99,8 @@ class ExceptionTestCase(TestCase): with self.assertRaises(Exception) as ctx: output, _ = drain_zipline(self, zipline) - self.assertEqual(ctx.exception.message,'Algo exception in handle_data') + self.assertEqual(ctx.exception.message, + 'Algo exception in handle_data') def test_zerodivision_exception_in_handle_data(self): @@ -117,8 +118,8 @@ class ExceptionTestCase(TestCase): with self.assertRaises(ZeroDivisionError) as ctx: output, _ = drain_zipline(self, zipline) - self.assertEqual(ctx.exception.message,'integer division or modulo by zero') - + self.assertEqual(ctx.exception.message, + 'integer division or modulo by zero') def test_initialize_timeout(self): @@ -134,7 +135,7 @@ class ExceptionTestCase(TestCase): with self.assertRaises(TimeoutException) as ctx: output, _ = drain_zipline(self, zipline) - self.assertEqual(ctx.exception.message,'Call to initialize timed out') + self.assertEqual(ctx.exception.message, 'Call to initialize timed out') def test_heartbeat(self): diff --git a/tests/test_finance.py b/tests/test_finance.py index aaf18b2c..c4d16202 100644 --- a/tests/test_finance.py +++ b/tests/test_finance.py @@ -22,7 +22,7 @@ from zipline.utils.test_utils import( assert_single_position ) -DEFAULT_TIMEOUT = 15 # seconds +DEFAULT_TIMEOUT = 15 # seconds EXTENDED_TIMEOUT = 90 @@ -32,7 +32,7 @@ class FinanceTestCase(TestCase): def setUp(self): self.zipline_test_config = { - 'sid' : 133, + 'sid': 133, } setup_logger(self) @@ -62,22 +62,22 @@ class FinanceTestCase(TestCase): env = TradingEnvironment( benchmark_returns, treasury_curves, - period_start = datetime(2008, 1, 1, tzinfo = pytz.utc), - period_end = datetime(2008, 12, 31, tzinfo = pytz.utc), - capital_base = 100000, + period_start=datetime(2008, 1, 1, tzinfo=pytz.utc), + period_end=datetime(2008, 12, 31, tzinfo=pytz.utc), + capital_base=100000, ) #holidays taken from: http://www.nyse.com/press/1191407641943.html - new_years = datetime(2008, 1, 1, tzinfo = pytz.utc) - mlk_day = datetime(2008, 1, 21, tzinfo = pytz.utc) - presidents = datetime(2008, 2, 18, tzinfo = pytz.utc) - good_friday = datetime(2008, 3, 21, tzinfo = pytz.utc) - memorial_day= datetime(2008, 5, 26, tzinfo = pytz.utc) - july_4th = datetime(2008, 7, 4, tzinfo = pytz.utc) - labor_day = datetime(2008, 9, 1, tzinfo = pytz.utc) - tgiving = datetime(2008, 11, 27, tzinfo = pytz.utc) - christmas = datetime(2008, 5, 25, tzinfo = pytz.utc) - a_saturday = datetime(2008, 8, 2, tzinfo = pytz.utc) - a_sunday = datetime(2008, 10, 12, tzinfo = pytz.utc) + new_years = datetime(2008, 1, 1, tzinfo=pytz.utc) + mlk_day = datetime(2008, 1, 21, tzinfo=pytz.utc) + presidents = datetime(2008, 2, 18, tzinfo=pytz.utc) + good_friday = datetime(2008, 3, 21, tzinfo=pytz.utc) + memorial_day = datetime(2008, 5, 26, tzinfo=pytz.utc) + july_4th = datetime(2008, 7, 4, tzinfo=pytz.utc) + labor_day = datetime(2008, 9, 1, tzinfo=pytz.utc) + tgiving = datetime(2008, 11, 27, tzinfo=pytz.utc) + christmas = datetime(2008, 5, 25, tzinfo=pytz.utc) + a_saturday = datetime(2008, 8, 2, tzinfo=pytz.utc) + a_sunday = datetime(2008, 10, 12, tzinfo=pytz.utc) holidays = [ new_years, mlk_day, @@ -95,8 +95,8 @@ class FinanceTestCase(TestCase): for holiday in holidays: self.assertTrue(not env.is_trading_day(holiday)) - first_trading_day = datetime(2008, 1, 2, tzinfo = pytz.utc) - last_trading_day = datetime(2008, 12, 31, tzinfo = pytz.utc) + first_trading_day = datetime(2008, 1, 2, tzinfo=pytz.utc) + last_trading_day = datetime(2008, 12, 31, tzinfo=pytz.utc) workdays = [first_trading_day, last_trading_day] for workday in workdays: @@ -121,32 +121,32 @@ class FinanceTestCase(TestCase): # create a scenario where order size and trade size are equal # so that orders must be spread out over several trades. - params ={ - 'trade_count':360, - 'trade_amount':100, + params = { + 'trade_count': 360, + 'trade_amount': 100, 'trade_interval': timedelta(minutes=1), - 'order_count':2, - 'order_amount':100, + 'order_count': 2, + 'order_amount': 100, 'order_interval': timedelta(minutes=1), # because we placed an order for 100 shares, and the volume # of each trade is 100, the simulator should spread the order # into 4 trades of 25 shares per order. - 'expected_txn_count':8, - 'expected_txn_volume':2 * 100 + 'expected_txn_count': 8, + 'expected_txn_volume': 2 * 100 } self.transaction_sim(**params) # same scenario, but with short sales - params2 ={ - 'trade_count':360, - 'trade_amount':100, + params2 = { + 'trade_count': 360, + 'trade_amount': 100, 'trade_interval': timedelta(minutes=1), - 'order_count':2, - 'order_amount':-100, + 'order_count': 2, + 'order_amount': -100, 'order_interval': timedelta(minutes=1), - 'expected_txn_count':8, - 'expected_txn_volume':2 * -100 + 'expected_txn_count': 8, + 'expected_txn_volume': 2 * -100 } self.transaction_sim(**params2) @@ -155,30 +155,30 @@ class FinanceTestCase(TestCase): def test_collapsing_orders(self): # create a scenario where order.amount <<< trade.volume # to test that several orders can be covered properly by one trade. - params1 ={ - 'trade_count':6, - 'trade_amount':100, + params1 = { + 'trade_count': 6, + 'trade_amount': 100, 'trade_interval': timedelta(hours=1), - 'order_count':24, - 'order_amount':1, + 'order_count': 24, + 'order_amount': 1, 'order_interval': timedelta(minutes=1), # because we placed an orders totaling less than 25% of one trade # the simulator should produce just one transaction. - 'expected_txn_count':1, - 'expected_txn_volume':24 * 1 + 'expected_txn_count': 1, + 'expected_txn_volume': 24 * 1 } self.transaction_sim(**params1) # second verse, same as the first. except short! - params2 ={ - 'trade_count':6, - 'trade_amount':100, + params2 = { + 'trade_count': 6, + 'trade_amount': 100, 'trade_interval': timedelta(hours=1), - 'order_count':24, - 'order_amount':-1, + 'order_count': 24, + 'order_amount': -1, 'order_interval': timedelta(minutes=1), - 'expected_txn_count':1, - 'expected_txn_volume':24 * -1 + 'expected_txn_count': 1, + 'expected_txn_volume': 24 * -1 } self.transaction_sim(**params2) @@ -187,33 +187,33 @@ class FinanceTestCase(TestCase): # create a scenario where orders expire without being filled # entirely params1 = { - 'trade_count':100, - 'trade_amount':100, + 'trade_count': 100, + 'trade_amount': 100, 'trade_delay': timedelta(minutes=5), 'trade_interval': timedelta(days=1), - 'order_count':3, - 'order_amount':1000, + 'order_count': 3, + 'order_amount': 1000, 'order_interval': timedelta(minutes=30), # because we placed an orders totaling less than 25% of one trade # the simulator should produce just one transaction. - 'expected_txn_count' : 1, - 'expected_txn_volume' : 25 + 'expected_txn_count': 1, + 'expected_txn_volume': 25 } self.transaction_sim(**params1) # same scenario, but short sales. params2 = { - 'trade_count' : 100, - 'trade_amount' : 100, - 'trade_delay' : timedelta(minutes=5), - 'trade_interval' : timedelta(days=1), - 'order_count' : 3, - 'order_amount' :-1000, - 'order_interval' : timedelta(minutes=30), + 'trade_count': 100, + 'trade_amount': 100, + 'trade_delay': timedelta(minutes=5), + 'trade_interval': timedelta(days=1), + 'order_count': 3, + 'order_amount': -1000, + 'order_interval': timedelta(minutes=30), # because we placed an orders totaling less than 25% of one trade # the simulator should produce just one transaction. - 'expected_txn_count' : 1, - 'expected_txn_volume' : -25 + 'expected_txn_count': 1, + 'expected_txn_volume': -25 } self.transaction_sim(**params2) @@ -221,16 +221,16 @@ class FinanceTestCase(TestCase): def test_alternating_long_short(self): # create a scenario where we alternate buys and sells params1 = { - 'trade_count' : int(6.5 * 60 * 4), - 'trade_amount' : 100, - 'trade_interval' : timedelta(minutes=1), - 'order_count' : 4, - 'order_amount' : 10, - 'order_interval' : timedelta(hours=24), - 'alternate' : True, - 'complete_fill' : True, - 'expected_txn_count' : 4, - 'expected_txn_volume' : 0 #equal buys and sells + 'trade_count': int(6.5 * 60 * 4), + 'trade_amount': 100, + 'trade_interval': timedelta(minutes=1), + 'order_count': 4, + 'order_amount': 10, + 'order_interval': timedelta(hours=24), + 'alternate': True, + 'complete_fill': True, + 'expected_txn_count': 4, + 'expected_txn_volume': 0 # equal buys and sells } self.transaction_sim(**params1) @@ -239,14 +239,13 @@ class FinanceTestCase(TestCase): results for conversion of orders to transactions given a trade history""" - trade_count = params['trade_count'] - trade_amount = params['trade_amount'] - trade_interval = params['trade_interval'] - trade_delay = params.get('trade_delay') - order_count = params['order_count'] - order_amount = params['order_amount'] - order_interval = params['order_interval'] - expected_txn_count = params['expected_txn_count'] + trade_count = params['trade_count'] + trade_interval = params['trade_interval'] + trade_delay = params.get('trade_delay') + order_count = params['order_count'] + order_amount = params['order_amount'] + order_interval = params['order_interval'] + expected_txn_count = params['expected_txn_count'] expected_txn_volume = params['expected_txn_volume'] # optional parameters # --------------------- @@ -279,9 +278,9 @@ class FinanceTestCase(TestCase): for i in xrange(order_count): order = ndict( { - 'sid' : sid, - 'amount' : order_amount * alternator**i, - 'dt' : order_date + 'sid': sid, + 'amount': order_amount * alternator ** i, + 'dt': order_date }) trade_sim.place_order(order) @@ -297,15 +296,14 @@ class FinanceTestCase(TestCase): # there should now be one open order list stored under the sid oo = trade_sim.open_orders self.assertEqual(len(oo), 1) - self.assertTrue(oo.has_key(sid)) + self.assertTrue(sid in oo) order_list = oo[sid] self.assertEqual(order_count, len(order_list)) for i in xrange(order_count): order = order_list[i] self.assertEqual(order.sid, sid) - self.assertEqual(order.amount, order_amount * alternator**i) - + self.assertEqual(order.amount, order_amount * alternator ** i) tracker = PerformanceTracker(trading_environment, [sid]) @@ -339,6 +337,6 @@ class FinanceTestCase(TestCase): # the open orders should now be empty oo = trade_sim.open_orders - self.assertTrue(oo.has_key(sid)) + self.assertTrue(sid in oo) order_list = oo[sid] self.assertEqual(0, len(order_list)) diff --git a/tests/test_ndict.py b/tests/test_ndict.py index 845bb97d..7d185590 100644 --- a/tests/test_ndict.py +++ b/tests/test_ndict.py @@ -5,6 +5,7 @@ from copy import deepcopy from zipline.utils.protocol_utils import ndict + def test_ndict(): nd = ndict({}) @@ -18,10 +19,10 @@ def test_ndict(): nd['x'] = 1 assert nd.x == 1 assert nd['x'] == 1 - assert nd.get('y') == None + assert nd.get('y') is None assert nd.get('y', 'fizzpop') == 'fizzpop' - assert nd.has_key('x') == True - assert nd.has_key('y') == False + assert 'x' in nd + assert 'y' not in nd assert 'x' in nd assert 'y' not in nd @@ -35,7 +36,7 @@ def test_ndict(): # Class isolation assert '__init__' not in nd assert '__iter__' not in nd - assert not nd.__dict__.has_key('x') + assert 'x' not in nd.__dict__ assert nd.get('__init__') is None assert 'x' not in set(dir(nd)) @@ -49,24 +50,27 @@ def test_ndict(): class ndictlike(object): x = 1 - assert { 'x': 1 } == nd + assert {'x': 1} == nd assert ndictlike() != nd # Deletion del nd['x'] - assert not nd.has_key('x') + assert 'x' not in nd assert nd.get('x') is None - for n in xrange(1000): dt = datetime.utcnow().replace(tzinfo=pytz.utc) - nd2 = ndict({"dt":dt, "otherdata":"ishere"*1000, "maybeanint":3}) + nd2 = ndict({"dt": dt, + "otherdata": "ishere" * 1000, + "maybeanint": 3}) nd2.dt2 = dt + def test_ndict_deepcopy(): def assert_correctly_copied(orig, copy): - assert nd == nd_dc, "Deepcopied ndict should have same keys and values." + assert nd == nd_dc, \ + "Deepcopied ndict should have same keys and values." nd_dc.z = 3 assert 'z' not in nd, "'z' also added to original ndict." @@ -79,9 +83,10 @@ def test_ndict_deepcopy(): nd_dc = deepcopy(nd) assert_correctly_copied(nd, nd_dc) - nd = ndict({'x':[1,2,3], 'y': {1: 1}}) + nd = ndict({'x': [1, 2, 3], + 'y': {1: 1}}) nd_dc = deepcopy(nd) assert_correctly_copied(nd, nd_dc) nd_dc.x.append(4) assert nd_dc.x[-1] == 4, "not correctly appended to copied." - assert nd.x[-1] != 4, "also copied to original." \ No newline at end of file + assert nd.x[-1] != 4, "also copied to original." diff --git a/tests/test_optimize.py b/tests/test_optimize.py index 6d5ccce2..9efb972a 100644 --- a/tests/test_optimize.py +++ b/tests/test_optimize.py @@ -1,6 +1,5 @@ """Tests for the zipline.finance package""" from unittest2 import TestCase, skip -from nose.tools import timed from collections import defaultdict import numpy as np @@ -9,6 +8,7 @@ from zipline.optimize.factory import create_predictable_zipline from zipline.utils.test_utils import setup_logger, teardown_logger + class TestUpDown(TestCase): """This unittest verifies that the BuySellAlgorithm in combination with the UpDownSource are suitable for usage in an @@ -19,14 +19,13 @@ class TestUpDown(TestCase): def setUp(self): self.zipline_test_config = { - 'sid' : [0], - 'trade_count' : 5, - 'amplitude' : 30, - 'base_price' : 50 + 'sid': [0], + 'trade_count': 5, + 'amplitude': 30, + 'base_price': 50 } setup_logger(self, '/var/log/qexec/qexec.log') - def tearDown(self): teardown_logger(self) @@ -49,18 +48,18 @@ class TestUpDown(TestCase): amplitude = self.zipline_test_config['amplitude'] prices = config['trade_source'][0].values - max_price_idx = np.where(prices==prices.max())[0] - min_price_idx = np.where(prices==prices.min())[0] + max_price_idx = np.where(prices == prices.max())[0] + min_price_idx = np.where(prices == prices.min())[0] self.assertTrue(np.all(max_price_idx % 2 == 1), "Maximum prices are not periodic." ) self.assertTrue(np.all(min_price_idx % 2 == 0), "Minimum prices are not periodic." ) - self.assertEqual(prices.max(), base_price+amplitude/2., + self.assertEqual(prices.max(), base_price + amplitude / 2., "Maximum price does not equal expected maximum price." ) - self.assertEqual(prices.min(), base_price-amplitude/2., + self.assertEqual(prices.min(), base_price - amplitude / 2., "Minimum price does not equal expected maximum price." ) @@ -69,8 +68,8 @@ class TestUpDown(TestCase): self.assertTrue(len(stats) != 0) orders = np.asarray(algo.orders) - max_order_idx = np.where(orders==orders.max())[0] - min_order_idx = np.where(orders==orders.min())[0] + max_order_idx = np.where(orders == orders.max())[0] + min_order_idx = np.where(orders == orders.min())[0] self.assertTrue(np.all(max_order_idx % 2 == 1), "Maximum orders are not periodic." @@ -111,20 +110,23 @@ class TestUpDown(TestCase): compound_returns[i] = results.returns.sum() - - self.assertTrue(np.all(compound_returns[supposed_max] > compound_returns[np.logical_not(supposed_max)]), + self.assertTrue(np.all( + compound_returns[supposed_max] > + compound_returns[np.logical_not(supposed_max)]), "Maximum compound returns are not where they are supposed to be." ) # test for concavity max_idx = np.where(supposed_max)[0][0] idx = np.array([max_idx, max_idx]) - for i in range((len(test_offsets)-1)/2): + for i in range((len(test_offsets) - 1) / 2): # going outwards, returns must decrease - self.assertTrue(compound_returns[idx[0]-1] < compound_returns[idx[0]], + self.assertTrue(compound_returns[idx[0] - 1] < + compound_returns[idx[0]], "Compound returns are not convex." ) - self.assertTrue(compound_returns[idx[1]+1] < compound_returns[idx[1]], + self.assertTrue(compound_returns[idx[1] + 1] < + compound_returns[idx[1]], "Compound returns are not convex." ) idx[0] -= 1 @@ -142,7 +144,8 @@ class TestUpDown(TestCase): self.zipline_test_config, offset=offset, ) - #function is getting minimized, so have to return negative cum returns. + # function is getting minimized, + # so have to return negative cum returns. return -zipline.get_cumulative_performance()['returns'] from scipy import optimize diff --git a/tests/test_risk.py b/tests/test_risk.py index 21b5785b..5c5a0d43 100644 --- a/tests/test_risk.py +++ b/tests/test_risk.py @@ -1,5 +1,4 @@ import unittest -import copy import datetime import calendar import pytz @@ -8,278 +7,712 @@ from zipline.utils import factory from zipline.finance.trading import TradingEnvironment + class Risk(unittest.TestCase): - + def setUp(self): #qutil.configure_logging() start_date = datetime.datetime( - year=2006, - month=1, - day=1, + year=2006, + month=1, + day=1, hour=0, minute=0, tzinfo=pytz.utc) - end_date = datetime.datetime(year=2006, month=12, day=31, tzinfo=pytz.utc) - + end_date = datetime.datetime( + year=2006, month=12, day=31, tzinfo=pytz.utc) + self.benchmark_returns, self.treasury_curves = \ factory.load_market_data() - + self.trading_env = TradingEnvironment( - self.benchmark_returns, + self.benchmark_returns, self.treasury_curves, - period_start = start_date, - period_end = end_date + period_start=start_date, + period_end=end_date ) - + 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() - + self.algo_returns_06 = factory.create_returns_from_list( - RETURNS, + RETURNS, self.trading_env ) - + self.metrics_06 = risk.RiskReport( - self.algo_returns_06, + self.algo_returns_06, self.trading_env ) - + start_08 = datetime.datetime( - year=2008, - month=1, - day=1, + year=2008, + month=1, + day=1, hour=0, minute=0, tzinfo=pytz.utc) - + end_08 = datetime.datetime( - year=2008, - month=12, - day=31, + year=2008, + month=12, + day=31, tzinfo=pytz.utc ) self.trading_env08 = TradingEnvironment( - self.benchmark_returns, + self.benchmark_returns, self.treasury_curves, - period_start = start_08, - period_end = end_08 + period_start=start_08, + period_end=end_08 ) - + def tearDown(self): return - + def test_factory(self): returns = [0.1] * 100 r_objects = factory.create_returns_from_list(returns, self.trading_env) - self.assertTrue(r_objects[-1].date <= datetime.datetime(year=2006, month=12, day=31, tzinfo=pytz.utc)) - + self.assertTrue(r_objects[-1].date <= + datetime.datetime( + year=2006, month=12, day=31, tzinfo=pytz.utc)) + def test_drawdown(self): - returns = factory.create_returns_from_list([1.0,-0.5,0.8,.17,1.0,-0.1,-0.45], self.trading_env) + returns = factory.create_returns_from_list( + [1.0, -0.5, 0.8, .17, 1.0, -0.1, -0.45], self.trading_env) #200, 100, 180, 210.6, 421.2, 379.8, 208.494 - metrics = risk.RiskMetrics(returns[0].date, returns[-1].date, returns, self.trading_env) + metrics = risk.RiskMetrics(returns[0].date, + returns[-1].date, + returns, + self.trading_env) self.assertEqual(metrics.max_drawdown, 0.505) - + def test_benchmark_returns_06(self): returns = factory.create_returns_from_range(self.trading_env) metrics = risk.RiskReport(returns, self.trading_env) - 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]) + 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): returns = factory.create_returns_from_range(self.trading_env) metrics = risk.RiskReport(returns, self.trading_env) - 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]) + 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): returns = factory.create_returns_from_range(self.trading_env) metrics = risk.RiskReport(returns, self.trading_env) - 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]) - + 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]) - + 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]) - + 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]) + 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]) + 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]) - #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. + 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]) + 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]) + 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): - returns = factory.create_returns_from_range(self.trading_env08) metrics = risk.RiskReport(returns, self.trading_env08) - monthly = [round(x.benchmark_period_returns, 3) for x in metrics.month_periods] - - self.assertEqual( monthly, - [-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]) - + monthly = [round(x.benchmark_period_returns, 3) + for x in metrics.month_periods] + + self.assertEqual(monthly, + [-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): returns = factory.create_returns_from_range(self.trading_env08) metrics = risk.RiskReport(returns, self.trading_env08) - 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]) - + 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): returns = factory.create_returns_from_range(self.trading_env08) metrics = risk.RiskReport(returns, self.trading_env08) - 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]) - + 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): returns = factory.create_returns_from_range(self.trading_env) metrics = risk.RiskReport(returns, self.trading_env) - 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]) - + 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) - + self.check_year_range(datetime.datetime(year=2008, month=1, day=1), + 2) + def test_partial_month(self): start = datetime.datetime( - year=1991, - month=1, - day=1, + year=1991, + month=1, + day=1, hour=0, minute=0, tzinfo=pytz.utc) - + #1992 and 1996 were leap years total_days = 365 * 5 + 2 - end = start + datetime.timedelta(days = total_days) + end = start + datetime.timedelta(days=total_days) trading_env90s = TradingEnvironment( - self.benchmark_returns, + self.benchmark_returns, self.treasury_curves, - period_start = start, - period_end = end + period_start=start, + period_end=end ) - - - returns = factory.create_returns(total_days, trading_env90s) - returns = returns[:-10] #truncate the returns series to end mid-month + + returns = factory.create_returns(total_days, trading_env90s) + returns = returns[:-10] # truncate the returns series to end mid-month metrics = risk.RiskReport(returns, trading_env90s) total_months = 60 self.check_metrics(metrics, total_months, start) - + 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) + # 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, self.trading_env08) metrics = risk.RiskReport(returns, self.trading_env) total_months = years * 12 self.check_metrics(metrics, total_months, start_date) - + def check_metrics(self, metrics, total_months, start_date): """ confirm that the right number of riskmetrics were calculated for each window length. """ self.assert_range_length( - metrics.month_periods, - total_months, - 1, + metrics.month_periods, + total_months, + 1, start_date ) - + self.assert_range_length( - metrics.three_month_periods, - total_months, - 3, + metrics.three_month_periods, + total_months, + 3, start_date ) - + self.assert_range_length( - metrics.six_month_periods, - total_months, - 6, + metrics.six_month_periods, + total_months, + 6, start_date ) - + self.assert_range_length( - metrics.year_periods, - total_months, - 12, + metrics.year_periods, + total_months, + 12, 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]): + 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): @@ -289,23 +722,27 @@ class Risk(unittest.TestCase): 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 + 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): + + 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( + 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, @@ -314,43 +751,42 @@ class Risk(unittest.TestCase): )) self.assert_month(start_date.month, col[-1].end_date.month) self.assert_last_day(col[-1].end_date) - - -RETURNS = [ + +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.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.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.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.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.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.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.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.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.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 ] diff --git a/tests/test_sources.py b/tests/test_sources.py index 548f8754..1f061358 100644 --- a/tests/test_sources.py +++ b/tests/test_sources.py @@ -3,6 +3,7 @@ from unittest2 import TestCase import zipline.utils.factory as factory from zipline.gens.tradegens import DataFrameSource + class TestDataFrameSource(TestCase): def test_streaming_of_df(self): source, df = factory.create_test_df_source() @@ -19,4 +20,4 @@ class TestDataFrameSource(TestCase): _, df = factory.create_test_df_source() source = DataFrameSource(df, sids=[0]) assert 1 not in [event.sid for event in source], \ - "DataFrameSource should only stream selected sid 0, not sid 1." \ No newline at end of file + "DataFrameSource should only stream selected sid 0, not sid 1." diff --git a/tests/test_transforms.py b/tests/test_transforms.py index 3e05436b..a3b63413 100644 --- a/tests/test_transforms.py +++ b/tests/test_transforms.py @@ -19,9 +19,11 @@ import zipline.utils.factory as factory from zipline.test_algorithms import BatchTransformAlgorithm + def to_dt(msg): return ndict({'dt': msg}) + class NoopEventWindow(EventWindow): """ A no-op EventWindow subclass for testing the base EventWindow logic. @@ -39,38 +41,40 @@ class NoopEventWindow(EventWindow): def handle_remove(self, event): self.removed.append(event) + class EventWindowTestCase(TestCase): def setUp(self): setup_logger(self) self.monday = datetime(2012, 7, 9, 16, tzinfo=pytz.utc) - self.eleven_normal_days = [self.monday + i*timedelta(days=1) + self.eleven_normal_days = [self.monday + i * timedelta(days=1) for i in xrange(11)] # Modify the end of the period slightly to exercise the # incomplete day logic. - self.eleven_normal_days[-1] -= timedelta(minutes = 1) - self.eleven_normal_days.append(self.monday+timedelta(days=11,seconds=1)) + self.eleven_normal_days[-1] -= timedelta(minutes=1) + self.eleven_normal_days.append(self.monday + + timedelta(days=11, seconds=1)) # Second set of dates to test holiday handling. self.jul4_monday = datetime(2012, 7, 2, 16, tzinfo=pytz.utc) - self.week_of_jul4 = [self.jul4_monday + i*timedelta(days=1) - for i in xrange(5)] + self.week_of_jul4 = [self.jul4_monday + i * timedelta(days=1) + for i in xrange(5)] def test_event_window_with_timedelta(self): # Keep all events within a 5 minute window. window = NoopEventWindow( - market_aware = False, - delta = timedelta(minutes = 5), - days = None + market_aware=False, + delta=timedelta(minutes=5), + days=None ) now = utcnow() # 15 dates, increasing in 1 minute increments. - dates = [now + i * timedelta(minutes = 1) + dates = [now + i * timedelta(minutes=1) for i in xrange(15)] # Turn the dates into the format required by EventWindow. @@ -88,13 +92,13 @@ class EventWindowTestCase(TestCase): # Assert that we removed only events that fall outside (or # on the boundary of) the delta. for dropped in window.removed: - assert message.dt - dropped.dt >= timedelta(minutes = 5) + assert message.dt - dropped.dt >= timedelta(minutes=5) def test_market_aware_window_normal_week(self): window = NoopEventWindow( - market_aware = True, - delta = None, - days = 3 + market_aware=True, + delta=None, + days=3 ) events = [to_dt(date) for date in self.eleven_normal_days] lengths = [] @@ -114,9 +118,9 @@ class EventWindowTestCase(TestCase): def test_market_aware_window_holiday(self): window = NoopEventWindow( - market_aware = True, - delta = None, - days = 2 + market_aware=True, + delta=None, + days=2 ) events = [to_dt(date) for date in self.week_of_jul4] lengths = [] @@ -134,6 +138,7 @@ class EventWindowTestCase(TestCase): def tearDown(self): setup_logger(self) + class FinanceTransformsTestCase(TestCase): def setUp(self): @@ -155,8 +160,8 @@ class FinanceTransformsTestCase(TestCase): def test_vwap(self): vwap = VWAP( - market_aware = False, - delta = timedelta(days = 2) + market_aware=False, + delta=timedelta(days=2) ) transformed = list(vwap.transform(self.source)) @@ -219,9 +224,9 @@ class FinanceTransformsTestCase(TestCase): def test_moving_average(self): mavg = MovingAverage( - market_aware = False, - fields = ['price', 'volume'], - delta = timedelta(days = 2), + market_aware=False, + fields=['price', 'volume'], + delta=timedelta(days=2), ) transformed = list(mavg.transform(self.source)) @@ -255,13 +260,13 @@ class FinanceTransformsTestCase(TestCase): 133, [10.0, 15.0, 13.0, 12.0], [100, 100, 100, 100], - timedelta(hours = 1), + timedelta(hours=1), self.trading_environment ) stddev = MovingStandardDev( - market_aware = False, - delta = timedelta(minutes = 150), + market_aware=False, + delta=timedelta(minutes=150), ) self.source = SpecificEquityTrades(event_list=trade_history) @@ -271,17 +276,17 @@ class FinanceTransformsTestCase(TestCase): expected = [ None, - np.std([10.0, 15.0], ddof = 1), - np.std([10.0, 15.0, 13.0], ddof = 1), - np.std([15.0, 13.0, 12.0], ddof = 1), + np.std([10.0, 15.0], ddof=1), + np.std([10.0, 15.0, 13.0], ddof=1), + np.std([15.0, 13.0, 12.0], ddof=1), ] # np has odd rounding behavior, cf. # http://docs.scipy.org/doc/np/reference/generated/np.std.html for v1, v2 in zip(vals, expected): - if v1 == None: - assert v2 == None + if v1 is None: + assert v2 is None continue assert round(v1, 5) == round(v2, 5) @@ -298,11 +303,16 @@ class BatchTransformTestCase(TestCase): algo = BatchTransformAlgorithm(sids=[0, 1]) algo.run(self.source) - self.assertEqual(algo.history_return_price_class[:2], [None, None], "First two iterations should return None") - self.assertEqual(algo.history_return_price_decorator[:2], [None, None], "First two iterations should return None") + self.assertEqual(algo.history_return_price_class[:2], + [None, None], + "First two iterations should return None") + self.assertEqual(algo.history_return_price_decorator[:2], + [None, None], + "First two iterations should return None") # test overloaded class - for test_history in [algo.history_return_price_class, algo.history_return_price_decorator]: + for test_history in [algo.history_return_price_class, + algo.history_return_price_decorator]: np.testing.assert_array_equal( range(4, 10), test_history[2].values.flatten() @@ -321,8 +331,10 @@ class BatchTransformTestCase(TestCase): def test_passing_of_args(self): algo = BatchTransformAlgorithm([0, 1], 1, kwarg='str') self.assertEqual(algo.args, (1,)) - self.assertEqual(algo.kwargs, {'kwarg':'str'}) + self.assertEqual(algo.kwargs, {'kwarg': 'str'}) algo.run(self.source) expected_item = ((1, ), {'kwarg': 'str'}) - self.assertEqual(algo.history_return_args, [None, None, expected_item, expected_item, expected_item]) + self.assertEqual( + algo.history_return_args, + [None, None, expected_item, expected_item, expected_item]) diff --git a/zipline/finance/commission.py b/zipline/finance/commission.py index 442117ec..df86f4d6 100644 --- a/zipline/finance/commission.py +++ b/zipline/finance/commission.py @@ -12,7 +12,6 @@ class PerShare(object): """ self.cost = cost - def calculate(self, transaction): """ returns a tuple of: @@ -20,6 +19,7 @@ class PerShare(object): """ return self.cost, abs(transaction.amount * self.cost) + class PerTrade(object): """ Calculates a commission for a transaction based on a per diff --git a/zipline/finance/performance.py b/zipline/finance/performance.py index d0219895..1d278daa 100644 --- a/zipline/finance/performance.py +++ b/zipline/finance/performance.py @@ -126,8 +126,8 @@ import zipline.finance.risk as risk log = logbook.Logger('Performance') -class PerformanceTracker(object): +class PerformanceTracker(object): """ Tracks the performance of the zipline as it is running in the simulator, relays this out to the Deluge broker and then @@ -141,24 +141,24 @@ class PerformanceTracker(object): def __init__(self, trading_environment, sid_list): - self.trading_environment = trading_environment - self.trading_day = datetime.timedelta(hours = 6, minutes = 30) - self.calendar_day = datetime.timedelta(hours = 24) - self.started_at = datetime.datetime.utcnow().replace(tzinfo=pytz.utc) + self.trading_environment = trading_environment + self.trading_day = datetime.timedelta(hours=6, minutes=30) + self.calendar_day = datetime.timedelta(hours=24) + self.started_at = datetime.datetime.utcnow().replace(tzinfo=pytz.utc) - self.period_start = self.trading_environment.period_start - self.period_end = self.trading_environment.period_end - self.market_open = self.trading_environment.first_open - self.market_close = self.market_open + self.trading_day - self.progress = 0.0 - self.total_days = self.trading_environment.days_in_period + self.period_start = self.trading_environment.period_start + self.period_end = self.trading_environment.period_end + self.market_open = self.trading_environment.first_open + self.market_close = self.market_open + self.trading_day + self.progress = 0.0 + self.total_days = self.trading_environment.days_in_period # one indexed so that we reach 100% - self.day_count = 0.0 - self.capital_base = self.trading_environment.capital_base - self.returns = [] - self.txn_count = 0 - self.event_count = 0 - self.last_dict = None + self.day_count = 0.0 + self.capital_base = self.trading_environment.capital_base + self.returns = [] + self.txn_count = 0 + self.event_count = 0 + self.last_dict = None # this performance period will span the entire simulation. self.cumulative_performance = PerformancePeriod( @@ -185,7 +185,7 @@ class PerformanceTracker(object): self.market_open, self.market_close, # save the transactions for the daily periods - keep_transactions = True + keep_transactions=True ) for sid in sid_list: @@ -216,17 +216,16 @@ class PerformanceTracker(object): Returns a dict object of the form described in header comments. """ return { - 'started_at' : self.started_at, - '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() + 'started_at': self.started_at, + '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() } - def process_event(self, event): message = None @@ -284,7 +283,8 @@ class PerformanceTracker(object): while not self.trading_environment.is_trading_day(self.market_open): if self.market_open > self.trading_environment.trading_days[-1]: - raise Exception("Attempt to backtest beyond available history.") + raise Exception( + "Attempt to backtest beyond available history.") self.market_open = self.market_open + self.calendar_day self.market_close = self.market_open + self.trading_day @@ -296,7 +296,7 @@ class PerformanceTracker(object): self.todays_performance.ending_cash, self.market_open, self.market_close, - keep_transactions = True + keep_transactions=True ) return daily_update @@ -309,7 +309,8 @@ class PerformanceTracker(object): log_msg = "Simulated {n} trading days out of {m}." log.info(log_msg.format(n=self.day_count, m=self.total_days)) - log.info("first open: {d}".format(d=self.trading_environment.first_open)) + log.info("first open: {d}".format( + d=self.trading_environment.first_open)) # the stream will end on the last trading day, but will not trigger # an end of day, so we trigger the final market close here. @@ -323,14 +324,15 @@ class PerformanceTracker(object): risk_dict = self.risk_report.to_dict() return risk_dict + class Position(object): def __init__(self, sid): - self.sid = sid - self.amount = 0 - self.cost_basis = 0.0 ##per share + self.sid = sid + self.amount = 0 + self.cost_basis = 0.0 # per share self.last_sale_price = 0.0 - self.last_sale_date = 0.0 + self.last_sale_date = 0.0 def update(self, txn): if(self.sid != txn.sid): @@ -341,25 +343,24 @@ class Position(object): self.cost_basis = 0.0 self.amount = 0 else: - prev_cost = self.cost_basis*self.amount - txn_cost = txn.amount*txn.price - total_cost = prev_cost + txn_cost - total_shares = self.amount + txn.amount - self.cost_basis = total_cost/total_shares - self.amount = self.amount + txn.amount + prev_cost = self.cost_basis * self.amount + txn_cost = txn.amount * txn.price + total_cost = prev_cost + txn_cost + total_shares = self.amount + txn.amount + self.cost_basis = total_cost / total_shares + self.amount = self.amount + txn.amount def currentValue(self): return self.amount * self.last_sale_price - def __repr__(self): template = "sid: {sid}, amount: {amount}, cost_basis: {cost_basis}, \ last_sale_price: {last_sale_price}" return template.format( - sid = self.sid, - amount = self.amount, - cost_basis = self.cost_basis, - last_sale_price = self.last_sale_price + sid=self.sid, + amount=self.amount, + cost_basis=self.cost_basis, + last_sale_price=self.last_sale_price ) def to_dict(self): @@ -368,10 +369,10 @@ class Position(object): Returns a dict object of the form: """ return { - 'sid' : self.sid, - 'amount' : self.amount, - 'cost_basis' : self.cost_basis, - 'last_sale_price' : self.last_sale_price + 'sid': self.sid, + 'amount': self.amount, + 'cost_basis': self.cost_basis, + 'last_sale_price': self.last_sale_price } @@ -389,32 +390,32 @@ class PerformancePeriod(object): self.period_open = period_open self.period_close = period_close - self.ending_value = 0.0 - self.period_capital_used = 0.0 - self.pnl = 0.0 + self.ending_value = 0.0 + self.period_capital_used = 0.0 + self.pnl = 0.0 #sid => position object - self.positions = initial_positions - self.starting_value = starting_value + self.positions = initial_positions + self.starting_value = starting_value #cash balance at start of period - self.starting_cash = starting_cash - self.ending_cash = starting_cash - self.keep_transactions = keep_transactions - self.processed_transactions = [] + self.starting_cash = starting_cash + self.ending_cash = starting_cash + self.keep_transactions = keep_transactions + self.processed_transactions = [] self.cumulative_capital_used = 0.0 - self.max_capital_used = 0.0 - self.max_leverage = 0.0 + self.max_capital_used = 0.0 + self.max_leverage = 0.0 self.calculate_performance() def calculate_performance(self): self.ending_value = self.calculate_positions_value() - total_at_start = self.starting_cash + self.starting_value - self.ending_cash = self.starting_cash + self.period_capital_used - total_at_end = self.ending_cash + self.ending_value + total_at_start = self.starting_cash + self.starting_value + self.ending_cash = self.starting_cash + self.period_capital_used + total_at_end = self.ending_cash + self.ending_value - self.pnl = total_at_end - total_at_start - if(total_at_start != 0): + self.pnl = total_at_end - total_at_start + if total_at_start != 0: self.returns = self.pnl / total_at_start else: self.returns = 0.0 @@ -423,16 +424,14 @@ class PerformancePeriod(object): # Update Position # ---------------- - if(not self.positions.has_key(txn.sid)): + if txn.sid not in self.positions: self.positions[txn.sid] = Position(txn.sid) self.positions[txn.sid].update(txn) self.period_capital_used += -1 * txn.price * txn.amount - # Max Leverage # --------------- # Calculate the maximum capital used and maximum leverage - transaction_cost = txn.price * txn.amount self.cumulative_capital_used += transaction_cost @@ -447,47 +446,47 @@ class PerformancePeriod(object): ) # we're adding a 10% cushion to the capital used. - self.max_leverage = 1.1 * self.max_capital_used / self.starting_cash + self.max_leverage = 1.1 * \ + self.max_capital_used / self.starting_cash # add transaction to the list of processed transactions if self.keep_transactions: self.processed_transactions.append(txn) def round_to_nearest(self, x, base=5): - return int(base * round(float(x)/base)) + return int(base * round(float(x) / base)) def calculate_positions_value(self): mktValue = 0.0 - for key,pos in self.positions.iteritems(): + for key, pos in self.positions.iteritems(): mktValue += pos.currentValue() return mktValue def update_last_sale(self, event): is_trade = event.type == zp.DATASOURCE_TYPE.TRADE - if self.positions.has_key(event.sid) and is_trade: + if event.sid in self.positions and is_trade: self.positions[event.sid].last_sale_price = event.price self.positions[event.sid].last_sale_date = event.dt def __core_dict(self): rval = { - 'ending_value' : self.ending_value, - 'capital_used' : self.period_capital_used, - 'starting_value' : self.starting_value, - 'starting_cash' : self.starting_cash, - 'ending_cash' : self.ending_cash, - 'portfolio_value' : self.ending_cash + self.ending_value, - 'cumulative_capital_used' : self.cumulative_capital_used, - 'max_capital_used' : self.max_capital_used, - 'max_leverage' : self.max_leverage, - 'pnl' : self.pnl, - 'returns' : self.returns, - 'period_open' : self.period_open, - 'period_close' : self.period_close + 'ending_value': self.ending_value, + 'capital_used': self.period_capital_used, + 'starting_value': self.starting_value, + 'starting_cash': self.starting_cash, + 'ending_cash': self.ending_cash, + 'portfolio_value': self.ending_cash + self.ending_value, + 'cumulative_capital_used': self.cumulative_capital_used, + 'max_capital_used': self.max_capital_used, + 'max_leverage': self.max_leverage, + 'pnl': self.pnl, + 'returns': self.returns, + 'period_open': self.period_open, + 'period_close': self.period_close } return rval - def to_dict(self): """ Creates a dictionary representing the state of this performance diff --git a/zipline/finance/risk.py b/zipline/finance/risk.py index aa7a4cd4..b589b1a5 100644 --- a/zipline/finance/risk.py +++ b/zipline/finance/risk.py @@ -45,6 +45,7 @@ from zipline.utils.date_utils import epoch_now log = logbook.Logger('Risk') + def advance_by_months(dt, jump_in_months): month = dt.month + jump_in_months years = month / 12 @@ -58,7 +59,7 @@ def advance_by_months(dt, jump_in_months): month = 12 years = years - 1 - return dt.replace(year = dt.year + years, month = month) + return dt.replace(year=dt.year + years, month=month) class DailyReturn(): @@ -71,8 +72,8 @@ class DailyReturn(): def to_dict(self): return { - 'dt' : self.date, - 'returns' : self.returns + 'dt': self.date, + 'returns': self.returns } def __repr__(self): @@ -108,16 +109,18 @@ class RiskMetrics(): ) raise Exception(message) - self.trading_days = len(self.benchmark_returns) - self.benchmark_volatility = self.calculate_volatility(self.benchmark_returns) - self.algorithm_volatility = self.calculate_volatility(self.algorithm_returns) + self.benchmark_volatility = self.calculate_volatility( + self.benchmark_returns) + self.algorithm_volatility = self.calculate_volatility( + self.algorithm_returns) self.treasury_period_return = self.choose_treasury() self.sharpe = self.calculate_sharpe() self.beta, self.algorithm_covariance, self.benchmark_variance, \ self.condition_number, self.eigen_values = self.calculate_beta() self.alpha = self.calculate_alpha() - self.excess_return = self.algorithm_period_returns - self.treasury_period_return + self.excess_return = self.algorithm_period_returns - \ + self.treasury_period_return self.max_drawdown = self.calculate_max_drawdown() def to_dict(self): @@ -127,18 +130,18 @@ class RiskMetrics(): """ period_label = self.end_date.strftime("%Y-%m") rval = { - 'trading_days' : self.trading_days, - 'benchmark_volatility' : self.benchmark_volatility, - 'algo_volatility' : self.algorithm_volatility, + 'trading_days': self.trading_days, + 'benchmark_volatility': self.benchmark_volatility, + 'algo_volatility': self.algorithm_volatility, 'treasury_period_return': self.treasury_period_return, - 'algorithm_period_return' : self.algorithm_period_returns, - 'benchmark_period_return' : self.benchmark_period_returns, - 'sharpe' : self.sharpe, - 'beta' : self.beta, - 'alpha' : self.alpha, - 'excess_return' : self.excess_return, - 'max_drawdown' : self.max_drawdown, - 'period_label' : period_label + 'algorithm_period_return': self.algorithm_period_returns, + 'benchmark_period_return': self.benchmark_period_returns, + 'sharpe': self.sharpe, + 'beta': self.beta, + 'alpha': self.alpha, + 'excess_return': self.excess_return, + 'max_drawdown': self.max_drawdown, + 'period_label': period_label } # check if a field in rval is nan, and replace it with @@ -149,26 +152,27 @@ class RiskMetrics(): else: return False - return {k:None if check_entry(k,v) else v for k,v in rval.iteritems()} + return {k: None if check_entry(k, v) else v + for k, v in rval.iteritems()} def __repr__(self): statements = [] metrics = [ - "algorithm_period_returns" , - "benchmark_period_returns" , - "excess_return" , - "trading_days" , - "benchmark_volatility" , - "algorithm_volatility" , - "sharpe" , - "algorithm_covariance" , - "benchmark_variance" , - "beta" , - "alpha" , - "max_drawdown" , - "algorithm_returns" , - "benchmark_returns" , - "condition_number" , + "algorithm_period_returns", + "benchmark_period_returns", + "excess_return", + "trading_days", + "benchmark_volatility", + "algorithm_volatility", + "sharpe", + "algorithm_covariance", + "benchmark_variance", + "beta", + "alpha", + "max_drawdown", + "algorithm_returns", + "benchmark_returns", + "condition_number", "eigen_values" ] @@ -208,8 +212,8 @@ class RiskMetrics(): if self.algorithm_volatility == 0: return 0.0 - return ( (self.algorithm_period_returns - self.treasury_period_return) / - self.algorithm_volatility ) + return ((self.algorithm_period_returns - self.treasury_period_return) / + self.algorithm_volatility) def calculate_beta(self): """ @@ -225,7 +229,8 @@ class RiskMetrics(): if len(self.algorithm_returns) < 2: return 0.0, 0.0, 0.0, 0.0, [] - returns_matrix = np.vstack([self.algorithm_returns, self.benchmark_returns]) + returns_matrix = np.vstack([self.algorithm_returns, + self.benchmark_returns]) C = np.cov(returns_matrix) eigen_values = la.eigvals(C) condition_number = max(eigen_values) / min(eigen_values) @@ -245,7 +250,10 @@ class RiskMetrics(): """ http://en.wikipedia.org/wiki/Alpha_(investment) """ - return self.algorithm_period_returns - (self.treasury_period_return + self.beta * (self.benchmark_period_returns - self.treasury_period_return)) + return self.algorithm_period_returns - \ + (self.treasury_period_return + + self.beta * + (self.benchmark_period_returns - self.treasury_period_return)) def calculate_max_drawdown(self): compounded_returns = [] @@ -255,26 +263,26 @@ class RiskMetrics(): cur_return = math.log(1.0 + r) + cur_return #this is a guard for a single day returning -100% except ValueError: - log.debug("{cur} return, zeroing the returns".format(cur=cur_return)) + log.debug("{cur} return, zeroing the returns".format( + cur=cur_return)) cur_return = 0.0 compounded_returns.append(cur_return) cur_max = None max_drawdown = None for cur in compounded_returns: - if cur_max == None or cur > cur_max: + if cur_max is None or cur > cur_max: cur_max = cur drawdown = (cur - cur_max) - if max_drawdown == None or drawdown < max_drawdown: + if max_drawdown is None or drawdown < max_drawdown: max_drawdown = drawdown - if max_drawdown == None: + if max_drawdown is None: return 0.0 return 1.0 - math.exp(max_drawdown) - def choose_treasury(self): td = self.end_date - self.start_date if td.days <= 31: @@ -298,22 +306,23 @@ class RiskMetrics(): else: self.treasury_duration = '30year' - one_day = datetime.timedelta(days=1) curve = None # in case end date is not a trading day, search for the next market # day for an interest rate for i in xrange(7): - if(self.treasury_curves.has_key(self.end_date + i * one_day)): - curve = self.treasury_curves[self.end_date + i * one_day] + day = self.end_date + i * one_day + if day in self.treasury_curves: + curve = self.treasury_curves[day] self.treasury_curve = curve rate = self.treasury_curve[self.treasury_duration] - #1month note data begins in 8/2001, so we can use 3month instead. - if rate == None and self.treasury_duration == '1month': + # 1month note data begins in 8/2001, + # so we can use 3month instead. + if rate is None and self.treasury_duration == '1month': rate = self.treasury_curve['3month'] - if rate != None: + if rate is not None: return rate * (td.days + 1) / 365 message = "no rate for end date = {dt} and term = {term}. Check \ @@ -325,7 +334,6 @@ class RiskMetrics(): raise Exception(message) - class RiskReport(): def __init__( @@ -370,15 +378,15 @@ class RiskReport(): provided for each period. """ return { - 'one_month' : [x.to_dict() for x in self.month_periods], - 'three_month' : [x.to_dict() for x in self.three_month_periods], - 'six_month' : [x.to_dict() for x in self.six_month_periods], - 'twelve_month' : [x.to_dict() for x in self.year_periods], - 'created' : self.created + 'one_month': [x.to_dict() for x in self.month_periods], + 'three_month': [x.to_dict() for x in self.three_month_periods], + 'six_month': [x.to_dict() for x in self.six_month_periods], + 'twelve_month': [x.to_dict() for x in self.year_periods], + 'created': self.created } def periodsInRange(self, months_per, start, end): - one_day = datetime.timedelta(days = 1) + one_day = datetime.timedelta(days=1) ends = [] cur_start = start.replace(day=1) @@ -389,7 +397,7 @@ class RiskReport(): #ensure that we have an end at the end of a calendar month, in case #the return series ends mid-month... - the_end = advance_by_months(end.replace(day=1),1) - one_day + the_end = advance_by_months(end.replace(day=1), 1) - one_day while True: cur_end = advance_by_months(cur_start, months_per) - one_day if(cur_end > the_end): diff --git a/zipline/finance/slippage.py b/zipline/finance/slippage.py index 53512438..5f8304a1 100644 --- a/zipline/finance/slippage.py +++ b/zipline/finance/slippage.py @@ -5,6 +5,7 @@ from functools import partial import zipline.protocol as zp + def transact_stub(slippage, commission, open_orders, events): """ This is intended to be wrapped in a partial, so that the @@ -23,12 +24,13 @@ def transact_stub(slippage, commission, open_orders, events): def transact_partial(slippage, commission): return partial(transact_stub, slippage, commission) + def create_transaction(sid, amount, price, dt): - txn = {'sid' : sid, - 'amount' : int(amount), - 'dt' : dt, - 'price' : price, + txn = {'sid': sid, + 'amount': int(amount), + 'dt': dt, + 'price': price, } transaction = zp.ndict(txn) @@ -83,7 +85,8 @@ class VolumeShareSlippage(object): if volume_share > self.volume_limit: volume_share = self.volume_limit simulated_amount = int(volume_share * event.volume * direction) - simulated_impact = (volume_share)**2 * self.price_impact * direction * event.price + simulated_impact = (volume_share) ** 2 \ + * self.price_impact * direction * event.price order.filled += (simulated_amount - total_order) total_order = simulated_amount @@ -92,26 +95,28 @@ class VolumeShareSlippage(object): if volume_share == self.volume_limit: break - - orders = [ x for x in orders if abs(x.amount - x.filled) > 0 and x.dt.day >= event.dt.day] + orders = [x for x in orders + if abs(x.amount - x.filled) > 0 + and x.dt.day >= event.dt.day] open_orders[event.sid] = orders - if simulated_amount != 0: return create_transaction( event.sid, simulated_amount, event.price + simulated_impact, - dt.replace(tzinfo = pytz.utc), + dt.replace(tzinfo=pytz.utc), ) + class FixedSlippage(object): def __init__(self, spread=0.0): """ - Use the fixed slippage model, which will just add/subtract a specified spread - spread/2 will be added on buys and subtracted on sells per share + Use the fixed slippage model, which will just add/subtract + a specified spread spread/2 will be added on buys and subtracted + on sells per share """ self.spread = spread @@ -134,7 +139,7 @@ class FixedSlippage(object): txn = create_transaction( event.sid, amount, - event.price + (self.spread/2.0 * direction), + event.price + (self.spread / 2.0 * direction), event.dt ) diff --git a/zipline/finance/trading.py b/zipline/finance/trading.py index dc13bef9..fa7a48ae 100644 --- a/zipline/finance/trading.py +++ b/zipline/finance/trading.py @@ -13,20 +13,21 @@ from zipline.finance.commission import PerShare log = logbook.Logger('Transaction Simulator') + class TransactionSimulator(object): def __init__(self, transact=None): - if transact != None: + + if transact is not None: self.transact = transact else: - self.transact = transact_partial( - VolumeShareSlippage(), - PerShare() - ) + self.transact = transact_partial( + VolumeShareSlippage(), + PerShare() + ) self.open_orders = defaultdict(list) - def place_order(self, order): # initialized filled field. order.filled = 0 @@ -53,9 +54,9 @@ class TradingEnvironment(object): self, benchmark_returns, treasury_curves, - period_start = None, - period_end = None, - capital_base = None + period_start=None, + period_end=None, + capital_base=None ): self.trading_days = [] @@ -88,8 +89,8 @@ class TradingEnvironment(object): """ Finds the first trading day on or after self.period_start. """ - first_open = self.period_start - one_day = datetime.timedelta(days=1) + first_open = self.period_start + one_day = datetime.timedelta(days=1) while not self.is_trading_day(first_open): first_open = first_open + one_day @@ -102,7 +103,7 @@ class TradingEnvironment(object): Finds the first trading day open that falls at least a day before period_start. """ - one_day = datetime.timedelta(days=1) + one_day = datetime.timedelta(days=1) first_open = self.period_start - one_day if first_open <= self.trading_days[0]: @@ -119,8 +120,8 @@ class TradingEnvironment(object): """ Finds the last trading day on or before self.period_end """ - last_close = self.period_end - one_day = datetime.timedelta(days=1) + last_close = self.period_end + one_day = datetime.timedelta(days=1) while not self.is_trading_day(last_close): last_close = last_close - one_day @@ -136,12 +137,12 @@ class TradingEnvironment(object): month=dt.month, day=dt.day ) - local = pytz.timezone ('US/Eastern') - local_dt = naive.replace (tzinfo = local) + local = pytz.timezone('US/Eastern') + local_dt = naive.replace(tzinfo=local) # set the clock to the opening bell in NYC time. local_dt = local_dt.replace(hour=hour, minute=minute) # convert to UTC - utc_dt = local_dt.astimezone (pytz.utc) + utc_dt = local_dt.astimezone(pytz.utc) return utc_dt def normalize_date(self, test_date): @@ -155,10 +156,10 @@ class TradingEnvironment(object): @property def days_in_period(self): """return the number of trading days within the period [start, end)""" - assert(self.period_start != None) - assert(self.period_end != None) + assert self.period_start is not None + assert self.period_end is not None - if self.period_trading_days == None: + if self.period_trading_days is None: self.period_trading_days = [] for date in self.trading_days: if date > self.period_end: @@ -166,7 +167,6 @@ class TradingEnvironment(object): if date >= self.period_start: self.period_trading_days.append(date) - return len(self.period_trading_days) def is_market_hours(self, test_date): @@ -181,11 +181,11 @@ class TradingEnvironment(object): def is_trading_day(self, test_date): dt = self.normalize_date(test_date) - return self.trading_day_map.has_key(dt) + return (dt in self.trading_day_map) def get_benchmark_daily_return(self, test_date): date = self.normalize_date(test_date) - if self.trading_day_map.has_key(date): + if date in self.trading_day_map: return self.trading_day_map[date].returns else: return 0.0 diff --git a/zipline/gens/composites.py b/zipline/gens/composites.py index 7effcf19..ad516d6c 100644 --- a/zipline/gens/composites.py +++ b/zipline/gens/composites.py @@ -3,6 +3,7 @@ from itertools import chain from zipline.gens.utils import roundrobin, done_message from zipline.gens.sort import date_sort + def date_sorted_sources(*sources): """ Takes an iterable of sources, generating namestrings and @@ -26,6 +27,7 @@ def date_sorted_sources(*sources): return date_sort(stream_in, names) + def sequential_transforms(stream_in, *transforms): """ Apply each transform in transforms sequentially to each event in stream_in. @@ -44,7 +46,6 @@ def sequential_transforms(stream_in, *transforms): transforms, stream_in) - dt_aliased = alias_dt(stream_out) return add_done(dt_aliased) diff --git a/zipline/gens/mavg.py b/zipline/gens/mavg.py index de572236..fb683c44 100644 --- a/zipline/gens/mavg.py +++ b/zipline/gens/mavg.py @@ -123,7 +123,7 @@ class MovingAverageEventWindow(EventWindow): We only allow events with all of our tracked fields. """ for field in self.fields: - assert event.has_key(field), \ + assert field in event, \ "Event missing [%s] in MovingAverageEventWindow" % field assert isinstance(event[field], Number), \ "Got %s for %s in MovingAverageEventWindow" % (event[field], diff --git a/zipline/gens/merge.py b/zipline/gens/merge.py index e0056288..4127bb84 100644 --- a/zipline/gens/merge.py +++ b/zipline/gens/merge.py @@ -27,9 +27,9 @@ def merge(stream_in, tnfm_ids): # Process incoming streams. for message in stream_in: assert isinstance(message, ndict) - assert message.has_key('tnfm_id') - assert message.has_key('tnfm_value') - assert message.has_key('dt') + assert 'tnmf_id' in message + assert 'tnfm_value' in message + assert 'dt' in message id = message.tnfm_id assert id in tnfm_ids, \ @@ -49,7 +49,8 @@ def merge(stream_in, tnfm_ids): assert len(queue) == 1, "Bad queue in merge on exit: %s" % queue assert queue[0].dt == "DONE", \ "Bad last message in merge on exit: %s" % queue - + + def merge_one(sources): event_fields = ndict() @@ -74,19 +75,23 @@ def merge_one(sources): def ready(sources): """ Feed is ready when every internal queue has at least one message. Note that - this include DONE messages, so done(sources) is True only if ready(sources). + this include DONE messages, so done(sources) is True, + only if ready(sources). """ assert isinstance(sources, dict) - return all( (queue_is_ready(source) for source in sources.itervalues()) ) + return all((queue_is_ready(source) for source in sources.itervalues())) + def queue_is_ready(queue): assert isinstance(queue, deque) return len(queue) > 0 + def done(sources): """Feed is done when all internal queues have only a "DONE" message.""" assert isinstance(sources, dict) - return all( (queue_is_done(source) for source in sources.itervalues()) ) + return all((queue_is_done(source) for source in sources.itervalues())) + def queue_is_done(queue): assert isinstance(queue, deque) diff --git a/zipline/gens/sort.py b/zipline/gens/sort.py index d8ebd173..9ae4cbdf 100644 --- a/zipline/gens/sort.py +++ b/zipline/gens/sort.py @@ -11,6 +11,7 @@ from zipline.gens.utils import \ log = logbook.Logger('Sorting') + def date_sort(stream_in, source_ids): """ A generator that takes a generator and a list of source_ids. We @@ -51,6 +52,7 @@ def date_sort(stream_in, source_ids): assert queue[0].dt == "DONE", \ "Bad last message in date_sort on exit: %s" % queue + def ready(sources): """ Feed is ready when every internal queue has at least one @@ -58,16 +60,19 @@ def ready(sources): True only if ready(sources). """ assert isinstance(sources, dict) - return all( (queue_is_ready(source) for source in sources.itervalues()) ) + return all((queue_is_ready(source) for source in sources.itervalues())) + def queue_is_ready(queue): assert isinstance(queue, deque) return len(queue) > 0 + def done(sources): """Feed is done when all internal queues have only a "DONE" message.""" assert isinstance(sources, dict) - return all( (queue_is_done(source) for source in sources.itervalues()) ) + return all((queue_is_done(source) for source in sources.itervalues())) + def queue_is_done(queue): assert isinstance(queue, deque) @@ -79,6 +84,7 @@ def queue_is_done(queue): else: return False + def pop_oldest(sources): oldest_event = None @@ -92,7 +98,7 @@ def pop_oldest(sources): if current_event.dt == "DONE": continue # Any event is older than nothing. - elif oldest_event == None: + elif oldest_event is None: oldest_event = current_event # Keep the older event. Break ties by source_id. This will # trip an assert if we have duplicate sources. @@ -102,6 +108,7 @@ def pop_oldest(sources): # Pop the oldest event we found from its queue and return it. return sources[oldest_event.source_id].popleft() + # Return the event with the older timestamp. Break ties by source_id. def older(oldest, current): assert isinstance(oldest, ndict) diff --git a/zipline/gens/stddev.py b/zipline/gens/stddev.py index 71759b90..858924a7 100644 --- a/zipline/gens/stddev.py +++ b/zipline/gens/stddev.py @@ -1,11 +1,10 @@ from numbers import Number -from datetime import datetime, timedelta from collections import defaultdict from math import sqrt -from zipline import ndict from zipline.gens.transform import EventWindow, TransformMeta + class MovingStandardDev(object): """ Class that maintains a dicitonary from sids to @@ -15,7 +14,7 @@ class MovingStandardDev(object): """ __metaclass__ = TransformMeta - def __init__(self, market_aware, days = None, delta = None): + def __init__(self, market_aware, days=None, delta=None): self.market_aware = market_aware @@ -31,11 +30,11 @@ class MovingStandardDev(object): else: assert self.delta and not self.days, \ "Non-market-aware mode requires a timedelta." - + # No way to pass arguments to the defaultdict factory, so we # need to define a method to generate the correct EventWindows. self.sid_windows = defaultdict(self.create_window) - + def create_window(self): """ Factory method for self.sid_windows. @@ -45,7 +44,7 @@ class MovingStandardDev(object): self.days, self.delta ) - + def update(self, event): """ Update the event window for this event's sid. Return an ndict @@ -56,46 +55,46 @@ class MovingStandardDev(object): window = self.sid_windows[event.sid] window.update(event) return window.get_stddev() - + + class MovingStandardDevWindow(EventWindow): """ Iteratively calculates standard deviation for a particular sid over a given time window. The expected functionality of this class is to be instantiated inside a MovingStandardDev. """ - + def __init__(self, market_aware, days, delta): - # Call the superclass constructor to set up base EventWindow # infrastructure. EventWindow.__init__(self, market_aware, days, delta) self.sum = 0.0 self.sum_sqr = 0.0 - + def handle_add(self, event): - assert event.has_key('price') + assert 'price' in event assert isinstance(event.price, Number) self.sum += event.price self.sum_sqr += event.price ** 2 - + def handle_remove(self, event): - assert event.has_key('price') + assert 'price' in event assert isinstance(event.price, Number) - + self.sum -= event.price self.sum_sqr -= event.price ** 2 - + def get_stddev(self): - # Sample standard deviation is undefined for a single event or # no events. if len(self) <= 1: return None else: - average = self.sum /len(self) - s_squared = (self.sum_sqr - self.sum*average) / (len(self) - 1) + average = self.sum / len(self) + s_squared = (self.sum_sqr - self.sum * average) \ + / (len(self) - 1) stddev = sqrt(s_squared) return stddev diff --git a/zipline/gens/tradegens.py b/zipline/gens/tradegens.py index f088ae8b..4e0081e8 100644 --- a/zipline/gens/tradegens.py +++ b/zipline/gens/tradegens.py @@ -5,7 +5,7 @@ and zipline development import random import pytz -from itertools import chain, cycle, ifilter, izip, repeat +from itertools import cycle, ifilter, izip from datetime import datetime, timedelta import pandas as pd from copy import copy @@ -15,19 +15,23 @@ from zipline.protocol import DATASOURCE_TYPE from zipline.utils import ndict from zipline.gens.utils import hash_args, create_trade -def date_gen(start = datetime(2006, 6, 6, 12, tzinfo=pytz.utc), - delta = timedelta(minutes = 1), - count = 100, - repeats = None): + +def date_gen(start=datetime(2006, 6, 6, 12, tzinfo=pytz.utc), + delta=timedelta(minutes=1), + count=100, + repeats=None): """ Utility to generate a stream of dates. """ if repeats: - return (start + (i * delta) for i in xrange(count) for n in xrange(repeats)) + return (start + (i * delta) + for i in xrange(count) + for n in xrange(repeats)) else: return (start + (i * delta) for i in xrange(count)) -def mock_prices(count, rand = False): + +def mock_prices(count, rand=False): """ Utility to generate a stream of mock prices. By default cycles through values from 0.0 to 10.0, n times. Optional @@ -39,7 +43,8 @@ def mock_prices(count, rand = False): else: return (float(i % 10) + 1.0 for i in xrange(count)) -def mock_volumes(count, rand = False): + +def mock_volumes(count, rand=False): """ Utility to generate a set of volumes. By default cycles through values from 100 to 1000, incrementing by 50. Optional @@ -48,16 +53,18 @@ def mock_volumes(count, rand = False): if rand: return (random.randrange(100, 1000) for i in xrange(count)) else: - return ((i * 50)%900 + 100 for i in xrange(count)) + return ((i * 50) % 900 + 100 for i in xrange(count)) -def fuzzy_dates(count = 500): + +def fuzzy_dates(count=500): """ Add +-10 seconds to each event from a date_gen. Note that this still guarantees sorting, since the default on date_gen is minute separation of events. """ - for date in date_gen(count = count): - yield date + timedelta(seconds = random.randint(-10, 10)) + for date in date_gen(count=count): + yield date + timedelta(seconds=random.randint(-10, 10)) + class SpecificEquityTrades(object): """ @@ -88,21 +95,28 @@ class SpecificEquityTrades(object): # class should serve a single purpose (either take an # event_list or autocreate events). self.count = kwargs.get('count', len(self.event_list)) - self.sids = kwargs.get('sids', np.unique([event.sid for event in self.event_list]).tolist()) + self.sids = kwargs.get( + 'sids', + np.unique([event.sid for event in self.event_list]).tolist()) self.start = kwargs.get('start', self.event_list[0].dt) self.end = kwargs.get('start', self.event_list[-1].dt) - self.delta = kwargs.get('delta', self.event_list[1].dt - self.event_list[0].dt) + self.delta = kwargs.get( + 'delta', + self.event_list[1].dt - self.event_list[0].dt) self.concurrent = kwargs.get('concurrent', False) else: # Unpack config dictionary with default values. self.count = kwargs.get('count', 500) self.sids = kwargs.get('sids', [1, 2]) - self.start = kwargs.get('start', datetime(2008, 6, 6, 15, tzinfo = pytz.utc)) - self.delta = kwargs.get('delta', timedelta(minutes = 1)) + self.start = kwargs.get( + 'start', + datetime(2008, 6, 6, 15, tzinfo=pytz.utc)) + self.delta = kwargs.get( + 'delta', + timedelta(minutes=1)) self.concurrent = kwargs.get('concurrent', False) - # Hash_value for downstream sorting. self.arg_string = hash_args(*args, **kwargs) @@ -142,10 +156,7 @@ class SpecificEquityTrades(object): delta=self.delta, repeats=len(self.sids), ) - - else: - dates = date_gen( count=self.count, start=self.start, @@ -161,13 +172,14 @@ class SpecificEquityTrades(object): arg_gen = izip(sids, prices, volumes, dates) # Convert argument packages into events. - unfiltered = (create_trade(*args, source_id = self.get_hash()) + unfiltered = (create_trade(*args, source_id=self.get_hash()) for args in arg_gen) # If we specified a sid filter, filter out elements that don't # match the filter. if self.filter: - filtered = ifilter(lambda event: event.sid in self.filter, unfiltered) + filtered = ifilter( + lambda event: event.sid in self.filter, unfiltered) # Otherwise just use all events. else: @@ -201,7 +213,7 @@ class DataFrameSource(SpecificEquityTrades): self.sids = kwargs.get('sids', data.columns) self.start = kwargs.get('start', data.index[0]) self.end = kwargs.get('end', data.index[-1]) - self.delta = kwargs.get('delta', data.index[1]-data.index[0]) + self.delta = kwargs.get('delta', data.index[1] - data.index[0]) # Hash_value for downstream sorting. self.arg_string = hash_args(data, **kwargs) diff --git a/zipline/gens/tradesimulation.py b/zipline/gens/tradesimulation.py index 70e067d1..6400a5e6 100644 --- a/zipline/gens/tradesimulation.py +++ b/zipline/gens/tradesimulation.py @@ -15,8 +15,9 @@ log = Logger('Trade Simulation') # TODO: make these arguments rather than global constants INIT_TIMEOUT = 5 -HEARTBEAT_INTERVAL = 1 # seconds -MAX_HEARTBEAT_INTERVALS = 15 #count +HEARTBEAT_INTERVAL = 1 # seconds +MAX_HEARTBEAT_INTERVALS = 15 # count + class TradeSimulationClient(object): """ @@ -105,6 +106,7 @@ class TradeSimulationClient(object): for message in performance_messages: yield message + class AlgorithmSimulator(object): def __init__(self, @@ -138,7 +140,7 @@ class AlgorithmSimulator(object): # Handler for heartbeats during calls to handle_data. def log_heartbeats(beat_count, stackframe): t = beat_count * HEARTBEAT_INTERVAL - warning = "handle_data has been processing for %i seconds" %t + warning = "handle_data has been processing for %i seconds" % t self.algolog.warn(warning) # Context manager that calls log_heartbeats every HEARTBEAT_INTERVAL @@ -189,10 +191,10 @@ class AlgorithmSimulator(object): """ assert sid in self.sids, "Order on invalid sid: %i" % sid order = ndict({ - 'dt' : self.simulation_dt, - 'sid' : sid, - 'amount' : int(amount), - 'filled' : 0 + 'dt': self.simulation_dt, + 'sid': sid, + 'amount': int(amount), + 'filled': 0 }) # Tell the user if they try to buy 0 shares of something. @@ -229,7 +231,7 @@ class AlgorithmSimulator(object): for date, snapshot in groupby(stream_in, attrgetter('dt')): # Set the simulation date to be the first event we see. # This should only occur once, at the start of the test. - if self.simulation_dt == None: + if self.simulation_dt is None: self.simulation_dt = date # Done message has the risk report, so we yield before exiting. @@ -253,10 +255,10 @@ class AlgorithmSimulator(object): elif date < self.simulation_dt: for event in snapshot: # Only yield if we have something interesting to say. - if event.perf_message != None: + if event.perf_message is not None: yield event.perf_message - # Delete the message before updating so we don't send it - # to the user. + # Delete the message before updating, + # so we don't send it to the user. del event['perf_message'] self.update_universe(event) @@ -265,13 +267,14 @@ class AlgorithmSimulator(object): else: for event in snapshot: # Only yield if we have something interesting to say. - if event.perf_message != None: + if event.perf_message is not None: yield event.perf_message del event['perf_message'] self.update_universe(event) - # Send the current state of the universe to the user's algo. + # Send the current state of the universe + # to the user's algo. self.simulate_snapshot(date) def update_universe(self, event): diff --git a/zipline/gens/transform.py b/zipline/gens/transform.py index 3ff259cc..ae355e7e 100644 --- a/zipline/gens/transform.py +++ b/zipline/gens/transform.py @@ -17,6 +17,7 @@ from zipline.gens.utils import assert_sort_unframe_protocol, hash_args log = logbook.Logger('Transform') + class Passthrough(object): PASSTHROUGH = True """ @@ -28,6 +29,7 @@ class Passthrough(object): def update(self, event): pass + class TransformMeta(type): """ Metaclass that automatically packages a class inside of @@ -79,7 +81,8 @@ class StatefulTransform(object): # interpreter for most classes anyway, but here we have to # be explicit because we've overridden the method that # usually resolves to our super call. - self.state = super(TransformMeta, tnfm_class).__call__(*args, **kwargs) + self.state = super(TransformMeta, tnfm_class).__call__( + *args, **kwargs) # Normal object instantiation. else: self.state = tnfm_class(*args, **kwargs) @@ -102,7 +105,7 @@ class StatefulTransform(object): # allow upstream generators to yield None to avoid # blocking. - if message == None: + if message is None: continue assert_sort_unframe_protocol(message) @@ -153,7 +156,6 @@ class StatefulTransform(object): log.info('Finished StatefulTransform [%s]' % self.get_hash()) - class EventWindow(object): """ Abstract base class for transform classes that calculate iterative @@ -185,7 +187,7 @@ class EventWindow(object): # Market-aware mode only works with full-day windows. if self.market_aware: - assert self.days and self.delta == None,\ + assert self.days and self.delta is None, \ "Market-aware mode only works with full-day windows." self.all_holidays = deque(non_trading_days) self.cur_holidays = deque() @@ -276,12 +278,14 @@ class EventWindow(object): # that arrive in sorted order. def assert_well_formed(self, event): assert isinstance(event, ndict), "Bad event in EventWindow:%s" % event - assert event.has_key('dt'), "Missing dt in EventWindow:%s" % event - assert isinstance(event.dt, datetime),"Bad dt in EventWindow:%s" % event + assert 'dt' in event, "Missing dt in EventWindow:%s" % event + assert isinstance(event.dt, datetime), \ + "Bad dt in EventWindow:%s" % event if len(self.ticks) > 0: # Something is wrong if new event is older than previous. assert event.dt >= self.ticks[-1].dt, \ - "Events arrived out of order in EventWindow: %s -> %s" % (event, self.ticks[0]) + "Events arrived out of order in EventWindow: %s -> %s" % \ + (event, self.ticks[0]) class BatchTransform(EventWindow): @@ -309,7 +313,8 @@ class BatchTransform(EventWindow): ``` - In you algorithm you would then have to instantiate this in the initialize() method: + In you algorithm you would then have to instantiate + this in the initialize() method: ``` self.my_batch_transform = MyBatchTransform() ``` @@ -322,8 +327,15 @@ class BatchTransform(EventWindow): """ - def __init__(self, func=None, refresh_period=None, market_aware=True, delta=None, days=None, sids=None): - super(BatchTransform, self).__init__(market_aware, days=days, delta=delta) + def __init__(self, + func=None, + refresh_period=None, + market_aware=True, + delta=None, + days=None, + sids=None): + super(BatchTransform, self).__init__( + market_aware, days=days, delta=delta) if func is not None: self.compute_transform_value = func else: @@ -341,8 +353,8 @@ class BatchTransform(EventWindow): def handle_data(self, data, *args, **kwargs): """ - New method to handle a data frame as sent to the algorithm's handle_data - method. + New method to handle a data frame as sent to the algorithm's + handle_data method. """ # extract dates dts = [data[sid].datetime for sid in self.sids] @@ -383,7 +395,9 @@ class BatchTransform(EventWindow): values_per_sid = {} for sid in self.sids: - values_per_sid[sid] = pd.Series({tick[sid].dt: tick[sid][field_name] for tick in self.ticks}) + values_per_sid[sid] = pd.Series( + {tick[sid].dt: tick[sid][field_name] + for tick in self.ticks}) # concatenate different sids into one df fields[field_name] = pd.DataFrame.from_dict(values_per_sid) @@ -400,14 +414,16 @@ class BatchTransform(EventWindow): self.full = True def get_value(self, *args, **kwargs): - raise NotImplementedError("Either overwrite get_value or provide a func argument.") + raise NotImplementedError( + "Either overwrite get_value or provide a func argument.") def get_transform_value(self, *args, **kwargs): if self.data is None: return None if self.updated: - self.cached = self.compute_transform_value(self.data, *args, **kwargs) + self.cached = self.compute_transform_value(self.data, + *args, **kwargs) return self.cached diff --git a/zipline/gens/utils.py b/zipline/gens/utils.py index ce91d550..7dc44c57 100644 --- a/zipline/gens/utils.py +++ b/zipline/gens/utils.py @@ -34,9 +34,9 @@ done_message = mock_done def alternate(g1, g2): """Specialized version of roundrobin for just 2 generators.""" for e1, e2 in izip_longest(g1, g2): - if e1 != None: + if e1 is not None: yield e1 - if e2 != None: + if e2 is not None: yield e2 diff --git a/zipline/optimize/example.py b/zipline/optimize/example.py index ef84fc0d..b1aa5e41 100644 --- a/zipline/optimize/example.py +++ b/zipline/optimize/example.py @@ -6,10 +6,10 @@ import pandas as pd import numpy as np import matplotlib.pyplot as plt -import cProfile from zipline.gens.mavg import MovingAverage from zipline.algorithm import TradingAlgorithm + class DMA(TradingAlgorithm): """Dual Moving Average algorithm. """ @@ -34,10 +34,14 @@ class DMA(TradingAlgorithm): for sid in self.sids: # access transforms via their user-defined tag - if (data[sid].short_mavg['price'] > data[sid].long_mavg['price']) and not self.invested[sid]: + if (data[sid].short_mavg['price'] > + data[sid].long_mavg['price']) \ + and not self.invested[sid]: self.order(sid, self.amount) self.invested[sid] = True - elif (data[sid].short_mavg['price'] < data[sid].long_mavg['price']) and self.invested[sid]: + elif (data[sid].short_mavg['price'] < + data[sid].long_mavg['price']) \ + and self.invested[sid]: self.order(sid, -self.amount) self.invested[sid] = False @@ -48,7 +52,7 @@ def load_close_px(indexes=None, stocks=None): from collections import OrderedDict if indexes is None: - indexes = {'SPX' : '^GSPC'} + indexes = {'SPX': '^GSPC'} if stocks is None: stocks = ['AAPL', 'GE', 'IBM', 'MSFT', 'XOM', 'AA', 'JNJ', 'PEP'] @@ -78,12 +82,16 @@ def load_close_px(indexes=None, stocks=None): def run((short_window, long_window)): data = pd.load('close_px.dat') #data = load_close_px() - myalgo = DMA([0, 1], amount=100, short_window=short_window, long_window=long_window) + myalgo = DMA([0, 1], + amount=100, + short_window=short_window, + long_window=long_window) stats = myalgo.run(data) stats['sw'] = short_window stats['lw'] = long_window return stats + def explore_params(): sws, lws = np.mgrid[10:20:5, 10:20:5] @@ -97,7 +105,6 @@ def explore_params(): plt.savefig('DMA_contour.png') plt.show() -#stats = run((10, 50)) def get_opt_holdings_qp(univ_rets, track_rets): from cvxopt import matrix @@ -115,6 +122,7 @@ def get_opt_holdings_qp(univ_rets, track_rets): raise Exception('optimum not reached by QP') return pd.Series(np.array(result['x']).ravel(), index=univ_rets.columns) + def opt_portfolio(cov, budget, min_return): from cvxopt import matrix from cvxopt.solvers import qp @@ -122,7 +130,7 @@ def opt_portfolio(cov, budget, min_return): cov = matrix(2 * cov) q = matrix(np.zeros(n)) - h = matrix(budget) # G*x < h + h = matrix(budget) # G*x < h # coneqp result = qp(cov, q, h=h) if result['status'] != 'optimal': @@ -130,10 +138,12 @@ def opt_portfolio(cov, budget, min_return): return pd.Series(np.array(result['x']).ravel()) + def calc_te(weights, univ_rets, track_rets): port_rets = (univ_rets * weights).sum(1) return (port_rets - track_rets).std() + def plot_returns(port_returns, bmk_returns): plt.figure() cum_port = ((1 + port_returns).cumprod() - 1) @@ -145,4 +155,4 @@ def plot_returns(port_returns, bmk_returns): plt.title('Portfolio performance') plt.legend(loc='best') -print run((10, 20)) \ No newline at end of file +print run((10, 20)) diff --git a/zipline/optimize/factory.py b/zipline/optimize/factory.py index 14fea2ad..90d65a17 100644 --- a/zipline/optimize/factory.py +++ b/zipline/optimize/factory.py @@ -7,7 +7,10 @@ from datetime import timedelta import zipline.protocol as zp -from zipline.utils.factory import get_next_trading_dt, create_trading_environment +from zipline.utils.factory import ( + get_next_trading_dt, + create_trading_environment +) from zipline.gens.tradegens import SpecificEquityTrades from zipline.optimize.algorithms import BuySellAlgorithm from zipline.finance.slippage import FixedSlippage @@ -15,7 +18,9 @@ from zipline.finance.slippage import FixedSlippage from copy import copy from itertools import cycle -def create_updown_trade_source(sid, trade_count, trading_environment, base_price, amplitude): + +def create_updown_trade_source(sid, trade_count, trading_environment, + base_price, amplitude): """Create the updown trade source. This source emits events with the price going up and down by the same amount in each iteration. The trade source is thus perfectly predictable. This is @@ -27,7 +32,8 @@ def create_updown_trade_source(sid, trade_count, trading_environment, base_price trade_count : int How many trade events to create (will also influence order count) trading_environment : TradeEnvironment object - The trading environment to use (see zipline.factory.create_trading_environment) + The trading environment to use + (see zipline.factory.create_trading_environment) base_price : int The average price that each iteration will hover around. amplitude : int @@ -39,28 +45,28 @@ def create_updown_trade_source(sid, trade_count, trading_environment, base_price """ volume = 1000 events = [] - price = base_price-amplitude/2. + price = base_price - amplitude / 2. cur = trading_environment.first_open - one_day = timedelta(minutes = 1)#days = 1) + one_day = timedelta(minutes=1) #create iterator to cycle through up and down phases - change = cycle([1,-1]) + change = cycle([1, -1]) for i in xrange(trade_count + 2): cur = get_next_trading_dt(cur, one_day, trading_environment) event = zp.ndict({ - "type" : zp.DATASOURCE_TYPE.TRADE, - "sid" : sid, - "price" : price, - "volume" : volume, - "dt" : cur, + "type": zp.DATASOURCE_TYPE.TRADE, + "sid": sid, + "price": price, + "volume": volume, + "dt": cur, }) events.append(event) - price += change.next()*amplitude + price += change.next() * amplitude trading_environment.period_end = cur diff --git a/zipline/protocol.py b/zipline/protocol.py index f796693f..1e9a7cb3 100644 --- a/zipline/protocol.py +++ b/zipline/protocol.py @@ -15,12 +15,12 @@ DATASOURCE_TYPE = Enum( #Transform type needs to be a ndict to facilitate merging. TRANSFORM_TYPE = ndict({ - 'PASSTHROUGH' : 'PASSTHROUGH', - 'EMPTY' : '' + 'PASSTHROUGH': 'PASSTHROUGH', + 'EMPTY': '' }) FINANCE_COMPONENT = namelookup({ - 'TRADING_CLIENT' : 'TRADING_CLIENT', - 'PORTFOLIO_CLIENT' : 'PORTFOLIO_CLIENT', + 'TRADING_CLIENT': 'TRADING_CLIENT', + 'PORTFOLIO_CLIENT': 'PORTFOLIO_CLIENT', }) diff --git a/zipline/test_algorithms.py b/zipline/test_algorithms.py index ab54b15a..cb49a1ad 100644 --- a/zipline/test_algorithms.py +++ b/zipline/test_algorithms.py @@ -141,6 +141,7 @@ class HeavyBuyAlgorithm(): def set_transact_setter(self, txn_sim_callable): pass + class NoopAlgorithm(object): """ Dolce fa niente. @@ -167,6 +168,7 @@ class NoopAlgorithm(object): def set_transact_setter(self, txn_sim_callable): pass + class ExceptionAlgorithm(object): """ Throw an exception from the method name specified in the @@ -213,6 +215,7 @@ class ExceptionAlgorithm(object): def set_transact_setter(self, txn_sim_callable): pass + class DivByZeroAlgorithm(): def __init__(self, sid): @@ -234,7 +237,7 @@ class DivByZeroAlgorithm(): def handle_data(self, data): self.incr += 1 if self.incr > 4: - 5/0 + 5 / 0 pass def get_sid_filter(self): @@ -243,6 +246,7 @@ class DivByZeroAlgorithm(): def set_transact_setter(self, txn_sim_callable): pass + class InitializeTimeoutAlgorithm(): def __init__(self, sid): self.sid = sid @@ -253,7 +257,6 @@ class InitializeTimeoutAlgorithm(): from zipline.gens.tradesimulation import INIT_TIMEOUT time.sleep(INIT_TIMEOUT + 1000) - def set_order(self, order_callable): pass @@ -272,6 +275,7 @@ class InitializeTimeoutAlgorithm(): def set_transact_setter(self, txn_sim_callable): pass + class TooMuchProcessingAlgorithm(): def __init__(self, sid): self.sid = sid @@ -300,6 +304,7 @@ class TooMuchProcessingAlgorithm(): def set_transact_setter(self, txn_sim_callable): pass + class TimeoutAlgorithm(): def __init__(self, sid): @@ -330,6 +335,7 @@ class TimeoutAlgorithm(): def set_transact_setter(self, txn_sim_callable): pass + class TestPrintAlgorithm(): def __init__(self, sid): @@ -357,6 +363,7 @@ class TestPrintAlgorithm(): def set_transact_setter(self, txn_sim_callable): pass + class TestLoggingAlgorithm(): def __init__(self, sid): @@ -390,6 +397,7 @@ from zipline.algorithm import TradingAlgorithm from zipline.gens.transform import BatchTransform, batch_transform from zipline.gens.mavg import MovingAverage + class TestRegisterTransformAlgorithm(TradingAlgorithm): def initialize(self): self.add_transform(MovingAverage, 'mavg', ['price'], @@ -399,6 +407,7 @@ class TestRegisterTransformAlgorithm(TradingAlgorithm): def handle_data(self, data): pass + ########################################## # Algorithm using simple batch transforms @@ -406,14 +415,17 @@ class ReturnPriceBatchTransform(BatchTransform): def get_value(self, data): return data.price + @batch_transform def return_price_batch_decorator(data): return data.price + @batch_transform def return_args_batch_decorator(data, *args, **kwargs): return args, kwargs + class BatchTransformAlgorithm(TradingAlgorithm): def initialize(self, *args, **kwargs): self.history_return_price_class = [] @@ -425,25 +437,32 @@ class BatchTransformAlgorithm(TradingAlgorithm): self.args = args self.kwargs = kwargs - self.return_price_class = ReturnPriceBatchTransform(sids=self.sids, - market_aware=False, - refresh_period=2, - delta=timedelta(days=self.days) + self.return_price_class = ReturnPriceBatchTransform( + sids=self.sids, + market_aware=False, + refresh_period=2, + delta=timedelta(days=self.days) ) - self.return_price_decorator = return_price_batch_decorator(sids=self.sids, - market_aware=False, - refresh_period=2, - delta=timedelta(days=self.days) + self.return_price_decorator = return_price_batch_decorator( + sids=self.sids, + market_aware=False, + refresh_period=2, + delta=timedelta(days=self.days) ) - self.return_args_batch = return_args_batch_decorator(sids=self.sids, - market_aware=False, - refresh_period=2, - delta=timedelta(days=self.days) + self.return_args_batch = return_args_batch_decorator( + sids=self.sids, + market_aware=False, + refresh_period=2, + delta=timedelta(days=self.days) ) def handle_data(self, data): - self.history_return_price_class.append(self.return_price_class.handle_data(data)) - self.history_return_price_decorator.append(self.return_price_decorator.handle_data(data)) - self.history_return_args.append(self.return_args_batch.handle_data(data, *self.args, **self.kwargs)) + self.history_return_price_class.append( + self.return_price_class.handle_data(data)) + self.history_return_price_decorator.append( + self.return_price_decorator.handle_data(data)) + self.history_return_args.append( + self.return_args_batch.handle_data( + data, *self.args, **self.kwargs)) diff --git a/zipline/utils/date_utils.py b/zipline/utils/date_utils.py index 8e2aeaaa..4f8e1e13 100644 --- a/zipline/utils/date_utils.py +++ b/zipline/utils/date_utils.py @@ -4,34 +4,44 @@ import time import pytz import iso8601 from dateutil import rrule -from datetime import datetime, date, timedelta -from dateutil.relativedelta import * +from datetime import datetime, timedelta # Datetime Tuple # -------------- -d_tuple = namedtuple('dt', ['year', 'month', 'day', 'hour', 'minute', 'second', 'micros']) +d_tuple = namedtuple('dt', + ['year', + 'month', + 'day', + 'hour', + 'minute', + 'second', + 'micros']) + # iso8061 utility # --------------------- def parse_iso8061(date_string): dt = iso8601.parse_date(date_string) - dt = dt.replace(tzinfo = pytz.utc) + dt = dt.replace(tzinfo=pytz.utc) return dt # Epoch utilities # --------------------- -UNIX_EPOCH = datetime(1970, 1, 1, 0, 0, tzinfo = pytz.utc) +UNIX_EPOCH = datetime(1970, 1, 1, 0, 0, tzinfo=pytz.utc) + + def EPOCH(utc_datetime): """ The key is to ensure all the dates you are using are in the utc timezone before you start converting. See http://pytz.sourceforge.net/ to learn how to do that properly. By normalizing to utc, you eliminate the ambiguity of - daylight savings transitions. Then you can safely use timedelta to calculate - distance from the unix epoch, and then convert to seconds or milliseconds. + daylight savings transitions. Then you can safely use timedelta to + calculate distance from the unix epoch, and then convert to seconds or + milliseconds. - Note that the resulting unix timestamp is itself in the UTC timezone. If you - wish to see the timestamp in a localized timezone, you will need to make - another conversion. + Note that the resulting unix timestamp is itself in the UTC timezone. + If you wish to see the timestamp in a localized timezone, you will need + to make another conversion. Also note that this will only work for dates after 1970. """ @@ -45,35 +55,42 @@ def EPOCH(utc_datetime): ms = seconds * 1000 return int(ms) + def UN_EPOCH(ms_since_epoch): - delta = timedelta(milliseconds = ms_since_epoch) + delta = timedelta(milliseconds=ms_since_epoch) dt = UNIX_EPOCH + delta return dt + def iso8061_to_epoch(datestring): dt = parse_iso8061(datestring) return EPOCH(dt) + def epoch_now(): dt = utcnow() return EPOCH(dt) + # UTC Datetime Subclasses # ----------------------- def utcnow(): return datetime.utcnow().replace(tzinfo=pytz.utc) + class utcdatetime(datetime): def __new__(cls, *args, **kwargs): kwargs['tzinfo'] = pytz.utc dt = datetime.__new__(cls, *args, **kwargs) return dt + def days_since_epoch(ms_since_epoch): dt = UN_EPOCH(ms_since_epoch) delta = dt - UNIX_EPOCH return delta.days + def epoch_from_days(days_since_epoch): delta = timedelta(days=days_since_epoch) dt = UNIX_EPOCH + delta @@ -86,15 +103,15 @@ def epoch_from_days(days_since_epoch): WEEKDAYS = [rrule.MO, rrule.TU, rrule.WE, rrule.TH, rrule.FR] HOLIDAYS = { - 'new_years' : datetime(2008 , 1 , 1 ), - 'mlk_day' : datetime(2008 , 1 , 21), - 'presidents' : datetime(2008 , 2 , 18), - 'good_friday' : datetime(2008 , 3 , 21), - 'memorial_day' : datetime(2008 , 5 , 26), - 'july_4th' : datetime(2008 , 7 , 4 ), - 'labor_day' : datetime(2008 , 9 , 1 ), - 'tgiving' : datetime(2008 , 11 , 27), - 'christmas' : datetime(2008 , 12 , 25), + 'new_years': datetime(2008, 1, 1), + 'mlk_day': datetime(2008, 1, 21), + 'presidents': datetime(2008, 2, 18), + 'good_friday': datetime(2008, 3, 21), + 'memorial_day': datetime(2008, 5, 26), + 'july_4th': datetime(2008, 7, 4), + 'labor_day': datetime(2008, 9, 1), + 'tgiving': datetime(2008, 11, 27), + 'christmas': datetime(2008, 12, 25), } # Create a rule to recur every weekday starting today @@ -112,6 +129,7 @@ rs.rrule(rule) for holiday in HOLIDAYS.itervalues(): rs.exdate(holiday) + def trading_days(after, before, inclusive=False): """ Iterates over the NYSE trading days between the two given @@ -136,13 +154,15 @@ if __name__ == '__main__': print day print time.time() - tic + def date_to_datetime(t): dt = datetime.fromordinal(t.toordinal()) - dt = dt.replace(tzinfo = pytz.utc) + dt = dt.replace(tzinfo=pytz.utc) return dt + def tuple_to_date(date_tuple): year, month, day, hour, minute, second, micros = date_tuple dt = datetime(year, month, day, hour, minute, second) - dt = dt.replace(microsecond = micros, tzinfo = pytz.utc) + dt = dt.replace(microsecond=micros, tzinfo=pytz.utc) return dt diff --git a/zipline/utils/delayed_signals.py b/zipline/utils/delayed_signals.py index 9ca8c811..78ccf453 100644 --- a/zipline/utils/delayed_signals.py +++ b/zipline/utils/delayed_signals.py @@ -1,5 +1,6 @@ from functools import wraps -from signal import signal +from signal import signal + class delayed_signals(object): """ diff --git a/zipline/utils/factory.py b/zipline/utils/factory.py index c8a2311b..69a0c67b 100644 --- a/zipline/utils/factory.py +++ b/zipline/utils/factory.py @@ -19,17 +19,20 @@ from zipline.gens.tradegens import SpecificEquityTrades, DataFrameSource from zipline.gens.utils import create_trade from zipline.finance.trading import TradingEnvironment + # TODO def data_path(): from zipline import data data_path = dirname(abspath(data.__file__)) return data_path + def logger_path(): import zipline log_path = dirname(abspath(zipline.__file__)) return os.join(log_path, 'logging.cfg') + def load_market_data(): fp_bm = open(join(data_path(), "benchmark.msgpack"), "rb") bm_list = msgpack.loads(fp_bm.read()) @@ -57,6 +60,7 @@ def load_market_data(): return bm_returns, tr_curves + def create_trading_environment(year=2006, start=None, end=None): """Construct a complete environment with reasonable defaults""" benchmark_returns, treasury_curves = load_market_data() @@ -64,14 +68,14 @@ def create_trading_environment(year=2006, start=None, end=None): if start is None: start = datetime(year, 1, 1, tzinfo=pytz.utc) if end is None: - end = datetime(year, 12, 31, tzinfo=pytz.utc) + end = datetime(year, 12, 31, tzinfo=pytz.utc) trading_environment = TradingEnvironment( benchmark_returns, treasury_curves, - period_start = start, - period_end = end, - capital_base = 100000.0 + period_start=start, + period_end=end, + capital_base=100000.0 ) return trading_environment @@ -86,6 +90,7 @@ def get_next_trading_dt(current, interval, trading_calendar): return next + def create_trade_history(sid, prices, amounts, interval, trading_calendar): trades = [] current = trading_calendar.first_open @@ -98,15 +103,17 @@ def create_trade_history(sid, prices, amounts, interval, trading_calendar): assert len(trades) == len(prices) return trades + def create_txn(sid, price, amount, datetime, btrid=None): txn = ndict({ - 'sid' : sid, - 'amount' : amount, - 'dt' : datetime, - 'price' : price, + 'sid': sid, + 'amount': amount, + 'dt': datetime, + 'price': price, }) return txn + def create_txn_history(sid, priceList, amtList, interval, trading_calendar): txns = [] current = trading_calendar.first_open @@ -126,7 +133,7 @@ def create_returns(daycount, trading_calendar): """ test_range = [] current = trading_calendar.first_open - one_day = timedelta(days = 1) + one_day = timedelta(days=1) for day in range(daycount): current = current + one_day @@ -140,7 +147,7 @@ def create_returns(daycount, trading_calendar): def create_returns_from_range(trading_calendar): current = trading_calendar.first_open end = trading_calendar.last_close - one_day = timedelta(days = 1) + one_day = timedelta(days=1) test_range = [] while current <= end: r = risk.DailyReturn(current, random.random()) @@ -149,9 +156,10 @@ def create_returns_from_range(trading_calendar): return test_range + def create_returns_from_list(returns, trading_calendar): current = trading_calendar.first_open - one_day = timedelta(days = 1) + one_day = timedelta(days=1) test_range = [] #sometimes the range starts with a non-trading day. @@ -165,21 +173,23 @@ def create_returns_from_list(returns, trading_calendar): return test_range + def create_random_trade_source(sid, trade_count, trading_environment): # create the source source = RandomEquityTrades(sid, trade_count) # make the period_end of trading_environment match cur = trading_environment.first_open - one_day = timedelta(days = 1) + one_day = timedelta(days=1) for i in range(trade_count + 2): - cur = get_next_trading_dt(cur, one_day, trading_environment) + cur = get_next_trading_dt(cur, one_day, trading_environment) trading_environment.period_end = cur return source -def create_daily_trade_source(sids, trade_count, trading_environment, concurrent=False): +def create_daily_trade_source(sids, trade_count, trading_environment, + concurrent=False): """ creates trade_count trades for each sid in sids list. first trade will be on trading_environment.period_start, and daily @@ -198,8 +208,8 @@ def create_daily_trade_source(sids, trade_count, trading_environment, concurrent ) -def create_minutely_trade_source(sids, trade_count, trading_environment, concurrent=False): - +def create_minutely_trade_source(sids, trade_count, trading_environment, + concurrent=False): """ creates trade_count trades for each sid in sids list. first trade will be on trading_environment.period_start, and every minute @@ -217,16 +227,19 @@ def create_minutely_trade_source(sids, trade_count, trading_environment, concurr concurrent=concurrent ) -def create_trade_source(sids, trade_count, trade_time_increment, trading_environment, concurrent=False): + +def create_trade_source(sids, trade_count, + trade_time_increment, trading_environment, + concurrent=False): args = tuple() kwargs = { - 'count' : trade_count, - 'sids' : sids, - 'start' : trading_environment.first_open, - 'delta' : trade_time_increment, - 'filter' : sids, - 'concurrent' : concurrent + 'count': trade_count, + 'sids': sids, + 'start': trading_environment.first_open, + 'delta': trade_time_increment, + 'filter': sids, + 'concurrent': concurrent } source = SpecificEquityTrades(*args, **kwargs) @@ -236,6 +249,7 @@ def create_trade_source(sids, trade_count, trade_time_increment, trading_environ return source + def create_test_df_source(): start = pd.datetime(1990, 1, 3, 0, 0, 0, 0, pytz.utc) end = pd.datetime(1990, 1, 8, 0, 0, 0, 0, pytz.utc) @@ -244,5 +258,3 @@ def create_test_df_source(): df = pd.DataFrame(x, index=index, columns=[0, 1]) return DataFrameSource(df), df - - diff --git a/zipline/utils/log_utils.py b/zipline/utils/log_utils.py index c7d7b9b9..102c9a7b 100644 --- a/zipline/utils/log_utils.py +++ b/zipline/utils/log_utils.py @@ -4,6 +4,7 @@ from contextlib import contextmanager log = logbook.Logger("LogUtils") + class redirector(object): def __init__(self, logger, name): self.logger = logger @@ -17,19 +18,20 @@ class redirector(object): if not self.buffer: return out_form = """ [{pipe_name}] \n{buffer}""".format( - pipe_name = self.name, - buffer = self.buffer + pipe_name=self.name, + buffer=self.buffer ) self.logger.error(out_form) self.buffer = bytes() + class log_redirector(object): def __init__(self, logger): self.logger = logger def write(self, line): #Absorb blank lines from print statements. - if line =='\n': + if line == '\n': return else: @@ -39,6 +41,7 @@ class log_redirector(object): def flush(self, final=False): pass + @contextmanager def stdout_pipe(logger, pipe_name): """ @@ -55,6 +58,7 @@ def stdout_pipe(logger, pipe_name): sys.stdout.flush() sys.stdout, sys.stderr = orig_fds + @contextmanager def stdout_only_pipe(logger, pipe_name): """ diff --git a/zipline/utils/protocol_utils.py b/zipline/utils/protocol_utils.py index 62f3aab9..f1c77102 100644 --- a/zipline/utils/protocol_utils.py +++ b/zipline/utils/protocol_utils.py @@ -3,6 +3,7 @@ import pandas from ctypes import Structure, c_ubyte from collections import MutableMapping + def Enum(*options): """ Fast enums are very important when we want really tight @@ -14,6 +15,7 @@ def Enum(*options): __iter__ = lambda s: iter(range(len(options))) return cstruct(*range(len(options))) + def FrameExceptionFactory(name): """ Exception factory with a closure around the frame class name. @@ -24,12 +26,13 @@ def FrameExceptionFactory(name): def __str__(self): return "Invalid {framecls} Frame: {got}".format( - framecls = name, - got = self.got, + framecls=name, + got=self.got, ) return InvalidFrame + class ndict(MutableMapping): """ Xtreme Namedicts 2.0 @@ -160,6 +163,7 @@ class ndict(MutableMapping): #return True + # This is not neccesarily the most intuitive construction, but # we're aiming for raw performance rather than readability. So # we do things that we would not normally do in business logic. @@ -167,14 +171,18 @@ def namelookup(dct): ks = dct.keys() vs = dct.values() dct = {} + class _lookup: __slots__ = ks + def __init__(self): for k, v in zip(ks, vs): - setattr(self,k,v) + setattr(self, k, v) self.__setattr__ = self.locked - def locked(self,k,v): + + def locked(self, k, v): raise Exception('Name lookups are fixed at init.') + def __repr__(self): return '' % self.__slots__ del dct diff --git a/zipline/utils/simfactory.py b/zipline/utils/simfactory.py index ba2d5abb..8691083f 100644 --- a/zipline/utils/simfactory.py +++ b/zipline/utils/simfactory.py @@ -5,109 +5,110 @@ from zipline.lines import SimulatedTrading from zipline.finance.slippage import FixedSlippage, transact_partial from zipline.finance.commission import PerShare + def create_test_zipline(**config): - """ - :param config: A configuration object that is a dict with: + """ + :param config: A configuration object that is a dict with: - - environment - a \ - :py:class:`zipline.finance.trading.TradingEnvironment` - - sid - an integer, which will be used as the security ID. - - order_count - the number of orders the test algo will place, - defaults to 100 - - order_amount - the number of shares per order, defaults to 100 - - trade_count - the number of trades to simulate, defaults to 101 - to ensure all orders are processed. - - algorithm - optional parameter providing an algorithm. defaults - to :py:class:`zipline.test.algorithms.TestAlgorithm` - - trade_source - optional parameter to specify trades, if present. - If not present :py:class:`zipline.sources.SpecificEquityTrades` - is the source, with daily frequency in trades. - - slippage: optional parameter that configures the - :py:class:`zipline.gens.tradingsimulation.TransactionSimulator`. Expects - an object with a simulate mehod, such as - :py:class:`zipline.gens.tradingsimulation.FixedSlippage`. - :py:mod:`zipline.finance.trading` - - transforms: optional parameter that provides a list - of StatefulTransform objects. - """ - assert isinstance(config, dict) - sid_list = config.get('sid_list') - if not sid_list: - sid = config.get('sid') - sid_list = [sid] + - environment - a \ + :py:class:`zipline.finance.trading.TradingEnvironment` + - sid - an integer, which will be used as the security ID. + - order_count - the number of orders the test algo will place, + defaults to 100 + - order_amount - the number of shares per order, defaults to 100 + - trade_count - the number of trades to simulate, defaults to 101 + to ensure all orders are processed. + - algorithm - optional parameter providing an algorithm. defaults + to :py:class:`zipline.test.algorithms.TestAlgorithm` + - trade_source - optional parameter to specify trades, if present. + If not present :py:class:`zipline.sources.SpecificEquityTrades` + is the source, with daily frequency in trades. + - slippage: optional parameter that configures the + :py:class:`zipline.gens.tradingsimulation.TransactionSimulator`. Expects + an object with a simulate mehod, such as + :py:class:`zipline.gens.tradingsimulation.FixedSlippage`. + :py:mod:`zipline.finance.trading` + - transforms: optional parameter that provides a list + of StatefulTransform objects. + """ + assert isinstance(config, dict) + sid_list = config.get('sid_list') + if not sid_list: + sid = config.get('sid') + sid_list = [sid] - concurrent_trades = config.get('concurrent_trades', False) + concurrent_trades = config.get('concurrent_trades', False) - #-------------------- - # Trading Environment - #-------------------- - if 'environment' in config: - trading_environment = config['environment'] - else: - trading_environment = factory.create_trading_environment() + #-------------------- + # Trading Environment + #-------------------- + if 'environment' in config: + trading_environment = config['environment'] + else: + trading_environment = factory.create_trading_environment() - if 'order_count' in config: - order_count = config['order_count'] - else: - order_count = 100 + if 'order_count' in config: + order_count = config['order_count'] + else: + order_count = 100 - if 'order_amount' in config: - order_amount = config['order_amount'] - else: - order_amount = 100 + if 'order_amount' in config: + order_amount = config['order_amount'] + else: + order_amount = 100 - if 'trade_count' in config: - trade_count = config['trade_count'] - else: - # to ensure all orders are filled, we provide one more - # trade than order - trade_count = 101 + if 'trade_count' in config: + trade_count = config['trade_count'] + else: + # to ensure all orders are filled, we provide one more + # trade than order + trade_count = 101 - slippage = config.get('slippage', FixedSlippage()) - commission = PerShare() - transact_method = transact_partial(slippage, commission) + slippage = config.get('slippage', FixedSlippage()) + commission = PerShare() + transact_method = transact_partial(slippage, commission) - #------------------- + #------------------- # Trade Source - #------------------- - if 'trade_source' in config: - trade_source = config['trade_source'] - else: - trade_source = factory.create_daily_trade_source( - sid_list, - trade_count, - trading_environment, - concurrent=concurrent_trades + #------------------- + if 'trade_source' in config: + trade_source = config['trade_source'] + else: + trade_source = factory.create_daily_trade_source( + sid_list, + trade_count, + trading_environment, + concurrent=concurrent_trades + ) + + #------------------- + # Transforms + #------------------- + transforms = config.get('transforms', []) + + #------------------- + # Create the Algo + #------------------- + if 'algorithm' in config: + test_algo = config['algorithm'] + else: + test_algo = TestAlgorithm( + sid, + order_amount, + order_count ) - #------------------- - # Transforms - #------------------- - transforms = config.get('transforms', []) + #------------------- + # Simulation + #------------------- - #------------------- - # Create the Algo - #------------------- - if 'algorithm' in config: - test_algo = config['algorithm'] - else: - test_algo = TestAlgorithm( - sid, - order_amount, - order_count - ) + sim = SimulatedTrading( + [trade_source], + transforms, + test_algo, + trading_environment, + transact_method + ) + #------------------- - #------------------- - # Simulation - #------------------- - - sim = SimulatedTrading( - [trade_source], - transforms, - test_algo, - trading_environment, - transact_method - ) - #------------------- - - return sim + return sim diff --git a/zipline/utils/timeout.py b/zipline/utils/timeout.py index 34d4a4a5..c666876a 100644 --- a/zipline/utils/timeout.py +++ b/zipline/utils/timeout.py @@ -2,26 +2,24 @@ import signal from functools import wraps -from pprint import pprint as pp from numbers import Number -from logbook import Logger + class TimeoutException(Exception): - + def __init__(self, frame, message=''): self.frame = frame self.message = message + # TODO: fix code replication here. - class Timeout(object): """ Utility to make a function raise TimeoutException if it spends more than a specified number of seconds executing. Can be used - as a decorator to apply a static timeout to a function, or as + as a decorator to apply a static timeout to a function, or as a context manager to dynamically add a timeout to a code block. """ - def __init__(self, seconds, message=''): self.seconds = seconds self.message = message @@ -32,7 +30,6 @@ class Timeout(object): raise TimeoutException(frame, self.message) def __call__(self, fn): - @wraps(fn) def call_fn_with_timeout(*args, **kwargs): # Set the alarm, saving any handler that existed previously. @@ -58,25 +55,26 @@ class Timeout(object): # Set the alarm on entrance. signal.signal(signal.SIGALRM, self.handler) signal.setitimer(signal.ITIMER_REAL, self.seconds, 0) - + def __exit__(self, type, value, traceback): # Deactivate the alarm on exit. This will re-raise # any exceptions raised inside the with block. signal.signal(signal.SIGALRM, self.handler) signal.setitimer(signal.ITIMER_REAL, 0, 0) - + + class Heartbeat(object): """ Utility to perform pseudo-heartbeat checks on a single-threaded - function. Calls frame_handler on the current stack frame of the + function. Calls frame_handler on the current stack frame of the wrapped function every ``interval`` seconds. After ``max_interval`` intervals, raises Timeout. Can be used either as a decorator or a context manager. """ - def __init__(self, - interval, - max_intervals, - frame_handler=None, + def __init__(self, + interval, + max_intervals, + frame_handler=None, timeout_message=''): self.interval = interval @@ -84,12 +82,12 @@ class Heartbeat(object): self.frame_handler = frame_handler self.timeout_message = timeout_message self.count = 0 - + def handler(self, signum, frame): self.count += 1 if self.frame_handler: self.frame_handler(self.count, frame) - + if self.count >= self.max_intervals: raise TimeoutException(frame, self.timeout_message) @@ -97,7 +95,7 @@ class Heartbeat(object): @wraps(fn) def call_fn_with_heartbeat(*args, **kwargs): - # Set a timer to call our handler every ``interval`` seconds. + # Set a timer to call our handler every ``interval`` seconds. signal.signal(signal.SIGALRM, self.handler) signal.setitimer(signal.ITIMER_REAL, self.interval, self.interval) try: @@ -115,13 +113,13 @@ class Heartbeat(object): # other exception was raised by self.handle. return outval return call_fn_with_heartbeat - + def __enter__(self): - # Set a timer to call our handler every N seconds. + # Set a timer to call our handler every N seconds. self.count = 0 signal.signal(signal.SIGALRM, self.handler) signal.setitimer(signal.ITIMER_REAL, self.interval, self.interval) - + def __exit__(self, type, value, traceback): # Turn off the timer on exit. This will re-raise any exception raised # during execution of the with-block diff --git a/zipline/utils/tradingcalendar.py b/zipline/utils/tradingcalendar.py index 36dd3eea..da58a3bc 100644 --- a/zipline/utils/tradingcalendar.py +++ b/zipline/utils/tradingcalendar.py @@ -1,108 +1,108 @@ import pytz -from datetime import datetime, timedelta, date +from datetime import datetime from dateutil import rrule from zipline.utils.date_utils import utcnow -start = datetime(2002, 1,1, tzinfo=pytz.utc) +start = datetime(2002, 1, 1, tzinfo=pytz.utc) end = utcnow() non_trading_rules = [] weekends = rrule.rrule( - rrule.YEARLY, - byweekday=(rrule.SA, rrule.SU), - cache = True, - dtstart = start, - until = end + rrule.YEARLY, + byweekday=(rrule.SA, rrule.SU), + cache=True, + dtstart=start, + until=end ) non_trading_rules.append(weekends) new_years = rrule.rrule( rrule.MONTHLY, - byyearday = 1, - cache = True, - dtstart = start, - until = end + byyearday=1, + cache=True, + dtstart=start, + until=end ) non_trading_rules.append(new_years) mlk_day = rrule.rrule( rrule.MONTHLY, - bymonth = 1, - byweekday = (rrule.MO(+3)), - cache = True, - dtstart = start, - until = end + bymonth=1, + byweekday=(rrule.MO(+3)), + cache=True, + dtstart=start, + until=end ) non_trading_rules.append(mlk_day) presidents_day = rrule.rrule( - rrule.MONTHLY, - bymonth = 2, - byweekday = (rrule.MO(3)), - cache = True, - dtstart = start, - until = end + rrule.MONTHLY, + bymonth=2, + byweekday=(rrule.MO(3)), + cache=True, + dtstart=start, + until=end ) non_trading_rules.append(presidents_day) good_friday = rrule.rrule( rrule.DAILY, - byeaster = -2, - cache = True, - dtstart = start, - until = end + byeaster=-2, + cache=True, + dtstart=start, + until=end ) non_trading_rules.append(good_friday) memorial_day = rrule.rrule( rrule.MONTHLY, - bymonth = 5, - byweekday = (rrule.MO(-1)), - cache = True, - dtstart = start, - until = end + bymonth=5, + byweekday=(rrule.MO(-1)), + cache=True, + dtstart=start, + until=end ) non_trading_rules.append(memorial_day) july_4th = rrule.rrule( rrule.MONTHLY, - bymonth = 7, - bymonthday = 4, - cache = True, - dtstart = start, - until = end + bymonth=7, + bymonthday=4, + cache=True, + dtstart=start, + until=end ) non_trading_rules.append(july_4th) labor_day = rrule.rrule( rrule.MONTHLY, - bymonth = 9, - byweekday = (rrule.MO(1)), - cache = True, - dtstart = start, - until = end + bymonth=9, + byweekday=(rrule.MO(1)), + cache=True, + dtstart=start, + until=end ) non_trading_rules.append(labor_day) thanksgiving = rrule.rrule( rrule.MONTHLY, - bymonth = 11, - byweekday = (rrule.TH(-1)), - cache = True, - dtstart = start, - until = end + bymonth=11, + byweekday=(rrule.TH(-1)), + cache=True, + dtstart=start, + until=end ) non_trading_rules.append(thanksgiving) christmas = rrule.rrule( rrule.MONTHLY, - bymonth = 12, - bymonthday = 25, - cache = True, - dtstart = start, - until = end + bymonth=12, + bymonthday=25, + cache=True, + dtstart=start, + until=end ) non_trading_rules.append(christmas) diff --git a/zipline/version.py b/zipline/version.py index fc9fc57e..0244a256 100644 --- a/zipline/version.py +++ b/zipline/version.py @@ -3,7 +3,8 @@ Zipline {version} Released under BSD3 """.strip() -VERSION = ( 0, 0, 1, 'dev' ) +VERSION = (0, 0, 1, 'dev') + def pretty_version(): return BANNER.format(version='.'.join(VERSION))