async def listen_for_trades(self, ev_loop: asyncio.BaseEventLoop,
                             output: asyncio.Queue):
     """
     Listen for trades using websocket trade channel
     """
     while True:
         try:
             async with websockets.connect(
                     uri=constants.WSS_URL,
                     ping_timeout=self.PING_TIMEOUT) as ws:
                 ws: websockets.WebSocketClientProtocol = ws
                 for trading_pair in self._trading_pairs:
                     params: Dict[str, Any] = {
                         "name":
                         "SubscribeTrades",
                         "data":
                         k2_utils.convert_to_exchange_trading_pair(
                             trading_pair)
                     }
                     await ws.send(ujson.dumps(params))
                 async for raw_msg in self._inner_messages(ws):
                     msg = ujson.loads(raw_msg)
                     if msg["method"] != "marketchanged":
                         continue
                     for trade_entry in msg["data"]["trades"]:
                         trade_msg: OrderBookMessage = K2OrderBook.trade_message_from_exchange(
                             trade_entry)
                         output.put_nowait(trade_msg)
         except asyncio.CancelledError:
             raise
         except Exception:
             self.logger().error("Unexpected error.", exc_info=True)
             await asyncio.sleep(5.0)
         finally:
             await ws.close()
예제 #2
0
    async def cancel_all(self, timeout_seconds: float):
        """
        Cancels all in-flight orders and waits for cancellation results.
        Used by bot's top level stop and exit commands (cancelling outstanding orders on exit)
        :param timeout_seconds: The timeout at which the operation will be canceled.
        :returns List of CancellationResult which indicates whether each order is successfully cancelled.
        """
        if self._trading_pairs is None:
            raise Exception(
                "cancel_all can only be used when trading_pairs are specified."
            )
        cancellation_results = []

        for trading_pair in self._trading_pairs:
            order_ids = [
                await o.get_exchange_order_id()
                for _, o in self.in_flight_orders.items()
                if o.trading_pair == trading_pair
            ]

            api_params = {
                "symbol":
                k2_utils.convert_to_exchange_trading_pair(trading_pair),
                "orderids": ",".join(order_ids)
            }

            try:
                await self._api_request(method="POST",
                                        path_url=constants.CANCEL_ALL_ORDERS,
                                        params=api_params,
                                        is_auth_required=True)
                open_orders = await self.get_open_orders()
                for cl_order_id, tracked_order in self._in_flight_orders.items(
                ):
                    open_order = [
                        o for o in open_orders
                        if o.client_order_id == cl_order_id
                    ]
                    if not open_order:
                        cancellation_results.append(
                            CancellationResult(cl_order_id, True))
                        self.trigger_event(
                            MarketEvent.OrderCancelled,
                            OrderCancelledEvent(self.current_timestamp,
                                                cl_order_id))
                    else:
                        cancellation_results.append(
                            CancellationResult(cl_order_id, False))
            except Exception:
                self.logger().network(
                    "Failed to cancel all orders.",
                    exc_info=True,
                    app_warning_msg=
                    "Failed to cancel all orders on K2. Check API key and network connection."
                )
        return cancellation_results
 async def listen_for_order_book_diffs(self, ev_loop: asyncio.BaseEventLoop,
                                       output: asyncio.Queue):
     """
     Listen for orderbook diffs using websocket book channel
     """
     while True:
         async with websockets.connect(constants.WSS_URL) as ws:
             try:
                 ws: websockets.WebSocketClientProtocol = ws
                 for trading_pair in self._trading_pairs:
                     params: Dict[str, Any] = {
                         "name":
                         "SubscribeOrderBook",
                         "data":
                         k2_utils.convert_to_exchange_trading_pair(
                             trading_pair)
                     }
                     await ws.send(ujson.dumps(params))
                 async for raw_msg in self._inner_messages(ws):
                     response = ujson.loads(raw_msg)
                     timestamp = int(time.time() * 1e3)
                     if response["method"] == "SubscribeOrderBook":
                         trading_pair = k2_utils.convert_from_exchange_trading_pair(
                             response["pair"])
                         message: OrderBookMessage = K2OrderBook.snapshot_message_from_exchange(
                             msg=response,
                             timestamp=timestamp,
                             metadata={"trading_pair": trading_pair})
                     elif response["method"] == "orderbookchanged":
                         data = ujson.loads(response["data"])
                         trading_pair = k2_utils.convert_from_exchange_trading_pair(
                             data["pair"])
                         message: OrderBookMessage = K2OrderBook.diff_message_from_exchange(
                             msg=data,
                             timestamp=timestamp,
                             metadata={"trading_pair": trading_pair})
                     else:
                         # Ignores all other messages
                         continue
                     output.put_nowait(message)
             except asyncio.CancelledError:
                 raise
             except Exception:
                 self.logger().network(
                     "Unexpected error with WebSocket connection.",
                     exc_info=True,
                     app_warning_msg=
                     "Unexpected error with WebSocket connection. Retrying in 30 seconds. "
                     "Check network connection.")
                 await asyncio.sleep(30.0)
             finally:
                 await ws.close()
