async def process_kline(self, data): """ Deal with 1min kline data, and publish kline message to EventCenter via KlineEvent. Args: data: Newest kline data. """ symbol = data["instrument_id"] if symbol not in self._symbols: return timestamp = tools.utctime_str_to_mts(data["candle"][0]) _open = "%.5f" % float(data["candle"][1]) high = "%.5f" % float(data["candle"][2]) low = "%.5f" % float(data["candle"][3]) close = "%.5f" % float(data["candle"][4]) volume = str(data["candle"][5]) # Publish EventKline kline = { "platform": self._platform, "symbol": symbol, "open": _open, "high": high, "low": low, "close": close, "volume": volume, "timestamp": timestamp, "kline_type": const.MARKET_TYPE_KLINE } EventKline(**kline).publish() logger.debug("symbol:", symbol, "kline:", kline, 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(hb_msg) elif op == "auth": await self.auth_callback(data) elif op == "sub": await self.sub_callback(data) elif op == "notify": if data["topic"] == self._order_channel: self._update_order(data) elif data["topic"] == "positions": self._update_position(data) elif data["topic"] == self._position_channel: self._update_position(data)
async def _send_message(self, method, params): """ Send message. Args: method: message method. params: message params. Returns: success: Success results, otherwise it's None. error: Error information, otherwise it's None. """ f = asyncio.futures.Future() request_id = await self._generate_query_id() self._queries[request_id] = f data = { "jsonrpc": "2.0", "id": request_id, "method": method, "params": params } await self.ws.send_json(data) logger.debug("send message:", data, caller=self) success, error = await f if error: logger.error("data:", data, "error:", error, caller=self) return success, error
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.process_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) elif table == "spot/ticker": for d in msg["data"]: await self.process_ticker(d)
async def process_binary(self, raw): """ 处理websocket上接收到的消息 @param raw 原始的压缩数据 """ msg = json.loads(gzip.decompress(raw).decode()) logger.debug("msg:", msg, caller=self) op = msg.get("op") if op == "auth": # 授权 if msg["err-code"] != 0: logger.error("do auth error! error:", msg, caller=self) return await self._auth_success_callback() elif op == "ping": # ping params = { "op": "pong", "ts": msg["ts"] } await self.ws.send_json(params) return elif op == "notify": # 订单更新通知 if msg["topic"] != self._order_channel: return data = msg["data"] data["utime"] = msg["ts"] self._update_order(data)
async def on_event_orderbook_update(self, orderbook: Orderbook): """ 订单薄更新 """ logger.debug("orderbook:", orderbook, caller=self) ask1_price = float(orderbook.asks[0][0]) # 卖一价格 bid1_price = float(orderbook.bids[0][0]) # 买一价格 price = (ask1_price + bid1_price) / 2 # 为了方便,这里假设盘口价格为 卖一 和 买一 的平均值 # 判断是否需要撤单 if self.order_no: if (self.create_order_price + 10 > price - 1) and (self.create_order_price + 10 < price + 1): return _, error = await self.trader.revoke_order(self.order_no) if error: logger.error("revoke order error! error:", error, caller=self) return self.order_no = None logger.info("revoke order:", self.order_no, caller=self) # 创建新订单 new_price = price + 10 quantity = "1" # 委托数量为1 action = ORDER_ACTION_BUY new_price = tools.float_to_str(new_price) # 将价格转换为字符串,保持精度 quantity = tools.float_to_str(quantity) # 将数量转换为字符串,保持精度 order_no, error = await self.trader.create_order(action, new_price, quantity) if error: logger.error("create order error! error:", error, caller=self) return self.order_no = order_no self.create_order_price = float(new_price) logger.info("create new order:", order_no, caller=self)
async def process_trade(self, data): """Deal with trade data, and publish trade message to EventCenter via TradeEvent.""" symbol = data["instrument_id"] if symbol not in self._symbols: return action = ORDER_ACTION_BUY if data[ "side"] == "buy" else ORDER_ACTION_SELL price = "%.5f" % float(data["price"]) if self._platform == const.OKEX_FUTURE: quantity = str(data["qty"]) else: quantity = str(data["size"]) timestamp = tools.utctime_str_to_mts(data["timestamp"]) # Publish EventTrade. trade = { "platform": self._platform, "symbol": symbol, "action": action, "price": price, "quantity": quantity, "timestamp": timestamp } EventTrade(**trade).publish() logger.debug("symbol:", symbol, "trade:", trade, caller=self)
async def process(self, msg): """ 处理websocket消息 """ logger.debug("msg:", json.dumps(msg), caller=self) # 请求消息 request_id = msg.get("id") if request_id: f = self._queries.pop(request_id) if f.done(): return success = msg.get("result") error = msg.get("error") f.set_result((success, error)) # 推送订阅消息 if msg.get("method") != "subscription": return if msg["params"]["channel"] == "user.portfolio.btc": name = "BTC" total = float(msg["params"]["data"]["equity"]) locked = float(msg["params"]["data"]["initial_margin"]) elif msg["params"]["channel"] == "user.portfolio.eth": name = "ETH" total = float(msg["params"]["data"]["equity"]) locked = float(msg["params"]["data"]["initial_margin"]) else: return self._assets[name] = { "free": "%.8f" % (total - locked), "locked": "%.8f" % locked, "total": "%.8f" % total }
async def connected_callback(self): """ 建立websocket成功,发起登录请求 """ params = { "platform": self._platform, "access_key": self._access_key, "secret_key": self._secret_key } ok, result = await self.do_request(self.LOGIN, params) if not ok: logger.error("login error!", "platform:", self._platform, "account:", self._account, "result:", result, caller=self) else: logger.debug("login success!", "platform:", self._platform, "account:", self._account, caller=self)
async def check_order_update(self, *args, **kwargs): """ 检查订单更新 """ # 获取需要查询的订单列表 order_nos = list(self._orders.keys()) logger.info('length:', len(order_nos), 'orders:', order_nos, caller=self) if not order_nos: # 暂时没有需要更新的委托单,那么延迟1秒,再次发布执行委托单更新事件 logger.debug('no find any order nos', caller=self) return # 获取订单最新状态,每次最多请求50个订单 while order_nos: nos = order_nos[:100] params = {"symbol": self._raw_symbol, "order_nos": nos} success, results = await self._request.do_request( Request.ORDER_STATUS, params) if not success: logger.error("get order status error!", "symbol:", self._symbol, "order_nos:", order_nos, "results:", results, caller=self) await self._process_order_update_infos(results) order_nos = order_nos[100:]
def _add_event_handler(self, event: Event, callback): key = "{exchange}:{routing_key}".format(exchange=event.exchange, routing_key=event.routing_key) if key in self._event_handler: self._event_handler[key].append(callback) else: self._event_handler[key] = [callback] logger.debug("event handlers:", self._event_handler.keys(), 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 on_event_orderbook_update(self, orderbook: Orderbook): """ 订单薄更新 """ logger.debug("orderbook:", orderbook, caller=self) bid3_price = orderbook.bids[2][0] # 买三价格 bid4_price = orderbook.bids[3][0] # 买四价格 # 判断是否需要撤单 if self.order_no: if float(self.create_order_price) < float(bid3_price) or float( self.create_order_price) > float(bid4_price): return _, error = await self.trader.revoke_order(self.order_no) if error: logger.error("revoke order error! error:", error, caller=self) return self.order_no = None logger.info("revoke order:", self.order_no, caller=self) # 创建新订单 new_price = (float(bid3_price) + float(bid4_price)) / 2 quantity = "0.1" # 假设委托数量为0.1 action = ORDER_ACTION_BUY price = tools.float_to_str(new_price) quantity = tools.float_to_str(quantity) order_no, error = await self.trader.create_order( action, price, quantity) if error: logger.error("create order error! error:", error, caller=self) return self.order_no = order_no self.create_order_price = price logger.info("create new order:", order_no, caller=self)
async def process_kline(self, data): """Process kline data and publish KlineEvent.""" symbol = data["instrument_id"].replace("-", "/") if symbol not in self._symbols: return timestamp = tools.utctime_str_to_mts(data["candle"][0]) _open = "%.8f" % float(data["candle"][1]) high = "%.8f" % float(data["candle"][2]) low = "%.8f" % float(data["candle"][3]) close = "%.8f" % float(data["candle"][4]) volume = "%.8f" % float(data["candle"][5]) kline = { "platform": self._platform, "symbol": symbol, "open": _open, "high": high, "low": low, "close": close, "volume": volume, "timestamp": timestamp, "kline_type": const.MARKET_TYPE_KLINE } EventKline(**kline).publish() logger.debug("symbol:", symbol, "kline:", kline, caller=self)
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 assigned 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.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_orderbook_update(self, orderbook: Orderbook): """ 订单薄更新 """ #logger.debug("orderbook:", orderbook, caller=self) ask1_price = float(orderbook.asks[0][0]) # 卖一价格 bid1_price = float(orderbook.bids[0][0]) # 买一价格 logger.debug("btc/busd :", ask1_price, bid1_price, self.bsud_usdt_price, caller=self) if self.bsud_usdt_price == 0: logger.debug("on_event_orderbook_update busd/usdt not get now") return self.highest_price = max(self.highest_price, ask1_price) self.lowest_price = min(self.lowest_price, bid1_price) if ask1_price < self.btc_busd_relative[ 'bid0_relative'] and not self.buy_open_order_no: logger.info("on_event_orderbook_update: Buy signal", bid1_price, orderbook.bids[0][1]) self.buy_open_price = float(orderbook.asks[0][0]) quantity = self.buy_open_quantity action = ORDER_ACTION_BUY new_price = tools.float_to_str(self.buy_open_price) logger.info('check :', action) order_no, error = await self.trader.create_order( action, new_price, quantity) s = "buy bitcoin" + new_price os.system('say ' + s) if error: logger.error("create order error! error:", error, caller=self) return self.buy_open_order_no = order_no logger.info("create buy open order:", order_no, caller=self) elif self.buy_open_order_no and not self.sell_close_order_no: #止损 if ask1_price < self.buy_open_price * (1 - self.threshold) or \ (ask1_price < self.highest_price * (1 - self.threshold) and ask1_price > self.buy_open_price * (1 + self.threshold)): price = bid1_price # 当前盘口价格, new_price = tools.float_to_str(price) # 将价格转换为字符串,保持精度 order_no, error = await self.trader.create_order( ORDER_ACTION_SELL, new_price, self.buy_open_quantity) if error: logger.error("create order error! error:", error, caller=self) return self.sell_close_order_no = order_no logger.info("create sell close order:", order_no, caller=self) elif self.sell_close_order_no: logger.info("wait for sell close") else: logger.info("wait for better price")
async def process(self, msg): """ 处理websocket上接收到的消息 """ if not isinstance(msg, dict): return logger.debug("msg:", json.dumps(msg), caller=self) # 请求授权、订阅 if msg.get("request"): if msg["request"]["op"] == "authKeyExpires": # 授权 if msg["success"]: # 订阅order和position data = { "op": "subscribe", "args": [self._order_channel, self._position_channel] } await self.ws.send_json(data) logger.info( "Websocket connection authorized successfully.", caller=self) else: e = Error( "Websocket connection authorized failed: {}".format( msg)) logger.error(e, caller=self) if self._init_success_callback: SingleTask.run(self._init_success_callback, False, e) if msg["request"]["op"] == "subscribe": # 订阅 if msg["subscribe"] == self._order_channel and msg["success"]: self._subscribe_order_ok = True logger.info("subscribe order successfully.", caller=self) if msg["subscribe"] == self._position_channel and msg[ "success"]: self._subscribe_position_ok = True logger.info("subscribe position successfully.", caller=self) if self._subscribe_order_ok and self._subscribe_position_ok: if self._init_success_callback: SingleTask.run(self._init_success_callback, True, None) return # 订单更新 if msg.get("table") == "order": for data in msg["data"]: order = self._update_order(data) if order and self._order_update_callback: SingleTask.run(self._order_update_callback, copy.copy(order)) return # 持仓更新 if msg.get("table") == "position": for data in msg["data"]: self._update_position(data) if self._position_update_callback: SingleTask.run(self._position_update_callback, copy.copy(self.position))
async def process(self, msg): """ Process message that received from websocket. Args: msg: message received from websocket. Returns: None. """ if not isinstance(msg, dict): return logger.debug("msg:", json.dumps(msg), caller=self) #{"type": "error", "code": 400, "msg": "Invalid login credentials"} if msg["type"] == "error": e = Error("Websocket connection failed: {}".format(msg)) logger.error(e, caller=self) SingleTask.run(self.cb.on_init_success_callback, False, e, **self.raw_kwargs) return if msg["type"] == "info" and msg["code"] == 20001: #交易所重启了,我们就断开连接,websocket会自动重连 @async_method_locker("FTXTrader._ws_close.locker") async def _ws_close(self): await self.ws.close() SingleTask.run(self._ws_close) return #{'type': 'subscribed', 'channel': 'trades', 'market': 'BTC-PERP'} if msg["type"] == "unsubscribed": return if msg["type"] == "subscribed": for sym in self._symbols: if self.cb.on_order_update_callback != None: orders, err = await self.get_orders(sym) if not err: for o in orders: SingleTask.run(self.cb.on_order_update_callback, o) #end for return channel = msg['channel'] if channel == 'orderbook': self._update_orderbook(msg) elif channel == 'trades': self._update_trades(msg) elif channel == 'ticker': self._update_ticker(msg) elif channel == 'orders': self._update_order(msg) elif channel == 'fills': self._update_fill(msg)
async def process(self, msg): """ 处理消息 """ logger.debug("receive message:", msg, caller=self) request_id = msg["request_id"] if request_id in self._querys: f = self._querys.pop(request_id) if f.done(): return f.set_result(msg)
async def _check_connection(self, *args, **kwargs): if self._connected and self._channel and self._channel.is_open: logger.debug("RabbitMQ connection ok.", caller=self) return logger.error("CONNECTION LOSE! START RECONNECT RIGHT NOW!", caller=self) self._connected = False self._protocol = None self._channel = None self._event_handler = {} SingleTask.run(self.connect, reconnect=True)
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 == "ORDER_TRADE_UPDATE": # Order update. self._update_order(msg["o"])
def _add_event_handler(self, event: Event, callback): """ 增加事件处理回调函数 * NOTE: {"exchange:routing_key": [callback_function, ...]} """ key = "{exchange}:{routing_key}".format(exchange=event.exchange, routing_key=event.routing_key) if key in self._event_handler: self._event_handler[key].append(callback) else: self._event_handler[key] = [callback] logger.debug("event handlers:", self._event_handler.keys(), caller=self)
async def process(self, msg): """ 处理websocket上接收到的消息 """ logger.debug("msg:", msg, caller=self) if not isinstance(msg, dict): return table = msg.get("table") if table == "orderBook10": # 订单薄数据 for item in msg["data"]: symbol = item.get("symbol") orderbook = { "platform": self._platform, "symbol": symbol, "asks": item.get("asks"), "bids": item.get("bids"), "timestamp": tools.utctime_str_to_mts(item["timestamp"]) } EventOrderbook(**orderbook).publish() logger.info("symbol:", symbol, "orderbook:", orderbook, caller=self) elif table == "trade": # 成交数据 for item in msg["data"]: symbol = item["symbol"] trade = { "platform": self._platform, "symbol": symbol, "action": ORDER_ACTION_BUY if item["side"] else ORDER_ACTION_SELL, "price": "%.1f" % item["price"], "quantity": str(item["size"]), "timestamp": tools.utctime_str_to_mts(item["timestamp"]) } EventTrade(**trade).publish() logger.info("symbol:", symbol, "trade:", trade, caller=self) elif table == "tradeBin1m": # 1分钟K线数据 for item in msg["data"]: symbol = item["symbol"] kline = { "platform": self._platform, "symbol": symbol, "open": "%.1f" % item["open"], # 开盘价 "high": "%.1f" % item["high"], # 最高价 "low": "%.1f" % item["low"], # 最低价 "close": "%.1f" % item["close"], # 收盘价 "volume": str(item["volume"]), # 交易量 "timestamp": tools.utctime_str_to_mts(item["timestamp"]), # 时间戳 "kline_type": MARKET_TYPE_KLINE } EventKline(**kline).publish() logger.info("symbol:", symbol, "kline:", kline, caller=self)
async def connect(self, reconnect=False): """ Connect to RabbitMQ server and create default exchange. Args: reconnect: If this invoke is a re-connection ? """ logger.info("host:", self._host, "port:", self._port, caller=self) if self._connected: return # Create a connection. try: transport, protocol = await aioamqp.connect( host=self._host, port=self._port, login=self._username, password=self._password, login_method="PLAIN") except Exception as e: logger.error("connection error:", e, caller=self) return finally: if self._connected: return channel = await protocol.channel() self._protocol = protocol self._channel = channel self._connected = True logger.info("Rabbitmq initialize success!", caller=self) # Create default exchanges. exchanges = [ "Orderbook", "Trade", "Kline", "Kline.5min", "Kline.15min", "Kline.1h", "Kline.4h", "Kline.1day", "Config", "Heartbeat", "Asset", "Order", ] for name in exchanges: await self._channel.exchange_declare(exchange_name=name, type_name="topic") logger.debug("create default exchanges success!", caller=self) if reconnect: self._bind_and_consume() else: # Maybe we should waiting for all modules to be initialized successfully. asyncio.get_event_loop().call_later(5, self._bind_and_consume)
async def connect(self, reconnect=False): """ 建立TCP连接 @param reconnect 是否是断线重连 """ logger.debug('host:', self._host, 'port:', self._port, caller=self) if self._connected: return # 建立连接 try: transport, protocol = await aioamqp.connect( host=self._host, port=self._port, login=self._username, password=self._password) except Exception as e: logger.error('connection error:', e, caller=self) return finally: # 如果已经有连接已经建立好,那么直接返回(此情况在连续发送了多个连接请求后,若干个连接建立好了连接) if self._connected: return channel = await protocol.channel() self._protocol = protocol self._channel = channel self._connected = True logger.info('Rabbitmq initialize success!', caller=self) # 创建默认的交换机 await self._channel.exchange_declare(exchange_name=EventAsset.EXCHANGE, type_name='topic') await self._channel.exchange_declare(exchange_name=EventOrder.EXCHANGE, type_name='topic') await self._channel.exchange_declare( exchange_name=EventConfig.EXCHANGE, type_name='topic') await self._channel.exchange_declare( exchange_name=EventHeartbeat.EXCHANGE, type_name='topic') await self._channel.exchange_declare(exchange_name=EventKline.EXCHANGE, type_name='topic') await self._channel.exchange_declare( exchange_name=EventKline5Min.EXCHANGE, type_name='topic') await self._channel.exchange_declare( exchange_name=EventKline15Min.EXCHANGE, type_name='topic') await self._channel.exchange_declare( exchange_name=EventOrderbook.EXCHANGE, type_name='topic') await self._channel.exchange_declare( exchange_name=EventTicker.EXCHANGE, type_name='topic') logger.info('create default exchanges success!', caller=self) # 如果是断线重连,那么直接绑定队列并开始消费数据,如果是首次连接,那么等待5秒再绑定消费(等待程序各个模块初始化完成) if reconnect: self._bind_and_consume() else: asyncio.get_event_loop().call_later(5, self._bind_and_consume)
def check_buy_arb_space(self, buy_price, volume_frac): """ 检查买方向是否有套利空间(主买从卖) """ bidprices_reference = [ bid[0] for bid in self.last_orderbook_reference.bids ] bidsizes_reference = [ bid[1] for bid in self.last_orderbook_reference.bids ] logger.debug( "check_buy_arb_space:", "{}, {}".format( buy_price * (1.0 + self.maker_fee_main + self.profit_level), bidprices_reference[0] * (1.0 - self.taker_fee_reference - self.profit_level)), caller=self) total_volume = 0.0 for i in range(20): if buy_price * (1.0 + self.maker_fee_main + self.profit_level) <= bidprices_reference[i] * ( 1.0 - self.taker_fee_reference - self.profit_level): total_volume += bidsizes_reference[i] sell_price = bidprices_reference[i] sell_level = i else: break if total_volume == 0: buy_side_arb_space = [None, None, None, None, None] else: # 账户最多能买的数量比例 buy_volume1 = self.max_frac * min( self.usdt_available_main / buy_price, self.eth_available_reference) # 单笔最大金额限制 buy_volume2 = self.max_amount / buy_price # 套利空间范围内,最大下单比例限制 buy_volume3 = total_volume * volume_frac # 计算最终可下单数量 buy_volume = min(buy_volume1, buy_volume2, buy_volume3, self.max_volume) sell_volume = buy_volume # 套利空间: main标的买价,main标的买数量,reference标的卖价,reference标的卖数量,计算套利空间在reference价格的档位 if buy_volume < max(self.min_volume_main, self.min_volume_reference): buy_side_arb_space = [None, None, None, None, None] #超过最小下单限制 else: buy_side_arb_space = [ buy_price, buy_volume, sell_price, sell_volume, sell_level ] #返回结果 return buy_side_arb_space
def check_sell_arb_space(self, sell_price, volume_frac): """ 检查卖方向是否有套利空间(主卖从买) """ askprices_reference = [ ask[0] for ask in self.last_orderbook_reference.asks ] asksizes_reference = [ ask[1] for ask in self.last_orderbook_reference.asks ] logger.debug( "check_sell_arb_space:", "{}, {}".format( sell_price * (1.0 - self.maker_fee_main - self.profit_level), askprices_reference[0] * (1.0 + self.taker_fee_reference + self.profit_level)), caller=self) total_volume = 0.0 for i in range(20): if sell_price * (1.0 - self.maker_fee_main - self.profit_level) >= askprices_reference[i] * ( 1.0 + self.taker_fee_reference + self.profit_level): total_volume += asksizes_reference[i] buy_price = askprices_reference[i] buy_level = i else: break if total_volume == 0: sell_side_arb_space = [None, None, None, None, None] else: # 账户最多能卖的数量比例 sell_volume1 = self.max_frac * min( self.eth_available_main, self.usdt_available_reference / buy_price) # 单笔最大金额限制 sell_volume2 = self.max_amount / buy_price # 套利空间范围内,最大下单比例限制 sell_volume3 = total_volume * volume_frac # 计算最终可下单数量 sell_volume = min(sell_volume1, sell_volume2, sell_volume3, self.max_volume) buy_volume = sell_volume # 套利空间: main标的卖价,main标的卖数量,reference标的买价,reference标的买数量,计算套利空间在reference价格的档位 if sell_volume < max(self.min_volume_main, self.min_volume_reference): sell_side_arb_space = [None, None, None, None, None] #超过最小下单限制 else: sell_side_arb_space = [ sell_price, sell_volume, buy_price, buy_volume, buy_level ] #返回结果 return sell_side_arb_space
async def on_event_orderbook_btcusdt_update(self, orderbook: Orderbook): """ 订单薄更新 """ if self.bsud_usdt_price == 0: logger.debug("busd/usdt not get now") return #logger.debug("orderbook:", orderbook, caller=self) ask1_price = float(orderbook.asks[0][0]) / self.bsud_usdt_price # 卖一价格 bid1_price = float(orderbook.bids[0][0]) / self.bsud_usdt_price # 买一价格 self.btc_busd_relative['ask0_relative'] = ask1_price self.btc_busd_relative['bid0_relative'] = bid1_price logger.debug("btc/busd relative:", self.btc_busd_relative, aller=self)
async def _send_heartbeat_msg(self, *args, **kwargs): """ 发送心跳给服务器 """ 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 msg failed! heartbeat msg:", self.heartbeat_msg, caller=self) return logger.debug("send ping message:", self.heartbeat_msg, caller=self)
async def process(self, msg): """ Process message that received from Websocket connection. """ logger.debug("msg:", msg, caller=self) if not isinstance(msg, list): return if msg[-2] == "book-10": await self.deal_orderbook_update(msg) elif msg[-2] == "trade": await self.deal_trade_update(msg)