Exemplo n.º 1
0
 async def _check_connection(self, *args, **kwargs) -> None:
     """Check Websocket connection, if connection closed, re-connect immediately."""
     if not self.ws:
         logger.warn("Websocket connection not connected yet!", caller=self)
         return
     if self.ws.closed:
         SingleTask.run(self.reconnect)
Exemplo n.º 2
0
    async def revoke_orders(self, symbol, order_ids=None, client_oids=None):
        """Cancelling multiple open orders with order_id,Maximum 10 orders can be cancelled at a time for each
            trading pair.

        Args:
            symbol: Trading pair, e.g. `BTC-USDT`.
            order_ids: Order id list, default is `None`.
            client_oids: Client order id list, default is `None`.

        Returns:
            success: Success results, otherwise it's None.
            error: Error information, otherwise it's None.

        Notes:
            `order_ids` and `order_oids` must exist one, using order_ids first.
        """
        uri = "/api/spot/v3/cancel_batch_orders"
        if order_ids:
            if len(order_ids) > 10:
                logger.warn("only revoke 10 orders per request!", caller=self)
            body = [{"instrument_id": symbol, "order_ids": order_ids[:10]}]
        elif client_oids:
            if len(client_oids) > 10:
                logger.warn("only revoke 10 orders per request!", caller=self)
            body = [{"instrument_id": symbol, "client_oids": client_oids[:10]}]
        else:
            return None, "order id list error!"
        result, error = await self.request("POST", uri, body=body, auth=True)
        return result, error
Exemplo n.º 3
0
    async def process(self, msg):
        """Process message that received from Websocket connection.

        Args:
            msg: message received from Websocket connection.
        """
        logger.debug("msg:", json.dumps(msg), caller=self)
        e = msg.get("e")
        if e == "executionReport":  # Order update.
            if msg["s"] != self._raw_symbol:
                return
            order_id = str(msg["i"])
            if msg["X"] == "NEW":
                status = ORDER_STATUS_SUBMITTED
            elif msg["X"] == "PARTIALLY_FILLED":
                status = ORDER_STATUS_PARTIAL_FILLED
            elif msg["X"] == "FILLED":
                status = ORDER_STATUS_FILLED
            elif msg["X"] == "CANCELED":
                status = ORDER_STATUS_CANCELED
            elif msg["X"] == "REJECTED":
                status = ORDER_STATUS_FAILED
            elif msg["X"] == "EXPIRED":
                status = ORDER_STATUS_FAILED
            else:
                logger.warn("unknown status:", msg, caller=self)
                SingleTask.run(self._error_callback, "order status error.")
                return
            order = self._orders.get(order_id)
            if not order:
                info = {
                    "platform": self._platform,
                    "account": self._account,
                    "strategy": self._strategy,
                    "order_id": order_id,
                    "client_order_id": msg["c"],
                    "action": ORDER_ACTION_BUY
                    if msg["S"] == "BUY" else ORDER_ACTION_SELL,
                    "order_type": ORDER_TYPE_LIMIT
                    if msg["o"] == "LIMIT" else ORDER_TYPE_MARKET,
                    "symbol": self._symbol,
                    "price": msg["p"],
                    "quantity": msg["q"],
                    "ctime": msg["O"]
                }
                order = Order(**info)
                self._orders[order_id] = order
            order.remain = float(msg["q"]) - float(msg["z"])
            order.status = status
            order.utime = msg["T"]

            SingleTask.run(self._order_update_callback, copy.copy(order))
            if status in [
                    ORDER_STATUS_FAILED, ORDER_STATUS_CANCELED,
                    ORDER_STATUS_FILLED
            ]:
                self._orders.pop(order_id)
