def setUp(self) -> None: super().setUp() self.log_records = [] self.mocking_assistant = NetworkMockingAssistant() self.async_tasks: List[asyncio.Task] = [] self.exchange = CoinbaseProExchange(self.api_key, self.api_secret, self.api_passphrase, trading_pairs=[self.trading_pair]) self.event_listener = EventLogger() self.exchange.logger().setLevel(1) self.exchange.logger().addHandler(self)
def setUpClass(cls): cls.ev_loop: asyncio.BaseEventLoop = asyncio.get_event_loop() cls.coinbase_pro_auth = CoinbaseProAuth(conf.coinbase_pro_api_key, conf.coinbase_pro_secret_key, conf.coinbase_pro_passphrase) cls.trading_pairs = ["ETH-USDC"] cls.user_stream_tracker: CoinbaseProUserStreamTracker = CoinbaseProUserStreamTracker( coinbase_pro_auth=cls.coinbase_pro_auth, trading_pairs=cls.trading_pairs) cls.user_stream_tracker_task: asyncio.Task = safe_ensure_future( cls.user_stream_tracker.start()) cls.clock: Clock = Clock(ClockMode.REALTIME) cls.market: CoinbaseProExchange = CoinbaseProExchange( conf.coinbase_pro_api_key, conf.coinbase_pro_secret_key, conf.coinbase_pro_passphrase, trading_pairs=cls.trading_pairs) print( "Initializing Coinbase Pro market... this will take about a minute." ) cls.clock.add_iterator(cls.market) cls.stack = contextlib.ExitStack() cls._clock = cls.stack.enter_context(cls.clock) cls.ev_loop.run_until_complete(cls.wait_til_ready()) print("Ready.")
def setUp(self) -> None: super().setUp() self.log_records = [] self.test_task: Optional[asyncio.Task] = None self.resume_test_event = asyncio.Event() self.exchange = CoinbaseProExchange( coinbase_pro_api_key="testAPIKey", coinbase_pro_secret_key="testSecret", coinbase_pro_passphrase="testPassphrase", trading_pairs=[self.trading_pair]) self.exchange.logger().setLevel(1) self.exchange.logger().addHandler(self) self._initialize_event_loggers()
def setUp(self) -> None: super().setUp() self.log_records = [] self.mocking_assistant = NetworkMockingAssistant() self.async_tasks: List[asyncio.Task] = [] self.client_config_map = ClientConfigAdapter(ClientConfigMap()) self.exchange = CoinbaseProExchange( client_config_map=self.client_config_map, coinbase_pro_api_key=self.api_key, coinbase_pro_secret_key=self.api_secret, coinbase_pro_passphrase=self.api_passphrase, trading_pairs=[self.trading_pair]) self.event_listener = EventLogger() self.exchange.logger().setLevel(1) self.exchange.logger().addHandler(self)
def setUpClass(cls): cls.ev_loop = asyncio.get_event_loop() trading_pair = "ETH-USDC" if API_MOCK_ENABLED: cls.web_app = MockWebServer.get_instance() cls.web_app.add_host_to_mock( API_BASE_URL, ["/time", "/products", f"/products/{trading_pair}/book"]) cls.web_app.start() cls.ev_loop.run_until_complete(cls.web_app.wait_til_started()) cls._patcher = mock.patch("aiohttp.client.URL") cls._url_mock = cls._patcher.start() cls._url_mock.side_effect = cls.web_app.reroute_local cls.web_app.update_response("get", API_BASE_URL, "/accounts", FixtureCoinbasePro.BALANCES) cls.web_app.update_response("get", API_BASE_URL, "/fees", FixtureCoinbasePro.TRADE_FEES) cls.web_app.update_response("get", API_BASE_URL, "/orders", FixtureCoinbasePro.ORDERS_STATUS) MockWebSocketServerFactory.start_new_server(WS_BASE_URL) cls._ws_patcher = unittest.mock.patch("websockets.connect", autospec=True) cls._ws_mock = cls._ws_patcher.start() cls._ws_mock.side_effect = MockWebSocketServerFactory.reroute_ws_connect cls._t_nonce_patcher = unittest.mock.patch( "hummingbot.connector.exchange.coinbase_pro.coinbase_pro_exchange.get_tracking_nonce" ) cls._t_nonce_mock = cls._t_nonce_patcher.start() cls.clock: Clock = Clock(ClockMode.REALTIME) cls.market: CoinbaseProExchange = CoinbaseProExchange( API_KEY, API_SECRET, API_PASSPHRASE, trading_pairs=[trading_pair]) print( "Initializing Coinbase Pro market... this will take about a minute." ) cls.clock.add_iterator(cls.market) cls.stack = contextlib.ExitStack() cls._clock = cls.stack.enter_context(cls.clock) cls.ev_loop.run_until_complete(cls.wait_til_ready()) print("Ready.")
def test_orders_saving_and_restoration(self): config_path: str = "test_config" strategy_name: str = "test_strategy" trading_pair: str = "ETH-USDC" sql: SQLConnectionManager = SQLConnectionManager( SQLConnectionType.TRADE_FILLS, db_path=self.db_path) order_id: Optional[str] = None recorder: MarketsRecorder = MarketsRecorder(sql, [self.market], config_path, strategy_name) recorder.start() try: self.assertEqual(0, len(self.market.tracking_states)) # Try to put limit buy order for 0.04 ETH, and watch for order creation event. current_bid_price: Decimal = self.market.get_price( trading_pair, True) bid_price: Decimal = current_bid_price * Decimal("0.8") quantize_bid_price: Decimal = self.market.quantize_order_price( trading_pair, bid_price) amount: Decimal = Decimal("0.02") quantized_amount: Decimal = self.market.quantize_order_amount( trading_pair, amount) order_id, exch_order_id = self.place_order( True, trading_pair, quantized_amount, OrderType.LIMIT_MAKER, quantize_bid_price, 10001, FixtureCoinbasePro.OPEN_BUY_LIMIT_ORDER, FixtureCoinbasePro.WS_ORDER_OPEN) [order_created_event] = self.run_parallel( self.market_logger.wait_for(BuyOrderCreatedEvent)) order_created_event: BuyOrderCreatedEvent = order_created_event self.assertEqual(order_id, order_created_event.order_id) # Verify tracking states self.assertEqual(1, len(self.market.tracking_states)) self.assertEqual(order_id, list(self.market.tracking_states.keys())[0]) # Verify orders from recorder recorded_orders: List[ Order] = recorder.get_orders_for_config_and_market( config_path, self.market) self.assertEqual(1, len(recorded_orders)) self.assertEqual(order_id, recorded_orders[0].id) # Verify saved market states saved_market_states: MarketState = recorder.get_market_states( config_path, self.market) self.assertIsNotNone(saved_market_states) self.assertIsInstance(saved_market_states.saved_state, dict) self.assertGreater(len(saved_market_states.saved_state), 0) # Close out the current market and start another market. self.clock.remove_iterator(self.market) for event_tag in self.events: self.market.remove_listener(event_tag, self.market_logger) self.market: CoinbaseProExchange = CoinbaseProExchange( coinbase_pro_api_key=API_KEY, coinbase_pro_secret_key=API_SECRET, coinbase_pro_passphrase=API_PASSPHRASE, trading_pairs=["ETH-USDC"]) for event_tag in self.events: self.market.add_listener(event_tag, self.market_logger) recorder.stop() recorder = MarketsRecorder(sql, [self.market], config_path, strategy_name) recorder.start() saved_market_states = recorder.get_market_states( config_path, self.market) self.clock.add_iterator(self.market) self.assertEqual(0, len(self.market.limit_orders)) self.assertEqual(0, len(self.market.tracking_states)) self.market.restore_tracking_states( saved_market_states.saved_state) self.assertEqual(1, len(self.market.limit_orders)) self.assertEqual(1, len(self.market.tracking_states)) # Cancel the order and verify that the change is saved. self.cancel_order(trading_pair, order_id, exch_order_id, FixtureCoinbasePro.WS_ORDER_CANCELLED) self.run_parallel(self.market_logger.wait_for(OrderCancelledEvent)) order_id = None self.assertEqual(0, len(self.market.limit_orders)) self.assertEqual(0, len(self.market.tracking_states)) saved_market_states = recorder.get_market_states( config_path, self.market) self.assertEqual(0, len(saved_market_states.saved_state)) finally: if order_id is not None: self.cancel_order(trading_pair, order_id, exch_order_id, FixtureCoinbasePro.WS_ORDER_CANCELLED) self.run_parallel( self.market_logger.wait_for(OrderCancelledEvent)) recorder.stop() os.unlink(self.db_path)
class BitfinexExchangeTests(TestCase): # the level is required to receive logs from the data source logger level = 0 @classmethod def setUpClass(cls) -> None: super().setUpClass() cls.base_asset = "COINALPHA" cls.quote_asset = "HBOT" cls.trading_pair = f"{cls.base_asset}-{cls.quote_asset}" cls.symbol = f"{cls.base_asset}{cls.quote_asset}" cls.listen_key = "TEST_LISTEN_KEY" def setUp(self) -> None: super().setUp() self.log_records = [] self.test_task: Optional[asyncio.Task] = None self.resume_test_event = asyncio.Event() self.exchange = CoinbaseProExchange( coinbase_pro_api_key="testAPIKey", coinbase_pro_secret_key="testSecret", coinbase_pro_passphrase="testPassphrase", trading_pairs=[self.trading_pair]) self.exchange.logger().setLevel(1) self.exchange.logger().addHandler(self) self._initialize_event_loggers() def tearDown(self) -> None: self.test_task and self.test_task.cancel() super().tearDown() def _initialize_event_loggers(self): self.buy_order_completed_logger = EventLogger() self.sell_order_completed_logger = EventLogger() self.order_filled_logger = EventLogger() events_and_loggers = [ (MarketEvent.BuyOrderCompleted, self.buy_order_completed_logger), (MarketEvent.SellOrderCompleted, self.sell_order_completed_logger), (MarketEvent.OrderFilled, self.order_filled_logger) ] for event, logger in events_and_loggers: self.exchange.add_listener(event, logger) def handle(self, record): self.log_records.append(record) def _is_logged(self, log_level: str, message: str) -> bool: return any( record.levelname == log_level and record.getMessage() == message for record in self.log_records) def async_run_with_timeout(self, coroutine: Awaitable, timeout: float = 1): ret = asyncio.get_event_loop().run_until_complete( asyncio.wait_for(coroutine, timeout)) return ret def _return_calculation_and_set_done_event(self, calculation: Callable, *args, **kwargs): if self.resume_test_event.is_set(): raise asyncio.CancelledError self.resume_test_event.set() return calculation(*args, **kwargs) def test_order_fill_event_takes_fee_from_update_event(self): self.exchange.start_tracking_order( order_id="OID1", trading_pair=self.trading_pair, order_type=OrderType.LIMIT, trade_type=TradeType.BUY, price=Decimal("10000"), amount=Decimal("1"), ) order = self.exchange.in_flight_orders.get("OID1") order.update_exchange_order_id("EOID1") partial_fill = { "type": "match", "trade_id": 1, "sequence": 50, "maker_order_id": "EOID1", "taker_order_id": "132fb6ae-456b-4654-b4e0-d681ac05cea1", "time": "2014-11-07T08:19:27.028459Z", "product_id": "BTC-USDT", "size": "0.1", "price": "10050.0", "side": "buy", "taker_user_id": "5844eceecf7e803e259d0365", "user_id": "5844eceecf7e803e259d0365", "taker_profile_id": "765d1549-9660-4be2-97d4-fa2d65fa3352", "profile_id": "765d1549-9660-4be2-97d4-fa2d65fa3352", "taker_fee_rate": "0.005" } mock_user_stream = AsyncMock() mock_user_stream.get.side_effect = functools.partial( self._return_calculation_and_set_done_event, lambda: partial_fill) self.exchange.user_stream_tracker._user_stream = mock_user_stream self.test_task = asyncio.get_event_loop().create_task( self.exchange._user_stream_event_listener()) self.async_run_with_timeout(self.resume_test_event.wait()) expected_executed_quote_amount = Decimal(str( partial_fill["size"])) * Decimal(str(partial_fill["price"])) expected_partial_event_fee = (Decimal(partial_fill["taker_fee_rate"]) * expected_executed_quote_amount) self.assertEqual(expected_partial_event_fee, order.fee_paid) self.assertEqual(1, len(self.order_filled_logger.event_log)) fill_event: OrderFilledEvent = self.order_filled_logger.event_log[0] self.assertEqual(Decimal("0.005"), fill_event.trade_fee.percent) self.assertEqual([], fill_event.trade_fee.flat_fees) self.assertTrue( self._is_logged( "INFO", f"Filled {Decimal(partial_fill['size'])} out of {order.amount} of the " f"{order.order_type_description} order {order.client_order_id}" )) self.assertEqual(0, len(self.buy_order_completed_logger.event_log)) complete_fill = { "type": "match", "trade_id": 2, "sequence": 50, "maker_order_id": "EOID1", "taker_order_id": "132fb6ae-456b-4654-b4e0-d681ac05cea1", "time": "2014-11-07T08:19:27.028459Z", "product_id": "BTC-USDT", "size": "0.9", "price": "10050.0", "side": "buy", "taker_user_id": "5844eceecf7e803e259d0365", "user_id": "5844eceecf7e803e259d0365", "taker_profile_id": "765d1549-9660-4be2-97d4-fa2d65fa3352", "profile_id": "765d1549-9660-4be2-97d4-fa2d65fa3352", "taker_fee_rate": "0.001" } self.resume_test_event = asyncio.Event() mock_user_stream = AsyncMock() mock_user_stream.get.side_effect = functools.partial( self._return_calculation_and_set_done_event, lambda: complete_fill) self.exchange.user_stream_tracker._user_stream = mock_user_stream self.test_task = asyncio.get_event_loop().create_task( self.exchange._user_stream_event_listener()) self.async_run_with_timeout(self.resume_test_event.wait()) expected_executed_quote_amount = Decimal(str( complete_fill["size"])) * Decimal(str(complete_fill["price"])) expected_partial_event_fee += Decimal( complete_fill["taker_fee_rate"]) * expected_executed_quote_amount self.assertEqual(expected_partial_event_fee, order.fee_paid) self.assertEqual(2, len(self.order_filled_logger.event_log)) fill_event: OrderFilledEvent = self.order_filled_logger.event_log[1] self.assertEqual(Decimal("0.001"), fill_event.trade_fee.percent) self.assertEqual([], fill_event.trade_fee.flat_fees) # The order should be marked as complete only when the "done" event arrives, not with the fill event self.assertFalse( self._is_logged( "INFO", f"The market buy order {order.client_order_id} has completed according to Coinbase Pro user stream." )) self.assertEqual(0, len(self.buy_order_completed_logger.event_log))
class TestCoinbaseProExchange(unittest.TestCase): # logging.Level required to receive logs from the exchange level = 0 @classmethod def setUpClass(cls) -> None: super().setUpClass() cls.ev_loop = asyncio.get_event_loop() cls.base_asset = "COINALPHA" cls.quote_asset = "HBOT" cls.trading_pair = f"{cls.base_asset}-{cls.quote_asset}" cls.ex_trading_pair = f"{cls.base_asset}_{cls.quote_asset}" cls.api_key = "someKey" cls.api_secret = "shht" cls.api_passphrase = "somePhrase" def setUp(self) -> None: super().setUp() self.log_records = [] self.mocking_assistant = NetworkMockingAssistant() self.async_tasks: List[asyncio.Task] = [] self.client_config_map = ClientConfigAdapter(ClientConfigMap()) self.exchange = CoinbaseProExchange( client_config_map=self.client_config_map, coinbase_pro_api_key=self.api_key, coinbase_pro_secret_key=self.api_secret, coinbase_pro_passphrase=self.api_passphrase, trading_pairs=[self.trading_pair]) self.event_listener = EventLogger() self.exchange.logger().setLevel(1) self.exchange.logger().addHandler(self) def tearDown(self) -> None: for task in self.async_tasks: task.cancel() super().tearDown() def handle(self, record): self.log_records.append(record) def _is_logged(self, log_level: str, message: str) -> bool: return any( record.levelname == log_level and record.getMessage() == message for record in self.log_records) def async_run_with_timeout(self, coroutine: Awaitable, timeout: int = 1): ret = self.ev_loop.run_until_complete( asyncio.wait_for(coroutine, timeout)) return ret def simulate_trading_rules_initialization(self, mock_api): url = f"{CONSTANTS.REST_URL}{CONSTANTS.PRODUCTS_PATH_URL}" alt_pair = "BTC-USDT" resp = self.get_products_response_mock(alt_pair) mock_api.get(url, body=json.dumps(resp)) self.async_run_with_timeout(self.exchange._update_trading_rules()) def simulate_execute_buy_order(self, mock_api, order_id): url = f"{CONSTANTS.REST_URL}{CONSTANTS.ORDERS_PATH_URL}" regex_url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?")) resp = self.get_orders_response_mock(order_id) mock_api.post(regex_url, body=json.dumps(resp)) self.async_run_with_timeout( self.exchange.execute_sell( order_id=order_id, trading_pair=self.trading_pair, amount=Decimal("1"), order_type=OrderType.LIMIT, price=Decimal("2"), )) def get_account_mock(self, base_balance: float, base_available: float, quote_balance: float, quote_available: float) -> List: account_mock = [{ "id": "7fd0abc0-e5ad-4cbb-8d54-f2b3f43364da", "currency": self.base_asset, "balance": str(base_balance), "available": str(base_available), "hold": "0.0000000000000000", "profile_id": "8058d771-2d88-4f0f-ab6e-299c153d4308", "trading_enabled": True }, { "id": "7fd0abc0-e5ad-4cbb-8d54-f2b3f43364da", "currency": self.quote_asset, "balance": str(quote_balance), "available": str(quote_available), "hold": "0.0000000000000000", "profile_id": "8058d771-2d88-4f0f-ab6e-299c153d4308", "trading_enabled": True }] return account_mock def get_products_response_mock(self, other_pair: str) -> List: products_mock = [{ "id": self.trading_pair, "base_currency": self.base_asset, "quote_currency": self.quote_asset, "base_min_size": "0.00100000", "base_max_size": "280.00000000", "quote_increment": "0.01000000", "base_increment": "0.00000001", "display_name": f"{self.base_asset}/{self.quote_asset}", "min_market_funds": "10", "max_market_funds": "1000000", "margin_enabled": False, "post_only": False, "limit_only": False, "cancel_only": False, "status": "online", "status_message": "", "auction_mode": True, }, { "id": other_pair, "base_currency": other_pair.split("-")[0], "quote_currency": other_pair.split("-")[1], "base_min_size": "0.00100000", "base_max_size": "280.00000000", "quote_increment": "0.01000000", "base_increment": "0.00000001", "display_name": other_pair.replace("-", "/"), "min_market_funds": "10", "max_market_funds": "1000000", "margin_enabled": False, "post_only": False, "limit_only": False, "cancel_only": False, "status": "online", "status_message": "", "auction_mode": True, }] return products_mock def get_orders_response_mock(self, order_id: str) -> Dict: orders_mock = { "id": order_id, "price": "10.00000000", "size": "1.00000000", "product_id": self.trading_pair, "profile_id": "8058d771-2d88-4f0f-ab6e-299c153d4308", "side": "buy", "type": "limit", "time_in_force": "GTC", "post_only": True, "created_at": "2020-03-11T20:48:46.622052Z", "fill_fees": "0.0000000000000000", "filled_size": "0.00000000", "executed_value": "0.0000000000000000", "status": "open", "settled": False } return orders_mock @aioresponses() def test_check_network_not_connected(self, mock_api): url = f"{CONSTANTS.REST_URL}{CONSTANTS.TIME_PATH_URL}" resp = "" mock_api.get(url, status=500, body=json.dumps(resp)) ret = self.async_run_with_timeout( coroutine=self.exchange.check_network()) self.assertEqual(ret, NetworkStatus.NOT_CONNECTED) @aioresponses() def test_check_network(self, mock_api): url = f"{CONSTANTS.REST_URL}{CONSTANTS.TIME_PATH_URL}" resp = {} mock_api.get(url, body=json.dumps(resp)) ret = self.async_run_with_timeout( coroutine=self.exchange.check_network()) self.assertEqual(ret, NetworkStatus.CONNECTED) @aioresponses() def test_update_fee_percentage(self, mock_api): url = f"{CONSTANTS.REST_URL}{CONSTANTS.FEES_PATH_URL}" resp = { "maker_fee_rate": "0.0050", "taker_fee_rate": "0.0050", "usd_volume": "43806.92" } mock_api.get(url, body=json.dumps(resp)) self.async_run_with_timeout(self.exchange._update_fee_percentage()) self.assertEqual(Decimal(resp["maker_fee_rate"]), self.exchange.maker_fee_percentage) self.assertEqual(Decimal(resp["taker_fee_rate"]), self.exchange.taker_fee_percentage) @aioresponses() def test_update_balances(self, mock_api): url = f"{CONSTANTS.REST_URL}{CONSTANTS.ACCOUNTS_PATH_URL}" resp = self.get_account_mock( base_balance=2, base_available=1, quote_balance=4, quote_available=3, ) mock_api.get(url, body=json.dumps(resp)) self.async_run_with_timeout(self.exchange._update_balances()) expected_available_balances = { self.base_asset: Decimal("1"), self.quote_asset: Decimal("3") } self.assertEqual(expected_available_balances, self.exchange.available_balances) expected_balances = { self.base_asset: Decimal("2"), self.quote_asset: Decimal("4") } self.assertEqual(expected_balances, self.exchange.get_all_balances()) @aioresponses() def test_update_trading_rules(self, mock_api): url = f"{CONSTANTS.REST_URL}{CONSTANTS.PRODUCTS_PATH_URL}" alt_pair = "BTC-USDT" resp = self.get_products_response_mock(alt_pair) mock_api.get(url, body=json.dumps(resp)) self.async_run_with_timeout(self.exchange._update_trading_rules()) trading_rules = self.exchange.trading_rules self.assertEqual(2, len(trading_rules)) self.assertIn(self.trading_pair, trading_rules) self.assertIn(alt_pair, trading_rules) self.assertIsInstance(trading_rules[self.trading_pair], TradingRule) self.assertIsInstance(trading_rules[alt_pair], TradingRule) @aioresponses() def test_execute_buy(self, mock_api): self.simulate_trading_rules_initialization(mock_api) some_order_id = "someID" url = f"{CONSTANTS.REST_URL}{CONSTANTS.ORDERS_PATH_URL}" regex_url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?")) resp = self.get_orders_response_mock(some_order_id) mock_api.post(regex_url, body=json.dumps(resp)) self.exchange.add_listener(MarketEvent.BuyOrderCreated, self.event_listener) self.exchange.add_listener(MarketEvent.OrderFilled, self.event_listener) self.async_run_with_timeout( self.exchange.execute_buy( order_id=some_order_id, trading_pair=self.trading_pair, amount=Decimal("1"), order_type=OrderType.LIMIT, price=Decimal("2"), )) self.assertEqual(1, len(self.event_listener.event_log)) event = self.event_listener.event_log[0] self.assertIsInstance(event, BuyOrderCreatedEvent) self.assertEqual(some_order_id, event.order_id) self.assertIn(some_order_id, self.exchange.in_flight_orders) @aioresponses() def test_execute_buy_handles_errors(self, mock_api): self.simulate_trading_rules_initialization(mock_api) url = f"{CONSTANTS.REST_URL}{CONSTANTS.ORDERS_PATH_URL}" regex_url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?")) mock_api.post(regex_url, exception=RuntimeError) self.exchange.add_listener(MarketEvent.BuyOrderCreated, self.event_listener) self.exchange.add_listener(MarketEvent.OrderFailure, self.event_listener) some_order_id = "someID" self.async_run_with_timeout( self.exchange.execute_buy( order_id=some_order_id, trading_pair=self.trading_pair, amount=Decimal("1"), order_type=OrderType.LIMIT, price=Decimal("2"), )) self.assertEqual(1, len(self.event_listener.event_log)) event = self.event_listener.event_log[0] self.assertIsInstance(event, MarketOrderFailureEvent) self.assertEqual(some_order_id, event.order_id) self.assertNotIn(some_order_id, self.exchange.in_flight_orders) @aioresponses() def test_execute_sell(self, mock_api): self.simulate_trading_rules_initialization(mock_api) some_order_id = "someID" url = f"{CONSTANTS.REST_URL}{CONSTANTS.ORDERS_PATH_URL}" regex_url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?")) resp = self.get_orders_response_mock(some_order_id) mock_api.post(regex_url, body=json.dumps(resp)) self.exchange.add_listener(MarketEvent.SellOrderCreated, self.event_listener) self.exchange.add_listener(MarketEvent.OrderFilled, self.event_listener) self.async_run_with_timeout( self.exchange.execute_sell( order_id=some_order_id, trading_pair=self.trading_pair, amount=Decimal("1"), order_type=OrderType.LIMIT, price=Decimal("2"), )) self.assertEqual(1, len(self.event_listener.event_log)) event = self.event_listener.event_log[0] self.assertIsInstance(event, SellOrderCreatedEvent) self.assertEqual(some_order_id, event.order_id) self.assertIn(some_order_id, self.exchange.in_flight_orders) @aioresponses() def test_execute_sell_handles_errors(self, mock_api): self.simulate_trading_rules_initialization(mock_api) url = f"{CONSTANTS.REST_URL}{CONSTANTS.ORDERS_PATH_URL}" regex_url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?")) mock_api.post(regex_url, exception=RuntimeError) self.exchange.add_listener(MarketEvent.SellOrderCreated, self.event_listener) self.exchange.add_listener(MarketEvent.OrderFailure, self.event_listener) some_order_id = "someID" self.async_run_with_timeout( self.exchange.execute_sell( order_id=some_order_id, trading_pair=self.trading_pair, amount=Decimal("1"), order_type=OrderType.LIMIT, price=Decimal("2"), )) self.assertEqual(1, len(self.event_listener.event_log)) event = self.event_listener.event_log[0] self.assertIsInstance(event, MarketOrderFailureEvent) self.assertEqual(some_order_id, event.order_id) self.assertNotIn(some_order_id, self.exchange.in_flight_orders) @aioresponses() def test_execute_cancel(self, mock_api): self.simulate_trading_rules_initialization(mock_api) some_order_id = "someID" self.simulate_execute_buy_order(mock_api, some_order_id) url = f"{CONSTANTS.REST_URL}{CONSTANTS.ORDERS_PATH_URL}/{some_order_id}" resp = some_order_id mock_api.delete(url, body=json.dumps(resp)) self.exchange.add_listener(MarketEvent.OrderCancelled, self.event_listener) self.async_run_with_timeout( self.exchange.execute_cancel(self.trading_pair, some_order_id)) self.assertEqual(1, len(self.event_listener.event_log)) event = self.event_listener.event_log[0] self.assertIsInstance(event, OrderCancelledEvent) self.assertEqual(some_order_id, event.order_id) self.assertNotIn(some_order_id, self.exchange.in_flight_orders) @aioresponses() def test_execute_cancel_order_does_not_exist(self, mock_api): self.simulate_trading_rules_initialization(mock_api) some_order_id = "someID" self.simulate_execute_buy_order(mock_api, some_order_id) url = f"{CONSTANTS.REST_URL}{CONSTANTS.ORDERS_PATH_URL}/{some_order_id}" mock_api.delete(url, exception=IOError("order not found")) self.exchange.add_listener(MarketEvent.OrderCancelled, self.event_listener) self.async_run_with_timeout( self.exchange.execute_cancel(self.trading_pair, some_order_id)) self.assertEqual(1, len(self.event_listener.event_log)) event = self.event_listener.event_log[0] self.assertIsInstance(event, OrderCancelledEvent) self.assertEqual(some_order_id, event.order_id) self.assertNotIn(some_order_id, self.exchange.in_flight_orders) @aioresponses() def test_get_order(self, mock_api): self.simulate_trading_rules_initialization(mock_api) some_order_id = "someID" self.simulate_execute_buy_order(mock_api, some_order_id) url = f"{CONSTANTS.REST_URL}{CONSTANTS.ORDERS_PATH_URL}/{some_order_id}" resp = self.get_orders_response_mock(some_order_id) mock_api.get(url, body=json.dumps(resp))