Merge branch 'master' of github.com:quantopian/zipline

This commit is contained in:
Thomas Wiecki
2012-04-23 07:21:46 -04:00
11 changed files with 189 additions and 284 deletions
+1 -1
View File
@@ -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
+7 -5
View File
@@ -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
+4
View File
@@ -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
# -----------------------
+85 -56
View File
@@ -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):
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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):
+10 -22
View File
@@ -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
+6 -14
View File
@@ -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)