class TestLiveRiskEngine: def setup(self): # Fixture Setup self.loop = asyncio.get_event_loop() self.loop.set_debug(True) self.clock = LiveClock() self.uuid_factory = UUIDFactory() self.logger = Logger(self.clock) self.trader_id = TestStubs.trader_id() self.account_id = TestStubs.account_id() self.order_factory = OrderFactory( trader_id=self.trader_id, strategy_id=StrategyId("S-001"), clock=self.clock, ) self.random_order_factory = OrderFactory( trader_id=TraderId("RANDOM-042"), strategy_id=StrategyId("S-042"), clock=self.clock, ) self.msgbus = MessageBus( trader_id=self.trader_id, clock=self.clock, logger=self.logger, ) self.cache = TestStubs.cache() self.portfolio = Portfolio( msgbus=self.msgbus, cache=self.cache, clock=self.clock, logger=self.logger, ) self.data_engine = LiveDataEngine( loop=self.loop, msgbus=self.msgbus, cache=self.cache, clock=self.clock, logger=self.logger, ) self.exec_engine = LiveExecutionEngine( loop=self.loop, msgbus=self.msgbus, cache=self.cache, clock=self.clock, logger=self.logger, ) self.risk_engine = LiveRiskEngine( loop=self.loop, portfolio=self.portfolio, msgbus=self.msgbus, cache=self.cache, clock=self.clock, logger=self.logger, ) self.exec_client = MockExecutionClient( client_id=ClientId("SIM"), venue_type=VenueType.ECN, account_id=TestStubs.account_id(), account_type=AccountType.MARGIN, base_currency=USD, msgbus=self.msgbus, cache=self.cache, clock=self.clock, logger=self.logger, ) # Wire up components self.exec_engine.register_client(self.exec_client) @pytest.mark.asyncio async def test_start_when_loop_not_running_logs(self): # Arrange, Act self.risk_engine.start() # Assert assert True # No exceptions raised self.risk_engine.stop() @pytest.mark.asyncio async def test_get_event_loop_returns_expected_loop(self): # Arrange, Act loop = self.risk_engine.get_event_loop() # Assert assert loop == self.loop @pytest.mark.asyncio async def test_message_qsize_at_max_blocks_on_put_command(self): # Arrange self.msgbus.deregister("RiskEngine.execute", self.risk_engine.execute) self.risk_engine = LiveRiskEngine( loop=self.loop, portfolio=self.portfolio, msgbus=self.msgbus, cache=self.cache, clock=self.clock, logger=self.logger, config=LiveRiskEngineConfig(qsize=1), ) strategy = TradingStrategy() strategy.register( trader_id=self.trader_id, portfolio=self.portfolio, msgbus=self.msgbus, cache=self.cache, clock=self.clock, logger=self.logger, ) order = strategy.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) submit_order = SubmitOrder( self.trader_id, strategy.id, None, order, self.uuid_factory.generate(), self.clock.timestamp_ns(), ) # Act self.risk_engine.execute(submit_order) self.risk_engine.execute(submit_order) await asyncio.sleep(0.1) # Assert assert self.risk_engine.qsize() == 1 assert self.risk_engine.command_count == 0 @pytest.mark.asyncio async def test_message_qsize_at_max_blocks_on_put_event(self): # Arrange self.msgbus.deregister("RiskEngine.execute", self.risk_engine.execute) self.risk_engine = LiveRiskEngine( loop=self.loop, portfolio=self.portfolio, msgbus=self.msgbus, cache=self.cache, clock=self.clock, logger=self.logger, config=LiveRiskEngineConfig(qsize=1), ) strategy = TradingStrategy() strategy.register( trader_id=self.trader_id, portfolio=self.portfolio, msgbus=self.msgbus, cache=self.cache, clock=self.clock, logger=self.logger, ) order = strategy.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) submit_order = SubmitOrder( self.trader_id, strategy.id, None, order, self.uuid_factory.generate(), self.clock.timestamp_ns(), ) event = TestStubs.event_order_submitted(order) # Act self.risk_engine.execute(submit_order) self.risk_engine.process(event) # Add over max size await asyncio.sleep(0.1) # Assert assert self.risk_engine.qsize() == 1 assert self.risk_engine.event_count == 0 @pytest.mark.asyncio async def test_start(self): # Arrange, Act self.risk_engine.start() await asyncio.sleep(0.1) # Assert assert self.risk_engine.is_running # Tear Down self.risk_engine.stop() @pytest.mark.asyncio async def test_kill_when_running_and_no_messages_on_queues(self): # Arrange, Act self.risk_engine.start() await asyncio.sleep(0) self.risk_engine.kill() # Assert assert self.risk_engine.is_stopped @pytest.mark.asyncio async def test_kill_when_not_running_with_messages_on_queue(self): # Arrange, Act self.risk_engine.kill() # Assert assert self.risk_engine.qsize() == 0 @pytest.mark.asyncio async def test_execute_command_places_command_on_queue(self): # Arrange self.risk_engine.start() strategy = TradingStrategy() strategy.register( trader_id=self.trader_id, portfolio=self.portfolio, msgbus=self.msgbus, cache=self.cache, clock=self.clock, logger=self.logger, ) order = strategy.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) submit_order = SubmitOrder( self.trader_id, strategy.id, None, order, self.uuid_factory.generate(), self.clock.timestamp_ns(), ) # Act self.risk_engine.execute(submit_order) await asyncio.sleep(0.1) # Assert assert self.risk_engine.qsize() == 0 assert self.risk_engine.command_count == 1 # Tear Down self.risk_engine.stop() await self.risk_engine.get_run_queue_task() @pytest.mark.asyncio async def test_handle_position_opening_with_position_id_none(self): # Arrange self.risk_engine.start() strategy = TradingStrategy() strategy.register( trader_id=self.trader_id, portfolio=self.portfolio, msgbus=self.msgbus, cache=self.cache, clock=self.clock, logger=self.logger, ) order = strategy.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) event = TestStubs.event_order_submitted(order) # Act self.risk_engine.process(event) await asyncio.sleep(0.1) # Assert assert self.risk_engine.qsize() == 0 assert self.risk_engine.event_count == 1 # Tear Down self.risk_engine.stop() await self.risk_engine.get_run_queue_task()
class TestLiveRiskEngine: def setup(self): # Fixture Setup self.clock = LiveClock() self.uuid_factory = UUIDFactory() self.logger = Logger(self.clock) self.trader_id = TraderId("TESTER", "000") self.account_id = TestStubs.account_id() self.order_factory = OrderFactory( trader_id=self.trader_id, strategy_id=StrategyId("S", "001"), clock=self.clock, ) self.random_order_factory = OrderFactory( trader_id=TraderId("RANDOM", "042"), strategy_id=StrategyId("S", "042"), clock=self.clock, ) self.portfolio = Portfolio( clock=self.clock, logger=self.logger, ) self.portfolio.register_cache(DataCache(self.logger)) self.analyzer = PerformanceAnalyzer() # Fresh isolated loop testing pattern self.loop = asyncio.new_event_loop() asyncio.set_event_loop(self.loop) self.database = BypassExecutionDatabase(trader_id=self.trader_id, logger=self.logger) self.exec_engine = LiveExecutionEngine( loop=self.loop, database=self.database, portfolio=self.portfolio, clock=self.clock, logger=self.logger, ) self.venue = Venue("SIM") self.exec_client = MockExecutionClient( self.venue.value, self.account_id, self.exec_engine, self.clock, self.logger, ) self.risk_engine = LiveRiskEngine( loop=self.loop, exec_engine=self.exec_engine, portfolio=self.portfolio, clock=self.clock, logger=self.logger, config={}, ) self.exec_engine.register_client(self.exec_client) self.exec_engine.register_risk_engine(self.risk_engine) def test_start_when_loop_not_running_logs(self): # Arrange # Act self.risk_engine.start() # Assert assert True # No exceptions raised self.risk_engine.stop() def test_get_event_loop_returns_expected_loop(self): # Arrange # Act loop = self.risk_engine.get_event_loop() # Assert assert loop == self.loop def test_message_qsize_at_max_blocks_on_put_command(self): # Arrange self.risk_engine = LiveRiskEngine( loop=self.loop, exec_engine=self.exec_engine, portfolio=self.portfolio, clock=self.clock, logger=self.logger, config={"qsize": 1}, ) strategy = TradingStrategy(order_id_tag="001") strategy.register_trader( TraderId("TESTER", "000"), self.clock, self.logger, ) self.exec_engine.register_strategy(strategy) order = strategy.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity(100000), ) submit_order = SubmitOrder( order.instrument_id, self.trader_id, self.account_id, strategy.id, PositionId.null(), order, self.uuid_factory.generate(), self.clock.timestamp_ns(), ) # Act self.risk_engine.execute(submit_order) self.risk_engine.execute(submit_order) # Assert assert self.risk_engine.qsize() == 1 assert self.risk_engine.command_count == 0 def test_message_qsize_at_max_blocks_on_put_event(self): # Arrange self.risk_engine = LiveRiskEngine( loop=self.loop, exec_engine=self.exec_engine, portfolio=self.portfolio, clock=self.clock, logger=self.logger, config={"qsize": 1}, ) strategy = TradingStrategy(order_id_tag="001") strategy.register_trader( TraderId("TESTER", "000"), self.clock, self.logger, ) self.exec_engine.register_strategy(strategy) order = strategy.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity(100000), ) submit_order = SubmitOrder( order.instrument_id, self.trader_id, self.account_id, strategy.id, PositionId.null(), order, self.uuid_factory.generate(), self.clock.timestamp_ns(), ) event = TestStubs.event_order_submitted(order) # Act self.risk_engine.execute(submit_order) self.risk_engine.process(event) # Add over max size # Assert assert self.risk_engine.qsize() == 1 assert self.risk_engine.event_count == 0 def test_start(self): async def run_test(): # Arrange # Act self.risk_engine.start() await asyncio.sleep(0.1) # Assert assert self.risk_engine.state == ComponentState.RUNNING # Tear Down self.risk_engine.stop() self.loop.run_until_complete(run_test()) def test_kill_when_running_and_no_messages_on_queues(self): async def run_test(): # Arrange # Act self.risk_engine.start() await asyncio.sleep(0) self.risk_engine.kill() # Assert assert self.risk_engine.state == ComponentState.STOPPED self.loop.run_until_complete(run_test()) def test_kill_when_not_running_with_messages_on_queue(self): async def run_test(): # Arrange # Act self.risk_engine.kill() # Assert assert self.risk_engine.qsize() == 0 self.loop.run_until_complete(run_test()) def test_execute_command_places_command_on_queue(self): async def run_test(): # Arrange self.risk_engine.start() strategy = TradingStrategy(order_id_tag="001") strategy.register_trader( TraderId("TESTER", "000"), self.clock, self.logger, ) self.exec_engine.register_strategy(strategy) order = strategy.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity(100000), ) submit_order = SubmitOrder( order.instrument_id, self.trader_id, self.account_id, strategy.id, PositionId.null(), order, self.uuid_factory.generate(), self.clock.timestamp_ns(), ) # Act self.risk_engine.execute(submit_order) await asyncio.sleep(0.1) # Assert assert self.risk_engine.qsize() == 0 assert self.risk_engine.command_count == 1 # Tear Down self.risk_engine.stop() self.loop.run_until_complete(run_test()) def test_handle_position_opening_with_position_id_none(self): async def run_test(): # Arrange self.risk_engine.start() strategy = TradingStrategy(order_id_tag="001") strategy.register_trader( TraderId("TESTER", "000"), self.clock, self.logger, ) self.exec_engine.register_strategy(strategy) order = strategy.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity(100000), ) event = TestStubs.event_order_submitted(order) # Act self.risk_engine.process(event) await asyncio.sleep(0.1) # Assert assert self.risk_engine.qsize() == 0 assert self.risk_engine.event_count == 1 # Tear Down self.risk_engine.stop() self.loop.run_until_complete(run_test())