def test_to_dict_returns_expected_dict(self): # Arrange tick = TradeTick( instrument_id=AUDUSD_SIM.id, price=Price.from_str("1.00000"), size=Quantity.from_int(10000), aggressor_side=AggressorSide.BUY, trade_id=TradeId("123456789"), ts_event=0, ts_init=0, ) # Act result = TradeTick.to_dict(tick) # Assert assert result == { "type": "TradeTick", "instrument_id": "AUD/USD.SIM", "price": "1.00000", "size": "10000", "aggressor_side": "BUY", "trade_id": "123456789", "ts_event": 0, "ts_init": 0, }
def test_handle_trade_tick_when_volume_at_threshold_sends_bar_to_handler( self): # Arrange bar_store = ObjectStorer() handler = bar_store.store instrument = AUDUSD_SIM bar_spec = BarSpecification(10000, BarAggregation.VOLUME, PriceType.LAST) bar_type = BarType(instrument.id, bar_spec) aggregator = VolumeBarAggregator( instrument, bar_type, handler, Logger(TestClock()), ) tick1 = TradeTick( instrument_id=instrument.id, price=Price.from_str("1.00001"), size=Quantity.from_int(3000), aggressor_side=AggressorSide.BUY, trade_id="123456", ts_event=0, ts_init=0, ) tick2 = TradeTick( instrument_id=instrument.id, price=Price.from_str("1.00002"), size=Quantity.from_int(4000), aggressor_side=AggressorSide.BUY, trade_id="123457", ts_event=0, ts_init=0, ) tick3 = TradeTick( instrument_id=instrument.id, price=Price.from_str("1.00000"), size=Quantity.from_int(3000), aggressor_side=AggressorSide.BUY, trade_id="123458", ts_event=0, ts_init=0, ) # Act aggregator.handle_trade_tick(tick1) aggregator.handle_trade_tick(tick2) aggregator.handle_trade_tick(tick3) # Assert assert len(bar_store.get_store()) == 1 assert bar_store.get_store()[0].open == Price.from_str("1.00001") assert bar_store.get_store()[0].high == Price.from_str("1.00002") assert bar_store.get_store()[0].low == Price.from_str("1.00000") assert bar_store.get_store()[0].close == Price.from_str("1.00000") assert bar_store.get_store()[0].volume == Quantity.from_int(10000)
def test_passive_fill_on_trade_tick(self): # Arrange: Prepare market # Market is 10 @ 15 snapshot = TestDataStubs.order_book_snapshot( instrument_id=USDJPY_SIM.id, bid_volume=1000, ask_volume=1000) self.data_engine.process(snapshot) self.exchange.process_order_book(snapshot) order = self.strategy.order_factory.limit( instrument_id=USDJPY_SIM.id, order_side=OrderSide.SELL, quantity=Quantity.from_int(2000), price=Price.from_str("14"), post_only=False, ) self.strategy.submit_order(order) # Act tick1 = TradeTick( instrument_id=USDJPY_SIM.id, price=Price.from_str("14.0"), size=Quantity.from_int(1000), aggressor_side=AggressorSide.SELL, trade_id=TradeId("123456789"), ts_event=0, ts_init=0, ) self.exchange.process_tick(tick1) # Assert assert order.status == OrderStatus.PARTIALLY_FILLED assert order.filled_qty == Quantity.from_int(1000.0) # No slippage assert order.avg_px == Decimal("14.0")
def test_handle_trade_tick_when_value_below_threshold_updates(self): # Arrange bar_store = ObjectStorer() handler = bar_store.store instrument_id = TestStubs.audusd_id() bar_spec = BarSpecification(100000, BarAggregation.VALUE, PriceType.LAST) bar_type = BarType(instrument_id, bar_spec) aggregator = ValueBarAggregator( AUDUSD_SIM, bar_type, handler, Logger(TestClock()), ) tick1 = TradeTick( instrument_id=AUDUSD_SIM.id, price=Price.from_str("15000.00"), size=Quantity.from_str("3.5"), aggressor_side=AggressorSide.BUY, trade_id="123456", ts_event=0, ts_init=0, ) # Act aggregator.handle_trade_tick(tick1) # Assert assert len(bar_store.get_store()) == 0 assert aggregator.get_cumulative_value() == Decimal("52500.000")
async def test_subscribe_trade_ticks(self, monkeypatch): handler = [] self.msgbus.subscribe( topic="data.trades.BINANCE.ETHUSDT", handler=handler.append, ) # Act self.data_client.subscribe_trade_ticks(ETHUSDT_BINANCE.id) raw_trade = pkgutil.get_data( package="tests.integration_tests.adapters.binance.resources.ws_messages", resource="ws_spot_trade.json", ) # Assert self.data_client._handle_ws_message(raw_trade) await asyncio.sleep(1) assert self.data_engine.data_count == 1 assert len(handler) == 1 # <-- handler received tick assert handler[0] == TradeTick( instrument_id=ETHUSDT_BINANCE.id, price=Price.from_str("4149.74000000"), size=Quantity.from_str("0.43870000"), aggressor_side=AggressorSide.SELL, trade_id=TradeId("705291099"), ts_event=1639351062243000064, ts_init=handler[0].ts_init, )
def test_handle_trade_tick_when_volume_below_threshold_updates(self): # Arrange bar_store = ObjectStorer() handler = bar_store.store instrument = AUDUSD_SIM bar_spec = BarSpecification(10000, BarAggregation.VOLUME, PriceType.LAST) bar_type = BarType(instrument.id, bar_spec) aggregator = VolumeBarAggregator( instrument, bar_type, handler, Logger(TestClock()), ) tick1 = TradeTick( instrument_id=instrument.id, price=Price.from_str("1.00001"), size=Quantity.from_int(1), aggressor_side=AggressorSide.BUY, trade_id="123456", ts_event=0, ts_init=0, ) # Act aggregator.handle_trade_tick(tick1) # Assert assert len(bar_store.get_store()) == 0
def test_data_catalog_instrument_ids_correctly_unmapped(self): # Arrange catalog = DataCatalog.from_env() instrument = TestInstrumentProvider.default_fx_ccy("AUD/USD", venue=Venue("SIM")) trade_tick = TradeTick( instrument_id=instrument.id, price=Price.from_str("2.0"), size=Quantity.from_int(10), aggressor_side=AggressorSide.UNKNOWN, trade_id=TradeId("1"), ts_event=0, ts_init=0, ) write_objects(catalog=catalog, chunk=[instrument, trade_tick]) # Act instrument = catalog.instruments(instrument_ids=["AUD/USD.SIM"], as_nautilus=True)[0] trade_tick = catalog.trade_ticks(instrument_ids=["AUD/USD.SIM"], as_nautilus=True)[0] # Assert assert instrument.id.value == "AUD/USD.SIM" assert trade_tick.instrument_id.value == "AUD/USD.SIM"
def test_from_dict_returns_expected_tick(self): # Arrange tick = TradeTick( instrument_id=AUDUSD_SIM.id, price=Price.from_str("1.00000"), size=Quantity.from_int(10000), aggressor_side=AggressorSide.BUY, trade_id=TradeId("123456789"), ts_event=0, ts_init=0, ) # Act result = TradeTick.from_dict(TradeTick.to_dict(tick)) # Assert assert tick == result
def parse_trade_tick_ws(instrument_id: InstrumentId, msg: Dict, ts_init: int) -> TradeTick: return TradeTick( instrument_id=instrument_id, price=Price.from_str(msg["p"]), size=Quantity.from_str(msg["q"]), aggressor_side=AggressorSide.SELL if msg["m"] else AggressorSide.BUY, trade_id=str(msg["t"]), ts_event=millis_to_nanos(msg["T"]), ts_init=ts_init, )
def parse_trade_tick_http(instrument_id: InstrumentId, msg: Dict, ts_init: int) -> TradeTick: return TradeTick( instrument_id=instrument_id, price=Price.from_str(msg["price"]), size=Quantity.from_str(msg["qty"]), aggressor_side=AggressorSide.SELL if msg["isBuyerMaker"] else AggressorSide.BUY, trade_id=TradeId(str(msg["id"])), ts_event=millis_to_nanos(msg["time"]), ts_init=ts_init, )
def parse_trade_tick_ws( instrument_id: InstrumentId, data: BinanceTradeData, ts_init: int, ) -> TradeTick: return TradeTick( instrument_id=instrument_id, price=Price.from_str(data.p), size=Quantity.from_str(data.q), aggressor_side=AggressorSide.SELL if data.m else AggressorSide.BUY, trade_id=TradeId(str(data.t)), ts_event=millis_to_nanos(data.T), ts_init=ts_init, )
def parse_line(d): if "status" in d: return {} elif "close_price" in d: # return {'timestamp': d['remote_timestamp'], "close_price": d['close_price']} return {} if "trade" in d: ts = millis_to_nanos( pd.Timestamp(d["remote_timestamp"]).timestamp()) return { "timestamp": d["remote_timestamp"], "op": "trade", "trade": TradeTick( instrument_id=InstrumentId(Symbol("TEST"), Venue("BETFAIR")), price=Price(d["trade"]["price"], 4), size=Quantity(d["trade"]["volume"], 4), aggressor_side=d["trade"]["side"], trade_id=TradeId(d["trade"]["trade_id"]), ts_event=ts, ts_init=ts, ), } elif "level" in d and d["level"]["orders"][0]["volume"] == 0: op = "delete" else: op = "update" order_like = d["level"]["orders"][0] if op != "trade" else d[ "trade"] return { "timestamp": d["remote_timestamp"], "op": op, "order": Order( price=Price(order_like["price"], precision=6), size=Quantity(abs(order_like["volume"]), precision=4), # Betting sides are reversed side={ 2: OrderSide.BUY, 1: OrderSide.SELL }[order_like["side"]], id=str(order_like["order_id"]), ), }
def trade_tick_3decimal( instrument_id=None, price=None, aggressor_side=None, quantity=None, ) -> TradeTick: return TradeTick( instrument_id=instrument_id or TestIdStubs.usdjpy_id(), price=price or Price.from_str("1.001"), size=quantity or Quantity.from_int(100000), aggressor_side=aggressor_side or AggressorSide.BUY, trade_id=TradeId("123456"), ts_event=0, ts_init=0, )
def test_hash_str_and_repr(self): # Arrange tick = TradeTick( instrument_id=AUDUSD_SIM.id, price=Price.from_str("1.00000"), size=Quantity.from_int(50000), aggressor_side=AggressorSide.BUY, trade_id=TradeId("123456789"), ts_event=0, ts_init=0, ) # Act, Assert assert isinstance(hash(tick), int) assert str(tick) == "AUD/USD.SIM,1.00000,50000,BUY,123456789,0" assert repr(tick) == "TradeTick(AUD/USD.SIM,1.00000,50000,BUY,123456789,0)"
def _on_trade_ticker_update(self, ticker: Ticker): instrument_id = self._instrument_provider.contract_id_to_instrument_id[ ticker.contract.conId] for tick in ticker.ticks: price = str(tick.price) size = str(tick.size) ts_event = dt_to_unix_nanos(tick.time) update = TradeTick( instrument_id=instrument_id, price=Price.from_str(price), size=Quantity.from_str(size), aggressor_side=AggressorSide.UNKNOWN, trade_id=generate_trade_id(symbol=instrument_id.value, ts_event=ts_event, price=price, size=size), ts_event=ts_event, ts_init=self._clock.timestamp_ns(), ) self._handle_data(update)
def parse_trade_ticks_ws( instrument: Instrument, data: List[Dict[str, Any]], ts_init: int, ) -> List[TradeTick]: ticks: List[TradeTick] = [] for trade in data: tick: TradeTick = TradeTick( instrument_id=instrument.id, price=Price(trade["price"], instrument.price_precision), size=Quantity(trade["size"], instrument.size_precision), aggressor_side=AggressorSide.BUY if trade["side"] == "buy" else AggressorSide.SELL, trade_id=TradeId(str(trade["id"])), ts_event=pd.to_datetime(trade["time"], utc=True).to_datetime64(), ts_init=ts_init, ) ticks.append(tick) return ticks
def parse_historic_trade_ticks(historic_ticks: List[HistoricalTickLast], instrument_id: InstrumentId) -> List[TradeTick]: trades = [] for tick in historic_ticks: ts_init = dt_to_unix_nanos(tick.time) trade_tick = TradeTick( instrument_id=instrument_id, price=Price.from_str(str(tick.price)), size=Quantity.from_str(str(tick.size)), aggressor_side=AggressorSide.UNKNOWN, trade_id=generate_trade_id( symbol=instrument_id.symbol.value, ts_event=ts_init, price=tick.price, size=tick.size, ), ts_init=ts_init, ts_event=ts_init, ) trades.append(trade_tick) return trades
def test_parse_historic_trade_ticks(self): # Arrange raw = IBTestStubs.historic_trades() instrument_id = IBTestStubs.instrument(symbol="AAPL").id # Act ticks = parse_historic_trade_ticks(historic_ticks=raw, instrument_id=instrument_id) # Assert assert all([isinstance(t, TradeTick) for t in ticks]) expected = TradeTick.from_dict({ "type": "TradeTick", "instrument_id": "AAPL.NASDAQ", "price": "6.2", "size": "30.0", "aggressor_side": "UNKNOWN", "trade_id": "2a62fd894bf039d1907675dcaa8d2a64a9022fe3fa4bdd0ef9972c4b40e041d5", "ts_event": 1646185673000000000, "ts_init": 1646185673000000000, }) assert ticks[0] == expected
def _handle_market_trades( runner, instrument, ts_event, ts_init, ): trade_ticks = [] for price, volume in runner.get("trd", []): if volume == 0: continue # Betfair doesn't publish trade ids, so we make our own # TODO - should we use clk here for ID instead of the hash? trade_id = hash_json(data=(ts_event, price, volume)) tick = TradeTick( instrument_id=instrument.id, price=price_to_probability(str(price)), size=Quantity(volume, precision=4), aggressor_side=AggressorSide.UNKNOWN, trade_id=TradeId(trade_id), ts_event=ts_event, ts_init=ts_init, ) trade_ticks.append(tick) return trade_ticks
def test_fully_qualified_name(self): # Arrange, Act, Assert assert TradeTick.fully_qualified_name() == "nautilus_trader.model.data.tick.TradeTick"
def test_handle_trade_tick_when_volume_beyond_threshold_sends_bars_to_handler( self): # Arrange bar_store = ObjectStorer() handler = bar_store.store instrument_id = TestStubs.audusd_id() bar_spec = BarSpecification(100000, BarAggregation.VALUE, PriceType.LAST) bar_type = BarType(instrument_id, bar_spec) aggregator = ValueBarAggregator( AUDUSD_SIM, bar_type, handler, Logger(TestClock()), ) tick1 = TradeTick( instrument_id=AUDUSD_SIM.id, price=Price.from_str("20.00001"), size=Quantity.from_str("3000.00"), aggressor_side=AggressorSide.BUY, trade_id="123456", ts_event=0, ts_init=0, ) tick2 = TradeTick( instrument_id=AUDUSD_SIM.id, price=Price.from_str("20.00002"), size=Quantity.from_str("4000.00"), aggressor_side=AggressorSide.BUY, trade_id="123457", ts_event=0, ts_init=0, ) tick3 = TradeTick( instrument_id=AUDUSD_SIM.id, price=Price.from_str("20.00000"), size=Quantity.from_str("5000.00"), aggressor_side=AggressorSide.BUY, trade_id="123458", ts_event=0, ts_init=0, ) # Act aggregator.handle_trade_tick(tick1) aggregator.handle_trade_tick(tick2) aggregator.handle_trade_tick(tick3) # Assert assert len(bar_store.get_store()) == 2 assert bar_store.get_store()[0].open == Price.from_str("20.00001") assert bar_store.get_store()[0].high == Price.from_str("20.00002") assert bar_store.get_store()[0].low == Price.from_str("20.00001") assert bar_store.get_store()[0].close == Price.from_str("20.00002") assert bar_store.get_store()[0].volume == Quantity.from_str("5000.00") assert bar_store.get_store()[1].open == Price.from_str("20.00002") assert bar_store.get_store()[1].high == Price.from_str("20.00002") assert bar_store.get_store()[1].low == Price.from_str("20.00000") assert bar_store.get_store()[1].close == Price.from_str("20.00000") assert bar_store.get_store()[1].volume == Quantity.from_str("5000.00") assert aggregator.get_cumulative_value() == Decimal("40000.11000")