1. 增加vn.trader里OKCOIN比特币交易所的接入

2. 增加CTA模块回测中的部分业绩分析数据:盈亏比等
3. 修复之前vn.okcoin里的几个BUG
This commit is contained in:
chenxy123 2016-07-12 23:38:26 +08:00
parent e405e760f4
commit e7582dd01f
11 changed files with 1125 additions and 31 deletions

View File

@ -15,9 +15,9 @@ api.connect(OKCOIN_USD, apiKey, secretKey, True)
sleep(1) sleep(1)
# 测试现货行情API # 测试现货行情API
api.subscribeSpotTicker(SYMBOL_BTC) #api.subscribeSpotTicker(SYMBOL_BTC)
#api.subscribeSpotTradeData(SYMBOL_BTC) #api.subscribeSpotTradeData(SYMBOL_BTC)
#api.subscribeSpotDepth(SYMBOL_BTC, DEPTH_20) api.subscribeSpotDepth(SYMBOL_BTC, DEPTH_20)
#api.subscribeSpotKline(SYMBOL_BTC, INTERVAL_1M) #api.subscribeSpotKline(SYMBOL_BTC, INTERVAL_1M)
# 测试现货交易API # 测试现货交易API

View File

@ -41,7 +41,7 @@ INTERVAL_1W = 'week'
# 交易代码,需要后缀货币名才能完整 # 交易代码,需要后缀货币名才能完整
TRADING_SYMBOL_BTC = 'btc_' TRADING_SYMBOL_BTC = 'btc_'
TRADING_SYMBOL_LTS = 'ltc_' TRADING_SYMBOL_LTC = 'ltc_'
# 委托类型 # 委托类型
TYPE_BUY = 'buy' TYPE_BUY = 'buy'
@ -86,6 +86,7 @@ class OkCoinApi(object):
self.apiKey = '' # 用户名 self.apiKey = '' # 用户名
self.secretKey = '' # 密码 self.secretKey = '' # 密码
self.host = '' # 服务器地址 self.host = '' # 服务器地址
self.currency = '' # 货币类型usd或者cny self.currency = '' # 货币类型usd或者cny
self.ws = None # websocket应用对象 self.ws = None # websocket应用对象
@ -165,10 +166,6 @@ class OkCoinApi(object):
self.thread = Thread(target=self.ws.run_forever) self.thread = Thread(target=self.ws.run_forever)
self.thread.start() self.thread.start()
#######################
## 现货相关
#######################
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def sendMarketDataRequest(self, channel): def sendMarketDataRequest(self, channel):
"""发送行情请求""" """发送行情请求"""
@ -200,6 +197,10 @@ class OkCoinApi(object):
j = json.dumps(d) j = json.dumps(d)
self.ws.send(j) self.ws.send(j)
#######################
## 现货相关
#######################
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def subscribeSpotTicker(self, symbol): def subscribeSpotTicker(self, symbol):
"""订阅现货普通报价""" """订阅现货普通报价"""
@ -224,8 +225,8 @@ class OkCoinApi(object):
def spotTrade(self, symbol, type_, price, amount): def spotTrade(self, symbol, type_, price, amount):
"""现货委托""" """现货委托"""
params = {} params = {}
params['symbol'] = str(symbol) params['symbol'] = str(symbol+self.currency)
params['type'] = str(type_+self.currency) params['type'] = str(type_)
params['price'] = str(price) params['price'] = str(price)
params['amount'] = str(amount) params['amount'] = str(amount)
@ -258,7 +259,7 @@ class OkCoinApi(object):
params['symbol'] = str(symbol+self.currency) params['symbol'] = str(symbol+self.currency)
params['order_id'] = str(orderid) params['order_id'] = str(orderid)
channel = 'ok_spot%s_orderinfo' channel = 'ok_spot%s_orderinfo' %(self.currency)
self.sendTradingRequest(channel, params) self.sendTradingRequest(channel, params)

View File