Exemplo n.º 4
0
    async def revoke_order(self, *order_ids):
        """Revoke (an) order(s).

        Args:
            order_ids: Order id list, you can set this param to 0 or multiple items. If you set 0 param, you can cancel
                all orders for this symbol(initialized in Trade object). If you set 1 param, you can cancel an order.
                If you set multiple param, you can cancel multiple orders. Do not set param length more than 100.

        Returns:
            Success or error, see bellow.

        NOTEs:
            DO NOT INPUT MORE THAT 10 ORDER IDs, you can invoke many times.
        """
        # If len(order_ids) == 0, you will cancel all orders for this symbol(initialized in Trade object).
        if len(order_ids) == 0:
            order_infos, error = await self._rest_api.get_open_orders(
                self._raw_symbol)
            if error:
                SingleTask.run(self._error_callback, error)
                return False, error
            if len(order_infos) > 100:
                logger.warn("order length too long! (more than 100)",
                            caller=self)
            for order_info in order_infos:
                order_id = order_info["order_id"]
                _, error = await self._rest_api.revoke_order(
                    self._raw_symbol, order_id)
                if error:
                    SingleTask.run(self._error_callback, error)
                    return False, error
            return True, None

        # If len(order_ids) == 1, you will cancel an order.
        if len(order_ids) == 1:
            success, error = await self._rest_api.revoke_order(
                self._raw_symbol, order_ids[0])
            if error:
                SingleTask.run(self._error_callback, error)
                return order_ids[0], error
            else:
                return order_ids[0], None

        # If len(order_ids) > 1, you will cancel multiple orders.
        if len(order_ids) > 1:
            success, error = [], []
            for order_id in order_ids:
                _, e = await self._rest_api.revoke_order(
                    self._raw_symbol, order_id)
                if e:
                    SingleTask.run(self._error_callback, e)
                    error.append((order_id, e))
                else:
                    success.append(order_id)
            return success, error
Exemplo n.º 5
0
    async def publish(self, event):
        """Publish a event.

        Args:
            event: A event to publish.
        """
        if not self._connected:
            logger.warn("RabbitMQ not ready right now!", caller=self)
            return
        data = event.dumps()
        await self._channel.basic_publish(payload=data, exchange_name=event.exchange, routing_key=event.routing_key)
Exemplo n.º 6
0
    async def connected_callback(self):
        """After websocket connection created successfully, pull back all open order information."""
        logger.info("Websocket connection authorized successfully.", caller=self)
        order_infos, error = await self._rest_api.get_open_orders(self._raw_symbol)
        if error:
            e = Error("get open orders error: {}".format(error))
            SingleTask.run(self._error_callback, e)
            SingleTask.run(self._init_callback, False)
            return
        for order_info in order_infos:
            if order_info["status"] == "NEW":
                status = ORDER_STATUS_SUBMITTED
            elif order_info["status"] == "PARTIALLY_FILLED":
                status = ORDER_STATUS_PARTIAL_FILLED
            elif order_info["status"] == "FILLED":
                status = ORDER_STATUS_FILLED
            elif order_info["status"] == "CANCELED":
                status = ORDER_STATUS_CANCELED
            elif order_info["status"] == "REJECTED":
                status = ORDER_STATUS_FAILED
            elif order_info["status"] == "EXPIRED":
                status = ORDER_STATUS_FAILED
            else:
                logger.warn("unknown status:", order_info, caller=self)
                SingleTask.run(self._error_callback, "order status error.")
                continue

            order_id = str(order_info["orderId"])
            info = {
                "platform": self._platform,
                "account": self._account,
                "strategy": self._strategy,
                "order_id": order_id,
                "client_order_id": order_info["clientOrderId"],
                "action": ORDER_ACTION_BUY if order_info["side"] == "BUY" else ORDER_ACTION_SELL,
                "order_type": ORDER_TYPE_LIMIT if order_info["type"] == "LIMIT" else ORDER_TYPE_MARKET,
                "symbol": self._symbol,
                "price": order_info["price"],
                "quantity": order_info["origQty"],
                "remain": float(order_info["origQty"]) - float(order_info["executedQty"]),
                "status": status,
                "avg_price": order_info["price"],
                "ctime": order_info["time"],
                "utime": order_info["updateTime"]
            }
            order = Order(**info)
            self._orders[order_id] = order
            SingleTask.run(self._order_update_callback, copy.copy(order))

        SingleTask.run(self._init_callback, True)
    async def dynamic_trade_with_binance(self, *args, **kwargs):
        """简化版的吃盘口毛刺策略"""
        if not self._is_ok:
            return
        success, error = await self._trade.rest_api.get_orderbook(
            self._symbol, 10)
        if error:
            # 通过 钉钉、微信等发送通知...
            # 或 接入风控系统;
            self._is_ok = False
            logger.warn("error: ", error, caller=self)
            return

        ask6_price = float(success["asks"][5][0])
        ask8_price = float(success["asks"][7][0])
        average_price = round((ask6_price + ask8_price) / 2, 4)
        logger.info(f"the average price is {average_price}....", caller=self)

        await self.strategy_process(ask6_price, ask8_price, average_price)
