def test_execute_unsubscribe_custom_data(self): # Arrange self.data_engine.register_client(self.binance_client) self.data_engine.register_client(self.quandl) self.binance_client.connect() handler = [] subscribe = Subscribe( client_id=ClientId("QUANDL"), data_type=DataType(str, metadata={"Type": "news"}), handler=handler.append, command_id=self.uuid_factory.generate(), timestamp_ns=self.clock.timestamp_ns(), ) self.data_engine.execute(subscribe) unsubscribe = Unsubscribe( client_id=ClientId("QUANDL"), data_type=DataType(str, metadata={"Type": "news"}), handler=handler.append, command_id=self.uuid_factory.generate(), timestamp_ns=self.clock.timestamp_ns(), ) # Act self.data_engine.execute(unsubscribe) # Assert self.assertEqual(2, self.data_engine.command_count) self.assertEqual(["subscribe", "unsubscribe"], self.quandl.calls)
def test_add_generic_data_adds_to_container(self): # Arrange engine = BacktestEngine() data_type = DataType(MyData, metadata={"news_wire": "hacks"}) generic_data1 = [ GenericData(data_type, MyData("AAPL hacked")), GenericData( data_type, MyData("AMZN hacked", 1000, 1000), ), GenericData( data_type, MyData("NFLX hacked", 3000, 3000), ), GenericData( data_type, MyData("MSFT hacked", 2000, 2000), ), ] generic_data2 = [ GenericData( data_type, MyData("FB hacked", 1500, 1500), ), ] # Act engine.add_generic_data(ClientId("NEWS_CLIENT"), generic_data1) engine.add_generic_data(ClientId("NEWS_CLIENT"), generic_data2)
def test_unsubscribe_bar_type_then_unsubscribes(self): # Arrange self.data_engine.register_client(self.binance_client) self.binance_client.connect() bar_spec = BarSpecification(1000, BarAggregation.TICK, PriceType.MID) bar_type = BarType(ETHUSDT_BINANCE.id, bar_spec, internal_aggregation=True) handler = ObjectStorer() subscribe = Subscribe( client_id=ClientId(BINANCE.value), data_type=DataType(Bar, metadata={"BarType": bar_type}), handler=handler.store_2, command_id=self.uuid_factory.generate(), timestamp_ns=self.clock.timestamp_ns(), ) self.data_engine.execute(subscribe) unsubscribe = Unsubscribe( client_id=ClientId(BINANCE.value), data_type=DataType(Bar, metadata={"BarType": bar_type}), handler=handler.store_2, command_id=self.uuid_factory.generate(), timestamp_ns=self.clock.timestamp_ns(), ) # Act self.data_engine.execute(unsubscribe) # Assert self.assertEqual([], self.data_engine.subscribed_bars)
def test_execute_unsubscribe_instrument_then_removes_handler(self): # Arrange self.data_engine.register_client(self.binance_client) self.binance_client.connect() handler = [] subscribe = Subscribe( client_id=ClientId(BINANCE.value), data_type=DataType( Instrument, metadata={"InstrumentId": ETHUSDT_BINANCE.id} ), handler=handler.append, command_id=self.uuid_factory.generate(), timestamp_ns=self.clock.timestamp_ns(), ) self.data_engine.execute(subscribe) unsubscribe = Unsubscribe( client_id=ClientId(BINANCE.value), data_type=DataType( Instrument, metadata={"InstrumentId": ETHUSDT_BINANCE.id} ), handler=handler.append, command_id=self.uuid_factory.generate(), timestamp_ns=self.clock.timestamp_ns(), ) # Act self.data_engine.execute(unsubscribe) # Assert self.assertEqual([], self.data_engine.subscribed_instruments)
def test_process_bar_when_subscribers_then_sends_to_registered_handlers( self): # Arrange self.data_engine.register_client(self.binance_client) self.binance_client.connect() bar_spec = BarSpecification(1000, BarAggregation.TICK, PriceType.MID) bar_type = BarType(ETHUSDT_BINANCE.id, bar_spec, internal_aggregation=True) handler1 = ObjectStorer() subscribe1 = Subscribe( client_id=ClientId(BINANCE.value), data_type=DataType(Bar, metadata={"bar_type": bar_type}), handler=handler1.store, command_id=self.uuid_factory.generate(), timestamp_ns=self.clock.timestamp_ns(), ) handler2 = ObjectStorer() subscribe2 = Subscribe( client_id=ClientId(BINANCE.value), data_type=DataType(Bar, metadata={"bar_type": bar_type}), handler=handler2.store, command_id=self.uuid_factory.generate(), timestamp_ns=self.clock.timestamp_ns(), ) self.data_engine.execute(subscribe1) self.data_engine.execute(subscribe2) bar = Bar( bar_type, Price.from_str("1051.00000"), Price.from_str("1055.00000"), Price.from_str("1050.00000"), Price.from_str("1052.00000"), Quantity.from_int(100), 0, 0, ) # Act self.data_engine.process(bar) # Assert self.assertEqual([bar], handler1.get_store()) self.assertEqual([bar], handler2.get_store())
def test_process_quote_tick_when_subscribers_then_sends_to_registered_handlers( self, ): # Arrange self.data_engine.register_client(self.binance_client) self.binance_client.connect() handler1 = [] subscribe1 = Subscribe( client_id=ClientId(BINANCE.value), data_type=DataType( QuoteTick, metadata={"InstrumentId": ETHUSDT_BINANCE.id} ), handler=handler1.append, command_id=self.uuid_factory.generate(), timestamp_ns=self.clock.timestamp_ns(), ) handler2 = [] subscribe2 = Subscribe( client_id=ClientId(BINANCE.value), data_type=DataType( QuoteTick, metadata={"InstrumentId": ETHUSDT_BINANCE.id} ), handler=handler2.append, command_id=self.uuid_factory.generate(), timestamp_ns=self.clock.timestamp_ns(), ) self.data_engine.execute(subscribe1) self.data_engine.execute(subscribe2) tick = QuoteTick( ETHUSDT_BINANCE.id, Price.from_str("100.003"), Price.from_str("100.003"), Quantity.from_int(1), Quantity.from_int(1), 0, 0, ) # Act self.data_engine.process(tick) # Assert self.assertEqual([ETHUSDT_BINANCE.id], self.data_engine.subscribed_quote_ticks) self.assertEqual([tick], handler1) self.assertEqual([tick], handler2)
def test_add_order_book_snapshots_adds_to_container(self): # Arrange data = BacktestDataContainer() snapshot1 = OrderBookSnapshot( instrument_id=ETHUSDT_BINANCE.id, level=OrderBookLevel.L2, bids=[[1550.15, 0.51], [1580.00, 1.20]], asks=[[1552.15, 1.51], [1582.00, 2.20]], timestamp_ns=0, ) snapshot2 = OrderBookSnapshot( instrument_id=ETHUSDT_BINANCE.id, level=OrderBookLevel.L2, bids=[[1551.15, 0.51], [1581.00, 1.20]], asks=[[1553.15, 1.51], [1583.00, 2.20]], timestamp_ns=1_000_000_000, ) # Act data.add_order_book_data([snapshot2, snapshot1]) # <-- reverse order # Assert assert ClientId("BINANCE") in data.clients assert ETHUSDT_BINANCE.id in data.books assert data.order_book_data == [snapshot1, snapshot2] # <-- sorted
def test_add_bars_adds_to_container(self): # Arrange data = BacktestDataContainer() data.add_instrument(USDJPY_SIM) # Act data.add_bars( USDJPY_SIM.id, BarAggregation.MINUTE, PriceType.BID, TestDataProvider.usdjpy_1min_bid()[:2000], ) data.add_bars( USDJPY_SIM.id, BarAggregation.MINUTE, PriceType.ASK, TestDataProvider.usdjpy_1min_ask()[:2000], ) # Assert assert ClientId("SIM") in data.clients assert USDJPY_SIM.id in data.bars_ask assert USDJPY_SIM.id in data.bars_bid assert len(data.bars_bid[USDJPY_SIM.id]) == 1 # MINUTE key assert len(data.bars_ask[USDJPY_SIM.id]) == 1 # MINUTE key
def test_execute_subscribe_order_book_intervals_then_adds_handler(self): # Arrange self.data_engine.register_client(self.binance_client) self.binance_client.connect() subscribe = Subscribe( client_id=ClientId(BINANCE.value), data_type=DataType( OrderBook, metadata={ "InstrumentId": ETHUSDT_BINANCE.id, "Level": 2, "Depth": 25, "Interval": 10, }, ), handler=[].append, command_id=self.uuid_factory.generate(), timestamp_ns=self.clock.timestamp_ns(), ) # Act self.data_engine.execute(subscribe) # Assert self.assertEqual([ETHUSDT_BINANCE.id], self.data_engine.subscribed_order_books)
def setup(self): # Fixture Setup self.clock = TestClock() self.uuid_factory = UUIDFactory() self.logger = Logger(self.clock) self.cache = TestStubs.cache() self.portfolio = Portfolio( cache=self.cache, clock=self.clock, logger=self.logger, ) self.data_engine = DataEngine( portfolio=self.portfolio, cache=self.cache, clock=self.clock, logger=self.logger, ) self.data_engine.process(USDJPY_SIM) self.client = BacktestMarketDataClient( client_id=ClientId("SIM"), engine=self.data_engine, clock=TestClock(), logger=self.logger, )
def setUp(self): # Fixture Setup self.clock = LiveClock() self.uuid_factory = UUIDFactory() self.logger = Logger(self.clock, level_stdout=LogLevel.DEBUG) self.cache = TestStubs.cache() self.portfolio = Portfolio( cache=self.cache, clock=self.clock, logger=self.logger, ) # Fresh isolated loop testing pattern self.loop = asyncio.new_event_loop() asyncio.set_event_loop(self.loop) self.engine = LiveDataEngine( loop=self.loop, portfolio=self.portfolio, cache=self.cache, clock=self.clock, logger=self.logger, ) self.client = LiveMarketDataClient( client_id=ClientId(BINANCE.value), engine=self.engine, clock=self.clock, logger=self.logger, )
def test_data_command_str_and_repr(self): # Arrange # Act handler = [].append command_id = self.uuid_factory.generate() command = Subscribe( client_id=ClientId(BINANCE.value), data_type=DataType( str, {"type": "newswire"}), # str data type is invalid handler=handler, command_id=command_id, timestamp_ns=self.clock.timestamp_ns(), ) # Assert self.assertEqual("Subscribe(<str> {'type': 'newswire'})", str(command)) self.assertEqual( f"Subscribe(" f"client_id=BINANCE, " f"data_type=<str> {{'type': 'newswire'}}, " f"handler={repr(handler)}, " f"id={command_id})", repr(command), )
def test_venue_data_response_message_str_and_repr(self): # Arrange, Act correlation_id = self.uuid_factory.generate() response_id = self.uuid_factory.generate() instrument_id = InstrumentId(Symbol("AUD/USD"), IDEALPRO) response = DataResponse( client_id=ClientId("IB"), venue=Venue("IDEALPRO"), data_type=DataType(QuoteTick, metadata={"instrument_id": instrument_id}), data=[], correlation_id=correlation_id, response_id=response_id, ts_init=self.clock.timestamp_ns(), ) # Assert assert ( str(response) == "DataResponse(QuoteTick{'instrument_id': InstrumentId('AUD/USD.IDEALPRO')})" ) assert repr(response) == ( f"DataResponse(" f"client_id=IB, " f"venue=IDEALPRO, " f"data_type=QuoteTick{{'instrument_id': InstrumentId('AUD/USD.IDEALPRO')}}, " f"correlation_id={correlation_id}, " f"id={response_id})")
def test_serialize_and_deserialize_submit_order_commands(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity(100000, precision=0), ) command = SubmitOrder( self.trader_id, StrategyId("SCALPER-001"), PositionId("P-123456"), order, UUID4(), 0, ClientId("SIM"), ) # Act serialized = self.serializer.serialize(command) deserialized = self.serializer.deserialize(serialized) # Assert assert deserialized == command assert deserialized.order == order print(command) print(len(serialized)) print(serialized) print(b64encode(serialized))
def test_process_trade_tick_when_subscriber_then_sends_to_registered_handler(self): # Arrange self.data_engine.register_client(self.binance_client) self.binance_client.connect() handler = [] subscribe = Subscribe( client_id=ClientId(BINANCE.value), data_type=DataType( TradeTick, metadata={"InstrumentId": ETHUSDT_BINANCE.id} ), handler=handler.append, command_id=self.uuid_factory.generate(), timestamp_ns=self.clock.timestamp_ns(), ) self.data_engine.execute(subscribe) tick = TradeTick( ETHUSDT_BINANCE.id, Price.from_str("1050.00000"), Quantity.from_int(100), AggressorSide.BUY, TradeMatchId("123456789"), 0, 0, ) # Act self.data_engine.process(tick) # Assert self.assertEqual([tick], handler)
def test_add_position_state_reports(self): report_id1 = UUID4() mass_status = ExecutionMassStatus( client_id=ClientId("IB"), account_id=AccountId("IB", "U123456789"), venue=Venue("IDEALPRO"), report_id=report_id1, ts_init=0, ) report_id2 = UUID4() report = PositionStatusReport( account_id=AccountId("IB", "U123456789"), instrument_id=AUDUSD_IDEALPRO, venue_position_id=PositionId("1"), position_side=PositionSide.LONG, quantity=Quantity.from_int(1_000_000), report_id=report_id2, ts_last=0, ts_init=0, ) # Act mass_status.add_position_reports([report]) # Assert assert mass_status.position_reports()[AUDUSD_IDEALPRO] == [report] assert ( repr(mass_status) == f"ExecutionMassStatus(client_id=IB, account_id=IB-U123456789, venue=IDEALPRO, order_reports={{}}, trade_reports={{}}, position_reports={{InstrumentId('AUD/USD.IDEALPRO'): [PositionStatusReport(account_id=IB-U123456789, instrument_id=AUD/USD.IDEALPRO, venue_position_id=1, position_side=LONG, quantity=1_000_000, net_qty=1000000, report_id={report_id2}, ts_last=0, ts_init=0)]}}, report_id={report_id1}, ts_init=0)" # noqa ) assert ( repr(report) == f"PositionStatusReport(account_id=IB-U123456789, instrument_id=AUD/USD.IDEALPRO, venue_position_id=1, position_side=LONG, quantity=1_000_000, net_qty=1000000, report_id={report_id2}, ts_last=0, ts_init=0)" # noqa )
def test_instantiate_execution_mass_status_report(self): # Arrange client_id = ClientId("IB") account_id = AccountId("IB", "U123456789") # Act report_id = UUID4() report = ExecutionMassStatus( client_id=client_id, account_id=account_id, venue=Venue("IDEALPRO"), report_id=report_id, ts_init=0, ) # Assert assert report.client_id == client_id assert report.account_id == account_id assert report.ts_init == 0 assert report.order_reports() == {} assert report.position_reports() == {} assert ( str(report) == f"ExecutionMassStatus(client_id=IB, account_id=IB-U123456789, venue=IDEALPRO, order_reports={{}}, trade_reports={{}}, position_reports={{}}, report_id={report_id}, ts_init=0)" # noqa ) assert ( repr(report) == f"ExecutionMassStatus(client_id=IB, account_id=IB-U123456789, venue=IDEALPRO, order_reports={{}}, trade_reports={{}}, position_reports={{}}, report_id={report_id}, ts_init=0)" # noqa )
def test_registered_clients_returns_expected_list(self): # Arrange # Act result = self.risk_engine.registered_clients # Assert assert result == [ClientId("SIM")]
def test_add_order_state_report(self): # Arrange report = ExecutionMassStatus( client_id=ClientId("IB"), account_id=TestStubs.account_id(), timestamp_ns=0, ) venue_order_id = VenueOrderId("1") order_report = OrderStatusReport( client_order_id=ClientOrderId("O-123456"), venue_order_id=venue_order_id, order_state=OrderState.REJECTED, filled_qty=Quantity.zero(), timestamp_ns=0, ) # Act report.add_order_report(order_report) # Assert assert report.order_reports()[venue_order_id] == order_report assert ( repr(report) == "ExecutionMassStatus(client_id=IB, account_id=SIM-000, timestamp_ns=0, order_reports={VenueOrderId('1'): OrderStatusReport(client_order_id=O-123456, venue_order_id=1, order_state=REJECTED, filled_qty=0, timestamp_ns=0)}, exec_reports={}, position_reports={})" # noqa ) assert ( repr(order_report) == "OrderStatusReport(client_order_id=O-123456, venue_order_id=1, order_state=REJECTED, filled_qty=0, timestamp_ns=0)" # noqa )
def test_register_client_successfully_adds_client(self): # Arrange # Act self.data_engine.register_client(self.binance_client) # Assert self.assertIn(ClientId(BINANCE.value), self.data_engine.registered_clients)
def test_add_position_state_report(self): report = ExecutionMassStatus( client_id=ClientId("IB"), account_id=TestStubs.account_id(), timestamp_ns=0, ) position_report = PositionStatusReport( instrument_id=AUDUSD_SIM, position_side=PositionSide.FLAT, qty=Quantity.zero(), timestamp_ns=0, ) # Act report.add_position_report(position_report) # Assert assert report.position_reports()[AUDUSD_SIM] == position_report assert ( repr(report) == "ExecutionMassStatus(client_id=IB, account_id=SIM-000, timestamp_ns=0, order_reports={}, exec_reports={}, position_reports={InstrumentId('AUD/USD.SIM'): PositionStatusReport(instrument_id=AUD/USD.SIM, side=FLAT, qty=0, timestamp_ns=0)})" # noqa ) # noqa assert ( repr(position_report) == "PositionStatusReport(instrument_id=AUD/USD.SIM, side=FLAT, qty=0, timestamp_ns=0)" # noqa ) # noqa
def test_send_data_request_when_data_type_unrecognized_logs_and_does_nothing(self): # Arrange self.data_engine.register_client(self.binance_client) handler = [] request = DataRequest( client_id=ClientId(BINANCE.value), data_type=DataType( str, metadata={ # str data type is invalid "InstrumentId": InstrumentId(Symbol("SOMETHING"), Venue("RANDOM")), "FromDateTime": None, "ToDateTime": None, "Limit": 1000, }, ), callback=handler.append, request_id=self.uuid_factory.generate(), timestamp_ns=self.clock.timestamp_ns(), ) # Act self.data_engine.send(request) # Assert self.assertEqual(1, self.data_engine.request_count)
def test_data_response_message_str_and_repr(self): # Arrange # Act correlation_id = self.uuid_factory.generate() response_id = self.uuid_factory.generate() instrument_id = InstrumentId(Symbol("AUD/USD"), IDEALPRO) response = DataResponse( client_id=ClientId(BINANCE.value), data_type=DataType(QuoteTick, metadata={"instrument_id": instrument_id}), data=[], correlation_id=correlation_id, response_id=response_id, timestamp_ns=self.clock.timestamp_ns(), ) # Assert self.assertEqual( "DataResponse(<QuoteTick> {'instrument_id': InstrumentId('AUD/USD.IDEALPRO')})", str(response), ) self.assertEqual( f"DataResponse(" f"client_id=BINANCE, " f"data_type=<QuoteTick> {{'instrument_id': InstrumentId('AUD/USD.IDEALPRO')}}, " f"correlation_id={correlation_id}, " f"id={response_id})", repr(response), )
async def run_test(): # Arrange self.engine.start() handler = [] request = DataRequest( client_id=ClientId("RANDOM"), data_type=DataType( QuoteTick, metadata={ "InstrumentId": InstrumentId(Symbol("SOMETHING"), Venue("RANDOM")), "FromDateTime": None, "ToDateTime": None, "Limit": 1000, }, ), callback=handler.append, request_id=self.uuid_factory.generate(), timestamp_ns=self.clock.timestamp_ns(), ) # Act self.engine.send(request) await asyncio.sleep(0.1) # Assert self.assertEqual(0, self.engine.message_qsize()) self.assertEqual(1, self.engine.request_count) # Tear Down self.engine.stop()
async def run_test(): # Arrange self.data_engine.start() # Also starts client await asyncio.sleep(0.3) # Allow engine message queue to start handler = ObjectStorer() request = DataRequest( client_id=ClientId(BINANCE.value), data_type=DataType( TradeTick, metadata={ "instrument_id": ETHUSDT, "from_datetime": None, "to_datetime": None, "limit": 100, }, ), callback=handler.store, request_id=self.uuid_factory.generate(), timestamp_ns=self.clock.timestamp_ns(), ) # Act self.data_engine.send(request) await asyncio.sleep(1) # Assert self.assertEqual(1, self.data_engine.response_count) self.assertEqual(1, handler.count) # Tear Down self.data_engine.stop() await self.data_engine.get_run_queue_task()
def setUp(self): # Fixture Setup self.clock = TestClock() self.uuid_factory = UUIDFactory() self.logger = Logger(self.clock) self.cache = TestStubs.cache() self.portfolio = Portfolio( cache=self.cache, clock=self.clock, logger=self.logger, ) self.data_engine = DataEngine( portfolio=self.portfolio, cache=self.cache, clock=self.clock, logger=self.logger, ) self.venue = Venue("SIM") self.client = DataClient( client_id=ClientId("TEST_PROVIDER"), engine=self.data_engine, clock=self.clock, logger=self.logger, )
def test_message_qsize_at_max_blocks_on_receive_response(self): # Arrange self.engine = LiveDataEngine( loop=self.loop, portfolio=self.portfolio, clock=self.clock, logger=self.logger, config={"qsize": 1}, ) response = DataResponse( client_id=ClientId("BINANCE"), data_type=DataType(QuoteTick), data=[], correlation_id=self.uuid_factory.generate(), response_id=self.uuid_factory.generate(), timestamp_ns=self.clock.timestamp_ns(), ) # Act self.engine.receive(response) self.engine.receive(response) # Add over max size # Assert self.assertEqual(1, self.engine.message_qsize()) self.assertEqual(0, self.engine.command_count)
def test_serialize_and_deserialize_submit_order_list_commands( self, ): # Arrange bracket = self.order_factory.bracket_market( AUDUSD_SIM.id, OrderSide.BUY, Quantity(100000, precision=0), stop_loss=Price(0.99900, precision=5), take_profit=Price(1.00010, precision=5), ) command = SubmitOrderList( client_id=ClientId("SIM"), trader_id=self.trader_id, strategy_id=StrategyId("SCALPER-001"), order_list=bracket, command_id=UUID4(), ts_init=0, ) # Act serialized = self.serializer.serialize(command) deserialized = self.serializer.deserialize(serialized) # Assert assert deserialized == command assert deserialized.list == bracket print(b64encode(serialized)) print(command)
def test_message_qsize_at_max_blocks_on_put_data_command(self): # Arrange self.engine = LiveDataEngine( loop=self.loop, portfolio=self.portfolio, clock=self.clock, logger=self.logger, config={"qsize": 1}, ) subscribe = Subscribe( client_id=ClientId(BINANCE.value), data_type=DataType(QuoteTick), handler=[].append, command_id=self.uuid_factory.generate(), timestamp_ns=self.clock.timestamp_ns(), ) # Act self.engine.execute(subscribe) self.engine.execute(subscribe) # Assert self.assertEqual(1, self.engine.message_qsize()) self.assertEqual(0, self.engine.command_count)
async def test_message_qsize_at_max_blocks_on_put_data_command(self): # Arrange self.msgbus.deregister(endpoint="DataEngine.execute", handler=self.engine.execute) self.msgbus.deregister(endpoint="DataEngine.process", handler=self.engine.process) self.msgbus.deregister(endpoint="DataEngine.request", handler=self.engine.request) self.msgbus.deregister(endpoint="DataEngine.response", handler=self.engine.response) self.engine = LiveDataEngine( loop=self.loop, msgbus=self.msgbus, cache=self.cache, clock=self.clock, logger=self.logger, config=LiveDataEngineConfig(qsize=1), ) subscribe = Subscribe( client_id=ClientId(BINANCE.value), data_type=DataType(QuoteTick), command_id=self.uuid_factory.generate(), ts_init=self.clock.timestamp_ns(), ) # Act self.engine.execute(subscribe) self.engine.execute(subscribe) await asyncio.sleep(0.1) # Assert assert self.engine.message_qsize() == 1 assert self.engine.command_count == 0