def test_volume_share_slippage(self): event = ndict({ 'volume': 200, 'TRANSACTION': None, 'type': 4, 'price': 3.0, 'datetime': datetime.datetime(2006, 1, 5, 14, 31, tzinfo=pytz.utc), 'high': 3.15, 'low': 2.85, 'sid': 133, 'source_id': 'test_source', 'close': 3.0, 'dt': datetime.datetime(2006, 1, 5, 14, 31, tzinfo=pytz.utc), 'open': 3.0 }) slippage_model = VolumeShareSlippage() open_orders = { 133: [ ndict({ 'dt': datetime.datetime(2006, 1, 5, 14, 30, tzinfo=pytz.utc), 'amount': 100, 'filled': 0, 'sid': 133 }) ] } txn = slippage_model.simulate(event, open_orders) expected_txn = { 'price': float(3.01875), 'dt': datetime.datetime(2006, 1, 5, 14, 31, tzinfo=pytz.utc), 'amount': int(50), 'sid': int(133) } self.assertIsNotNone(txn) for key, value in expected_txn.items(): self.assertEquals(value, txn[key])
def order(self, sid, amount): """ Closure to pass into the user's algo to allow placing orders into the transaction simulator's dict of open orders. """ order = ndict({ 'dt': self.simulation_dt, 'sid': sid, 'amount': int(amount), 'filled': 0 }) # Tell the user if they try to buy 0 shares of something. if order.amount == 0: zero_message = "Requested to trade zero shares of {sid}".format( sid=order.sid) log.debug(zero_message) # Don't bother placing orders for 0 shares. return # Add non-zero orders to the order book. # !!!IMPORTANT SIDE-EFFECT!!! # This modifies the internal state of the transaction # simulator so that it can fill the placed order when it # receives its next message. self.order_book.place_order(order)
def order(self, sid, amount): """ Closure to pass into the user's algo to allow placing orders into the transaction simulator's dict of open orders. """ order = ndict({ 'dt': self.simulation_dt, 'sid': sid, 'amount': int(amount), 'filled': 0 }) # Tell the user if they try to buy 0 shares of something. if order.amount == 0: zero_message = "Requested to trade zero shares of {sid}".format( sid=order.sid ) log.debug(zero_message) # Don't bother placing orders for 0 shares. return # Add non-zero orders to the order book. # !!!IMPORTANT SIDE-EFFECT!!! # This modifies the internal state of the transaction # simulator so that it can fill the placed order when it # receives its next message. self.order_book.place_order(order)
def mock_done(id): return ndict({ 'dt': "DONE", "source_id": id, 'tnfm_id': id, 'tnfm_value': None, 'type': DATASOURCE_TYPE.DONE })
def get_stddevs(self): """ Return an ndict of all our tracked standard deviations. """ out = ndict() for field in self.fields: out[field] = self.stdev(field) return out
def get_averages(self): """ Return an ndict of all our tracked averages. """ out = ndict() for field in self.fields: out[field] = self.average(field) return out
def _gen(self, stream_in): # IMPORTANT: Messages may contain pointers that are shared with # other streams. Transforms that modify their input # messages should only manipulate copies. log.info('Running StatefulTransform [%s]' % self.get_hash()) for message in stream_in: # allow upstream generators to yield None to avoid # blocking. if message is None: continue assert_sort_unframe_protocol(message) # This flag is set by by merged_transforms to ensure # isolation of messages. if self.merged: message = deepcopy(message) tnfm_value = self.state.update(message) # PASSTHROUGH flag means we want to keep all original # values, plus append tnfm_id and tnfm_value. Used for # preserving the original event fields when our output # will be fed into a merge. Currently only Passthrough # uses this flag. if self.passthrough and self.merged: out_message = message out_message.tnfm_id = self.namestring out_message.tnfm_value = tnfm_value yield out_message # If the merged flag is set, we create a new message # containing just the tnfm_id, the event's datetime, and # the calculated tnfm_value. This is the default behavior # for a non-passthrough transform being fed into a merge. elif self.merged: out_message = ndict() out_message.tnfm_id = self.namestring out_message.tnfm_value = tnfm_value out_message.dt = message.dt yield out_message # Sequential flag should be used to add a single new # key-value pair to the event. The new key is this # transform's namestring, and its value is the value # returned by state.update(event). This is almost # identical to the behavior of FORWARDER, except we # compress the two calculated values (tnfm_id, and # tnfm_value) into a single field. This mode is used by # the sequential_transforms composite and is the default # if no behavior is specified by the internal state class. elif self.sequential: out_message = message out_message[self.namestring] = tnfm_value yield out_message log.info('Finished StatefulTransform [%s]' % self.get_hash())
def test_volume_share_slippage(self): event = ndict( {'volume': 200, 'TRANSACTION': None, 'type': 4, 'price': 3.0, 'datetime': datetime.datetime( 2006, 1, 5, 14, 31, tzinfo=pytz.utc), 'high': 3.15, 'low': 2.85, 'sid': 133, 'source_id': 'test_source', 'close': 3.0, 'dt': datetime.datetime(2006, 1, 5, 14, 31, tzinfo=pytz.utc), 'open': 3.0} ) slippage_model = VolumeShareSlippage() open_orders = {133: [ ndict( {'dt': datetime.datetime(2006, 1, 5, 14, 30, tzinfo=pytz.utc), 'amount': 100, 'filled': 0, 'sid': 133}) ]} txn = slippage_model.simulate( event, open_orders ) expected_txn = { 'price': float(3.01875), 'dt': datetime.datetime( 2006, 1, 5, 14, 31, tzinfo=pytz.utc), 'amount': int(50), 'sid': int(133) } self.assertIsNotNone(txn) for key, value in expected_txn.items(): self.assertEquals(value, txn[key])
def __init__(self, blotter, perf_tracker, algo, algo_start): # ========== # Algo Setup # ========== # We extract the order book from the txn client so that # the algo can place new orders. self.blotter = blotter self.perf_tracker = perf_tracker self.perf_key = self.EMISSION_TO_PERF_KEY_MAP[ perf_tracker.emission_rate] self.algo = algo self.algo_start = algo_start.replace(hour=0, minute=0, second=0, microsecond=0) # Monkey patch the user algorithm to place orders in the # TransactionSimulator's order book and use our logger. self.algo.set_order(self.order) # ============== # Snapshot Setup # ============== # The algorithm's universe as of our most recent event. # We want an ndict that will have empty objects as default # values on missing keys. self.universe = ndict(internal=defaultdict(SIDData)) # 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 create_trade(sid, price, amount, datetime, source_id="test_factory"): row = ndict({ 'source_id': source_id, 'type': DATASOURCE_TYPE.TRADE, 'sid': sid, 'dt': datetime, 'price': price, 'close': price, 'open': price, 'low': price * .95, 'high': price * 1.05, 'volume': amount }) return row
def __init__(self, order_book, algo, algo_start): # ========== # Algo Setup # ========== # We extract the order book from the txn client so that # the algo can place new orders. self.order_book = order_book self.algo = algo self.algo_start = algo_start.replace(hour=0, minute=0, second=0, microsecond=0) # Monkey patch the user algorithm to place orders in the # TransactionSimulator's order book and use our logger. self.algo.set_order(self.order) # ============== # Snapshot Setup # ============== # The algorithm's universe as of our most recent event. # We want an ndict that will have empty ndicts as default # values on missing keys. self.universe = ndict(internal=defaultdict(ndict)) # 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): record.extra["algo_dt"] = self.snapshot_dt self.processor = Processor(inject_algo_dt)
def handle_data(self, data, *args, **kwargs): """ New method to handle a data frame as sent to the algorithm's handle_data method. """ # extract dates #dts = [data[sid].datetime for sid in self.sids] dts = [event.datetime for event in data.itervalues()] # we have to provide the event with a dt. This is only for # checking if the event is outside the window or not so a # couple of seconds shouldn't matter. We don't add it to # the data parameter, because it would mix dt with the # sid keys. event = ndict() event.dt = max(dts) event.data = data # append data frame to window. update() will call handle_add() and # handle_remove() appropriately self.update(event) # return newly computed or cached value return self.get_transform_value(*args, **kwargs)
def to_dt(msg): return ndict({'dt': msg})
def genTrades(self): # create a sequence of trades events = [ ndict( {'volume': 2000, 'TRANSACTION': None, 'type': DATASOURCE_TYPE.TRADE, 'price': 3.0, 'datetime': datetime.datetime( 2006, 1, 5, 14, 31, tzinfo=pytz.utc), 'high': 3.15, 'low': 2.85, 'sid': 133, 'source_id': 'test_source', 'close': 3.0, 'dt': datetime.datetime(2006, 1, 5, 14, 31, tzinfo=pytz.utc), 'open': 3.0 }), ndict( {'volume': 2000, 'TRANSACTION': None, 'type': DATASOURCE_TYPE.TRADE, 'price': 3.5, 'datetime': datetime.datetime( 2006, 1, 5, 14, 32, tzinfo=pytz.utc), 'high': 3.15, 'low': 2.85, 'sid': 133, 'source_id': 'test_source', 'close': 3.5, 'dt': datetime.datetime(2006, 1, 5, 14, 32, tzinfo=pytz.utc), 'open': 3.0 }), ndict( {'volume': 2000, 'TRANSACTION': None, 'type': DATASOURCE_TYPE.TRADE, 'price': 4.0, 'datetime': datetime.datetime( 2006, 1, 5, 14, 33, tzinfo=pytz.utc), 'high': 3.15, 'low': 2.85, 'sid': 133, 'source_id': 'test_source', 'close': 4.0, 'dt': datetime.datetime(2006, 1, 5, 14, 33, tzinfo=pytz.utc), 'open': 3.5 }), ndict( {'volume': 2000, 'TRANSACTION': None, 'type': DATASOURCE_TYPE.TRADE, 'price': 3.5, 'datetime': datetime.datetime( 2006, 1, 5, 14, 34, tzinfo=pytz.utc), 'high': 3.15, 'low': 2.85, 'sid': 133, 'source_id': 'test_source', 'close': 3.5, 'dt': datetime.datetime(2006, 1, 5, 14, 34, tzinfo=pytz.utc), 'open': 4.0 }), ndict( {'volume': 2000, 'TRANSACTION': None, 'type': DATASOURCE_TYPE.TRADE, 'price': 3.0, 'datetime': datetime.datetime( 2006, 1, 5, 14, 35, tzinfo=pytz.utc), 'high': 3.15, 'low': 2.85, 'sid': 133, 'source_id': 'test_source', 'close': 3.0, 'dt': datetime.datetime(2006, 1, 5, 14, 35, tzinfo=pytz.utc), 'open': 3.5 }) ] return events
ERRORS = ndict({ # Raised if a user script calls the override_slippage magic # with a slipage object that isn't a VolumeShareSlippage or # FixedSlipapge 'UNSUPPORTED_SLIPPAGE_MODEL': "You attempted to override slippage with an unsupported class. \ Please use VolumeShareSlippage or FixedSlippage.", # Raised if a users script calls override_slippage magic # after the initialize method has returned. 'OVERRIDE_SLIPPAGE_POST_INIT': "You attempted to override slippage after the simulation has \ started. You may only call override_slippage in your initialize \ method.", # Raised if a user script calls the override_commission magic # with a commission object that isn't a PerShare or # PerTrade commission 'UNSUPPORTED_COMMISSION_MODEL': "You attempted to override commission with an unsupported class. \ Please use PerShare or PerTrade.", # Raised if a users script calls override_commission magic # after the initialize method has returned. 'OVERRIDE_COMMISSION_POST_INIT': "You attempted to override commission after the simulation has \ started. You may only call override_commission in your initialize \ method.", })
def to_dt(msg): return ndict({"dt": msg})