vnpy/beta/gateway/okexFuturesGateway/okexFuturesGateway.py

908 lines
38 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# encoding: UTF-8
from __future__ import print_function
import json
import sys
import traceback
import zlib
from collections import defaultdict
from enum import Enum
from typing import Dict, List
from vnpy.api.rest import Request
from vnpy.trader.gateway.okexFuturesGateway.OkexFuturesBase import \
OkexFuturesRestBaseV3, \
OkexFuturesWebSocketBase
from vnpy.trader.vtFunction import getJsonPath
from vnpy.trader.vtGateway import *
########################################################################
class ApiError(Exception):
"""Okex的API常常变动当API发生奇葩的变动的时候会发升这个异常"""
pass
########################################################################
class OkexFuturesEasySymbol(object):
BTC = 'btc'
LTC = 'ltc'
ETH = 'eth'
ETC = 'etc'
BCH = 'bch'
EOS = 'eos'
XRP = 'xrp'
BTG = 'btg'
########################################################################
class OkexFuturesSymbol(object):
BTC = 'btc_usd'
LTC = 'ltc_usd'
ETH = 'eth_usd'
ETC = 'etc_usd'
BCH = 'bch_usd'
########################################################################
class OkexFuturesPriceType(object):
Buy = 'buy'
Sell = 'sell'
########################################################################
class OkexFuturesContractType(object):
ThisWeek = 'this_week'
NextWeek = 'next_week'
Quarter = 'quarter'
########################################################################
class OkexFuturesOrderType(object):
OpenLong = '1'
OpenShort = '2'
CloseLong = '3'
CloseShort = '4'
########################################################################
class OkexFuturesOrderStatus(object):
NotTraded = 0
PartialTraded = 1
Finished = 2
########################################################################
class Order(object):
_lastLocalId = 0
#----------------------------------------------------------------------
def __init__(self):
Order._lastLocalId += 1
self.localId = str(Order._lastLocalId)
self.remoteId = None
self.vtOrder = None # type: VtOrderData
########################################################################
class Symbol(object):
#----------------------------------------------------------------------
def __init__(self):
self.v3 = None # type: str # BTC_USD_1891201
self.ui = None # type: str # BTC_USD_NEXTWEEK
self.v1Symbol = None # type: str # btc_usd
self.uiSymbol = None # type: str # btc_usd
self.easySymbol = None # type: str # btc
self.localContractTYpe = None # type: str # THISWEEK
self.remoteContractType = None # type: str # this_week
########################################################################
class ChannelType(Enum):
Login = 1
ForecastPrice = 2
Tick = 3
Depth = 4
Trade = 5
Index = 6
UserTrade = 7
UserInfo = 8
Position = 9
Order = 10
########################################################################
class Channel(object):
#----------------------------------------------------------------------
def __init__(self, type_, symbol=None, remoteContractType=None, extra_=None):
self.type = type_
self.symbol = symbol
self.remoteContractType = remoteContractType
self.extra = extra_
########################################################################
class OkexFuturesGateway(VtGateway):
"""OKEX期货交易接口"""
#----------------------------------------------------------------------
def __init__(self, eventEngine, gatewayName='OKEXFUTURES'):
"""Constructor"""
super(OkexFuturesGateway, self).__init__(eventEngine, gatewayName)
self.exchange = constant.EXCHANGE_OKEXFUTURE
self.apiKey = None # type: str
self.apiSecret = None # type: str
self.apiPassphrase = None # type: str
self.restClient = OkexFuturesRestBaseV3()
self.webSocket = OkexFuturesWebSocketBase()
self.webSocket.onPacket = self.onWebSocketPacket
self.webSocket.unpackData = self.webSocketUnpackData
self.leverRate = 1
self.symbols = []
self._symbolDict = {} # type: Dict[str, Symbol]
self.tradeID = 0
self._orders = {} # type: Dict[str, Order]
self._remoteIds = {} # type: Dict[str, Order]
self._lastTicker = None # type: VtTickData
self._utcOffset = datetime.now() - datetime.utcnow()
#----------------------------------------------------------------------
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:
"""连接"""
# 载入json文件
try:
# todo: check by predefined settings names and types
# 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:
log = VtLogData()
log.gatewayName = self.gatewayName
log.logContent = u'连接配置缺少字段,请检查'
self.onLog(log)
return
#----------------------------------------------------------------------
def connect(self):
"""连接"""
self.loadSetting()
self.restClient.init(self.apiKey, self.apiSecret, self.apiPassphrase)
self.webSocket.init(self.apiKey, self.apiSecret, self.apiPassphrase)
self.restClient.start()
self.webSocket.start()
self.queryContracts()
#----------------------------------------------------------------------
def subscribe(self, subscribeReq): # type: (VtSubscribeReq)->None
"""订阅行情"""
s = self.parseSymbol(subscribeReq.symbol)
remoteSymbol = s.v1Symbol.lower()
remoteContractType = s.remoteContractType
# ticker
self.webSocket.sendPacket({
'event': 'addChannel',
'channel':
'ok_sub_futureusd_' + remoteSymbol.lower() + '_ticker_' + remoteContractType
})
# depth
self.webSocket.sendPacket({
'event': 'addChannel',
'channel':
'ok_sub_futureusd_' + remoteSymbol.lower() + '_depth' + remoteContractType + '_5'
})
#----------------------------------------------------------------------
def subscribeUserTrade(self):
self.webSocket.sendPacket({
'event': 'addChannel',
'channel': 'ok_sub_futureusd_trades'
})
#----------------------------------------------------------------------
def _writeError(self, msg): # type: (str)->None
e = VtErrorData()
e.gatewayName = self.gatewayName
e.errorMsg = msg
self.onError(e)
#----------------------------------------------------------------------
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 _generateLocalOrder(self, symbol, price, volume, direction, offset):
myorder = Order()
localId = myorder.localId
self._orders[localId] = myorder
myorder.vtOrder = VtOrderData.createFromGateway(self,
localId,
symbol,
self.exchange,
price,
volume,
direction,
offset)
return myorder
#----------------------------------------------------------------------
def parseSymbol(self, symbol):
return self._symbolDict[symbol]
#----------------------------------------------------------------------
def sendOrder(self, vtRequest): # type: (VtOrderReq)->str
"""发单"""
symbol = self.parseSymbol(vtRequest.symbol).v3
myorder = self._generateLocalOrder(symbol,
vtRequest.price,
vtRequest.volume,
vtRequest.direction,
vtRequest.offset)
orderType = _orderTypeMap[(vtRequest.direction, vtRequest.offset)] # 开多、开空、平多、平空
data = {
'client_oid': None,
'instrument_id': symbol,
'type': orderType,
'size': vtRequest.volume,
'leverage': self.leverRate,
}
if vtRequest.priceType == constant.PRICETYPE_MARKETPRICE:
data['match_price'] = '1'
else:
data['price'] = vtRequest.price
self.restClient.addRequest('POST', '/api/futures/v3/order',
callback=self._onOrderSent,
onFailed=self._onSendOrderFailed,
data=data,
extra=myorder
)
return myorder.vtOrder.vtOrderID
#----------------------------------------------------------------------
def cancelOrder(self, vtCancel): # type: (VtCancelOrderReq)->None
"""撤单"""
myorder = self._getOrderByLocalId(vtCancel.orderID)
assert myorder is not None, u"理论上是无法取消一个不存在的本地单的"
symbol = vtCancel.symbol
remoteId = myorder.remoteId
path = '/api/futures/v3/cancel_order/' + symbol + '/' + remoteId
self.restClient.addRequest('POST', path,
callback=self._onOrderCanceled,
onFailed=self._onCancelOrderFailed,
data={
'instrument_id': symbol,
'order_id': remoteId
},
extra=myorder
)
#----------------------------------------------------------------------
def queryContracts(self):
return self.restClient.addRequest('GET', '/api/futures/v3/instruments',
callback=self._onQueryContracts,
)
#----------------------------------------------------------------------
def queryOrders(self, symbol, status): # type: (str, OkexFuturesOrderStatus)->None
"""
:param symbol:
:param status: OkexFuturesOrderStatus
:return:
"""
symbol = self.parseSymbol(symbol).v3
path = '/api/futures/v3/orders/' + symbol
self.restClient.addRequest("POST", path,
data={
'status': status,
'instrument_id': symbol,
# 'from': 0,
# 'to': 2,
# 'limit': 100,
},
callback=self._onQueryOrders,
)
#----------------------------------------------------------------------
def qryAccount(self):
"""查询账户资金
Okex 的API变化太大不单独实现API了所有东西都封装在这里面
"""
return self.restClient.addRequest('GET', '/api/futures/v3/accounts',
callback=self._onQryAccounts,
)
#----------------------------------------------------------------------
def _onQryAccounts(self, data, _):
if 'info' not in data:
raise ApiError("unable to parse account data")
for easySymbol, detail in data['info'].items(): # type: str, dict
acc = VtAccountData()
acc.gatewayName = self.gatewayName
acc.accountID = easySymbol
acc.vtAccountID = acc.gatewayName + '.' + acc.accountID
acc.balance = detail.get('equity', 0)
acc.available = detail['total_avail_balance']
if 'contracts' in detail:
keys = {'available_qty': 'available_qty',
'fixed_balance': 'fixed_balance',
'margin_for_unfilled': 'margin',
'margin_frozen': 'margin',
'realized_pnl': 'realized_pnl',
'unrealized_pnl': 'unrealized_pnl'}
for v in keys.values():
detail[v] = 0.0
for c in detail['contracts']:
for k, v in keys.items():
detail[v] += float(c[k])
acc.margin = detail['margin']
acc.positionProfit = data.get('unrealized_pnl', 0)
acc.closeProfit = data.get('realized_pnl', 0)
self.onAccount(acc)
#----------------------------------------------------------------------
def qryPosition(self):
"""查询持仓"""
return self.restClient.addRequest('GET', '/api/futures/v3/position',
callback=self._onQueryPosition,
)
#----------------------------------------------------------------------
def close(self):
"""关闭"""
self.restClient.stop()
self.webSocket.stop()
#----------------------------------------------------------------------
def _onOrderSent(self, data, request): #type: (dict, Request)->None
"""下单回调"""
# errorCode = data['error_code'],
# errorMessage = data['error_message'],
myorder = request.extra # type: Order
remoteId = data['order_id']
if remoteId != '-1':
myorder.remoteId = remoteId
myorder.vtOrder.status = constant.STATUS_NOTTRADED
self._saveRemoteId(myorder.remoteId, myorder)
else:
myorder.vtOrder.status = constant.STATUS_REJECTED
self.onOrder(myorder.vtOrder)
#----------------------------------------------------------------------
def _onSendOrderFailed(self, _, request): # type:(int, Request)->None
myorder = request.extra # type: Order
myorder.vtOrder.status = constant.STATUS_REJECTED
self.onOrder(myorder.vtOrder)
#----------------------------------------------------------------------
def _onOrderCanceled(self, data, request): #type: (dict, Request)->None
myorder = request.extra # type: Order
result = data['result']
if result is True:
myorder.vtOrder.status = constant.STATUS_CANCELLED
self.onOrder(myorder.vtOrder)
else:
# todo: more detail about error
print('failed to cancel order: ' + json.dumps(data))
self._writeError('Failed to cancel order {}'.format(myorder.localId))
#----------------------------------------------------------------------
def _onCancelOrderFailed(self, _, request): # type:(int, Request)->None
myorder = request.extra # type: Order
self._writeError(u'Failed to cancel order {}'.format(myorder.localId))
#----------------------------------------------------------------------
def _onQueryContracts(self, data, _): #type: (dict, Request)->None
# 首先建立THISWEEK, NEXTWEEK, QUARTER和相应Contract的映射
symbols = set()
for contract in data:
symbol = contract['instrument_id']
symbols.add(symbol)
# 一般来说一个币种对有三种到期日期不同的symbol。
# 将这三种symbol按顺序排列就能依次得到ThisWeek, NextWeek和Quarter三种symbol
s2 = defaultdict(list)
for symbol in sorted(symbols):
easySymbol = symbol[:3]
s2[easySymbol].append(symbol)
# 按顺序取出排列好的symbols对应上ThisWeek, NextWeek和Quarter
# 然后记录下来他们的几种symbols形式和相应的一些常量
# v1Symbol: BTC_USD_THISWEEK
# v3Symbol: BTC_USD_181222
# easySymbol: btc, eth, ...
# remoteContractType: this_week, next_week, ...
# localContractType: THISWEEK, NEXTWEEK, ...
symbolDict = {}
for easySymbol, sortedSymbols in s2.items():
if len(sortedSymbols) == 3:
for contractType, v3symbol in zip(_contractTypeMap.keys(), sortedSymbols):
uiSymbol = '{}_USD_{}'.format(easySymbol, contractType) # ETC_USD_THISWEEK
remoteContractName = '{}{}'.format(easySymbol, v3symbol[-4:]) # ETC1201
s = Symbol()
s.v1Symbol = '{}_{}'.format(easySymbol.lower(), "usd")
s.v3 = v3symbol
s.easySymbol = easySymbol
s.remoteContractType = _contractTypeMap[contractType]
s.localContractTYpe = contractType
s.uiSymbol = uiSymbol
# normal map
symbolDict[uiSymbol.upper()] = s
symbolDict[uiSymbol.lower()] = s
symbolDict[uiSymbol] = s
symbolDict[v3symbol] = s
# switch between '-' and '_'
symbolDict[uiSymbol.upper().replace('_', '-')] = s
symbolDict[uiSymbol.lower().replace('_', '-')] = s
symbolDict[uiSymbol.replace('_', '-')] = s
symbolDict[v3symbol.replace('-', '_')] = s
# BTCUSD181228 BTCUSDTHISWEEK, btcusdthisweek
symbolDict[v3symbol.upper().replace('-', '')] = s
symbolDict[uiSymbol.upper().replace('_', '')] = s
symbolDict[uiSymbol.lower().replace('_', '')] = s
symbolDict[remoteContractName.upper()] = s
symbolDict[remoteContractName.lower()] = s
# unicode and str
for k, v in symbolDict.items():
self._symbolDict[str(k)] = v
self._symbolDict[unicode(k)] = v
# 其次响应onContract也是该函数的本职工作
for contract in data:
symbol = contract['instrument_id']
size = contract['quote_increment'] if 'quote_increment' in contract else contract[
'trade_increment'],
vtContract = VtContractData.createFromGateway(
gateway=self,
exchange=self.exchange,
symbol=symbol,
productClass=constant.PRODUCT_FUTURES,
priceTick=contract['tick_size'],
size=size,
name=symbol,
expiryDate=contract['delivery'],
underlyingSymbol=contract['underlying_index'],
)
self.onContract(vtContract)
# 最后订阅symbols还有查询其他东西
for symbol in self.symbols:
s = self.parseSymbol(symbol)
# noinspection PyTypeChecker
req = VtSubscribeReq()
req.symbol = s.v3
self.subscribe(req)
# 查询账户啊,持仓啊,委托单啊之类的东西
self.qryAccount()
self.qryPosition()
# 查询所有未成交的委托
# v3 API尚未支持该操作
# for symbol in symbols:
# # noinspection PyTypeChecker
# self.queryOrders(symbol, OkexFuturesOrderStatus.NotTraded)
#----------------------------------------------------------------------
def _onQueryOrders(self, data, _): #type: (dict, Request)->None
if data['result'] is True:
for info in data['orders']:
remoteId = info['order_id']
tradedVolume = info['filled_qty']
myorder = self._getOrderByRemoteId(remoteId)
if myorder:
# 如果订单已经缓存在本地,则尝试更新订单状态
# 有新交易才推送更新
if tradedVolume != myorder.vtOrder.tradedVolume:
myorder.vtOrder.tradedVolume = tradedVolume
myorder.vtOrder.status = constant.STATUS_PARTTRADED
self.onOrder(myorder.vtOrder)
else:
# 本地无此订单的缓存(例如,用其他工具的下单)
# 缓存该订单,并推送
symbol = info['instrument_id']
direction, offset = remoteOrderTypeToLocal(info['type'])
myorder = self._generateLocalOrder(symbol,
info['price'],
info['size'],
direction,
offset)
myorder.vtOrder.tradedVolume = tradedVolume
myorder.remoteId = remoteId
self._saveRemoteId(myorder.remoteId, myorder)
self.onOrder(myorder.vtOrder)
#----------------------------------------------------------------------
def _onQueryPosition(self, data, _): #type: (dict, Request)->None
if 'holding' in data:
posex = data['holding']
elif 'position' in data:
posex = data['position']
else:
raise ApiError("Failed to parse position data")
for pos in posex:
symbol = self.parseSymbol(pos['instrument_id']).uiSymbol
# 多头持仓
vtPos = VtPositionData.createFromGateway(
gateway=self,
exchange=self.exchange,
symbol=symbol,
direction=constant.DIRECTION_NET,
position=pos['long_qty'],
price=pos['long_avg_cost'],
)
self.onPosition(vtPos)
# 多头持仓
vtPos = VtPositionData.createFromGateway(
gateway=self,
exchange=self.exchange,
symbol=symbol,
direction=constant.DIRECTION_NET,
position=pos['short_qty'],
price=pos['short_avg_cost'],
)
self.onPosition(vtPos)
#----------------------------------------------------------------------
@staticmethod
def webSocketUnpackData(data):
"""重载websocket.unpackData"""
return json.loads(zlib.decompress(data, -zlib.MAX_WBITS))
#----------------------------------------------------------------------
def onWebSocketPacket(self, packets):
for packet in packets:
channelName = None
if 'channel' in packet:
channelName = packet['channel']
if not channelName or channelName == 'addChannel':
return
data = packet['data']
channel = parseChannel(channelName) # type: Channel
if not channel:
print("unknown websocket channel : ", json.dumps(packet, indent=2))
return
try:
if channel.type == ChannelType.Tick:
uiSymbol = remoteSymbolToLocal(channel.symbol,
remoteContractTypeToLocal(
channel.remoteContractType))
if self._lastTicker is None:
self._lastTicker = VtTickData.createFromGateway(
gateway=self,
symbol=uiSymbol,
exchange=self.exchange,
lastPrice=float(data['last']),
lastVolume=float(data['vol']),
highPrice=float(data['high']),
lowPrice=float(data['low']),
openInterest=float(data['hold_amount']),
lowerLimit=float(data['limitLow']),
upperLimit=float(data['limitHigh']),
)
else:
self._lastTicker.lastPrice = float(data['last'])
self._lastTicker.lastVolume = float(data['vol'])
self._lastTicker.highPrice = float(data['high'])
self._lastTicker.lowPrice = float(data['low'])
self._lastTicker.openInterest = float(data['hold_amount'])
self._lastTicker.lowerLimit = float(data['limitLow'])
self._lastTicker.upperLimit = float(data['limitHigh'])
self._lastTicker.datetime = datetime.now()
self._lastTicker.date = self._lastTicker.datetime.strftime('%Y%m%d')
self._lastTicker.time = self._lastTicker.datetime.strftime('%H:%M:%S')
self.onTick(self._lastTicker)
elif channel.type == ChannelType.Depth:
asks = data['asks']
bids = data['bids']
if self._lastTicker is not None:
timestamp = float(data['timestamp'])
ts = datetime.utcfromtimestamp(timestamp/1000) + self._utcOffset
self._lastTicker.askPrice1 = asks[0][0]
self._lastTicker.askPrice2 = asks[1][0]
self._lastTicker.askPrice3 = asks[2][0]
self._lastTicker.askPrice4 = asks[3][0]
self._lastTicker.askPrice5 = asks[4][0]
self._lastTicker.askVolume1 = asks[0][1]
self._lastTicker.askVolume2 = asks[1][1]
self._lastTicker.askVolume3 = asks[2][1]
self._lastTicker.askVolume4 = asks[3][1]
self._lastTicker.askVolume5 = asks[4][1]
self._lastTicker.bidPrice1 = bids[0][0]
self._lastTicker.bidPrice2 = bids[1][0]
self._lastTicker.bidPrice3 = bids[2][0]
self._lastTicker.bidPrice4 = bids[3][0]
self._lastTicker.bidPrice5 = bids[4][0]
self._lastTicker.bidVolume1 = bids[0][1]
self._lastTicker.bidVolume2 = bids[1][1]
self._lastTicker.bidVolume3 = bids[2][1]
self._lastTicker.bidVolume4 = bids[3][1]
self._lastTicker.bidVolume5 = bids[4][1]
self._lastTicker.datetime = ts
self._lastTicker.date = self._lastTicker.datetime.strftime('%Y%m%d')
self._lastTicker.time = self._lastTicker.datetime.strftime('%H:%M:%S')
self.onTick(self._lastTicker)
elif channel.type == ChannelType.Position:
symbol = data['symbol']
positions = data['positions']
for pos in positions:
if pos['position'] == '1':
direction = constant.DIRECTION_LONG
else:
direction = constant.DIRECTION_SHORT
total = pos['hold_amount']
usable = pos['eveningup']
# margin = _tryGetValue(pos, 'margin', 'fixmargin')
profit = _tryGetValue(pos, 'profitreal', 'realized')
symbol = self.parseSymbol(pos['contract_name']).uiSymbol
self.onPosition(VtPositionData.createFromGateway(
gateway=self,
exchange=self.exchange,
symbol=symbol,
direction=direction,
position=total,
frozen=total - usable,
price=pos['avgprice'],
profit=profit,
))
elif channel.type == ChannelType.UserInfo:
# ws 的acc没有分货币没法用
pass
elif channel.type == ChannelType.UserTrade:
tradeID = str(self.tradeID)
self.tradeID += 1
order = self._getOrderByRemoteId(data['orderid'])
if order:
self.onTrade(VtTradeData.createFromOrderData(
order=order.vtOrder,
tradeID=tradeID,
tradePrice=data['price'],
# todo: 这里应该填写的到底是order总共成交了的数量还是该次trade成交的数量
tradeVolume=data['deal_amount'],
))
else:
# todo: 与order无关联的trade该如何处理
# uiSymbol = remoteSymbolToLocal(info.symbol,
# remoteContractTypeToLocal(info.remoteContractType))
pass
except KeyError:
print("WebSocket error: parsing {}:\n{}".format(channelName, data))
traceback.print_exception(*sys.exc_info())
#----------------------------------------------------------------------
# noinspection PyUnusedLocal
def onApiError(self, exceptionType, exceptionValue, tb,
request=None # type: Request
):
msg = traceback.format_exception(exceptionType, exceptionValue, tb)
self._writeError(msg)
#----------------------------------------------------------------------
def onApiFailed(self, _, request): # type:(int, Request)->None
self._writeError(str(request))
pass
#----------------------------------------------------------------------
def setQryEnabled(self, _):
"""dummy function"""
pass
#----------------------------------------------------------------------
def localOrderTypeToRemote(direction, offset): # type: (str, str)->str
return _orderTypeMap[(direction, offset)]
#----------------------------------------------------------------------
def remoteOrderTypeToLocal(orderType): # type: (str)->(str, str)
"""
:param orderType:
:return: direction, offset
"""
return _orderTypeMapReverse[orderType]
#----------------------------------------------------------------------
def localContractTypeToRemote(localContractType):
return _contractTypeMap[localContractType]
#----------------------------------------------------------------------
def remoteContractTypeToLocal(remoteContractType):
return _contractTypeMapReverse[remoteContractType]
#----------------------------------------------------------------------
def localSymbolToRemote(symbol): # type: (str)->(OkexFuturesSymbol, OkexFuturesContractType)
"""
:return: remoteSymbol, remoteContractType
"""
return _symbolsForUi[symbol]
#----------------------------------------------------------------------
def remoteSymbolToLocal(remoteSymbol, localContractType):
return remoteSymbol.upper() + '_' + localContractType
#----------------------------------------------------------------------
def remotePrefixToRemoteContractType(prefix):
return _prefixForRemoteContractType[prefix]
#----------------------------------------------------------------------
def parseChannel(channel): # type: (str)->Channel
if channel == 'login':
return Channel(ChannelType.Login)
# 还未提供订阅的channel都注释掉
# elif channel[4:12] == 'forecast': # eg: 'btc_forecast_price'
# return SymbolChannel(ChannelType.ForecastPrice, channel[:3])
sp = channel.split('_')
if sp[-1] == 'trades': # eg: 'ok_sub_futureusd_trades'
return Channel(ChannelType.UserTrade)
# if sp[-1] == 'userinfo': # eg: 'ok_sub_futureusd_btc_userinfo'
# return Channel(ChannelType.UserInfo)
# if sp[-1] == 'index': # eg: 'ok_sub_futureusd_btc_index'
# return SymbolChannel(ChannelType.Index, channel[17:20])
if sp[-1] == 'positions': # eg: 'ok_sub_futureusd_positions'
return Channel(ChannelType.Position)
if sp[-1] == 'userinfo': # eg: 'ok_sub_futureusd_positions'
return Channel(ChannelType.UserInfo)
lsp = len(sp)
if sp[-1] == 'quarter':
if lsp == 7:
_, _, _, easySymbol, crash, typeName, contractTypePrefix = sp
return Channel(ChannelType.Tick,
easySymbol + '_' + crash,
remotePrefixToRemoteContractType(contractTypePrefix))
elif sp[-1] == 'week':
if lsp == 8:
_, _, _, easySymbol, crash, typeName, contractTypePrefix, _ = sp
return Channel(ChannelType.Tick,
easySymbol + '_' + crash,
remotePrefixToRemoteContractType(contractTypePrefix))
if sp[-1] == '5':
if lsp == 7: # eg "ok_sub_futureusd_eth_usd_depthquarter_5"
_, _, _, easySymbol, crash, typeName_contractTypePrefix, depth = sp
return Channel(ChannelType.Depth, easySymbol + '_' + crash,
remotePrefixToRemoteContractType(typeName_contractTypePrefix[5:]),
depth)
if lsp == 8: # eg "ok_sub_futureusd_eth_usd_depthnext_week_5"
_, _, _, easySymbol, crash, typeName_contractTypePrefix, _, depth = sp
return Channel(ChannelType.Depth, easySymbol + '_' + crash,
remotePrefixToRemoteContractType(typeName_contractTypePrefix[5:]),
depth)
#----------------------------------------------------------------------
def _tryGetValue(dict, *keys):
"""尝试从字典中获取某些键中的一个"""
for k in keys:
if k in dict:
return dict[k]
return None
_prefixForRemoteContractType = {v.split('_')[0]: v for k, v in
OkexFuturesContractType.__dict__.items() if
not k.startswith('_')}
_orderTypeMap = {
(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 OkexFuturesContractType.__dict__.items() if not k.startswith('_')
}
_contractTypeMapReverse = {v: k for k, v in _contractTypeMap.items()}
_easySymbols = {
v for k, v in OkexFuturesEasySymbol.__dict__.items() if not k.startswith('_')
}
_remoteSymbols = {
v for k, v in OkexFuturesSymbol.__dict__.items() if not k.startswith('_')
}
# symbols for ui,
# keys:给用户看的symbols : f"{internalSymbol}_{contractType}"
# values: API接口使用的symbol和contractType字段
_symbolsForUi = {(remoteSymbol.upper() + '_' + upperContractType.upper())
: (remoteSymbol, remoteContractType)
for remoteSymbol in _remoteSymbols
for upperContractType, remoteContractType in
_contractTypeMap.items()
} # type: Dict[str, List[str, str]]
_symbolsForUiReverse = {v: k for k, v in _symbolsForUi.items()}
_channel_for_subscribe = {
'ok_sub_futureusd_' + easySymbol + '_ticker_' + remoteContractType
: (easySymbol, remoteContractType)
for easySymbol in _easySymbols
for remoteContractType in _contractTypeMap.values()
}