예제 #4
0
    async def get_order_book_data(trading_pair: str) -> Dict[str, any]:
        """
        Obtain orderbook using REST API
        """
        async with aiohttp.ClientSession() as client:
            params = {"symbol": k2_utils.convert_to_exchange_trading_pair(trading_pair)}
            async with client.get(url=f"{constants.REST_URL}{constants.GET_ORDER_BOOK}",
                                  params=params) as resp:
                if resp.status != 200:
                    raise IOError(
                        f"Error fetching OrderBook for {trading_pair} at {constants.EXCHANGE_NAME}. "
                        f"HTTP status is {resp.status}."
                    )

                orderbook_data: Dict[str, Any] = await resp.json()

        return orderbook_data
예제 #5
0
 async def get_last_traded_prices(cls, trading_pairs: List[str]) -> Dict[str, float]:
     result = {}
     async with aiohttp.ClientSession() as client:
         async with client.get(f"{constants.REST_URL}{constants.GET_TRADING_PAIRS_STATS}") as resp:
             resp_json = await resp.json()
             if resp_json["success"] is False:
                 raise IOError(
                     f"Error fetching last traded prices at {constants.EXCHANGE_NAME}. "
                     f"HTTP status is {resp.status}."
                     f"Content: {resp.content}"
                 )
             for t_pair in trading_pairs:
                 last_trade = [o["lastprice"]
                               for o in resp_json["data"] if o["symbol"] == k2_utils.convert_to_exchange_trading_pair(t_pair)]
                 if last_trade and last_trade[0] is not None:
                     result[t_pair] = last_trade[0]
     return result
예제 #6
0
    async def _create_order(self, trade_type: TradeType, order_id: str,
                            trading_pair: str, amount: Decimal,
                            order_type: OrderType, price: Decimal):
        """
        Calls create-order API end point to place an order, starts tracking the order and triggers order created event.
        :param trade_type: BUY or SELL
        :param order_id: Internal order id (also called client_order_id)
        :param trading_pair: The market to place order
        :param amount: The order amount (in base token value)
        :param order_type: The order type
        :param price: The order price
        """
        if not order_type.is_limit_type():
            raise Exception(f"Unsupported order type: {order_type}")

        trading_rule = self._trading_rules[trading_pair]
        amount = self.quantize_order_amount(trading_pair, amount)
        price = self.quantize_order_price(trading_pair, price)
        if amount < trading_rule.min_order_size:
            raise ValueError(
                f"Order amount {amount} is lower than the minimum order size "
                f"{trading_rule.min_order_size}.")

        api_params = {
            "tempid": order_id,
            "symbol": k2_utils.convert_to_exchange_trading_pair(trading_pair),
            "price": f"{price:f}",
            "quantity": f"{amount:f}",
            "type": trade_type.name.lower(
            ),  # `type` parameter here refers to Side i.e. Buy or Sell
        }

        self.start_tracking_order(order_id, None, trading_pair, trade_type,
                                  price, amount, order_type)
        try:
            order_result = await self._api_request(
                method="POST",
                path_url=constants.PLACE_ORDER,
                data=api_params,
                is_auth_required=True)
            exchange_order_id = str(order_result["data"]["orderids"][0]["id"])
            tracked_order = self._in_flight_orders.get(order_id)
            if tracked_order is not None:
                self.logger().info(
                    f"Created {order_type.name} {trade_type.name} order {order_id} for "
                    f"{amount} {trading_pair}.")
                tracked_order.update_exchange_order_id(exchange_order_id)

            event_tag = MarketEvent.BuyOrderCreated if trade_type is TradeType.BUY else MarketEvent.SellOrderCreated
            event_class = BuyOrderCreatedEvent if trade_type is TradeType.BUY else SellOrderCreatedEvent
            self.trigger_event(
                event_tag,
                event_class(self.current_timestamp, order_type, trading_pair,
                            amount, price, order_id))
        except asyncio.CancelledError:
            raise
        except Exception as e:
            self.stop_tracking_order(order_id)
            self.logger().network(
                f"Error submitting {trade_type.name} {order_type.name} order to K2 for "
                f"{amount} {trading_pair} "
                f"{price}.",
                exc_info=True,
                app_warning_msg=str(e))
            self.trigger_event(
                MarketEvent.OrderFailure,
                MarketOrderFailureEvent(self.current_timestamp, order_id,
                                        order_type))