[Add] 将OkexFutureRestClient做成独立的API分离出来

This commit is contained in:
nanoric 2018-10-10 22:00:36 -04:00
parent b34dcf6a73
commit f5755b222a
3 changed files with 349 additions and 259 deletions

View File

@ -0,0 +1,337 @@
# encoding: UTF-8
from enum import Enum
from typing import Any, Callable, List, Union
from vnpy.api.okexfuture.vnokexFuture import OkexFutureRestBase
from vnpy.network.RestClient import Request
class _OkexFutureCustomExtra(object):
def __init__(self, onSuccess, onFailed, extra):
self.onFailed = onFailed
self.onSuccess = onSuccess
self.extra = extra
class OkexFuturePriceType(Enum):
Buy = 'buy'
Sell = 'sell'
class OkexFutureContractType(Enum):
ThisWeek = 'this_week'
NextWeek = 'next_week'
Quarter = 'quarter'
class OkexFutureStatus(Enum):
NoTraded = '0'
PartialTraded = '1'
AllTraded = '2'
Canceled = '-1'
CancelProcessing = '4'
Canceling = '5'
class OkexFutureOrderType(Enum):
OpenLong = '1'
OpenShort = '2'
CloseLong = '3'
CloseShort = '4'
class OkexFutureOrder(object):
def __init__(self):
self.symbol = None
self.volume = None
self.price = None
self.priceAvg = None
self.status = None
self.orderType = None
self.unitAmount = None
self.leverRate = None
self.remoteId = None
self.fee = None
self.tradedVolume = None
self.createDate = None
class OkexFutureUserInfo(object):
def __init__(self):
self.accountRights = None
self.keepDeposit = None
self.profitReal = None
self.profitUnreal = None
self.riskRate = None
class OkexFuturePosition(object):
def __init__(self, ):
self.forceLiquidatePrice = None
self.holding = [] # type: List[OkexFuturePositionDetail]
class OkexFuturePositionDetail(object):
def __init__(self, ):
self.buyAmount = None
self.buyAvailable = None
self.buyPriceAvg = None
self.buyPriceCost = None
self.buyProfitReal = None
self.contractId = None
self.createDate = None
self.leverRate = None
self.sellAmount = None
self.sellAvailable = None
self.sellPriceAvg = None
self.sellPriceCost = None
self.sellProfitReal = None
self.symbol = None
self.contractType = None
########################################################################
class OkexFutureRestClient(OkexFutureRestBase):
#----------------------------------------------------------------------
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)
#----------------------------------------------------------------------
def sendOrder(self, symbol, contractType, orderType, volume,
onSuccess, onFailed=None,
price=None, useMarketPrice=False, leverRate=None,
extra=None): # type:(str, OkexFutureContractType, OkexFutureOrderType, float, Callable[[int, Any], Any], Callable[[object], Any], float, bool, Union[int, None], Any)->Request
"""
:param symbol: str
:param contractType: OkexFutureContractType
:param orderType: OkexFutureOrderType
:param volume: float
:param onSuccess: (orderId: int)->Any
:param onFailed: ()->Any
:param price: float
:param useMarketPrice: bool
:param leverRate: int | None
:param extra: Any
:return:
"""
data = {}
if useMarketPrice:
data['match_price'] = 1
else:
data['price'] = price
data.update({
'symbol': symbol,
'contract_typ': contractType, # 合约类型:当周/下周/季度
'amount': volume,
'type': orderType,
'lever_rate': leverRate # 杠杆倍数
})
request = self.addReq('POST',
'/future_trade.do',
callback=self.onOrderSent,
data=data,
extra=_OkexFutureCustomExtra(onSuccess, onFailed, extra))
return request
#----------------------------------------------------------------------
def cancelOrder(self, symbol, contractType, orderId, onSuccess, onFailed=None,
extra=None): # type: (str, OkexFutureContractType, str, Callable[[object], Any], Callable[[object], Any], Any)->Request
"""
:param symbol: str
:param contractType: OkexFutureContractType
:param orderId: str
:param onSuccess: ()->Any
:param onFailed: ()->Any
:param extra: Any
:return: Request
"""
data = {
'symbol': symbol,
'contractType': contractType,
'order_id': orderId
}
return self.addReq('POST',
'/future_cancel.do',
callback=self.onOrderCanceled,
data=data,
extra=_OkexFutureCustomExtra(onSuccess, onFailed, extra))
#----------------------------------------------------------------------
def queryOrder(self, symbol, contractType, orderId, onSuccess, onFailed=None,
extra=None): # type: (str, OkexFutureContractType, str, Callable[[OkexFutureOrder, Any], Any], Callable[[Any], Any], Any)->Request
"""
:param symbol: str
:param contractType: OkexFutureContractType
:param orderId: str
:param onSuccess: (OkexFutureOrder, extra:Any)->Any
:param onFailed: (extra: Any)->Any
:param extra: Any
:return: Request
"""
data = {
'symbol': symbol,
'contractType': contractType,
'order_id': orderId
}
return self.addReq('POST',
'/future_order_info.do',
callback=self.onOrder,
data=data,
extra=_OkexFutureCustomExtra(onSuccess, onFailed, extra))
#----------------------------------------------------------------------
def queryUserInfo(self, onSuccess, onFailed=None,
extra=None): # type: (Callable[[List[OkexFutureUserInfo], Any], Any], Callable[[Any], Any], Any)->Request
"""
查询用户信息
:param onSuccess: (userInfos: List[OkexFutureUserInfo], extra: Any)->Any
:param onFailed: (extra: Any)->Any
:param extra: Any
:return: Request
"""
return self.addReq('POST',
'/future_userinfo.do',
callback=self.onOrder,
extra=_OkexFutureCustomExtra(onSuccess, onFailed, extra))
#----------------------------------------------------------------------
def queryPosition(self, symbol, contractType,
onSuccess, onFailed=None,
extra=None): # type: (str, OkexFutureContractType, Callable[[OkexFuturePosition, Any], Any], Callable[[Any], Any], Any)->Request
data = {
'symbol': symbol,
'contractType': contractType
}
return self.addReq('POST',
'/future_position.do',
data=data,
callback=self.onPosition,
extra=_OkexFutureCustomExtra(onSuccess, onFailed, extra))
#----------------------------------------------------------------------
@staticmethod
def onOrderSent(data, req): # type: (dict, Request)->None
"""
下单回执一般用来保存sysId
"""
extra = req.extra # type: _OkexFutureCustomExtra
if data['result'] is True:
remoteId = data['order_id']
extra.onSuccess(remoteId, extra.extra)
else:
extra.onFailed(extra.extra)
#----------------------------------------------------------------------
@staticmethod
def onOrderCanceled(data, req): # type: (dict, Request)->None
"""
取消订单回执
"""
success = data['result']
extra = req.extra # type: _OkexFutureCustomExtra
if success:
extra.onSuccess(extra.extra)
else:
extra.onFailed(extra.extra)
#----------------------------------------------------------------------
@staticmethod
def onOrder(data, req): # type: (dict, Request)->None
success = data['result']
extra = req.extra # type: _OkexFutureCustomExtra
if success:
order = data['orders'][0]
okexOrder = OkexFutureOrder()
okexOrder.symbol = order['symbol']
okexOrder.volume = order['amount']
okexOrder.price = order['price']
okexOrder.priceAvg = order['price_avg']
okexOrder.status = order['status']
okexOrder.orderType = order['type']
okexOrder.unitAmount = order['unit_amount']
okexOrder.leverRate = order['lever_rate']
okexOrder.remoteId = order['order_id']
okexOrder.fee = order['fee']
okexOrder.tradedVolume = order['deal_amount']
okexOrder.createDate = order['create_date']
extra.onSuccess(okexOrder, extra.extra)
else:
extra.onFailed(extra.extra)
#----------------------------------------------------------------------
@staticmethod
def onUserInfo(data, req): # type: (dict, Request)->None
success = data['result']
extra = req.extra # type: _OkexFutureCustomExtra
if success:
infos = data['info']
uis = []
for symbol, info in infos.items(): # type: str, dict
ui = OkexFutureUserInfo()
ui.accountRights = info['account_rights']
ui.keepDeposit = info['keep_deposit']
ui.profitReal = info['profit_real']
ui.profitUnreal = info['profit_unreal']
ui.riskRate = info['risk_rate']
uis.append(ui)
extra.onSuccess(uis, extra.extra)
else:
extra.onFailed(extra.extra)
#----------------------------------------------------------------------
@staticmethod
def onPosition(data, req): # type: (dict, Request)->None
success = data['result']
extra = req.extra # type: _OkexFutureCustomExtra
if success:
pos = OkexFuturePosition()
pos.forceLiquidatePrice = data['force_liqu_price']
for item in data['holding']:
posDetail = OkexFuturePositionDetail()
posDetail.buyAmount = item['buy_amount']
posDetail.buyAvailable = item['buy_available']
posDetail.buyPriceAvg = item['buy_price_avg']
posDetail.buyPriceCost = item['buy_price_cost']
posDetail.buyProfitReal = item['buy_profit_real']
posDetail.contractId = item['contract_id']
posDetail.contractType = item['contract_type']
posDetail.createDate = item['create_date']
posDetail.leverRate = item['lever_rate']
posDetail.sellAmount = item['sell_amount']
posDetail.sellAvailable = item['sell_available']
posDetail.sellPriceAvg = item['sell_price_avg']
posDetail.sellPriceCost = item['sell_price_cost']
posDetail.sellProfitReal = item['sell_profit_real']
posDetail.symbol = item['symbol']
pos.holding.append(posDetail)
extra.onSuccess(pos, extra.extra)
else:
extra.onFailed(extra.extra)

