def test_trader_detects_duplicate_identifiers(self): # Arrange strategies = [ TradingStrategy("000"), TradingStrategy("000"), ] # Act self.assertRaises(ValueError, self.trader.initialize_strategies, strategies)
def test_strategy_equality(self): # Arrange strategy1 = TradingStrategy(config=TradingStrategyConfig( order_id_tag="AUD/USD-001")) strategy2 = TradingStrategy(config=TradingStrategyConfig( order_id_tag="AUD/USD-001")) strategy3 = TradingStrategy(config=TradingStrategyConfig( order_id_tag="AUD/USD-002")) # Act, Assert assert strategy1 == strategy1 assert strategy1 == strategy2 assert strategy2 != strategy3
def test_clear_strategies(self): # Arrange strategies = [ TradingStrategy(TradingStrategyConfig(order_id_tag="001")), TradingStrategy(TradingStrategyConfig(order_id_tag="002")), ] self.trader.add_strategies(strategies) # Act self.trader.clear_strategies() # Assert assert self.trader.strategy_states() == {}
def test_change_strategies(self): # Arrange strategies = [ TradingStrategy(TradingStrategyConfig(order_id_tag="003")), TradingStrategy(TradingStrategyConfig(order_id_tag="004")), ] # Act self.trader.add_strategies(strategies) # Assert assert strategies[0].id in self.trader.strategy_states() assert strategies[1].id in self.trader.strategy_states() assert len(self.trader.strategy_states()) == 2
def test_change_strategies(self): # Arrange strategies = [ TradingStrategy("003"), TradingStrategy("004"), ] # Act self.trader.initialize_strategies(strategies, warn_no_strategies=True) # Assert self.assertTrue(strategies[0].id in self.trader.strategy_states()) self.assertTrue(strategies[1].id in self.trader.strategy_states()) self.assertEqual(2, len(self.trader.strategy_states()))
def test_add_strategies(self): # Arrange strategies = [ TradingStrategy(TradingStrategyConfig(order_id_tag="001")), TradingStrategy(TradingStrategyConfig(order_id_tag="002")), ] # Act self.trader.add_strategies(strategies) # Assert assert self.trader.strategy_states() == { StrategyId("TradingStrategy-001"): "INITIALIZED", StrategyId("TradingStrategy-002"): "INITIALIZED", }
def test_modify_order_when_no_changes_does_not_submit_command(self): # Arrange 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.limit( USDJPY_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("90.001"), ) strategy.submit_order(order) # Act strategy.modify_order( order=order, quantity=Quantity.from_int(100000), price=Price.from_str("90.001"), ) # Assert assert self.exec_engine.command_count == 1
def test_cancel_order_when_completed_does_not_submit_command(self): # Arrange 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.stop_market( USDJPY_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("90.006"), ) strategy.submit_order(order) self.exchange.process(0) self.exec_engine.process(TestStubs.event_order_expired(order)) # Act strategy.cancel_order(order) self.exchange.process(0) # Assert assert strategy.cache.orders()[0].status == OrderStatus.EXPIRED assert order not in strategy.cache.orders_working() assert strategy.cache.order_exists(order.client_order_id) assert not strategy.cache.is_order_working(order.client_order_id) assert strategy.cache.is_order_completed(order.client_order_id)
def test_submit_bracket_order_when_instrument_not_in_cache_then_denies(self): # Arrange self.exec_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, ) bracket = strategy.order_factory.bracket_market( GBPUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), stop_loss=Price.from_str("1.00000"), take_profit=Price.from_str("1.00010"), ) submit_bracket = SubmitOrderList( self.trader_id, strategy.id, bracket, self.uuid_factory.generate(), self.clock.timestamp_ns(), ) # Act self.risk_engine.execute(submit_bracket) # Assert assert self.exec_engine.command_count == 0 # <-- command never reaches engine
def test_cancel_order_when_order_does_not_exist_then_denies(self): # Arrange self.exec_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, ) cancel = CancelOrder( self.trader_id, strategy.id, AUDUSD_SIM.id, ClientOrderId("1"), VenueOrderId("1"), self.uuid_factory.generate(), self.clock.timestamp_ns(), ) # Act self.risk_engine.execute(cancel) # Assert assert self.exec_client.calls == ["_start"] assert self.risk_engine.command_count == 1 assert self.exec_engine.command_count == 0
async def run_test(): # Arrange self.exec_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.symbol, OrderSide.BUY, Quantity(100000), ) event = TestStubs.event_order_submitted(order) # Act self.exec_engine.process(event) await asyncio.sleep(0.1) # Assert self.assertEqual(0, self.exec_engine.qsize()) self.assertEqual(1, self.exec_engine.event_count) # Tear Down self.exec_engine.stop()
def test_run_with_empty_strategy(): # Arrange data = BacktestDataContainer() data.add_instrument(USDJPY_SIM) data.add_bars(USDJPY_SIM.symbol, BarAggregation.MINUTE, PriceType.BID, TestDataProvider.usdjpy_1min_bid()) data.add_bars(USDJPY_SIM.symbol, BarAggregation.MINUTE, PriceType.ASK, TestDataProvider.usdjpy_1min_ask()) strategies = [TradingStrategy("001")] engine = BacktestEngine( data=data, strategies=strategies, bypass_logging=True, ) engine.add_exchange( venue=Venue("SIM"), oms_type=OMSType.HEDGING, starting_balances=[Money(1_000_000, USD)], fill_model=FillModel(), ) start = datetime(2013, 1, 1, 22, 0, 0, 0, tzinfo=pytz.utc) stop = datetime(2013, 8, 10, 0, 0, 0, 0, tzinfo=pytz.utc) stats_file = "perf_stats_backtest_run_empty.prof" cProfile.runctx("engine.run(start, stop)", globals(), locals(), stats_file) s = pstats.Stats(stats_file) s.strip_dirs().sort_stats("time").print_stats()
def setUp(self): # Fixture Setup self.venue = Venue("SIM") self.usdjpy = TestInstrumentProvider.default_fx_ccy(Symbol("USD/JPY", self.venue)) data = BacktestDataContainer() data.add_instrument(self.usdjpy) data.add_bars(self.usdjpy.symbol, BarAggregation.MINUTE, PriceType.BID, TestDataProvider.usdjpy_1min_bid()) data.add_bars(self.usdjpy.symbol, BarAggregation.MINUTE, PriceType.ASK, TestDataProvider.usdjpy_1min_ask()) self.engine = BacktestEngine( data=data, strategies=[TradingStrategy('000')], bypass_logging=True, use_tick_cache=True, ) interest_rate_data = pd.read_csv(os.path.join(PACKAGE_ROOT + "/data/", "short-term-interest.csv")) fx_rollover_interest = FXRolloverInterestModule(rate_data=interest_rate_data) self.engine.add_exchange( venue=self.venue, oms_type=OMSType.HEDGING, starting_balances=[Money(1_000_000, USD)], modules=[fx_rollover_interest] )
def test_submit_order_when_invalid_quantity_less_than_minimum_then_denies(self): # Arrange self.exec_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.limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(1), # <- invalid quantity Price.from_str("1.00000"), ) 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) # Assert assert self.exec_engine.command_count == 0 # <-- command never reaches engine
def test_submit_order_when_instrument_not_in_cache_then_denies(self): # Arrange self.exec_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( GBPUSD_SIM.id, # <-- not in the cache 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) # Assert assert self.exec_engine.command_count == 0 # <-- command never reaches engine
def test_submit_order_when_risk_bypassed_sends_to_execution_engine(self): # Arrange self.exec_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( trader_id=self.trader_id, strategy_id=strategy.id, position_id=None, order=order, command_id=self.uuid_factory.generate(), ts_init=self.clock.timestamp_ns(), ) # Act self.risk_engine.execute(submit_order) # Assert assert self.exec_engine.command_count == 1 # <-- initial account event assert self.exec_client.calls == ["_start", "submit_order"]
def test_submit_order_with_default_settings_then_sends_to_client(self): # Arrange self.exec_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) # Assert assert self.exec_engine.command_count == 1 assert self.exec_client.calls == ["_start", "submit_order"]
def test_cancel_order(self): # Arrange strategy = TradingStrategy(order_id_tag="001") strategy.register_trader( TraderId("TESTER", "000"), clock=self.clock, uuid_factory=self.uuid_factory, logger=self.logger, ) self.exec_engine.register_strategy(strategy) order = strategy.order_factory.stop( USDJPY_FXCM, OrderSide.BUY, Quantity(100000), Price("90.005"), ) strategy.submit_order(order) # Act strategy.cancel_order(order) # Assert self.assertTrue(order in strategy.execution.orders()) self.assertEqual(OrderState.CANCELLED, strategy.execution.orders()[0].state()) self.assertEqual(order.cl_ord_id, strategy.execution.orders_completed()[0].cl_ord_id) self.assertTrue(order.cl_ord_id not in strategy.execution.orders_working()) self.assertTrue(strategy.execution.order_exists(order.cl_ord_id)) self.assertFalse(strategy.execution.is_order_working(order.cl_ord_id)) self.assertTrue(strategy.execution.is_order_completed(order.cl_ord_id))
def test_modify_order(self): # Arrange strategy = TradingStrategy(order_id_tag="001") strategy.register_trader( TraderId("TESTER", "000"), clock=self.clock, uuid_factory=self.uuid_factory, logger=self.logger) self.exec_engine.register_strategy(strategy) order = strategy.order_factory.limit( USDJPY_FXCM, OrderSide.BUY, Quantity(100000), Price("90.001"), ) strategy.submit_order(order) # Act strategy.modify_order(order, Quantity(110000), Price("90.002")) # Assert self.assertEqual(order, strategy.execution.orders()[0]) self.assertEqual(OrderState.WORKING, strategy.execution.orders()[0].state()) self.assertEqual(Quantity(110000), strategy.execution.orders()[0].quantity) self.assertEqual(Price("90.002"), strategy.execution.orders()[0].price) self.assertTrue(strategy.execution.is_flat()) self.assertTrue(strategy.execution.order_exists(order.cl_ord_id)) self.assertTrue(strategy.execution.is_order_working(order.cl_ord_id)) self.assertFalse(strategy.execution.is_order_completed(order.cl_ord_id))
def setup(self): # Fixture Setup usdjpy = TestInstrumentProvider.default_fx_ccy("USD/JPY") data = BacktestDataContainer() data.add_instrument(usdjpy) data.add_bars( usdjpy.id, BarAggregation.MINUTE, PriceType.BID, TestDataProvider.usdjpy_1min_bid()[:2000], ) data.add_bars( usdjpy.id, BarAggregation.MINUTE, PriceType.ASK, TestDataProvider.usdjpy_1min_ask()[:2000], ) self.engine = BacktestEngine( data=data, strategies=[TradingStrategy("000")], use_data_cache=True, ) self.engine.add_exchange( venue=Venue("SIM"), oms_type=OMSType.HEDGING, starting_balances=[Money(1_000_000, USD)], fill_model=FillModel(), )
def test_flatten_position(self): # Arrange 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( USDJPY_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) strategy.submit_order(order) self.exchange.process(0) position = self.cache.positions_open()[0] # Act strategy.flatten_position(position) self.exchange.process(0) # Assert assert order.status == OrderStatus.FILLED assert strategy.portfolio.is_completely_flat()
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()
def setUp(self): # Fixture Setup # Fresh isolated loop testing pattern self.loop = asyncio.new_event_loop() asyncio.set_event_loop(self.loop) config = { "trader": { "name": "tester", "id_tag": "000", }, "logging": { "log_level_console": "INF", "log_level_file": "DBG", "log_level_store": "WRN", }, "exec_database": { "type": "in-memory", }, "strategy": { "load_state": True, "save_state": True, }, "data_clients": {}, "exec_clients": {}, } self.node = TradingNode( strategies=[TradingStrategy("000")], config=config, )
def test_submit_order_when_not_connected_logs_and_does_not_send(self): # Arrange strategy = TradingStrategy("000") order = self.order_factory.market( ETHUSDT_BINANCE.symbol, OrderSide.BUY, Quantity(100), ) command = SubmitOrder( BINANCE, self.trader_id, self.account_id, strategy.id, PositionId.null(), order, self.uuid_factory.generate(), self.clock.utc_now(), ) # Act self.exec_client.submit_order(command) # Assert self.assertEqual(OrderState.INITIALIZED, order.state)
def setUp(self): # Fixture Setup self.venue = Venue("SIM") self.audusd = TestInstrumentProvider.default_fx_ccy("AUD/USD") data = BacktestDataContainer() data.add_instrument(self.audusd) data.add_quote_ticks(self.audusd.id, TestDataProvider.audusd_ticks()) self.engine = BacktestEngine( data=data, strategies=[TradingStrategy("000")], bypass_logging=True, use_data_cache=True, ) interest_rate_data = pd.read_csv( os.path.join(PACKAGE_ROOT + "/data/", "short-term-interest.csv")) fx_rollover_interest = FXRolloverInterestModule( rate_data=interest_rate_data) self.engine.add_exchange( venue=self.venue, oms_type=OMSType.HEDGING, starting_balances=[Money(1_000_000, AUD)], modules=[fx_rollover_interest], )
def test_update_order_when_no_order_found_denies(self): # Arrange self.exec_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, ) modify = ModifyOrder( self.trader_id, strategy.id, AUDUSD_SIM.id, ClientOrderId("invalid"), VenueOrderId("1"), Quantity.from_int(100000), Price.from_str("1.00010"), None, self.uuid_factory.generate(), self.clock.timestamp_ns(), ) # Act self.risk_engine.execute(modify) # Assert assert self.exec_client.calls == ["_start"] assert self.risk_engine.command_count == 1 assert self.exec_engine.command_count == 0
def test_submit_order_with_default_settings_sends_to_client(self): # Arrange self.exec_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( self.venue, self.trader_id, self.account_id, strategy.id, PositionId.null(), order, self.uuid_factory.generate(), self.clock.utc_now(), ) # Act self.risk_engine.execute(submit_order) # Assert assert self.exec_client.calls == ['connect', 'submit_order']
def test_submit_order_with_valid_order_successfully_submits(self): # Arrange 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( USDJPY_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) # Act strategy.submit_order(order) self.exchange.process(0) # Assert assert order in strategy.cache.orders() assert strategy.cache.orders()[0].status == OrderStatus.FILLED assert order.client_order_id not in strategy.cache.orders_working() assert not strategy.cache.is_order_working(order.client_order_id) assert strategy.cache.is_order_completed(order.client_order_id)
def test_submit_bracket_order_when_not_connected_logs_and_does_not_send( self): # Arrange strategy = TradingStrategy("000") entry = self.order_factory.market( ETHUSDT_BINANCE.symbol, OrderSide.BUY, Quantity(100), ) bracket = self.order_factory.bracket(entry, Price("500.00000")) command = SubmitBracketOrder( BINANCE, self.trader_id, self.account_id, strategy.id, bracket, self.uuid_factory.generate(), self.clock.utc_now(), ) # Act self.exec_client.submit_bracket_order(command) # Assert self.assertEqual(OrderState.INITIALIZED, entry.state)
def test_submit_order_list_with_valid_order_successfully_submits(self): # Arrange strategy = TradingStrategy() strategy.register( trader_id=self.trader_id, portfolio=self.portfolio, msgbus=self.msgbus, cache=self.cache, clock=self.clock, logger=self.logger, ) bracket = strategy.order_factory.bracket_market( USDJPY_SIM.id, OrderSide.BUY, Quantity.from_int(100000), stop_loss=Price.from_str("90.000"), take_profit=Price.from_str("90.500"), ) # Act strategy.submit_order_list(bracket) # Assert assert bracket.orders[0] in strategy.cache.orders() assert bracket.orders[1] in strategy.cache.orders() assert bracket.orders[2] in strategy.cache.orders()