async def fetch_trading_pairs() -> List[str]:
        async with aiohttp.ClientSession() as client:
            resp = await client.get(f"{REST_URL}/ticker")

            if resp.status != 200:
                # Do nothing if the request fails -- there will be no autocomplete for kucoin trading pairs
                return []

            data: Dict[str, Dict[str, Any]] = await resp.json()
            return [convert_from_exchange_trading_pair(item["symbol"]) for item in data["data"]]
Esempio n. 2
0
    async def fetch_trading_pairs(client: Optional[aiohttp.ClientSession] = None, throttler: Optional[AsyncThrottler] = None) -> List[str]:
        client = client or AscendExAPIOrderBookDataSource._get_session_instance()
        throttler = throttler or AscendExAPIOrderBookDataSource._get_throttler_instance()
        async with throttler.execute_task(CONSTANTS.TICKER_PATH_URL):
            resp = await client.get(f"{CONSTANTS.REST_URL}/{CONSTANTS.TICKER_PATH_URL}")

        if resp.status != 200:
            # Do nothing if the request fails -- there will be no autocomplete for kucoin trading pairs
            return []

        data: Dict[str, Dict[str, Any]] = await resp.json()
        return [convert_from_exchange_trading_pair(item["symbol"]) for item in data["data"]]
 def _format_trading_rules(
         self,
         instruments_info: Dict[str,
                                Any]) -> Dict[str, AscendExTradingRule]:
     """
     Converts json API response into a dictionary of trading rules.
     :param instruments_info: The json API response
     :return A dictionary of trading rules.
     Response Example:
     {
         "code": 0,
         "data": [
             {
                 "symbol":                "BTMX/USDT",
                 "baseAsset":             "BTMX",
                 "quoteAsset":            "USDT",
                 "status":                "Normal",
                 "minNotional":           "5",
                 "maxNotional":           "100000",
                 "marginTradable":         true,
                 "commissionType":        "Quote",
                 "commissionReserveRate": "0.001",
                 "tickSize":              "0.000001",
                 "lotSize":               "0.001"
             }
         ]
     }
     """
     trading_rules = {}
     for rule in instruments_info["data"]:
         try:
             trading_pair = ascend_ex_utils.convert_from_exchange_trading_pair(
                 rule["symbol"])
             trading_rules[trading_pair] = AscendExTradingRule(
                 trading_pair,
                 min_price_increment=Decimal(rule["tickSize"]),
                 min_base_amount_increment=Decimal(rule["lotSize"]),
                 min_notional_size=Decimal(rule["minNotional"]),
                 max_notional_size=Decimal(rule["maxNotional"]),
                 commission_type=AscendExCommissionType[
                     rule["commissionType"].upper()],
                 commission_reserve_rate=Decimal(
                     rule["commissionReserveRate"]),
             )
         except Exception:
             self.logger().error(
                 f"Error parsing the trading pair rule {rule}. Skipping.",
                 exc_info=True)
     return trading_rules
Esempio n. 4
0
    async def listen_for_order_book_diffs(self, ev_loop: asyncio.BaseEventLoop,
                                          output: asyncio.Queue):
        while True:
            try:
                trading_pairs = ",".join(
                    list(
                        map(
                            lambda trading_pair:
                            convert_to_exchange_trading_pair(trading_pair),
                            self._trading_pairs)))
                ch = f"depth:{trading_pairs}"
                payload = {"op": CONSTANTS.SUB_ENDPOINT_NAME, "ch": ch}

                async with websockets.connect(CONSTANTS.WS_URL) as ws:
                    ws: websockets.WebSocketClientProtocol = ws
                    async with self._throttler.execute_task(
                            CONSTANTS.SUB_ENDPOINT_NAME):
                        await ws.send(ujson.dumps(payload))

                    async for raw_msg in self._inner_messages(ws):
                        msg = ujson.loads(raw_msg)
                        if msg is None:
                            continue
                        if msg.get("m", '') == "ping":
                            async with self._throttler.execute_task(
                                    CONSTANTS.PONG_ENDPOINT_NAME):
                                await ws.send(
                                    ujson.dumps(
                                        dict(op=CONSTANTS.PONG_ENDPOINT_NAME)))
                        if msg.get("m", '') == "depth":
                            msg_timestamp: int = msg.get("data").get("ts")
                            trading_pair: str = convert_from_exchange_trading_pair(
                                msg.get("symbol"))
                            order_book_message: OrderBookMessage = AscendExOrderBook.diff_message_from_exchange(
                                msg.get("data"),
                                msg_timestamp,
                                metadata={"trading_pair": trading_pair})
                            output.put_nowait(order_book_message)
            except asyncio.CancelledError:
                raise
            except Exception as e:
                self.logger().debug(str(e))
                self.logger().error(
                    "Unexpected error with WebSocket connection. Retrying after 30 seconds...",
                    exc_info=True)
                await asyncio.sleep(30.0)