View File

@ -8,11 +8,11 @@ from vnpy.network.RestClient import RestClient, Request
########################################################################
class OkexFutureRestClient(RestClient):
class OkexFutureRestBase(RestClient):
#----------------------------------------------------------------------
def __init__(self):
super(OkexFutureRestClient, self).__init__()
super(OkexFutureRestBase, self).__init__()
self.apiKey = None
self.apiSecret = None
@ -20,7 +20,7 @@ class OkexFutureRestClient(RestClient):
# noinspection PyMethodOverriding
def init(self, apiKey, apiSecret):
# type: (str, str) -> any
super(OkexFutureRestClient, self).init('https://www.okex.com/api/v1')
super(OkexFutureRestBase, self).init('https://www.okex.com/api/v1')
self.apiKey = apiKey
self.apiSecret = apiSecret

View File

@ -5,16 +5,15 @@ from __future__ import print_function
import json
from abc import abstractmethod, abstractproperty
from vnpy.api.okexfuture.vnokexFuture import OkexFutureRestClient
from vnpy.network.RestClient import Request
from vnpy.api.okexfuture.OkexFutureApi import *
from vnpy.trader.vtFunction import getJsonPath
from vnpy.trader.vtGateway import *
orderTypeMap = {
(constant.DIRECTION_LONG, constant.OFFSET_OPEN): 1,
(constant.DIRECTION_SHORT, constant.OFFSET_OPEN): 2,
(constant.DIRECTION_LONG, constant.OFFSET_CLOSE): 3,
(constant.DIRECTION_SHORT, constant.OFFSET_CLOSE): 4,
(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,
}
orderTypeMapReverse = {v: k for k, v in orderTypeMap.items()}
@ -23,9 +22,9 @@ contracts = (
)
contractTypeMap = {
'THISWEEK': 'this_week',
'NEXTWEEK': 'next_week',
'QUARTER': 'quarter',
'THISWEEK': OkexFutureContractType.ThisWeek,
'NEXTWEEK': OkexFutureContractType.NextWeek,
'QUARTER': OkexFutureContractType.Quarter,
}
# symbols for ui,
@ -98,7 +97,7 @@ class OkexFutureGateway(VnpyGateway):
super(OkexFutureGateway, self).__init__(eventEngine)
self.apiKey = None # type: str
self.apiSecret = None # type: str
self.api = OkexFutureApi(self)
self.api = OkexFutureRestClient()
self.leverRate = 1.0
self.symbols = []
@ -166,249 +165,3 @@ class OkexFutureGateway(VnpyGateway):
def close(self):
"""关闭"""
self.api.close()
########################################################################
class VnpyOrder():
def __init__(self):
"""
这个东西将VtOrderReq和VtOrderData还有Request三者绑定起来以便查询
"""
self.vtRequest = None # type: VtOrderReq # 如果这个order是通过sendOrder产生的则会有对应的vtRequest
self.request = None # type: Request # 如果这个order是通过sendOrder产生的request就是对应的网络请求
self.order = None # type: VtOrderData # 对应的VtOrderData
self.remoteId = None # type: str # 当确定了这个order在交易所内部的id的时候这个值才会有效
########################################################################
class ApiBase(object):
"""
写在前面现在不打算重构MainEngine才会有这个类的存在
所以这个类注定活不久
每个Api实现发单等等操作的时候有太多重复代码
于是我写了这个类以期简化Api的实现
发单实现
重写_sendOrder函数
在_sendOrder的下单请求回执中获取API使用的orderId并调用_processOrderSent函数即可
例如
def _sendOrder(self, vtRequest):
return self.httpClient.addReq(..., callback=self._onOrderSent)
def _onOrderSent(self, data, req):
remoteId = None
if data['success'] is True:
remoteId = data['order_id']
self._processOrderSent(req, remoteId)
撤单实现
重写_cancelOrder函数
"""
#----------------------------------------------------------------------
def __init__(self, gateway):
self.gateway = gateway # type: VnpyGateway
self._lastOrderId = 0
self._orders = {} # type: dict[str, VnpyOrder]
self._cancelDict = {} # type: dict[str, VtCancelOrderReq]
#----------------------------------------------------------------------
# todo: push this / make this standalone
def generateVnpyOrder(self):
localId = str(self._lastOrderId)
self._lastOrderId += 1
order = VnpyOrder()
order.order = VtOrderData()
order.order.orderID = localId
order.order.vtOrderID = ".".join(self.gateway.gatewayName, localId)
order.order.exchange = self.gateway.exchange
self._orders[localId] = order
return order
#----------------------------------------------------------------------
@staticmethod
def fillVnpyOrder(order, symbol, price, totalVolume,
direction): # type: (VnpyOrder, str, float, float, str)->None
order.order.symbol = symbol
order.order.vtSymbol = '.'.join([order.order.symbol, order.order.exchange])
order.order.price = price
order.order.totalVolume = totalVolume
order.order.direction = direction
#----------------------------------------------------------------------
def local2remote(self, localId):
"""将localIdorderId转化为remoteId"""
return self._orders[localId]
#----------------------------------------------------------------------
def getOrder(self, localId=None, remoteId=None):
"""通过localId或者remoteId获取order"""
if remoteId:
raise NotImplementedError()
return self._orders[localId]
#----------------------------------------------------------------------
def sendOrder(self, vtRequest): # type: (VtOrderReq)->str
"""发单"""
# 内部状态相关
order = self.generateVnpyOrder()
self.fillVnpyOrder(order,
vtRequest.symbol,
vtRequest.price,
vtRequest.volume,
vtRequest.direction)
order.vtRequest = vtRequest
# 发送发单请求
order.request = self._sendOrder(vtRequest)
# 增加反向引用
# 这个写法在逻辑上有漏洞当请求返回特别快理论上可能的时候返回回调中的extra仍为空
# 但是这种情况几乎不可能出现在Python中就更不可能会出现。所以就这样写把美观一些
order.request.extra = order
return order.order.vtOrderID
#----------------------------------------------------------------------
def cancelOrder(self, vtCancelRequest): # type: (VtCancelOrderReq)->Request
localId = vtCancelRequest.orderID
order = self.getOrder(localId)
if order.remoteId:
return self._cancelOrder(vtCancelRequest)
else:
self._cancelDict[localId] = vtCancelRequest
#----------------------------------------------------------------------
def _processOrderSent(self, request, remoteId):
"""
如果在_sendOrder中发送了HTTP请求则应该在收到响应的时候调用该函数
并且将remoteId设置为交易所API使用的ID
如果该下单请求失败将remoteId设为None即可
"""
order = request.extra # type: VnpyOrder
localId = order.order.orderID
if remoteId:
order.remoteId = remoteId # None就是失败或者未返回
self.gateway.onOrder(order.order)
order.order.status = constant.STATUS_NOTTRADED
# todo: 撤单委托相关
if localId in self._cancelDict:
req = self._cancelDict[localId]
self.cancelOrder(req)
del self._cancelDict[localId]
#----------------------------------------------------------------------
def _processOrderCancel(self, req, success):
"""
如果在_cancelOrder中发送了HTTP请求则应该在收到响应的时候调用该函数
如果取消订单成功应该将success设置为True如果取消订单失败则应该讲success设置为False
"""
order = req.extra # type: VnpyOrder
order.order.status = constant.STATUS_CANCELLED
pass
#----------------------------------------------------------------------
@abstractmethod
def _sendOrder(self, vtRequest): # type: (VtOrderReq)->Request
"""
这个函数实现下单请求
:return: 如果发送了HTTP请求就应该返回addReq的值
"""
pass
#----------------------------------------------------------------------
@abstractmethod
def _cancelOrder(self, vtCancelRequest): # type: (VtCancelOrderReq)->Request
"""
这个函数实现下单请求
:return: 如果发送了HTTP请求就应该返回addReq的值
"""
pass
########################################################################
class OkexFutureApi(ApiBase):
"""OKEX的API实现"""
#----------------------------------------------------------------------
def __init__(self, gateway):
"""Constructor"""
super(OkexFutureApi, self).__init__()
self.gateway = gateway # type: OkexFutureGateway
self.localID = 0
self.client = OkexFutureRestClient()
#----------------------------------------------------------------------
def onOrderSent(self, data, req): # type: (dict, Request)->None
"""
下单回执一般用来保存sysId
"""
remoteId = None
if data['result'] is True:
remoteId = data['order_id']
super(OkexFutureApi, self)._processOrderSent(req, remoteId)
#----------------------------------------------------------------------
def onOrderCanceled(self, data, req): # type: (dict, Request)->None
"""
取消订单回执
:param data:
:param req:
:return:
"""
success = data['result']
super(OkexFutureApi, self)._processOrderCancel(req, success)
#----------------------------------------------------------------------
def _cancelOrder(self, vtCancelRequest): # type: (VtCancelOrderReq)->Request
localId = vtCancelRequest.orderID
order = self.getOrder(localId)
data = {'symbol': order.order.symbol}
return self.client.addReq('POST',
'/future_cancel.do',
callback=self.onOrderCanceled,
data=data
)
#----------------------------------------------------------------------
def _sendOrder(self, vtRequest): # type: (VtOrderReq)->Request
"""
单纯的发单
"""
symbol, contractType = symbolsForUi[vtRequest.symbol]
orderType = orderTypeMap[(vtRequest.priceType, vtRequest.offset)] # 开多、开空、平多、平空
data = {}
if vtRequest.priceType == constant.PRICETYPE_MARKETPRICE:
data['match_price'] = 1
else:
data['price'] = vtRequest.price
data.update({
'symbol': symbol,
'contract_typ': contractType, # 合约类型:当周/下周/季度
'amount': vtRequest.volume,
'type': orderType,
'lever_rate': self.gateway.leverRate # 杠杆倍数
})
request = self.client.addReq('POST',
'/future_trade.do',
callback=self.onOrderSent,
data=data)
return request