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连接
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://api.testnet.fmex.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 = FCOIN_FUTURE self._symbol = kwargs["symbol"] self._host = kwargs["host"] 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._update_order_interval = kwargs.get("update_order_interval", 1) 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._subscribe_order_ok = False self._subscribe_position_ok = False # 初始化 REST API 对象 self._rest_api = FCoinFutureRestAPI(self._host, self._access_key, self._secret_key) # 初始化资产订阅 if self._asset_update_callback: AssetSubscribe(self._platform, self._account, self.on_event_asset_update) # 注册定时任务,定时查询订单更新、仓位更新情况 if self._position_update_callback: LoopRunTask.register(self._check_position_update, 60) # 初始化订单订阅 if self._order_update_callback: LoopRunTask.register(self.check_order_update, self._update_order_interval)
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://api.gateio.ws" if not kwargs.get("wss"): kwargs["wss"] = "wss://fx-ws.gateio.ws" 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 = GATE_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._contract_update_callback = kwargs.get('contract_update_callback') self._user_id = None url = self._wss + "/v4/ws" super(GateFutureTrade, 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, ... } self._position = Position(self._platform, self._account, self._strategy, self._symbol) # 初始化 REST API 对象 self._rest_api = GateFutureRestAPI(self._host, self._access_key, self._secret_key) # 初始化资产订阅 if self._asset_update_callback: AssetSubscribe(self._platform, self._account, self.on_event_asset_update) # 注册定时任务 LoopRunTask.register(self._check_position_update, 1) # 获取持仓
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 __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("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._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() LoopRunTask.register(self.send_heartbeat_msg, 5) 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._symbol + '/' + self._contract_type) 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 on_position_update_callback(self, position: Position): """ 持仓更新 """ if self.native_to_system: position.symbol = self.native_to_system[ position.symbol] #'交易所原始符号'转换成'量化平台通用符号' await self._original_on_position_update_callback(position)
def __init__(self, account, strategy, symbol, host=None, wss=None, access_key=None, secret_key=None, order_update_callback=None, position_update_callback=None): """ 初始化 @param account 账户 @param strategy 策略名称 @param symbol 交易对(合约名称) @param host HTTP请求主机地址 @param wss websocket连接地址 @param access_key ACCESS KEY @param secret_key SECRET KEY @param order_update_callback 订单更新回调 @param position_update_callback 持仓更新回调 """ self._account = account self._strategy = strategy self._platform = BITMEX self._symbol = symbol self._host = host self._wss = wss self._access_key = access_key self._secret_key = secret_key self._order_update_callback = order_update_callback self._position_update_callback = position_update_callback super(BitmexTrade, self).__init__(self._wss, send_hb_interval=5) self.heartbeat_msg = "ping" self._orders = {} self._position = Position(self._platform, self._account, strategy, symbol) # 仓位 # 初始化REST API对象 self._rest_api = BitmexAPI(self._host, self._access_key, self._secret_key) self.initialize()
def __init__(self, account, strategy, symbol, host=None, wss=None, access_key=None, secret_key=None, order_update_callback=None, position_update_callback=None): """ 初始化 @param account 账户 @param strategy 策略名称 @param symbol 交易对(合约名称) @param host HTTP请求主机地址 @param wss websocket连接地址 @param access_key ACCESS KEY @param secret_key SECRET KEY @param order_update_callback 订单更新回调 @param position_update_callback 持仓更新回调 """ self._account = account self._strategy = strategy self._platform = DERIBIT self._symbol = symbol self._host = host if host else "https://www.deribit.com" self._wss = wss if wss else "wss://deribit.com/ws/api/v2" self._access_key = access_key self._secret_key = secret_key self._order_update_callback = order_update_callback self._position_update_callback = position_update_callback self._order_channel = "user.orders.{symbol}.raw".format(symbol=symbol) # 订单订阅频道 super(DeribitTrade, self).__init__(self._wss, send_hb_interval=5) self._orders = {} # 订单 self._position = Position(self._platform, self._account, strategy, symbol) # 仓位 self._query_id = 0 # 消息序号id,用来唯一标识请求消息 self._queries = {} # 未完成的post请求 {"request_id": future} self.initialize() # 注册定时任务 LoopRunTask.register(self._do_auth, 60 * 60) # 每隔1小时重新授权 LoopRunTask.register(self._check_position_update, 1) # 获取持仓 self._ok = False # 是否建立授权成功的websocket连接
def __init__(self, account, strategy, symbol, host=None, wss=None, access_key=None, secret_key=None, passphrase=None, order_update_callback=None, position_update_callback=None): """ 初始化 @param account 账户 @param strategy 策略名称 @param symbol 交易对(合约名称) @param host HTTP请求主机地址 @param wss websocket连接地址 @param access_key ACCESS KEY @param secret_key SECRET KEY @param passphrase 密码 @param order_update_callback 订单更新回调 @param position_update_callback 持仓更新回调 """ self._account = account self._strategy = strategy self._platform = OKEX_FUTURE self._symbol = symbol self._host = host if host else "https://www.okex.com" self._wss = wss if wss else "wss://real.okex.com:10442/ws/v3" self._access_key = access_key self._secret_key = secret_key self._passphrase = passphrase self._order_update_callback = order_update_callback self._position_update_callback = position_update_callback super(OKExFutureTrade, self).__init__(self._wss, send_hb_interval=5) self.heartbeat_msg = "ping" self._orders = {} # 订单 self._position = Position(self._platform, self._account, strategy, symbol) # 仓位 # 初始化 REST API 对象 self._rest_api = OKExFutureRestAPI(self._host, self._access_key, self._secret_key, self._passphrase) self.initialize()
async def get_position(self, symbol): """ 获取当前持仓 Args: symbol: Trade target Returns: position: Position if successfully, otherwise it's None. error: Error information, otherwise it's None. """ #{"result": [{"collateralUsed": 0.35986, "cost": -1.7984, "entryPrice": 179.84, "estimatedLiquidationPrice": 11184.0123266, "future": "ETH-PERP", "initialMarginRequirement": 0.2, "longOrderSize": 0.0, "maintenanceMarginRequirement": 0.03, "netSize": -0.01, "openSize": 0.01, "realizedPnl": 0.01866927, "recentAverageOpenPrice": 179.84, "recentPnl": -0.0009, "shortOrderSize": 0.0, "side": "sell", "size": 0.01, "unrealizedPnl": -0.0009}], "success": true} success, error = await self._rest_api.get_positions(True) if error: return None, error if not success["success"]: return None, "get_position error" p = next(filter(lambda x: x['future'] == symbol, success["result"]), None) if p == None: return None, "symbol not exist" if p["netSize"] == 0: return None, "currently no positions" pos = Position(self._platform, self._account, self._strategy, symbol) if p["netSize"] < 0: #空头仓位 pos.update(short_quantity=abs(p["netSize"]), short_avg_price=p["recentAverageOpenPrice"], liquid_price=p["estimatedLiquidationPrice"]) else: #多头仓位 pos.update(long_quantity=abs(p["netSize"]), long_avg_price=p["recentAverageOpenPrice"], liquid_price=p["estimatedLiquidationPrice"]) #因为ftx websocket接口里面没有仓位通知,所以只能这样模拟 SingleTask.run(self.cb.on_position_update_callback, pos) return pos, None
class DeribitTrade(Websocket): """ Deribit Trade module 交易模块 """ def __init__(self, account, strategy, symbol, host=None, wss=None, access_key=None, secret_key=None, order_update_callback=None, position_update_callback=None): """ 初始化 @param account 账户 @param strategy 策略名称 @param symbol 交易对(合约名称) @param host HTTP请求主机地址 @param wss websocket连接地址 @param access_key ACCESS KEY @param secret_key SECRET KEY @param order_update_callback 订单更新回调 @param position_update_callback 持仓更新回调 """ self._account = account self._strategy = strategy self._platform = DERIBIT self._symbol = symbol self._host = host if host else "https://www.deribit.com" self._wss = wss if wss else "wss://deribit.com/ws/api/v2" self._access_key = access_key self._secret_key = secret_key self._order_update_callback = order_update_callback self._position_update_callback = position_update_callback self._order_channel = "user.orders.{symbol}.raw".format(symbol=symbol) # 订单订阅频道 super(DeribitTrade, self).__init__(self._wss, send_hb_interval=5) self._orders = {} # 订单 self._position = Position(self._platform, self._account, strategy, symbol) # 仓位 self._query_id = 0 # 消息序号id,用来唯一标识请求消息 self._queries = {} # 未完成的post请求 {"request_id": future} self.initialize() # 注册定时任务 LoopRunTask.register(self._do_auth, 60 * 60) # 每隔1小时重新授权 LoopRunTask.register(self._check_position_update, 1) # 获取持仓 self._ok = False # 是否建立授权成功的websocket连接 @property def position(self): return copy.copy(self._position) @property def orders(self): return copy.copy(self._orders) async def connected_callback(self): """ 建立连接之后,授权登陆,然后订阅order和position """ # 授权 success, error = await self._do_auth() if error: return if success.get("access_token"): self._ok = True else: return # 获取未完全成交的订单 success, error = await self.get_open_orders() if error: return for order_info in success: order = self._update_order(order_info) if self._order_update_callback: SingleTask.run(self._order_update_callback, order) # 获取持仓 await self._check_position_update() # 授权成功之后,订阅数据 method = "private/subscribe" params = { "channels": [ self._order_channel ] } await self._send_message(method, params) async def _do_auth(self, *args, **kwargs): """ 鉴权 """ method = "public/auth" params = { "grant_type": "client_credentials", "client_id": self._access_key, "client_secret": self._secret_key } success, error = await self._send_message(method, params) return success, error async def get_server_time(self): """ 获取服务器时间 """ method = "public/get_time" params = {} success, error = await self._send_message(method, params) return success, error async def get_position(self): """ 获取当前持仓 """ method = "private/get_position" params = {"instrument_name": self._symbol} success, error = await self._send_message(method, params) return success, error 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 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 quantity = abs(int(quantity)) if action == ORDER_ACTION_BUY: method = "private/buy" elif action == ORDER_ACTION_SELL: method = "private/sell" else: logger.error("action error! action:", action, caller=self) return None if order_type == ORDER_TYPE_LIMIT: type_ = "limit" else: type_ = "market" params = { "instrument_name": self._symbol, "price": price, "amount": quantity, "type": type_, "label": str(trade_type) } success, error = await self._send_message(method, params) if error: return None, error order_no = success["order"]["order_id"] return order_no, None async def revoke_order(self, *order_nos): """ 撤销订单 @param order_nos 订单号,如果没有指定订单号,那么撤销所有订单 * NOTE: 单次调换最多只能撤销100个订单,如果订单超过100个,请多次调用 """ # 如果传入order_nos为空,即撤销全部委托单 if len(order_nos) == 0: method = "private/cancel_all_by_instrument" params = {"instrument_name": self._symbol} success, error = await self._send_message(method, params) if error: return False, error else: return True, None # 如果传入order_nos为一个委托单号,那么只撤销一个委托单 if len(order_nos) == 1: method = "private/cancel" params = {"order_id": order_nos[0]} success, error = await self._send_message(method, params) if error: return order_nos[0], error else: return order_nos[0], None # 如果传入order_nos数量大于1,那么就批量撤销传入的委托单 if len(order_nos) > 1: success, error = [], [] method = "private/cancel" for order_no in order_nos: params = {"order_id": order_no} r, e = await self._send_message(method, params) if e: error.append((order_no, e)) else: success.append(order_no) return success, error async def get_order_status(self, order_no): """ 获取订单状态 @param order_no 订单号 """ method = "private/get_order_state" params = {"order_id": order_no} success, error = await self._send_message(method, params) return success, error async def get_open_orders(self): """ 获取未完全成交订单 """ method = "private/get_open_orders_by_instrument" params = {"instrument_name": self._symbol} success, error = await self._send_message(method, params) return success, error async def get_open_order_nos(self): """ 获取未完全成交订单号列表 """ method = "private/get_open_orders_by_instrument" params = {"instrument_name": self._symbol} success, error = await self._send_message(method, params) if error: return None, error else: order_nos = [] for item in success: order_nos.append(item["order_id"]) return order_nos, None async def _send_message(self, method, params): """ 发送消息 """ 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_method_locker("generate_query_id.locker") async def _generate_query_id(self): """ 生成query id,加锁,确保每个请求id唯一 """ self._query_id += 1 return self._query_id @async_method_locker("process.locker") 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": if msg["params"]["channel"] == self._order_channel: order_info = msg["params"]["data"] order = self._update_order(order_info) if self._order_update_callback: SingleTask.run(self._order_update_callback, copy.copy(order)) async def _check_position_update(self, *args, **kwargs): """ 定时获取持仓 """ if not self._ok: return update = False success, error = await self.get_position() if error: return if not self._position.utime: # 如果持仓还没有被初始化,那么初始化之后推送一次 update = True self._position.update() size = int(success["size"]) average_price = float(success["average_price"]) liquid_price = float(success["estimated_liquidation_price"]) if size > 0: if self._position.long_quantity != size: update = True self._position.update(0, 0, size, average_price, liquid_price) elif size < 0: if self._position.short_quantity != abs(size): update = True self._position.update(abs(size), average_price, 0, 0, liquid_price) elif size == 0: if self._position.long_quantity != 0 or self._position.short_quantity != 0: update = True self._position.update() if update: await self._position_update_callback(self._position) def _update_order(self, order_info): """ 更新订单信息 @param order_info 订单信息 """ order_no = order_info["order_id"] quantity = int(order_info["amount"]) filled_amount = int(order_info["filled_amount"]) remain = quantity - filled_amount average_price = order_info.get("average_price") state = order_info["order_state"] if state == "open": status = ORDER_STATUS_SUBMITTED if filled_amount > 0: status = ORDER_STATUS_PARTIAL_FILLED elif state == "filled": status = ORDER_STATUS_FILLED elif state == "cancelled": status = ORDER_STATUS_CANCELED else: status = ORDER_STATUS_FAILED order = self._orders.get(order_no) if not order: action = ORDER_ACTION_BUY if order_info["direction"] == "buy" else ORDER_ACTION_SELL trade_type = int(order_info.get("label")) info = { "platform": self._platform, "account": self._account, "strategy": self._strategy, "symbol": self._symbol, "order_no": order_no, "action": action, "price": order_info["price"], "quantity": quantity, "remain": remain, "trade_type": trade_type } order = Order(**info) self._orders[order_no] = order order.status = status order.remain = remain order.avg_price = average_price order.ctime = order_info["creation_timestamp"] order.utime = order_info["last_update_timestamp"] if order.status in [ORDER_STATUS_FILLED, ORDER_STATUS_CANCELED, ORDER_STATUS_FAILED]: self._orders.pop(order.order_no) return order
class OKExSwapTrade(Websocket): """ OKEx Swap Trade module. You can initialize trade object with some attributes in kwargs. Attributes: account: Account name for this trade exchange. strategy: What's name would you want to created for you strategy. symbol: Symbol name for your trade. host: HTTP request host. (default "https://www.okex.com") wss: Websocket address. (default "wss://real.okex.com:10442") access_key: Account's ACCESS KEY. secret_key Account's SECRET KEY. passphrase API KEY Passphrase. init_success_callback: You can use this param to specific a async callback function when you initializing Trade object. `init_success_callback` is like `async def on_init_success_callback(success: bool, error: Error): pass` and this callback function will be executed asynchronous after Trade module object initialized successfully. """ 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_SWAP 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(OKExSwapTrade, 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 = "swap/order:{symbol}".format(symbol=self._symbol) self._position_channel = "swap/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 = OKExSwapRestAPI(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() @property def assets(self): return copy.copy(self._assets) @property def orders(self): return copy.copy(self._orders) @property def position(self): return copy.copy(self._position) @property def rest_api(self): return self._rest_api async def connected_callback(self): """ After websocket connection created successfully, we will send a message to server for authentication. """ timestamp = str(time.time()).split(".")[0] + "." + str( time.time()).split(".")[1][:3] message = str(timestamp) + "GET" + "/users/self/verify" mac = hmac.new(bytes(self._secret_key, encoding="utf8"), bytes(message, encoding="utf8"), digestmod="sha256") d = mac.digest() signature = base64.b64encode(d).decode() data = { "op": "login", "args": [self._access_key, self._passphrase, timestamp, signature] } await self.ws.send_json(data) @async_method_locker("process_binary.locker") async def process_binary(self, raw): """ Process binary message that receive from websocket. Args: raw Binary message receive from websocket. Returns: None. """ decompress = zlib.decompressobj(-zlib.MAX_WBITS) msg = decompress.decompress(raw) msg += decompress.flush() msg = msg.decode() # Heartbeat message received. 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) if self._init_success_callback: 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)) 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) # Fetch positions from server. 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 self._update_position(position) if self._position_update_callback: SingleTask.run(self._position_update_callback, self.position) # 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: if self._init_success_callback: SingleTask.run(self._init_success_callback, True, None) return # Order update message received. if msg.get("table") == "swap/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 # Position update message receive. if msg.get("table") == "swap/position": for data in msg["data"]: self._update_position(data) if self._position_update_callback: SingleTask.run(self._position_update_callback, self.position) async def create_order(self, action, price, quantity, order_type=ORDER_TYPE_LIMIT, match_price=0, **kwargs): """ Create an order. Args: action: Trade direction, BUY or SELL. price: Price of each contract. quantity: The buying or selling quantity. order_type: match_price: Order at best counter party price? (0: no, 1: yes), When posting orders at best bid price, order_type can only be 0 (regular order). order_type: Fill in number for parameter, 0: Normal limit order (Unfilled and 0 represent normal limit order) 1: Post only, 2: Fill Or Kill, 3: Immediately Or Cancel. Returns: order_no: Order ID if created successfully, otherwise it's None. error: Error information, otherwise it's None. """ if int(quantity) > 0: if action == ORDER_ACTION_BUY: trade_type = "1" else: trade_type = "3" else: if action == ORDER_ACTION_BUY: trade_type = "4" else: trade_type = "2" quantity = abs(int(quantity)) if order_type == ORDER_TYPE_LIMIT: order_type_2 = 0 elif order_type == ORDER_TYPE_MARKET: order_type_2 = 2 else: return None, "order type error" result, error = await self._rest_api.create_order( self._symbol, trade_type, price, quantity, match_price, order_type_2) if error: return None, error order_no = result["order_id"] return order_no, None async def revoke_order(self, *order_nos): """ Revoke (an) order(s). Args: order_nos: Order id list, you can set this param to 0 or multiple items. If you set 0 param, you can cancel all orders for this symbol(initialized in Trade object). If you set 1 param, you can cancel an order. If you set multiple param, you can cancel multiple orders. Do not set param length more than 100. Returns: Success or error, see bellow. """ # If len(order_nos) == 0, you will cancel all orders for this symbol(initialized in Trade object). if len(order_nos) == 0: result, error = await self._rest_api.get_order_list( self._symbol, 6) if error: return False, error for order_info in result["order_info"]: order_no = order_info["order_id"] _, error = await self._rest_api.revoke_order( self._symbol, order_no) if error: return False, error return True, None # If len(order_nos) == 1, you will cancel an order. if len(order_nos) == 1: success, error = await self._rest_api.revoke_order( self._symbol, order_nos[0]) if error: return order_nos[0], error else: return order_nos[0], None # If len(order_nos) > 1, you will cancel multiple orders. if len(order_nos) > 1: success, error = [], [] for order_no in order_nos: _, e = await self._rest_api.revoke_order( self._symbol, order_no) if e: error.append((order_no, e)) else: success.append(order_no) return success, error async def get_open_order_nos(self): """ Get open order id list. Args: None. Returns: order_nos: Open order id list, otherwise it's None. error: Error information, otherwise it's None. """ success, error = await self._rest_api.get_order_list(self._symbol, 6) if error: return None, error else: order_nos = [] for order_info in success["order_info"]: order_nos.append(order_info["order_id"]) return order_nos, None def _update_order(self, order_info): """ Order update. Args: order_info: Order information. Returns: Return order object if or 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, "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 if state in ["-1", "2"]: self._orders.pop(order_no) return order def _update_position(self, position_info): """ Position update. Args: position_info: Position information. Returns: None. """ if len( position_info["holding"] ) == 0: # When the length of `holding` is 0, specific all the position is closed self._position.update(0, 0, 0, 0, 0) return for item in position_info["holding"]: if item["side"] == "long": self._position.liquid_price = item["liquidation_price"] self._position.long_quantity = int(item["position"]) self._position.long_avg_price = item["avg_cost"] elif item["side"] == "short": self._position.short_quantity = int(item["position"]) self._position.short_avg_price = item["avg_cost"] else: continue self._position.utime = tools.utctime_str_to_mts(item["timestamp"]) async def on_event_asset_update(self, asset: Asset): """ Asset event data callback. Args: asset: Asset object callback from EventCenter. Returns: None. """ self._assets = asset SingleTask.run(self._asset_update_callback, asset)
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 __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()
class FCoinFutureTrade: """ FCOIN Future Trade module """ 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://api.testnet.fmex.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 = FCOIN_FUTURE self._symbol = kwargs["symbol"] self._host = kwargs["host"] 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._update_order_interval = kwargs.get("update_order_interval", 1) 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._subscribe_order_ok = False self._subscribe_position_ok = False # 初始化 REST API 对象 self._rest_api = FCoinFutureRestAPI(self._host, self._access_key, self._secret_key) # 初始化资产订阅 if self._asset_update_callback: AssetSubscribe(self._platform, self._account, self.on_event_asset_update) # 注册定时任务,定时查询订单更新、仓位更新情况 if self._position_update_callback: LoopRunTask.register(self._check_position_update, 60) # 初始化订单订阅 if self._order_update_callback: LoopRunTask.register(self.check_order_update, self._update_order_interval) async def on_event_asset_update(self, asset: Asset): """ 资产数据更新回调 """ self._assets = asset SingleTask.run(self._asset_update_callback, asset) @property def assets(self): return copy.copy(self._assets) @property def orders(self): return copy.copy(self._orders) @property def position(self): return copy.copy(self._position) 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 int(quantity) > 0: if action == ORDER_ACTION_BUY: trade_type = "1" else: trade_type = "3" else: if action == ORDER_ACTION_BUY: trade_type = "4" else: trade_type = "2" quantity = abs(int(quantity)) result, error = await self._rest_api.create_order( self._symbol, trade_type, price, quantity) if error: return None, error return result['data']["id"], None async def revoke_order(self, *order_nos): """ 撤销订单 @param order_nos 订单号,可传入任意多个,如果不传入,那么就撤销所有订单 * NOTE: 单次调用最多只能撤销100个订单,如果订单超过100个,请多次调用 """ # 如果传入order_nos为空,即撤销全部委托单 if len(order_nos) == 0: result, error = self._rest_api.get_open_order_list() if error: return False, error success, error = [], [] for order_info in result['data']['results']: order_no = order_info['id'] _, e = await self._rest_api.revoke_order(order_no) if e: error.append((order_no, e)) else: success.append(order_no) return success, error # 如果传入order_nos为一个委托单号,那么只撤销一个委托单 if len(order_nos) == 1: success, error = await self._rest_api.revoke_order(order_nos[0]) if error: return order_nos[0], error else: return order_nos[0], None # 如果传入order_nos数量大于1,那么就批量撤销传入的委托单 if len(order_nos) > 1: success, error = [], [] for order_no in order_nos: _, e = await self._rest_api.revoke_order(order_no) if e: error.append((order_no, e)) else: success.append(order_no) return success, error async def get_open_order_info(self, order_no): """查询活动订单详情""" result, error = await self._rest_api.get_open_order_info(order_no) if error: return None, error else: for data in result['data']['results']: if data['id'] == order_no: logger.info(data) is_update, order = self._update_order(data) if is_update and self._order_update_callback: SingleTask.run(self._order_update_callback, copy.copy(order)) return copy.copy(order), None async def get_open_order_nos(self): """ 获取未完全成交订单号列表 """ success, error = await self._rest_api.get_open_order_list() if error: return None, error else: order_nos = [] for order_info in success['data']['results']: order_nos.append(order_info['id']) return order_nos, None @async_method_locker('check_order_update', False) async def check_order_update(self): """定时查询订单信息""" open_result, error = await self._rest_api.get_open_order_list() closed_result, error = await self._rest_api.get_closed_order_list( self._symbol) orders = list() # 查询所有未完成订单 if open_result: open_orders = open_result['data']['results'] orders += open_orders # 查询所有已关闭订单 if closed_result: close_orders = open_result['data']['results'] orders += close_orders if len(orders) != 0: for order_info in copy.copy(orders): is_update, order = await self._update_order(order_info) if is_update: if self._order_update_callback: SingleTask.run(self._order_update_callback, copy.copy(order)) async def _update_order(self, order_info): """ 更新订单信息 """ order_no = order_info.get('id') is_updated = False if order_no is None: return is_updated, None remain = order_info['unfilled_quantity'] quantity = order_info['quantity'] state = order_info['status'] ctime = order_info['created_at'] utime = order_info['utime'] if state == 'FULLY_FILLED': status = ORDER_STATUS_FAILED elif state == 'PARTIAL_FILLED': status = ORDER_STATUS_PARTIAL_FILLED elif state == 'PENDING': status = ORDER_STATUS_SUBMITTED elif state == 'PARTIAL_CANCELLED': status = ORDER_STATUS_PARTIAL_CANCELED elif state == 'FULLY_CANCELLED': status = ORDER_STATUS_CANCELED else: logger.error('订单状态未处理', order_info) return is_updated, 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": ORDER_ACTION_BUY if order_info["direction"] == 'LONG' else ORDER_ACTION_SELL, "symbol": self._symbol, "price": order_info["price"], "quantity": quantity, "trade_type": TRADE_TYPE_NONE, 'remain': remain, 'status': status } order = Order(**info) is_updated = True else: if order.remain != remain: is_updated = True order.remain = remain elif order.status != status: is_updated = True order.status = status order.ctime = ctime order.utime = utime self._orders[order_no] = order if state in ('FULLY_FILLED', 'PARTIAL_CANCELLED', 'FULLY_CANCELLED'): self._orders.pop(order_no) return is_updated, order 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))
async def get_position(self, symbol): """ 获取当前持仓 Args: symbol: Trade target Returns: position: Position if successfully, otherwise it's None. error: Error information, otherwise it's None. """ #{"result": [{"collateralUsed": 0.35986, "cost": -1.7984, "entryPrice": 179.84, "estimatedLiquidationPrice": 11184.0123266, "future": "ETH-PERP", "initialMarginRequirement": 0.2, "longOrderSize": 0.0, "maintenanceMarginRequirement": 0.03, "netSize": -0.01, "openSize": 0.01, "realizedPnl": 0.01866927, "recentAverageOpenPrice": 179.84, "recentPnl": -0.0009, "shortOrderSize": 0.0, "side": "sell", "size": 0.01, "unrealizedPnl": -0.0009}], "success": true} success, error = await self._rest_api.get_positions(True) if error: return None, error if not success["success"]: return None, "get_position error" p = next(filter(lambda x: x['future'] == symbol, success["result"]), None) if p == None: return Position(self._platform, self._account, self._strategy, symbol), None if p["netSize"] == 0: return Position(self._platform, self._account, self._strategy, symbol), None pos = Position(self._platform, self._account, self._strategy, symbol) pos.margin_mode = MARGIN_MODE_CROSSED #ftx只有全仓模式,如果想要逐仓模式的话就用子账户的方式来实现 pos.utime = tools.get_cur_timestamp_ms() if p["netSize"] < 0: #空头仓位 pos.long_quantity = 0 pos.long_avail_qty = 0 pos.long_open_price = 0 pos.long_hold_price = 0 pos.long_liquid_price = 0 pos.long_unrealised_pnl = 0 pos.long_leverage = 0 pos.long_margin = 0 # pos.short_quantity = abs(p["netSize"]) pos.short_avail_qty = pos.short_quantity - p["longOrderSize"] if p[ "longOrderSize"] < pos.short_quantity else 0 pos.short_open_price = p["recentAverageOpenPrice"] pos.short_hold_price = p["entryPrice"] pos.short_liquid_price = p["estimatedLiquidationPrice"] pos.short_unrealised_pnl = p["unrealizedPnl"] pos.short_leverage = int(1 / p["initialMarginRequirement"]) pos.short_margin = p["collateralUsed"] else: #多头仓位 pos.long_quantity = abs(p["netSize"]) pos.long_avail_qty = pos.long_quantity - p["shortOrderSize"] if p[ "shortOrderSize"] < pos.long_quantity else 0 pos.long_open_price = p["recentAverageOpenPrice"] pos.long_hold_price = p["entryPrice"] pos.long_liquid_price = p["estimatedLiquidationPrice"] pos.long_unrealised_pnl = p["unrealizedPnl"] pos.long_leverage = int(1 / p["initialMarginRequirement"]) pos.long_margin = p["collateralUsed"] # pos.short_quantity = 0 pos.short_avail_qty = 0 pos.short_open_price = 0 pos.short_hold_price = 0 pos.short_liquid_price = 0 pos.short_unrealised_pnl = 0 pos.short_leverage = 0 pos.short_margin = 0 return pos, None
def parse(self): """ Parse self._data to Order object. """ position = Position(**self.data) return position
class BinanceFutureTrade: """ Binance Future Trade module. You can initialize trade object with some attributes in kwargs. Attributes: account: Account name for this trade exchange. strategy: What's name would you want to created for you strategy. symbol: Symbol name for your trade. host: HTTP request host. (default "https://fapi.binance.com") wss: Websocket address. (default "wss://fstream.binance.com") access_key: Account's ACCESS KEY. secret_key Account's SECRET KEY. asset_update_callback: You can use this param to specific a async callback function when you initializing Trade object. `asset_update_callback` is like `async def on_asset_update_callback(asset: Asset): pass` and this callback function will be executed asynchronous when received AssetEvent. order_update_callback: You can use this param to specific a async callback function when you initializing Trade object. `order_update_callback` is like `async def on_order_update_callback(order: Order): pass` and this callback function will be executed asynchronous when some order state updated. init_success_callback: You can use this param to specific a async callback function when you initializing Trade object. `init_success_callback` is like `async def on_init_success_callback(success: bool, error: Error, **kwargs): pass` and this callback function will be executed asynchronous after Trade module object initialized successfully. """ 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) @property def assets(self): return copy.copy(self._assets) @property def orders(self): return copy.copy(self._orders) @property def rest_api(self): return self._rest_api async def _init_websocket(self): """ Initialize Websocket connection. """ # Get listen key first. success, error = await self._rest_api.get_listen_key() if error: e = Error("get listen key failed: {}".format(error)) logger.error(e, caller=self) SingleTask.run(self._init_success_callback, False, e) return self._listen_key = success["listenKey"] uri = "/ws/" + self._listen_key url = urljoin(self._wss, uri) self._ws = Websocket(url, self.connected_callback, process_callback=self.process) self._ws.initialize() 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 _send_heartbeat_msg(self, *args, **kwargs): # """Send ping to server.""" # hb = {"ping": tools.get_cur_timestamp_ms()} # await self._ws.send(hb) 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._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"] == "PARTIAL_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, "trade_type": int(order_info["clientOrderId"][-1]), "ctime": order_info["updateTime"], "utime": order_info["updateTime"] } order = Order(**info) self._orders[order_no] = order SingleTask.run(self._order_update_callback, copy.copy(order)) self._ok = True SingleTask.run(self._init_success_callback, True, None) async def create_order(self, action, price, quantity, *args, **kwargs): """ Create an order. Args: action: Trade direction, BUY or SELL. price: Price of each contract. quantity: The buying or selling quantity. Returns: order_no: Order ID if created successfully, otherwise it's None. error: Error information, otherwise it's None. """ if float(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 quantity = abs(float(quantity)) price = tools.float_to_str(price) quantity = tools.float_to_str(quantity) client_order_id = tools.get_uuid1().replace("-", "")[:21] + str(trade_type) result, error = await self._rest_api.create_order( action, self._raw_symbol, price, quantity, client_order_id) if error: return None, error order_no = "{}_{}".format(result["orderId"], result["clientOrderId"]) return order_no, None async def revoke_order(self, *order_nos): """ Revoke (an) order(s). Args: order_nos: Order id list, you can set this param to 0 or multiple items. If you set 0 param, you can cancel all orders for this symbol(initialized in Trade object). If you set 1 param, you can cancel an order. If you set multiple param, you can cancel multiple orders. Do not set param length more than 100. Returns: Success or error, see bellow. """ # If len(order_nos) == 0, you will cancel all orders for this symbol(initialized in Trade object). if len(order_nos) == 0: order_infos, error = await self._rest_api.get_open_orders( self._raw_symbol) if error: return False, error for order_info in order_infos: _, error = await self._rest_api.revoke_order( self._raw_symbol, order_info["orderId"], order_info["clientOrderId"]) if error: return False, error return True, None # If len(order_nos) == 1, you will cancel an order. if len(order_nos) == 1: order_id, client_order_id = order_nos[0].split("_") success, error = await self._rest_api.revoke_order( self._raw_symbol, order_id, client_order_id) if error: return order_nos[0], error else: return order_nos[0], None # If len(order_nos) > 1, you will cancel multiple orders. if len(order_nos) > 1: success, error = [], [] for order_no in order_nos: order_id, client_order_id = order_no.split("_") _, e = await self._rest_api.revoke_order( self._raw_symbol, order_id, client_order_id) if e: error.append((order_no, e)) else: success.append(order_no) return success, error async def get_open_order_nos(self): """ Get open order no list. """ success, error = await self._rest_api.get_open_orders(self._raw_symbol) if error: return None, error order_nos = [] for order_info in success: order_no = "{}_{}".format(order_info["orderId"], order_info["clientOrderId"]) order_nos.append(order_no) return order_nos, None @async_method_locker("BinanceTrade.process.locker") 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"]) async def _check_position_update(self, *args, **kwargs): """Check position update.""" if not self._ok: return update = False success, error = await self._rest_api.get_position() if error: return position_info = None for item in success: if item["symbol"] == self._raw_symbol: position_info = item break if not self._position.utime: # Callback position info when initialized. update = True self._position.update() size = float(position_info["positionAmt"]) average_price = float(position_info["entryPrice"]) if size > 0: if self._position.long_quantity != size: update = True self._position.update(0, 0, size, average_price, 0) elif size < 0: if self._position.short_quantity != abs(size): update = True self._position.update(abs(size), average_price, 0, 0, 0) elif size == 0: if self._position.long_quantity != 0 or self._position.short_quantity != 0: update = True self._position.update() if update: await self._position_update_callback(copy.copy(self._position)) def _update_order(self, order_info): """ Order update. Args: order_info: Order information. Returns: Return order object if or None. """ if order_info["s"] != self._raw_symbol: return order_no = "{}_{}".format(order_info["i"], order_info["c"]) if order_info["X"] == "NEW": status = ORDER_STATUS_SUBMITTED elif order_info["X"] == "PARTIAL_FILLED": status = ORDER_STATUS_PARTIAL_FILLED elif order_info["X"] == "FILLED": status = ORDER_STATUS_FILLED elif order_info["X"] == "CANCELED": status = ORDER_STATUS_CANCELED elif order_info["X"] == "REJECTED": status = ORDER_STATUS_FAILED elif order_info["X"] == "EXPIRED": status = ORDER_STATUS_FAILED else: return 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["S"], "order_type": order_info["o"], "symbol": self._symbol, "price": order_info["p"], "quantity": order_info["q"], "ctime": order_info["T"] } order = Order(**info) self._orders[order_no] = order order.remain = float(order_info["q"]) - float(order_info["z"]) order.avg_price = order_info["L"] order.status = status order.utime = order_info["T"] order.trade_type = int(order_no[-1]) SingleTask.run(self._order_update_callback, copy.copy(order)) async def on_event_asset_update(self, asset: Asset): """ Asset data update callback. Args: asset: Asset object. """ self._assets = asset SingleTask.run(self._asset_update_callback, asset)
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()
class GateFutureTrade(Websocket): """ OKEX Future Trade module """ 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://api.gateio.ws" if not kwargs.get("wss"): kwargs["wss"] = "wss://fx-ws.gateio.ws" 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 = GATE_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._contract_update_callback = kwargs.get('contract_update_callback') self._user_id = None url = self._wss + "/v4/ws" super(GateFutureTrade, 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, ... } self._position = Position(self._platform, self._account, self._strategy, self._symbol) # 初始化 REST API 对象 self._rest_api = GateFutureRestAPI(self._host, self._access_key, self._secret_key) # 初始化资产订阅 if self._asset_update_callback: AssetSubscribe(self._platform, self._account, self.on_event_asset_update) # 注册定时任务 LoopRunTask.register(self._check_position_update, 1) # 获取持仓 async def on_event_asset_update(self, asset: Asset): """ 资产数据更新回调 """ self._assets = asset SingleTask.run(self._asset_update_callback, asset) @property def assets(self): return copy.copy(self._assets) @property def orders(self): return copy.copy(self._orders) @property def rest_api(self): return self._rest_api @property def position(self): return copy.copy(self._position) async def _check_position_update(self): update = False success, error = await self._rest_api.get_position(self._symbol) if error: return if not self._position.utime: # 如果持仓还没有被初始化,那么初始化之后推送一次 update = True self._position.update() size = success['size'] average_price = float(success['entry_price']) liquid_price = float(success["liq_price"]) if size > 0: if self._position.long_quantity != size: update = True self._position.update(0, 0, size, average_price, liquid_price) elif size < 0: if self._position.short_quantity != abs(size): update = True self._position.update(abs(size), average_price, 0, 0, liquid_price) elif size == 0: if self._position.long_quantity != 0 or self._position.short_quantity != 0: update = True self._position.update() if update: await self._position_update_callback(copy.copy(self._position)) async def connected_callback(self): """ 建立连接之后,然后订阅order和position """ # 订阅order while True: success, error = await self._rest_api.get_user_account() if success: self._user_id = success['user'] break t = int(time.time()) order_message = 'channel=%s&event=%s&time=%s' % ('futures.orders', 'subscribe', t) sign = hmac.new(self._secret_key, order_message, hashlib.sha512).hexdigest() data = { 'time': t, 'channel': "futures.orders", "event": "subscribe", "payload": [self._user_id, self._symbol], "auth": { "method": "api_key", "KEY": self._access_key, "SIGN": sign } } await self.ws.send_json(data) # 获取持仓 await self._check_position_update() # # 订阅仓位 # positon_message = 'channel=%s&event=%s&time=%s' % ('futures.position_closes', 'subscribe', t) # sign = hmac.new(self._secret_key, positon_message, hashlib.sha512).hexdigest() # data = { # 'time': t, # 'channel': "futures.position_closes", # "event": "subscribe", # "payload": [self._user_id, self._symbol], # "auth": { # "method": "api_key", # "KEY": self._access_key, # "SIGN": sign # } # } # await self.ws.send_json(data) @async_method_locker('process.locker') async def process(self, msg): channel = msg['channel'] event = msg['event'] result = msg['result'] if channel == "futures.orders": # 订单订阅返回消息 if event == "subscribe": error = msg['error'] if error: if self._init_success_callback: e = Error( "subscribe order event error: {}".format(error)) SingleTask.run(self._init_success_callback, False, e) else: # 获取之前未完成的订单信息并推送 result, error = await self._rest_api.get_order_list( self._symbol, open) 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: is_update, order = self._update_order(order_info) if is_update: if self._order_update_callback: SingleTask.run(self._order_update_callback, copy.copy(order)) # 订单更新推送 else: for data in result: is_update, order = self._update_order(data) if is_update: if order and self._order_update_callback: SingleTask.run(self._order_update_callback, copy.copy(order)) return else: logger.warn("unhandle msg:", msg, caller=self) # elif channel == 'futures.position_closes': # # 仓位订阅返回消息 # if event == "subscribe": # error = msg['error'] # if error: # if self._init_success_callback: # e = Error("subscribe position event error: {}".format(error)) # SingleTask.run(self._init_success_callback, False, e) async def create_order(self, action, price, quantity, order_type=ORDER_TYPE_LIMIT): """ :param action: 交易方向 BUY/SELL :param price: 价格,数字,如果等于0表示是市价单 :param quantity: 委托数量(可以是正数,也可以是负数) :param order_type:ORDER_TYPE_LIMIT/ORDER_TYPE_MARKET :return: """ if int(quantity) > 0: if action == ORDER_ACTION_BUY: trade_type = "1" else: trade_type = "3" else: if action == ORDER_ACTION_BUY: trade_type = "4" else: trade_type = "2" result, error = self._rest_api.create_order(self._symbol, trade_type, price, quantity, order_type=order_type) if error: return None, error return result['id'], None async def revoker_order(self, *order_nos): # 如果传入order_nos为空,即撤销全部委托单 if len(order_nos) == 0: result, error = await self._rest_api.revoke_orders(self._symbol, side=None) if error: return False, error return True, None # 如果传入order_nos为一个委托单号,那么只撤销一个委托单 elif len(order_nos) == 1: success, error = await self._rest_api.revoke_order(order_nos[0]) if error: # 如果撤销失败,查询订单详情 # if error.get('label', '') == 'ORDER_FINISHED': # await self.get_order_info(order_nos[0]) return order_nos[0], error else: return order_nos[0], None # 如果传入order_nos数量大于1,那么就批量撤销传入的委托单 elif len(order_nos) > 1: success, error = [], [] for order_no in order_nos: _, e = await self._rest_api.revoke_order(order_no) if e: # if e.get('label') == 'ORDER_FINISHED': # await self.get_order_info(order_nos[0]) error.append((order_no, e)) else: success.append(order_no) return success, error async def get_order_info(self, order_no): order_info, error = await self._rest_api.get_order_info(order_no) if error: return None, error else: logger.info(order_info) is_update, order = await self._update_order(order_info) if is_update: if self._order_update_callback: SingleTask.run(self._order_update_callback, copy.copy(order)) return copy.copy(order), None async def _update_order(self, order_info): """ 更新订单信息 :param order_info: 订单信息 :return: """ order_no = order_info.get('id') is_updated = False if order_no is None: return is_updated, None remain = order_info['left'] quantity = order_info['size'] finish_as = order_info['finish_as'] state = order_info['status'] ctime = order_info['create_tim'] if state == 'finished': if finish_as == 'filled' or finish_as == 'ioc' or finish_as == 'auto_deleveraged': status = ORDER_STATUS_FILLED elif finish_as == 'cancelled' or finish_as == 'liquidated' or finish_as == 'reduce_only': status = ORDER_STATUS_CANCELED else: logger.error("status error! order_info:", order_info, caller=self) return None elif state == 'open': if quantity != remain: if remain != 0: status = ORDER_STATUS_PARTIAL_FILLED else: status = ORDER_STATUS_FILLED else: status = ORDER_STATUS_SUBMITTED else: logger.error("status error! order_info:", order_info, caller=self) return None order = self._orders.get(order_no) if order: if order.remain != remain: is_updated = True order.remain = remain elif order.status != status: is_updated = True order.status = status elif order.avg_price != order_info['fill_price']: is_updated = True order.price = order_info["price"] else: info = { "platform": self._platform, "account": self._account, "strategy": self._strategy, "order_no": order_no, "action": ORDER_ACTION_BUY if order_info["size"] > 0 else ORDER_ACTION_SELL, "symbol": self._symbol, "price": float(order_info["price"]), "quantity": abs(quantity), "trade_type": int(order_info["text"].split('-')[1]), "remain": abs(remain), "status": status, "avg_price": order_info["fill_price"] } order = Order(**info) self._orders[order_no] = order is_updated = True order.ctime = ctime order.utime = int(time.time()) if status in [ORDER_STATUS_CANCELED, ORDER_STATUS_FILLED]: self._orders.pop(order_no) return is_updated, order