@ -4,6 +4,7 @@
本文件中包含的是CTA模块的回测引擎回测引擎的API和CTA引擎一致 本文件中包含的是CTA模块的回测引擎回测引擎的API和CTA引擎一致
可以使用和实盘相同的代码进行回测 可以使用和实盘相同的代码进行回测
''' '''
from __future__ import division
from datetime import datetime, timedelta from datetime import datetime, timedelta
from collections import OrderedDict from collections import OrderedDict
@ -504,6 +505,11 @@ class BacktestingEngine(object):
capitalList = [] # 盈亏汇总的时间序列 capitalList = [] # 盈亏汇总的时间序列
drawdownList = [] # 回撤的时间序列 drawdownList = [] # 回撤的时间序列
winningResult = 0 # 盈利次数
losingResult = 0 # 亏损次数
totalWinning = 0 # 总盈利金额
totalLosing = 0 # 总亏损金额
for time, result in resultDict.items(): for time, result in resultDict.items():
capital += result.pnl capital += result.pnl
maxCapital = max(capital, maxCapital) maxCapital = max(capital, maxCapital)
@ -519,6 +525,19 @@ class BacktestingEngine(object):
totalCommission += result.commission totalCommission += result.commission
totalSlippage += result.slippage totalSlippage += result.slippage
if result.pnl >= 0:
winningResult += 1
totalWinning += result.pnl
else:
losingResult += 1
totalLosing += result.pnl
# 计算盈亏相关数据
winningRate = winningResult/totalResult*100 # 胜率
averageWinning = totalWinning/winningResult # 平均每笔盈利
averageLosing = totalLosing/losingResult # 平均每笔亏损
profitLossRatio = -averageWinning/averageLosing # 盈亏比
# 返回回测结果 # 返回回测结果
d = {} d = {}
d['capital'] = capital d['capital'] = capital
@ -532,6 +551,11 @@ class BacktestingEngine(object):
d['pnlList'] = pnlList d['pnlList'] = pnlList
d['capitalList'] = capitalList d['capitalList'] = capitalList
d['drawdownList'] = drawdownList d['drawdownList'] = drawdownList
d['winningRate'] = winningRate
d['averageWinning'] = averageWinning
d['averageLosing'] = averageLosing
d['profitLossRatio'] = profitLossRatio
return d return d
#---------------------------------------------------------------------- #----------------------------------------------------------------------
@ -552,6 +576,11 @@ class BacktestingEngine(object):
self.output(u'平均每笔滑点:\t%s' %formatNumber(d['totalSlippage']/d['totalResult'])) self.output(u'平均每笔滑点:\t%s' %formatNumber(d['totalSlippage']/d['totalResult']))
self.output(u'平均每笔佣金:\t%s' %formatNumber(d['totalCommission']/d['totalResult'])) self.output(u'平均每笔佣金:\t%s' %formatNumber(d['totalCommission']/d['totalResult']))
self.output(u'胜率\t\t%s%%' %formatNumber(d['winningRate']))
self.output(u'平均每笔盈利\t%s' %formatNumber(d['averageWinning']))
self.output(u'平均每笔亏损\t%s' %formatNumber(d['averageLosing']))
self.output(u'盈亏比:\t%s' %formatNumber(d['profitLossRatio']))
# 绘图 # 绘图
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
@ -651,11 +680,11 @@ class TradingResult(object):
self.exit = exit # 平仓价格 self.exit = exit # 平仓价格
self.volume = volume # 交易数量(+/-代表方向) self.volume = volume # 交易数量(+/-代表方向)
self.turnover = (self.entry+self.exit)*size # 成交金额 self.turnover = (self.entry+self.exit)*size*abs(volume) # 成交金额
self.commission = self.turnover*rate # 手续费成本 self.commission = self.turnover*rate # 手续费成本
self.slippage = slippage*2*size # 滑点成本 self.slippage = slippage*2*size*abs(volume) # 滑点成本
self.pnl = ((self.exit - self.entry) * volume * size self.pnl = ((self.exit - self.entry) * volume * size
- self.commission - self.slippage) # 净盈亏 - self.commission - self.slippage) # 净盈亏
######################################################################## ########################################################################

View File

@ -259,21 +259,21 @@ if __name__ == '__main__':
# 设置使用的历史数据库 # 设置使用的历史数据库
engine.setDatabase(MINUTE_DB_NAME, 'IF0000') engine.setDatabase(MINUTE_DB_NAME, 'IF0000')
## 在引擎中创建策略对象 # 在引擎中创建策略对象
#d = {'atrLength': 11} d = {'atrLength': 11}
#engine.initStrategy(AtrRsiStrategy, d) engine.initStrategy(AtrRsiStrategy, d)
## 开始跑回测 # 开始跑回测
#engine.runBacktesting() engine.runBacktesting()
## 显示回测结果 # 显示回测结果
#engine.showBacktestingResult() engine.showBacktestingResult()
# 跑优化 ## 跑优化
setting = OptimizationSetting() # 新建一个优化任务设置对象 #setting = OptimizationSetting() # 新建一个优化任务设置对象
setting.setOptimizeTarget('capital') # 设置优化排序的目标是策略净盈利 #setting.setOptimizeTarget('capital') # 设置优化排序的目标是策略净盈利
setting.addParameter('atrLength', 11, 12, 1) # 增加第一个优化参数atrLength起始11结束12步进1 #setting.addParameter('atrLength', 11, 12, 1) # 增加第一个优化参数atrLength起始11结束12步进1
setting.addParameter('atrMa', 20, 30, 5) # 增加第二个优化参数atrMa起始20结束30步进1 #setting.addParameter('atrMa', 20, 30, 5) # 增加第二个优化参数atrMa起始20结束30步进1
engine.runOptimization(AtrRsiStrategy, setting) # 运行优化函数,自动输出结果 #engine.runOptimization(AtrRsiStrategy, setting) # 运行优化函数,自动输出结果

View File

@ -0,0 +1,7 @@
{
"host": "CNY",
"apiKey": "OKCOIN网站申请",
"secretKey": "OKCOIN网站申请",
"trace": false,
"leverage": 20
}

View File

View File

@ -0,0 +1,657 @@
# encoding: UTF-8
'''
vn.okcoin的gateway接入
注意
1. 该接口尚处于测试阶段用于实盘请谨慎
2. 目前仅支持USD和CNY的现货交易USD的期货合约交易暂不支持
'''
import os
import json
from datetime import datetime
from copy import copy
from threading import Condition
import vnokcoin
from vtGateway import *
# 价格类型映射
priceTypeMap = {}
priceTypeMap['buy'] = (DIRECTION_LONG, PRICETYPE_LIMITPRICE)
priceTypeMap['buy_market'] = (DIRECTION_LONG, PRICETYPE_MARKETPRICE)
priceTypeMap['sell'] = (DIRECTION_SHORT, PRICETYPE_LIMITPRICE)
priceTypeMap['sell_market'] = (DIRECTION_SHORT, PRICETYPE_MARKETPRICE)
priceTypeMapReverse = {v: k for k, v in priceTypeMap.items()}
# 方向类型映射
directionMap = {}
directionMapReverse = {v: k for k, v in directionMap.items()}
# 委托状态印射
statusMap = {}
statusMap[-1] = STATUS_CANCELLED
statusMap[0] = STATUS_NOTTRADED
statusMap[1] = STATUS_PARTTRADED
statusMap[2] = STATUS_ALLTRADED
statusMap[4] = STATUS_UNKNOWN
############################################
## 交易合约代码
############################################
# USD
BTC_USD_SPOT = 'BTC_USD_SPOT'
BTC_USD_THISWEEK = 'BTC_USD_THISWEEK'
BTC_USD_NEXTWEEK = 'BTC_USD_NEXTWEEK'
BTC_USD_QUARTER = 'BTC_USD_QUARTER'
LTC_USD_SPOT = 'LTC_USD_SPOT'
LTC_USD_THISWEEK = 'LTC_USD_THISWEEK'
LTC_USD_NEXTWEEK = 'LTC_USD_NEXTWEEK'
LTC_USD_QUARTER = 'LTC_USD_QUARTER'
# CNY
BTC_CNY_SPOT = 'BTC_CNY_SPOT'
LTC_CNY_SPOT = 'LTC_CNY_SPOT'
# 印射字典
spotSymbolMap = {}
spotSymbolMap['ltc_usd'] = LTC_USD_SPOT
spotSymbolMap['btc_usd'] = BTC_USD_SPOT
spotSymbolMap['ltc_cny'] = LTC_CNY_SPOT
spotSymbolMap['btc_cny'] = BTC_CNY_SPOT
spotSymbolMapReverse = {v: k for k, v in spotSymbolMap.items()}
############################################
## Channel和Symbol的印射
############################################
channelSymbolMap = {}
# USD
channelSymbolMap['ok_sub_spotusd_btc_ticker'] = BTC_USD_SPOT
channelSymbolMap['ok_sub_spotusd_ltc_ticker'] = LTC_USD_SPOT
channelSymbolMap['ok_sub_spotusd_btc_depth_20'] = BTC_USD_SPOT
channelSymbolMap['ok_sub_spotusd_ltc_depth_20'] = LTC_USD_SPOT
# CNY
channelSymbolMap['ok_sub_spotcny_btc_ticker'] = BTC_CNY_SPOT
channelSymbolMap['ok_sub_spotcny_ltc_ticker'] = LTC_CNY_SPOT
channelSymbolMap['ok_sub_spotcny_btc_depth_20'] = BTC_CNY_SPOT
channelSymbolMap['ok_sub_spotcny_ltc_depth_20'] = LTC_CNY_SPOT
########################################################################
class OkcoinGateway(VtGateway):
"""OkCoin接口"""
#----------------------------------------------------------------------
def __init__(self, eventEngine, gatewayName='OKCOIN'):
"""Constructor"""
super(OkcoinGateway, self).__init__(eventEngine, gatewayName)
self.api = Api(self)
self.leverage = 0
self.connected = False
#----------------------------------------------------------------------
def connect(self):
"""连接"""
# 载入json文件
fileName = self.gatewayName + '_connect.json'
fileName = os.getcwd() + '/okcoinGateway/' + fileName
try:
f = file(fileName)
except IOError:
log = VtLogData()
log.gatewayName = self.gatewayName
log.logContent = u'读取连接配置出错,请检查'
self.onLog(log)
return
# 解析json文件
setting = json.load(f)
try:
host = str(setting['host'])
apiKey = str(setting['apiKey'])
secretKey = str(setting['secretKey'])
trace = setting['trace']
leverage = setting['leverage']
except KeyError:
log = VtLogData()
log.gatewayName = self.gatewayName
log.logContent = u'连接配置缺少字段,请检查'
self.onLog(log)
return
# 初始化接口
self.leverage = leverage
if host == 'CNY':
host = vnokcoin.OKCOIN_CNY
else:
host = vnokcoin.OKCOIN_USD
self.api.connect(host, apiKey, secretKey, trace)
log = VtLogData()
log.gatewayName = self.gatewayName
log.logContent = u'接口初始化成功'
self.onLog(log)
# 启动查询
self.initQuery()
self.startQuery()
#----------------------------------------------------------------------
def subscribe(self, subscribeReq):
"""订阅行情"""
pass
#----------------------------------------------------------------------
def sendOrder(self, orderReq):
"""发单"""
return self.api.spotSendOrder(orderReq)
#----------------------------------------------------------------------
def cancelOrder(self, cancelOrderReq):
"""撤单"""
self.api.spotCancel(cancelOrderReq)
#----------------------------------------------------------------------
def qryAccount(self):
"""查询账户资金"""
self.api.spotUserInfo()
#----------------------------------------------------------------------
def qryPosition(self):
"""查询持仓"""
pass
#----------------------------------------------------------------------
def close(self):
"""关闭"""
pass
#----------------------------------------------------------------------
def initQuery(self):
"""初始化连续查询"""
if self.qryEnabled:
# 需要循环的查询函数列表
self.qryFunctionList = [self.qryAccount]
self.qryCount = 0 # 查询触发倒计时
self.qryTrigger = 2 # 查询触发点
self.qryNextFunction = 0 # 上次运行的查询函数索引
self.startQuery()
#----------------------------------------------------------------------
def query(self, event):
"""注册到事件处理引擎上的查询函数"""
self.qryCount += 1
if self.qryCount > self.qryTrigger:
# 清空倒计时
self.qryCount = 0
# 执行查询函数
function = self.qryFunctionList[self.qryNextFunction]
function()
# 计算下次查询函数的索引如果超过了列表长度则重新设为0
self.qryNextFunction += 1
if self.qryNextFunction == len(self.qryFunctionList):
self.qryNextFunction = 0
#----------------------------------------------------------------------
def startQuery(self):
"""启动连续查询"""
self.eventEngine.register(EVENT_TIMER, self.query)
#----------------------------------------------------------------------
def setQryEnabled(self, qryEnabled):
"""设置是否要启动循环查询"""
self.qryEnabled = qryEnabled
########################################################################
class Api(vnokcoin.OkCoinApi):
"""OkCoin的API实现"""
#----------------------------------------------------------------------
def __init__(self, gateway):
"""Constructor"""
super(Api, self).__init__()
self.gateway = gateway # gateway对象
self.gatewayName = gateway.gatewayName # gateway对象名称
self.cbDict = {}
self.tickDict = {}
self.orderDict = {}
self.lastOrderID = ''
self.orderCondition = Condition()
self.initCallback()
#----------------------------------------------------------------------
def onMessage(self, ws, evt):
"""信息推送"""
data = self.readData(evt)[0]
channel = data['channel']
callback = self.cbDict[channel]
callback(data)
#----------------------------------------------------------------------
def onError(self, ws, evt):
"""错误推送"""
error = VtErrorData()
error.gatewayName = self.gatewayName
error.errorMsg = str(evt)
self.gateway.onError(error)
#----------------------------------------------------------------------
def onClose(self, ws):
"""接口断开"""
self.gateway.connected = True
self.writeLog(u'服务器连接断开')
#----------------------------------------------------------------------
def onOpen(self, ws):
self.gateway.connected = True
self.writeLog(u'服务器连接成功')
# 连接后查询账户和委托数据
self.spotUserInfo()
self.spotOrderInfo(vnokcoin.TRADING_SYMBOL_LTC, '-1')
self.spotOrderInfo(vnokcoin.TRADING_SYMBOL_BTC, '-1')
# 连接后订阅现货的成交和账户数据
self.subscribeSpotTrades()
self.subscribeSpotUserInfo()
self.subscribeSpotTicker(vnokcoin.SYMBOL_BTC)
self.subscribeSpotTicker(vnokcoin.SYMBOL_LTC)
self.subscribeSpotDepth(vnokcoin.SYMBOL_BTC, vnokcoin.DEPTH_20)
self.subscribeSpotDepth(vnokcoin.SYMBOL_LTC, vnokcoin.DEPTH_20)
# 如果连接的是USD网站则订阅期货相关回报数据
if self.currency == vnokcoin.CURRENCY_USD:
self.subscribeFutureTrades()
self.subscribeFutureUserInfo()
self.subscribeFuturePositions()
# 返回合约信息
if self.currency == vnokcoin.CURRENCY_CNY:
l = self.generateCnyContract()
else:
l = self.generateUsdContract()
for contract in l:
contract.gatewayName = self.gatewayName
self.gateway.onContract(contract)
#----------------------------------------------------------------------
def writeLog(self, content):
"""快速记录日志"""
log = VtLogData()
log.gatewayName = self.gatewayName
log.logContent = content
self.gateway.onLog(log)
#----------------------------------------------------------------------
def initCallback(self):
"""初始化回调函数"""
# USD_SPOT
self.cbDict['ok_sub_spotusd_btc_ticker'] = self.onTicker
self.cbDict['ok_sub_spotusd_ltc_ticker'] = self.onTicker
self.cbDict['ok_sub_spotusd_btc_depth_20'] = self.onDepth
self.cbDict['ok_sub_spotusd_ltc_depth_20'] = self.onDepth
self.cbDict['ok_spotusd_userinfo'] = self.onSpotUserInfo
self.cbDict['ok_spotusd_orderinfo'] = self.onSpotOrderInfo
self.cbDict['ok_sub_spotusd_userinfo'] = self.onSpotSubUserInfo
self.cbDict['ok_sub_spotusd_trades'] = self.onSpotSubTrades
self.cbDict['ok_spotusd_trade'] = self.onSpotTrade
self.cbDict['ok_spotusd_cancel_order'] = self.onSpotCancelOrder
# CNY_SPOT
self.cbDict['ok_sub_spotcny_btc_ticker'] = self.onTicker
self.cbDict['ok_sub_spotcny_ltc_ticker'] = self.onTicker
self.cbDict['ok_sub_spotcny_btc_depth_20'] = self.onDepth
self.cbDict['ok_sub_spotcny_ltc_depth_20'] = self.onDepth
self.cbDict['ok_spotcny_userinfo'] = self.onSpotUserInfo
self.cbDict['ok_spotcny_orderinfo'] = self.onSpotOrderInfo
self.cbDict['ok_sub_spotcny_userinfo'] = self.onSpotSubUserInfo
self.cbDict['ok_sub_spotcny_trades'] = self.onSpotSubTrades
self.cbDict['ok_spotcny_trade'] = self.onSpotTrade
self.cbDict['ok_spotcny_cancel_order'] = self.onSpotCancelOrder
# USD_FUTURES
#----------------------------------------------------------------------
def onTicker(self, data):
""""""
if 'data' not in data:
return
channel = data['channel']
symbol = channelSymbolMap[channel]
if symbol not in self.tickDict:
tick = VtTickData()
tick.symbol = symbol
tick.vtSymbol = symbol
tick.gatewayName = self.gatewayName
self.tickDict[symbol] = tick
else:
tick = self.tickDict[symbol]
rawData = data['data']
tick.highPrice = float(rawData['high'])
tick.lowPrice = float(rawData['low'])
tick.lastPrice = float(rawData['last'])
tick.volume = float(rawData['vol'].replace(',', ''))
tick.date, tick.time = generateDateTime(rawData['timestamp'])
newtick = copy(tick)
self.gateway.onTick(newtick)
#----------------------------------------------------------------------
def onDepth(self, data):
""""""
if 'data' not in data:
return
channel = data['channel']
symbol = channelSymbolMap[channel]
if symbol not in self.tickDict:
tick = VtTickData()
tick.symbol = symbol
tick.vtSymbol = symbol
tick.gatewayName = self.gatewayName
self.tickDict[symbol] = tick
else:
tick = self.tickDict[symbol]
if 'data' not in data:
return
rawData = data['data']
tick.bidPrice1, tick.bidVolume1 = rawData['bids'][0]
tick.bidPrice2, tick.bidVolume2 = rawData['bids'][1]
tick.bidPrice3, tick.bidVolume3 = rawData['bids'][2]
tick.bidPrice4, tick.bidVolume4 = rawData['bids'][3]
tick.bidPrice5, tick.bidVolume5 = rawData['bids'][4]
tick.askPrice1, tick.askVolume1 = rawData['asks'][0]
tick.askPrice2, tick.askVolume2 = rawData['asks'][1]
tick.askPrice3, tick.askVolume3 = rawData['asks'][2]
tick.askPrice4, tick.askVolume4 = rawData['asks'][3]
tick.askPrice5, tick.askVolume5 = rawData['asks'][4]
newtick = copy(tick)
self.gateway.onTick(newtick)
#----------------------------------------------------------------------
def onSpotUserInfo(self, data):
"""现货账户资金推送"""
rawData = data['data']
info = rawData['info']
funds = rawData['info']['funds']
# 持仓信息
for symbol in ['btc', 'ltc', self.currency]:
if symbol in funds['free']:
pos = VtPositionData()
pos.gatewayName = self.gatewayName
pos.symbol = symbol
pos.vtSymbol = symbol
pos.vtPositionName = symbol
pos.direction = DIRECTION_NET
pos.frozen = float(funds['freezed'][symbol])
pos.position = pos.frozen + float(funds['free'][symbol])
self.gateway.onPosition(pos)
# 账户资金
account = VtAccountData()
account.gatewayName = self.gatewayName
account.accountID = self.gatewayName
account.vtAccountID = account.accountID
account.balance = float(funds['asset']['net'])
self.gateway.onAccount(account)
#----------------------------------------------------------------------
def onSpotSubUserInfo(self, data):
"""现货账户资金推送"""
if 'data' not in data:
return
rawData = data['data']
info = rawData['info']
# 持仓信息
for symbol in ['btc', 'ltc', self.currency]:
if symbol in info['free']:
pos = VtPositionData()
pos.gatewayName = self.gatewayName
pos.symbol = symbol
pos.vtSymbol = symbol
pos.vtPositionName = symbol
pos.direction = DIRECTION_NET
pos.frozen = float(info['freezed'][symbol])
pos.position = pos.frozen + float(info['free'][symbol])
self.gateway.onPosition(pos)
#----------------------------------------------------------------------
def onSpotSubTrades(self, data):
"""成交和委托推送"""
if 'data' not in data:
return
rawData = data['data']
# 委托信息
orderID = str(rawData['orderId'])
if orderID not in self.orderDict:
order = VtOrderData()
order.gatewayName = self.gatewayName
order.symbol = spotSymbolMap[rawData['symbol']]
order.vtSymbol = order.symbol
order.orderID = str(rawData['orderId'])
order.vtOrderID = '.'.join([self.gatewayName, order.orderID])
order.price = float(rawData['tradeUnitPrice'])
order.totalVolume = float(rawData['tradeAmount'])
order.direction, priceType = priceTypeMap[rawData['tradeType']]
self.orderDict[orderID] = order
else:
order = self.orderDict[orderID]
order.tradedVolume = float(rawData['completedTradeAmount'])
order.status = statusMap[rawData['status']]
self.gateway.onOrder(copy(order))
# 成交信息
if 'sigTradeAmount' in rawData and float(rawData['sigTradeAmount'])>0:
trade = VtTradeData()
trade.gatewayName = self.gatewayName
trade.symbol = spotSymbolMap[rawData['symbol']]
trade.vtSymbol = order.symbol
trade.tradeID = str(rawData['id'])
trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID])
trade.orderID = str(rawData['orderId'])
trade.vtOrderID = '.'.join([self.gatewayName, trade.orderID])
trade.price = float(rawData['sigTradePrice'])
trade.volume = float(rawData['sigTradeAmount'])
trade.direction, priceType = priceTypeMap[rawData['tradeType']]
trade.tradeTime = datetime.now().strftime('%H:%M:%S')
self.gateway.onTrade(trade)
#----------------------------------------------------------------------
def onSpotOrderInfo(self, data):
"""委托信息查询回调"""
rawData = data['data']
for d in rawData['orders']:
orderID = str(d['order_id'])
if orderID not in self.orderDict:
order = VtOrderData()
order.gatewayName = self.gatewayName
order.symbol = spotSymbolMap[d['symbol']]
order.vtSymbol = order.symbol
order.orderID = str(d['order_id'])
order.vtOrderID = '.'.join([self.gatewayName, order.orderID])
order.price = d['price']
order.totalVolume = d['amount']
order.direction, priceType = priceTypeMap[d['type']]
self.orderDict[orderID] = order
else:
order = self.orderDict[orderID]
order.tradedVolume = d['deal_amount']
order.status = statusMap[d['status']]
self.gateway.onOrder(copy(order))
#----------------------------------------------------------------------
def generateSpecificContract(self, contract, symbol):
"""生成合约"""
new = copy(contract)
new.symbol = symbol
new.vtSymbol = symbol
new.name = symbol
return new
#----------------------------------------------------------------------
def generateCnyContract(self):
"""生成CNY合约信息"""
contractList = []
contract = VtContractData()
contract.exchange = EXCHANGE_OKCOIN
contract.productClass = PRODUCT_SPOT
contract.size = 1
contract.priceTick = 0.01
contractList.append(self.generateSpecificContract(contract, BTC_CNY_SPOT))
contractList.append(self.generateSpecificContract(contract, LTC_CNY_SPOT))
return contractList
#----------------------------------------------------------------------
def generateUsdContract(self):
"""生成USD合约信息"""
contractList = []
# 现货
contract = VtContractData()
contract.exchange = EXCHANGE_OKCOIN
contract.productClass = PRODUCT_SPOT
contract.size = 1
contract.priceTick = 0.01
contractList.append(self.generateSpecificContract(contract, BTC_USD_SPOT))
contractList.append(self.generateSpecificContract(contract, LTC_USD_SPOT))
# 期货
contract.productClass = PRODUCT_FUTURES
contractList.append(self.generateSpecificContract(contract, BTC_USD_THISWEEK))
contractList.append(self.generateSpecificContract(contract, BTC_USD_NEXTWEEK))
contractList.append(self.generateSpecificContract(contract, BTC_USD_QUARTER))
contractList.append(self.generateSpecificContract(contract, LTC_USD_THISWEEK))
contractList.append(self.generateSpecificContract(contract, LTC_USD_NEXTWEEK))
contractList.append(self.generateSpecificContract(contract, LTC_USD_QUARTER))
return contractList
#----------------------------------------------------------------------
def onSpotTrade(self, data):
"""委托回报"""
rawData = data['data']
self.lastOrderID = rawData['order_id']
# 收到委托号后,通知发送委托的线程返回委托号
self.orderCondition.acquire()
self.orderCondition.notify()
self.orderCondition.release()
#----------------------------------------------------------------------
def onSpotCancelOrder(self, data):
"""撤单回报"""
pass
#----------------------------------------------------------------------
def spotSendOrder(self, req):
"""发单"""
symbol = spotSymbolMapReverse[req.symbol][:4]
type_ = priceTypeMapReverse[(req.direction, req.priceType)]
self.spotTrade(symbol, type_, str(req.price), str(req.volume))
# 等待发单回调推送委托号信息
self.orderCondition.acquire()
self.orderCondition.wait()
self.orderCondition.release()
vtOrderID = '.'.join([self.gatewayName, self.lastOrderID])
self.lastOrderID = ''
return vtOrderID
#----------------------------------------------------------------------
def spotCancel(self, req):
"""撤单"""
symbol = spotSymbolMapReverse[req.symbol][:4]
self.spotCancelOrder(symbol, req.orderID)
#----------------------------------------------------------------------
def generateDateTime(s):
"""生成时间"""
dt = datetime.fromtimestamp(float(s)/1e3)
time = dt.strftime("%H:%M:%S.%f")
date = dt.strftime("%Y%m%d")
return date, time