Exemplo n.º 8
0
 async def _receive(self):
     """Receive stream message from Websocket connection."""
     async for msg in self.ws:
         if msg.type == aiohttp.WSMsgType.TEXT:
             if self._process_callback:
                 try:
                     data = json.loads(msg.data)
                 except:
                     data = msg.data
                 SingleTask.run(self._process_callback, data)
         elif msg.type == aiohttp.WSMsgType.BINARY:
             if self._process_binary_callback:
                 SingleTask.run(self._process_binary_callback, msg.data)
         elif msg.type == aiohttp.WSMsgType.CLOSED:
             logger.warn("receive event CLOSED:", msg, caller=self)
             SingleTask.run(self.reconnect)
         elif msg.type == aiohttp.WSMsgType.ERROR:
             logger.error("receive event ERROR:", msg, caller=self)
         else:
             logger.warn("unhandled msg:", msg, caller=self)
Exemplo n.º 9
0
    async def on_event_orderbook_update(self, orderbook: Orderbook):
        """订单薄更新回调"""

        if orderbook.platform == config.A["platform"] and orderbook.symbol == config.A["symbol"]:
            self._a_orderbook_ok = True
            self._a_orderbook = orderbook
        elif orderbook.platform == config.B["platform"] and orderbook.symbol == config.B["symbol"]:
            self._b_orderbook_ok = True
            self._b_orderbook = orderbook
        elif orderbook.platform == config.C["platform"] and orderbook.symbol == config.C["symbol"]:
            self._c_orderbook_ok = True
            self._c_orderbook = orderbook

        # 判断maker和taker订单薄是否准备就绪
        if not self._a_orderbook_ok or not self._b_orderbook_ok or not self._c_orderbook_ok:
            logger.warn("orderbook not ok.", caller=self)
            return

        # A同时进行买入和卖出ETH的检查
        SingleTask.run(self.a_do_action_buy)
        SingleTask.run(self.a_do_action_sell)
Exemplo n.º 10
0
    async def send(self, data) -> bool:
        """ Send message to Websocket server.

        Args:
            data: Message content, must be dict or string.

        Returns:
            If send successfully, return True, otherwise return False.
        """
        if not self.ws:
            logger.warn("Websocket connection not connected yet!", caller=self)
            return False
        if isinstance(data, dict):
            await self.ws.send_json(data)
        elif isinstance(data, str):
            await self.ws.send_str(data)
        else:
            logger.error("send message failed:", data, caller=self)
            return False
        logger.debug("send message:", data, caller=self)
        return True
Exemplo n.º 11
0
    async def get_open_order_ids(self):
        """Get open order id list.

        Args:
            None.

        Returns:
            order_ids: Open order id list, otherwise it's None.
            error: Error information, otherwise it's None.
        """
        success, error = await self._rest_api.get_open_orders(self._raw_symbol)
        if error:
            SingleTask.run(self._error_callback, error)
            return None, error
        else:
            if len(success) > 100:
                logger.warn("order length too long! (more than 100)",
                            caller=self)
            order_ids = []
            for order_info in success:
                order_ids.append(order_info["order_id"])
            return order_ids, None
Exemplo n.º 12
0
 async def reconnect(self) -> None:
     """Re-connect to Websocket server."""
     logger.warn("reconnecting to Websocket server right now!", caller=self)
     await self.close()
     await self._connect()
