[Add] okexFuture相关修改。

我决定了,每一点小修改都commit,无论是否能运行。所以我把修改都提交上来了。
This commit is contained in:
nanoric 2018-10-09 06:07:55 -04:00
parent 54b93a89b5
commit c04850c9a0
7 changed files with 422 additions and 0 deletions

View File

View File

@ -0,0 +1,42 @@
# encoding: UTF-8
import hashlib
import urllib
########################################################################
from vnpy.network.HttpClient import HttpClient
########################################################################
class OkexFutureHttpClient(HttpClient):
#----------------------------------------------------------------------
def __init__(self):
super(OkexFutureHttpClient, self).__init__()
self.apiKey = None
self.apiSecret = None
#----------------------------------------------------------------------
# noinspection PyMethodOverriding
def init(self, apiKey, apiSecret):
# type: (str, str) -> any
super(OkexFutureHttpClient, self).init('https://www.okex.com/api/v1')
self.apiKey = apiKey
self.apiSecret = apiSecret
#----------------------------------------------------------------------
def beforeRequest(self, method, path, params, data): # type: (str, str, dict, dict)->(str, str, dict, dict, dict)
args = params or {}
args.update(data or {})
if 'sign' in args:
args.pop('sign')
if 'apiKey' not in args:
args['api_key'] = self.apiKey
data = urllib.urlencode(sorted(args.items()))
data += "&secret_key=" + self.apiSecret
sign = hashlib.md5(data.encode()).hexdigest().upper()
data += "&sign=" + sign
return method, path, params, data, {'Content-Type': 'application/x-www-form-urlencoded'}

View File

@ -0,0 +1,38 @@
# encoding: UTF-8
########################################################################
from abc import abstractmethod
from vnpy.network.HttpClient import HttpClient
########################################################################
class HeadersAuthenticateHttpClient(HttpClient):
"""
该类简化了RESTFulAPI客户端的重载
该类提供了一个setUser函数可以方便地设置apiKey和apiSecret
使用self.apiKey和self.apiSecret便可以访问设置后的值
要建立一个签名在HTTP Headers的RESTFul客户端继承该类并重载authencitate即可
"""
#----------------------------------------------------------------------
def __init__(self):
super(HeadersAuthenticateHttpClient, self).__init__()
self.apiKey = None # type: str
self.apiSecret = None # type: str
#----------------------------------------------------------------------
def beforeRequest(self, method, path, params, data): # type: (str, str, dict, dict)->(str, str, dict, dict, dict)
return method, path, params, data, self.onAuthenticate(method, path, params, data)
#----------------------------------------------------------------------
@abstractmethod
def onAuthenticate(self, method, path, params, data):
"""
重载该函数以添加签名到头部
该函数在每个请求之前都会被调用
@:return dict 返回的数据会被加入到HTTP请求头部中
"""
return {}

View File

