diff --git a/zipline/finance/trading.py b/zipline/finance/trading.py index 3392f1a9..1dcd69bc 100644 --- a/zipline/finance/trading.py +++ b/zipline/finance/trading.py @@ -29,6 +29,12 @@ class TradeSimulationClient(qmsg.Component): ) self.perf = perf.PerformanceTracker(self.trading_environment) + ################################################################## + # TODO: the next line of code need refactoring from RealDiehl + # The below sets up the performance object to trigger a full risk + # report with rolling periods over the entire test duration. We + # would prefer something more explicit than a callback. + ################################################################## self.on_done = self.perf.handle_simulation_end @@ -107,6 +113,13 @@ class TradeSimulationClient(qmsg.Component): self.order_socket.send(str(zp.ORDER_PROTOCOL.DONE)) def queue_event(self, event): + ################################################################## + # TODO: the next line of code need refactoring from RealDiehl + # the performance class needs to process each event, without skipping + # and any callbacks should wait until the performance has been + # updated, so that down stream components can safely assume that + # performance is up to date. + ################################################################## self.perf.process_event(event) if self.event_queue == None: self.event_queue = [] @@ -157,7 +170,7 @@ class OrderDataSource(qmsg.DataSource): orders = [] count = 0 while True: - + (rlist, wlist, xlist) = select( [self.order_socket], [], diff --git a/zipline/lines.py b/zipline/lines.py index 88ead984..93356154 100644 --- a/zipline/lines.py +++ b/zipline/lines.py @@ -13,48 +13,7 @@ provides complete zipline topologies. You can extend any zipline without the need to extend the class. Simply instantiate any additional components that you would like included in the zipline, and add them to the zipline before invoking simulate. -""" -import mock -import pytz - -from datetime import datetime, timedelta -from collections import defaultdict - -from nose.tools import timed - -import zipline.test.factory as factory -import zipline.util as qutil -import zipline.finance.risk as risk -import zipline.protocol as zp -import zipline.finance.performance as perf -import zipline.messaging as zmsg - -from zipline.test.client import TestAlgorithm -from zipline.sources import SpecificEquityTrades -from zipline.finance.trading import TransactionSimulator, OrderDataSource, \ -TradeSimulationClient -from zipline.simulator import AddressAllocator, Simulator -from zipline.monitor import Controller - - - -class SimulatedTrading(object): - """ - Zipline with:: - - _no_ data sources. - - Trade simulation client, which is available to send callbacks on - events and also accept orders to be simulated. - - An order data source, which will receive orders from the trade - simulation client, and feed them into the event stream to be - serialized and order alongside all other data source events. - - transaction simulation transformation, which receives the order - events and estimates a theoretical execution price and volume. - - All components in this zipline are subject to heartbeat checks and - a control monitor, which can kill the entire zipline in the event of - exceptions in one of the components or an external request to end the - simulation. Here is a diagram of the SimulatedTrading zipline: @@ -107,6 +66,49 @@ class SimulatedTrading(object): | | | | +---------------------------------+ + +""" + +import mock +import pytz + +from datetime import datetime, timedelta +from collections import defaultdict + +from nose.tools import timed + +import zipline.test.factory as factory +import zipline.util as qutil +import zipline.finance.risk as risk +import zipline.protocol as zp +import zipline.finance.performance as perf +import zipline.messaging as zmsg + +from zipline.test.client import TestAlgorithm +from zipline.sources import SpecificEquityTrades +from zipline.finance.trading import TransactionSimulator, OrderDataSource, \ +TradeSimulationClient +from zipline.simulator import AddressAllocator, Simulator +from zipline.monitor import Controller + + + +class SimulatedTrading(object): + """ + Zipline with:: + - _no_ data sources. + - Trade simulation client, which is available to send callbacks on + events and also accept orders to be simulated. + - An order data source, which will receive orders from the trade + simulation client, and feed them into the event stream to be + serialized and order alongside all other data source events. + - transaction simulation transformation, which receives the order + events and estimates a theoretical execution price and volume. + + All components in this zipline are subject to heartbeat checks and + a control monitor, which can kill the entire zipline in the event of + exceptions in one of the components or an external request to end the + simulation. """ def __init__(self, algorithm, trading_environment, allocator): @@ -167,8 +169,16 @@ class SimulatedTrading(object): self.sim.on_done = self.shutdown() self.started = False + ################################################################## + #TODO: the next three lines of code need refactoring from RealDiehl + ################################################################## + #wire up a callback inside the algorithm to receive frames from the + #trading client self.trading_client.add_event_callback(self.algorithm.handle_frame) + #register the trading_client's order method with the algorithm self.algorithm.set_order(self.trading_client.order) + #register the algorithm to signal order's are done + self.algorithm.set_done(self.trading_client.signal_order_done) def add_source(self, source): assert isinstance(source, zmsg.DataSource) diff --git a/zipline/test/client.py b/zipline/test/client.py index 7ec121c9..7cad8a6f 100644 --- a/zipline/test/client.py +++ b/zipline/test/client.py @@ -87,15 +87,21 @@ class TestClient(qmsg.Component): class TestAlgorithm(): - def __init__(self, sid, amount, order_count, trading_client): - self.trading_client = trading_client - self.trading_client.add_event_callback(self.handle_frame) + def __init__(self, sid, amount, order_count): self.count = order_count self.sid = sid self.amount = amount self.incr = 0 self.done = False + self.order = None + self.on_done = None + + def set_order(self, order_callable): + self.order = order_callable + def set_done(self, done_callable): + self.on_done = done_callable + def handle_frame(self, frame): for dt, s in frame.iteritems(): data = {} @@ -103,8 +109,9 @@ class TestAlgorithm(): event = zp.namedict(data) #place an order for 100 shares of sid:133 if self.incr < self.count: - self.trading_client.order(self.sid, self.amount) + self.order(self.sid, self.amount) self.incr += 1 elif not self.done: - self.trading_client.signal_order_done() + if self.on_done: + self.on_done() self.done = True diff --git a/zipline/test/test_finance.py b/zipline/test/test_finance.py index 683c6910..a7273eae 100644 --- a/zipline/test/test_finance.py +++ b/zipline/test/test_finance.py @@ -17,7 +17,7 @@ import zipline.finance.performance as perf from zipline.test.client import TestAlgorithm from zipline.sources import SpecificEquityTrades from zipline.finance.trading import TransactionSimulator, OrderDataSource, \ -TradeSimulationClient +TradeSimulationClient, TradingEnvironment from zipline.simulator import AddressAllocator, Simulator from zipline.monitor import Controller from zipline.lines import SimulatedTrading @@ -37,7 +37,7 @@ class FinanceTestCase(TestCase): start = datetime.strptime("01/1/2006","%m/%d/%Y") start = start.replace(tzinfo=pytz.utc) - self.trading_environment = risk.TradingEnvironment( + self.trading_environment = TradingEnvironment( self.benchmark_returns, self.treasury_curves, period_start = start, @@ -164,23 +164,25 @@ class FinanceTestCase(TestCase): self.trading_environment ) - # Simulation - # ---------- - zipline = SimulatedTrading( - self.trading_environment, - self.allocator - ) - zipline.add_source(trade_source) - + # Create the Algo + #------------------- order_amount = 100 order_count = 10 test_algo = TestAlgorithm( SID, order_amount, - order_count, - zipline.trading_client + order_count + ) + + # Simulation + # ---------- + zipline = SimulatedTrading( + test_algo, + self.trading_environment, + self.allocator ) + zipline.add_source(trade_source) zipline.simulate(blocking=True) self.assertTrue(zipline.sim.ready()) @@ -205,24 +207,27 @@ class FinanceTestCase(TestCase): trade_count, self.trading_environment ) - - # Simulation - # ---------- - zipline = SimulatedTrading( - self.trading_environment, - self.allocator - ) - zipline.add_source(trade_source) + # Create the Algo + #------------------- order_amount = 100 order_count = 25 test_algo = TestAlgorithm( SID, order_amount, - order_count, - zipline.trading_client + order_count ) + # Simulation + # ---------- + zipline = SimulatedTrading( + test_algo, + self.trading_environment, + self.allocator + ) + + zipline.add_source(trade_source) + zipline.simulate(blocking=True) self.assertEqual( diff --git a/zipline/test/test_perf_tracking.py b/zipline/test/test_perf_tracking.py index 36a99ae8..0038d009 100644 --- a/zipline/test/test_perf_tracking.py +++ b/zipline/test/test_perf_tracking.py @@ -9,7 +9,7 @@ import zipline.util as qutil import zipline.finance.performance as perf import zipline.finance.risk as risk import zipline.protocol as zp -from zipline.finance.trading import TradeSimulationClient +from zipline.finance.trading import TradeSimulationClient, TradingEnvironment class PerformanceTestCase(unittest.TestCase): def setUp(self): @@ -17,7 +17,7 @@ class PerformanceTestCase(unittest.TestCase): self.benchmark_returns, self.treasury_curves = \ factory.load_market_data() - self.trading_environment = risk.TradingEnvironment( + self.trading_environment = TradingEnvironment( self.benchmark_returns, self.treasury_curves ) diff --git a/zipline/test/test_risk.py b/zipline/test/test_risk.py index 57264ad6..e42ae205 100644 --- a/zipline/test/test_risk.py +++ b/zipline/test/test_risk.py @@ -7,6 +7,8 @@ import zipline.finance.risk as risk import zipline.test.factory as factory import zipline.util as qutil +from zipline.finance.trading import TradingEnvironment + class Risk(unittest.TestCase): def setUp(self): @@ -17,7 +19,7 @@ class Risk(unittest.TestCase): self.benchmark_returns, self.treasury_curves = \ factory.load_market_data() - self.trading_calendar = risk.TradingEnvironment( + self.trading_env = TradingEnvironment( self.benchmark_returns, self.treasury_curves ) @@ -27,9 +29,9 @@ class Risk(unittest.TestCase): self.tradingday = datetime.timedelta(hours=6, minutes=30) self.dt = datetime.datetime.utcnow() - self.algo_returns_06 = factory.create_returns_from_list(RETURNS, start_date, self.trading_calendar) + self.algo_returns_06 = factory.create_returns_from_list(RETURNS, start_date, self.trading_env) - self.metrics_06 = risk.RiskReport(self.algo_returns_06, self.trading_calendar) + self.metrics_06 = risk.RiskReport(self.algo_returns_06, self.trading_env) def tearDown(self): return @@ -37,21 +39,21 @@ class Risk(unittest.TestCase): def test_factory(self): returns = [0.1] * 100 start_date = datetime.datetime(year=2006, month=1, day=1, tzinfo=pytz.utc) - r_objects = factory.create_returns_from_list(returns, start_date, self.trading_calendar) + r_objects = factory.create_returns_from_list(returns, start_date, self.trading_env) self.assertTrue(r_objects[-1].date <= datetime.datetime(year=2006, month=12, day=31, tzinfo=pytz.utc)) def test_drawdown(self): start_date = datetime.datetime(year=2006, month=1, day=1) - returns = factory.create_returns_from_list([1.0,-0.5,0.8,.17,1.0,-0.1,-0.45], start_date, self.trading_calendar) + returns = factory.create_returns_from_list([1.0,-0.5,0.8,.17,1.0,-0.1,-0.45], start_date, 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_calendar) + 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): start_date = datetime.datetime(year=2006, month=1, day=1) end_date = datetime.datetime(year=2006, month=12, day=31) - returns = factory.create_returns_from_range(start_date, end_date, self.trading_calendar) - metrics = risk.RiskReport(returns, self.trading_calendar) + returns = factory.create_returns_from_range(start_date, end_date, 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], @@ -63,16 +65,16 @@ class Risk(unittest.TestCase): def test_trading_days_06(self): start_date = datetime.datetime(year=2006, month=1, day=1) end_date = datetime.datetime(year=2006, month=12, day=31) - returns = factory.create_returns_from_range(start_date, end_date, self.trading_calendar) - metrics = risk.RiskReport(returns, self.trading_calendar) + returns = factory.create_returns_from_range(start_date, end_date, 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]) def test_benchmark_volatility_06(self): start_date = datetime.datetime(year=2006, month=1, day=1) end_date = datetime.datetime(year=2006, month=12, day=31) - returns = factory.create_returns_from_range(start_date, end_date, self.trading_calendar) - metrics = risk.RiskReport(returns, self.trading_calendar) + returns = factory.create_returns_from_range(start_date, end_date, 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], @@ -133,8 +135,8 @@ class Risk(unittest.TestCase): def test_benchmark_returns_08(self): start_date = datetime.datetime(year=2008, month=1, day=1) end_date = datetime.datetime(year=2008, month=12, day=31) - returns = factory.create_returns_from_range(start_date, end_date, self.trading_calendar) - metrics = risk.RiskReport(returns, self.trading_calendar) + returns = factory.create_returns_from_range(start_date, end_date, self.trading_env) + metrics = risk.RiskReport(returns, self.trading_env) self.assertEqual([round(x.benchmark_period_returns, 3) for x in metrics.month_periods], [-0.061,-0.035,-0.006,0.048,0.011,-0.086,-0.01,0.012,-0.091,-0.169,-0.075,0.008]) self.assertEqual([round(x.benchmark_period_returns, 3) for x in metrics.three_month_periods], @@ -146,16 +148,16 @@ class Risk(unittest.TestCase): def test_trading_days_08(self): start_date = datetime.datetime(year=2008, month=1, day=1) end_date = datetime.datetime(year=2008, month=12, day=31) - returns = factory.create_returns_from_range(start_date, end_date, self.trading_calendar) - metrics = risk.RiskReport(returns, self.trading_calendar) + returns = factory.create_returns_from_range(start_date, end_date, self.trading_env) + metrics = risk.RiskReport(returns, self.trading_env) self.assertEqual([x.trading_days for x in metrics.year_periods],[253]) self.assertEqual([x.trading_days for x in metrics.month_periods],[21,20,20,22,21,21,22,21,21,23,19,22]) def test_benchmark_volatility_08(self): start_date = datetime.datetime(year=2008, month=1, day=1) end_date = datetime.datetime(year=2008, month=12, day=31) - returns = factory.create_returns_from_range(start_date, end_date, self.trading_calendar) - metrics = risk.RiskReport(returns, self.trading_calendar) + returns = factory.create_returns_from_range(start_date, end_date, self.trading_env) + metrics = risk.RiskReport(returns, self.trading_env) 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], @@ -168,8 +170,8 @@ class Risk(unittest.TestCase): def test_treasury_returns_06(self): start_date = datetime.datetime(year=2006, month=1, day=1) end_date = datetime.datetime(year=2006, month=12, day=31) - returns = factory.create_returns_from_range(start_date, end_date, self.trading_calendar) - metrics = risk.RiskReport(returns, self.trading_calendar) + returns = factory.create_returns_from_range(start_date, end_date, 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], @@ -184,9 +186,9 @@ class Risk(unittest.TestCase): def test_partial_month(self): start_date = datetime.datetime(year=1991, month=1, day=1) - returns = factory.create_returns(365 * 5 + 2, start_date, self.trading_calendar) #1992 and 1996 were leap years + returns = factory.create_returns(365 * 5 + 2, start_date, self.trading_env) #1992 and 1996 were leap years returns = returns[:-10] #truncate the returns series to end mid-month - metrics = risk.RiskReport(returns, self.trading_calendar) + metrics = risk.RiskReport(returns, self.trading_env) total_months = 60 self.check_metrics(metrics, total_months, start_date) @@ -196,8 +198,8 @@ class Risk(unittest.TestCase): else: #because we may catch the leap of the last year, and i think this func is [start,end) ld = calendar.leapdays(start_date.year, start_date.year + years + 1) - returns = factory.create_returns(365 * years + ld, start_date, self.trading_calendar) - metrics = risk.RiskReport(returns, self.trading_calendar) + returns = factory.create_returns(365 * years + ld, start_date, self.trading_env) + metrics = risk.RiskReport(returns, self.trading_env) total_months = years * 12 self.check_metrics(metrics, total_months, start_date)