Exemplo n.º 13
0
    async def fetch(cls,
                    method,
                    url,
                    params=None,
                    body=None,
                    data=None,
                    headers=None,
                    timeout=30,
                    **kwargs):
        """ Create a HTTP request.

        Args:
            method: HTTP request method. `GET` / `POST` / `PUT` / `DELETE`
            url: Request url.
            params: HTTP query params.
            body: HTTP request body, string or bytes format.
            data: HTTP request body, dict format.
            headers: HTTP request header.
            timeout: HTTP request timeout(seconds), default is 30s.

            kwargs:
                proxy: HTTP proxy.

        Return:
            code: HTTP response code.
            success: HTTP response data. If something wrong, this field is None.
            error: If something wrong, this field will holding a Error information, otherwise it's None.

        Raises:
            HTTP request exceptions or response data parse exceptions. All the exceptions will be captured and return
            Error information.
        """
        session = cls._get_session(url)
        if not kwargs.get("proxy"):
            kwargs[
                "proxy"] = config.proxy  # If there is a `HTTP PROXY` Configuration in config file?
        try:
            if method == "GET":
                response = await session.get(url,
                                             params=params,
                                             headers=headers,
                                             timeout=timeout,
                                             **kwargs)
            elif method == "POST":
                response = await session.post(url,
                                              params=params,
                                              data=body,
                                              json=data,
                                              headers=headers,
                                              timeout=timeout,
                                              **kwargs)
            elif method == "PUT":
                response = await session.put(url,
                                             params=params,
                                             data=body,
                                             json=data,
                                             headers=headers,
                                             timeout=timeout,
                                             **kwargs)
            elif method == "DELETE":
                response = await session.delete(url,
                                                params=params,
                                                data=body,
                                                json=data,
                                                headers=headers,
                                                timeout=timeout,
                                                **kwargs)
            else:
                error = "http method error!"
                return None, None, error
        except Exception as e:
            logger.error("method:",
                         method,
                         "url:",
                         url,
                         "headers:",
                         headers,
                         "params:",
                         params,
                         "body:",
                         body,
                         "data:",
                         data,
                         "Error:",
                         e,
                         caller=cls)
            return None, None, e
        code = response.status
        if code not in (200, 201, 202, 203, 204, 205, 206):
            text = await response.text()
            logger.error("method:",
                         method,
                         "url:",
                         url,
                         "headers:",
                         headers,
                         "params:",
                         params,
                         "body:",
                         body,
                         "data:",
                         data,
                         "code:",
                         code,
                         "result:",
                         text,
                         caller=cls)
            return code, None, text
        try:
            result = await response.json()
        except:
            result = await response.text()
            logger.warn("response data is not json format!",
                        "method:",
                        method,
                        "url:",
                        url,
                        "headers:",
                        headers,
                        "params:",
                        params,
                        "body:",
                        body,
                        "data:",
                        data,
                        "code:",
                        code,
                        "result:",
                        result,
                        caller=cls)
        logger.debug("method:",
                     method,
                     "url:",
                     url,
                     "headers:",
                     headers,
                     "params:",
                     params,
                     "body:",
                     body,
                     "data:",
                     data,
                     "code:",
                     code,
                     "result:",
                     json.dumps(result),
                     caller=cls)
        return code, result, None
Exemplo n.º 14
0
    async def process_binary(self, raw):
        """Process binary message that received from websocket.

        Args:
            raw: Binary message received from websocket.

        Returns:
            None.
        """
        decompress = zlib.decompressobj(-zlib.MAX_WBITS)
        msg = decompress.decompress(raw)
        msg += decompress.flush()
        msg = msg.decode()
        if msg == "pong":
            return
        logger.debug("msg:", msg, caller=self)
        msg = json.loads(msg)

        # Authorization message received.
        if msg.get("event") == "login":
            if not msg.get("success"):
                e = Error(
                    "Websocket connection authorized failed: {}".format(msg))
                logger.error(e, caller=self)
                SingleTask.run(self._error_callback, e)
                SingleTask.run(self._init_callback, False)
                return
            logger.info("Websocket connection authorized successfully.",
                        caller=self)

            # Fetch orders from server. (open + partially filled)
            order_infos, error = await self._rest_api.get_open_orders(
                self._raw_symbol)
            if error:
                e = Error("get open orders error: {}".format(msg))
                SingleTask.run(self._error_callback, e)
                SingleTask.run(self._init_callback, False)
                return
            if len(order_infos) > 100:
                logger.warn("order length too long! (more than 100)",
                            caller=self)
            for order_info in order_infos:
                order_info["ctime"] = order_info["created_at"]
                order_info["utime"] = order_info["timestamp"]
                self._update_order(order_info)

            # Subscribe order channel.
            data = {"op": "subscribe", "args": [self._order_channel]}
            await self._ws.send(data)
            return

        # Subscribe response message received.
        if msg.get("event") == "subscribe":
            if msg.get("channel") == self._order_channel:
                SingleTask.run(self._init_callback, True)
            else:
                e = Error("subscribe order event error: {}".format(msg))
                SingleTask.run(self._error_callback, e)
                SingleTask.run(self._init_callback, False)
            return

        # Order update message received.
        if msg.get("table") == "spot/order":
            for data in msg["data"]:
                data["ctime"] = data["timestamp"]
                data["utime"] = data["last_fill_time"]
                self._update_order(data)