View File

@ -0,0 +1,382 @@
# encoding: UTF-8
import hashlib
import zlib
import json
from time import sleep
from threading import Thread
import websocket
# OKCOIN网站
OKCOIN_CNY = 'wss://real.okcoin.cn:10440/websocket/okcoinapi'
OKCOIN_USD = 'wss://real.okcoin.com:10440/websocket/okcoinapi'
# 账户货币代码
CURRENCY_CNY = 'cny'
CURRENCY_USD = 'usd'
# 电子货币代码
SYMBOL_BTC = 'btc'
SYMBOL_LTC = 'ltc'
# 行情深度
DEPTH_20 = 20
DEPTH_60 = 60
# K线时间区间
INTERVAL_1M = '1min'
INTERVAL_3M = '3min'
INTERVAL_5M = '5min'
INTERVAL_15M = '15min'
INTERVAL_30M = '30min'
INTERVAL_1H = '1hour'
INTERVAL_2H = '2hour'
INTERVAL_4H = '4hour'
INTERVAL_6H = '6hour'
INTERVAL_1D = 'day'
INTERVAL_3D = '3day'
INTERVAL_1W = 'week'
# 交易代码,需要后缀货币名才能完整
TRADING_SYMBOL_BTC = 'btc_'
TRADING_SYMBOL_LTC = 'ltc_'
# 委托类型
TYPE_BUY = 'buy'
TYPE_SELL = 'sell'
TYPE_BUY_MARKET = 'buy_market'
TYPE_SELL_MARKET = 'sell_market'
# 期货合约到期类型
FUTURE_EXPIRY_THIS_WEEK = 'this_week'
FUTURE_EXPIRY_NEXT_WEEK = 'next_week'
FUTURE_EXPIRY_QUARTER = 'quarter'
# 期货委托类型
FUTURE_TYPE_LONG = 1
FUTURE_TYPE_SHORT = 2
FUTURE_TYPE_SELL = 3
FUTURE_TYPE_COVER = 4
# 期货是否用现价
FUTURE_ORDER_MARKET = 1
FUTURE_ORDER_LIMIT = 0
# 期货杠杆
FUTURE_LEVERAGE_10 = 10
FUTURE_LEVERAGE_20 = 20
# 委托状态
ORDER_STATUS_NOTTRADED = 0
ORDER_STATUS_PARTTRADED = 1
ORDER_STATUS_ALLTRADED = 2
ORDER_STATUS_CANCELLED = -1
ORDER_STATUS_CANCELLING = 4
########################################################################
class OkCoinApi(object):
"""基于Websocket的API对象"""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
self.apiKey = '' # 用户名
self.secretKey = '' # 密码
self.host = '' # 服务器地址
self.currency = '' # 货币类型usd或者cny
self.ws = None # websocket应用对象
self.thread = None # 工作线程
#######################
## 通用函数
#######################
#----------------------------------------------------------------------
def readData(self, evt):
"""解压缩推送收到的数据"""
# 创建解压器
decompress = zlib.decompressobj(-zlib.MAX_WBITS)
# 将原始数据解压成字符串
inflated = decompress.decompress(evt) + decompress.flush()
# 通过json解析字符串
data = json.loads(inflated)
return data
#----------------------------------------------------------------------
def generateSign(self, params):
"""生成签名"""
l = []
for key in sorted(params.keys()):
l.append('%s=%s' %(key, params[key]))
l.append('secret_key=%s' %self.secretKey)
sign = '&'.join(l)
return hashlib.md5(sign.encode('utf-8')).hexdigest().upper()
#----------------------------------------------------------------------
def onMessage(self, ws, evt):
"""信息推送"""
print 'onMessage'
data = self.readData(evt)
print data
#----------------------------------------------------------------------
def onError(self, ws, evt):
"""错误推送"""
print 'onError'
print evt
#----------------------------------------------------------------------
def onClose(self, ws):
"""接口断开"""
print 'onClose'
#----------------------------------------------------------------------
def onOpen(self, ws):
"""接口打开"""
print 'onOpen'
#----------------------------------------------------------------------
def connect(self, host, apiKey, secretKey, trace=False):
"""连接服务器"""
self.host = host
self.apiKey = apiKey
self.secretKey = secretKey
if self.host == OKCOIN_CNY:
self.currency = CURRENCY_CNY
else:
self.currency = CURRENCY_USD
websocket.enableTrace(trace)
self.ws = websocket.WebSocketApp(host,
on_message=self.onMessage,
on_error=self.onError,
on_close=self.onClose,
on_open=self.onOpen)
self.thread = Thread(target=self.ws.run_forever)
self.thread.start()
#----------------------------------------------------------------------
def sendMarketDataRequest(self, channel):
"""发送行情请求"""
# 生成请求
d = {}
d['event'] = 'addChannel'
d['binary'] = True
d['channel'] = channel
# 使用json打包并发送
j = json.dumps(d)
self.ws.send(j)
#----------------------------------------------------------------------
def sendTradingRequest(self, channel, params):
"""发送交易请求"""
# 在参数字典中加上api_key和签名字段
params['api_key'] = self.apiKey
params['sign'] = self.generateSign(params)
# 生成请求
d = {}
d['event'] = 'addChannel'
d['binary'] = True
d['channel'] = channel
d['parameters'] = params
# 使用json打包并发送
j = json.dumps(d)
self.ws.send(j)
#######################
## 现货相关
#######################
#----------------------------------------------------------------------
def subscribeSpotTicker(self, symbol):
"""订阅现货普通报价"""
self.sendMarketDataRequest('ok_sub_spot%s_%s_ticker' %(self.currency, symbol))
#----------------------------------------------------------------------
def subscribeSpotDepth(self, symbol, depth):
"""订阅现货深度报价"""
self.sendMarketDataRequest('ok_sub_spot%s_%s_depth_%s' %(self.currency, symbol, depth))
#----------------------------------------------------------------------
def subscribeSpotTradeData(self, symbol):
"""订阅现货成交记录"""
self.sendMarketDataRequest('ok_sub_spot%s_%s_trades' %(self.currency, symbol))
#----------------------------------------------------------------------
def subscribeSpotKline(self, symbol, interval):
"""订阅现货K线"""
self.sendMarketDataRequest('ok_sub_spot%s_%s_kline_%s' %(self.currency, symbol, interval))
#----------------------------------------------------------------------
def spotTrade(self, symbol, type_, price, amount):
"""现货委托"""
params = {}
params['symbol'] = str(symbol+self.currency)
params['type'] = str(type_)
params['price'] = str(price)
params['amount'] = str(amount)
channel = 'ok_spot%s_trade' %(self.currency)
self.sendTradingRequest(channel, params)
#----------------------------------------------------------------------
def spotCancelOrder(self, symbol, orderid):
"""现货撤单"""
params = {}
params['symbol'] = str(symbol+self.currency)
params['order_id'] = str(orderid)
channel = 'ok_spot%s_cancel_order' %(self.currency)
self.sendTradingRequest(channel, params)
#----------------------------------------------------------------------
def spotUserInfo(self):
"""查询现货账户"""
channel = 'ok_spot%s_userinfo' %(self.currency)
self.sendTradingRequest(channel, {})
#----------------------------------------------------------------------
def spotOrderInfo(self, symbol, orderid):
"""查询现货委托信息"""
params = {}
params['symbol'] = str(symbol+self.currency)
params['order_id'] = str(orderid)
channel = 'ok_spot%s_orderinfo' %(self.currency)
self.sendTradingRequest(channel, params)
#----------------------------------------------------------------------
def subscribeSpotTrades(self):
"""订阅现货成交信息"""
channel = 'ok_sub_spot%s_trades' %(self.currency)
self.sendTradingRequest(channel, {})
#----------------------------------------------------------------------
def subscribeSpotUserInfo(self):
"""订阅现货账户信息"""
channel = 'ok_sub_spot%s_userinfo' %(self.currency)
self.sendTradingRequest(channel, {})
#######################
## 期货相关
#######################
#----------------------------------------------------------------------
def subscribeFutureTicker(self, symbol, expiry):
"""订阅期货普通报价"""
self.sendMarketDataRequest('ok_sub_future%s_%s_ticker_%s' %(self.currency, symbol, expiry))
#----------------------------------------------------------------------
def subscribeFutureDepth(self, symbol, expiry, depth):
"""订阅期货深度报价"""
self.sendMarketDataRequest('ok_sub_future%s_%s_depth_%s_%s' %(self.currency, symbol,
expiry, depth))
#----------------------------------------------------------------------
def subscribeFutureTradeData(self, symbol, expiry):
"""订阅期货成交记录"""
self.sendMarketDataRequest('ok_sub_future%s_%s_trade_%s' %(self.currency, symbol, expiry))
#----------------------------------------------------------------------
def subscribeFutureKline(self, symbol, expiry, interval):
"""订阅期货K线"""
self.sendMarketDataRequest('ok_sub_future%s_%s_kline_%s_%s' %(self.currency, symbol,
expiry, interval))
#----------------------------------------------------------------------
def subscribeFutureIndex(self, symbol):
"""订阅期货指数"""
self.sendMarketDataRequest('ok_sub_future%s_%s_index' %(self.currency, symbol))
#----------------------------------------------------------------------
def futureTrade(self, symbol, expiry, type_, price, amount, order, leverage):
"""期货委托"""
params = {}
params['symbol'] = str(symbol+self.currency)
params['type'] = str(type_)
params['price'] = str(price)
params['amount'] = str(amount)
params['contract_type'] = str(expiry)
params['match_price'] = str(order)
params['lever_rate'] = str(leverage)
channel = 'ok_future%s_trade' %(self.currency)
self.sendTradingRequest(channel, params)
#----------------------------------------------------------------------
def futureCancelOrder(self, symbol, expiry, orderid):
"""期货撤单"""
params = {}
params['symbol'] = str(symbol+self.currency)
params['order_id'] = str(orderid)
params['contract_type'] = str(expiry)
channel = 'ok_future%s_cancel_order' %(self.currency)
self.sendTradingRequest(channel, params)
#----------------------------------------------------------------------
def futureUserInfo(self):
"""查询期货账户"""
channel = 'ok_future%s_userinfo' %(self.currency)
self.sendTradingRequest(channel, {})
#----------------------------------------------------------------------
def futureOrderInfo(self, symbol, expiry, orderid, status, page, length):
"""查询期货委托信息"""
params = {}
params['symbol'] = str(symbol+self.currency)
params['order_id'] = str(orderid)
params['contract_type'] = expiry
params['status'] = status
params['current_page'] = page
params['page_length'] = length
channel = 'ok_future%s_orderinfo'
self.sendTradingRequest(channel, params)
#----------------------------------------------------------------------
def subscribeFutureTrades(self):
"""订阅期货成交信息"""
channel = 'ok_sub_future%s_trades' %(self.currency)
self.sendTradingRequest(channel, {})
#----------------------------------------------------------------------
def subscribeFutureUserInfo(self):
"""订阅期货账户信息"""
channel = 'ok_sub_future%s_userinfo' %(self.currency)
self.sendTradingRequest(channel, {})
#----------------------------------------------------------------------
def subscribeFuturePositions(self):
"""订阅期货持仓信息"""
channel = 'ok_sub_future%s_positions' %(self.currency)
self.sendTradingRequest(channel, {})

