async def _reset_listen_key(self, *args, **kwargs): """Reset listen key.""" if not self._listen_key: logger.error("listen key not initialized!", caller=self) return await self._rest_api.put_listen_key(self._listen_key) logger.info("reset listen key success!", 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_id: 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_id) if error: logger.error("revoke order error! error:", error, caller=self) return self.order_id = None logger.info("revoke order:", self.order_id, caller=self) # 创建新订单 price = (float(bid3_price) + float(bid4_price)) / 2 quantity = "0.1" # 假设委托数量为0.1 action = ORDER_ACTION_BUY order_id, error = await self.trader.create_order( action, price, quantity) if error: logger.error("create order error! error:", error, caller=self) return self.order_id = order_id self.create_order_price = price logger.info("create new order:", order_id, caller=self)
def ticker(self): """Loop run ticker per self._interval. """ self._count += 1 if self._print_interval > 0: if self._count % self._print_interval == 0: logger.info("do server heartbeat, count:", self._count, caller=self) # Later call next ticker. asyncio.get_event_loop().call_later(self._interval, self.ticker) # Exec tasks. for task_id, task in self._tasks.items(): interval = task["interval"] if self._count % interval != 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 get_k_line(self): """获取K线数据""" symbol = self.symbol success, error = await self._rest_api.get_klines(symbol, interval="1m", limit=10) logger.info("success", success, caller=self) logger.info("error", error, caller=self)
async def on_event_order_update(self, order: Order): """ 订单状态更新 """ logger.info("order update:", order, caller=self) # 如果订单失败、订单取消、订单完成交易 if order.status in [ ORDER_STATUS_FAILED, ORDER_STATUS_CANCELED, ORDER_STATUS_FILLED ]: self.order_id = None
async def on_init_callback(self, success: bool, **kwargs): """用于标记: 初始化Trade()成功或失败""" logger.info("success", success, caller=self) if not success: return _, error = await self._trade.revoke_order() if error: return self._is_ok = True
async def subscribe(self, event: Event, callback=None, multi=False): """Subscribe a event. Args: event: Event type. callback: Asynchronous callback. multi: If subscribe multiple channel(routing_key) ? """ logger.info("NAME:", event.name, "EXCHANGE:", event.exchange, "QUEUE:", event.queue, "ROUTING_KEY:", event.routing_key, caller=self) self._subscribers.append((event, callback, multi))
async def create_new_order(self, price: float): """下单""" action = self._action quantity = self._quantity # min 2.5 logger.info("Doing price: ", price, "quantity: ", self._quantity) order_id, error = await self._trade.create_order( action, price, quantity) if error: return self._order_id = order_id self._price = price logger.info("order_id", self._order_id, "price", price, caller=self)
async def _connect(self) -> None: logger.info("url:", self._url, caller=self) proxy = config.proxy session = aiohttp.ClientSession() try: self._ws = await session.ws_connect(self._url, proxy=proxy) except aiohttp.ClientConnectorError: logger.error("connect to Websocket server error! url:", self._url, caller=self) return if self._connected_callback: SingleTask.run(self._connected_callback) SingleTask.run(self._receive)
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)
def start(self, config_file=None, entrance_func=None) -> None: """Start the event loop.""" def keyboard_interrupt(s, f): print("KeyboardInterrupt (ID: {}) has been caught. Cleaning up...".format(s)) self.stop() signal.signal(signal.SIGINT, keyboard_interrupt) self._initialize(config_file) if entrance_func: if inspect.iscoroutinefunction(entrance_func): self.loop.create_task(entrance_func()) else: entrance_func() logger.info("start io loop ...", caller=self) self.loop.run_forever()
async def on_order_update_callback(self, order: Order): """不会主动被调用,当订单有变化的时候会被调用""" logger.info("order", order, caller=self) if order.status == ORDER_STATUS_FILLED: # 完全成交 self._order_id = "" self._price = 0.0 # TODO: 完全对冲 elif order.status == ORDER_STATUS_PARTIAL_FILLED: # 部分成交 # TODO: 部分对冲 pass elif order.status == ORDER_STATUS_FAILED: # 报警, 触发风控 pass else: return
async def _initialize(self, event: Event, callback=None, multi=False): if event.queue: await self._channel.queue_declare(queue_name=event.queue, auto_delete=True) queue_name = event.queue else: result = await self._channel.queue_declare(exclusive=True) queue_name = result["queue"] await self._channel.queue_bind(queue_name=queue_name, exchange_name=event.exchange, routing_key=event.routing_key) await self._channel.basic_qos(prefetch_count=event.prefetch_count) if callback: if multi: await self._channel.basic_consume(callback=callback, queue_name=queue_name, no_ack=True) logger.info("multi message queue:", queue_name, caller=self) else: await self._channel.basic_consume(self._on_consume_event_msg, queue_name=queue_name) logger.info("queue:", queue_name, caller=self) self._add_event_handler(event, callback)
async def process_binary(self, raw): """Process binary message that received from websocket. Args: raw: Binary message received from websocket. Returns: None. """ 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: 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) await self._auth_success_callback() elif op == "ping": # ping params = {"op": "pong", "ts": msg["ts"]} await self._ws.send(params) elif op == "sub": if msg["topic"] != self._order_channel: return if msg["err-code"] != 0: e = Error("subscribe order event error: {}".format(msg)) SingleTask.run(self._error_callback, e) SingleTask.run(self._init_callback, False) else: SingleTask.run(self._init_callback, True) elif op == "notify": if msg["topic"] != self._order_channel: return data = msg["data"] data["utime"] = msg["ts"] self._update_order(data)
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)
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", "Kline", "Trade"] 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 on_error_callback(self, error: Error, **kwargs): """执行过程中有任意的失败情况下都会报错""" self._is_ok = False logger.info("error", error, caller=self) quant.stop()
async def revoke_order(self, order_id: str): """撤销订单""" _, error = await self._trade.revoke_order(order_id) if error: return logger.info("order_id: ", order_id, caller=self)
async def get_trade(self): """获取最新的逐笔成交数据""" symbol = self.symbol success, error = await self._rest_api.get_latest_trade(symbol, 10) logger.info("success", success, caller=self) logger.info("error", error, caller=self)
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)
async def get_orderbook(self): """获取盘口订单薄数据""" symbol = self.symbol success, error = await self._rest_api.get_orderbook(symbol, 10) logger.info("success", success, caller=self)
async def on_event_error_callback(self, error, **kwargs): """错误回调""" logger.info("error:", error, "kwargs:", kwargs, caller=self)
async def on_event_init_callback(self, success: bool, **kwargs): """初始化回调""" logger.info("success:", success, "kwargs:", kwargs, caller=self)
def stop(self) -> None: """Stop the event loop.""" logger.info("stop io loop.", caller=self) # TODO: clean up running coroutine self.loop.stop()