async def connected_callback(self): """ After create Websocket connection successfully, we will subscribing orderbook/trade events. """ symbols = [] for s in self._symbols: t = s.replace("/", "-") symbols.append(t) self._symbols_map[t] = s if not symbols: logger.warn("symbols not found in config file.", caller=self) return if not self._channels: logger.warn("channels not found in config file.", caller=self) return channels = [] for ch in self._channels: if ch == "orderbook": sub = {"name": "level2", "product_ids": symbols} channels.append(sub) elif ch == "trade": sub = {"name": "ticker", "product_ids": symbols} channels.append(sub) else: logger.error("channel error! channel:", ch, caller=self) if channels: msg = {"type": "subscribe", "channels": channels} await self._ws.send(msg) logger.info("subscribe orderbook/trade success.", caller=self)
async def 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 connected_callback(self): """After create Websocket connection successfully, we will subscribing orderbook/trade/kline.""" ches = [] for ch in self._channels: if ch == "orderbook": for symbol in self._symbols: ch = "spot/depth:{s}".format(s=symbol.replace("/", '-')) ches.append(ch) elif ch == "trade": for symbol in self._symbols: ch = "spot/trade:{s}".format(s=symbol.replace("/", '-')) ches.append(ch) elif ch == "kline": for symbol in self._symbols: ch = "spot/candle60s:{s}".format(s=symbol.replace("/", '-')) ches.append(ch) else: logger.error("channel error! channel:", ch, caller=self) if ches: msg = { "op": "subscribe", "args": ches } await self._ws.send(msg) logger.info("subscribe orderbook/trade/kline success.", caller=self)
async def connected_callback(self): """ After create connection to Websocket server successfully, we will subscribe orderbook event. """ for ch in self._channels: if ch == "kline": for symbol in self._symbols: channel = self._symbol_to_channel(symbol, "kline") if not channel: continue kline = {"sub": channel} await self._ws.send(kline) elif ch == "orderbook": for symbol in self._symbols: channel = self._symbol_to_channel(symbol, "depth") if not channel: continue data = {"sub": channel} await self._ws.send(data) elif ch == "trade": for symbol in self._symbols: channel = self._symbol_to_channel(symbol, "trade") if not channel: continue data = {"sub": channel} await self._ws.send(data) else: logger.error("channel error! channel:", ch, caller=self)
def initialize(): """ 初始化 """ for platform in config.platforms: if platform == OKEX or platform == OKEX_MARGIN: from platforms.okex import OKEx as Market elif platform == OKEX_FUTURE: from platforms.okex_ftu import OKExFuture as Market elif platform == BINANCE: from platforms.binance import Binance as Market elif platform == DERIBIT: from platforms.deribit import Deribit as Market elif platform == BITMEX: from platforms.bitmex import Bitmex as Market elif platform == HUOBI: from platforms.huobi import Huobi as Market elif platform == COINSUPER: from platforms.coinsuper import CoinsuperMarket as Market else: from quant.utils import logger logger.error("platform error! platform:", platform) continue cc = config.platforms[platform] cc["platform"] = platform Market(**cc)
async def create_order(self, action, price, quantity, order_type=ORDER_TYPE_LIMIT): """ 创建订单 @param action 委托方向 BUY SELL @param price 委托价格 @param quantity 委托数量 @param order_type 委托类型 LIMIT / MARKET """ if action == ORDER_ACTION_BUY: action_tmp = "Buy" else: action_tmp = "Sell" if int(quantity) > 0: if action == ORDER_ACTION_BUY: trade_type = TRADE_TYPE_BUY_OPEN else: trade_type = TRADE_TYPE_SELL_CLOSE else: if action == ORDER_ACTION_BUY: trade_type = TRADE_TYPE_BUY_CLOSE else: trade_type = TRADE_TYPE_SELL_OPEN if order_type == ORDER_TYPE_LIMIT: order_type_tmp = "Limit" elif order_type == ORDER_TYPE_MARKET: order_type_tmp = "Market" else: logger.error("order_type error! order_type:", order_type, caller=self) return None quantity = abs(int(quantity)) success, error = await self._rest_api.create_order(action_tmp, self._symbol, price, quantity, order_type_tmp, trade_type) if error: return None, error else: return success["orderID"], None
async def connected_callback(self): """ After create Websocket connection successfully, we will subscribing orderbook/trade/kline. """ if not self._symbols: logger.warn("symbols not found in config file.", caller=self) return if not self._channels: logger.warn("channels not found in config file.", caller=self) return for ch in self._channels: if ch == "orderbook": # subscription = {"name": "book"} LoopRunTask.register(self.on_event_update_orderbook, 2) continue elif ch == "trade": subscription = {"name": "trade"} # elif ch == "kline": # TODO: something wrong from exchange server? subscribe ohlc-1 but receive ohlc-5 ? # subscription = {"name": "ohlc", "interval": 1} else: logger.error("channel error:", ch, caller=self) continue d = { "event": "subscribe", "pair": self._symbols, "subscription": subscription } await self.ws.send_json(d) logger.info("subscribe", ch, "success.", caller=self)
def __init__(self, **kwargs): self._platform = kwargs["platform"] self._host = None self._symbols = list(set(kwargs.get("symbols"))) self._channels = kwargs.get("channels") self._access_key = None self._secret_key = None for item in config.accounts: if item["platform"] == self._platform: self._host = item.get( "host", "https://api-rest.premium.coinsuper.com") self._access_key = item["access_key"] self._secret_key = item["secret_key"] if not self._host or not self._access_key or not self._access_key: logger.error( "no find coinsuper account in ACCOUNTS from config file.", caller=self) return self._orderbook_interval = kwargs.get("orderbook_interval", 2) self._orderbook_length = kwargs.get("orderbook_length", 10) self._trade_interval = kwargs.get("trade_interval", 5) # REST API client. self._rest_api = CoinsuperPreRestAPI(self._host, self._access_key, self._secret_key) self._initialize()
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)
def __init__(self, **kwargs): self._platform = kwargs["platform"] self._wss = None self._symbols = list(set(kwargs.get("symbols"))) self._channels = kwargs.get("channels") self._orderbook_length = kwargs.get("orderbook_length", 10) self._access_key = None self._secret_key = None self._last_msg_ts = tools.get_cur_timestamp_ms() # 上次接收到消息的时间戳 for item in config.accounts: if item["platform"] == self._platform: self._wss = item.get("wss", "wss://deribit.com") self._access_key = item["access_key"] self._secret_key = item["secret_key"] if not self._wss or not self._access_key or not self._access_key: logger.error( "no find deribit account in ACCOUNTS from config file.", caller=self) return url = self._wss + "/ws/api/v1/" self._ws = Websocket(url, connected_callback=self.connected_callback, process_callback=self.process) self._ws.initialize() LoopRunTask.register(self.send_heartbeat_msg, 10)
async def connected_callback(self): """ 订阅消息 """ for ch in self._channels: if ch == "kline": # 订阅K线数据 for symbol in self._symbols: channel = self._symbol_to_channel(symbol, "kline") if not channel: continue kline = {"sub": channel} await self.ws.send_json(kline) elif ch == "orderbook": # 订阅订单薄数据 for symbol in self._symbols: channel = self._symbol_to_channel(symbol, "depth") if not channel: continue data = {"sub": channel} await self.ws.send_json(data) elif ch == "trade": # 实时交易数据 for symbol in self._symbols: channel = self._symbol_to_channel(symbol, "trade") if not channel: continue data = {"sub": channel} await self.ws.send_json(data) else: logger.error("channel error! channel:", ch, caller=self)
async def initialize(self, file_folder='.'): """ """ type_map = {'platform': str, 'account': str, 'symbol': str, 'strategy': str, 'order_no': str, 'fill_no': str, 'price': float, 'quantity': float, 'side': str, 'liquidity': str, 'fee': float, 'ctime': np.int64} trades = pd.read_csv(os.path.join(file_folder, 'trades.csv'), ',', dtype=type_map) if trades.empty: logger.error("error:", "无法读取成交列表", caller=self) return False #因为我们时间用的都是东八区时间,换成时间戳以后并不是按天对齐的,为了能够按天对齐进行运算,要先加八小时,再减八小时 trades['trade_date'] = (trades['ctime']+CHINA_TZONE_SHIFT)//ONE_DAY*ONE_DAY-CHINA_TZONE_SHIFT self.trades = trades.set_index(['platform', 'symbol', 'ctime']).sort_index(axis=0) #保存交易符号列表 self.trades.groupby(by=['platform', 'symbol']).apply(lambda gp_df: self.universe.append((gp_df.index.levels[0][0], gp_df.index.levels[1][0]))) #回测天数 self.period_day = int(config.backtest["period_day"]) #获取每日收盘价 closes = await self.get_daily_closes() if closes is None: logger.error("error:", "无法获取每日收盘价", caller=self) return False self.closes = closes # return True
def initialize(): """ 初始化 """ from quant.utils import logger from quant.config import config for platform, info in config.platforms.items(): for item in info["assets"]: if platform == OKEX: from assets.okex import OKExAsset as AssetServer elif platform == BINANCE: from assets.binance import BinanceAsset as AssetServer elif platform == HUOBI: from assets.huobi import HuobiAsset as AssetServer elif platform == OKEX_FUTURE: from assets.okex_future import OKExFutureAsset as AssetServer elif platform == DERIBIT: from assets.deribit import DeribitAsset as AssetServer elif platform == BITMEX: from assets.bitmex import BitmexAsset as AssetServer elif platform == COINSUPER: from assets.coinsuper import CoinsuperAsset as AssetServer else: logger.error("platform error! platform:", platform) continue item["platform"] = platform AssetServer(**item)
async def process(self, msg): """ 处理websocket上接收到的消息 """ # logger.debug("msg:", msg, caller=self) if not isinstance(msg, dict): return channel = msg.get("stream") if channel not in self._c_to_s: logger.warn("unkown channel, msg:", msg, caller=self) return symbol = self._c_to_s[channel] data = msg.get("data") e = data.get("e") # 事件名称 # 保存数据到数据库 if e == "kline": # K线 kline = { "platform": self._platform, "symbol": symbol, "open": data.get("k").get("o"), # 开盘价 "high": data.get("k").get("h"), # 最高价 "low": data.get("k").get("l"), # 最低价 "close": data.get("k").get("c"), # 收盘价 "volume": data.get("k").get("q"), # 交易量 "timestamp": data.get("k").get("t"), # 时间戳 "kline_type": const.MARKET_TYPE_KLINE } EventKline(**kline).publish() logger.info("symbol:", symbol, "kline:", kline, caller=self) elif channel.endswith("depth20"): # 订单薄 bids = [] asks = [] for bid in data.get("bids"): bids.append(bid[:2]) for ask in data.get("asks"): asks.append(ask[:2]) orderbook = { "platform": self._platform, "symbol": symbol, "asks": asks, "bids": bids, "timestamp": tools.get_cur_timestamp_ms() } EventOrderbook(**orderbook).publish() logger.info("symbol:", symbol, "orderbook:", orderbook, caller=self) elif e == "trade": # 实时成交信息 trade = { "platform": self._platform, "symbol": symbol, "action": ORDER_ACTION_SELL if data["m"] else ORDER_ACTION_BUY, "price": data.get("p"), "quantity": data.get("q"), "timestamp": data.get("T") } EventTrade(**trade).publish() logger.info("symbol:", symbol, "trade:", trade, caller=self) else: logger.error("event error! msg:", msg, caller=self)
async def connected_callback(self): """ 建立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 create_order(self, action, symbol, price, quantity, order_type=ORDER_TYPE_LIMIT): """ 创建订单 @param action 操作类型 BUY SELL @param symbol 交易对 @param quantity 交易量 @param price 交易价格 @param order_type 订单类型 市价 / 限价 """ info = { "side": "buy" if action == ORDER_ACTION_BUY else "sell", "instrument_id": symbol, "margin_trading": 1 } if order_type == ORDER_TYPE_LIMIT: info["type"] = "limit" info["price"] = price info["size"] = quantity elif order_type == ORDER_TYPE_MARKET: info["type"] = "market" if action == ORDER_ACTION_BUY: info["notional"] = quantity # 买入金额,市价买入是必填notional else: info["size"] = quantity # 卖出数量,市价卖出时必填size else: logger.error("order_type error! order_type:", order_type, caller=self) return None result, error = await self.request("POST", "/api/spot/v3/orders", body=info, auth=True) return result, error
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()
async def _connect(self): logger.info("url:", self._url, caller=self) proxy = config.proxy if not self.session: self.session = aiohttp.ClientSession() try: self._ws = await self.session.ws_connect(self._url, proxy=proxy) except (aiohttp.client_exceptions.ClientConnectorError, aiohttp.client_exceptions.ClientHttpProxyError, aiohttp.client_exceptions.ServerDisconnectedError, aiohttp.client_exceptions.WSServerHandshakeError, asyncio.TimeoutError) as e: logger.error("connect to server error! url:", self._url, caller=self) state = State( self._platform, self._account, "connect to server error! url: {}, error: {}".format( self._url, e), State.STATE_CODE_CONNECT_FAILED) SingleTask.run(self.cb.on_state_update_callback, state) asyncio.get_event_loop().create_task( self._reconnect()) #如果连接出错就重新连接 return state = State(self._platform, self._account, "connect to server success! url: {}".format(self._url), State.STATE_CODE_CONNECT_SUCCESS) SingleTask.run(self.cb.on_state_update_callback, state) asyncio.get_event_loop().create_task(self.connected_callback()) asyncio.get_event_loop().create_task(self.receive())
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.deribit.com" if not kwargs.get("wss"): kwargs["wss"] = "wss://deribit.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 = DERIBIT 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._order_channel = "user.orders.{symbol}.raw".format(symbol=self._symbol) # 订单订阅频道 url = self._wss + "/ws/api/v2" super(DeribitTrade, self).__init__(url, send_hb_interval=5) self._assets = {} # 资产 {"BTC": {"free": "1.1", "locked": "2.2", "total": "3.3"}, ... } self._orders = {} # 订单 self._position = Position(self._platform, self._account, self._strategy, self._symbol) # 仓位 self._query_id = 0 # 消息序号id,用来唯一标识请求消息 self._queries = {} # 未完成的post请求 {"request_id": future} self.initialize() # 初始化资产订阅 if self._asset_update_callback: AssetSubscribe(self._platform, self._account, self.on_event_asset_update) # 注册定时任务 LoopRunTask.register(self._do_auth, 60 * 60) # 每隔1小时重新授权 LoopRunTask.register(self._check_position_update, 1) # 获取持仓 self._ok = False # 是否建立授权成功的websocket连接
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 connected_callback(self): """After create Websocket connection successfully, we will subscribing orderbook/trade/kline events.""" if not self._symbols: logger.warn("symbols not found in config file.", caller=self) return if not self._channels: logger.warn("channels not found in config file.", caller=self) return for ch in self._channels: if ch == "trade": params = [] for s in self._symbols: params.append(s.replace("/", "_")) request_id = await self._generate_request_id() d = {"id": request_id, "method": "trades.subscribe", "params": params} await self._ws.send(d) logger.info("subscribe trade success.", caller=self) elif ch == "orderbook": await self.subscribe_orderbook() elif ch == "kline": for s in self._symbols: params = [s.replace("/", "_"), 60] request_id = await self._generate_request_id() d = {"id": request_id, "method": "kline.subscribe", "params": params} await self._ws.send(d) logger.info("subscribe kline success.", caller=self) else: logger.error("channel error:", ch, caller=self) continue
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 == "partical-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, order)
async def create_order(self, action, instrument_id, price, quantity, order_type=ORDER_TYPE_LIMIT): """ Create an order. Args: action: Action type, `BUY` or `SELL`. instrument_id: Trading pair, e.g. BTC-USDT. price: Order price. quantity: Order quantity. order_type: Order type, `MARKET` or `LIMIT`. Returns: success: Success results, otherwise it's None. error: Error information, otherwise it's None. """ uri = "/api/margin/v3/orders" info = { "side": "buy" if action == ORDER_ACTION_BUY else "sell", "instrument_id": instrument_id, "margin_trading": 2 } if order_type == ORDER_TYPE_LIMIT: info["type"] = "limit" info["price"] = price info["size"] = quantity elif order_type == ORDER_TYPE_MARKET: info["type"] = "market" if action == ORDER_ACTION_BUY: info["notional"] = quantity # buy price else: info["size"] = quantity # sell quantity else: logger.error("order_type error! order_type:", order_type, caller=self) return None success, error = await self.request("POST", uri, body=info, auth=True) return success, error
def __init__(self, **kwargs): """initialize trader object. Args: strategy: 策略名称,由哪个策略发起 platform: 交易平台 symbols: 策略需要订阅和交易的币种 account: 交易所登陆账号,如果为空就只是订阅市场公共行情数据,不进行登录认证,所以也无法进行交易等 access_key: 登录令牌 secret_key: 令牌密钥 cb: ExchangeGateway.ICallBack { on_init_success_callback: `初始化是否成功`回调通知函数 on_kline_update_callback: `K线数据`回调通知函数 (值为None就不启用此通知回调) on_orderbook_update_callback: `订单簿深度数据`回调通知函数 (值为None就不启用此通知回调) on_trade_update_callback: `市场最新成交`回调通知函数 (值为None就不启用此通知回调) on_ticker_update_callback: `市场行情tick`回调通知函数 (值为None就不启用此通知回调) on_order_update_callback: `用户挂单`回调通知函数 (值为None就不启用此通知回调) on_fill_update_callback: `用户挂单成交`回调通知函数 (值为None就不启用此通知回调) on_position_update_callback: `用户持仓`回调通知函数 (值为None就不启用此通知回调) on_asset_update_callback: `用户资产`回调通知函数 (值为None就不启用此通知回调) } """ T = gateway_class(kwargs["platform"]) if T == None: logger.error("platform not found:", kwargs["platform"], caller=self) cb = kwargs["cb"] SingleTask.run(cb.on_init_success_callback, False, Error("platform not found")) return self._t = T(**kwargs)
async def send_heartbeat_msg(self, *args, **kwargs): request_id = await self.generate_request_id() data = {"id": request_id, "type": "ping"} if not self._ws: logger.error("") return await self._ws.send(data)
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 _initialize(self): """ Initialize of Fetching open orders information. """ 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 while order_nos: nos = order_nos[:50] order_nos = order_nos[50:] success, error = await self._rest_api.get_order_list(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 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:]
async def connected_callback(self): """ 建立连接之后,订阅事件 ticker """ ches = [] for ch in self._channels: if ch == "orderbook": # 订阅orderbook行情 for symbol in self._symbols: ch = "spot/depth:{s}".format(s=symbol.replace("/", '-')) ches.append(ch) elif ch == "trade": for symbol in self._symbols: ch = "spot/trade:{s}".format(s=symbol.replace("/", '-')) ches.append(ch) elif ch == "kline": for symbol in self._symbols: ch = "spot/candle60s:{s}".format(s=symbol.replace("/", '-')) ches.append(ch) else: logger.error("channel error! channel:", ch, caller=self) if ches: msg = { "op": "subscribe", "args": ches } await self.ws.send_json(msg) logger.info("subscribe orderbook success.", caller=self)
async def _check_position_update(self): """更新持仓信息""" positions, error = await self._rest_api.get_position_list() if error: logger.error("get position error: {}".format(error)) return for position_info in positions['data']['results']: if position_info['symbol'] == self._symbol: update = False if not self._position.utime: # 如果持仓还没有被初始化,那么初始化之后推送一次 update = True self._position.update() size = position_info['quantity'] average_price = position_info['entry_price'] liquid_price = position_info['liquidation_price'] direction = position_info['direction'] if direction == 'SHORT': if self._position.short_quantity != size: update = True self._position.update(size, average_price, 0, 0, liquid_price) else: if self._position.long_quantity != size: update = True self._position.update(0, 0, size, average_price, liquid_price) if update and self._position_update_callback: SingleTask.run(self._position_update_callback, copy.copy(self._position))