mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-16 11:18:11 +08:00
Merge branch 'master' of github.com:quantopian/zipline
This commit is contained in:
@@ -4,4 +4,4 @@ gevent-zeromq==0.2.2
|
||||
msgpack-python==0.1.12
|
||||
humanhash==0.0.1
|
||||
ujson==1.18
|
||||
iso8601==0.1.4
|
||||
iso8601==0.1.4
|
||||
|
||||
@@ -86,6 +86,7 @@ class Component(object):
|
||||
self.start_tic = None
|
||||
self.stop_tic = None
|
||||
self.note = None
|
||||
self.confirmed = False
|
||||
|
||||
# Humanhashes make this way easier to debug because they
|
||||
# stick in your mind unlike a 32 byte string of random hex.
|
||||
@@ -235,12 +236,13 @@ class Component(object):
|
||||
"""
|
||||
Send a synchronization request to the host.
|
||||
"""
|
||||
if not self.confirmed:
|
||||
# TODO: proper framing
|
||||
self.sync_socket.send(self.get_id + ":RUN")
|
||||
|
||||
# TODO: proper framing
|
||||
self.sync_socket.send(self.get_id + ":RUN")
|
||||
|
||||
self.receive_sync_ack() # blocking
|
||||
|
||||
self.receive_sync_ack() # blocking
|
||||
self.confirmed = True
|
||||
|
||||
def runtime(self):
|
||||
if self.ready() and self.start_tic and self.stop_tic:
|
||||
return self.stop_tic - self.start_tic
|
||||
|
||||
@@ -55,6 +55,10 @@ def UN_EPOCH(ms_since_epoch):
|
||||
def iso8061_to_epoch(datestring):
|
||||
dt = parse_iso8061(datestring)
|
||||
return EPOCH(dt)
|
||||
|
||||
def epoch_now():
|
||||
dt = datetime.utcnow().replace(tzinfo=pytz.utc)
|
||||
return EPOCH(dt)
|
||||
|
||||
# UTC Datetime Subclasses
|
||||
# -----------------------
|
||||
|
||||
@@ -20,22 +20,6 @@ Performance Tracking
|
||||
+-----------------+----------------------------------------------------+
|
||||
| started_at | datetime in utc marking the start of this test |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| cumulative_capti| The net capital used (positive is spent) through |
|
||||
| al_used | the course of all the events sent to this tracker |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| max_capital_used| The maximum amount of capital deployed through the |
|
||||
| | course of all the events sent to this tracker |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| last_close | The most recent close of the market. datetime in |
|
||||
| | pytz.utc timezone. Will always be 23:59 on the |
|
||||
| | date in UTC. The fact that the time may be on the |
|
||||
| | next day in the exchange's local time is ignored |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| last_open | The most recent open of the market. datetime in |
|
||||
| | pytz.utc timezone. Will always be 00:00 on the |
|
||||
| | date in UTC. The fact that the time may be on the |
|
||||
| | next day in the exchange's local time is ignored |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| capital_base | The initial capital assumed for this tracker. |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| cumulative_perf | A dictionary representing the cumulative |
|
||||
@@ -72,9 +56,6 @@ Position Tracking
|
||||
+-----------------+----------------------------------------------------+
|
||||
| last_sale_price | price at last sale of the security on the exchange |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| transactions | all the transactions that were acrued into this |
|
||||
| | position. |
|
||||
+-----------------+----------------------------------------------------+
|
||||
|
||||
|
||||
Performance Period
|
||||
@@ -106,6 +87,23 @@ Performance Period
|
||||
| returns | percentage returns for the entire portfolio over the |
|
||||
| | period |
|
||||
+---------------+------------------------------------------------------+
|
||||
| cumulative_ | The net capital used (positive is spent) during |
|
||||
| capital_used | the period |
|
||||
+---------------+------------------------------------------------------+
|
||||
| max_capital_ | The maximum amount of capital deployed during the |
|
||||
| used | period. |
|
||||
+---------------+------------------------------------------------------+
|
||||
| max_leverage | The maximum leverage used during the period. |
|
||||
+---------------+------------------------------------------------------+
|
||||
| period_close | The last close of the market in period. datetime in |
|
||||
| | pytz.utc timezone. |
|
||||
+---------------+------------------------------------------------------+
|
||||
| period_open | The first open of the market in period. datetime in |
|
||||
| | pytz.utc timezone. |
|
||||
+---------------+------------------------------------------------------+
|
||||
| transactions | all the transactions that were acrued during this |
|
||||
| | period. Unset/missing for cumulative periods. |
|
||||
+---------------+------------------------------------------------------+
|
||||
|
||||
|
||||
"""
|
||||
@@ -136,10 +134,10 @@ class PerformanceTracker():
|
||||
def __init__(self, trading_environment):
|
||||
|
||||
|
||||
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
|
||||
@@ -164,7 +162,10 @@ class PerformanceTracker():
|
||||
# initial portfolio positions have zero value
|
||||
0,
|
||||
# initial cash is your capital base.
|
||||
starting_cash = self.capital_base
|
||||
self.capital_base,
|
||||
# the cumulative period will be calculated over the entire test.
|
||||
self.period_start,
|
||||
self.period_end
|
||||
)
|
||||
|
||||
# this performance period will span just the current market day
|
||||
@@ -174,7 +175,10 @@ class PerformanceTracker():
|
||||
# initial portfolio positions have zero value
|
||||
0,
|
||||
# initial cash is your capital base.
|
||||
starting_cash = self.capital_base,
|
||||
self.capital_base,
|
||||
# the daily period will be calculated for the market day
|
||||
self.market_open,
|
||||
self.market_close,
|
||||
# save the transactions for the daily periods
|
||||
keep_transactions = True
|
||||
)
|
||||
@@ -206,10 +210,6 @@ class PerformanceTracker():
|
||||
'period_start' : self.period_start,
|
||||
'period_end' : self.period_end,
|
||||
'progress' : self.progress,
|
||||
'cumulative_capital_used' : self.cumulative_performance.cumulative_capital_used,
|
||||
'max_capital_used' : self.cumulative_performance.max_capital_used,
|
||||
'last_close' : self.market_close,
|
||||
'last_open' : self.market_open,
|
||||
'capital_base' : self.capital_base,
|
||||
'cumulative_perf' : self.cumulative_performance.to_dict(),
|
||||
'daily_perf' : self.todays_performance.to_dict(),
|
||||
@@ -283,6 +283,8 @@ class PerformanceTracker():
|
||||
self.todays_performance.positions,
|
||||
self.todays_performance.ending_value,
|
||||
self.todays_performance.ending_cash,
|
||||
self.market_open,
|
||||
self.market_close,
|
||||
keep_transactions = True
|
||||
)
|
||||
|
||||
@@ -369,20 +371,32 @@ class Position():
|
||||
|
||||
class PerformancePeriod():
|
||||
|
||||
def __init__(self, initial_positions, starting_value, starting_cash, keep_transactions=False):
|
||||
self.ending_value = 0.0
|
||||
self.period_capital_used = 0.0
|
||||
self.pnl = 0.0
|
||||
def __init__(
|
||||
self,
|
||||
initial_positions,
|
||||
starting_value,
|
||||
starting_cash,
|
||||
period_open=None,
|
||||
period_close=None,
|
||||
keep_transactions=False):
|
||||
|
||||
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
|
||||
#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.calculate_performance()
|
||||
|
||||
@@ -456,19 +470,30 @@ class PerformancePeriod():
|
||||
positions = self.get_positions_list()
|
||||
transactions = [x.as_dict() for x in self.processed_transactions]
|
||||
|
||||
return {
|
||||
'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,
|
||||
'positions' : positions,
|
||||
'pnl' : self.pnl,
|
||||
'returns' : self.returns,
|
||||
'transactions' : transactions,
|
||||
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,
|
||||
'positions' : positions,
|
||||
'pnl' : self.pnl,
|
||||
'returns' : self.returns,
|
||||
'transactions' : transactions,
|
||||
'period_open' : self.period_open,
|
||||
'period_close' : self.period_close
|
||||
}
|
||||
|
||||
# we want the key to be absent, not just empty
|
||||
if not self.keep_transactions:
|
||||
del(rval['transactions'])
|
||||
|
||||
return rval
|
||||
|
||||
def to_namedict(self):
|
||||
"""
|
||||
Creates a namedict representing the state of this perfomance period.
|
||||
@@ -481,12 +506,16 @@ class PerformancePeriod():
|
||||
positions = zp.namedict(positions)
|
||||
|
||||
return zp.namedict({
|
||||
'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,
|
||||
'positions' : positions
|
||||
'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,
|
||||
'cumulative_capital_used' : self.cumulative_capital_used,
|
||||
'max_capital_used' : self.max_capital_used,
|
||||
'max_leverage' : self.max_leverage,
|
||||
'positions' : positions,
|
||||
'transactions' : self.processed_transactions
|
||||
})
|
||||
|
||||
def get_positions(self, namedicted=False):
|
||||
|
||||
@@ -194,7 +194,7 @@ class RiskMetrics():
|
||||
http://en.wikipedia.org/wiki/Sharpe_ratio
|
||||
"""
|
||||
if self.algorithm_volatility == 0:
|
||||
return None
|
||||
return 0.0
|
||||
|
||||
return ( (self.algorithm_period_returns - self.treasury_period_return) /
|
||||
self.algorithm_volatility )
|
||||
@@ -292,7 +292,7 @@ class RiskMetrics():
|
||||
curve = None
|
||||
# in case end date is not a trading day, search for the next market
|
||||
# day for an interest rate
|
||||
for i in range(7):
|
||||
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]
|
||||
break
|
||||
|
||||
+45
-155
@@ -27,7 +27,7 @@ SIMULATION_STYLE = Enum(
|
||||
|
||||
class TradeSimulationClient(qmsg.Component):
|
||||
|
||||
def __init__(self, trading_environment):
|
||||
def __init__(self, trading_environment, sim_style):
|
||||
qmsg.Component.__init__(self)
|
||||
self.received_count = 0
|
||||
self.prev_dt = None
|
||||
@@ -38,8 +38,9 @@ class TradeSimulationClient(qmsg.Component):
|
||||
self.current_dt = trading_environment.period_start
|
||||
self.last_iteration_dur = datetime.timedelta(seconds=0)
|
||||
self.algorithm = None
|
||||
self.max_wait = datetime.timedelta(seconds=7)
|
||||
self.max_wait = datetime.timedelta(seconds=60)
|
||||
self.last_msg_dt = datetime.datetime.utcnow()
|
||||
self.txn_sim = TransactionSimulator(sim_style)
|
||||
|
||||
assert self.trading_environment.frame_index != None
|
||||
self.event_frame = pandas.DataFrame(
|
||||
@@ -63,12 +64,8 @@ class TradeSimulationClient(qmsg.Component):
|
||||
|
||||
def open(self):
|
||||
self.result_feed = self.connect_result()
|
||||
self.order_socket = self.connect_order()
|
||||
# send a wake up call to the order data source.
|
||||
self.order_socket.send(str(zp.ORDER_PROTOCOL.BREAK))
|
||||
|
||||
def do_work(self):
|
||||
|
||||
# poll all the sockets
|
||||
socks = dict(self.poll.poll(self.heartbeat_timeout))
|
||||
|
||||
@@ -99,54 +96,49 @@ class TradeSimulationClient(qmsg.Component):
|
||||
# update performance and relay the event to the algorithm
|
||||
self.process_event(event)
|
||||
|
||||
# signal loop is done for order source.
|
||||
self.order_socket.send(str(zp.ORDER_PROTOCOL.BREAK))
|
||||
else:
|
||||
# no events in the sock means the non-order sources are
|
||||
# drained. Signal the order_source that we're done, and
|
||||
# the done will cascade through the whole zipline.
|
||||
# shutdown the feedback loop to the OrderDataSource
|
||||
wait_time = datetime.datetime.utcnow() - self.last_msg_dt
|
||||
if wait_time > self.max_wait:
|
||||
self.signal_order_done()
|
||||
|
||||
|
||||
def process_event(self, event):
|
||||
# track the number of transactions, for testing purposes.
|
||||
if(event.TRANSACTION != None):
|
||||
|
||||
# generate transactions, if applicable
|
||||
txn = self.txn_sim.apply_trade_to_open_orders(event)
|
||||
if txn:
|
||||
event.TRANSACTION = txn
|
||||
# track the number of transactions, for testing purposes.
|
||||
self.txn_count += 1
|
||||
else:
|
||||
event.TRANSACTION = None
|
||||
|
||||
# the performance class needs to process each event, without
|
||||
# skipping. Algorithm should wait until the performance has been
|
||||
# updated, so that down stream components can safely assume that
|
||||
# performance is up to date. Note that this is done before we
|
||||
# mark the time for the algorithm's processing, thereby not
|
||||
# running the algo's clock for performance book keeping.
|
||||
self.perf.process_event(event)
|
||||
|
||||
#filter order flow out of the events sent to callbacks
|
||||
if event.source_id != zp.FINANCE_COMPONENT.ORDER_SOURCE:
|
||||
|
||||
# the performance class needs to process each event, without
|
||||
# skipping. Algorithm should wait until the performance has been
|
||||
# updated, so that down stream components can safely assume that
|
||||
# performance is up to date. Note that this is done before we
|
||||
# mark the time for the algorithm's processing, thereby not
|
||||
# running the algo's clock for performance book keeping.
|
||||
self.perf.process_event(event)
|
||||
|
||||
# mark the start time for client's processing of this event.
|
||||
event_start = datetime.datetime.utcnow()
|
||||
|
||||
# queue the event.
|
||||
self.queue_event(event)
|
||||
|
||||
# if the event is later than our current time, run the algo
|
||||
# otherwise, the algorithm has fallen behind the feed
|
||||
# and processing per event is longer than time between events.
|
||||
if event.dt >= self.current_dt:
|
||||
# compress time by moving the current_time up to the event
|
||||
# time.
|
||||
self.current_dt = event.dt
|
||||
self.run_algorithm()
|
||||
|
||||
# tally the time spent on this iteration
|
||||
self.last_iteration_dur = datetime.datetime.utcnow() - event_start
|
||||
# move the algorithm's clock forward to include iteration time
|
||||
self.current_dt = self.current_dt + self.last_iteration_dur
|
||||
# mark the start time for client's processing of this event.
|
||||
event_start = datetime.datetime.utcnow()
|
||||
|
||||
|
||||
# queue the event.
|
||||
self.queue_event(event)
|
||||
|
||||
|
||||
# if the event is later than our current time, run the algo
|
||||
# otherwise, the algorithm has fallen behind the feed
|
||||
# and processing per event is longer than time between events.
|
||||
if event.dt >= self.current_dt:
|
||||
# compress time by moving the current_time up to the event
|
||||
# time.
|
||||
self.current_dt = event.dt
|
||||
self.run_algorithm()
|
||||
|
||||
# tally the time spent on this iteration
|
||||
self.last_iteration_dur = datetime.datetime.utcnow() - event_start
|
||||
# move the algorithm's clock forward to include iteration time
|
||||
self.current_dt = self.current_dt + self.last_iteration_dur
|
||||
|
||||
|
||||
def run_algorithm(self):
|
||||
"""
|
||||
As per the algorithm protocol:
|
||||
@@ -164,15 +156,14 @@ class TradeSimulationClient(qmsg.Component):
|
||||
return self.connect_push_socket(self.addresses['order_address'])
|
||||
|
||||
def order(self, sid, amount):
|
||||
|
||||
order = zp.namedict({
|
||||
'dt':self.current_dt,
|
||||
'sid':sid,
|
||||
'amount':amount
|
||||
})
|
||||
self.order_socket.send(zp.ORDER_FRAME(order))
|
||||
self.order_count += 1
|
||||
self.perf.log_order(order)
|
||||
self.txn_sim.add_open_order(order)
|
||||
|
||||
def signal_order_done(self):
|
||||
self.order_socket.send(str(zp.ORDER_PROTOCOL.DONE))
|
||||
@@ -188,89 +179,11 @@ class TradeSimulationClient(qmsg.Component):
|
||||
self.event_frame[event['sid']] = event
|
||||
self.event_queue = []
|
||||
return self.event_frame
|
||||
|
||||
class OrderDataSource(qmsg.DataSource):
|
||||
"""DataSource that relays orders from the client"""
|
||||
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
:param simulation_time: datetime in UTC timezone, sets the start
|
||||
time of simulation. orders
|
||||
will be timestamped relative to this datetime.
|
||||
event = {
|
||||
'sid' : an integer for security id,
|
||||
'dt' : datetime object,
|
||||
'price' : float for price,
|
||||
'volume' : integer for volume
|
||||
}
|
||||
"""
|
||||
qmsg.DataSource.__init__(self, zp.FINANCE_COMPONENT.ORDER_SOURCE)
|
||||
self.sent_count = 0
|
||||
self.recv_count = Counter()
|
||||
self.works = 0
|
||||
|
||||
@property
|
||||
def get_type(self):
|
||||
return zp.DATASOURCE_TYPE.ORDER
|
||||
|
||||
def open(self):
|
||||
qmsg.DataSource.open(self)
|
||||
self.order_socket = self.bind_order()
|
||||
|
||||
def bind_order(self):
|
||||
return self.bind_pull_socket(self.addresses['order_address'])
|
||||
|
||||
def do_work(self):
|
||||
|
||||
self.works += 1
|
||||
|
||||
#pull all orders from client.
|
||||
count = 0
|
||||
|
||||
# one iteration of the client could include several orders
|
||||
# so iterate until the client signals a break or a close.
|
||||
while True:
|
||||
# poll all the sockets
|
||||
# we reduce the timeout here by a factor of 2, because we need
|
||||
# to potentially receive the client's done message before the
|
||||
# controller or heartbeat times out.
|
||||
|
||||
# this will block for timeout/2, and return an empty dict if there
|
||||
# are no messages.
|
||||
socks = dict(self.poll.poll(self.heartbeat_timeout/2))
|
||||
|
||||
# see if the poller has results for the result_feed
|
||||
if self.order_socket in socks and \
|
||||
socks[self.order_socket] == self.zmq.POLLIN:
|
||||
|
||||
order_msg = self.order_socket.recv()
|
||||
|
||||
if order_msg == str(zp.ORDER_PROTOCOL.DONE):
|
||||
qutil.LOGGER.info("order source is done")
|
||||
self.signal_done()
|
||||
self.recv_count['done'] += 1
|
||||
return
|
||||
|
||||
if order_msg == str(zp.ORDER_PROTOCOL.BREAK):
|
||||
# send a blank message to avoid an empty buffer
|
||||
# in the feed
|
||||
self.recv_count['break'] += 1
|
||||
if count == 0:
|
||||
self.send(namedict({}))
|
||||
break
|
||||
|
||||
order = zp.ORDER_UNFRAME(order_msg)
|
||||
self.recv_count['order'] += 1
|
||||
#send the order along
|
||||
self.send(order)
|
||||
count += 1
|
||||
self.sent_count += 1
|
||||
|
||||
|
||||
class TransactionSimulator(qmsg.BaseTransform):
|
||||
class TransactionSimulator(object):
|
||||
|
||||
def __init__(self, style=SIMULATION_STYLE.PARTIAL_VOLUME):
|
||||
qmsg.BaseTransform.__init__(self, zp.TRANSFORM_TYPE.TRANSACTION)
|
||||
self.open_orders = {}
|
||||
self.order_count = 0
|
||||
self.txn_count = 0
|
||||
@@ -287,27 +200,6 @@ class TransactionSimulator(qmsg.BaseTransform):
|
||||
elif style == SIMULATION_STYLE.NOOP:
|
||||
self.apply_trade_to_open_orders = self.simulate_noop
|
||||
|
||||
def transform(self, event):
|
||||
"""
|
||||
Pulls one message from the event feed, then
|
||||
loops on orders until client sends DONE message.
|
||||
"""
|
||||
if(event.type == zp.DATASOURCE_TYPE.ORDER):
|
||||
self.add_open_order(event)
|
||||
self.state['value'] = None
|
||||
elif(event.type == zp.DATASOURCE_TYPE.TRADE):
|
||||
txn = self.apply_trade_to_open_orders(event)
|
||||
self.state['value'] = txn
|
||||
else:
|
||||
self.state['value'] = None
|
||||
log = "unexpected event type in transform: {etype}".format(
|
||||
etype=event.type
|
||||
)
|
||||
qutil.LOGGER.info(log)
|
||||
|
||||
#TODO: what to do if we get another kind of datasource event.type?
|
||||
return self.state
|
||||
|
||||
def add_open_order(self, event):
|
||||
"""Orders are captured in a buffer by sid. No calculations are done here.
|
||||
Amount is explicitly converted to an int.
|
||||
@@ -322,8 +214,6 @@ class TransactionSimulator(qmsg.BaseTransform):
|
||||
)
|
||||
qutil.LOGGER.debug(log)
|
||||
return
|
||||
|
||||
|
||||
|
||||
if(not self.open_orders.has_key(event.sid)):
|
||||
self.open_orders[event.sid] = []
|
||||
@@ -435,7 +325,7 @@ class TransactionSimulator(qmsg.BaseTransform):
|
||||
dt.replace(tzinfo = pytz.utc),
|
||||
direction
|
||||
)
|
||||
else:
|
||||
elif len(orders) > 0:
|
||||
warning = """
|
||||
Calculated a zero volume transaction on trade:
|
||||
{event}
|
||||
|
||||
+9
-7
@@ -86,8 +86,7 @@ import zipline.messaging as zmsg
|
||||
|
||||
from zipline.test.algorithms import TestAlgorithm
|
||||
from zipline.sources import SpecificEquityTrades
|
||||
from zipline.finance.trading import TransactionSimulator, OrderDataSource, \
|
||||
TradeSimulationClient
|
||||
from zipline.finance.trading import TradeSimulationClient
|
||||
from zipline.simulator import AddressAllocator, Simulator
|
||||
from zipline.monitor import Controller
|
||||
from zipline.finance.trading import SIMULATION_STYLE
|
||||
@@ -164,18 +163,21 @@ class SimulatedTrading(object):
|
||||
self.sim = config['simulator_class'](addresses)
|
||||
|
||||
self.clients = {}
|
||||
self.trading_client = TradeSimulationClient(self.trading_environment)
|
||||
self.trading_client = TradeSimulationClient(
|
||||
self.trading_environment,
|
||||
self.sim_style
|
||||
)
|
||||
self.add_client(self.trading_client)
|
||||
|
||||
# setup all sources
|
||||
self.sources = {}
|
||||
self.order_source = OrderDataSource()
|
||||
self.add_source(self.order_source)
|
||||
#self.order_source = OrderDataSource()
|
||||
#self.add_source(self.order_source)
|
||||
|
||||
#setup transforms
|
||||
self.transaction_sim = TransactionSimulator(self.sim_style)
|
||||
#self.transaction_sim = TransactionSimulator(self.sim_style)
|
||||
self.transforms = {}
|
||||
self.add_transform(self.transaction_sim)
|
||||
#self.add_transform(self.transaction_sim)
|
||||
|
||||
self.sim.register_controller( self.con )
|
||||
self.sim.on_done = self.shutdown()
|
||||
|
||||
+7
-14
@@ -110,7 +110,7 @@ class ComponentHost(Component):
|
||||
self.launch_component(component)
|
||||
self.launch_controller()
|
||||
|
||||
def is_timed_out(self):
|
||||
def is_running(self):
|
||||
"""
|
||||
DEPRECATED, left in for compatability for now.
|
||||
"""
|
||||
@@ -119,23 +119,16 @@ class ComponentHost(Component):
|
||||
|
||||
if len(self.components) == 0:
|
||||
qutil.LOGGER.info("Component register is empty.")
|
||||
return True
|
||||
return False
|
||||
|
||||
for source, last_dt in self.sync_register.iteritems():
|
||||
if (cur_time - last_dt) > self.timeout:
|
||||
qutil.LOGGER.info(
|
||||
"Time out for {source}. Current component registery: {reg}".
|
||||
format(source=source, reg=self.components)
|
||||
)
|
||||
return True
|
||||
|
||||
return False
|
||||
return True
|
||||
|
||||
def loop(self, lockstep=True):
|
||||
|
||||
while not self.is_timed_out():
|
||||
# wait for synchronization request
|
||||
socks = dict(self.sync_poller.poll(self.heartbeat_timeout)) #timeout after 2 seconds.
|
||||
while self.is_running():
|
||||
# wait for synchronization request at start, and DONE at end.
|
||||
# don't timeout.
|
||||
socks = dict(self.sync_poller.poll())
|
||||
|
||||
if self.sync_socket in socks and socks[self.sync_socket] == self.zmq.POLLIN:
|
||||
msg = self.sync_socket.recv()
|
||||
|
||||
+13
-8
@@ -628,8 +628,6 @@ def PERF_FRAME(perf):
|
||||
assert isinstance(perf['started_at'], datetime.datetime)
|
||||
assert isinstance(perf['period_start'], datetime.datetime)
|
||||
assert isinstance(perf['period_end'], datetime.datetime)
|
||||
assert isinstance(perf['last_close'], datetime.datetime)
|
||||
assert isinstance(perf['last_open'], datetime.datetime)
|
||||
|
||||
assert isinstance(perf['daily_perf'], dict)
|
||||
assert isinstance(perf['cumulative_perf'], dict)
|
||||
@@ -638,19 +636,26 @@ def PERF_FRAME(perf):
|
||||
cp = perf['cumulative_perf']
|
||||
|
||||
assert isinstance(tp['transactions'], list)
|
||||
assert isinstance(cp['transactions'], list)
|
||||
# we never want to send transactions for the cumulative period.
|
||||
# performance.py should never send them, but just to be safe:
|
||||
assert not cp.has_key('transactions')
|
||||
assert isinstance(tp['positions'], list)
|
||||
assert isinstance(cp['positions'], list)
|
||||
assert isinstance(tp['period_close'], datetime.datetime)
|
||||
assert isinstance(tp['period_open'], datetime.datetime)
|
||||
assert isinstance(cp['period_close'], datetime.datetime)
|
||||
assert isinstance(cp['period_open'], datetime.datetime)
|
||||
|
||||
perf['started_at'] = EPOCH(perf['started_at'])
|
||||
perf['period_start'] = EPOCH(perf['period_start'])
|
||||
perf['period_end'] = EPOCH(perf['period_end'])
|
||||
perf['last_close'] = EPOCH(perf['last_close'])
|
||||
perf['last_open'] = EPOCH(perf['last_open'])
|
||||
tp['period_close'] = EPOCH(tp['period_close'])
|
||||
tp['period_open'] = EPOCH(tp['period_open'])
|
||||
cp['period_close'] = EPOCH(cp['period_close'])
|
||||
cp['period_open'] = EPOCH(cp['period_open'])
|
||||
|
||||
tp['transactions'] = convert_transactions(tp['transactions'])
|
||||
cp['transactions'] = convert_transactions(cp['transactions'])
|
||||
|
||||
tp['transactions'] = convert_transactions(tp['transactions'])
|
||||
|
||||
return BT_UPDATE_FRAME('PERF', perf)
|
||||
|
||||
def convert_transactions(transactions):
|
||||
|
||||
@@ -16,7 +16,7 @@ import zipline.finance.performance as perf
|
||||
|
||||
from zipline.test.algorithms import TestAlgorithm
|
||||
from zipline.sources import SpecificEquityTrades
|
||||
from zipline.finance.trading import TransactionSimulator, OrderDataSource, \
|
||||
from zipline.finance.trading import TransactionSimulator, \
|
||||
TradeSimulationClient, TradingEnvironment
|
||||
from zipline.simulator import AddressAllocator, Simulator
|
||||
from zipline.monitor import Controller
|
||||
@@ -214,14 +214,8 @@ class FinanceTestCase(TestCase):
|
||||
zipline.algorithm.incr,
|
||||
"The test algorithm should send as many orders as specified.")
|
||||
|
||||
order_source = zipline.sources[zp.FINANCE_COMPONENT.ORDER_SOURCE]
|
||||
self.assertEqual(
|
||||
order_source.sent_count,
|
||||
zipline.algorithm.count,
|
||||
"The order source should have sent as many orders as the algo."
|
||||
)
|
||||
|
||||
transaction_sim = zipline.transforms[zp.TRANSFORM_TYPE.TRANSACTION]
|
||||
transaction_sim = zipline.trading_client.txn_sim
|
||||
self.assertEqual(
|
||||
transaction_sim.txn_count,
|
||||
zipline.trading_client.perf.txn_count,
|
||||
@@ -426,11 +420,7 @@ class FinanceTestCase(TestCase):
|
||||
'dt' : start_date + i * order_interval
|
||||
})
|
||||
|
||||
sim_state = trade_sim.transform(order)
|
||||
|
||||
# there should not be a new transaction from an order.
|
||||
self.assertTrue(sim_state['name'] == trade_sim.get_id)
|
||||
self.assertTrue(sim_state['value'] == None)
|
||||
trade_sim.add_open_order(order)
|
||||
|
||||
# there should now be one open order list stored under the sid
|
||||
oo = trade_sim.open_orders
|
||||
@@ -446,21 +436,19 @@ class FinanceTestCase(TestCase):
|
||||
|
||||
tracker = PerformanceTracker(trading_environment)
|
||||
|
||||
# this approximates the loop inside TradingSimulationClient
|
||||
transactions = []
|
||||
for trade in generated_trades:
|
||||
if trade_delay:
|
||||
trade.dt = trade.dt + trade_delay
|
||||
|
||||
sim_state = trade_sim.transform(trade)
|
||||
|
||||
self.assertEqual(sim_state['name'], trade_sim.get_id)
|
||||
|
||||
txn = None
|
||||
if sim_state['value']:
|
||||
txn = sim_state['value']
|
||||
txn = trade_sim.apply_trade_to_open_orders(trade)
|
||||
if txn:
|
||||
transactions.append(txn)
|
||||
trade[sim_state['name']] = txn
|
||||
|
||||
trade.TRANSACTION = txn
|
||||
else:
|
||||
trade.TRANSACTION = None
|
||||
|
||||
tracker.process_event(trade)
|
||||
|
||||
total_volume = 0
|
||||
|
||||
@@ -10,7 +10,8 @@ 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, TradingEnvironment
|
||||
from zipline.finance.trading import TradeSimulationClient, TradingEnvironment, \
|
||||
SIMULATION_STYLE
|
||||
class PerformanceTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
@@ -539,11 +540,7 @@ shares in position"
|
||||
self.trading_environment.capital_base = 1000.0
|
||||
self.trading_environment.frame_index = ['sid', 'volume', 'dt', \
|
||||
'price', 'changed']
|
||||
client = TradeSimulationClient(self.trading_environment)
|
||||
# the client expects an algorithm that fullfills the algorithm
|
||||
# protocol, so we use the noop algorithm.
|
||||
test_algo = zipline.test.algorithms.NoopAlgorithm()
|
||||
client.set_algorithm(test_algo)
|
||||
perf_tracker = perf.PerformanceTracker(self.trading_environment)
|
||||
|
||||
for event in trade_history:
|
||||
#create a transaction for all but
|
||||
@@ -559,18 +556,13 @@ shares in position"
|
||||
else:
|
||||
txn = None
|
||||
event[zp.TRANSFORM_TYPE.TRANSACTION] = txn
|
||||
client.process_event(event)
|
||||
|
||||
df = client.get_frame()
|
||||
|
||||
self.assertEqual(df[133]['price'], price)
|
||||
self.assertEqual(df[134]['price'], price2)
|
||||
perf_tracker.process_event(event)
|
||||
|
||||
#we skip two trades, to test case of None transaction
|
||||
txn_count = len(trade_history) - 2
|
||||
self.assertEqual(client.perf.txn_count, txn_count)
|
||||
self.assertEqual(perf_tracker.txn_count, txn_count)
|
||||
|
||||
cumulative_pos = client.perf.cumulative_performance.positions[sid]
|
||||
cumulative_pos = perf_tracker.cumulative_performance.positions[sid]
|
||||
expected_size = txn_count / 2 * -25
|
||||
self.assertEqual(cumulative_pos.amount, expected_size)
|
||||
|
||||
Reference in New Issue
Block a user