def transaction_sim(self, **params): """ This is a utility method that asserts expected results for conversion of orders to transactions given a trade history""" trade_count = params['trade_count'] trade_interval = params['trade_interval'] order_count = params['order_count'] order_amount = params['order_amount'] order_interval = params['order_interval'] expected_txn_count = params['expected_txn_count'] expected_txn_volume = params['expected_txn_volume'] # optional parameters # --------------------- # if present, alternate between long and short sales alternate = params.get('alternate') # if present, expect transaction amounts to match orders exactly. complete_fill = params.get('complete_fill') sid = 1 sim_params = factory.create_simulation_parameters() blotter = Blotter() price = [10.1] * trade_count volume = [100] * trade_count start_date = sim_params.first_open generated_trades = factory.create_trade_history( sid, price, volume, trade_interval, sim_params ) if alternate: alternator = -1 else: alternator = 1 order_date = start_date for i in range(order_count): blotter.set_date(order_date) blotter.order(sid, order_amount * alternator ** i, None, None) order_date = order_date + order_interval # move after market orders to just after market next # market open. if order_date.hour >= 21: if order_date.minute >= 00: order_date = order_date + timedelta(days=1) order_date = order_date.replace(hour=14, minute=30) # there should now be one open order list stored under the sid oo = blotter.open_orders self.assertEqual(len(oo), 1) self.assertTrue(sid in oo) order_list = oo[sid] self.assertEqual(order_count, len(order_list)) for i in range(order_count): order = order_list[i] self.assertEqual(order.sid, sid) self.assertEqual(order.amount, order_amount * alternator ** i) tracker = PerformanceTracker(sim_params) benchmark_returns = [ Event({'dt': dt, 'returns': ret, 'type': zipline.protocol.DATASOURCE_TYPE.BENCHMARK, 'source_id': 'benchmarks'}) for dt, ret in trading.environment.benchmark_returns.iterkv() if dt.date() >= sim_params.period_start.date() and dt.date() <= sim_params.period_end.date() ] generated_events = date_sorted_sources(generated_trades, benchmark_returns) # this approximates the loop inside TradingSimulationClient transactions = [] for dt, events in itertools.groupby(generated_events, operator.attrgetter('dt')): for event in events: if event.type == DATASOURCE_TYPE.TRADE: for txn, order in blotter.process_trade(event): transactions.append(txn) tracker.process_event(txn) tracker.process_event(event) if complete_fill: self.assertEqual(len(transactions), len(order_list)) total_volume = 0 for i in range(len(transactions)): txn = transactions[i] total_volume += txn.amount if complete_fill: order = order_list[i] self.assertEqual(order.amount, txn.amount) self.assertEqual(total_volume, expected_txn_volume) self.assertEqual(len(transactions), expected_txn_count) cumulative_pos = tracker.cumulative_performance.positions[sid] self.assertEqual(total_volume, cumulative_pos.amount) # the open orders should now be empty oo = blotter.open_orders self.assertTrue(sid in oo) order_list = oo[sid] self.assertEqual(0, len(order_list))
def transaction_sim(self, **params): """ This is a utility method that asserts expected results for conversion of orders to transactions given a trade history""" trade_count = params['trade_count'] trade_interval = params['trade_interval'] order_count = params['order_count'] order_amount = params['order_amount'] order_interval = params['order_interval'] expected_txn_count = params['expected_txn_count'] expected_txn_volume = params['expected_txn_volume'] # optional parameters # --------------------- # if present, alternate between long and short sales alternate = params.get('alternate') # if present, expect transaction amounts to match orders exactly. complete_fill = params.get('complete_fill') sid = 1 sim_params = factory.create_simulation_parameters() trade_sim = TransactionSimulator() price = [10.1] * trade_count volume = [100] * trade_count start_date = sim_params.first_open generated_trades = factory.create_trade_history( sid, price, volume, trade_interval, sim_params) if alternate: alternator = -1 else: alternator = 1 order_date = start_date for i in xrange(order_count): order = Order( **{ 'sid': sid, 'amount': order_amount * alternator**i, 'dt': order_date }) trade_sim.place_order(order) order_date = order_date + order_interval # move after market orders to just after market next # market open. if order_date.hour >= 21: if order_date.minute >= 00: order_date = order_date + timedelta(days=1) order_date = order_date.replace(hour=14, minute=30) # there should now be one open order list stored under the sid oo = trade_sim.open_orders self.assertEqual(len(oo), 1) self.assertTrue(sid in oo) order_list = oo[sid] self.assertEqual(order_count, len(order_list)) for i in xrange(order_count): order = order_list[i] self.assertEqual(order.sid, sid) self.assertEqual(order.amount, order_amount * alternator**i) tracker = PerformanceTracker(sim_params) # this approximates the loop inside TradingSimulationClient transactions = [] for dt, trades in itertools.groupby(generated_trades, operator.attrgetter('dt')): for trade in trades: trade_sim.update(trade) if trade.TRANSACTION: transactions.append(trade.TRANSACTION) tracker.process_event(trade) if complete_fill: self.assertEqual(len(transactions), len(order_list)) total_volume = 0 for i in xrange(len(transactions)): txn = transactions[i] total_volume += txn.amount if complete_fill: order = order_list[i] self.assertEqual(order.amount, txn.amount) self.assertEqual(total_volume, expected_txn_volume) self.assertEqual(len(transactions), expected_txn_count) cumulative_pos = tracker.cumulative_performance.positions[sid] self.assertEqual(total_volume, cumulative_pos.amount) # the open orders should now be empty oo = trade_sim.open_orders self.assertTrue(sid in oo) order_list = oo[sid] self.assertEqual(0, len(order_list))
class AlgorithmSimulator(object): EMISSION_TO_PERF_KEY_MAP = { 'minute': 'intraday_perf', 'daily': 'daily_perf' } def get_hash(self): """ There should only ever be one TSC in the system, so we don't bother passing args into the hash. """ return self.__class__.__name__ + hash_args() def __init__(self, algo, sim_params): # ============== # Simulation # Param Setup # ============== self.sim_params = sim_params # ============== # Perf Tracker # Setup # ============== self.perf_tracker = PerformanceTracker(self.sim_params) self.perf_key = self.EMISSION_TO_PERF_KEY_MAP[ self.perf_tracker.emission_rate] # ============== # Algo Setup # ============== self.algo = algo self.algo_start = self.sim_params.first_open self.algo_start = self.algo_start.replace(hour=0, minute=0, second=0, microsecond=0) # ============== # Snapshot Setup # ============== # The algorithm's data as of our most recent event. # We want an object that will have empty objects as default # values on missing keys. self.current_data = BarData() # We don't have a datetime for the current snapshot until we # receive a message. self.simulation_dt = None self.snapshot_dt = None # ============= # Logging Setup # ============= # Processor function for injecting the algo_dt into # user prints/logs. def inject_algo_dt(record): if not 'algo_dt' in record.extra: record.extra['algo_dt'] = self.snapshot_dt self.processor = Processor(inject_algo_dt) def transform(self, stream_in): """ Main generator work loop. """ # Set the simulation date to be the first event we see. peek_date, peek_snapshot = next(stream_in) self.simulation_dt = peek_date # Stitch back together the generator by placing the peeked # event back in front stream = itertools.chain([(peek_date, peek_snapshot)], stream_in) # inject the current algo # snapshot time to any log record generated. with self.processor.threadbound(): updated = False bm_updated = False for date, snapshot in stream: self.perf_tracker.set_date(date) self.algo.blotter.set_date(date) # If we're still in the warmup period. Use the event to # update our universe, but don't yield any perf messages, # and don't send a snapshot to handle_data. if date < self.algo_start: for event in snapshot: if event.type in (DATASOURCE_TYPE.TRADE, DATASOURCE_TYPE.CUSTOM): self.update_universe(event) self.perf_tracker.process_event(event) else: for event in snapshot: if event.type in (DATASOURCE_TYPE.TRADE, DATASOURCE_TYPE.CUSTOM): self.update_universe(event) updated = True if event.type == DATASOURCE_TYPE.BENCHMARK: bm_updated = True txns, orders = self.algo.blotter.process_trade(event) for data in chain([event], txns, orders): self.perf_tracker.process_event(data) # Update our portfolio. self.algo.set_portfolio(self.perf_tracker.get_portfolio()) # Send the current state of the universe # to the user's algo. if updated: self.simulate_snapshot(date) updated = False # run orders placed in the algorithm call # above through perf tracker before emitting # the perf packet, so that the perf includes # placed orders for order in self.algo.blotter.new_orders: self.perf_tracker.process_event(order) self.algo.blotter.new_orders = [] # The benchmark is our internal clock. When it # updates, we need to emit a performance message. if bm_updated: bm_updated = False yield self.get_message(date) risk_message = self.perf_tracker.handle_simulation_end() # When emitting minutely, it is still useful to have a final # packet with the entire days performance rolled up. if self.perf_tracker.emission_rate == 'minute': daily_rollup = self.perf_tracker.to_dict( emission_type='daily' ) daily_rollup['daily_perf']['recorded_vars'] = \ self.algo.recorded_vars yield daily_rollup yield risk_message def get_message(self, date): rvars = self.algo.recorded_vars if self.perf_tracker.emission_rate == 'daily': perf_message = \ self.perf_tracker.handle_market_close() perf_message['daily_perf']['recorded_vars'] = rvars return perf_message elif self.perf_tracker.emission_rate == 'minute': self.perf_tracker.handle_minute_close(date) perf_message = self.perf_tracker.to_dict() perf_message['intraday_perf']['recorded_vars'] = rvars return perf_message def update_universe(self, event): """ Update the universe with new event information. """ # Update our knowledge of this event's sid sid_data = self.current_data[event.sid] sid_data.__dict__.update(event.__dict__) def simulate_snapshot(self, date): """ Run the user's algo against our current snapshot and update the algo's simulated time. """ # Needs to be set so that we inject the proper date into algo # log/print lines. self.snapshot_dt = date self.algo.set_datetime(self.snapshot_dt) # Update the simulation time. self.simulation_dt = date self.algo.handle_data(self.current_data)
def transaction_sim(self, **params): """ This is a utility method that asserts expected results for conversion of orders to transactions given a trade history""" trade_count = params['trade_count'] trade_interval = params['trade_interval'] order_count = params['order_count'] order_amount = params['order_amount'] order_interval = params['order_interval'] expected_txn_count = params['expected_txn_count'] expected_txn_volume = params['expected_txn_volume'] # optional parameters # --------------------- # if present, alternate between long and short sales alternate = params.get('alternate') # if present, expect transaction amounts to match orders exactly. complete_fill = params.get('complete_fill') sid = 1 sim_params = factory.create_simulation_parameters() blotter = Blotter() price = [10.1] * trade_count volume = [100] * trade_count start_date = sim_params.first_open generated_trades = factory.create_trade_history( sid, price, volume, trade_interval, sim_params) if alternate: alternator = -1 else: alternator = 1 order_date = start_date for i in xrange(order_count): blotter.set_date(order_date) blotter.order(sid, order_amount * alternator**i, None, None) order_date = order_date + order_interval # move after market orders to just after market next # market open. if order_date.hour >= 21: if order_date.minute >= 00: order_date = order_date + timedelta(days=1) order_date = order_date.replace(hour=14, minute=30) # there should now be one open order list stored under the sid oo = blotter.open_orders self.assertEqual(len(oo), 1) self.assertTrue(sid in oo) order_list = oo[sid] self.assertEqual(order_count, len(order_list)) for i in xrange(order_count): order = order_list[i] self.assertEqual(order.sid, sid) self.assertEqual(order.amount, order_amount * alternator**i) tracker = PerformanceTracker(sim_params) benchmark_returns = [ Event({ 'dt': ret.date, 'returns': ret.returns, 'type': zipline.protocol.DATASOURCE_TYPE.BENCHMARK, 'source_id': 'benchmarks' }) for ret in trading.environment.benchmark_returns if ret.date.date() >= sim_params.period_start.date() and ret.date.date() <= sim_params.period_end.date() ] generated_events = date_sorted_sources(generated_trades, benchmark_returns) # this approximates the loop inside TradingSimulationClient transactions = [] for dt, events in itertools.groupby(generated_events, operator.attrgetter('dt')): for event in events: if event.type == DATASOURCE_TYPE.TRADE: for txn, order in blotter.process_trade(event): transactions.append(txn) tracker.process_event(txn) tracker.process_event(event) if complete_fill: self.assertEqual(len(transactions), len(order_list)) total_volume = 0 for i in xrange(len(transactions)): txn = transactions[i] total_volume += txn.amount if complete_fill: order = order_list[i] self.assertEqual(order.amount, txn.amount) self.assertEqual(total_volume, expected_txn_volume) self.assertEqual(len(transactions), expected_txn_count) cumulative_pos = tracker.cumulative_performance.positions[sid] self.assertEqual(total_volume, cumulative_pos.amount) # the open orders should now be empty oo = blotter.open_orders self.assertTrue(sid in oo) order_list = oo[sid] self.assertEqual(0, len(order_list))
def transaction_sim(self, **params): """ This is a utility method that asserts expected results for conversion of orders to transactions given a trade history""" trade_count = params['trade_count'] trade_interval = params['trade_interval'] trade_delay = params.get('trade_delay') order_count = params['order_count'] order_amount = params['order_amount'] order_interval = params['order_interval'] expected_txn_count = params['expected_txn_count'] expected_txn_volume = params['expected_txn_volume'] # optional parameters # --------------------- # if present, alternate between long and short sales alternate = params.get('alternate') # if present, expect transaction amounts to match orders exactly. complete_fill = params.get('complete_fill') sid = 1 sim_params = factory.create_simulation_parameters() trade_sim = TransactionSimulator() price = [10.1] * trade_count volume = [100] * trade_count start_date = sim_params.first_open generated_trades = factory.create_trade_history( sid, price, volume, trade_interval, sim_params ) if alternate: alternator = -1 else: alternator = 1 order_date = start_date for i in xrange(order_count): order = ndict({ 'sid': sid, 'amount': order_amount * alternator ** i, 'dt': order_date }) trade_sim.place_order(order) order_date = order_date + order_interval # move after market orders to just after market next # market open. if order_date.hour >= 21: if order_date.minute >= 00: order_date = order_date + timedelta(days=1) order_date = order_date.replace(hour=14, minute=30) # there should now be one open order list stored under the sid oo = trade_sim.open_orders self.assertEqual(len(oo), 1) self.assertTrue(sid in oo) order_list = oo[sid] self.assertEqual(order_count, len(order_list)) for i in xrange(order_count): order = order_list[i] self.assertEqual(order.sid, sid) self.assertEqual(order.amount, order_amount * alternator ** i) tracker = PerformanceTracker(sim_params) # this approximates the loop inside TradingSimulationClient transactions = [] for trade in generated_trades: if trade_delay: trade.dt = trade.dt + trade_delay trade_sim.update(trade) if trade.TRANSACTION: transactions.append(trade.TRANSACTION) tracker.process_event(trade) if complete_fill: self.assertEqual(len(transactions), len(order_list)) total_volume = 0 for i in xrange(len(transactions)): txn = transactions[i] total_volume += txn.amount if complete_fill: order = order_list[i] self.assertEqual(order.amount, txn.amount) self.assertEqual(total_volume, expected_txn_volume) self.assertEqual(len(transactions), expected_txn_count) cumulative_pos = tracker.cumulative_performance.positions[sid] self.assertEqual(total_volume, cumulative_pos.amount) # the open orders should now be empty oo = trade_sim.open_orders self.assertTrue(sid in oo) order_list = oo[sid] self.assertEqual(0, len(order_list))