mirror of
https://github.com/wassname/catalyst.git
synced 2026-06-27 18:04:12 +08:00
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:
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
]
|
||||
|
||||
@@ -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
@@ -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])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from functools import wraps
|
||||
from signal import signal
|
||||
from signal import signal
|
||||
|
||||
|
||||
class delayed_signals(object):
|
||||
"""
|
||||
|
||||
+37
-25
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user