async def process_binary(self, msg): """ Process binary message that received from Websocket connection. """ data = json.loads(gzip.decompress(msg).decode()) logger.debug("data:", json.dumps(data), caller=self) channel = data.get("ch") if not channel: if data.get("ping"): hb_msg = {"pong": data.get("ping")} await self.ws.send_json(hb_msg) if data.get("rep"): await self.process_hist_kline(data) return symbol = self._c_to_s[channel] if channel.find("kline") != -1: await self.process_kline(data) elif channel.find("depth") != -1: await self.process_orderbook(data) elif channel.find("trade") != -1: await self.process_trade(data) else: logger.error("event error! msg:", msg, caller=self)
async def process_orderbook(self, data): """ process orderbook data """ channel = data.get("ch") symbol = self._c_to_s[channel] d = data.get("tick") asks, bids = [], [] if d.get("asks"): for item in d.get("asks")[:self._orderbook_length]: price = "%.8f" % item[0] quantity = "%.8f" % item[1] asks.append([price, quantity]) if d.get("bids"): for item in d.get("bids")[:self._orderbook_length]: price = "%.8f" % item[0] quantity = "%.8f" % item[1] bids.append([price, quantity]) info = { "platform": self._platform, "symbol": symbol, "asks": asks, "bids": bids, "timestamp": d.get("ts") } orderbook = Orderbook(**info) self._orderbooks.append(orderbook) SingleTask.run(self._orderbook_update_callback, copy.copy(orderbook)) logger.debug("symbol:", symbol, "orderbook:", orderbook, caller=self)
async def process_binary(self, raw): """ 处理websocket上接收到的消息 @param raw 原始的压缩数据 """ data = json.loads(gzip.decompress(raw).decode()) logger.debug("data:", data, caller=self) op = data.get("op") if op == "ping": hb_msg = {"op": "pong", "ts": data.get("ts")} await self.ws.send_json(hb_msg) elif op == "auth": await self.auth_callback(data) elif op == "sub": await self.sub_callback(data) elif op == "notify": if data["topic"].startswith("orders"): self._update_order(data) elif data["topic"].startswith("positions"): self._update_position(data) elif data["topic"].startswith("accounts"): self._update_asset(data)
async def on_event_init_success_callback(self, success: bool, error: Error, **kwargs): """ init success callback """ logger.debug("init success callback update:", success, error, kwargs, caller=self)
async def on_ticker(self, *args, **kwargs): """ 定时执行任务 """ ts_diff = int(time.time() * 1000) - self.last_orderbook_timestamp if ts_diff > self.orderbook_invalid_seconds * 1000: logger.debug("received orderbook timestamp exceed:", self.strategy, self.symbol, ts_diff, caller=self) return
async def on_event_orderbook_update(self, orderbook: Orderbook): """ orderbook更新 self.market.orderbooks 是最新的orderbook组成的队列,记录的是历史N次orderbook的数据。 本回调所传的orderbook是最新的单次orderbook。 """ logger.debug("orderbook:", orderbook, caller=self) if orderbook.asks: self.ask1_price = float(orderbook.asks[0][0]) # 卖一价格 self.ask1_volume = float(orderbook.asks[0][1]) # 卖一数量 if orderbook.bids: self.bid1_price = float(orderbook.bids[0][0]) # 买一价格 self.bid1_volume = float(orderbook.bids[0][1]) # 买一数量 self.last_orderbook_timestamp = orderbook.timestamp
async def on_event_kline_update(self, kline: Kline): """ kline更新 self.market.klines 是最新的kline组成的队列,记录的是历史N次kline的数据。 本回调所传的kline是最新的单次kline。 """ logger.debug("kline update:", kline, caller=self) result = await mongo.MongoDBBase('quant', 'kline').find_one_and_update({'platform': kline.platform, 'symbol': kline.symbol, \ 'timestamp': kline.timestamp, 'kline_type': kline.kline_type}, {'$set': {'open': kline.open, 'high': kline.high, \ 'close': kline.close, 'low': kline.low, 'volume': kline.volume }}, upsert=True, return_document=True) if not result: logger.error("insert mongo error ", kline.platform + kline.symbol, kline, result) logger.debug("insert mongo success: ", result)
async def process_kline(self, data): """ process kline data """ channel = data.get("ch") symbol = self._c_to_s[channel] d = data.get("tick") info = { "platform": self._platform, "symbol": symbol, "open": "%.8f" % d["open"], "high": "%.8f" % d["high"], "low": "%.8f" % d["low"], "close": "%.8f" % d["close"], "volume": "%.8f" % d["amount"], "timestamp": int(data.get("ts")), "kline_type": MARKET_TYPE_KLINE } kline = Kline(**info) self._klines.append(kline) SingleTask.run(self._kline_update_callback, copy.copy(kline)) logger.debug("symbol:", symbol, "kline:", kline, caller=self)
async def process_trade(self, data): """ process trade """ channel = data.get("ch") symbol = self._c_to_s[channel] ticks = data.get("tick") for tick in ticks["data"]: direction = tick.get("direction") price = tick.get("price") quantity = tick.get("amount") info = { "platform": self._platform, "symbol": symbol, "action": ORDER_ACTION_BUY if direction == "buy" else ORDER_ACTION_SELL, "price": "%.8f" % price, "quantity": "%.8f" % quantity, "timestamp": tick.get("ts") } trade = Trade(**info) self._trades.append(trade) SingleTask.run(self._trade_update_callback, copy.copy(trade)) logger.debug("symbol:", symbol, "trade:", trade, caller=self)
async def _send_heartbeat_msg(self, *args, **kwargs): """ 发送心跳给服务器 """ if not self.ws: logger.warn("websocket connection not connected yet!", caller=self) return if self.heartbeat_msg: try: 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 msg failed! heartbeat msg:", self.heartbeat_msg, caller=self) return logger.debug("send ping message:", self.heartbeat_msg, caller=self) except ConnectionResetError: traceback.print_exc() await asyncio.get_event_loop().create_task(self._reconnect())
def ticker(self): """ 启动心跳, 每interval间隔执行一次 """ self._count += 1 # 打印心跳次数 if self._print_interval > 0: if self._count % int(self._print_interval*200) == 0: logger.debug("do server heartbeat, count:", self._count, caller=self) # 设置下一次心跳回调 asyncio.get_event_loop().call_later(self._interval, self.ticker) # 执行任务回调 for task_id, task in self._tasks.items(): interval = task["interval"] if self._count % int(interval*200) != 0: continue func = task["func"] args = task["args"] kwargs = task["kwargs"] kwargs["task_id"] = task_id kwargs["heart_beat_count"] = self._count asyncio.get_event_loop().create_task(func(*args, **kwargs))
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 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
async def on_event_trade_update(self, trade: MarketTrade): """ market trade更新 self.market.trades 是最新的逐笔成交组成的队列,记录的是历史N次trade的数据。 本回调所传的trade是最新的单次trade。 """ logger.debug("trade update:", trade, caller=self)
async def on_event_kline_update(self, kline: Kline): """ kline更新 self.market.klines 是最新的kline组成的队列,记录的是历史N次kline的数据。 本回调所传的kline是最新的单次kline。 """ logger.debug("kline update:", kline, caller=self)
async def on_event_position_update(self, position: Position): """ 仓位更新 """ logger.debug("position update:", position, caller=self)
async def on_event_asset_update(self, asset: Asset): """ 资产更新 """ logger.debug("asset update:", asset, caller=self)
async def on_event_order_update(self, order: Order): """ 订单状态更新 """ logger.debug("order update:", order, caller=self)
async def on_event_orderbook_update_future(self, orderbook: Orderbook): """ orderbook更新 self.market.orderbooks 是最新的orderbook组成的队列,记录的是历史N次orderbook的数据。 本回调所传的orderbook是最新的单次orderbook。 """ logger.debug("future orderbook:", orderbook, caller=self)
async def delta_hedging(self, *args, **kwargs): """ delta hedge """ logger.debug("delta hedging", caller=self) option_delta = 0 assets, error = await self.trader.rest_api.get_asset_info( self.raw_symbol) if error: logger.error(self.strategy, "get option asset error! error:", error, caller=self) else: for item in assets["data"]: if item["symbol"] == self.raw_symbol: o_margin_balance = item["margin_balance"] o_delta = item["delta"] o_gamma = item["gamma"] o_theta = item["theta"] o_vega = item["vega"] option_delta = o_delta + o_margin_balance #增加delta对冲,使用期货对冲。 accounts, error = await self.future_trader.rest_api.get_account_position( self.raw_symbol) if error: logger.error(self.strategy, "get future account and position error! error:", error, caller=self) else: margin_balance = accounts["data"][0]["margin_balance"] long_position = 0 short_position = 0 delta_long = 0 delta_short = 0 long_last_price = 0 short_last_price = 0 for position in accounts["data"][0]["positions"]: if position["direction"] == "buy": long_position = position["volume"] long_cost_open = position["cost_open"] long_last_price = position["last_price"] if position["direction"] == "sell": short_position = position["volume"] short_cost_open = position["cost_open"] short_last_price = position["last_price"] if long_position: delta_long = self.future_volume_usd * int( long_position) / float(long_last_price) if short_position: delta_short = self.future_volume_usd * int( short_position) / float(short_last_price) future_delta = margin_balance - delta_short + delta_long t_delta = option_delta + future_delta orders_data = [] # 对冲对应数量的币 if abs(t_delta) >= self.delta_limit: if t_delta > 0: # 开空单 price = 0 volume = int(t_delta * long_last_price / self.future_volume_usd) if volume: quantity = -volume # action = ORDER_ACTION_SELL new_price = str(price) # 将价格转换为字符串,保持精度 if quantity: orders_data.append({ "price": new_price, "quantity": quantity, "action": action, "order_type": ORDER_TYPE_MARKET }) else: # 开多单 price = 0 volume = abs( int(t_delta * long_last_price / self.future_volume_usd)) if volume: quantity = volume # action = ORDER_ACTION_BUY new_price = str(price) # 将价格转换为字符串,保持精度 if quantity: orders_data.append({ "price": new_price, "quantity": quantity, "action": action, "order_type": ORDER_TYPE_MARKET }) if orders_data: order_nos, error = await self.future_trader.create_orders( orders_data) if error: logger.error(self.strategy, "create future order error! error:", error, caller=self) else: logger.info(self.strategy, "create future orders success:", order_nos, caller=self)