mirror of
https://github.com/wassname/catalyst.git
synced 2026-06-29 04:42:38 +08:00
added documentation/todo for callbacks, hopefully simplifying the algorithm classes.
This commit is contained in:
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user