async def _initialize(self): """ 初始化 """ # 获取当前所有未完全成交的订单 order_nos, error = await self._rest_api.get_open_order_nos( self._raw_symbol) if error: e = Error("get open order nos failed: {}".format(error)) logger.error(e, caller=self) if self._init_success_callback: SingleTask.run(self._init_success_callback, False, e) return if order_nos: success, error = await self._rest_api.get_order_list(order_nos) if error: e = Error("get order infos failed: {}".format(error)) logger.error(e, caller=self) if self._init_success_callback: SingleTask.run(self._init_success_callback, False, e) return for order_info in success: await self._update_order(order_info) if self._init_success_callback: SingleTask.run(self._init_success_callback, True, None)
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: e = Error( "Websocket connection authorized failed: {}".format(msg)) logger.error(e, caller=self) SingleTask.run(self._init_success_callback, False, e) 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._init_success_callback, False, e) else: SingleTask.run(self._init_success_callback, True, None) elif op == "notify": # 订单更新通知 if msg["topic"] != self._order_channel: return data = msg["data"] data["utime"] = msg["ts"] self._update_order(data)
def __init__(self, **kwargs): """ 初始化 """ e = None if not kwargs.get("account"): e = Error("param account miss") if not kwargs.get("strategy"): e = Error("param strategy miss") if not kwargs.get("symbol"): e = Error("param symbol miss") if not kwargs.get("host"): kwargs["host"] = "https://www.okex.com" if not kwargs.get("wss"): kwargs["wss"] = "wss://real.okex.com:10442" if not kwargs.get("access_key"): e = Error("param access_key miss") if not kwargs.get("secret_key"): e = Error("param secret_key miss") if not kwargs.get("passphrase"): e = Error("param passphrase miss") if e: logger.error(e, caller=self) if kwargs.get("init_success_callback"): SingleTask.run(kwargs["init_success_callback"], False, e) return self._account = kwargs["account"] self._strategy = kwargs["strategy"] self._platform = OKEX_FUTURE self._symbol = kwargs["symbol"] self._host = kwargs["host"] self._wss = kwargs["wss"] self._access_key = kwargs["access_key"] self._secret_key = kwargs["secret_key"] self._passphrase = kwargs["passphrase"] self._asset_update_callback = kwargs.get("asset_update_callback") self._order_update_callback = kwargs.get("order_update_callback") self._position_update_callback = kwargs.get("position_update_callback") self._init_success_callback = kwargs.get("init_success_callback") url = self._wss + "/ws/v3" super(OKExFutureTrade, self).__init__(url, send_hb_interval=5) self.heartbeat_msg = "ping" self._orders = {} # 订单 self._position = Position(self._platform, self._account, self._strategy, self._symbol) # 仓位 # 订阅频道 # ch_account = "futures/account:BTC" self._order_channel = "futures/order:{symbol}".format( symbol=self._symbol) self._position_channel = "futures/position:{symbol}".format( symbol=self._symbol) # 标记订阅订单、持仓是否成功 self._subscribe_order_ok = False self._subscribe_position_ok = False # 初始化 REST API 对象 self._rest_api = OKExFutureRestAPI(self._host, self._access_key, self._secret_key, self._passphrase) self.initialize()
def __init__(self, **kwargs): """ 初始化 """ e = None if not kwargs.get("account"): e = Error("param account miss") if not kwargs.get("strategy"): e = Error("param strategy miss") if not kwargs.get("symbol"): e = Error("param symbol miss") if not kwargs.get("host"): kwargs["host"] = "https://www.bitmex.com" if not kwargs.get("wss"): kwargs["wss"] = "wss://www.bitmex.com" if not kwargs.get("access_key"): e = Error("param access_key miss") if not kwargs.get("secret_key"): e = Error("param secret_key miss") if e: logger.error(e, caller=self) if kwargs.get("init_success_callback"): SingleTask.run(kwargs["init_success_callback"], False, e) return self._account = kwargs["account"] self._strategy = kwargs["strategy"] self._platform = BITMEX self._symbol = kwargs["symbol"] self._host = kwargs["host"] self._wss = kwargs["wss"] self._access_key = kwargs["access_key"] self._secret_key = kwargs["secret_key"] self._asset_update_callback = kwargs.get("asset_update_callback") self._order_update_callback = kwargs.get("order_update_callback") self._position_update_callback = kwargs.get("position_update_callback") self._init_success_callback = kwargs.get("init_success_callback") url = self._wss + "/realtime" super(BitmexTrade, self).__init__(url, send_hb_interval=5) self.heartbeat_msg = "ping" self._order_channel = "order:{symbol}".format( symbol=self._symbol) # 订单订阅频道 self._position_channel = "position:{symbol}".format( symbol=self._symbol) # 持仓订阅频道 # 标记订阅订单、持仓是否成功 self._subscribe_order_ok = False self._subscribe_position_ok = False self._assets = { } # 资产 {"XBT": {"free": "1.1", "locked": "2.2", "total": "3.3"}, ... } self._orders = {} self._position = Position(self._platform, self._account, self._strategy, self._symbol) # 仓位 # 初始化REST API对象 self._rest_api = BitmexAPI(self._host, self._access_key, self._secret_key) # 初始化资产订阅 if self._asset_update_callback: AssetSubscribe(self._platform, self._account, self.on_event_asset_update) self.initialize()
def publish(self): """ 发布此事件 """ from quant.quant import quant SingleTask.run(quant.handle_event_center.publish, 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)
def _update_order(self, order_info): """ Order update. Args: order_info: Order information. Returns: None. """ order_no = str(order_info["order_id"]) state = order_info["state"] remain = int(order_info["size"]) - int(order_info["filled_qty"]) ctime = tools.utctime_str_to_mts(order_info["timestamp"]) if state == "-2": status = ORDER_STATUS_FAILED elif state == "-1": status = ORDER_STATUS_CANCELED elif state == "0": status = ORDER_STATUS_SUBMITTED elif state == "1": status = ORDER_STATUS_PARTIAL_FILLED elif state == "2": status = ORDER_STATUS_FILLED else: return None 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": order_info["client_oid"], "action": ORDER_ACTION_BUY if order_info["type"] in ["1", "4"] else ORDER_ACTION_SELL, "symbol": self._symbol, "price": order_info["price"], "quantity": order_info["size"], "trade_type": int(order_info["type"]) } order = Order(**info) order.remain = remain order.status = status order.avg_price = order_info["price_avg"] order.ctime = ctime order.utime = ctime self._orders[order_no] = order 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_no)
def __init__(self, **kwargs): """Initialize.""" e = None if not kwargs.get("account"): e = Error("param account miss") if not kwargs.get("strategy"): e = Error("param strategy miss") if not kwargs.get("symbol"): e = Error("param symbol miss") if not kwargs.get("host"): kwargs["host"] = "https://www.okex.com" if not kwargs.get("wss"): kwargs["wss"] = "wss://real.okex.com:8443" if not kwargs.get("access_key"): e = Error("param access_key miss") if not kwargs.get("secret_key"): e = Error("param secret_key miss") if not kwargs.get("passphrase"): e = Error("param passphrase miss") if e: logger.error(e, caller=self) if kwargs.get("init_success_callback"): SingleTask.run(kwargs["init_success_callback"], False, e) return self._account = kwargs["account"] self._strategy = kwargs["strategy"] self._platform = OKEX_FUTURE self._symbol = kwargs["symbol"] self._host = kwargs["host"] self._wss = kwargs["wss"] self._access_key = kwargs["access_key"] self._secret_key = kwargs["secret_key"] self._passphrase = kwargs["passphrase"] self._asset_update_callback = kwargs.get("asset_update_callback") self._order_update_callback = kwargs.get("order_update_callback") self._position_update_callback = kwargs.get("position_update_callback") self._init_success_callback = kwargs.get("init_success_callback") url = self._wss + "/ws/v3" super(OKExFutureTrade, self).__init__(url, send_hb_interval=5) self.heartbeat_msg = "ping" self._assets = { } # Asset object. e.g. {"BTC": {"free": "1.1", "locked": "2.2", "total": "3.3"}, ... } self._orders = {} # Order objects. e.g. {"order_no": Order, ... } self._position = Position(self._platform, self._account, self._strategy, self._symbol) # Subscribing our channels. self._order_channel = "futures/order:{symbol}".format( symbol=self._symbol) self._position_channel = "futures/position:{symbol}".format( symbol=self._symbol) # If our channels that subscribed successfully. self._subscribe_order_ok = False self._subscribe_position_ok = False # Initializing our REST API client. self._rest_api = OKExFutureRestAPI(self._host, self._access_key, self._secret_key, self._passphrase) # Subscribing our asset event. if self._asset_update_callback: AssetSubscribe(self._platform, self._account, self.on_event_asset_update) self.initialize()
async def _update_order(self, order_info): """ 处理委托单更新 @param order_info 委托单详情 """ if not order_info: return status_updated = False order_no = str(order_info["orderNo"]) state = order_info["state"] order = self._orders.get(order_no) if not order: info = { "platform": self._platform, "account": self._account, "strategy": self._strategy, "order_no": order_no, "action": order_info["action"], "symbol": self._symbol, "price": order_info["priceLimit"], "quantity": order_info["quantity"], "remain": order_info["quantityRemaining"], "avg_price": order_info["priceLimit"] } order = Order(**info) self._orders[order_no] = order # 已提交 if state == "UNDEAL" or state == "PROCESSING": if order.status != ORDER_STATUS_SUBMITTED: order.status = ORDER_STATUS_SUBMITTED status_updated = True # 订单部分成交 elif state == "PARTDEAL": order.status = ORDER_STATUS_PARTIAL_FILLED if order.order_type == ORDER_TYPE_LIMIT: if float(order.remain) != float(order_info["quantityRemaining"]): status_updated = True order.remain = float(order_info["quantityRemaining"]) else: if float(order.remain) != float(order_info["amountRemaining"]): status_updated = True order.remain = float(order_info["amountRemaining"]) # 订单成交完成 elif state == "DEAL": order.status = ORDER_STATUS_FILLED order.remain = 0 status_updated = True # 订单取消 elif state == "CANCEL": order.status = ORDER_STATUS_CANCELED if order.order_type == ORDER_TYPE_LIMIT: if float(order.remain) != float(order_info["quantityRemaining"]): order.remain = float(order_info["quantityRemaining"]) else: if float(order.remain) != float(order_info["amountRemaining"]): order.remain = float(order_info["amountRemaining"]) status_updated = True else: logger.warn("state error! order_info:", order_info, caller=self) return # 有状态更新 执行回调 if status_updated: order.ctime = order_info["utcCreate"] order.utime = order_info["utcUpdate"] if self._order_update_callback: SingleTask.run(self._order_update_callback, copy.copy(order)) # 删除已完成订单 if order.status in [ORDER_STATUS_FAILED, ORDER_STATUS_CANCELED, ORDER_STATUS_FILLED]: self._orders.pop(order_no)
async def on_event_update_orderbook(self, *args, **kwargs): """ Loop run to fetch orderbook information. """ for symbol in self._symbols: SingleTask.run(self.get_newest_orderbook, symbol)
def __init__(self, **kwargs): """Initialize Trade module.""" e = None if not kwargs.get("account"): e = Error("param account miss") if not kwargs.get("strategy"): e = Error("param strategy miss") if not kwargs.get("symbol"): e = Error("param symbol miss") if not kwargs.get("host"): kwargs["host"] = "https://fapi.binance.com" if not kwargs.get("wss"): kwargs["wss"] = "wss://fstream.binance.com" if not kwargs.get("access_key"): e = Error("param access_key miss") if not kwargs.get("secret_key"): e = Error("param secret_key miss") if e: logger.error(e, caller=self) if kwargs.get("init_success_callback"): SingleTask.run(kwargs["init_success_callback"], False, e) return self._account = kwargs["account"] self._strategy = kwargs["strategy"] self._platform = BINANCE_FUTURE self._symbol = kwargs["symbol"] self._host = kwargs["host"] self._wss = kwargs["wss"] self._access_key = kwargs["access_key"] self._secret_key = kwargs["secret_key"] self._asset_update_callback = kwargs.get("asset_update_callback") self._order_update_callback = kwargs.get("order_update_callback") self._position_update_callback = kwargs.get("position_update_callback") self._init_success_callback = kwargs.get("init_success_callback") self._ok = False # Initialize successfully ? self._raw_symbol = self._symbol # Row symbol name, same as Binance Exchange. self._listen_key = None # Listen key for Websocket authentication. self._assets = { } # Asset data. e.g. {"BTC": {"free": "1.1", "locked": "2.2", "total": "3.3"}, ... } self._orders = {} # Order data. e.g. {order_no: order, ... } self._position = Position(self._platform, self._account, self._strategy, self._symbol) # 仓位 # Initialize our REST API client. self._rest_api = BinanceFutureRestAPI(self._host, self._access_key, self._secret_key) # Subscribe our AssetEvent. if self._asset_update_callback: AssetSubscribe(self._platform, self._account, self.on_event_asset_update) # Create a loop run task to reset listen key every 20 minutes. LoopRunTask.register(self._reset_listen_key, 60 * 20) # Create a loop run task to check position information per 1 second. LoopRunTask.register(self._check_position_update, 1) # Create a loop run task to send ping message to server per 30 seconds. # LoopRunTask.register(self._send_heartbeat_msg, 10) # Create a coroutine to initialize Websocket connection. SingleTask.run(self._init_websocket)
def initialize(self): LoopRunTask.register(self._check_connection, self._check_conn_interval) if self._send_hb_interval > 0: LoopRunTask.register(self._send_heartbeat_msg, self._send_hb_interval) SingleTask.run(self._connect)
def _update_order(self, order_info): """ 更新订单信息 @param order_info 订单信息 * NOTE: order-state: 订单状态, submitting , submitted 已提交, partial-filled 部分成交, partial-canceled 部分成交撤销, filled 完全成交, canceled 已撤销 """ order_no = str(order_info["order-id"]) action = ORDER_ACTION_BUY if order_info["order-type"] in [ "buy-market", "buy-limit" ] else ORDER_ACTION_SELL state = order_info["order-state"] remain = "%.8f" % float(order_info["unfilled-amount"]) avg_price = "%.8f" % float(order_info["price"]) ctime = order_info["created-at"] utime = order_info["utime"] if state == "canceled": status = ORDER_STATUS_CANCELED elif state == "partial-canceled": status = ORDER_STATUS_CANCELED elif state == "submitting": status = ORDER_STATUS_SUBMITTED elif state == "submitted": status = ORDER_STATUS_SUBMITTED elif state == "partial-filled": status = ORDER_STATUS_PARTIAL_FILLED elif state == "filled": status = ORDER_STATUS_FILLED else: logger.error("status error! order_info:", order_info, caller=self) return None order = self._orders.get(order_no) if not order: info = { "platform": self._platform, "account": self._account, "strategy": self._strategy, "order_no": order_no, "action": action, "symbol": self._symbol, "price": "%.8f" % float(order_info["order-price"]), "quantity": "%.8f" % float(order_info["order-amount"]), "remain": remain, "status": status } order = Order(**info) self._orders[order_no] = order order.remain = remain order.status = status order.avg_price = avg_price order.ctime = ctime order.utime = utime if status in [ ORDER_STATUS_FAILED, ORDER_STATUS_CANCELED, ORDER_STATUS_FILLED ]: self._orders.pop(order_no) if order and self._order_update_callback: SingleTask.run(self._order_update_callback, copy.copy(order)) # publish order EventOrder(**order.__dict__).publish() logger.info("symbol:", order.symbol, "order:", order, caller=self)
def _update_asset(self, data): ast = self._convert_asset_format(data) if self.cb.on_asset_update_callback: SingleTask.run(self.cb.on_asset_update_callback, ast)
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"): state = State( self._platform, self._account, "Websocket connection authorized failed: {}".format(msg), State.STATE_CODE_GENERAL_ERROR) logger.error(state, caller=self) SingleTask.run(self.cb.on_state_update_callback, state) return logger.info("Websocket connection authorized successfully.", caller=self) await self._auth_success_callback() # Subscribe response message received. elif msg.get("event") == "subscribe": #msg.get("channel") self._subscribe_response_count = self._subscribe_response_count + 1 #每来一次订阅响应计数就加一 count = len(self._account_channel) + len( self._order_channel) #应该要返回的订阅响应数 if self._subscribe_response_count == count: #所有的订阅都成功了,通知上层接口都准备好了 state = State(self._platform, self._account, "Environment ready", State.STATE_CODE_READY) SingleTask.run(self.cb.on_state_update_callback, state) elif msg.get("event") == "error": state = State(self._platform, self._account, "Websocket processing failed: {}".format(msg), State.STATE_CODE_GENERAL_ERROR) SingleTask.run(self.cb.on_state_update_callback, state) # Order update message received. elif msg.get("table") == "spot/order": """ { "table":"spot/order", "data":[ { "client_oid":"", "filled_notional":"0", "filled_size":"0", "instrument_id":"ETC-USDT", "last_fill_px":"0", "last_fill_qty":"0", "last_fill_time":"1970-01-01T00:00:00.000Z", "margin_trading":"1", "notional":"", "order_id":"3576398568830976", "order_type":"0", "price":"5.826", "side":"buy", "size":"0.1", "state":"0", "status":"open", "timestamp":"2019-09-24T06:45:11.394Z", "type":"limit", "created_at":"2019-09-24T06:45:11.394Z" } ] } """ for data in msg["data"]: self._update_order(data) elif msg.get("table") == "spot/account": self._update_asset(msg["data"])
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) # 登陆成功之后再订阅数据 if msg.get("event") == "login": if not msg.get("success"): 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) return 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(msg)) if self._init_success_callback: SingleTask.run(self._init_success_callback, False, e) return for order_info in order_infos: order_info["ctime"] = order_info["created_at"] order_info["utime"] = order_info["timestamp"] order = self._update_order(order_info) if self._order_update_callback: SingleTask.run(self._order_update_callback, order) # 订阅 order data = {"op": "subscribe", "args": [self._order_channel]} await self.ws.send_json(data) return # 订阅返回消息 if msg.get("event") == "subscribe": if msg.get("channel") == self._order_channel: if self._init_success_callback: SingleTask.run(self._init_success_callback, True, None) else: if self._init_success_callback: e = Error("subscribe order event error: {}".format(msg)) SingleTask.run(self._init_success_callback, False, e) return # 订单更新 if msg.get("table") == "spot/order": for data in msg["data"]: data["ctime"] = data["timestamp"] data["utime"] = data["last_fill_time"] order = self._update_order(data) if order and self._order_update_callback: SingleTask.run(self._order_update_callback, order)
def __init__(self, strategy=None, platform=None, symbol=None, host=None, wss=None, account=None, access_key=None, secret_key=None, passphrase=None, asset_update_callback=None, order_update_callback=None, position_update_callback=None, init_success_callback=None, **kwargs): """initialize trade object.""" kwargs["strategy"] = strategy kwargs["platform"] = platform kwargs["symbol"] = symbol kwargs["host"] = host kwargs["wss"] = wss kwargs["account"] = account kwargs["access_key"] = access_key kwargs["secret_key"] = secret_key kwargs["passphrase"] = passphrase kwargs["asset_update_callback"] = asset_update_callback kwargs["order_update_callback"] = self._on_order_update_callback kwargs["position_update_callback"] = self._on_position_update_callback kwargs["init_success_callback"] = self._on_init_success_callback self._order_update_callback = order_update_callback self._position_update_callback = position_update_callback self._init_success_callback = init_success_callback if platform == const.OKEX: from quant.platform.okex import OKExTrade as T elif platform == const.OKEX_MARGIN: from quant.platform.okex_margin import OKExMarginTrade as T elif platform == const.OKEX_FUTURE: from quant.platform.okex_future import OKExFutureTrade as T elif platform == const.OKEX_SWAP: from quant.platform.okex_swap import OKExSwapTrade as T elif platform == const.DERIBIT: from quant.platform.deribit import DeribitTrade as T elif platform == const.BITMEX: from quant.platform.bitmex import BitmexTrade as T elif platform == const.BINANCE: from quant.platform.binance import BinanceTrade as T elif platform == const.HUOBI: from quant.platform.huobi import HuobiTrade as T elif platform == const.COINSUPER: from quant.platform.coinsuper import CoinsuperTrade as T elif platform == const.COINSUPER_PRE: from quant.platform.coinsuper_pre import CoinsuperPreTrade as T elif platform == const.KRAKEN: from quant.platform.kraken import KrakenTrade as T elif platform == const.GATE: from quant.platform.gate import GateTrade as T elif platform == const.KUCOIN: from quant.platform.kucoin import KucoinTrade as T else: logger.error("platform error:", platform, caller=self) e = Error("platform error") SingleTask.run(self._init_success_callback, False, e) return kwargs.pop("platform") self._t = T(**kwargs)
def __init__(self, **kwargs): """Initialize.""" e = None if not kwargs.get("account"): e = Error("param account miss") if not kwargs.get("strategy"): e = Error("param strategy miss") if not kwargs.get("symbol"): e = Error("param symbol miss") if not kwargs.get("contract_type"): e = Error("param contract_type miss") if not kwargs.get("contract_code"): e = Error("param contract_code miss") if not kwargs.get("host"): kwargs["host"] = "https://api.hbdm.com" if not kwargs.get("wss"): kwargs["wss"] = "wss://api.hbdm.com" if not kwargs.get("access_key"): e = Error("param access_key miss") if not kwargs.get("secret_key"): e = Error("param secret_key miss") if e: logger.error(e, caller=self) if kwargs.get("init_success_callback"): SingleTask.run(kwargs["init_success_callback"], False, e) return self._account = kwargs["account"] self._strategy = kwargs["strategy"] self._platform = HUOBI_FUTURE self._symbol = kwargs["symbol"] self._contract_type = kwargs["contract_type"] self._contract_code = kwargs["contract_code"] self._host = kwargs["host"] self._wss = kwargs["wss"] self._access_key = kwargs["access_key"] self._secret_key = kwargs["secret_key"] self._asset_update_callback = kwargs.get("asset_update_callback") self._order_update_callback = kwargs.get("order_update_callback") self._position_update_callback = kwargs.get("position_update_callback") self._init_success_callback = kwargs.get("init_success_callback") url = self._wss + "/notification" self._ws = Websocket(url, self.connected_callback, process_binary_callback=self.process_binary) self._ws.initialize() self._assets = { } # Asset detail, {"BTC": {"free": "1.1", "locked": "2.2", "total": "3.3"}, ... }. self._orders = {} # Order objects, {"order_id": order, ...}. self._position = Position(self._platform, self._account, self._strategy, self._contract_code) self._order_channel = "orders.{symbol}".format( symbol=self._symbol.lower()) self._position_channel = "positions.{symbol}".format( symbol=self._symbol.lower()) self._subscribe_order_ok = False self._subscribe_position_ok = False self._rest_api = HuobiFutureRestAPI(self._host, self._access_key, self._secret_key) # Subscribe AssetEvent. if self._asset_update_callback: AssetSubscribe(self._platform, self._account, self.on_event_asset_update)
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._init_success_callback, False, e) return logger.info("Websocket connection authorized successfully.", caller=self) # Fetch orders from server. (open + partially filled) result, error = await self._rest_api.get_order_list( self._symbol, 6) if error: e = Error("get open orders error: {}".format(error)) SingleTask.run(self._init_success_callback, False, e) return if len(result) > 100: logger.warn("order length too long! (more than 100)", caller=self) for order_info in result["order_info"]: self._update_order(order_info) # Fetch positions from server. position, error = await self._rest_api.get_position(self._symbol) if error: e = Error("get position error: {}".format(error)) SingleTask.run(self._init_success_callback, False, e) return if len(position["holding"]) > 0: self._update_position(position["holding"][0]) # Subscribe order channel and position channel. data = { "op": "subscribe", "args": [self._order_channel, self._position_channel] } await self.ws.send_json(data) return # Subscribe response message received. if msg.get("event") == "subscribe": if msg.get("channel") == self._order_channel: self._subscribe_order_ok = True if msg.get("channel") == self._position_channel: self._subscribe_position_ok = True if self._subscribe_order_ok and self._subscribe_position_ok: SingleTask.run(self._init_success_callback, True, None) return # Order update message received. if msg.get("table") == "futures/order": for data in msg["data"]: self._update_order(data) return # Position update message receive. if msg.get("table") == "futures/position": for data in msg["data"]: self._update_position(data)
def _update_order(self, order_info): """ Order update. Args: order_info: Order information. """ if order_info["contract_code"] != self._contract_code: return order_no = str(order_info["order_id"]) status = order_info["status"] order = self._orders.get(order_no) if not order: if order_info["direction"] == "buy": if order_info["offset"] == "open": trade_type = TRADE_TYPE_BUY_OPEN else: trade_type = TRADE_TYPE_BUY_CLOSE else: if order_info["offset"] == "close": trade_type = TRADE_TYPE_SELL_CLOSE else: trade_type = TRADE_TYPE_SELL_OPEN info = { "platform": self._platform, "account": self._account, "strategy": self._strategy, "order_no": order_no, "action": ORDER_ACTION_BUY if order_info["direction"] == "buy" else ORDER_ACTION_SELL, "symbol": self._contract_code, "price": order_info["price"], "quantity": order_info["volume"], "trade_type": trade_type } order = Order(**info) self._orders[order_no] = order if status in [1, 2, 3]: order.status = ORDER_STATUS_SUBMITTED elif status == 4: order.status = ORDER_STATUS_PARTIAL_FILLED order.remain = int(order.quantity) - int( order_info["trade_volume"]) elif status == 6: order.status = ORDER_STATUS_FILLED order.remain = 0 elif status in [5, 7]: order.status = ORDER_STATUS_CANCELED order.remain = int(order.quantity) - int( order_info["trade_volume"]) else: return order.avg_price = order_info["trade_avg_price"] order.ctime = order_info["created_at"] order.utime = order_info["ts"] SingleTask.run(self._order_update_callback, copy.copy(order)) # Delete order that already completed. if order.status in [ ORDER_STATUS_FAILED, ORDER_STATUS_CANCELED, ORDER_STATUS_FILLED ]: self._orders.pop(order_no)
async def _update_order(self, order_info): """ Update order object. Args: order_info: Order information. """ if not order_info: return status_updated = False order_no = str(order_info["orderNo"]) state = order_info["state"] order = self._orders.get(order_no) if not order: info = { "platform": self._platform, "account": self._account, "strategy": self._strategy, "order_no": order_no, "action": order_info["action"], "symbol": self._symbol, "price": order_info["priceLimit"], "quantity": order_info["quantity"], "remain": order_info["quantityRemaining"], "avg_price": order_info["priceLimit"] } order = Order(**info) self._orders[order_no] = order if state == "UNDEAL" or state == "PROCESSING": if order.status != ORDER_STATUS_SUBMITTED: order.status = ORDER_STATUS_SUBMITTED status_updated = True elif state == "PARTDEAL": order.status = ORDER_STATUS_PARTIAL_FILLED if order.order_type == ORDER_TYPE_LIMIT: if float(order.remain) != float( order_info["quantityRemaining"]): status_updated = True order.remain = float(order_info["quantityRemaining"]) else: if float(order.remain) != float(order_info["amountRemaining"]): status_updated = True order.remain = float(order_info["amountRemaining"]) elif state == "DEAL": order.status = ORDER_STATUS_FILLED order.remain = 0 status_updated = True elif state == "CANCEL": order.status = ORDER_STATUS_CANCELED status_updated = True else: logger.warn("state error! order_info:", order_info, caller=self) return # If order status updated, callback newest order information. if status_updated: order.ctime = order_info["utcCreate"] order.utime = order_info["utcUpdate"] if self._order_update_callback: SingleTask.run(self._order_update_callback, copy.copy(order)) # Delete order that already completed. if order.status in [ ORDER_STATUS_FAILED, ORDER_STATUS_CANCELED, ORDER_STATUS_FILLED ]: self._orders.pop(order_no)
async def _update_order(self, order_info): """ Update order object. Args: order_info: Order information. """ if not order_info: return status_updated = False order_no = str(order_info["orderNumber"]) state = order_info["status"] order = self._orders.get(order_no) if not order: info = { "platform": self._platform, "account": self._account, "strategy": self._strategy, "order_no": order_no, "action": ORDER_ACTION_BUY if order_info["type"] == "buy" else ORDER_ACTION_SELL, "symbol": self._symbol, "price": order_info["rate"], "quantity": order_info["amount"], "remain": order_info["amount"], "avg_price": order_info["filledRate"] } order = Order(**info) self._orders[order_no] = order if state == "open": filled_amount = float(order_info["filledAmount"]) if filled_amount == 0: state = ORDER_STATUS_SUBMITTED if order.status != state: order.status = ORDER_STATUS_SUBMITTED status_updated = True else: remain = float(order.quantity) - filled_amount if order.remain != remain: order.status = ORDER_STATUS_PARTIAL_FILLED order.remain = remain status_updated = True elif state == "closed": order.status = ORDER_STATUS_FILLED order.remain = 0 status_updated = True elif state == "cancelled": order.status = ORDER_STATUS_CANCELED status_updated = True else: logger.warn("state error! order_info:", order_info, caller=self) return if status_updated: order.avg_price = order_info["filledRate"] order.ctime = int(order_info["timestamp"] * 1000) order.utime = int(order_info["timestamp"] * 1000) if self._order_update_callback: SingleTask.run(self._order_update_callback, copy.copy(order)) # Delete order that already completed. if order.status in [ ORDER_STATUS_FAILED, ORDER_STATUS_CANCELED, ORDER_STATUS_FILLED ]: self._orders.pop(order_no)
def publish(self): """Publish this event.""" from quant.quant import quant SingleTask.run(quant.event_center.publish, self)
async def _update_order(self, order_info): """ Update order object. Args: order_info: Order information. """ if not order_info: return status_updated = False order_no = str(order_info["order_id"]) status = order_info[ "status"] # 订单状态,0-未成交,1-部分成交,2-完全成交,3-已撤销未成交,4-已撤销部分成交 order = self._orders.get(order_no) if not order: info = { "platform": self._platform, "account": self._account, "strategy": self._strategy, "order_no": order_no, "action": ORDER_ACTION_BUY if order_info["type"] == "buy" else ORDER_ACTION_SELL, "symbol": self._symbol, "price": order_info["price"], "quantity": order_info["amount"], "remain": order_info["amount"], "avg_price": order_info["avg_price"] } order = Order(**info) self._orders[order_no] = order if status == 0: if order.status != ORDER_STATUS_SUBMITTED: order.status = ORDER_STATUS_SUBMITTED status_updated = True elif status == 1: remain = float(order_info["amount"]) - float( order_info["executed_amount"]) if order.remain != remain: order.remain = remain order.status = ORDER_STATUS_PARTIAL_FILLED status_updated = True elif status == 2: order.status = ORDER_STATUS_FILLED order.remain = 0 status_updated = True elif status == 3 or status == 4: order.status = ORDER_STATUS_CANCELED remain = float(order_info["amount"]) - float( order_info["executed_amount"]) order.remain = remain status_updated = True else: logger.warn("state error! order_info:", order_info, caller=self) return if status_updated: order.avg_price = order_info["avg_price"] order.ctime = int(order_info["created_date"] * 1000) order.utime = int(order_info["finished_date"] * 1000) SingleTask.run(self._order_update_callback, copy.copy(order)) # Delete order that already completed. if order.status in [ ORDER_STATUS_FAILED, ORDER_STATUS_CANCELED, ORDER_STATUS_FILLED ]: self._orders.pop(order_no)
def _bind_and_consume(self): async def do_them(): for event, callback, multi in self._subscribers: await self._initialize(event, callback, multi) SingleTask.run(do_them)
def initialize(self): LoopRunTask.register(self._check_connection, self._check_conn_interval) SingleTask.run(self._connect)
async def on_event_asset_update(self, asset: Asset): """ 资产数据更新回调 """ self._assets = asset SingleTask.run(self._asset_update_callback, asset)
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._init_success_callback, False, e) 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._init_success_callback, False, e) 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_json(data) return # Subscribe response message received. if msg.get("event") == "subscribe": if msg.get("channel") == self._order_channel: SingleTask.run(self._init_success_callback, True, None) else: e = Error("subscribe order event error: {}".format(msg)) SingleTask.run(self._init_success_callback, False, e) 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 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) # 登陆成功之后再订阅数据 if msg.get("event") == "login": if not msg.get("success"): 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) return logger.info("Websocket connection authorized successfully.", caller=self) # 获取当前等待成交和部分成交的订单信息 result, error = await self._rest_api.get_order_list( self._symbol, 6) 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 result["order_info"]: order = self._update_order(order_info) if self._order_update_callback: SingleTask.run(self._order_update_callback, order) # 获取当前持仓 position, error = await self._rest_api.get_position(self._symbol) if error: e = Error("get position error: {}".format(error)) if self._init_success_callback: SingleTask.run(self._init_success_callback, False, e) return if len(position["holding"]) > 0: self._update_position(position["holding"][0]) if self._position_update_callback: SingleTask.run(self._position_update_callback, self.position) # 订阅account, order, position data = { "op": "subscribe", "args": [self._order_channel, self._position_channel] } await self.ws.send_json(data) return # 订阅返回消息 if msg.get("event") == "subscribe": if msg.get("channel") == self._order_channel: self._subscribe_order_ok = True if msg.get("channel") == self._position_channel: self._subscribe_position_ok = True 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") == "futures/order": for data in msg["data"]: order = self._update_order(data) if order and self._order_update_callback: SingleTask.run(self._order_update_callback, order) return # 持仓更新 if msg.get("table") == "futures/position": for data in msg["data"]: self._update_position(data) if self._position_update_callback: SingleTask.run(self._position_update_callback, self.position)
def __init__(self, **kwargs): """ 初始化 """ e = None if not kwargs.get("account"): e = Error("param account miss") if not kwargs.get("strategy"): e = Error("param strategy miss") if not kwargs.get("symbol"): e = Error("param symbol miss") if not kwargs.get("host"): kwargs["host"] = "https://www.okex.com" if not kwargs.get("wss"): kwargs["wss"] = "wss://real.okex.com:10442" if not kwargs.get("access_key"): e = Error("param access_key miss") if not kwargs.get("secret_key"): e = Error("param secret_key miss") if not kwargs.get("passphrase"): e = Error("param passphrase miss") if e: logger.error(e, caller=self) if kwargs.get("init_success_callback"): SingleTask.run(kwargs["init_success_callback"], False, e) return self._account = kwargs["account"] self._strategy = kwargs["strategy"] self._platform = OKEX self._symbol = kwargs["symbol"] self._host = kwargs["host"] self._wss = kwargs["wss"] self._access_key = kwargs["access_key"] self._secret_key = kwargs["secret_key"] self._passphrase = kwargs["passphrase"] self._asset_update_callback = kwargs.get("asset_update_callback") self._order_update_callback = kwargs.get("order_update_callback") self._init_success_callback = kwargs.get("init_success_callback") self._raw_symbol = self._symbol.replace("/", "-") # 转换成交易所对应的交易对格式 self._order_channel = "spot/order:{symbol}".format( symbol=self._raw_symbol) # 订单订阅频道 url = self._wss + "/ws/v3" super(OKExTrade, self).__init__(url, send_hb_interval=5) self.heartbeat_msg = "ping" self._assets = { } # 资产 {"BTC": {"free": "1.1", "locked": "2.2", "total": "3.3"}, ... } self._orders = {} # 订单 {"order_no": order, ... } # 初始化 REST API 对象 self._rest_api = OKExRestAPI(self._host, self._access_key, self._secret_key, self._passphrase) # 初始化资产订阅 if self._asset_update_callback: AssetSubscribe(self._platform, self._account, self.on_event_asset_update) self.initialize()