コード例 #1
0
    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)
コード例 #2
0
    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.")
コード例 #3
0
    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()
コード例 #4
0
    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)
コード例 #5
0
    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.")
コード例 #6
0
    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)
コード例 #7
0
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))
コード例 #8
0
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))