Esempio n. 5
0
    async def listen_for_trades(self, ev_loop: asyncio.BaseEventLoop,
                                output: asyncio.Queue):
        while True:
            try:
                trading_pairs = ",".join(
                    list(
                        map(
                            lambda trading_pair:
                            convert_to_exchange_trading_pair(trading_pair),
                            self._trading_pairs)))
                payload = {"op": "sub", "ch": f"trades:{trading_pairs}"}

                async with websockets.connect(WS_URL) as ws:
                    ws: websockets.WebSocketClientProtocol = ws
                    await ws.send(ujson.dumps(payload))

                    async for raw_msg in self._inner_messages(ws):
                        try:
                            msg = ujson.loads(raw_msg)
                            if (msg is None or msg.get("m") != "trades"):
                                continue

                            trading_pair: str = convert_from_exchange_trading_pair(
                                msg.get("symbol"))

                            for trade in msg.get("data"):
                                trade_timestamp: int = trade.get("ts")
                                trade_msg: OrderBookMessage = AscendExOrderBook.trade_message_from_exchange(
                                    trade,
                                    trade_timestamp,
                                    metadata={"trading_pair": trading_pair})
                                output.put_nowait(trade_msg)
                        except Exception:
                            raise
            except asyncio.CancelledError:
                raise
            except Exception as e:
                self.logger().debug(str(e))
                self.logger().error(
                    "Unexpected error with WebSocket connection. Retrying after 30 seconds...",
                    exc_info=True)
                await asyncio.sleep(30.0)
Esempio n. 6
0
 async def listen_for_order_book_diffs(self, ev_loop: asyncio.BaseEventLoop, output: asyncio.Queue):
     msg_queue = self._message_queue[self.DIFF_TOPIC_ID]
     while True:
         try:
             msg = await msg_queue.get()
             msg_timestamp: int = msg.get("data").get("ts")
             trading_pair: str = convert_from_exchange_trading_pair(msg.get("symbol"))
             order_book_message: OrderBookMessage = AscendExOrderBook.diff_message_from_exchange(
                 msg.get("data"),
                 msg_timestamp,
                 metadata={"trading_pair": trading_pair}
             )
             output.put_nowait(order_book_message)
         except asyncio.CancelledError:
             raise
         except Exception as e:
             self.logger().debug(str(e))
             self.logger().error("Unexpected error with WebSocket connection. Retrying after 30 seconds...",
                                 exc_info=True)
             await self._sleep(30.0)
Esempio n. 7
0
    async def listen_for_trades(self, ev_loop: asyncio.BaseEventLoop, output: asyncio.Queue):
        msg_queue = self._message_queue[self.TRADE_TOPIC_ID]
        while True:
            try:
                msg = await msg_queue.get()
                trading_pair: str = convert_from_exchange_trading_pair(msg.get("symbol"))
                trades = msg.get("data")

                for trade in trades:
                    trade_timestamp: int = trade.get("ts")
                    trade_msg: OrderBookMessage = AscendExOrderBook.trade_message_from_exchange(
                        trade,
                        trade_timestamp,
                        metadata={"trading_pair": trading_pair}
                    )
                    output.put_nowait(trade_msg)
            except asyncio.CancelledError:
                raise
            except Exception:
                self.logger().error("Unexpected error with WebSocket connection. Retrying after 30 seconds...",
                                    exc_info=True)
Esempio n. 8
0
    async def get_open_orders(self) -> List[OpenOrder]:
        result = await self._api_request(
            method="get",
            path_url=CONSTANTS.ORDER_OPEN_PATH_URL,
            is_auth_required=True,
            force_auth_path_url="order/open"
        )
        ret_val = []
        for order in result["data"]:
            if order["orderType"].lower() != "limit":
                self.logger().debug(f"Unsupported orderType: {order['orderType']}. Order: {order}",
                                    exc_info=True)
                continue

            exchange_order_id = order["orderId"]
            client_order_id = None
            for in_flight_order in self._in_flight_orders.values():
                if in_flight_order.exchange_order_id == exchange_order_id:
                    client_order_id = in_flight_order.client_order_id

            if client_order_id is None:
                self.logger().debug(f"Unrecognized Order {exchange_order_id}: {order}")
                continue

            ret_val.append(
                OpenOrder(
                    client_order_id=client_order_id,
                    trading_pair=ascend_ex_utils.convert_from_exchange_trading_pair(order["symbol"]),
                    price=Decimal(str(order["price"])),
                    amount=Decimal(str(order["orderQty"])),
                    executed_amount=Decimal(str(order["cumFilledQty"])),
                    status=order["status"],
                    order_type=OrderType.LIMIT,
                    is_buy=True if order["side"].lower() == "buy" else False,
                    time=int(order["lastExecTime"]),
                    exchange_order_id=exchange_order_id
                )
            )
        return ret_val
