def setUp(self): super().setUp() self.log_records = [] self.clock: Clock = Clock(ClockMode.BACKTEST, self.clock_tick_size, self.start_timestamp, self.end_timestamp) self.market: BacktestMarket = BacktestMarket() self.maker_data: MockOrderBookLoader = MockOrderBookLoader( *self.maker_trading_pairs) self.mid_price = 100 self.order_delay_time = 15 self.cancel_order_wait_time = 45 self.maker_data.set_balanced_order_book(mid_price=self.mid_price, min_price=1, max_price=200, price_step_size=1, volume_step_size=10) self.market.add_data(self.maker_data) self.market.set_balance("COINALPHA", 500) self.market.set_balance("WETH", 50000) self.market.set_balance("QETH", 500) self.market.set_quantization_param( QuantizationParams(self.maker_trading_pairs[0], 6, 6, 6, 6)) self.market_info: MarketTradingPairTuple = MarketTradingPairTuple( *([self.market] + self.maker_trading_pairs)) # Define strategies to test self.limit_buy_strategy: TwapTradeStrategy = TwapTradeStrategy( [self.market_info], order_price=Decimal("99"), cancel_order_wait_time=self.cancel_order_wait_time, is_buy=True, order_delay_time=self.order_delay_time, target_asset_amount=Decimal("2.0"), order_step_size=Decimal("1.0")) self.limit_sell_strategy: TwapTradeStrategy = TwapTradeStrategy( [self.market_info], order_price=Decimal("101"), cancel_order_wait_time=self.cancel_order_wait_time, is_buy=False, order_delay_time=self.order_delay_time, target_asset_amount=Decimal("5.0"), order_step_size=Decimal("1.67")) self.clock.add_iterator(self.market) self.maker_order_fill_logger: EventLogger = EventLogger() self.cancel_order_logger: EventLogger = EventLogger() self.buy_order_completed_logger: EventLogger = EventLogger() self.sell_order_completed_logger: EventLogger = EventLogger() self.market.add_listener(MarketEvent.BuyOrderCompleted, self.buy_order_completed_logger) self.market.add_listener(MarketEvent.SellOrderCompleted, self.sell_order_completed_logger) self.market.add_listener(MarketEvent.OrderFilled, self.maker_order_fill_logger) self.market.add_listener(MarketEvent.OrderCancelled, self.cancel_order_logger)
def test_strategy_time_span_execution(self): span_start_time = self.start_timestamp + (self.clock_tick_size * 5) span_end_time = self.start_timestamp + (self.clock_tick_size * 7) strategy = TwapTradeStrategy( [self.market_info], order_price=Decimal("99"), cancel_order_wait_time=self.cancel_order_wait_time, is_buy=True, order_delay_time=self.order_delay_time, target_asset_amount=Decimal("100.0"), order_step_size=Decimal("1.0"), execution_state=RunInTimeConditionalExecutionState( start_timestamp=datetime.fromtimestamp(span_start_time), end_timestamp=datetime.fromtimestamp(span_end_time))) self.clock.add_iterator(strategy) # check no orders are placed before span start self.clock.backtest_til(span_start_time - self.clock_tick_size) self.assertEqual(0, len(self.limit_sell_strategy.active_asks)) order_time_1 = span_start_time + self.clock_tick_size self.clock.backtest_til(order_time_1) self.assertEqual(1, len(strategy.active_bids)) first_bid_order: LimitOrder = strategy.active_bids[0][1] self.assertEqual(Decimal("99"), first_bid_order.price) self.assertEqual(1, first_bid_order.quantity) # check no orders are placed after span end order_time_2 = span_end_time + (self.clock_tick_size * 10) self.clock.backtest_til(order_time_2) self.assertEqual(1, len(strategy.active_bids))
def test_status_with_time_span_execution(self): exchange = MockExchange( client_config_map=ClientConfigAdapter(ClientConfigMap())) exchange.buy_price = Decimal("25100") exchange.sell_price = Decimal("24900") exchange.update_account_balance({ "ETH": Decimal("100000"), "USDT": Decimal(10000) }) exchange.update_account_available_balance({ "ETH": Decimal("100000"), "USDT": Decimal(10000) }) marketTuple = MarketTradingPairTuple(exchange, "ETH-USDT", "ETH", "USDT") start_time_string = "2021-06-24 10:00:00" end_time_string = "2021-06-24 10:30:00" execution_type = RunInTimeConditionalExecutionState( start_timestamp=datetime.fromisoformat(start_time_string), end_timestamp=datetime.fromisoformat(end_time_string)) strategy = TwapTradeStrategy(market_infos=[marketTuple], is_buy=True, target_asset_amount=Decimal(100), order_step_size=Decimal(10), order_price=Decimal(25000), execution_state=execution_type) status = strategy.format_status() expected_status = ( "\n Configuration:\n" " Total amount: 100 ETH Order price: 25000 USDT Order size: 10.00 ETH\n" f" Execution type: run between {start_time_string} and {end_time_string}\n\n" " Markets:\n" " Exchange Market Best Bid Price Best Ask Price Mid Price\n" " 0 MockExchange ETH-USDT 24900 25100 25000\n\n" " Assets:\n" " Exchange Asset Total Balance Available Balance\n" " 0 MockExchange ETH 100000 100000\n" " 1 MockExchange USDT 10000 10000\n\n" " No active maker orders.\n\n" " Average filled orders price: 0 USDT\n" " Pending amount: 100 ETH\n\n" "*** WARNINGS ***\n" " Markets are offline for the ETH-USDT pair. " "Continued trading with these markets may be dangerous.\n") self.assertEqual(expected_status, status)
def test_tick_logs_warning_when_market_not_connected(self): exchange = MockExchange() exchange.ready = True marketTuple = MarketTradingPairTuple(exchange, "ETH-USDT", "ETH", "USDT") strategy = TwapTradeStrategy(market_infos=[marketTuple]) strategy.logger().setLevel(1) strategy.logger().addHandler(self) start_timestamp = time.time() strategy.start(Clock(ClockMode.BACKTEST), start_timestamp) strategy.tick(start_timestamp + 1000) self.assertTrue( self._is_logged('WARNING', ( "WARNING: Some markets are not connected or are down at the moment. " "Market making may be dangerous when markets or networks are unstable." )))
def test_tick_logs_warning_when_market_not_ready(self): exchange = MockExchange() exchange.ready = False marketTuple = MarketTradingPairTuple(exchange, "ETH-USDT", "ETH", "USDT") strategy = TwapTradeStrategy(market_infos=[marketTuple]) strategy.logger().setLevel(1) strategy.logger().addHandler(self) start_timestamp = time.time() strategy.start(Clock(ClockMode.BACKTEST), start_timestamp) strategy.tick(start_timestamp + 1000) self.assertTrue( self._is_logged( 'WARNING', "Markets are not ready. No market making trades are permitted." ))
def test_creation_without_market_info_fails(self): with self.assertRaises(ValueError) as ex_context: TwapTradeStrategy(market_infos=[], is_buy=True, target_asset_amount=1, order_step_size=1, order_price=1) self.assertEqual(str(ex_context.exception), "market_infos must not be empty.")
def test_start(self): exchange = MockExchange() marketTuple = MarketTradingPairTuple(exchange, "ETH-USDT", "ETH", "USDT") strategy = TwapTradeStrategy(market_infos=[marketTuple]) strategy.logger().setLevel(1) strategy.logger().addHandler(self) start_timestamp = time.time() strategy.start(Clock(ClockMode.BACKTEST), start_timestamp) self.assertTrue( self._is_logged('INFO', 'Waiting for 10.0 to place orders'))
def test_status(self): exchange = MockExchange() exchange.buy_price = Decimal("25100") exchange.sell_price = Decimal("24900") exchange.update_account_balance({ "ETH": Decimal("100000"), "USDT": Decimal(10000) }) exchange.update_account_available_balance({ "ETH": Decimal("100000"), "USDT": Decimal(10000) }) marketTuple = MarketTradingPairTuple(exchange, "ETH-USDT", "ETH", "USDT") strategy = TwapTradeStrategy(market_infos=[marketTuple], is_buy=True, target_asset_amount=Decimal(100), order_step_size=Decimal(10), order_price=Decimal(25000)) status = strategy.format_status() expected_status = ( "\n Configuration:\n" " Total amount: 100 ETH Order price: 25000 USDT Order size: 10.00 ETH\n" " Execution type: run continuously\n\n" " Markets:\n" " Exchange Market Best Bid Price Best Ask Price Mid Price\n" " 0 MockExchange ETH-USDT 24900 25100 25000\n\n" " Assets:\n" " Exchange Asset Total Balance Available Balance\n" " 0 MockExchange ETH 100000 100000\n" " 1 MockExchange USDT 10000 10000\n\n" " No active maker orders.\n\n" " Average filled orders price: 0 USDT\n" " Pending amount: 100 ETH\n\n" "*** WARNINGS ***\n" " Markets are offline for the ETH-USDT pair. " "Continued trading with these markets may be dangerous.\n") self.assertEqual(expected_status, status)
def test_tick_logs_warning_when_market_not_ready(self): exchange = MockExchange( client_config_map=ClientConfigAdapter(ClientConfigMap())) exchange.ready = False marketTuple = MarketTradingPairTuple(exchange, "ETH-USDT", "ETH", "USDT") strategy = TwapTradeStrategy(market_infos=[marketTuple], is_buy=True, target_asset_amount=1, order_step_size=1, order_price=1) strategy.logger().setLevel(1) strategy.logger().addHandler(self) start_timestamp = time.time() strategy.start(Clock(ClockMode.BACKTEST), start_timestamp) strategy.tick(start_timestamp + 1000) self.assertTrue( self._is_logged( 'WARNING', "Markets are not ready. No market making trades are permitted." ))
def test_start(self): exchange = MockExchange( client_config_map=ClientConfigAdapter(ClientConfigMap())) marketTuple = MarketTradingPairTuple(exchange, "ETH-USDT", "ETH", "USDT") strategy = TwapTradeStrategy(market_infos=[marketTuple], is_buy=True, target_asset_amount=1, order_step_size=1, order_price=1) strategy.logger().setLevel(1) strategy.logger().addHandler(self) start_timestamp = time.time() strategy.start(Clock(ClockMode.BACKTEST), start_timestamp) self.assertTrue( self._is_logged('INFO', 'Waiting for 10.0 to place orders'))
def test_creation_without_market_info_fails(self): with self.assertRaises(ValueError) as ex_context: TwapTradeStrategy([]) self.assertEqual(str(ex_context.exception), "market_infos must not be empty.")