diff --git a/vnpy/api/okexfuture/__init__.py b/vnpy/api/okexfuture/__init__.py deleted file mode 100644 index 6bac0f36..00000000 --- a/vnpy/api/okexfuture/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .OkexFutureApi import OkexFutureRestClient, OkexFutureWebSocketClient, OkexFutureSymbol, OkexFutureContractType, OkexFutureOrder, OkexFutureOrderStatus, OkexFuturePosition, \ - OkexFuturePositionDetail, OkexFuturePriceType, OkexFutureUserInfo diff --git a/vnpy/api/okexfuture/vnokexFuture.py b/vnpy/api/okexfuture/vnokexFuture.py deleted file mode 100644 index 67c0a879..00000000 --- a/vnpy/api/okexfuture/vnokexFuture.py +++ /dev/null @@ -1,114 +0,0 @@ -# encoding: UTF-8 -import hashlib -import urllib - -from vnpy.api.rest import Request, RestClient -from vnpy.api.websocket import WebSocketClient - - -#---------------------------------------------------------------------- -def paramsToData(params): - return urllib.urlencode(sorted(params.items())) - - -#---------------------------------------------------------------------- -def sign(dataWithApiKey, apiSecret): - """ - usage: - params = { ... , 'api_key': ...} - data = paramsToData(params) - signature = sign(data, apiSecret) - data += "&sign" + signature - - :param dataWithApiKey: sorted urlencoded args with apiKey - :return: param 'sign' for okex api - """ - dataWithSecret = dataWithApiKey + "&secret_key=" + apiSecret - return hashlib.md5(dataWithSecret.encode()).hexdigest().upper() - - -######################################################################## -class OkexFutureRestBase(RestClient): - host = 'https://www.okex.com/api/v1' - - #---------------------------------------------------------------------- - def __init__(self): - super(OkexFutureRestBase, self).__init__() - self.apiKey = None - self.apiSecret = None - - #---------------------------------------------------------------------- - # noinspection PyMethodOverriding - def init(self, apiKey, apiSecret): - # type: (str, str) -> any - super(OkexFutureRestBase, self).init(self.host) - self.apiKey = apiKey - self.apiSecret = apiSecret - - #---------------------------------------------------------------------- - def beforeRequest(self, req): # type: (Request)->Request - args = req.params or {} - args.update(req.data or {}) - if 'sign' in args: - args.pop('sign') - if 'apiKey' not in args: - args['api_key'] = self.apiKey - data = paramsToData(args) - signature = sign(data, self.apiSecret) - data += "&sign=" + signature - - req.headers = {'Content-Type': 'application/x-www-form-urlencoded'} - req.data = data - return req - - -######################################################################## -class OkexFutureWebSocketBase(WebSocketClient): - """ - Okex期货websocket客户端 - 实例化后使用init设置apiKey和secretKey(apiSecret) - """ - host = 'wss://real.okex.com:10440/websocket/okexapi' - - def __init__(self): - super(OkexFutureWebSocketBase, self).__init__() - super(OkexFutureWebSocketBase, self).init(OkexFutureWebSocketBase.host) - self.apiKey = None - self.apiSecret = None - self.autoLogin = True - - self.onConnected = self._onConnected - - #---------------------------------------------------------------------- - # noinspection PyMethodOverriding - def init(self, apiKey, secretKey, autoLogin=True): - - self.apiKey = apiKey - self.apiSecret = secretKey - self.autoLogin = autoLogin - - #---------------------------------------------------------------------- - def sendPacket(self, dictObj, authenticate=False): - if authenticate: - data = urllib.urlencode(sorted(dictObj.items())) - signature = sign(data, self.apiSecret) - dictObj['sign'] = signature - return super(OkexFutureWebSocketBase, self).sendPacket(dictObj) - - #---------------------------------------------------------------------- - def _login(self, ): - - params = {"api_key": self.apiKey, } - data = paramsToData(params) - signature = sign(data, self.apiSecret) - params['sign'] = signature - - self.sendPacket({ - "event": "login", - "parameters": params - }, authenticate=False) - - #---------------------------------------------------------------------- - def _onConnected(self): - if self.autoLogin: - self._login() diff --git a/vnpy/api/okexfuture/OkexFutureApi.py b/vnpy/api/okexfutures/OkexFuturesApi.py similarity index 55% rename from vnpy/api/okexfuture/OkexFutureApi.py rename to vnpy/api/okexfutures/OkexFuturesApi.py index 30bf7b03..6974dc1d 100644 --- a/vnpy/api/okexfuture/OkexFutureApi.py +++ b/vnpy/api/okexfutures/OkexFuturesApi.py @@ -1,14 +1,24 @@ # encoding: UTF-8 +""" +# Okex Futures API V3 坑记: +* https://www.okex.com/api/futures/v3/instruments 返回值中的trade_increment有时候会变成quote_increment +* /api/futures/v3/order 如果下单时不提供client_id,返回值将不会有client_id字段 +* websocket居然还没升级好就把API放出来了?! + +""" + from enum import Enum from typing import Any, Callable, List, Union -from vnpy.api.okexfuture.vnokexFuture import OkexFutureRestBase, OkexFutureWebSocketBase +from vnpy.api.okexfutures.OkexFuturesBase import OkexFuturesRestBaseV1, OkexFuturesRestBaseV3, \ + OkexFuturesWebSocketBase from vnpy.api.rest import Request ######################################################################## -class _OkexFutureCustomExtra(object): +class _OkexFuturesCustomExtra(object): + #---------------------------------------------------------------------- def __init__(self, onSuccess, onFailed, extra): self.onFailed = onFailed self.onSuccess = onSuccess @@ -16,7 +26,7 @@ class _OkexFutureCustomExtra(object): ######################################################################## -class OkexFutureEasySymbol(object): +class OkexFuturesEasySymbol(object): BTC = 'btc' LTC = 'ltc' ETH = 'eth' @@ -28,7 +38,7 @@ class OkexFutureEasySymbol(object): ######################################################################## -class OkexFutureSymbol(object): +class OkexFuturesSymbol(object): BTC = 'btc_usd' LTC = 'ltc_usd' ETH = 'eth_usd' @@ -37,20 +47,20 @@ class OkexFutureSymbol(object): ######################################################################## -class OkexFuturePriceType(object): +class OkexFuturesPriceType(object): Buy = 'buy' Sell = 'sell' ######################################################################## -class OkexFutureContractType(object): +class OkexFuturesContractType(object): ThisWeek = 'this_week' NextWeek = 'next_week' Quarter = 'quarter' ######################################################################## -class OkexFutureOrderType(object): +class OkexFuturesOrderType(object): OpenLong = '1' OpenShort = '2' CloseLong = '3' @@ -58,13 +68,13 @@ class OkexFutureOrderType(object): ######################################################################## -class OkexFutureOrderStatus(object): +class OkexFuturesOrderStatus(object): NotFinished = '1' Finished = '2' ######################################################################## -class OkexFutureOrder(object): +class OkexFuturesOrder(object): #---------------------------------------------------------------------- def __init__(self): @@ -84,7 +94,7 @@ class OkexFutureOrder(object): ######################################################################## -class OkexFutureUserInfo(object): +class OkexFuturesUserInfo(object): #---------------------------------------------------------------------- def __init__(self): @@ -97,16 +107,16 @@ class OkexFutureUserInfo(object): ######################################################################## -class OkexFuturePosition(object): +class OkexFuturesPosition(object): #---------------------------------------------------------------------- def __init__(self, ): self.forceLiquidatePrice = None - self.holding = [] # type: List[OkexFuturePositionDetail] + self.holding = [] # type: List[OkexFuturesPositionDetail] ######################################################################## -class OkexFuturePositionDetail(object): +class OkexFuturesPositionDetail(object): #---------------------------------------------------------------------- def __init__(self, ): @@ -128,10 +138,11 @@ class OkexFuturePositionDetail(object): ######################################################################## -class OkexFutureTickInfo(object): +class OkexFuturesTickInfo(object): #---------------------------------------------------------------------- - def __init__(self, symbol, remoteContractType, last, limitHigh, limitLow, vol, sell, buy, unitAmount, holdAmount, + def __init__(self, symbol, remoteContractType, last, limitHigh, limitLow, vol, sell, buy, + unitAmount, holdAmount, contractId, high, low): self.symbol = symbol self.remoteContractType = remoteContractType @@ -149,7 +160,7 @@ class OkexFutureTickInfo(object): ######################################################################## -class OkexFutureTradeInfo(object): +class OkexFuturesTradeInfo(object): #---------------------------------------------------------------------- def __init__(self, symbol, remoteContractType, index, price, volume, time, direction, coinVolume): @@ -164,7 +175,7 @@ class OkexFutureTradeInfo(object): ######################################################################## -class OkexFutureUserTradeInfo(object): +class OkexFuturesUserTradeInfo(object): #---------------------------------------------------------------------- def __init__(self, symbol, remoteContractType, amount, @@ -190,39 +201,362 @@ class OkexFutureUserTradeInfo(object): ######################################################################## -class OkexFutureRestClient(OkexFutureRestBase): +class OkexFuturesContractsInfoV3(object): + + #---------------------------------------------------------------------- + def __init__(self, + instrumentId, + underlyingIndex, + quoteCurrency, + quote_increment, + contractVal, + listing, + delivery, + tickSize, + ): + self.symbol = instrumentId # String # 合约ID,如BTC-USD-180213 + self.underlyingIndex = underlyingIndex # String # 交易货币币种,如:btc-usdt中的btc + self.quoteCurrency = quoteCurrency # String # 计价货币币种,如:btc-usdt中的usdt + self.quoteIncrement = quote_increment # Number # 下单数量精度 + self.contractVal = contractVal # Number # 合约面值(美元) + self.listing = listing # Date # 上线日期 + self.delivery = delivery # Date # 交割日期 + self.tickSize = tickSize # Number # 下单价格精度 + + +######################################################################## +class OkexFuturesAccountInfoV3(object): + + #---------------------------------------------------------------------- + def __init__(self, currency, balance, hold, available): + self.currency = currency # String # 币种 + self.balance = balance # number # 余额 + self.hold = hold # number # 冻结(不可用) + self.available = available # number # 可用于提现或资金划转的数量 + + +######################################################################## +class OkexFuturesPositionInfoV3(object): + + #---------------------------------------------------------------------- + def __init__(self, marginMode, liquidationPrice, longQty, longAvailQty, longAvgCost, + longSettlementPrice, + realizedPnl, shortQty, shortAvailQty, shortAvgCost, shortSettlementPrice, + instrumentId, + leverage, createAt, updatAt, + ): + self.marginMode = marginMode # String # 账户类型:全仓 crossed + self.liquidationPrice = liquidationPrice # Price # 预估爆仓价 + self.longQty = longQty # Number # 多仓数量 + self.longAvailQty = longAvailQty # Number # 多仓可平仓数量 + self.longAvgCost = longAvgCost # Price # 开仓平均价 + self.longSettlementPrice = longSettlementPrice # Price # 多仓结算基准价 + self.realizedPnl = realizedPnl # Number # 已实现盈余 + self.shortQty = shortQty # Number # 空仓数量 + self.shortAvailQty = shortAvailQty # Number # 空仓可平仓数量 + self.shortAvgCost = shortAvgCost # Price # 开仓平均价 + self.shortSettlementPrice = shortSettlementPrice # String # 空仓结算基准价 + self.symbol = instrumentId # Number # 合约ID,如BTC-USD-180213 + self.leverage = leverage # Date # 杠杆倍数 + self.createAt = createAt # Date # 创建时间 + self.updatAt = updatAt # Date # 更新时间 + + +######################################################################## +class OkexFuturesOrderSentInfoV3(object): + + #---------------------------------------------------------------------- + def __init__(self, orderId, clientOid, errorCode, errorMessage): + self.orderId = orderId # String # 订单ID,下单失败时,此字段值为-1 + self.clientOid = clientOid # String # 由您设置的订单ID来识别您的订单 + self.errorCode = errorCode # Number # 错误码,下单成功时为0,下单失败时会显示相应错误码 + self.errorMessage = errorMessage # String # 错误信息,下单成功时为空,下单失败时会显示错误信息 + + +######################################################################## +class OkexFuturesOrderDetailV3(object): + + #---------------------------------------------------------------------- + def __init__(self, instrumentId, size, timestamp, filledQty, fee, orderId, price, priceAvg, + status, orderType, contractVal, leverage, ): + self.symbol = instrumentId # String # 合约ID,如BTC-USD-180213 + self.volume = size # Number # 数量 + self.timestamp = timestamp # Date # 委托时间 + self.tradedVolume = filledQty # Number # 成交数量 + self.fee = fee # Price # 手续费 + self.remoteId = orderId # String # 订单ID + self.price = price # Price # 订单价格 + self.priceAvg = priceAvg # Price # 平均价格 + self.status = status # Number # 订单状态(0:等待成交 1:部分成交 2:已完成) + self.orderType = orderType # Number # 订单类型(1:开多 2:开空 3:开多 4:平空) + self.contractVal = contractVal # Price # 合约面值 + self.leverage = leverage # Number # 杠杆倍数 value:10/20 默认10 + + +######################################################################## +class OkexFuturesRestClientV3(OkexFuturesRestBaseV3): + """ + Okex新出了V3版本的API,这里用的是V3的版本 + """ + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + super(OkexFuturesRestClientV3, self).__init__() + + #---------------------------------------------------------------------- + def sendOrder(self, + symbol, + orderType, + price, + volume, + leverRate, # type: int # 档杆倍数,10或者20 + onSuccess, # type: Callable[[OkexFuturesOrderSentInfoV3, Any], Any] + onFailed=None, # type: Callable[[OkexFuturesOrderSentInfoV3, Any], Any] + matchPrice=False, # type: bool # 是否为市价单 + clientOid=None, # type: str # OkexAPI提供的的用户自定义字段 + extra=None + ): + """ 下单 """ + data = { + 'client_oid': clientOid, + 'instrument_id': symbol, + 'type': orderType, + 'size': volume, + 'leverage': leverRate, + + 'price': price, + 'match_price': '0' + } + # if matchPrice: + # data['match_price'] = '1' + # else: + # data['price'] = price + + return self.addRequest('POST', '/api/futures/v3/order', + callback=self._onOrderSent, + data=data, + extra=_OkexFuturesCustomExtra(onSuccess, onFailed, extra) + ) + + #---------------------------------------------------------------------- + def cancelOrder(self, + symbol, # type: str + remoteId, # type: str + onSuccess, # type: Callable[[Any], Any] + onFailed=None, # type: Callable[[Any], Any] + extra=None + ): # type: (...)->Request + """撤单""" + path = '/api/futures/v3/cancel_order/' + symbol + '/' + remoteId + return self.addRequest('POST', path, + callback=self._onOrderCanceled, + data={ + 'instrument_id' : symbol, + 'order_id': remoteId + }, + extra=_OkexFuturesCustomExtra(onSuccess, onFailed, extra) + ) + + #---------------------------------------------------------------------- + def queryAccount(self, + onSuccess, # type: Callable[[List[OkexFuturesAccountInfoV3], Any], Any] + extra=None + ): # type: (...)->Request + """ + 查询全部账户资金 + """ + return self.addRequest('GET', '/api/account/v3/wallet', + callback=self._onAccounts, + extra=_OkexFuturesCustomExtra(onSuccess, None, extra) + ) + + #---------------------------------------------------------------------- + def queryOrders(self, + symbol, + status, # type: OkexFuturesOrderStatus + onSuccess, # type: Callable[[List[OkexFuturesOrderDetailV3], Any], Any] + onFailed=None, # type: Callable[[Any], Any] + startPage=0, # type: int # 取回的页数区间为:(start, end) + endPage=2, # type: int # 取回的页数区间为:(start, end) + numberPerPage=100, # type: int + extra=None + ): # type: (...)->Request + """查询账户订单""" + path = '/api/futures/v3/orders/' + symbol + return self.addRequest("POST", path, + data={ + 'status': status, + 'instrument_id': symbol, + 'from': startPage, + 'to': endPage, + 'limit': numberPerPage, + }, + callback=self._onOrders, + extra=_OkexFuturesCustomExtra(onSuccess, onFailed, extra) + ) + + #---------------------------------------------------------------------- + def queryPositions(self, + onSuccess, + extra=None): + """ 获取全部持仓 """ + return self.addRequest('GET', '/api/account/v3/position', + callback=self._onPositions, + extra=_OkexFuturesCustomExtra(onSuccess, None, extra) + ) + + #---------------------------------------------------------------------- + def queryContracts(self, + onSuccess, # type: Callable[[List[OkexFuturesContractsInfoV3], Any], Any] + extra=None): + """ 获取全部合约信息 """ + return self.addRequest('GET', '/api/futures/v3/instruments', + callback=self._onContracts, + extra=_OkexFuturesCustomExtra(onSuccess, None, extra) + ) + + #---------------------------------------------------------------------- + @staticmethod + def _onOrderSent(data, request): #type: (dict, Request)->None + """下单回调""" + extra = request.extra # type: _OkexFuturesCustomExtra + order = OkexFuturesOrderSentInfoV3( + data['order_id'], + data['client_oid'] if 'client_oid' in data else None, + data['error_code'], + data['error_message'], + ) + if order.orderId != '-1': + extra.onSuccess(order, extra.extra) + else: + if extra.onFailed: + extra.onFailed(order, extra.extra) + + #---------------------------------------------------------------------- + def _onOrderCanceled(self, data, request): #type: (dict, Request)->None + """撤单回调""" + extra = request.extra # type: _OkexFuturesCustomExtra + result = data['result'] + if result is True: + extra.onSuccess(extra.extra) + else: + if extra.onFailed: + extra.onFailed(extra.extra) + + #---------------------------------------------------------------------- + @staticmethod + def _onAccounts(data, request): #type: (dict, Request)->None + """账户资金回调""" + extra = request.extra # type: _OkexFuturesCustomExtra + accs = [] + for acc in data: + accs.append(OkexFuturesAccountInfoV3( + acc['currency'], + acc['balance'], + acc['hold'], + acc['available'], + )) + extra.onSuccess(accs, extra.extra) + + #---------------------------------------------------------------------- + @staticmethod + def _onOrders(data, request): #type: (dict, Request)->None + """ + 查询订单回调 + https://www.okex.com/docs/zh/#futures-list + """ + extra = request.extra # type: _OkexFuturesCustomExtra + if data['result'] is True: + os = [] + for info in data['orders']: + os.append(OkexFuturesOrderDetailV3( + info['instrument_id'], + info['size'], + info['timestamp'], + info['filled_qty'], + info['fee'], + info['order_id'], + info['price'], + info['price_avg'], + info['status'], + info['type'], + info['contract_val'], + info['leverage'], + )) + extra.onSuccess(os, extra.extra) + else: + if extra.onFailed: + extra.onFailed(extra.extra) + + + #---------------------------------------------------------------------- + @staticmethod + def _onPositions(data, request): #type: (dict, Request)->None + extra = request.extra # type: _OkexFuturesCustomExtra + accs = [] + for acc in data: + accs.append(OkexFuturesPositionInfoV3( + acc['margin_mode'], + acc['liquidation_price'], + acc['long_qty'], + acc['long_avail_qty'], + acc['long_avg_cost'], + acc['long_settlement_price'], + acc['realized_pnl'], + acc['short_qty'], + acc['short_avail_qty'], + acc['short_avg_cost'], + acc['short_settlement_price'], + acc['instrument_id'], + acc['leverage'], + acc['create_at'], + acc['updat_at'], + )) + extra.onSuccess(accs, extra.extra) + + #---------------------------------------------------------------------- + @staticmethod + def _onContracts(data, request): #type: (dict, Request)->None + """ + 合约信息回调 + https://www.okex.com/docs/zh/#futures-contract_information + """ + extra = request.extra # type: _OkexFuturesCustomExtra + ins = [] + for instrument in data: + ins.append(OkexFuturesContractsInfoV3( + instrument['instrument_id'], + instrument['underlying_index'], + instrument['quote_currency'], + instrument['quote_increment'] if 'quote_increment' in instrument else instrument['trade_increment'], + instrument['contract_val'], + instrument['listing'], + instrument['delivery'], + instrument['tick_size'], + )) + extra.onSuccess(ins, extra.extra) + + +######################################################################## +class OkexFuturesRestClientV1(OkexFuturesRestBaseV1): + """ + 这里用的是旧的v1版本的OkexAPI + """ #---------------------------------------------------------------------- def __init__(self): """Constructor""" - super(OkexFutureRestClient, self).__init__() - - self.client = () - - self._redirectedOnError = None # type: Callable[[object, object, object, Request], Any] - - #---------------------------------------------------------------------- - def setOnError(self, callback): # type: (Callable[[object, object, object, Request], Any])->None - self._redirectedOnError = callback - - #---------------------------------------------------------------------- - def onError(self, exceptionType, exceptionValue, tb, req): - if self._redirectedOnError: - self._redirectedOnError(exceptionType, exceptionValue, tb, req) - - #---------------------------------------------------------------------- - def onFailed(self, httpStatusCode, req): - super(OkexFutureRestClient, self).onFailed(httpStatusCode, req) + super(OkexFuturesRestClientV1, self).__init__() #---------------------------------------------------------------------- def sendOrder(self, symbol, contractType, orderType, volume, onSuccess, onFailed=None, price=None, useMarketPrice=False, leverRate=None, - extra=None): # type:(str, OkexFutureContractType, OkexFutureOrderType, float, Callable[[str, Any], Any], Callable[[int, Any], Any], float, bool, Union[int, None], Any)->Request + extra=None): # type:(str, OkexFuturesContractType, OkexFuturesOrderType, float, Callable[[str, Any], Any], Callable[[int, Any], Any], float, bool, Union[int, None], Any)->Request """ :param symbol: str - :param contractType: OkexFutureContractType - :param orderType: OkexFutureOrderType + :param contractType: OkexFuturesContractType + :param orderType: OkexFuturesOrderType :param volume: float :param onSuccess: (orderId: int)->Any :param onFailed: ()->Any @@ -251,15 +585,15 @@ class OkexFutureRestClient(OkexFutureRestBase): '/future_trade.do', callback=self.onOrderSent, data=data, - extra=_OkexFutureCustomExtra(onSuccess, onFailed, extra)) + extra=_OkexFuturesCustomExtra(onSuccess, onFailed, extra)) return request #---------------------------------------------------------------------- def cancelOrder(self, symbol, contractType, orderId, onSuccess, onFailed=None, - extra=None): # type: (str, OkexFutureContractType, str, Callable[[object], Any], Callable[[int, Any], Any], Any)->Request + extra=None): # type: (str, OkexFuturesContractType, str, Callable[[object], Any], Callable[[int, Any], Any], Any)->Request """ :param symbol: str - :param contractType: OkexFutureContractType + :param contractType: OkexFuturesContractType :param orderId: str :param onSuccess: ()->Any :param onFailed: ()->Any @@ -275,18 +609,18 @@ class OkexFutureRestClient(OkexFutureRestBase): '/future_cancel.do', callback=self.onOrderCanceled, data=data, - extra=_OkexFutureCustomExtra(onSuccess, onFailed, extra)) + extra=_OkexFuturesCustomExtra(onSuccess, onFailed, extra)) #---------------------------------------------------------------------- def queryOrder(self, symbol, contractType, orderId, onSuccess, onFailed=None, - extra=None): # type: (str, OkexFutureContractType, str, Callable[[List[OkexFutureOrder], Any], Any], Callable[[int, Any], Any], Any)->Request + extra=None): # type: (str, OkexFuturesContractType, str, Callable[[List[OkexFuturesOrder], Any], Any], Callable[[int, Any], Any], Any)->Request """ @note onSuccess接收的第一个参数是列表,并且有可能为空 :param symbol: str - :param contractType: OkexFutureContractType + :param contractType: OkexFuturesContractType :param orderId: str - :param onSuccess: (orders: List[OkexFutureOrder], extra:Any)->Any + :param onSuccess: (orders: List[OkexFuturesOrder], extra:Any)->Any :param onFailed: (extra: Any)->Any :param extra: Any :return: Request @@ -300,19 +634,19 @@ class OkexFutureRestClient(OkexFutureRestBase): '/future_order_info.do', callback=self.onOrder, data=data, - extra=_OkexFutureCustomExtra(onSuccess, onFailed, extra)) + extra=_OkexFuturesCustomExtra(onSuccess, onFailed, extra)) #---------------------------------------------------------------------- def queryOrders(self, symbol, contractType, status, onSuccess, onFailed=None, pageIndex=0, pageLength=50, - extra=None): # type: (str, OkexFutureContractType, OkexFutureOrderStatus, Callable[[List[OkexFutureOrder], Any], Any], Callable[[int, Any], Any], int, int, Any)->Request + extra=None): # type: (str, OkexFuturesContractType, OkexFuturesOrderStatus, Callable[[List[OkexFuturesOrder], Any], Any], Callable[[int, Any], Any], int, int, Any)->Request """ @note onSuccess接收的第一个参数是列表,并且有可能为空 :param symbol: str - :param contractType: OkexFutureContractType - :param onSuccess: (List[OkexFutureOrder], extra:Any)->Any + :param contractType: OkexFuturesContractType + :param onSuccess: (List[OkexFuturesOrder], extra:Any)->Any :param onFailed: (extra: Any)->Any :param pageIndex: 页码 :param pageLength: 最大显示数量(最大值50) @@ -332,14 +666,14 @@ class OkexFutureRestClient(OkexFutureRestBase): '/future_order_info.do', callback=self.onOrder, data=data, - extra=_OkexFutureCustomExtra(onSuccess, onFailed, extra)) + extra=_OkexFuturesCustomExtra(onSuccess, onFailed, extra)) #---------------------------------------------------------------------- def queryUserInfo(self, onSuccess, onFailed=None, - extra=None): # type: (Callable[[List[OkexFutureUserInfo], Any], Any], Callable[[int, Any], Any], Any)->Request + extra=None): # type: (Callable[[List[OkexFuturesUserInfo], Any], Any], Callable[[int, Any], Any], Any)->Request """ 查询用户信息 - :param onSuccess: (userInfos: List[OkexFutureUserInfo], extra: Any)->Any + :param onSuccess: (userInfos: List[OkexFuturesUserInfo], extra: Any)->Any :param onFailed: (extra: Any)->Any :param extra: Any :return: Request @@ -347,16 +681,16 @@ class OkexFutureRestClient(OkexFutureRestBase): return self.addRequest('POST', '/future_userinfo.do', callback=self.onOrder, - extra=_OkexFutureCustomExtra(onSuccess, onFailed, extra)) + extra=_OkexFuturesCustomExtra(onSuccess, onFailed, extra)) #---------------------------------------------------------------------- def queryPosition(self, symbol, contractType, onSuccess, onFailed=None, - extra=None): # type: (str, OkexFutureContractType, Callable[[OkexFuturePosition, Any], Any], Callable[[int, Any], Any], Any)->Request + extra=None): # type: (str, OkexFuturesContractType, Callable[[OkexFuturesPosition, Any], Any], Callable[[int, Any], Any], Any)->Request """ - :param symbol: OkexFutureSymbol - :param contractType: OkexFutureContractType - :param onSuccess: (pos:OkexFuturePosition, extra: any)->Any + :param symbol: OkexFuturesSymbol + :param contractType: OkexFuturesContractType + :param onSuccess: (pos:OkexFuturesPosition, extra: any)->Any :param onFailed: (errorCode: int, extra: any)->Any :param extra: :return: @@ -369,15 +703,15 @@ class OkexFutureRestClient(OkexFutureRestBase): '/future_position.do', data=data, callback=self.onPosition, - extra=_OkexFutureCustomExtra(onSuccess, onFailed, extra)) + extra=_OkexFuturesCustomExtra(onSuccess, onFailed, extra)) #---------------------------------------------------------------------- @staticmethod - def onOrderSent(data, req): # type: (dict, Request)->None + def onOrderSent(data, request): # type: (dict, Request)->None """ 下单回执,一般用来保存sysId """ - extra = req.extra # type: _OkexFutureCustomExtra + extra = request.extra # type: _OkexFuturesCustomExtra if data['result'] is True: remoteId = data['order_id'] extra.onSuccess(remoteId, extra.extra) @@ -390,12 +724,12 @@ class OkexFutureRestClient(OkexFutureRestBase): #---------------------------------------------------------------------- @staticmethod - def onOrderCanceled(data, req): # type: (dict, Request)->None + def onOrderCanceled(data, request): # type: (dict, Request)->None """ 取消订单回执 """ success = data['result'] - extra = req.extra # type: _OkexFutureCustomExtra + extra = request.extra # type: _OkexFuturesCustomExtra if success: extra.onSuccess(extra.extra) else: @@ -407,13 +741,13 @@ class OkexFutureRestClient(OkexFutureRestBase): #---------------------------------------------------------------------- @staticmethod - def onOrder(data, req): # type: (dict, Request)->None + def onOrder(data, request): # type: (dict, Request)->None success = data['result'] - extra = req.extra # type: _OkexFutureCustomExtra + extra = request.extra # type: _OkexFuturesCustomExtra if success: orders = [] for order in data['orders']: - okexOrder = OkexFutureOrder() + okexOrder = OkexFuturesOrder() okexOrder.volume = order['amount'] okexOrder.contractName = order['contract_name'] @@ -421,7 +755,7 @@ class OkexFutureRestClient(OkexFutureRestBase): okexOrder.tradedVolume = order['deal_amount'] okexOrder.fee = order['fee'] okexOrder.leverRate = order['lever_rate'] - okexOrder.remoteId = order['order_id'] + okexOrder.remoteId = str(order['orderId']) okexOrder.price = order['price'] okexOrder.priceAvg = order['price_avg'] okexOrder.status = order['status'] @@ -439,14 +773,14 @@ class OkexFutureRestClient(OkexFutureRestBase): #---------------------------------------------------------------------- @staticmethod - def onUserInfo(data, req): # type: (dict, Request)->None + def onUserInfo(data, request): # type: (dict, Request)->None success = data['result'] - extra = req.extra # type: _OkexFutureCustomExtra + extra = request.extra # type: _OkexFuturesCustomExtra if success: infos = data['info'] uis = [] for easySymbol, info in infos.items(): # type: str, dict - ui = OkexFutureUserInfo() + ui = OkexFuturesUserInfo() ui.easySymbol = easySymbol ui.accountRights = info['account_rights'] ui.keepDeposit = info['keep_deposit'] @@ -464,14 +798,14 @@ class OkexFutureRestClient(OkexFutureRestBase): #---------------------------------------------------------------------- @staticmethod - def onPosition(data, req): # type: (dict, Request)->None + def onPosition(data, request): # type: (dict, Request)->None success = data['result'] - extra = req.extra # type: _OkexFutureCustomExtra + extra = request.extra # type: _OkexFuturesCustomExtra if success: - pos = OkexFuturePosition() + pos = OkexFuturesPosition() pos.forceLiquidatePrice = data['force_liqu_price'] for item in data['holding']: - posDetail = OkexFuturePositionDetail() + posDetail = OkexFuturesPositionDetail() posDetail.buyAmount = item['buy_amount'] posDetail.buyAvailable = item['buy_available'] posDetail.buyPriceAvg = item['buy_price_avg'] @@ -504,16 +838,16 @@ class OkexFutureRestClient(OkexFutureRestBase): ######################################################################## -class OkexFutureWebSocketClient(OkexFutureWebSocketBase): +class OkexFuturesWebSocketClient(OkexFuturesWebSocketBase): #---------------------------------------------------------------------- def __init__(self): - super(OkexFutureWebSocketClient, self).__init__() + super(OkexFuturesWebSocketClient, self).__init__() self.onTick = self.defaultOnTick self.onUserTrade = self.defaultOnUserTrade #---------------------------------------------------------------------- - def subscribe(self, easySymbol, contractType): # type: (OkexFutureEasySymbol, OkexFutureContractType)->None + def subscribe(self, easySymbol, contractType): # type: (OkexFuturesEasySymbol, OkexFuturesContractType)->None self.sendPacket({ 'event': 'addChannel', 'channel': 'ok_sub_futureusd_' + easySymbol + '_ticker_' + contractType @@ -521,18 +855,15 @@ class OkexFutureWebSocketClient(OkexFutureWebSocketBase): #---------------------------------------------------------------------- def subscribeUserTrade(self): - # todo: 没有测试条件 self.sendPacket({ 'event': 'addChannel', 'channel': 'ok_sub_futureusd_trades' }) #---------------------------------------------------------------------- - def defaultOnPacket(self, packets): + def onPacket(self, packets): for packet in packets: - print('packets:') - print(packets) channelName = None if 'channel' in packet: channelName = packet['channel'] @@ -543,56 +874,55 @@ class OkexFutureWebSocketClient(OkexFutureWebSocketBase): channel = parseChannel(channelName) # type: ExtraSymbolChannel if channel.type == ChannelType.Tick: - self.onTick(OkexFutureTickInfo( + self.onTick(OkexFuturesTickInfo( symbol=channel.symbol, remoteContractType=channel.remoteContractType, - last=packet['last'], # float # 最高买入限制价格 - limitHigh=packet['limitHigh'], # str # 最高买入限制价格 - limitLow=packet['limitLow'], # str # 最低卖出限制价格 - vol=packet['vol'], # float # 24 小时成交量 - sell=packet['sell'], # float # 卖一价格 - buy=packet['buy'], # float # 买一价格 - unitAmount=packet['unitAmount'], # float # 合约价值 - holdAmount=packet['hold_amount'], # float # 当前持仓量 - contractId=packet['contractId'], # long # 合约ID - high=packet['high'], # float # 24 小时最高价格 - low=packet['low'], # float # 24 小时最低价格 + last=packet['last'], + limitHigh=packet['limitHigh'], + limitLow=packet['limitLow'], + vol=packet['vol'], + sell=packet['sell'], + buy=packet['buy'], + unitAmount=packet['unitAmount'], + holdAmount=packet['hold_amount'], + contractId=packet['contractId'], + high=packet['high'], + low=packet['low'], )) # elif channel.type == ChannelType.Trade: # trades = [] # for tradeInfo in packet: - # trades.append(OkexFutureTradeInfo( + # trades.append(OkexFuturesTradeInfo( # channel.symbol, channel.remoteContractType, *tradeInfo # )) # self.onTrades(trades) - # todo: 没有测试条件 elif channel.type == ChannelType.UserTrade: - self.onUserTrade(OkexFutureUserTradeInfo( - symbol=packet['symbol'], # str # btc_usd ltc_usd eth_usd etc_usd bch_usd + self.onUserTrade(OkexFuturesUserTradeInfo( + symbol=packet['symbol'], remoteContractType=packet['contract_type'], - amount=packet['amount'], # float # 委托数量 - contractName=packet['contract_name'], # str # 合约名称 - createdDate=packet['created_date'], # long # 委托时间 - createDateStr=packet['create_date_str'], # str # 委托时间字符串 - dealAmount=packet['deal_amount'], # float # 成交数量 - fee=packet['fee'], # float # 手续费 - orderId=packet['order_id'], # long # 订单ID - price=packet['price'], # float # 订单价格 - priceAvg=packet['price_avg'], # float # 平均价格 - status=packet['status'], # int # 订单状态(0等待成交 1部分成交 2全部成交 -1撤单 4撤单处理中) - type=packet['type'], # int # 订单类型 1:开多 2:开空 3:平多 4:平空 - unitAmount=packet['unit_amount'], # float # 合约面值 - leverRate=packet['lever_rate'], # float # 杠杆倍数 value:10/20 默认10 - systemType=packet['system_type'], # int # 订单类型 0:普通 1:交割 2:强平 4:全平 5:系统反单 + amount=packet['amount'], + contractName=packet['contract_name'], + createdDate=packet['created_date'], + createDateStr=packet['create_date_str'], + dealAmount=packet['deal_amount'], + fee=packet['fee'], + orderId=packet['order_id'], + price=packet['price'], + priceAvg=packet['price_avg'], + status=packet['status'], + type=packet['type'], + unitAmount=packet['unit_amount'], + leverRate=packet['lever_rate'], + systemType=packet['system_type'], )) #---------------------------------------------------------------------- - def defaultOnTick(self, tick): # type: (OkexFutureTickInfo)->None + def defaultOnTick(self, tick): # type: (OkexFuturesTickInfo)->None pass #---------------------------------------------------------------------- - def defaultOnUserTrade(self, tick): # type: (OkexFutureUserTradeInfo)->None + def defaultOnUserTrade(self, tick): # type: (OkexFuturesUserTradeInfo)->None pass @@ -823,5 +1153,5 @@ def remotePrefixToRemoteContractType(prefix): return _prefixForRemoteContractType[prefix] -_prefixForRemoteContractType = {v.split('_')[0]: v for k, v in OkexFutureContractType.__dict__.items() if +_prefixForRemoteContractType = {v.split('_')[0]: v for k, v in OkexFuturesContractType.__dict__.items() if not k.startswith('_')} diff --git a/vnpy/api/okexfutures/OkexFuturesBase.py b/vnpy/api/okexfutures/OkexFuturesBase.py new file mode 100644 index 00000000..4a0ab12d --- /dev/null +++ b/vnpy/api/okexfutures/OkexFuturesBase.py @@ -0,0 +1,171 @@ +# encoding: UTF-8 +import base64 +import hashlib +import hmac +import json +import urllib + +import time + +from vnpy.api.rest import Request, RestClient +from vnpy.api.websocket import WebSocketClient + + +#---------------------------------------------------------------------- +def paramsToDataV1(params): + return urllib.urlencode(sorted(params.items())) + + +#---------------------------------------------------------------------- +def signV1(dataWithApiKey, apiSecret): + """ + usage: + params = { ... , 'api_key': ...} + data = paramsToData(params) + signature = sign(data, apiSecret) + data += "&sign" + signature + + :param dataWithApiKey: sorted urlencoded args with apiKey + :return: param 'sign' for okex api + """ + dataWithSecret = dataWithApiKey + "&secret_key=" + apiSecret + return hashlib.md5(dataWithSecret.encode()).hexdigest().upper() + + +#---------------------------------------------------------------------- +def signV3(dataToSign, apiSecret): + return base64.b64encode( hmac.new(apiSecret, dataToSign.encode(), hashlib.sha256).digest()) + +######################################################################## +class OkexFuturesRestBaseV1(RestClient): + host = 'https://www.okex.com/api/v1' + + #---------------------------------------------------------------------- + def __init__(self): + super(OkexFuturesRestBaseV1, self).__init__() + self.apiKey = None + self.apiSecret = None + + #---------------------------------------------------------------------- + # noinspection PyMethodOverriding + def init(self, apiKey, apiSecret, apiPassphrase): + # type: (str, str, str) -> any + super(OkexFuturesRestBaseV1, self).init(self.host) + self.apiKey = apiKey + self.apiSecret = apiSecret + + #---------------------------------------------------------------------- + def sign(self, request): # type: (Request)->Request + args = request.params or {} + args.update(request.data or {}) + if 'sign' in args: + args.pop('sign') + if 'apiKey' not in args: + args['api_key'] = self.apiKey + data = paramsToDataV1(args) + signature = signV1(data, self.apiSecret) + data += "&sign=" + signature + + request.headers = {'Content-Type': 'application/x-www-form-urlencoded'} + request.data = data + return request + + +######################################################################## +class OkexFuturesRestBaseV3(RestClient): + """ + Okex Rest API v3基础类 + """ + host = 'https://www.okex.com' + + #---------------------------------------------------------------------- + def __init__(self): + super(OkexFuturesRestBaseV3, self).__init__() + self.apiKey = None + self.apiSecret = None + self.apiPassphrase = None + + #---------------------------------------------------------------------- + # noinspection PyMethodOverriding + def init(self, apiKey, apiSecret, apiPassphrase): + # type: (str, str, str) -> any + super(OkexFuturesRestBaseV3, self).init(self.host) + self.apiKey = apiKey + self.apiSecret = apiSecret + self.apiPassphrase = apiPassphrase + + #---------------------------------------------------------------------- + def sign(self, request): # type: (Request)->Request + timestamp = str(time.time()) + + data = json.dumps(request.data) + request.data = data + dataToSign = timestamp + request.method + request.path + data + + signature = signV3(dataToSign, self.apiSecret) + + request.headers = { + 'OK-ACCESS-KEY': self.apiKey, + 'OK-ACCESS-SIGN': signature, + 'OK-ACCESS-TIMESTAMP': timestamp, + 'OK-ACCESS-PASSPHRASE': self.apiPassphrase, + 'Content-Type': 'application/json' + } + return request + + +######################################################################## +class OkexFuturesWebSocketBase(WebSocketClient): + """ + Okex期货websocket客户端 + 实例化后使用init设置apiKey和secretKey(apiSecret) + """ + host = 'wss://real.okex.com:10440/websocket/okexapi' + + def __init__(self): + super(OkexFuturesWebSocketBase, self).__init__() + super(OkexFuturesWebSocketBase, self).init(OkexFuturesWebSocketBase.host) + self.apiKey = None + self.apiSecret = None + self.apiPassphrase = None + + self.autoLogin = True + + self.onConnected = self._onConnected + + #---------------------------------------------------------------------- + # noinspection PyMethodOverriding + def init(self, apiKey, secretKey, apiPassphrase, autoLogin=True): + + self.apiKey = apiKey + self.apiSecret = secretKey + self.apiPassphrase = apiPassphrase + self.autoLogin = autoLogin + + #---------------------------------------------------------------------- + def sendPacket(self, dictObj, authenticate=False): + if authenticate: + pass + return super(OkexFuturesWebSocketBase, self).sendPacket(dictObj) + + #---------------------------------------------------------------------- + def _login(self, ): + timestamp = str(time.time()) + + data = timestamp + 'GET' + '/users/self/verify' + signature = signV3(data, self.apiSecret) + + self.sendPacket({ + "event": "login", + "parameters": { + "api_key": self.apiKey, + "timestamp": timestamp, + "passphrase": self.apiPassphrase, + "sign": signature, + } + }, authenticate=False) + + #---------------------------------------------------------------------- + def _onConnected(self): + if self.autoLogin: + self._login() diff --git a/vnpy/api/okexfutures/__init__.py b/vnpy/api/okexfutures/__init__.py new file mode 100644 index 00000000..0b3fb37f --- /dev/null +++ b/vnpy/api/okexfutures/__init__.py @@ -0,0 +1,6 @@ +from .OkexFuturesApi import OkexFuturesAccountInfoV3, OkexFuturesContractType, \ + OkexFuturesContractsInfoV3, OkexFuturesOrder, OkexFuturesOrderDetailV3, \ + OkexFuturesOrderSentInfoV3, OkexFuturesOrderStatus, OkexFuturesPosition, \ + OkexFuturesPositionDetail, OkexFuturesPositionInfoV3, OkexFuturesPriceType, \ + OkexFuturesRestClientV1, OkexFuturesRestClientV3, OkexFuturesSymbol, OkexFuturesUserInfo, \ + OkexFuturesWebSocketClient diff --git a/vnpy/trader/gateway/okexFutureGateway/__init__.py b/vnpy/trader/gateway/okexFuturesGateway/__init__.py similarity index 100% rename from vnpy/trader/gateway/okexFutureGateway/__init__.py rename to vnpy/trader/gateway/okexFuturesGateway/__init__.py diff --git a/vnpy/trader/gateway/okexFutureGateway/okexFutureGateway.py b/vnpy/trader/gateway/okexFuturesGateway/okexFutureGateway.py similarity index 70% rename from vnpy/trader/gateway/okexFutureGateway/okexFutureGateway.py rename to vnpy/trader/gateway/okexFuturesGateway/okexFutureGateway.py index f9346d9e..f998b601 100644 --- a/vnpy/trader/gateway/okexFutureGateway/okexFutureGateway.py +++ b/vnpy/trader/gateway/okexFuturesGateway/okexFutureGateway.py @@ -3,57 +3,20 @@ from __future__ import print_function import json -from abc import abstractmethod from typing import Dict -from vnpy.api.okexfuture.OkexFutureApi import * +from vnpy.api.okexfutures.OkexFuturesApi import * from vnpy.trader.vtFunction import getJsonPath from vnpy.trader.vtGateway import * -######################################################################## -class VnpyGateway(VtGateway): - """ - 每个gateway有太多重复代码,难以拓展和维护。 - 于是我设计了这个类,将重复代码抽取出来,简化gateway的实现 - """ - - #---------------------------------------------------------------------- - def readConfig(self): - """ - 从json文件中读取设置,并将其内容返回为一个dict - :一个一个return: - """ - fileName = self.gatewayName + '_connect.json' - filePath = getJsonPath(fileName, __file__) - - try: - with open(filePath, 'rt') as f: - return json.load(f) - except IOError: - log = VtLogData() - log.gatewayName = self.gatewayName - log.logContent = u'读取连接配置出错,请检查' - # todo: pop a message box is better - self.onLog(log) - return - - #---------------------------------------------------------------------- - @abstractmethod - def loadSetting(self): - """ - 载入设置,在connect的时候会被调用到。 - """ - pass - - ######################################################################## class _Order(object): _lastLocalId = 0 #---------------------------------------------------------------------- - def __ini__(self): + def __init__(self): _Order._lastLocalId += 1 self.localId = str(_Order._lastLocalId) self.remoteId = None @@ -61,19 +24,21 @@ class _Order(object): ######################################################################## -class OkexFutureGateway(VnpyGateway): +class OkexFuturesGateway(VtGateway): """OKEX期货交易接口""" #---------------------------------------------------------------------- def __init__(self, eventEngine, *_, **__): # args, kwargs is needed for compatibility """Constructor""" - super(OkexFutureGateway, self).__init__(eventEngine, 'OkexFutureGateway') + super(OkexFuturesGateway, self).__init__(eventEngine, 'OkexFuturesGateway') + self.exchange = constant.EXCHANGE_OKEXFUTURE self.apiKey = None # type: str self.apiSecret = None # type: str + self.apiPassphrase = None # type: str - self.restApi = OkexFutureRestClient() + self.restApi = OkexFuturesRestClientV3() - self.webSocket = OkexFutureWebSocketClient() + self.webSocket = OkexFuturesWebSocketClient() self.webSocket.onTick = self._onTick self.webSocket.onUserTrade = self._onUserTrade @@ -83,14 +48,30 @@ class OkexFutureGateway(VnpyGateway): self.tradeID = 0 self._orders = {} # type: Dict[str, _Order] self._remoteIds = {} # type: Dict[str, _Order] - + #---------------------------------------------------------------------- - @property - def exchange(self): # type: ()->str - return constant.EXCHANGE_OKEXFUTURE + def readConfig(self): + """ + 从json文件中读取设置,并将其内容返回为一个dict + :一个一个return: + """ + fileName = self.gatewayName + '_connect.json' + filePath = getJsonPath(fileName, __file__) + + try: + with open(filePath, 'rt') as f: + return json.load(f) + except IOError: + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'读取连接配置出错,请检查' + # todo: pop a message box is better + self.onLog(log) + return None #---------------------------------------------------------------------- def loadSetting(self): + """载入设置""" setting = self.readConfig() if setting: """连接""" @@ -100,6 +81,7 @@ class OkexFutureGateway(VnpyGateway): # or check by validator self.apiKey = str(setting['apiKey']) self.apiSecret = str(setting['secretKey']) + self.apiPassphrase = str(setting['passphrase']) self.leverRate = setting['leverRate'] self.symbols = setting['symbols'] except KeyError: @@ -111,12 +93,13 @@ class OkexFutureGateway(VnpyGateway): #---------------------------------------------------------------------- def connect(self): + """连接""" self.loadSetting() - self.restApi.init(self.apiKey, self.apiSecret) - self.webSocket.init(self.apiKey, self.apiSecret) + self.restApi.init(self.apiKey, self.apiSecret, self.apiPassphrase) + self.webSocket.init(self.apiKey, self.apiSecret, self.apiPassphrase) self.restApi.start() self.webSocket.start() - + #---------------------------------------------------------------------- def subscribe(self, subscribeReq): # type: (VtSubscribeReq)->None """订阅行情""" @@ -125,63 +108,64 @@ class OkexFutureGateway(VnpyGateway): #---------------------------------------------------------------------- def _getOrderByLocalId(self, localId): + """从本地Id获取对应的内部Order对象""" if localId in self._orders: return self._orders[localId] return None #---------------------------------------------------------------------- def _getOrderByRemoteId(self, remoteId): + """从Api的OrderId获取对应的内部Order对象""" if remoteId in self._remoteIds: return self._remoteIds[remoteId] return None #---------------------------------------------------------------------- def _saveRemoteId(self, remoteId, myorder): + """将remoteId和队友的""" myorder.remoteId = remoteId self._remoteIds[remoteId] = myorder #---------------------------------------------------------------------- - def _genereteLocalOrder(self, symbol, price, volume, direction, offset): + def _generateLocalOrder(self, symbol, price, volume, direction, offset): myorder = _Order() localId = myorder.localId self._orders[localId] = myorder myorder.vtOrder = VtOrderData.createFromGateway(self, - self.exchange, - localId, - symbol, - price, - volume, - direction, - offset) + self.exchange, + localId, + symbol, + price, + volume, + direction, + offset) return myorder - + #---------------------------------------------------------------------- def sendOrder(self, vtRequest): # type: (VtOrderReq)->str """发单""" - myorder = self._genereteLocalOrder(vtRequest.symbol, + myorder = self._generateLocalOrder(vtRequest.symbol, vtRequest.price, vtRequest.volume, vtRequest.direction, vtRequest.offset) - remoteSymbol, remoteContractType = localSymbolToRemote(vtRequest.symbol) - orderType = _orderTypeMap[(vtRequest.priceType, vtRequest.offset)] # 开多、开空、平多、平空 + orderType = _orderTypeMap[(vtRequest.direction, vtRequest.offset)] # 开多、开空、平多、平空 userMarketPrice = False if vtRequest.priceType == constant.PRICETYPE_MARKETPRICE: userMarketPrice = True - self.restApi.sendOrder(symbol=remoteSymbol, - contractType=remoteContractType, + self.restApi.sendOrder(symbol=vtRequest.symbol, orderType=orderType, volume=vtRequest.volume, price=vtRequest.price, - useMarketPrice=userMarketPrice, + matchPrice=userMarketPrice, leverRate=self.leverRate, onSuccess=self._onOrderSent, - extra=None, + onFailed=self._onSendOrderFailed, + extra=myorder, ) - return myorder.localId #---------------------------------------------------------------------- @@ -190,54 +174,39 @@ class OkexFutureGateway(VnpyGateway): myorder = self._getOrderByLocalId(vtCancel.orderID) assert myorder is not None, u"理论上是无法取消一个不存在的本地单的" - symbol, contractType = localSymbolToRemote(vtCancel.symbol) - self.restApi.cancelOrder(symbol=symbol, - contractType=contractType, - orderId=myorder.remoteId, + self.restApi.cancelOrder(vtCancel.symbol, + myorder.remoteId, onSuccess=self._onOrderCanceled, - extra=myorder, + extra=myorder ) - # cancelDict: 不存在的,没有localId就没有remoteId,没有remoteId何来cancel #---------------------------------------------------------------------- - def queryOrders(self, symbol, contractType, - status): # type: (str, str, OkexFutureOrderStatus)->None + def queryContracts(self): + self.restApi.queryContracts(onSuccess=self._onQueryContracts) + + #---------------------------------------------------------------------- + def queryOrders(self, symbol, status): # type: (str, OkexFuturesOrderStatus)->None """ :param symbol: - :param contractType: 这个参数可以传'THISWEEK', 'NEXTWEEK', 'QUARTER',也可以传OkexFutureContractType - :param status: OkexFutureOrderStatus + :param status: OkexFuturesOrderStatus :return: """ - if contractType in _contractTypeMap: - localContractType = contractType - remoteContractType = localContractTypeToRemote(localContractType) - else: - remoteContractType = contractType - localContractType = remoteContractTypeToLocal(remoteContractType) - self.restApi.queryOrders(symbol=symbol, - contractType=remoteContractType, status=status, onSuccess=self._onQueryOrders, - extra=localContractType) + ) #---------------------------------------------------------------------- def qryAccount(self): - self.restApi.queryUserInfo(onSuccess=self._onQueryAccount) + self.restApi.queryAccount(onSuccess=self._onQueryAccount) """查询账户资金""" pass #---------------------------------------------------------------------- def qryPosition(self): """查询持仓""" - for remoteSymbol in _remoteSymbols: - for localContractType, remoteContractType in _contractTypeMap.items(): - self.restApi.queryPosition(remoteSymbol, - remoteContractType, - onSuccess=self._onQueryPosition, - extra=localContractType - ) + self.restApi.queryPositions(onSuccess=self._onQueryPosition) #---------------------------------------------------------------------- def close(self): @@ -246,19 +215,40 @@ class OkexFutureGateway(VnpyGateway): self.webSocket.stop() #---------------------------------------------------------------------- - def _onOrderSent(self, remoteId, myorder): #type: (str, _Order)->None - myorder.remoteId = remoteId + def _onOrderSent(self, order, myorder): #type: (OkexFuturesOrderSentInfoV3, _Order)->None + myorder.remoteId = order.orderId myorder.vtOrder.status = constant.STATUS_NOTTRADED - self._saveRemoteId(remoteId, myorder) + self._saveRemoteId(myorder.remoteId, myorder) self.onOrder(myorder.vtOrder) #---------------------------------------------------------------------- @staticmethod - def _onOrderCanceled(myorder): #type: (_Order)->None + def _onSendOrderFailed(order, myorder): #type: (OkexFuturesOrderSentInfoV3, _Order)->None + myorder.vtOrder.status = constant.STATUS_REJECTED + + #---------------------------------------------------------------------- + @staticmethod + def _onOrderCanceled(myorder): # type: (_Order)->Any myorder.vtOrder.status = constant.STATUS_CANCELLED + + #---------------------------------------------------------------------- + def _onQueryContracts(self, contracts, extra): # type: (List[OkexFuturesContractsInfoV3], Any)->None + for contract in contracts: + vtContract = VtContractData.createFromGateway( + gateway=self, + exchange=self.exchange, + symbol=contract.symbol, + productClass=constant.PRODUCT_FUTURES, + priceTick=contract.tickSize, + size=contract.quoteIncrement, + name=contract.symbol, + expiryDate=contract.delivery, + underlyingSymbol=contract.underlyingIndex + ) + self.onContract(vtContract) #---------------------------------------------------------------------- - def _onQueryOrders(self, orders, extra): # type: (List[OkexFutureOrder], Any)->None + def _onQueryOrders(self, orders, extra): # type: (List[OkexFuturesOrderDetailV3], Any)->None localContractType = extra for order in orders: remoteId = order.remoteId @@ -277,52 +267,53 @@ class OkexFutureGateway(VnpyGateway): # 缓存该订单,并推送 symbol = remoteSymbolToLocal(order.symbol, localContractType) direction, offset = remoteOrderTypeToLocal(order.orderType) - myorder = self._genereteLocalOrder(symbol, order.price, order.volume, direction, offset) + myorder = self._generateLocalOrder(symbol, order.price, order.volume, direction, offset) myorder.vtOrder.tradedVolume = order.tradedVolume myorder.remoteId = order.remoteId self._saveRemoteId(myorder.remoteId, myorder) self.onOrder(myorder.vtOrder) - + #---------------------------------------------------------------------- - def _onQueryAccount(self, infos, _): # type: (List[OkexFutureUserInfo], Any)->None + def _onQueryAccount(self, infos, _): # type: (List[OkexFuturesAccountInfoV3], Any)->None for info in infos: vtAccount = VtAccountData() - vtAccount.accountID = info.easySymbol + vtAccount.accountID = info.currency vtAccount.vtAccountID = self.gatewayName + '.' + vtAccount.accountID - vtAccount.balance = info.accountRights - vtAccount.margin = info.keepDeposit - vtAccount.closeProfit = info.profitReal - vtAccount.positionProfit = info.profitUnreal + vtAccount.balance = info.balance + vtAccount.available = info.available + vtAccount.margin = info.hold # todo: is this right? self.onAccount(vtAccount) #---------------------------------------------------------------------- - def _onQueryPosition(self, posinfo, extra): # type: (OkexFuturePosition, Any)->None + def _onQueryPosition(self, posex, extra): # type: (List[OkexFuturesPositionInfoV3], Any)->None localContractType = extra - for info in posinfo.holding: - # 先生成多头持仓 - pos = VtPositionData.createFromGateway( + for pos in posex: + # 多头持仓 + posex = VtPositionData.createFromGateway( gateway=self, exchange=self.exchange, - symbol=remoteSymbolToLocal(info.symbol, localContractType), + symbol=remoteSymbolToLocal(pos.symbol, localContractType), direction=constant.DIRECTION_NET, - position=float(info.buyAmount), + position=float(pos.longQty), + price=pos.longAvgCost, ) - self.onPosition(pos) + self.onPosition(posex) - # 再生存空头持仓 - pos = VtPositionData.createFromGateway( + # 空头持仓 + posex = VtPositionData.createFromGateway( gateway=self, exchange=self.exchange, - symbol=remoteSymbolToLocal(info.symbol, localContractType), + symbol=remoteSymbolToLocal(pos.symbol, localContractType), direction=constant.DIRECTION_SHORT, - position=float(info.sellAmount), + position=float(pos.shortQty), + price=pos.shortAvgCost, ) - self.onPosition(pos) + self.onPosition(posex) #---------------------------------------------------------------------- - def _onTick(self, info): # type: (OkexFutureTickInfo)->None + def _onTick(self, info): # type: (OkexFuturesTickInfo)->None uiSymbol = remoteSymbolToLocal(info.symbol, remoteContractTypeToLocal(info.remoteContractType)) self.onTick(VtTickData.createFromGateway( gateway=self, @@ -337,7 +328,7 @@ class OkexFutureGateway(VnpyGateway): upperLimit=info.limitHigh, )) - def _onUserTrade(self, info): # type: (OkexFutureUserTradeInfo)->None + def _onUserTrade(self, info): # type: (OkexFuturesUserTradeInfo)->None tradeID = str(self.tradeID) self.tradeID += 1 order = self._getOrderByRemoteId(info.remoteId) @@ -380,7 +371,7 @@ def remoteContractTypeToLocal(remoteContractType): #---------------------------------------------------------------------- -def localSymbolToRemote(symbol): # type: (str)->(OkexFutureSymbol, OkexFutureContractType) +def localSymbolToRemote(symbol): # type: (str)->(OkexFuturesSymbol, OkexFuturesContractType) """ :return: remoteSymbol, remoteContractType """ @@ -393,24 +384,24 @@ def remoteSymbolToLocal(remoteSymbol, localContractType): _orderTypeMap = { - (constant.DIRECTION_LONG, constant.OFFSET_OPEN): OkexFutureOrderType.OpenLong, - (constant.DIRECTION_SHORT, constant.OFFSET_OPEN): OkexFutureOrderType.OpenShort, - (constant.DIRECTION_LONG, constant.OFFSET_CLOSE): OkexFutureOrderType.CloseLong, - (constant.DIRECTION_SHORT, constant.OFFSET_CLOSE): OkexFutureOrderType.CloseShort, + (constant.DIRECTION_LONG, constant.OFFSET_OPEN): OkexFuturesOrderType.OpenLong, + (constant.DIRECTION_SHORT, constant.OFFSET_OPEN): OkexFuturesOrderType.OpenShort, + (constant.DIRECTION_LONG, constant.OFFSET_CLOSE): OkexFuturesOrderType.CloseLong, + (constant.DIRECTION_SHORT, constant.OFFSET_CLOSE): OkexFuturesOrderType.CloseShort, } _orderTypeMapReverse = {v: k for k, v in _orderTypeMap.items()} _contractTypeMap = { - k.upper(): v for k, v in OkexFutureContractType.__dict__.items() if not k.startswith('_') + k.upper(): v for k, v in OkexFuturesContractType.__dict__.items() if not k.startswith('_') } _contractTypeMapReverse = {v: k for k, v in _contractTypeMap.items()} _easySymbols = { - v for k, v in OkexFutureEasySymbol.__dict__.items() if not k.startswith('_') + v for k, v in OkexFuturesEasySymbol.__dict__.items() if not k.startswith('_') } _remoteSymbols = { - v for k, v in OkexFutureSymbol.__dict__.items() if not k.startswith('_') + v for k, v in OkexFuturesSymbol.__dict__.items() if not k.startswith('_') } # symbols for ui,