Esempio n. 9
0
    async def get_open_orders(self) -> List[OpenOrder]:
        result = await self._api_request(
            "get",
            "cash/order/open",
            {},
            True,
            force_auth_path_url="order/open"
        )
        ret_val = []
        for order in result["data"]:
            if order["type"] != "LIMIT":
                raise Exception(f"Unsupported order type {order['type']}")

            exchange_order_id = order["orderId"]
            client_order_id = None
            for in_flight_order in self._in_flight_orders.values():
                if in_flight_order.exchange_order_id == exchange_order_id:
                    client_order_id = in_flight_order.client_order_id

            if client_order_id is None:
                raise Exception(f"Client order id for {exchange_order_id} not found.")

            ret_val.append(
                OpenOrder(
                    client_order_id=client_order_id,
                    trading_pair=ascend_ex_utils.convert_from_exchange_trading_pair(order["symbol"]),
                    price=Decimal(str(order["price"])),
                    amount=Decimal(str(order["orderQty"])),
                    executed_amount=Decimal(str(order["cumFilledQty"])),
                    status=order["status"],
                    order_type=OrderType.LIMIT,
                    is_buy=True if order["side"].lower() == "buy" else False,
                    time=int(order["lastExecTime"]),
                    exchange_order_id=exchange_order_id
                )
            )
        return ret_val
Esempio n. 10
0
 def test_convert_from_exchange_trading_pair(self):
     trading_pair = "BTC/USDT"
     self.assertEqual("BTC-USDT", utils.convert_from_exchange_trading_pair(trading_pair))
Esempio n. 11
0
    async def _update_order_status(self):
        """
        Calls REST API to get status update for each in-flight order.
        """

        if len(self._in_flight_order_tracker.active_orders) == 0:
            return

        tracked_orders: List[InFlightOrder] = list(self._in_flight_order_tracker.active_orders.values())

        ex_oid_to_c_oid_map: Dict[str, str] = {}
        # Check a second time the order is not done because it can be updated in other async process
        for order in (tracked_order for tracked_order in tracked_orders if not tracked_order.is_done):
            try:
                exchange_id = await order.get_exchange_order_id()
                ex_oid_to_c_oid_map[exchange_id] = order.client_order_id
            except asyncio.TimeoutError:
                self.logger().debug(
                    f"Tracked order {order.client_order_id} does not have an exchange id. "
                    f"Attempting fetch in next polling interval."
                )
                self._stop_tracking_order_exceed_no_exchange_id_limit(tracked_order=order)
                continue

        if ex_oid_to_c_oid_map:
            exchange_order_ids_param_str: str = ",".join(list(ex_oid_to_c_oid_map.keys()))
            params = {"orderId": exchange_order_ids_param_str}
            try:
                resp = await self._api_request(
                    method="get",
                    path_url=CONSTANTS.ORDER_STATUS_PATH_URL,
                    params=params,
                    is_auth_required=True,
                    force_auth_path_url="order/status",
                )
            except Exception:
                self.logger().exception(
                    f"There was an error requesting updates for the active orders ({ex_oid_to_c_oid_map})")
                raise
            self.logger().debug(f"Polling for order status updates of {len(ex_oid_to_c_oid_map)} orders.")
            self.logger().debug(f"cash/order/status?orderId={exchange_order_ids_param_str} response: {resp}")
            # The data returned from this end point can be either a list or a dict depending on number of orders
            resp_records: List = []
            if isinstance(resp["data"], dict):
                resp_records.append(resp["data"])
            elif isinstance(resp["data"], list):
                resp_records = resp["data"]

            order_updates: List[OrderUpdate] = []
            try:
                for order_data in resp_records:
                    exchange_order_id = order_data["orderId"]
                    client_order_id = ex_oid_to_c_oid_map[exchange_order_id]
                    new_state: OrderState = CONSTANTS.ORDER_STATE[order_data["status"]]
                    order_updates.append(
                        OrderUpdate(
                            client_order_id=client_order_id,
                            exchange_order_id=exchange_order_id,
                            trading_pair=ascend_ex_utils.convert_from_exchange_trading_pair(order_data["symbol"]),
                            update_timestamp=order_data["lastExecTime"] * 1e-3,
                            new_state=new_state,
                        )
                    )
                for update in order_updates:
                    self._in_flight_order_tracker.process_order_update(update)

            except Exception:
                self.logger().info(
                    f"Unexpected error during processing order status. The Ascend Ex Response: {resp}", exc_info=True
                )