View File

@ -89,6 +89,9 @@ class MainWindow(QtGui.QMainWindow):
connectOandaAction = QtGui.QAction(u'连接OANDA', self) connectOandaAction = QtGui.QAction(u'连接OANDA', self)
connectOandaAction.triggered.connect(self.connectOanda) connectOandaAction.triggered.connect(self.connectOanda)
connectOkcoinAction = QtGui.QAction(u'连接OKCOIN', self)
connectOkcoinAction.triggered.connect(self.connectOkcoin)
connectDbAction = QtGui.QAction(u'连接数据库', self) connectDbAction = QtGui.QAction(u'连接数据库', self)
connectDbAction.triggered.connect(self.mainEngine.dbConnect) connectDbAction.triggered.connect(self.mainEngine.dbConnect)
@ -137,6 +140,8 @@ class MainWindow(QtGui.QMainWindow):
sysMenu.addAction(connectIbAction) sysMenu.addAction(connectIbAction)
if 'OANDA' in self.mainEngine.gatewayDict: if 'OANDA' in self.mainEngine.gatewayDict:
sysMenu.addAction(connectOandaAction) sysMenu.addAction(connectOandaAction)
if 'OKCOIN' in self.mainEngine.gatewayDict:
sysMenu.addAction(connectOkcoinAction)
sysMenu.addSeparator() sysMenu.addSeparator()
if 'Wind' in self.mainEngine.gatewayDict: if 'Wind' in self.mainEngine.gatewayDict:
sysMenu.addAction(connectWindAction) sysMenu.addAction(connectWindAction)
@ -239,6 +244,11 @@ class MainWindow(QtGui.QMainWindow):
"""连接OANDA""" """连接OANDA"""
self.mainEngine.connect('OANDA') self.mainEngine.connect('OANDA')
#----------------------------------------------------------------------
def connectOkcoin(self):
"""连接OKCOIN"""
self.mainEngine.connect('OKCOIN')
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def test(self): def test(self):
"""测试按钮用的函数""" """测试按钮用的函数"""

View File

@ -69,6 +69,7 @@ EXCHANGE_GLOBEX = 'GLOBEX' # CME电子交易平台
EXCHANGE_IDEALPRO = 'IDEALPRO' # IB外汇ECN EXCHANGE_IDEALPRO = 'IDEALPRO' # IB外汇ECN
EXCHANGE_OANDA = 'OANDA' # OANDA外汇做市商 EXCHANGE_OANDA = 'OANDA' # OANDA外汇做市商
EXCHANGE_OKCOIN = 'OKCOIN' # OKCOIN比特币交易所
# 货币类型 # 货币类型
CURRENCY_USD = 'USD' # 美元 CURRENCY_USD = 'USD' # 美元

View File

@ -115,6 +115,13 @@ class MainEngine(object):
except Exception, e: except Exception, e:
print e print e
try:
from okcoinGateway.okcoinGateway import OkcoinGateway
self.addGateway(OkcoinGateway, 'OKCOIN')
self.gatewayDict['OKCOIN'].setQryEnabled(True)
except Exception, e:
print e
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def addGateway(self, gateway, gatewayName=None): def addGateway(self, gateway, gatewayName=None):
"""创建接口""" """创建接口"""