added documentation/todo for callbacks, hopefully simplifying the algorithm classes.

This commit is contained in:
fawce
2012-03-19 14:28:21 -04:00
parent 2324c054e9
commit e7f44884cf
6 changed files with 132 additions and 95 deletions
+14 -1
View File
@@ -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],
[],
+51 -41
View File
@@ -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)
+12 -5
View File
@@ -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
+27 -22
View File
@@ -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(
+2 -2
View File
@@ -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
)
+26 -24
View File
@@ -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)