@ -0,0 +1,340 @@
# encoding: UTF-8
from __future__ import print_function
import json
from abc import abstractmethod, abstractproperty
from vnpy.api.okexfuture.vnokexFuture import OkexFutureHttpClient
from vnpy.network.HttpClient import Request
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,
}
orderTypeMapReverse = {v: k for k, v in orderTypeMap.items()}
contracts = (
'btc_usd', 'ltc_usd', 'eth_usd', 'etc_usd', 'bch_usd',
)
contractTypeMap = {
'THISWEEK': 'this_week',
'NEXTWEEK': 'next_week',
'QUARTER': 'quarter',
}
# symbols for ui,
# keys:给用户看的symbols
# values: API接口使用的symbol和contractType字段
symbolsForUi = {} # type: dict[str, [str, str]]
for s in contracts:
for vtContractType, contractType_ in contractTypeMap.items():
vtSymbol = s + '_' + vtContractType
symbolsForUi[vtSymbol] = (s, contractType_)
########################################################################
class VnpyGateway(VtGateway):
"""
每个gateway有太多重复代码难以拓展和维护
于是我设计了这个类将重复代码抽取出来简化gateway的实现
"""
#----------------------------------------------------------------------
def __init__(self, eventEngine):
super(VnpyGateway, self).__init__(eventEngine, self.gatewayName)
#----------------------------------------------------------------------
@abstractproperty
def gatewayName(self): # type: ()->str
return 'VnpyGateway'
#----------------------------------------------------------------------
@abstractproperty
def exchange(self): # type: ()->str
return constant.EXCHANGE_UNKNOWN
#----------------------------------------------------------------------
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 OkexFutureGateway(VnpyGateway):
"""OKEX期货交易接口"""
#----------------------------------------------------------------------
def __init__(self, eventEngine, *args, **kwargs): # args, kwargs is needed for compatibility
"""Constructor"""
super(OkexFutureGateway, self).__init__(eventEngine)
self.apiKey = None # type: str
self.apiSecret = None # type: str
self.api = OkexFutureApi(self)
self.leverRate = 1.0
self.symbols = []
#----------------------------------------------------------------------
@property
def gatewayName(self):
return 'OkexFutureGateway'
#----------------------------------------------------------------------
@abstractproperty
def exchange(self): # type: ()->str
return constant.EXCHANGE_OKEXFUTURE
#----------------------------------------------------------------------
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.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.api.init(self.apiKey, self.apiSecret)
#----------------------------------------------------------------------
def subscribe(self, subscribeReq):
"""订阅行情"""
pass
#----------------------------------------------------------------------
def sendOrder(self, orderReq):
"""发单"""
return self.api.sendOrder(orderReq)
#----------------------------------------------------------------------
def cancelOrder(self, cancelOrderReq):
"""撤单"""
self.api.cancelOrder(cancelOrderReq)
#----------------------------------------------------------------------
def qryAccount(self):
"""查询账户资金"""
pass
#----------------------------------------------------------------------
def qryPosition(self):
"""查询持仓"""
self.api.spotUserInfo()
#----------------------------------------------------------------------
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):
"""
每个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
#----------------------------------------------------------------------
# todo: push this / make this standalone
def generateVnpyOrder(self):
order = VnpyOrder()
order.order = VtOrderData()
order.order.exchange = self.gateway.exchange
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 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 _processOrderSent(self, request, remoteId):
"""
如果在_sendOrder中发送了HTTP请求则应该在收到响应的时候调用该函数
并且将remoteId设置为交易所API使用的ID
如果该下单请求失败将remoteId设为None即可
"""
if remoteId:
order = request.extra # type: VnpyOrder
order.remoteId = remoteId # None就是失败或者未返回
self.gateway.onOrder(order.order)
# todo: 撤单委托相关
#----------------------------------------------------------------------
@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 = OkexFutureHttpClient()
#----------------------------------------------------------------------
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 _cancelOrder(self, vtCancelRequest): # type: (VtCancelOrderReq)->Request
localId = vtCancelRequest.orderID
pass
#----------------------------------------------------------------------
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

View File

@ -87,6 +87,7 @@ EXCHANGE_HUOBI = 'HUOBI' # 火币比特币交易所
EXCHANGE_LBANK = 'LBANK' # LBANK比特币交易所
EXCHANGE_ZB = 'ZB' # 比特币中国比特币交易所
EXCHANGE_OKEX = 'OKEX' # OKEX比特币交易所
EXCHANGE_OKEXFUTURE = 'OKEXFUTURE' # OKEX比特币交易所-期货
EXCHANGE_BINANCE = "BINANCE" # 币安比特币交易所
EXCHANGE_BITFINEX = "BITFINEX" # Bitfinex比特币交易所
EXCHANGE_BITMEX = 'BITMEX' # BitMEX比特币交易所

View File

@ -83,6 +83,7 @@ EXCHANGE_HUOBI = 'HUOBI' # 火币比特币交易所
EXCHANGE_LBANK = 'LBANK' # LBANK比特币交易所
EXCHANGE_ZB = 'ZB' # 比特币中国比特币交易所
EXCHANGE_OKEX = 'OKEX' # OKEX比特币交易所
EXCHANGE_OKEXFUTURE = 'OKEXFUTURE' # OKEX比特币交易所-期货
EXCHANGE_BINANCE = "BINANCE" # 币安比特币交易所
EXCHANGE_BITFINEX = "BITFINEX" # Bitfinex比特币交易所
EXCHANGE_BITMEX = 'BITMEX' # BitMEX比特币交易所