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.
This commit is contained in:
Eddie Hebert
2012-10-04 16:05:13 -04:00
parent 0cd8931a5b
commit 77af1ca632
36 changed files with 1461 additions and 868 deletions
+5 -4
View File
@@ -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
+3 -2
View File
@@ -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)
+12 -11
View File
@@ -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):
+84 -86
View File
@@ -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))
+16 -11
View File
@@ -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."
assert nd.x[-1] != 4, "also copied to original."
+21 -18
View File
@@ -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
+615 -179
View File
@@ -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
]
+2 -1
View File
@@ -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."
"DataFrameSource should only stream selected sid 0, not sid 1."
+46 -34
View File
@@ -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])
+1 -1
View File
@@ -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
+85 -86
View File
@@ -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
+65 -57
View File
@@ -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):
+17 -12
View File
@@ -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
)
+23 -23
View File
@@ -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
+2 -1
View File
@@ -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)
+1 -1
View File
@@ -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],
+12 -7
View File
@@ -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)
+10 -3
View File
@@ -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)
+17 -18
View File
@@ -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
+35 -23
View File
@@ -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)
+16 -13
View File
@@ -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):
+31 -15
View File
@@ -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
+2 -2
View File
@@ -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
+18 -8
View File
@@ -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))
print run((10, 20))
+18 -12
View File
@@ -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
+4 -4
View File
@@ -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',
})
+36 -17
View File
@@ -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))
+42 -22
View File
@@ -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
+2 -1
View File
@@ -1,5 +1,6 @@
from functools import wraps
from signal import signal
from signal import signal
class delayed_signals(object):
"""
+37 -25
View File
@@ -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
+7 -3
View File
@@ -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):
"""
+12 -4
View File
@@ -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 '<namelookup %s>' % self.__slots__
del dct
+93 -92
View File
@@ -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
+18 -20
View File
@@ -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
+51 -51
View File
@@ -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)
+2 -1
View File
@@ -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))