def test_get_new_order_book(self, mock_api): mock_response = { "code": 200, "data": { "asks": [{ "price": "57974.06", "quantity": "0.247421" }], "bids": [{ "price": "57974.01", "quantity": "0.201635" }], "version": "562370278" } } trading_pair = convert_to_exchange_trading_pair(self.trading_pair) tick_url = CONSTANTS.MEXC_DEPTH_URL.format(trading_pair=trading_pair) url = CONSTANTS.MEXC_BASE_URL + tick_url mock_api.get(url, body=json.dumps(mock_response)) results = self.async_run_with_timeout( asyncio.gather( self.data_source.get_new_order_book(self.trading_pair))) result: OrderBook = results[0] self.assertTrue(type(result) == OrderBook)
async def place_order(self, order_id: str, trading_pair: str, amount: Decimal, is_buy: bool, order_type: OrderType, price: Decimal) -> str: if order_type is OrderType.LIMIT: order_type_str = "LIMIT_ORDER" elif order_type is OrderType.LIMIT_MAKER: order_type_str = "POST_ONLY" data = { 'client_order_id': order_id, 'order_type': order_type_str, 'trade_type': "BID" if is_buy else "ASK", 'symbol': convert_to_exchange_trading_pair(trading_pair), 'quantity': str(amount), 'price': str(price) } exchange_order_id = await self._api_request( "POST", path_url=CONSTANTS.MEXC_PLACE_ORDER, params={}, data=data, is_auth_required=True) return str(exchange_order_id.get('data'))
async def get_last_traded_prices( cls, trading_pairs: List[str], throttler: Optional[AsyncThrottler] = None, shared_client: Optional[aiohttp.ClientSession] = None ) -> Dict[str, float]: client = shared_client or cls._get_session_instance() throttler = throttler or cls._get_throttler_instance() async with throttler.execute_task(CONSTANTS.MEXC_TICKERS_URL): url = CONSTANTS.MEXC_BASE_URL + CONSTANTS.MEXC_TICKERS_URL async with client.get(url) as products_response: products_response: aiohttp.ClientResponse = products_response if products_response.status != 200: # raise IOError(f"Error get tickers from MEXC markets. HTTP status is {products_response.status}.") return {} data = await products_response.json() data = data['data'] all_markets: pd.DataFrame = pd.DataFrame.from_records( data=data) all_markets.set_index("symbol", inplace=True) out: Dict[str, float] = {} for trading_pair in trading_pairs: exchange_trading_pair = convert_to_exchange_trading_pair( trading_pair) out[trading_pair] = float( all_markets['last'][exchange_trading_pair]) return out
def test_get_order_book_data(self, mock_api, ms_mock): ms_mock.return_value = 1 mock_response = { "code": 200, "data": { "asks": [{ "price": "57974.06", "quantity": "0.247421" }], "bids": [{ "price": "57974.01", "quantity": "0.201635" }], "ts": 1, "version": "562370278" } } trading_pair = convert_to_exchange_trading_pair(self.trading_pair) tick_url = CONSTANTS.MEXC_DEPTH_URL.format(trading_pair=trading_pair) url = CONSTANTS.MEXC_BASE_URL + tick_url mock_api.get(url, body=json.dumps(mock_response)) results = self.async_run_with_timeout( asyncio.gather( self.data_source.get_snapshot(self.data_source._shared_client, self.trading_pair))) result = results[0] self.assertTrue("asks" in result) self.assertGreaterEqual(len(result), 0) self.assertEqual(mock_response.get("data"), result)
def test_get_order_book_data_raises_exception_when_response_has_error_code(self, mock_api): mock_response = "Erroneous response" trading_pair = convert_to_exchange_trading_pair(self.trading_pair) tick_url = CONSTANTS.MEXC_DEPTH_URL.format(trading_pair=trading_pair) url = CONSTANTS.MEXC_BASE_URL + tick_url mock_api.get(url, body=json.dumps(mock_response), status=100) with self.assertRaises(IOError) as context: self.async_run_with_timeout(self.data_source.get_snapshot(self.data_source._shared_client, self.trading_pair)) self.assertEqual(str(context.exception), f'Error fetching MEXC market snapshot for {self.trading_pair.replace("-", "_")}. ' f'HTTP status is {100}.')
def test_can_get_price_after_order_book_init(self, mock_api): trading_pair = convert_to_exchange_trading_pair(self.trading_pair) tick_url = CONSTANTS.MEXC_DEPTH_URL.format(trading_pair=trading_pair) url = CONSTANTS.MEXC_BASE_URL + tick_url mock_api.get(url, body=json.dumps(self.mock_data)) init_order_books_task = self.ev_loop.create_task( self.tracker._init_order_books() ) self.async_run_with_timeout(init_order_books_task) ob = self.tracker.order_books[self.trading_pair] ask_price = ob.get_price(True) self.assertEqual(Decimal("56454.0"), ask_price)
def test_listen_for_snapshots_cancelled_when_fetching_snapshot(self, mock_api): trading_pair = convert_to_exchange_trading_pair(self.trading_pair) tick_url = CONSTANTS.MEXC_DEPTH_URL.format(trading_pair=trading_pair) url = CONSTANTS.MEXC_BASE_URL + tick_url mock_api.get(url, exception=asyncio.CancelledError) msg_queue: asyncio.Queue = asyncio.Queue() with self.assertRaises(asyncio.CancelledError): self.listening_task = self.ev_loop.create_task( self.data_source.listen_for_order_book_snapshots(self.ev_loop, msg_queue) ) self.async_run_with_timeout(self.listening_task) self.assertEqual(msg_queue.qsize(), 0)
def test_listen_for_snapshots_successful(self, mock_api, mock_sleep): # the queue and the division by zero error are used just to synchronize the test sync_queue = deque() sync_queue.append(1) mock_response = {"code": 200, "data": {"asks": [{"price": "57974.06", "quantity": "0.247421"}], "bids": [{"price": "57974.01", "quantity": "0.201635"}], "version": "562370278"}} trading_pair = convert_to_exchange_trading_pair(self.trading_pair) tick_url = CONSTANTS.MEXC_DEPTH_URL.format(trading_pair=trading_pair) url = CONSTANTS.MEXC_BASE_URL + tick_url mock_api.get(url, body=json.dumps(mock_response)) mock_sleep.side_effect = lambda delay: 1 / 0 if len(sync_queue) == 0 else sync_queue.pop() msg_queue: asyncio.Queue = asyncio.Queue() with self.assertRaises(ZeroDivisionError): self.listening_task = self.ev_loop.create_task( self.data_source.listen_for_order_book_snapshots(self.ev_loop, msg_queue)) self.async_run_with_timeout(self.data_source.listen_for_order_book_snapshots(self.ev_loop, msg_queue)) self.assertEqual(msg_queue.qsize(), 1)
async def get_snapshot( client: aiohttp.ClientSession, trading_pair: str, throttler: Optional[AsyncThrottler] = None) -> Dict[str, Any]: throttler = throttler or MexcAPIOrderBookDataSource._get_throttler_instance( ) async with throttler.execute_task(CONSTANTS.MEXC_DEPTH_URL): trading_pair = convert_to_exchange_trading_pair(trading_pair) tick_url = CONSTANTS.MEXC_DEPTH_URL.format( trading_pair=trading_pair) url = CONSTANTS.MEXC_BASE_URL + tick_url async with client.get(url) as response: response: aiohttp.ClientResponse = response status = response.status if status != 200: raise IOError( f"Error fetching MEXC market snapshot for {trading_pair}. " f"HTTP status is {status}.") api_data = await response.json() data = api_data['data'] data['ts'] = microseconds() return data
def test_init_order_books(self, mock_api): trading_pair = convert_to_exchange_trading_pair(self.trading_pair) tick_url = CONSTANTS.MEXC_DEPTH_URL.format(trading_pair=trading_pair) url = CONSTANTS.MEXC_BASE_URL + tick_url mock_api.get(url, body=json.dumps(self.mock_data)) self.tracker._order_books_initialized.clear() self.tracker._tracking_message_queues.clear() self.tracker._tracking_tasks.clear() self.tracker._order_books.clear() self.assertEqual(0, len(self.tracker.order_books)) self.assertEqual(0, len(self.tracker._tracking_message_queues)) self.assertEqual(0, len(self.tracker._tracking_tasks)) self.assertFalse(self.tracker._order_books_initialized.is_set()) init_order_books_task = self.ev_loop.create_task( self.tracker._init_order_books() ) self.async_run_with_timeout(init_order_books_task) self.assertIsInstance(self.tracker.order_books[self.trading_pair], OrderBook) self.assertTrue(self.tracker._order_books_initialized.is_set())
async def subscribe_to_order_book_streams(self, trading_pairs: List[str]): try: for trading_pair in trading_pairs: trading_pair = mexc_utils.convert_to_exchange_trading_pair(trading_pair) subscribe_deal_request: Dict[str, Any] = { "op": "sub.deal", "symbol": trading_pair, } async with self._throttler.execute_task(CONSTANTS.MEXC_WS_URL_PUBLIC): await self.send_request_str(json.dumps(subscribe_deal_request)) subscribe_depth_request: Dict[str, Any] = { "op": "sub.depth", "symbol": trading_pair, } async with self._throttler.execute_task(CONSTANTS.MEXC_WS_URL_PUBLIC): await self.send_request_str(json.dumps(subscribe_depth_request)) except asyncio.CancelledError: raise except Exception: self.logger().error( "Unexpected error occurred subscribing to order book trading and delta streams...", exc_info=True ) raise