[Add] okexFuture相关修改。
我决定了,每一点小修改都commit,无论是否能运行。所以我把修改都提交上来了。
This commit is contained in:
parent
54b93a89b5
commit
c04850c9a0
0
vnpy/api/okexfuture/__init__.py
Normal file
0
vnpy/api/okexfuture/__init__.py
Normal file
42
vnpy/api/okexfuture/vnokexFuture.py
Normal file
42
vnpy/api/okexfuture/vnokexFuture.py
Normal 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'}
|
||||
|
38
vnpy/network/HeadersAuthenticateHttpClient.py
Normal file
38
vnpy/network/HeadersAuthenticateHttpClient.py
Normal 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 {}
|
0
vnpy/trader/gateway/okexFutureGateway/__init__.py
Normal file
0
vnpy/trader/gateway/okexFutureGateway/__init__.py
Normal file
340
vnpy/trader/gateway/okexFutureGateway/okexFutureGateway.py
Normal file
340
vnpy/trader/gateway/okexFutureGateway/okexFutureGateway.py
Normal 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
|
@ -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比特币交易所
|
||||
|
@ -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比特币交易所
|
||||
|
Loading…
Reference in New Issue
Block a user