async def check_asset_update(self, *args, **kwargs): """Fetch asset information.""" result, error = await self._rest_api.get_user_account() if error: logger.warn("platform:", self._platform, "account:", self._account, "get asset info failed!", caller=self) return assets = {} for item in result["info"]: symbol = item["instrument_id"].split("-")[0] total = float(item["equity"]) locked = float(item["margin"]) if total > 0: assets[symbol] = { "total": "%.8f" % total, "free": "%.8f" % (total - locked), "locked": "%.8f" % locked } if assets == self._assets: update = False else: update = True self._assets = assets # Publish AssetEvent. timestamp = tools.get_cur_timestamp_ms() EventAsset(self._platform, self._account, self._assets, timestamp, update).publish() logger.info("platform:", self._platform, "account:", self._account, "asset:", self._assets, caller=self)
async def process_binary(self, raw): """ 处理websocket上接收到的消息 @param raw 原始的压缩数据 """ decompress = zlib.decompressobj(-zlib.MAX_WBITS) msg = decompress.decompress(raw) msg += decompress.flush() msg = msg.decode() if msg == "pong": # 心跳返回 return msg = json.loads(msg) # logger.debug("msg:", msg, caller=self) table = msg.get("table") if table == "spot/depth": # 订单薄 if msg.get("action") == "partial": # 首次返回全量数据 for d in msg["data"]: await self.deal_orderbook_partial(d) elif msg.get("action") == "update": # 返回增量数据 for d in msg["data"]: await self.deal_orderbook_update(d) else: logger.warn("unhandle msg:", msg, caller=self) elif table == "spot/trade": for d in msg["data"]: await self.deal_trade_update(d) elif table == "spot/candle60s": for d in msg["data"]: await self.deal_kline_update(d) else: logger.warn("unhandle msg:", msg, caller=self)
async def connected_callback(self): """After create connection to Websocket server successfully, we will subscribe orderbook/trade/kline event.""" if not self._symbols: logger.warn("symbols not found in config file.", caller=self) return events = [] for channel in self._channels: if channel == "orderbook": events.append("order_book") elif channel == "trade": events.append("trade") if not events: logger.warn("channels not found in config file.", caller=self) return nonce = tools.get_cur_timestamp_ms() uri = "/api/v1/private/subscribe" params = {"instrument": self._symbols, "event": events} sign = self.deribit_signature(nonce, uri, params, self._access_key, self._secret_key) data = { "id": "thenextquant", "action": uri, "arguments": params, "sig": sign } await self._ws.send(data) logger.info("subscribe orderbook success.", caller=self)
async def check_asset_update(self, *args, **kwargs): """Fetch asset information.""" result, error = await self._rest_api.get_user_account() if error: logger.warn("platform:", self._platform, "account:", self._account, "get asset info failed!", caller=self) return assets = {} for item in result: symbol = item["currency"] total = float(item["balance"]) free = float(item["available"]) locked = float(item["frozen"]) if total > 0: assets[symbol] = { "total": "%.8f" % total, "free": "%.8f" % free, "locked": "%.8f" % locked } if assets == self._assets: update = False else: update = True self._assets = assets # Publish AssetEvent. timestamp = tools.get_cur_timestamp_ms() EventAsset(self._platform, self._account, self._assets, timestamp, update).publish() logger.info("platform:", self._platform, "account:", self._account, "asset:", self._assets, caller=self)
async def connected_callback(self): """After create Websocket connection successfully, we will subscribing orderbook/trade/kline events.""" if not self._symbols: logger.warn("symbols not found in config file.", caller=self) return if not self._channels: logger.warn("channels not found in config file.", caller=self) return for ch in self._channels: if ch == "trade": params = [] for s in self._symbols: params.append(s.replace("/", "_")) request_id = await self._generate_request_id() d = {"id": request_id, "method": "trades.subscribe", "params": params} await self._ws.send(d) logger.info("subscribe trade success.", caller=self) elif ch == "orderbook": await self.subscribe_orderbook() elif ch == "kline": for s in self._symbols: params = [s.replace("/", "_"), 60] request_id = await self._generate_request_id() d = {"id": request_id, "method": "kline.subscribe", "params": params} await self._ws.send(d) logger.info("subscribe kline success.", caller=self) else: logger.error("channel error:", ch, caller=self) continue
async def process(self, msg): """ 处理websocket上接收到的消息 """ # logger.debug("msg:", msg, caller=self) if not isinstance(msg, dict): return channel = msg.get("stream") if channel not in self._c_to_s: logger.warn("unkown channel, msg:", msg, caller=self) return symbol = self._c_to_s[channel] data = msg.get("data") e = data.get("e") # 事件名称 # 保存数据到数据库 if e == "kline": # K线 kline = { "platform": self._platform, "symbol": symbol, "open": data.get("k").get("o"), # 开盘价 "high": data.get("k").get("h"), # 最高价 "low": data.get("k").get("l"), # 最低价 "close": data.get("k").get("c"), # 收盘价 "volume": data.get("k").get("q"), # 交易量 "timestamp": data.get("k").get("t"), # 时间戳 "kline_type": const.MARKET_TYPE_KLINE } EventKline(**kline).publish() logger.info("symbol:", symbol, "kline:", kline, caller=self) elif channel.endswith("depth20"): # 订单薄 bids = [] asks = [] for bid in data.get("bids"): bids.append(bid[:2]) for ask in data.get("asks"): asks.append(ask[:2]) orderbook = { "platform": self._platform, "symbol": symbol, "asks": asks, "bids": bids, "timestamp": tools.get_cur_timestamp_ms() } EventOrderbook(**orderbook).publish() logger.info("symbol:", symbol, "orderbook:", orderbook, caller=self) elif e == "trade": # 实时成交信息 trade = { "platform": self._platform, "symbol": symbol, "action": ORDER_ACTION_SELL if data["m"] else ORDER_ACTION_BUY, "price": data.get("p"), "quantity": data.get("q"), "timestamp": data.get("T") } EventTrade(**trade).publish() logger.info("symbol:", symbol, "trade:", trade, caller=self) else: logger.error("event error! msg:", msg, caller=self)
async def connected_callback(self): """ After create Websocket connection successfully, we will subscribing orderbook/trade events. """ symbols = [] for s in self._symbols: t = s.replace("/", "-") symbols.append(t) self._symbols_map[t] = s if not symbols: logger.warn("symbols not found in config file.", caller=self) return if not self._channels: logger.warn("channels not found in config file.", caller=self) return channels = [] for ch in self._channels: if ch == "orderbook": sub = {"name": "level2", "product_ids": symbols} channels.append(sub) elif ch == "trade": sub = {"name": "ticker", "product_ids": symbols} channels.append(sub) else: logger.error("channel error! channel:", ch, caller=self) if channels: msg = {"type": "subscribe", "channels": channels} await self._ws.send(msg) logger.info("subscribe orderbook/trade success.", caller=self)
async def process_binary(self, raw): """ Process binary message that received from Websocket connection. Args: raw: Raw message that received from Websocket connection. """ decompress = zlib.decompressobj(-zlib.MAX_WBITS) msg = decompress.decompress(raw) msg += decompress.flush() msg = msg.decode() # logger.debug("msg:", msg, caller=self) if msg == "pong": return msg = json.loads(msg) table = msg.get("table") if table == "spot/depth": if msg.get("action") == "partial": for d in msg["data"]: await self.process_orderbook_partial(d) elif msg.get("action") == "update": for d in msg["data"]: await self.deal_orderbook_update(d) else: logger.warn("unhandle msg:", msg, caller=self) elif table == "spot/trade": for d in msg["data"]: await self.process_trade(d) elif table == "spot/candle60s": for d in msg["data"]: await self.process_kline(d)
async def check_asset_update(self, *args, **kwargs): """ 检查账户资金是否更新 """ result, error = await self._rest_api.get_user_account() if error: logger.warn("platform:", self._platform, "account:", self._account, "get asset info failed!", caller=self) return assets = {} for name, item in result["info"].items(): symbol = name.upper() total = float(item["equity"]) locked = float(item["margin"]) if total > 0: assets[symbol] = { "total": "%.8f" % total, "free": "%.8f" % (total - locked), "locked": "%.8f" % locked } if assets == self._assets: update = False else: update = True self._assets = assets # 推送当前资产 timestamp = tools.get_cur_timestamp_ms() EventAsset(self._platform, self._account, self._assets, timestamp, update).publish() logger.info("platform:", self._platform, "account:", self._account, "asset:", self._assets, caller=self)
async def _check_connection(self, *args, **kwargs): """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)
async def revoke_orders(self, instrument_id, order_ids): """ Cancelling multiple open orders with order_id,Maximum 10 orders can be cancelled at a time for each trading pair. Args: instrument_id: Contract ID, e.g. BTC-USD-180213. order_ids: order ID list. Returns: success: Success results, otherwise it's None. error: Error information, otherwise it's None. """ assert isinstance(order_ids, list) if len(order_ids) > 10: logger.warn("order id list too long! no more than 10!", caller=self) uri = "/api/futures/v3/cancel_batch_orders/{instrument_id}".format( instrument_id=instrument_id) body = {"order_ids": order_ids} success, error = await self.request("POST", uri, body=body, auth=True) if error: return None, error if not success["result"]: return None, success return success, None
async def process(self, msg): """Process message that received from Websocket connection. Args: msg: Message received from Websocket connection. """ # logger.debug("msg:", msg, caller=self) if not isinstance(msg, dict): return channel = msg.get("stream") if channel not in self._c_to_s: logger.warn("unkown channel, msg:", msg, caller=self) return symbol = self._c_to_s[channel] data = msg.get("data") e = data.get("e") if e == "kline": await self.process_kline(symbol, data) elif channel.endswith("depth20"): await self.process_orderbook(symbol, data) elif e == "trade": await self.process_trade(symbol, data)
async def connected_callback(self): """ After create Websocket connection successfully, we will subscribing orderbook/trade/kline. """ if not self._symbols: logger.warn("symbols not found in config file.", caller=self) return if not self._channels: logger.warn("channels not found in config file.", caller=self) return for ch in self._channels: if ch == "orderbook": # subscription = {"name": "book"} LoopRunTask.register(self.on_event_update_orderbook, 2) continue elif ch == "trade": subscription = {"name": "trade"} # elif ch == "kline": # TODO: something wrong from exchange server? subscribe ohlc-1 but receive ohlc-5 ? # subscription = {"name": "ohlc", "interval": 1} else: logger.error("channel error:", ch, caller=self) continue d = { "event": "subscribe", "pair": self._symbols, "subscription": subscription } await self.ws.send_json(d) logger.info("subscribe", ch, "success.", caller=self)
async def publish_orderbook(self, symbol): """Publish OrderbookEvent.""" ob = copy.copy(self._orderbooks[symbol]) if not ob["asks"] or not ob["bids"]: logger.warn("symbol:", symbol, "asks:", ob["asks"], "bids:", ob["bids"], caller=self) return ask_keys = sorted(list(ob["asks"].keys())) bid_keys = sorted(list(ob["bids"].keys()), reverse=True) if ask_keys[0] <= bid_keys[0]: logger.warn("symbol:", symbol, "ask1:", ask_keys[0], "bid1:", bid_keys[0], caller=self) return asks = [] for k in ask_keys[:self._orderbook_length]: price = "%.8f" % k quantity = "%.8f" % ob["asks"].get(k) asks.append([price, quantity]) bids = [] for k in bid_keys[:self._orderbook_length]: price = "%.8f" % k quantity = "%.8f" % ob["bids"].get(k) bids.append([price, quantity]) orderbook = { "platform": self._platform, "symbol": symbol, "asks": asks, "bids": bids, "timestamp": ob["timestamp"] } EventOrderbook(**orderbook).publish() logger.debug("symbol:", symbol, "orderbook:", orderbook, caller=self)
async def connected_callback(self): """ 建立连接之后,获取当前所有未完全成交的订单 """ order_infos, error = await self._rest_api.get_open_orders( self._raw_symbol) if error: return for order_info in order_infos: order_no = "{}_{}".format(order_info["orderId"], order_info["clientOrderId"]) 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) return info = { "platform": self._platform, "account": self._account, "strategy": self._strategy, "order_no": order_no, "action": order_info["side"], "order_type": order_info["type"], "symbol": self._symbol, "price": order_info["price"], "quantity": order_info["origQty"], "remain": float(order_info["origQty"]) - float(order_info["executedQty"]), "status": status, "ctime": order_info["time"], "utime": order_info["updateTime"] } order = Order(**info) self._orders[order_no] = order if self._order_update_callback: SingleTask.run(self._order_update_callback, order)
async def publish(self, event): """ 发布消息 @param event 发布的事件对象 """ 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)
async def _check_connection(self, *args, **kwargs): """ 检查连接是否正常 """ # 检查websocket连接是否关闭,如果关闭,那么立即重连 if not self.ws: logger.warn("websocket connection not connected yet!", caller=self) return if self.ws.closed: await asyncio.get_event_loop().create_task(self._reconnect()) return
async def publish_orderbook(self, *args, **kwargs): """ 推送orderbook数据 """ for symbol, data in self._orderbooks.items(): ob = copy.copy(data) if not ob["asks"] or not ob["bids"]: logger.warn("symbol:", symbol, "asks:", ob["asks"], "bids:", ob["bids"], caller=self) continue ask_keys = sorted(list(ob["asks"].keys())) bid_keys = sorted(list(ob["bids"].keys()), reverse=True) if ask_keys[0] <= bid_keys[0]: logger.warn("symbol:", symbol, "ask1:", ask_keys[0], "bid1:", bid_keys[0], caller=self) continue # 卖 asks = [] for k in ask_keys[:self._length]: price = "%.8f" % k quantity = "%.8f" % ob["asks"].get(k) asks.append([price, quantity]) # 买 bids = [] for k in bid_keys[:self._length]: price = "%.8f" % k quantity = "%.8f" % ob["bids"].get(k) bids.append([price, quantity]) # 推送订单薄数据 orderbook = { "platform": self._platform, "symbol": symbol, "asks": asks, "bids": bids, "timestamp": ob["timestamp"] } EventOrderbook(**orderbook).publish() logger.info("symbol:", symbol, "orderbook:", orderbook, caller=self)
async def receive(self): """ 接收消息 """ """ See: client_ws.py async def __anext__(self): msg = await self.receive() if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED): raise StopAsyncIteration # NOQA return msg """ self.last_timestamp = tools.get_cur_timestamp() #单位:秒,连接检测时间初始化 async for msg in self.ws: #参考aiohttp的源码,当ws连接被关闭后,本循环将退出 self.last_timestamp = tools.get_cur_timestamp( ) #单位:秒,每收到一个消息就更新一下此变量,用于判断网络是否出问题,是否需要重连 if msg.type == aiohttp.WSMsgType.TEXT: try: data = json.loads(msg.data) except: data = msg.data #await asyncio.get_event_loop().create_task(self.process(data)) #这样写的好处是如果里面这个函数发生异常不会影响本循环,因为它在单独任务里面执行 try: await self.process(data) except Exception as e: logger.error("process ERROR:", e, caller=self) await self.socket_close() #关闭 break #退出循环 elif msg.type == aiohttp.WSMsgType.BINARY: #await asyncio.get_event_loop().create_task(self.process_binary(msg.data)) #好处同上 try: await self.process_binary(msg.data) except Exception as e: logger.error("process_binary ERROR:", e, caller=self) await self.socket_close() #关闭 break #退出循环 elif msg.type == aiohttp.WSMsgType.ERROR: logger.error("receive event ERROR:", msg, caller=self) break #退出循环 else: #aiohttp.WSMsgType.CONTINUATION #aiohttp.WSMsgType.PING #aiohttp.WSMsgType.PONG logger.warn("unhandled msg:", msg, caller=self) #当代码执行到这里的时候ws已经是关闭状态,所以不需要再调用close去关闭ws了. #当ws连接被关闭或者出现任何错误,将重新连接 state = State(self._platform, self._account, "connection lost! url: {}".format(self._url), State.STATE_CODE_DISCONNECT) SingleTask.run(self.cb.on_state_update_callback, state) self.last_timestamp = 0 #先置0,相当于关闭连接检测 asyncio.get_event_loop().create_task(self._reconnect())
async def _reconnect(self, delay=5): """ 重新建立websocket连接 """ if delay > 0: await asyncio.sleep(delay) #等待一段时间再重连 logger.warn("reconnecting websocket right now!", caller=self) state = State( self._platform, self._account, "reconnecting websocket right now! url: {}".format(self._url), State.STATE_CODE_RECONNECTING) SingleTask.run(self.cb.on_state_update_callback, state) await self._connect()
async def check_asset_update(self, *args, **kwargs): """Fetch asset information.""" result, error = await self._rest_api.get_account_balance() if error: logger.warn("platform:", self._platform, "account:", self._account, "get asset info failed!", caller=self) return temps = {} for item in result.get("list"): name = item.get("currency").upper() t = item.get("type") b = float(item.get("balance")) if name not in temps: temps[name] = {} if t == "trade": temps[name]["free"] = b else: temps[name]["locked"] = b assets = {} for name, item in temps.items(): total = item["free"] + item["locked"] if total <= 0: continue assets[name] = { "free": "%.8f" % item["free"], "locked": "%.8f" % item["locked"], "total": "%.8f" % total } if assets == self._assets: update = False else: update = True self._assets = assets # Publish AssetEvent. timestamp = tools.get_cur_timestamp_ms() EventAsset(self._platform, self._account, self._assets, timestamp, update).publish() logger.info("platform:", self._platform, "account:", self._account, "asset:", self._assets, caller=self)
async def revoke_order(self, *order_nos): """ Revoke (an) order(s). Args: order_nos: 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 NOs, you can invoke many times. """ # If len(order_nos) == 0, you will cancel all orders for this symbol(initialized in Trade object). if len(order_nos) == 0: result, error = await self._rest_api.get_order_list( self._symbol, 6) if error: return False, error if len(result) > 100: logger.warn("order length too long! (more than 100)", caller=self) for order_info in result["order_info"]: order_no = order_info["order_id"] _, error = await self._rest_api.revoke_order( self._symbol, order_no) if error: return False, error return True, None # If len(order_nos) == 1, you will cancel an order. if len(order_nos) == 1: success, error = await self._rest_api.revoke_order( self._symbol, order_nos[0]) if error: return order_nos[0], error else: return order_nos[0], None # If len(order_nos) > 1, you will cancel multiple orders. if len(order_nos) > 1: success, error = [], [] for order_no in order_nos: _, e = await self._rest_api.revoke_order( self._symbol, order_no) if e: error.append((order_no, e)) else: success.append(order_no) return success, error
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) logger.debug("Publish to RabbitMQ: ", event, caller=self)
async def revoke_orders(self, symbol, order_nos): """ 批量撤销委托单 @param symbol 交易对 @param order_nos 订单列表 * NOTE: 单次不超过4个订单id """ if len(order_nos) > 4: logger.warn("only revoke 4 orders per request!", caller=self) body = [{"instrument_id": symbol, "order_ids": order_nos[:4]}] result, error = await self.request("POST", "/api/spot/v3/cancel_batch_orders", body=body, auth=True) return result, error
async def _send_heartbeat_msg(self, *args, **kwargs): """Send heartbeat message to Websocket server.""" if not self.ws: logger.warn("Websocket connection not connected yet!", caller=self) return if self._heartbeat_msg: if isinstance(self._heartbeat_msg, dict): await self.ws.send_json(self._heartbeat_msg) elif isinstance(self._heartbeat_msg, str): await self.ws.send_str(self._heartbeat_msg) else: logger.error("send heartbeat message failed:", self._heartbeat_msg, caller=self) return logger.debug("send heartbeat message:", self._heartbeat_msg, caller=self)
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)) if self._init_success_callback: SingleTask.run(self._init_success_callback, False, e) return for order_info in order_infos: order_no = "{}_{}".format(order_info["orderId"], order_info["clientOrderId"]) 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) continue info = { "platform": self._platform, "account": self._account, "strategy": self._strategy, "order_no": order_no, "action": order_info["side"], "order_type": order_info["type"], "symbol": self._symbol, "price": order_info["price"], "quantity": order_info["origQty"], "remain": float(order_info["origQty"]) - float(order_info["executedQty"]), "status": status, "ctime": order_info["time"], "utime": order_info["updateTime"] } order = Order(**info) self._orders[order_no] = order if self._order_update_callback: SingleTask.run(self._order_update_callback, copy.copy(order)) if self._init_success_callback: SingleTask.run(self._init_success_callback, True, None)
async def receive(self): """ 接收消息 """ async for msg in self.ws: if msg.type == aiohttp.WSMsgType.TEXT: try: data = json.loads(msg.data) except: data = msg.data await asyncio.get_event_loop().create_task(self.process(data)) elif msg.type == aiohttp.WSMsgType.BINARY: await asyncio.get_event_loop().create_task( self.process_binary(msg.data)) elif msg.type == aiohttp.WSMsgType.CLOSED: logger.warn("receive event CLOSED:", msg, caller=self) await asyncio.get_event_loop().create_task(self._reconnect()) elif msg.type == aiohttp.WSMsgType.CLOSE: logger.warn("receive event CLOSE:", msg, caller=self) await asyncio.get_event_loop().create_task(self._reconnect()) elif msg.type == aiohttp.WSMsgType.CLOSING: logger.warn("receive event CLOSING:", msg, caller=self) await asyncio.get_event_loop().create_task(self._reconnect()) elif msg.type == aiohttp.WSMsgType.ERROR: logger.error("receive event ERROR:", msg, caller=self) await asyncio.get_event_loop().create_task(self._reconnect()) else: logger.warn("unhandled msg:", msg, caller=self)
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_no = "{}_{}".format(msg["i"], msg["c"]) 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) return order = self._orders.get(order_no) if not order: info = { "platform": self._platform, "account": self._account, "strategy": self._strategy, "order_no": order_no, "client_order_id": msg["c"], "action": msg["S"], "order_type": msg["o"], "symbol": self._symbol, "price": msg["p"], "quantity": msg["q"], "ctime": msg["O"] } order = Order(**info) self._orders[order_no] = order order.remain = float(msg["q"]) - float(msg["z"]) order.status = status order.utime = msg["T"] if self._order_update_callback: SingleTask.run(self._order_update_callback, copy.copy(order))
async def do_orderbook_update(self, *args, **kwargs): """ 执行订单薄数据更新 """ for symbol in self._symbols: result, error = await self._rest_api.get_orderbook( symbol, self._orderbook_fetch_count) if error: continue bids = [] asks = [] for item in result["asks"]: a = [item["limitPrice"], item["quantity"]] asks.append(a) for item in result["bids"]: b = [item["limitPrice"], item["quantity"]] bids.append(b) if not bids and not asks: logger.warn("no orderbook data", caller=self) continue # 判断买一是否小于卖一,防止异常数据 if len(bids) > 0 and len(asks) > 0 and float(bids[0][0]) >= float( asks[0][0]): logger.warn("symbol:", symbol, "bids one is grate than asks one! asks:", asks, "bids:", bids, caller=self) continue orderbook = { "platform": self._platform, "symbol": symbol, "asks": asks, "bids": bids, "timestamp": tools.get_cur_timestamp_ms() } EventOrderbook(**orderbook).publish() logger.info("symbol:", symbol, "orderbook:", orderbook, caller=self) # 间隔0.1秒发起下一次请求 await asyncio.sleep(0.1)
async def fetch(cls, method, url, params=None, body=None, data=None, headers=None, timeout=30, **kwargs): """ 发起HTTP请求 @param method 请求方法 GET/POST/PUT/DELETE @param url 请求的url @param params 请求的uri参数 @param body 请求的body参数 @param data json格式的数据 @param headers 请求的headers @param timeout 超时时间(秒) @return (code, success, error) 如果成功,error为None,失败success为None,error为失败信息 """ session = cls._get_session(url) if not kwargs.get("proxy"): kwargs["proxy"] = config.proxy # HTTP代理配置 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, "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, "params:", params, "body:", body, "headers:", headers, "code:", code, "result:", text, caller=cls) return code, None, text try: result = await response.json() except: logger.warn("response data is not json format!", "method:", method, "url:", url, "params:", params, caller=cls) result = await response.text() logger.debug("method:", method, "url:", url, "params:", params, "body:", body, "data:", data, "code:", code, "result:", result, caller=cls) return code, result, None