Merge pull request #399 from vnpy/dev

v1.7.0 beta1
This commit is contained in:
vn.py 2017-07-10 21:58:02 +08:00 committed by GitHub
commit 53e2dc0e3e
113 changed files with 3305 additions and 294 deletions

View File

@ -37,7 +37,8 @@ Using the vn.py project, institutional investors and professional traders, such
- LTSvn.lts
- QDPvn.qdp
- CSHSHLPvn.cshshlp
**Chinese Precious Metal Market**

View File

Before

Width:  |  Height:  |  Size: 618 KiB

After

Width:  |  Height:  |  Size: 618 KiB

View File

Before

Width:  |  Height:  |  Size: 234 KiB

After

Width:  |  Height:  |  Size: 234 KiB

View File

Before

Width:  |  Height:  |  Size: 465 KiB

After

Width:  |  Height:  |  Size: 465 KiB

View File

Before

Width:  |  Height:  |  Size: 465 KiB

After

Width:  |  Height:  |  Size: 465 KiB

View File

Before

Width:  |  Height:  |  Size: 320 KiB

After

Width:  |  Height:  |  Size: 320 KiB

View File

Before

Width:  |  Height:  |  Size: 319 KiB

After

Width:  |  Height:  |  Size: 319 KiB

View File

@ -61,7 +61,7 @@ RUN echo "开始配置系vnpy环境" \
&& echo "更改 pip 源" \
&& mkdir ~/.pip \
&& echo "[global]" >> ~/.pip/pip.conf \
&& echo "index-url = http://pypi.douban.com/simple" >> ~/.pip/pip.conf \
&& echo "index-url = https://pypi.doubanio.com/simple/" >> ~/.pip/pip.conf \
&& echo "使用 pip 安装 python 库" \
&& pip install TA-Lib \
&& conda clean -ay \
@ -70,4 +70,4 @@ RUN echo "开始配置系vnpy环境" \
WORKDIR /srv/vnpy
# 暂时不设置入口点,否则不能使用 -it 交互模式
# ENTRYPOINT python /srv/vnpy/vn.trader/vtServer.py
# ENTRYPOINT python /srv/vnpy/vn.trader/vtServer.py

View File

@ -79,13 +79,13 @@
"name": "stdout",
"output_type": "stream",
"text": [
"2017-06-02 16:07:31.265000\t开始载入数据\n",
"2017-06-02 16:07:31.423000\t载入完成数据量331890\n",
"2017-06-02 16:07:31.423000\t开始回测\n",
"2017-06-02 16:07:31.441000\t策略初始化完成\n",
"2017-06-02 16:07:31.441000\t策略启动完成\n",
"2017-06-02 16:07:31.442000\t开始回放数据\n",
"2017-06-02 16:07:54.738000\t数据回放结束\n"
"2017-07-06 23:15:17.471000\t开始载入数据\n",
"2017-07-06 23:15:17.485000\t载入完成数据量0\n",
"2017-07-06 23:15:17.485000\t开始回测\n",
"2017-07-06 23:15:17.485000\t策略初始化完成\n",
"2017-07-06 23:15:17.485000\t策略启动完成\n",
"2017-07-06 23:15:17.485000\t开始回放数据\n",
"2017-07-06 23:15:17.486000\t数据回放结束\n"
]
}
],

View File

@ -0,0 +1,19 @@
[
{
"name": "double ema",
"className": "EmaDemoStrategy",
"vtSymbol": "IF1706"
},
{
"name": "atr rsi",
"className": "AtrRsiStrategy",
"vtSymbol": "IC1706"
},
{
"name": "king keltner",
"className": "KkStrategy",
"vtSymbol": "IH1706"
}
]

View File

@ -0,0 +1,7 @@
{
"brokerID": "9999",
"mdAddress": "tcp://180.168.146.187:10011",
"tdAddress": "tcp://180.168.146.187:10001",
"userID": "simnow申请",
"password": "simnow申请"
}

View File

@ -0,0 +1,119 @@
# encoding: UTF-8
import multiprocessing
from time import sleep
from datetime import datetime, time
from vnpy.event import EventEngine2
from vnpy.trader.vtEvent import EVENT_LOG
from vnpy.trader.vtEngine import MainEngine
from vnpy.trader.gateway import ctpGateway
from vnpy.trader.app import ctaStrategy
from vnpy.trader.app.ctaStrategy.ctaBase import EVENT_CTA_LOG
#----------------------------------------------------------------------
def printLog(content):
"""输出日志"""
t = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print '%s\t%s' %(t, content)
#----------------------------------------------------------------------
def processLogEvent(event):
"""处理日志事件"""
log = event.dict_['data']
if log.gatewayName:
content = '%s:%s' %(log.gatewayName, log.logContent)
else:
content = '%s:%s' %('MainEngine', log.logContent)
printLog(content)
#----------------------------------------------------------------------
def processCtaLogEvent(event):
"""处理CTA模块日志事件"""
log = event.dict_['data']
content = '%s:%s' %('CTA Engine', log.logContent)
printLog(content)
#----------------------------------------------------------------------
def runChildProcess():
"""子进程运行函数"""
print '-'*20
printLog(u'启动CTA策略运行子进程')
ee = EventEngine2()
printLog(u'事件引擎创建成功')
me = MainEngine(ee)
me.addGateway(ctpGateway)
me.addApp(ctaStrategy)
printLog(u'主引擎创建成功')
ee.register(EVENT_LOG, processLogEvent)
ee.register(EVENT_CTA_LOG, processCtaLogEvent)
printLog(u'注册日志事件监听')
me.connect('CTP')
printLog(u'连接CTP接口')
sleep(5) # 等待CTP接口初始化
cta = me.appDict[ctaStrategy.appName]
cta.loadSetting()
printLog(u'CTA策略载入成功')
cta.initAll()
printLog(u'CTA策略初始化成功')
cta.startAll()
printLog(u'CTA策略启动成功')
while True:
sleep(1)
#----------------------------------------------------------------------
def runParentProcess():
"""父进程运行函数"""
printLog(u'启动CTA策略守护父进程')
DAY_START = time(8, 45) # 日盘启动和停止时间
DAY_END = time(15, 30)
NIGHT_START = time(20, 45) # 夜盘启动和停止时间
NIGHT_END = time(2, 45)
p = None # 子进程句柄
while True:
currentTime = datetime.now().time()
recording = False
# 判断当前处于的时间段
if ((currentTime >= DAY_START and currentTime <= DAY_END) or
(currentTime >= NIGHT_START) or
(currentTime <= NIGHT_END)):
recording = True
# 记录时间则需要启动子进程
if recording and p is None:
printLog(u'启动子进程')
p = multiprocessing.Process(target=runChildProcess)
p.start()
printLog(u'子进程启动成功')
# 非记录时间则退出子进程
if not recording and p is not None:
printLog(u'关闭子进程')
p.terminate()
p.join()
p = None
printLog(u'子进程关闭成功')
sleep(5)
if __name__ == '__main__':
runChildProcess()
# 尽管同样实现了无人值守但强烈建议每天启动时人工检查为自己的PNL负责
# runParentProcess()

View File

@ -0,0 +1,7 @@
{
"brokerID": "9999",
"mdAddress": "tcp://180.168.146.187:10011",
"tdAddress": "tcp://180.168.146.187:10001",
"userID": "simnow申请",
"password": "simnow申请"
}

View File

@ -0,0 +1,18 @@
{
"working": true,
"tick":
[
],
"bar":
[
["BTC_CNY_SPOT", "OKCOIN"],
["LTC_CNY_SPOT", "OKCOIN"],
["ETH_CNY_SPOT", "OKCOIN"]
],
"active":
{
}
}

View File

@ -0,0 +1,95 @@
# encoding: UTF-8
import multiprocessing
from time import sleep
from datetime import datetime, time
from vnpy.event import EventEngine2
from vnpy.trader.vtEvent import EVENT_LOG
from vnpy.trader.vtEngine import MainEngine
from vnpy.trader.gateway import ctpGateway
from vnpy.trader.app import dataRecorder
#----------------------------------------------------------------------
def printLog(content):
"""输出日志"""
t = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print '%s\t%s' %(t, content)
#----------------------------------------------------------------------
def processLogEvent(event):
"""处理日志事件"""
log = event.dict_['data']
if log.gatewayName:
content = '%s:%s' %(log.gatewayName, log.logContent)
else:
content = '%s:%s' %('MainEngine', log.logContent)
printLog(content)
#----------------------------------------------------------------------
def runChildProcess():
"""子进程运行函数"""
print '-'*20
printLog(u'启动行情记录运行子进程')
ee = EventEngine2()
printLog(u'事件引擎创建成功')
me = MainEngine(ee)
me.addGateway(ctpGateway)
me.addApp(dataRecorder)
printLog(u'主引擎创建成功')
ee.register(EVENT_LOG, processLogEvent)
printLog(u'注册日志事件监听')
me.connect('CTP')
printLog(u'连接CTP接口')
while True:
sleep(1)
#----------------------------------------------------------------------
def runParentProcess():
"""父进程运行函数"""
printLog(u'启动行情记录守护父进程')
DAY_START = time(8, 57) # 日盘启动和停止时间
DAY_END = time(15, 18)
NIGHT_START = time(20, 57) # 夜盘启动和停止时间
NIGHT_END = time(2, 33)
p = None # 子进程句柄
while True:
currentTime = datetime.now().time()
recording = False
# 判断当前处于的时间段
if ((currentTime >= DAY_START and currentTime <= DAY_END) or
(currentTime >= NIGHT_START) or
(currentTime <= NIGHT_END)):
recording = True
# 记录时间则需要启动子进程
if recording and p is None:
printLog(u'启动子进程')
p = multiprocessing.Process(target=runChildProcess)
p.start()
printLog(u'子进程启动成功')
# 非记录时间则退出子进程
if not recording and p is not None:
printLog(u'关闭子进程')
p.terminate()
p.join()
p = None
printLog(u'子进程关闭成功')
sleep(5)
if __name__ == '__main__':
runChildProcess()
#runParentProcess()

11
examples/README.md Normal file
View File

@ -0,0 +1,11 @@
# vn.py项目的应用示例
本文件夹中的内容主要是关于如何在交易业务中使用vn.py的示例
* VnTrader最常用的vn.py图形交易界面
* DataRecording全自动行情记录工具无需用户每日定时重启
* CtaTrading无图形界面模式的CTA策略交易
* CtaBacktestingCTA策略的回测和优化

View File

@ -0,0 +1,11 @@
{
"MONGO_HOST": "localhost",
"MONGO_PORT": 27017,
"SHCIFCO_IP": "180.169.126.123",
"SHCIFCO_PORT": "45065",
"SHCIFCO_TOKEN": "请联系上海中期申请",
"SYMBOLS": ["cu1707", "cu1708", "cu1709", "cu1712",
"m1707", "m1708", "m1709", "m1712"]
}

View File

@ -0,0 +1,91 @@
# encoding: UTF-8
import json
import time
import datetime
import random
from pymongo import MongoClient, ASCENDING
from vnpy.data.shcifco.vnshcifco import ShcifcoApi, PERIOD_1MIN
from vnpy.trader.vtObject import VtBarData
from vnpy.trader.app.ctaStrategy.ctaBase import MINUTE_DB_NAME
# 加载配置
config = open('config.json')
setting = json.load(config)
MONGO_HOST = setting['MONGO_HOST']
MONGO_PORT = setting['MONGO_PORT']
SHCIFCO_IP = setting['SHCIFCO_IP']
SHCIFCO_PORT = setting['SHCIFCO_PORT']
SHCIFCO_TOKEN = setting['SHCIFCO_TOKEN']
SYMBOLS = setting['SYMBOLS']
api = ShcifcoApi(SHCIFCO_IP, SHCIFCO_PORT, SHCIFCO_TOKEN) # 历史行情服务API对象
mc = MongoClient(MONGO_HOST, MONGO_PORT) # Mongo连接
db = mc[MINUTE_DB_NAME] # 数据库
#----------------------------------------------------------------------
def generateVtBar(d):
"""生成K线"""
bar = VtBarData()
bar.symbol = d['symbol']
bar.vtSymbol = d['symbol']
bar.date = d['date']
bar.time = ':'.join([d['time'][:2], d['time'][2:]])
bar.open = d['open']
bar.high = d['high']
bar.low = d['low']
bar.close = d['close']
bar.volume = d['volume']
bar.openInterest = d['openInterest']
bar.datetime = datetime.datetime.strptime(' '.join([bar.date, bar.time]), '%Y%m%d %H:%M')
return bar
#----------------------------------------------------------------------
def downMinuteBarBySymbol(symbol, num):
"""下载某一合约的分钟线数据"""
start = time.time()
cl = db[symbol] # 集合
cl.ensure_index([('datetime', ASCENDING)], unique=True) # 添加索引
l = api.getHisBar(symbol, num, period=PERIOD_1MIN)
if not l:
print u'%s数据下载失败' %symbol
return
for d in l:
bar = generateVtBar(d)
d = bar.__dict__
flt = {'datetime': bar.datetime}
cl.replace_one(flt, d, True)
end = time.time()
cost = (end - start) * 1000
print u'合约%s数据下载完成%s - %s,耗时%s毫秒' %(symbol, generateVtBar(l[0]).datetime,
generateVtBar(l[-1]).datetime, cost)
#----------------------------------------------------------------------
def downloadAllMinuteBar(num):
"""下载所有配置中的合约的分钟线数据"""
print '-' * 50
print u'开始下载合约分钟线数据'
print '-' * 50
for symbol in SYMBOLS:
downMinuteBarBySymbol(symbol, num)
time.sleep(1)
print '-' * 50
print u'合约分钟线数据下载完成'
print '-' * 50

View File

@ -0,0 +1,11 @@
# encoding: UTF-8
"""
立即下载数据到数据库中用于手动执行更新操作
"""
from dataService import *
if __name__ == '__main__':
downloadAllMinuteBar(1000)

View File

@ -0,0 +1,30 @@
# encoding: UTF-8
"""
定时服务可无人值守运行实现每日自动下载更新历史行情数据到数据库中
"""
from dataService import *
if __name__ == '__main__':
taskCompletedDate = None
# 生成一个随机的任务下载时间,用于避免所有用户在同一时间访问数据服务器
taskTime = datetime.time(hour=17, minute=random.randint(1,59))
# 进入主循环
while True:
t = datetime.datetime.now()
# 每天到达任务下载时间后,执行数据下载的操作
if t.time() > taskTime and (taskCompletedDate is None or t.date() != taskCompletedDate):
# 下载1000根分钟线数据足以覆盖过去两天的行情
downloadAllMinuteBar(1000)
# 更新任务完成的日期
taskCompletedDate = t.date()
else:
print u'当前时间%s,任务定时%s' %(t, taskTime)
time.sleep(60)

View File

@ -0,0 +1,19 @@
[
{
"name": "double ema",
"className": "EmaDemoStrategy",
"vtSymbol": "IF1706"
},
{
"name": "atr rsi",
"className": "AtrRsiStrategy",
"vtSymbol": "IC1706"
},
{
"name": "king keltner",
"className": "KkStrategy",
"vtSymbol": "IH1706"
}
]

View File

@ -0,0 +1,7 @@
{
"brokerID": "9999",
"mdAddress": "tcp://180.168.146.187:10011",
"tdAddress": "tcp://180.168.146.187:10001",
"userID": "simnow申请",
"password": "simnow申请"
}

View File

@ -0,0 +1,18 @@
{
"working": true,
"tick":
[
],
"bar":
[
["BTC_CNY_SPOT", "OKCOIN"],
["LTC_CNY_SPOT", "OKCOIN"],
["ETH_CNY_SPOT", "OKCOIN"]
],
"active":
{
}
}

View File

@ -0,0 +1,7 @@
{
"brokerID": "0110",
"tdAddress": "tcp://118.126.16.229:17111",
"password": "飞马账户请自行联系期货公司申请",
"mdAddress": "tcp://118.126.16.229:17101",
"userID": "飞马账户请自行联系期货公司申请"
}

View File

@ -0,0 +1,7 @@
{
"accessKey": "火币网站申请",
"secretKey": "火币网站申请",
"interval": 0.5,
"market": "cny",
"debug": false
}

View File

@ -0,0 +1,6 @@
{
"host": "localhost",
"port": 7497,
"clientId": 888,
"accountCode": "DU545254"
}

View File

@ -0,0 +1,5 @@
{
"token": "请在OANDA网站申请",
"accountId": "请在OANDA网站申请",
"settingName": "practice"
}

View File

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

View File

@ -0,0 +1,9 @@
{
"orderFlowClear": 1,
"orderCancelLimit": 10,
"workingOrderLimit": 20,
"tradeLimit": 1000,
"orderSizeLimit": 100,
"active": true,
"orderFlowLimit": 50
}

View File

@ -0,0 +1,7 @@
{
"brokerID": "9999",
"tdAddress": "tcp://140.206.81.6:37776",
"password": "888888",
"mdAddress": "tcp://140.206.81.6:37777",
"userID": "0600035"
}

View File

@ -0,0 +1,8 @@
{
"frontAddress": "222.73.119.230",
"frontPort": 7003,
"marketAddress": "222.73.119.230",
"marketPort": 9003,
"userId": "demo000604",
"userPwd": "888888"
}

View File

@ -0,0 +1,85 @@
[
{
"name": "m.09-01",
"activeLeg":
{
"vtSymbol": "m1709",
"ratio": 1,
"multiplier": 1.0,
"payup": 2
},
"passiveLegs": [
{
"vtSymbol": "m1801",
"ratio": -1,
"multiplier": -1.0,
"payup": 2
}
]
},
{
"name": "IF.07-09",
"activeLeg":
{
"vtSymbol": "IF1707",
"ratio": 1,
"multiplier": 1.0,
"payup": 2
},
"passiveLegs": [
{
"vtSymbol": "IF1709",
"ratio": -1,
"multiplier": -1.0,
"payup": 2
}
]
},
{
"name": "IH.07-09",
"activeLeg":
{
"vtSymbol": "IH1707",
"ratio": 1,
"multiplier": 1.0,
"payup": 2
},
"passiveLegs": [
{
"vtSymbol": "IH1709",
"ratio": -1,
"multiplier": -1.0,
"payup": 2
}
]
},
{
"name": "IC.07-09",
"activeLeg":
{
"vtSymbol": "IC1707",
"ratio": 1,
"multiplier": 1.0,
"payup": 2
},
"passiveLegs": [
{
"vtSymbol": "IC1709",
"ratio": -1,
"multiplier": -1.0,
"payup": 2
}
]
}
]

View File

@ -0,0 +1,11 @@
{
"fontFamily": "微软雅黑",
"fontSize": 12,
"mongoHost": "localhost",
"mongoPort": 27017,
"mongoLogging": true,
"darkStyle": true,
"language": "chinese"
}

View File

@ -0,0 +1,6 @@
{
"tdAddress": "tcp://203.187.171.250:10910",
"password": "请联系飞创申请",
"mdAddress": "tcp://203.187.171.250:10915",
"accountID": "请联系飞创申请"
}

View File

@ -17,8 +17,7 @@ from vnpy.trader.gateway import (ctpGateway, femasGateway, xspeedGateway,
shzdGateway, huobiGateway, okcoinGateway)
# 加载上层应用
from vnpy.trader.app import (riskManager, dataRecorder,
ctaStrategy)
from vnpy.trader.app import (riskManager, ctaStrategy, spreadTrading)
#----------------------------------------------------------------------
@ -43,8 +42,8 @@ def main():
# 添加上层应用
me.addApp(riskManager)
me.addApp(dataRecorder)
me.addApp(ctaStrategy)
me.addApp(spreadTrading)
# 创建主窗口
mw = MainWindow(me, ee)

View File

@ -12,6 +12,6 @@ cmake ..
make VERBOSE=1 -j 1
ln -fs `pwd`/lib/vnctpmd.so ../vnctpmd/test/vnctpmd.so
ln -fs `pwd`/lib/vnctptd.so ../vnctptd/test/vnctptd.so
cp ../vnctpmd/test/vnctpmd.* ../../vn.trader/ctpGateway/
cp ../vnctptd/test/vnctptd.* ../../vn.trader/ctpGateway/
cp ../vnctpmd/test/vnctpmd.* ../../../vn.trader/gateway/ctpGateway/
cp ../vnctptd/test/vnctptd.* ../../../vn.trader/gateway/ctpGateway/
popd

View File

@ -20,6 +20,7 @@ CURRENCY_USD = 'usd'
# 电子货币代码
SYMBOL_BTC = 'btc'
SYMBOL_LTC = 'ltc'
SYMBOL_ETH = 'eth'
# 行情深度
DEPTH_20 = 20
@ -42,6 +43,7 @@ INTERVAL_1W = 'week'
# 交易代码,需要后缀货币名才能完整
TRADING_SYMBOL_BTC = 'btc_'
TRADING_SYMBOL_LTC = 'ltc_'
TRADING_SYMBOL_ETH = 'eth_'
# 委托类型
TYPE_BUY = 'buy'

5
vnpy/data/README.md Normal file
View File

@ -0,0 +1,5 @@
# vn.data - 数据相关工具
### 历史数据
* datayes通联数据接口
* shcifco上海中期接口

0
vnpy/data/__init__.py Normal file
View File

View File

@ -0,0 +1 @@
from vndatayes import DatayesApi

View File

@ -0,0 +1,52 @@
# encoding: UTF-8
'''一个简单的通联数据客户端主要使用requests开发比通联官网的python例子更为简洁。'''
import os
import requests
import json
HTTP_OK = 200
########################################################################
class DatayesApi(object):
"""通联数据API"""
#----------------------------------------------------------------------
def __init__(self, token,
domain="http://api.wmcloud.com/data",
version="v1"):
"""Constructor"""
self.domain = domain # 主域名
self.version = version # API版本
self.token = token # 授权码
self.header = {} # http请求头部
self.header['Connection'] = 'keep_alive'
self.header['Authorization'] = 'Bearer ' + self.token
#----------------------------------------------------------------------
def downloadData(self, path, params):
"""下载数据"""
url = '/'.join([self.domain, self.version, path])
r = requests.get(url=url, headers=self.header, params=params)
if r.status_code != HTTP_OK:
print u'http请求失败状态代码%s' %r.status_code
return None
else:
result = r.json()
if 'retMsg' in result and result['retMsg'] == 'Success':
return result['data']
else:
if 'retMsg' in result:
print u'查询失败,返回信息%s' %result['retMsg']
elif 'message' in result:
print u'查询失败,返回信息%s' %result['message']
return None

View File

27
vnpy/data/shcifco/test.py Normal file
View File

@ -0,0 +1,27 @@
# encoding: UTF-8
from vnshcifco import ShcifcoApi, PERIOD_1MIN
if __name__ == "__main__":
ip = '180.169.126.123'
port = '45065'
token = '请联系上海中期申请'
symbol = 'cu1709'
# 创建API对象
api = ShcifcoApi(ip, port, token)
# 获取最新tick
print api.getLastTick(symbol)
# 获取最新价格
print api.getLastPrice(symbol)
# 获取最新分钟线
print api.getLastBar(symbol)
# 获取历史分钟线
print api.getHisBar(symbol, 500, period=PERIOD_1MIN)

View File

@ -0,0 +1,147 @@
# encoding: UTF-8
import requests
HTTP_OK = 200
PERIOD_1MIN = '1m'
PERIOD_5MIN = '5m'
PERIOD_15MIN = '15m'
PERIOD_60MIN = '60m'
PERIOD_1DAY = '1d'
########################################################################
class ShcifcoApi(object):
"""数据接口"""
#----------------------------------------------------------------------
def __init__(self, ip, port, token):
"""Constructor"""
self.ip = ip
self.port = port
self.token = token
self.service = 'ShcifcoApi'
self.domain = 'http://' + ':'.join([self.ip, self.port])
#----------------------------------------------------------------------
def getData(self, path, params):
"""下载数据"""
url = '/'.join([self.domain, self.service, path])
params['token'] = self.token
r = requests.get(url=url, params=params)
if r.status_code != HTTP_OK:
print u'http请求失败状态代码%s' %r.status_code
return None
else:
return r.text
#----------------------------------------------------------------------
def getLastTick(self, symbol):
"""获取最新Tick"""
path = 'lasttick'
params = {'ids': symbol}
data = self.getData(path, params)
if not data:
return None
data = data.split(';')[0]
l = data.split(',')
d = {
'symbol': l[0],
'lastPrice': float(l[1]),
'bidPrice': float(l[2]),
'bidVolume': int(l[3]),
'askPrice': float(l[4]),
'askVolume': int(l[5]),
'volume': int(l[6]),
'openInterest': int(l[7])
}
return d
#----------------------------------------------------------------------
def getLastPrice(self, symbol):
"""获取最新成交价"""
path = 'lastprice'
params = {'ids': symbol}
data = self.getData(path, params)
if not data:
return None
data = data.split(';')[0]
price = float(data)
return price
#----------------------------------------------------------------------
def getLastBar(self, symbol):
"""获取最新的一分钟K线数据"""
path = 'lastbar'
params = {'id': symbol}
data = self.getData(path, params)
if not data:
return None
data = data.split(';')[0]
l = data.split(',')
d = {
'symbol': l[0],
'time': l[1],
'open': float(l[2]),
'high': float(l[3]),
'low': float(l[4]),
'close': float(l[5]),
'volume': int(l[6]),
'openInterest': int(l[7])
}
return d
#----------------------------------------------------------------------
def getHisBar(self, symbol, num, date='', period=''):
"""获取历史K线数据"""
path = 'hisbar'
# 默认参数
params = {
'id': symbol,
'num': num
}
# 可选参数
if date:
params['date'] = date
if period:
params['period'] = period
data = self.getData(path, params)
if not data:
return None
barList = []
l = data.split(';')
for barStr in l:
# 过滤某些空数据
if ',' not in barStr:
continue
barData = barStr.split(',')
d = {
'symbol': barData[0],
'date': barData[1],
'time': barData[2],
'open': float(barData[3]),
'high': float(barData[4]),
'low': float(barData[5]),
'close': float(barData[6]),
'volume': int(barData[7]),
'openInterest': int(barData[8])
}
barList.append(d)
return barList

View File

@ -180,7 +180,7 @@ class RpcServer(RpcObject):
def publish(self, topic, data):
"""
广播推送数据
topic主题内容
topic主题内容注意必须是ascii编码
data具体的数据
"""
# 序列化数据
@ -294,6 +294,8 @@ class RpcClient(RpcObject):
订阅特定主题的广播数据
可以使用topic=''来订阅所有的主题
注意topic必须是ascii编码
"""
self.__socketSUB.setsockopt(zmq.SUBSCRIBE, topic)

View File

@ -139,7 +139,7 @@ class BacktestingEngine(object):
# 载入初始化需要用的数据
flt = {'datetime':{'$gte':self.dataStartDate,
'$lt':self.strategyStartDate}}
initCursor = collection.find(flt)
initCursor = collection.find(flt).sort('datetime')
# 将数据从查询指针中读取出,并生成列表
self.initData = [] # 清空initData列表
@ -154,7 +154,7 @@ class BacktestingEngine(object):
else:
flt = {'datetime':{'$gte':self.strategyStartDate,
'$lte':self.dataEndDate}}
self.dbCursor = collection.find(flt)
self.dbCursor = collection.find(flt).sort('datetime')
self.output(u'载入完成,数据量:%s' %(initCursor.count() + self.dbCursor.count()))
@ -228,7 +228,6 @@ class BacktestingEngine(object):
order.vtSymbol = vtSymbol
order.price = self.roundToPriceTick(price)
order.totalVolume = volume
order.status = STATUS_NOTTRADED # 刚提交尚未成交
order.orderID = orderID
order.vtOrderID = orderID
order.orderTime = str(self.dt)
@ -273,8 +272,8 @@ class BacktestingEngine(object):
so.price = self.roundToPriceTick(price)
so.volume = volume
so.strategy = strategy
so.stopOrderID = stopOrderID
so.status = STOPORDER_WAITING
so.stopOrderID = stopOrderID
if orderType == CTAORDER_BUY:
so.direction = DIRECTION_LONG
@ -293,6 +292,9 @@ class BacktestingEngine(object):
self.stopOrderDict[stopOrderID] = so
self.workingStopOrderDict[stopOrderID] = so
# 推送停止单初始更新
self.strategy.onStopOrder(so)
return stopOrderID
#----------------------------------------------------------------------
@ -303,6 +305,7 @@ class BacktestingEngine(object):
so = self.workingStopOrderDict[stopOrderID]
so.status = STOPORDER_CANCELLED
del self.workingStopOrderDict[stopOrderID]
self.strategy.onStopOrder(so)
#----------------------------------------------------------------------
def crossLimitOrder(self):
@ -321,6 +324,11 @@ class BacktestingEngine(object):
# 遍历限价单字典中的所有限价单
for orderID, order in self.workingLimitOrderDict.items():
# 推送委托进入队列(未成交)的状态更新
if not order.status:
order.status = STATUS_NOTTRADED
self.strategy.onOrder(order)
# 判断是否会成交
buyCross = (order.direction==DIRECTION_LONG and
order.price>=buyCrossPrice and
@ -391,6 +399,11 @@ class BacktestingEngine(object):
# 如果发生了成交
if buyCross or sellCross:
# 更新停止单状态,并从字典中删除该停止单
so.status = STOPORDER_TRIGGERED
if stopOrderID in self.workingStopOrderDict:
del self.workingStopOrderDict[stopOrderID]
# 推送成交数据
self.tradeCount += 1 # 成交编号自增1
tradeID = str(self.tradeCount)
@ -410,19 +423,15 @@ class BacktestingEngine(object):
orderID = str(self.limitOrderCount)
trade.orderID = orderID
trade.vtOrderID = orderID
trade.direction = so.direction
trade.offset = so.offset
trade.volume = so.volume
trade.tradeTime = str(self.dt)
trade.dt = self.dt
self.strategy.onTrade(trade)
self.tradeDict[tradeID] = trade
# 推送委托数据
so.status = STOPORDER_TRIGGERED
order = VtOrderData()
order.vtSymbol = so.vtSymbol
order.symbol = so.vtSymbol
@ -435,14 +444,14 @@ class BacktestingEngine(object):
order.tradedVolume = so.volume
order.status = STATUS_ALLTRADED
order.orderTime = trade.tradeTime
self.strategy.onOrder(order)
self.limitOrderDict[orderID] = order
# 从字典中删除该限价单
if stopOrderID in self.workingStopOrderDict:
del self.workingStopOrderDict[stopOrderID]
# 按照顺序推送数据
self.strategy.onStopOrder(so)
self.strategy.onOrder(order)
self.strategy.onTrade(trade)
#----------------------------------------------------------------------
def insertData(self, dbName, collectionName, data):
"""考虑到回测中不允许向数据库插入数据,防止实盘交易中的一些代码出错"""
@ -812,7 +821,7 @@ class BacktestingEngine(object):
l.append(pool.apply_async(optimize, (strategyClass, setting,
targetName, self.mode,
self.startDate, self.initDays, self.endDate,
self.slippage, self.rate, self.size,
self.slippage, self.rate, self.size, self.priceTick,
self.dbName, self.symbol)))
pool.close()
pool.join()
@ -929,7 +938,7 @@ def formatNumber(n):
#----------------------------------------------------------------------
def optimize(strategyClass, setting, targetName,
mode, startDate, initDays, endDate,
slippage, rate, size,
slippage, rate, size, priceTick,
dbName, symbol):
"""多进程优化时跑在每个进程中运行的函数"""
engine = BacktestingEngine()
@ -939,6 +948,7 @@ def optimize(strategyClass, setting, targetName,
engine.setSlippage(slippage)
engine.setRate(rate)
engine.setSize(size)
engine.setPriceTick(priceTick)
engine.setDatabase(dbName, symbol)
engine.initStrategy(strategyClass, setting)

View File

@ -33,6 +33,10 @@ MINUTE_DB_NAME = 'VnTrader_1Min_Db'
ENGINETYPE_BACKTESTING = 'backtesting' # 回测
ENGINETYPE_TRADING = 'trading' # 实盘
# CTA模块事件
EVENT_CTA_LOG = 'eCtaLog' # CTA相关的日志事件
EVENT_CTA_STRATEGY = 'eCtaStrategy.' # CTA策略状态变化事件
# CTA引擎中涉及的数据类定义
from vnpy.trader.vtConstant import EMPTY_UNICODE, EMPTY_STRING, EMPTY_FLOAT, EMPTY_INT

View File

@ -29,10 +29,10 @@ from vnpy.trader.vtEvent import *
from vnpy.trader.vtConstant import *
from vnpy.trader.vtObject import VtTickData, VtBarData
from vnpy.trader.vtGateway import VtSubscribeReq, VtOrderReq, VtCancelOrderReq, VtLogData
from vnpy.trader.vtFunction import todayDate
from vnpy.trader.vtFunction import todayDate, getJsonPath
from vnpy.trader.app.ctaStrategy.ctaBase import *
from vnpy.trader.app.ctaStrategy.strategy import STRATEGY_CLASS
from .ctaBase import *
from .strategy import STRATEGY_CLASS
@ -41,8 +41,7 @@ from vnpy.trader.app.ctaStrategy.strategy import STRATEGY_CLASS
class CtaEngine(object):
"""CTA策略引擎"""
settingFileName = 'CTA_setting.json'
path = os.path.abspath(os.path.dirname(__file__))
settingFileName = os.path.join(path, settingFileName)
settingfilePath = getJsonPath(settingFileName, __file__)
#----------------------------------------------------------------------
def __init__(self, mainEngine, eventEngine):
@ -211,6 +210,9 @@ class CtaEngine(object):
self.stopOrderDict[stopOrderID] = so
self.workingStopOrderDict[stopOrderID] = so
# 推送停止单状态
strategy.onStopOrder(so)
return stopOrderID
#----------------------------------------------------------------------
@ -221,6 +223,7 @@ class CtaEngine(object):
so = self.workingStopOrderDict[stopOrderID]
so.status = STOPORDER_CANCELLED
del self.workingStopOrderDict[stopOrderID]
so.strategy.onStopOrder(so)
#----------------------------------------------------------------------
def processStopOrder(self, tick):
@ -245,6 +248,7 @@ class CtaEngine(object):
so.status = STOPORDER_TRIGGERED
self.sendOrder(so.vtSymbol, so.orderType, price, so.volume, so.strategy)
del self.workingStopOrderDict[so.stopOrderID]
so.strategy.onStopOrder(so)
#----------------------------------------------------------------------
def processTickEvent(self, event):
@ -255,10 +259,15 @@ class CtaEngine(object):
# 推送tick到对应的策略实例进行处理
if tick.vtSymbol in self.tickStrategyDict:
# 添加datetime字段
if not tick.datetime:
tick.datetime = datetime.strptime(' '.join([tick.date, tick.time]), '%Y%m%d %H:%M:%S.%f')
# tick时间可能出现异常数据使用try...except实现捕捉和过滤
try:
# 添加datetime字段
if not tick.datetime:
tick.datetime = datetime.strptime(' '.join([tick.date, tick.time]), '%Y%m%d %H:%M:%S.%f')
except ValueError:
self.writeCtaLog(traceback.format_exc())
return
# 逐个推送到策略实例中
l = self.tickStrategyDict[tick.vtSymbol]
for strategy in l:
@ -337,7 +346,7 @@ class CtaEngine(object):
startDate = self.today - timedelta(days)
d = {'datetime':{'$gte':startDate}}
barData = self.mainEngine.dbQuery(dbName, collectionName, d)
barData = self.mainEngine.dbQuery(dbName, collectionName, d, 'datetime')
l = []
for d in barData:
@ -352,7 +361,7 @@ class CtaEngine(object):
startDate = self.today - timedelta(days)
d = {'datetime':{'$gte':startDate}}
tickData = self.mainEngine.dbQuery(dbName, collectionName, d)
tickData = self.mainEngine.dbQuery(dbName, collectionName, d, 'datetime')
l = []
for d in tickData:
@ -463,12 +472,30 @@ class CtaEngine(object):
if so.strategy is strategy:
self.cancelStopOrder(stopOrderID)
else:
self.writeCtaLog(u'策略实例不存在:%s' %name)
self.writeCtaLog(u'策略实例不存在:%s' %name)
#----------------------------------------------------------------------
def initAll(self):
"""全部初始化"""
for name in self.strategyDict.keys():
self.initStrategy(name)
#----------------------------------------------------------------------
def startAll(self):
"""全部启动"""
for name in self.strategyDict.keys():
self.startStrategy(name)
#----------------------------------------------------------------------
def stopAll(self):
"""全部停止"""
for name in self.strategyDict.keys():
self.stopStrategy(name)
#----------------------------------------------------------------------
def saveSetting(self):
"""保存策略配置"""
with open(self.settingFileName, 'w') as f:
with open(self.settingfilePath, 'w') as f:
l = []
for strategy in self.strategyDict.values():
@ -483,7 +510,7 @@ class CtaEngine(object):
#----------------------------------------------------------------------
def loadSetting(self):
"""读取策略配置"""
with open(self.settingFileName) as f:
with open(self.settingfilePath) as f:
l = json.load(f)
for setting in l:

View File

@ -13,10 +13,11 @@ from multiprocessing.pool import ThreadPool
import pymongo
from vnpy.data.datayes import DatayesApi
from vnpy.trader.vtGlobal import globalSetting
from vnpy.trader.vtConstant import *
from vnpy.trader.vtObject import VtBarData
from vnpy.trader.app.ctaStrategy.datayesClient import DatayesClient
from .ctaBase import SETTING_DB_NAME, TICK_DB_NAME, MINUTE_DB_NAME
# 以下为vn.trader和通联数据规定的交易所代码映射
@ -33,10 +34,10 @@ class HistoryDataEngine(object):
"""CTA模块用的历史数据引擎"""
#----------------------------------------------------------------------
def __init__(self):
def __init__(self, token):
"""Constructor"""
self.dbClient = pymongo.MongoClient(globalSetting['mongoHost'], globalSetting['mongoPort'])
self.datayesClient = DatayesClient()
self.datayesClient = DatayesApi(token)
#----------------------------------------------------------------------
def lastTradeDate(self):

View File

@ -90,6 +90,11 @@ class CtaTemplate(object):
"""收到Bar推送必须由用户继承实现"""
raise NotImplementedError
#----------------------------------------------------------------------
def onStopOrder(self, so):
"""收到停止单推送(必须由用户继承实现)"""
raise NotImplementedError
#----------------------------------------------------------------------
def buy(self, price, volume, stop=False):
"""买开"""

View File

@ -237,3 +237,7 @@ class AtrRsiStrategy(CtaTemplate):
# 发出状态更新事件
self.putEvent()
#----------------------------------------------------------------------
def onStopOrder(self, so):
"""停止单推送"""
pass

View File

@ -221,3 +221,8 @@ class DualThrustStrategy(CtaTemplate):
def onTrade(self, trade):
# 发出状态更新事件
self.putEvent()
#----------------------------------------------------------------------
def onStopOrder(self, so):
"""停止单推送"""
pass

View File

@ -188,6 +188,11 @@ class EmaDemoStrategy(CtaTemplate):
# 对于无需做细粒度委托控制的策略可以忽略onOrder
pass
#----------------------------------------------------------------------
def onStopOrder(self, so):
"""停止单推送"""
pass
########################################################################################
class OrderManagementDemoStrategy(CtaTemplate):
@ -294,3 +299,8 @@ class OrderManagementDemoStrategy(CtaTemplate):
"""收到成交推送(必须由用户继承实现)"""
# 对于无需做细粒度委托控制的策略可以忽略onOrder
pass
#----------------------------------------------------------------------
def onStopOrder(self, so):
"""停止单推送"""
pass

View File

@ -278,3 +278,8 @@ class KkStrategy(CtaTemplate):
# 将委托号记录到列表中
self.orderList.append(self.buyOrderID)
self.orderList.append(self.shortOrderID)
#----------------------------------------------------------------------
def onStopOrder(self, so):
"""停止单推送"""
pass

View File

@ -9,7 +9,8 @@ from vnpy.event import Event
from vnpy.trader.vtEvent import *
from vnpy.trader.uiBasicWidget import QtGui, QtCore, QtWidgets, BasicCell
from vnpy.trader.app.ctaStrategy.language import text
from .ctaBase import EVENT_CTA_LOG, EVENT_CTA_STRATEGY
from .language import text
########################################################################
@ -227,20 +228,17 @@ class CtaEngineManager(QtWidgets.QWidget):
#----------------------------------------------------------------------
def initAll(self):
"""全部初始化"""
for name in self.ctaEngine.strategyDict.keys():
self.ctaEngine.initStrategy(name)
self.ctaEngine.initAll()
#----------------------------------------------------------------------
def startAll(self):
"""全部启动"""
for name in self.ctaEngine.strategyDict.keys():
self.ctaEngine.startStrategy(name)
self.ctaEngine.startAll()
#----------------------------------------------------------------------
def stopAll(self):
"""全部停止"""
for name in self.ctaEngine.strategyDict.keys():
self.ctaEngine.stopStrategy(name)
self.ctaEngine.stopAll()
#----------------------------------------------------------------------
def load(self):
@ -267,13 +265,14 @@ class CtaEngineManager(QtWidgets.QWidget):
#----------------------------------------------------------------------
def closeEvent(self, event):
"""关闭窗口时的事件"""
reply = QtWidgets.QMessageBox.question(self, text.SAVE_POSITION_DATA,
text.SAVE_POSITION_QUESTION, QtWidgets.QMessageBox.Yes |
QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No)
if reply == QtWidgets.QMessageBox.Yes:
self.ctaEngine.savePosition()
if self.isVisible():
reply = QtWidgets.QMessageBox.question(self, text.SAVE_POSITION_DATA,
text.SAVE_POSITION_QUESTION, QtWidgets.QMessageBox.Yes |
QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No)
if reply == QtWidgets.QMessageBox.Yes:
self.ctaEngine.savePosition()
event.accept()

View File

@ -0,0 +1,7 @@
gateway,symbol,exchange,currency,product,tick,bar,active
CTP,IF1709,,,,y,y,IF0000
CTP,IC1709,,,,y,,
CTP,m1709,,,,y,,
SGIT,IH1709,,,,y,y,IH0000
LTS,600036,SZSE,,,y,,
IB,EUR.USD,IDEALPRO,USD,Íâ»ã,y,y,
1 gateway symbol exchange currency product tick bar active
2 CTP IF1709 y y IF0000
3 CTP IC1709 y
4 CTP m1709 y
5 SGIT IH1709 y y IH0000
6 LTS 600036 SZSE y
7 IB EUR.USD IDEALPRO USD Íâ»ã y y

View File

@ -1,32 +1,18 @@
{
"working": false,
"working": true,
"tick":
[
["m1609", "XSPEED"],
["IF1606", "SGIT"],
["IH1606", "SGIT"],
["IH1606", "SGIT"],
["IC1606", "SGIT"],
["IC1606", "SGIT"],
["600036", "LTS", "SZSE"],
["EUR.USD", "IB", "IDEALPRO", "USD", "外汇"]
],
"bar":
[
["IF1605", "SGIT"],
["IF1606", "SGIT"],
["IH1606", "SGIT"],
["IH1606", "SGIT"],
["IC1606", "SGIT"],
["IC1606", "SGIT"]
["BTC_CNY_SPOT", "OKCOIN"],
["LTC_CNY_SPOT", "OKCOIN"],
["ETH_CNY_SPOT", "OKCOIN"]
],
"active":
{
"IF0000": "IF1605",
"IH0000": "IH1605",
"IC0000": "IC1605"
}
}

View File

@ -12,6 +12,8 @@ TICK_DB_NAME = 'VnTrader_Tick_Db'
DAILY_DB_NAME = 'VnTrader_Daily_Db'
MINUTE_DB_NAME = 'VnTrader_1Min_Db'
# 行情记录模块事件
EVENT_DATARECORDER_LOG = 'eDataRecorderLog' # 行情记录日志更新事件
# CTA引擎中涉及的数据类定义
from vnpy.trader.vtConstant import EMPTY_UNICODE, EMPTY_STRING, EMPTY_FLOAT, EMPTY_INT

View File

@ -6,7 +6,8 @@
使用DR_setting.json来配置需要收集的合约以及主力合约代码
'''
import json
#import json
import csv
import os
import copy
from collections import OrderedDict
@ -16,7 +17,7 @@ from threading import Thread
from vnpy.event import Event
from vnpy.trader.vtEvent import *
from vnpy.trader.vtFunction import todayDate
from vnpy.trader.vtFunction import todayDate, getJsonPath
from vnpy.trader.vtObject import VtSubscribeReq, VtLogData, VtBarData, VtTickData
from vnpy.trader.app.dataRecorder.drBase import *
@ -28,8 +29,7 @@ class DrEngine(object):
"""数据记录引擎"""
settingFileName = 'DR_setting.json'
path = os.path.abspath(os.path.dirname(__file__))
settingFileName = os.path.join(path, settingFileName)
settingFilePath = getJsonPath(settingFileName, __file__)
#----------------------------------------------------------------------
def __init__(self, mainEngine, eventEngine):
@ -49,6 +49,9 @@ class DrEngine(object):
# K线对象字典
self.barDict = {}
# 配置字典
self.settingDict = OrderedDict()
# 负责执行数据库插入的单独线程相关
self.active = False # 工作状态
self.queue = Queue() # 队列
@ -57,49 +60,55 @@ class DrEngine(object):
# 载入设置,订阅行情
self.loadSetting()
# 启动数据插入线程
self.start()
# 注册事件监听
self.registerEvent()
#----------------------------------------------------------------------
def loadSetting(self):
"""载入设置"""
"""加载配"""
with open(self.settingFileName) as f:
drSetting = json.load(f)
# 如果working设为False则不启动行情记录功能
working = drSetting['working']
if not working:
return
if 'tick' in drSetting:
l = drSetting['tick']
for setting in l:
symbol = setting[0]
vtSymbol = symbol
req = VtSubscribeReq()
req.symbol = setting[0]
# 针对LTS和IB接口订阅行情需要交易所代码
if len(setting)>=3:
req.exchange = setting[2]
vtSymbol = '.'.join([symbol, req.exchange])
# 针对IB接口订阅行情需要货币和产品类型
if len(setting)>=5:
req.currency = setting[3]
req.productClass = setting[4]
self.mainEngine.subscribe(req, setting[1])
tick = VtTickData() # 该tick实例可以用于缓存部分数据目前未使用
self.tickDict[vtSymbol] = tick
if 'bar' in drSetting:
l = drSetting['bar']
for setting in l:
symbol = setting[0]
vtSymbol = symbol
req = VtSubscribeReq()
req.symbol = symbol
@ -110,31 +119,81 @@ class DrEngine(object):
if len(setting)>=5:
req.currency = setting[3]
req.productClass = setting[4]
self.mainEngine.subscribe(req, setting[1])
bar = VtBarData()
self.barDict[vtSymbol] = bar
if 'active' in drSetting:
d = drSetting['active']
# 注意这里的vtSymbol对于IB和LTS接口应该后缀.交易所
for activeSymbol, vtSymbol in d.items():
self.activeSymbolDict[vtSymbol] = activeSymbol
# 启动数据插入线程
self.start()
# 注册事件监听
self.registerEvent()
self.registerEvent()
##----------------------------------------------------------------------
#def loadCsvSetting(self):
#"""加载CSV配置"""
#with open(self.settingFileName) as f:
#drSetting = csv.DictReader(f)
#for d in drSetting:
## 读取配置
#gatewayName = d['gateway']
#symbol = d['symbol']
#exchange = d['exchange']
#currency = d['currency']
#productClass = d['product']
#recordTick = d['tick']
#recordBar = d['bar']
#activeSymbol = d['active']
#if exchange:
#vtSymbol = '.'.join([symbol, exchange])
#else:
#vtSymbol = symbol
## 订阅行情
#req = VtSubscribeReq()
#req.symbol = symbol
#req.exchange = exchange
#req.currency = currency
#req.productClass = productClass
#self.mainEngine.subscribe(req, gatewayName)
## 设置需要记录的数据
#if recordTick:
#tick = VtTickData()
#self.tickDict[vtSymbol] = VtTickData()
#if recordBar:
#self.barDict[vtSymbol] = VtBarData()
#if activeSymbol:
#self.activeSymbolDict[vtSymbol] = activeSymbol
## 保存配置到缓存中
self.settingDict[vtSymbol] = d
#----------------------------------------------------------------------
def getSetting(self):
"""获取配置"""
return self.settingDict
#----------------------------------------------------------------------
def procecssTickEvent(self, event):
"""处理行情推送"""
tick = event.dict_['data']
vtSymbol = tick.vtSymbol
# 转化Tick格式
if not tick.datetime:
tick.datetime = datetime.strptime(' '.join([tick.date, tick.time]), '%Y%m%d %H:%M:%S.%f')
@ -159,7 +218,9 @@ class DrEngine(object):
bar = self.barDict[vtSymbol]
# 如果第一个TICK或者新的一分钟
if not bar.datetime or bar.datetime.minute != tick.datetime.minute:
if (not bar.datetime or
bar.datetime.minute != tick.datetime.minute or
bar.datetime.hour != tick.datetime.hour):
if bar.vtSymbol:
newBar = copy.copy(bar)
self.insertData(MINUTE_DB_NAME, vtSymbol, newBar)

View File

@ -4,14 +4,10 @@
行情记录模块相关的GUI控制组件
'''
import json
from qtpy import QtWidgets, QtGui, QtCore
from vnpy.event import Event
from vnpy.trader.vtEvent import *
from vnpy.trader.app.dataRecorder.language import text
from vnpy.trader.uiQt import QtWidgets, QtCore
from .drBase import EVENT_DATARECORDER_LOG
from .language import text
########################################################################
@ -119,33 +115,24 @@ class DrEngineManager(QtWidgets.QWidget):
#----------------------------------------------------------------------
def updateSetting(self):
"""显示引擎行情记录配置"""
with open(self.drEngine.settingFileName) as f:
drSetting = json.load(f)
if 'tick' in drSetting:
l = drSetting['tick']
for setting in l:
self.tickTable.insertRow(0)
self.tickTable.setItem(0, 0, TableCell(setting[0]))
self.tickTable.setItem(0, 1, TableCell(setting[1]))
if 'bar' in drSetting:
l = drSetting['bar']
for setting in l:
self.barTable.insertRow(0)
self.barTable.setItem(0, 0, TableCell(setting[0]))
self.barTable.setItem(0, 1, TableCell(setting[1]))
if 'active' in drSetting:
d = drSetting['active']
for activeSymbol, symbol in d.items():
self.activeTable.insertRow(0)
self.activeTable.setItem(0, 0, TableCell(activeSymbol))
self.activeTable.setItem(0, 1, TableCell(symbol))
setting = self.drEngine.getSetting()
for d in setting.values():
if d['tick']:
self.tickTable.insertRow(0)
self.tickTable.setItem(0, 0, TableCell(d['symbol']))
self.tickTable.setItem(0, 1, TableCell(d['gateway']))
if d['bar']:
self.barTable.insertRow(0)
self.barTable.setItem(0, 0, TableCell(d['symbol']))
self.barTable.setItem(0, 1, TableCell(d['gateway']))
if d['active']:
self.activeTable.insertRow(0)
self.activeTable.setItem(0, 0, TableCell(d['active']))
self.activeTable.setItem(0, 1, TableCell(d['symbol']))
self.tickTable.resizeColumnsToContents()
self.barTable.resizeColumnsToContents()
self.activeTable.resizeColumnsToContents()

View File

@ -2,8 +2,8 @@
"orderFlowClear": 1,
"orderCancelLimit": 10,
"workingOrderLimit": 20,
"tradeLimit": 100,
"orderSizeLimit": 10,
"tradeLimit": 1000,
"orderSizeLimit": 100,
"active": true,
"orderFlowLimit": 50
}

View File

@ -15,14 +15,14 @@ from vnpy.event import Event
from vnpy.trader.vtEvent import *
from vnpy.trader.vtConstant import *
from vnpy.trader.vtGateway import VtLogData
from vnpy.trader.vtFunction import getJsonPath
########################################################################
class RmEngine(object):
"""风控引擎"""
settingFileName = 'RM_setting.json'
path = os.path.abspath(os.path.dirname(__file__))
settingFileName = os.path.join(path, settingFileName)
settingFilePath = getJsonPath(settingFileName, __file__)
name = u'风控模块'
@ -64,7 +64,7 @@ class RmEngine(object):
#----------------------------------------------------------------------
def loadSetting(self):
"""读取配置"""
with open(self.settingFileName) as f:
with open(self.settingFilePath) as f:
d = json.load(f)
# 设置风控参数
@ -84,7 +84,7 @@ class RmEngine(object):
#----------------------------------------------------------------------
def saveSetting(self):
"""保存风控参数"""
with open(self.settingFileName, 'w') as f:
with open(self.settingFilePath, 'w') as f:
# 保存风控参数
d = {}

View File

@ -0,0 +1,85 @@
[
{
"name": "m.09-01",
"activeLeg":
{
"vtSymbol": "m1709",
"ratio": 1,
"multiplier": 1.0,
"payup": 2
},
"passiveLegs": [
{
"vtSymbol": "m1801",
"ratio": -1,
"multiplier": -1.0,
"payup": 2
}
]
},
{
"name": "IF.07-09",
"activeLeg":
{
"vtSymbol": "IF1707",
"ratio": 1,
"multiplier": 1.0,
"payup": 2
},
"passiveLegs": [
{
"vtSymbol": "IF1709",
"ratio": -1,
"multiplier": -1.0,
"payup": 2
}
]
},
{
"name": "IH.07-09",
"activeLeg":
{
"vtSymbol": "IH1707",
"ratio": 1,
"multiplier": 1.0,
"payup": 2
},
"passiveLegs": [
{
"vtSymbol": "IH1709",
"ratio": -1,
"multiplier": -1.0,
"payup": 2
}
]
},
{
"name": "IC.07-09",
"activeLeg":
{
"vtSymbol": "IC1707",
"ratio": 1,
"multiplier": 1.0,
"payup": 2
},
"passiveLegs": [
{
"vtSymbol": "IC1709",
"ratio": -1,
"multiplier": -1.0,
"payup": 2
}
]
}
]

View File

@ -0,0 +1,10 @@
# encoding: UTF-8
from .stEngine import StEngine
from .uiStWidget import StManager
appName = 'SpreadTrading'
appDisplayName = u'价差交易'
appEngine = StEngine
appWidget = StManager
appIco = 'st.ico'

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

View File

@ -0,0 +1,517 @@
# encoding: UTF-8
from math import floor
from vnpy.trader.vtConstant import (EMPTY_INT, EMPTY_FLOAT,
EMPTY_STRING, EMPTY_UNICODE,
DIRECTION_LONG, DIRECTION_SHORT,
STATUS_ALLTRADED, STATUS_CANCELLED, STATUS_REJECTED)
########################################################################
class StAlgoTemplate(object):
"""价差算法交易模板"""
MODE_LONGSHORT = u'双向'
MODE_LONGONLY = u'做多'
MODE_SHORTONLY = u'做空'
SPREAD_LONG = 1
SPREAD_SHORT = 2
#----------------------------------------------------------------------
def __init__(self, algoEngine, spread):
"""Constructor"""
self.algoEngine = algoEngine # 算法引擎
self.spreadName = spread.name # 价差名称
self.spread = spread # 价差对象
self.algoName = EMPTY_STRING # 算法名称
self.active = False # 工作状态
self.mode = self.MODE_LONGSHORT # 工作模式
self.buyPrice = EMPTY_FLOAT # 开平仓价格
self.sellPrice = EMPTY_FLOAT
self.shortPrice = EMPTY_FLOAT
self.coverPrice = EMPTY_FLOAT
self.maxPosSize = EMPTY_INT # 最大单边持仓量
self.maxOrderSize = EMPTY_INT # 最大单笔委托量
#----------------------------------------------------------------------
def updateSpreadTick(self, spread):
""""""
raise NotImplementedError
#----------------------------------------------------------------------
def updateSpreadPos(self, spread):
""""""
raise NotImplementedError
#----------------------------------------------------------------------
def updateTrade(self, trade):
""""""
raise NotImplementedError
#----------------------------------------------------------------------
def updateOrder(self, order):
""""""
raise NotImplementedError
#----------------------------------------------------------------------
def updateTimer(self):
""""""
raise NotImplementedError
#----------------------------------------------------------------------
def start(self):
""""""
raise NotImplementedError
#----------------------------------------------------------------------
def stop(self):
""""""
raise NotImplementedError
#----------------------------------------------------------------------
def setBuyPrice(self, buyPrice):
"""设置买开的价格"""
self.buyPrice = buyPrice
#----------------------------------------------------------------------
def setSellPrice(self, sellPrice):
"""设置卖平的价格"""
self.sellPrice = sellPrice
#----------------------------------------------------------------------
def setShortPrice(self, shortPrice):
"""设置卖开的价格"""
self.shortPrice = shortPrice
#----------------------------------------------------------------------
def setCoverPrice(self, coverPrice):
"""设置买平的价格"""
self.coverPrice = coverPrice
#----------------------------------------------------------------------
def setMode(self, mode):
"""设置算法交易方向"""
self.mode = mode
#----------------------------------------------------------------------
def setMaxOrderSize(self, maxOrderSize):
"""设置最大单笔委托数量"""
self.maxOrderSize = maxOrderSize
#----------------------------------------------------------------------
def setMaxPosSize(self, maxPosSize):
"""设置最大持仓数量"""
self.maxPosSize = maxPosSize
#----------------------------------------------------------------------
def putEvent(self):
"""发出算法更新事件"""
self.algoEngine.putAlgoEvent(self)
#----------------------------------------------------------------------
def writeLog(self, content):
"""输出算法日志"""
prefix = ' '.join([self.spreadName, self.algoName])
content = ':'.join([prefix, content])
self.algoEngine.writeLog(content)
#----------------------------------------------------------------------
def getAlgoParams(self):
"""获取算法参数"""
d = {
"spreadName": self.spreadName,
"algoName": self.algoName,
"buyPrice": self.buyPrice,
"sellPrice": self.sellPrice,
"shortPrice": self.shortPrice,
"coverPrice": self.coverPrice,
"maxOrderSize": self.maxOrderSize,
"maxPosSize": self.maxPosSize,
"mode": self.mode
}
return d
#----------------------------------------------------------------------
def setAlgoParams(self, d):
"""设置算法参数"""
self.buyPrice = d.get('buyPrice', EMPTY_FLOAT)
self.sellPrice = d.get('sellPrice', EMPTY_FLOAT)
self.shortPrice = d.get('shortPrice', EMPTY_FLOAT)
self.coverPrice = d.get('coverPrice', EMPTY_FLOAT)
self.maxOrderSize = d.get('maxOrderSize', EMPTY_INT)
self.maxPosSize = d.get('maxPosSize', EMPTY_INT)
self.mode = d.get('mode', self.MODE_LONGSHORT)
########################################################################
class SniperAlgo(StAlgoTemplate):
"""狙击算法(市价委托)"""
FINISHED_STATUS = [STATUS_ALLTRADED, STATUS_CANCELLED, STATUS_REJECTED]
#----------------------------------------------------------------------
def __init__(self, algoEngine, spread):
"""Constructor"""
super(SniperAlgo, self).__init__(algoEngine, spread)
self.algoName = u'Sniper'
self.quoteInterval = 2 # 主动腿报价撤单再发前等待的时间
self.quoteCount = 0 # 报价计数
self.hedgeInterval = 2 # 对冲腿对冲撤单再发前的等待时间
self.hedgeCount = 0 # 对冲计数
self.activeVtSymbol = spread.activeLeg.vtSymbol # 主动腿代码
self.passiveVtSymbols = [leg.vtSymbol for leg in spread.passiveLegs] # 被动腿代码列表
# 缓存每条腿对象的字典
self.legDict = {}
self.legDict[spread.activeLeg.vtSymbol] = spread.activeLeg
for leg in spread.passiveLegs:
self.legDict[leg.vtSymbol] = leg
self.hedgingTaskDict = {} # 被动腿需要对冲的数量字典 vtSymbol:volume
self.legOrderDict = {} # vtSymbol: list of vtOrderID
self.orderTradedDict = {} # vtOrderID: tradedVolume
#----------------------------------------------------------------------
def updateSpreadTick(self, spread):
"""价差行情更新"""
self.spread = spread
# 若算法没有启动则直接返回
if not self.active:
return
# 若当前已有主动腿委托则直接返回
if (self.activeVtSymbol in self.legOrderDict and
self.legOrderDict[self.activeVtSymbol]):
return
# 允许做多
if self.mode == self.MODE_LONGSHORT or self.mode == self.MODE_LONGONLY:
# 买入
if (spread.netPos >= 0 and
spread.netPos < self.maxPosSize and
spread.askPrice <= self.buyPrice):
self.quoteActiveLeg(self.SPREAD_LONG)
self.writeLog(u'买入开仓')
# 卖出
elif (spread.netPos > 0 and
spread.bidPrice >= self.sellPrice):
self.quoteActiveLeg(self.SPREAD_SHORT)
self.writeLog(u'卖出平仓')
# 允许做空
if self.mode == self.MODE_LONGSHORT or self.mode == self.MODE_SHORTONLY:
# 做空
if (spread.netPos <= 0 and
spread.netPos > -self.maxPosSize and
spread.bidPrice >= self.shortPrice):
self.quoteActiveLeg(self.SPREAD_SHORT)
self.writeLog(u'卖出开仓')
# 平空
elif (spread.netPos < 0 and
spread.askPrice <= self.coverPrice):
self.quoteActiveLeg(self.SPREAD_LONG)
self.writeLog(u'买入平仓')
#----------------------------------------------------------------------
def updateSpreadPos(self, spread):
"""价差持仓更新"""
self.spread = spread
#----------------------------------------------------------------------
def updateTrade(self, trade):
"""成交更新"""
pass
#----------------------------------------------------------------------
def updateOrder(self, order):
"""委托更新"""
if not self.active:
return
vtOrderID = order.vtOrderID
vtSymbol = order.vtSymbol
newTradedVolume = order.tradedVolume
lastTradedVolume = self.orderTradedDict.get(vtOrderID, 0)
# 检查是否有新的成交
if newTradedVolume > lastTradedVolume:
self.orderTradedDict[vtOrderID] = newTradedVolume # 缓存委托已经成交数量
volume = newTradedVolume - lastTradedVolume # 计算本次成交数量
if vtSymbol == self.activeVtSymbol:
self.newActiveLegTrade(vtSymbol, order.direction, volume)
else:
self.newPassiveLegTrade(vtSymbol, order.direction, volume)
# 处理完成委托
if order.status in self.FINISHED_STATUS:
vtOrderID = order.vtOrderID
vtSymbol = order.vtSymbol
# 从委托列表中移除该委托
orderList = self.legOrderDict[vtSymbol]
if vtOrderID in orderList:
orderList.remove(vtOrderID)
# 检查若是被动腿,且已经没有未完成委托,则执行对冲
if not orderList and vtSymbol in self.passiveVtSymbols:
self.hedgePassiveLeg(vtSymbol)
#----------------------------------------------------------------------
def updateTimer(self):
"""计时更新"""
if not self.active:
return
self.quoteCount += 1
self.hedgeCount += 1
# 计时到达报价间隔后,则对尚未成交的主动腿委托全部撤单
# 收到撤单回报后清空委托列表,等待下次价差更新再发单
if self.quoteCount > self.quoteInterval:
self.cancelLegOrder(self.activeVtSymbol)
self.quoteCount = 0
# 计时到达对冲间隔后,则对尚未成交的全部被动腿委托全部撤单
# 收到撤单回报后,会自动发送新的对冲委托
if self.hedgeCount > self.hedgeInterval:
self.cancelAllPassiveLegOrders()
self.hedgeCount = 0
#----------------------------------------------------------------------
def start(self):
"""启动"""
# 如果已经运行则直接返回状态
if self.active:
return self.active
# 做多检查
if self.mode != self.MODE_SHORTONLY:
if self.buyPrice >= self.sellPrice:
self.writeLog(u'启动失败允许多头交易时BuyPrice必须小于SellPrice')
return self.active
# 做空检查
if self.mode != self.MODE_LONGONLY:
if self.shortPrice <= self.coverPrice:
self.writeLog(u'启动失败允许空头交易时ShortPrice必须大于CoverPrice')
return self.active
# 多空检查
if self.mode == self.MODE_LONGSHORT:
if self.buyPrice >= self.coverPrice:
self.writeLog(u'启动失败允许双向交易时BuyPrice必须小于CoverPrice')
return self.active
if self.shortPrice <= self.sellPrice:
self.writeLog(u'启动失败允许双向交易时ShortPrice必须大于SellPrice')
return self.active
# 启动算法
self.quoteCount = 0
self.hedgeCount = 0
self.active = True
self.writeLog(u'算法启动')
return self.active
#----------------------------------------------------------------------
def stop(self):
"""停止"""
if self.active:
self.hedgingTaskDict.clear()
self.cancelAllOrders()
self.active = False
self.writeLog(u'算法停止')
return self.active
#----------------------------------------------------------------------
def sendLegOrder(self, leg, legVolume):
"""发送每条腿的委托"""
vtSymbol = leg.vtSymbol
volume = abs(legVolume)
payup = leg.payup
# 发送委托
if legVolume > 0:
price = leg.askPrice
if leg.shortPos > 0:
orderList = self.algoEngine.cover(vtSymbol, price, volume, payup)
else:
orderList = self.algoEngine.buy(vtSymbol, price, volume, payup)
elif legVolume < 0:
price = leg.bidPrice
if leg.longPos > 0:
orderList = self.algoEngine.sell(vtSymbol, price, volume, payup)
else:
orderList = self.algoEngine.short(vtSymbol, price, volume, payup)
# 保存到字典中
if vtSymbol not in self.legOrderDict:
self.legOrderDict[vtSymbol] = orderList
else:
self.legOrderDict[vtSymbol].extend(orderList)
#----------------------------------------------------------------------
def quoteActiveLeg(self, direction):
"""发出主动腿"""
spread = self.spread
# 首先计算不带正负号的价差委托量
if direction == self.SPREAD_LONG:
spreadVolume = min(spread.askVolume,
self.maxPosSize - spread.netPos,
self.maxOrderSize)
# 有价差空头持仓的情况下,则本次委托最多平完空头
if spread.shortPos > 0:
spreadVolume = min(spreadVolume, spread.shortPos)
else:
spreadVolume = min(spread.bidVolume,
self.maxPosSize + spread.netPos,
self.maxOrderSize)
# 有价差多头持仓的情况下,则本次委托最多平完多头
if spread.longPos > 0:
spreadVolume = min(spreadVolume, spread.longPos)
if spreadVolume <= 0:
return
# 加上价差方向
if direction == self.SPREAD_SHORT:
spreadVolume = -spreadVolume
# 计算主动腿委托量
leg = self.legDict[self.activeVtSymbol]
legVolume = spreadVolume * leg.ratio
self.sendLegOrder(leg, legVolume)
self.writeLog(u'发出新的主动腿%s狙击单' %self.activeVtSymbol)
self.quoteCount = 0 # 重置主动腿报价撤单等待计数
#----------------------------------------------------------------------
def hedgePassiveLeg(self, vtSymbol):
"""被动腿对冲"""
if vtSymbol not in self.hedgingTaskDict:
return
orderList = self.legOrderDict.get(vtSymbol, [])
if orderList:
return
legVolume = self.hedgingTaskDict[vtSymbol]
leg = self.legDict[vtSymbol]
self.sendLegOrder(leg, legVolume)
self.writeLog(u'发出新的被动腿%s对冲单' %vtSymbol)
#----------------------------------------------------------------------
def hedgeAllPassiveLegs(self):
"""执行所有被动腿对冲"""
for vtSymbol in self.hedgingTaskDict.keys():
self.hedgePassiveLeg(vtSymbol)
self.hedgeCount = 0 # 重置被动腿对冲撤单等待计数
#----------------------------------------------------------------------
def newActiveLegTrade(self, vtSymbol, direction, volume):
"""新的主动腿成交"""
# 输出日志
self.writeLog(u'主动腿%s成交,方向%s,数量%s' %(vtSymbol, direction, volume))
# 将主动腿成交带上方向
if direction == DIRECTION_SHORT:
volume = -volume
# 计算主动腿成交后,对应的价差仓位
spread = self.spread
activeRatio = spread.activeLeg.ratio
spreadVolume = round(volume / activeRatio) # 四舍五入求主动腿成交量对应的价差份数
# 计算价差新仓位,对应的被动腿需要对冲部分
for leg in self.spread.passiveLegs:
newHedgingTask = leg.ratio * spreadVolume
if leg.vtSymbol not in self.hedgingTaskDict:
self.hedgingTaskDict[leg.vtSymbol] = newHedgingTask
else:
self.hedgingTaskDict[leg.vtSymbol] += newHedgingTask
# 发出被动腿对冲委托
self.hedgeAllPassiveLegs()
#----------------------------------------------------------------------
def newPassiveLegTrade(self, vtSymbol, direction, volume):
"""新的被动腿成交"""
if vtSymbol in self.hedgingTaskDict:
# 计算完成的对冲数量
if direction == DIRECTION_LONG:
hedgedVolume = volume
else:
hedgedVolume = -volume
# 计算剩余尚未完成的数量
self.hedgingTaskDict[vtSymbol] -= hedgedVolume
# 如果已全部完成,则从字典中移除
if not self.hedgingTaskDict[vtSymbol]:
del self.hedgingTaskDict[vtSymbol]
# 输出日志
self.writeLog(u'被动腿%s成交,方向%s,数量%s' %(vtSymbol, direction, volume))
#----------------------------------------------------------------------
def cancelLegOrder(self, vtSymbol):
"""撤销某条腿的委托"""
if vtSymbol not in self.legOrderDict:
return
orderList = self.legOrderDict[vtSymbol]
if not orderList:
return
for vtOrderID in orderList:
self.algoEngine.cancelOrder(vtOrderID)
self.writeLog(u'撤单%s的所有委托' %vtSymbol)
#----------------------------------------------------------------------
def cancelAllOrders(self):
"""撤销全部委托"""
for orderList in self.legOrderDict.values():
for vtOrderID in orderList:
self.algoEngine.cancelOrder(vtOrderID)
self.writeLog(u'全部撤单')
#----------------------------------------------------------------------
def cancelAllPassiveLegOrders(self):
"""撤销全部被动腿委托"""
cancelPassive = False
for vtSymbol in self.passiveVtSymbols:
if vtSymbol in self.legOrderDict and self.legOrderDict[vtSymbol]:
self.cancelLegOrder(vtSymbol)
cancelPassive = True
# 只有确实发出撤单委托时,才输出信息
if cancelPassive:
self.writeLog(u'被动腿全撤')

View File

@ -0,0 +1,168 @@
# encoding: UTF-8
from __future__ import division
from math import floor
from datetime import datetime
from vnpy.trader.vtConstant import (EMPTY_INT, EMPTY_FLOAT,
EMPTY_STRING, EMPTY_UNICODE)
EVENT_SPREADTRADING_TICK = 'eSpreadTradingTick.'
EVENT_SPREADTRADING_POS = 'eSpreadTradingPos.'
EVENT_SPREADTRADING_LOG = 'eSpreadTradingLog'
EVENT_SPREADTRADING_ALGO = 'eSpreadTradingAlgo.'
EVENT_SPREADTRADING_ALGOLOG = 'eSpreadTradingAlgoLog'
########################################################################
class StLeg(object):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
self.vtSymbol = EMPTY_STRING # 代码
self.ratio = EMPTY_INT # 实际交易时的比例
self.multiplier = EMPTY_FLOAT # 计算价差时的乘数
self.payup = EMPTY_INT # 对冲时的超价tick
self.bidPrice = EMPTY_FLOAT
self.askPrice = EMPTY_FLOAT
self.bidVolume = EMPTY_INT
self.askVolume = EMPTY_INT
self.longPos = EMPTY_INT
self.shortPos = EMPTY_INT
self.netPos = EMPTY_INT
########################################################################
class StSpread(object):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
self.name = EMPTY_UNICODE # 名称
self.symbol = EMPTY_STRING # 代码(基于组成腿计算)
self.activeLeg = None # 主动腿
self.passiveLegs = [] # 被动腿(支持多条)
self.allLegs = [] # 所有腿
self.bidPrice = EMPTY_FLOAT
self.askPrice = EMPTY_FLOAT
self.bidVolume = EMPTY_INT
self.askVolume = EMPTY_INT
self.time = EMPTY_STRING
self.longPos = EMPTY_INT
self.shortPos = EMPTY_INT
self.netPos = EMPTY_INT
#----------------------------------------------------------------------
def initSpread(self):
"""初始化价差"""
# 价差最少要有一条主动腿
if not self.activeLeg:
return
# 生成所有腿列表
self.allLegs.append(self.activeLeg)
self.allLegs.extend(self.passiveLegs)
# 生成价差代码
legSymbolList = []
for leg in self.allLegs:
if leg.multiplier >= 0:
legSymbol = '+%s*%s' %(leg.multiplier, leg.vtSymbol)
else:
legSymbol = '%s*%s' %(leg.multiplier, leg.vtSymbol)
legSymbolList.append(legSymbol)
self.symbol = ''.join(legSymbolList)
#----------------------------------------------------------------------
def calculatePrice(self):
"""计算价格"""
# 清空价格和委托量数据
self.bidPrice = EMPTY_FLOAT
self.askPrice = EMPTY_FLOAT
self.askVolume = EMPTY_INT
self.bidVolume = EMPTY_INT
# 遍历价差腿列表
for n, leg in enumerate(self.allLegs):
# 计算价格
if leg.multiplier > 0:
self.bidPrice += leg.bidPrice * leg.multiplier
self.askPrice += leg.askPrice * leg.multiplier
else:
self.bidPrice += leg.askPrice * leg.multiplier
self.askPrice += leg.bidPrice * leg.multiplier
# 计算报单量
if leg.ratio > 0:
legAdjustedBidVolume = floor(leg.bidVolume / leg.ratio)
legAdjustedAskVolume = floor(leg.askVolume / leg.ratio)
else:
legAdjustedBidVolume = floor(leg.askVolume / abs(leg.ratio))
legAdjustedAskVolume = floor(leg.bidVolume / abs(leg.ratio))
if n == 0:
self.bidVolume = legAdjustedBidVolume # 对于第一条腿,直接初始化
self.askVolume = legAdjustedAskVolume
else:
self.bidVolume = min(self.bidVolume, legAdjustedBidVolume) # 对于后续的腿,价差可交易报单量取较小值
self.askVolume = min(self.askVolume, legAdjustedAskVolume)
# 更新时间
self.time = datetime.now().strftime('%H:%M:%S.%f')[:-3]
#----------------------------------------------------------------------
def calculatePos(self):
"""计算持仓"""
# 清空持仓数据
self.longPos = EMPTY_INT
self.shortPos = EMPTY_INT
self.netPos = EMPTY_INT
# 遍历价差腿列表
for n, leg in enumerate(self.allLegs):
if leg.ratio > 0:
legAdjustedLongPos = floor(leg.longPos / leg.ratio)
legAdjustedShortPos = floor(leg.shortPos / leg.ratio)
else:
legAdjustedLongPos = floor(leg.shortPos / abs(leg.ratio))
legAdjustedShortPos = floor(leg.longPos / abs(leg.ratio))
if n == 0:
self.longPos = legAdjustedLongPos
self.shortPos = legAdjustedShortPos
else:
self.longPos = min(self.longPos, legAdjustedLongPos)
self.shortPos = min(self.shortPos, legAdjustedShortPos)
# 计算净仓位
self.longPos = int(self.longPos)
self.shortPos = int(self.shortPos)
self.netPos = self.longPos - self.shortPos
#----------------------------------------------------------------------
def addActiveLeg(self, leg):
"""添加主动腿"""
self.activeLeg = leg
#----------------------------------------------------------------------
def addPassiveLeg(self, leg):
"""添加被动腿"""
self.passiveLegs.append(leg)

View File

@ -0,0 +1,584 @@
# encoding: UTF-8
import json
import traceback
import shelve
from vnpy.event import Event
from vnpy.trader.vtFunction import getJsonPath, getTempPath
from vnpy.trader.vtEvent import (EVENT_TICK, EVENT_TRADE, EVENT_POSITION,
EVENT_TIMER, EVENT_ORDER)
from vnpy.trader.vtObject import (VtSubscribeReq, VtOrderReq,
VtCancelOrderReq, VtLogData)
from vnpy.trader.vtConstant import (DIRECTION_LONG, DIRECTION_SHORT,
OFFSET_OPEN, OFFSET_CLOSE,
PRICETYPE_LIMITPRICE)
from .stBase import (StLeg, StSpread, EVENT_SPREADTRADING_TICK,
EVENT_SPREADTRADING_POS, EVENT_SPREADTRADING_LOG,
EVENT_SPREADTRADING_ALGO, EVENT_SPREADTRADING_ALGOLOG)
from .stAlgo import SniperAlgo
########################################################################
class StDataEngine(object):
"""价差数据计算引擎"""
settingFileName = 'ST_setting.json'
settingFilePath = getJsonPath(settingFileName, __file__)
#----------------------------------------------------------------------
def __init__(self, mainEngine, eventEngine):
"""Constructor"""
self.mainEngine = mainEngine
self.eventEngine = eventEngine
# 腿、价差相关字典
self.legDict = {} # vtSymbol:StLeg
self.spreadDict = {} # name:StSpread
self.vtSymbolSpreadDict = {} # vtSymbol:StSpread
self.registerEvent()
#----------------------------------------------------------------------
def loadSetting(self):
"""加载配置"""
try:
with open(self.settingFilePath) as f:
l = json.load(f)
for setting in l:
result, msg = self.createSpread(setting)
self.writeLog(msg)
self.writeLog(u'价差配置加载完成')
except:
content = u'价差配置加载出错,原因:' + traceback.format_exc()
self.writeLog(content)
#----------------------------------------------------------------------
def saveSetting(self):
"""保存配置"""
with open(self.settingFilePath) as f:
pass
#----------------------------------------------------------------------
def createSpread(self, setting):
"""创建价差"""
result = False
msg = ''
# 检查价差重名
if setting['name'] in self.spreadDict:
msg = u'%s价差存在重名' %setting['name']
return result, msg
# 检查腿是否已使用
l = []
l.append(setting['activeLeg']['vtSymbol'])
for d in setting['passiveLegs']:
l.append(d['vtSymbol'])
for vtSymbol in l:
if vtSymbol in self.vtSymbolSpreadDict:
existingSpread = self.vtSymbolSpreadDict[vtSymbol]
msg = u'%s合约已经存在于%s价差中' %(vtSymbol, existingSpread.name)
return result, msg
# 创建价差
spread = StSpread()
spread.name = setting['name']
self.spreadDict[spread.name] = spread
# 创建主动腿
activeSetting = setting['activeLeg']
activeLeg = StLeg()
activeLeg.vtSymbol = str(activeSetting['vtSymbol'])
activeLeg.ratio = float(activeSetting['ratio'])
activeLeg.multiplier = float(activeSetting['multiplier'])
activeLeg.payup = int(activeSetting['payup'])
spread.addActiveLeg(activeLeg)
self.legDict[activeLeg.vtSymbol] = activeLeg
self.vtSymbolSpreadDict[activeLeg.vtSymbol] = spread
self.subscribeMarketData(activeLeg.vtSymbol)
# 创建被动腿
passiveSettingList = setting['passiveLegs']
passiveLegList = []
for d in passiveSettingList:
passiveLeg = StLeg()
passiveLeg.vtSymbol = str(d['vtSymbol'])
passiveLeg.ratio = float(d['ratio'])
passiveLeg.multiplier = float(d['multiplier'])
passiveLeg.payup = int(d['payup'])
spread.addPassiveLeg(passiveLeg)
self.legDict[passiveLeg.vtSymbol] = passiveLeg
self.vtSymbolSpreadDict[passiveLeg.vtSymbol] = spread
self.subscribeMarketData(passiveLeg.vtSymbol)
# 初始化价差
spread.initSpread()
self.putSpreadTickEvent(spread)
self.putSpreadPosEvent(spread)
# 返回结果
result = True
msg = u'%s价差创建成功' %spread.name
return result, msg
#----------------------------------------------------------------------
def processTickEvent(self, event):
"""处理行情推送"""
# 检查行情是否需要处理
tick = event.dict_['data']
if tick.vtSymbol not in self.legDict:
return
# 更新腿价格
leg = self.legDict[tick.vtSymbol]
leg.bidPrice = tick.bidPrice1
leg.askPrice = tick.askPrice1
leg.bidVolume = tick.bidVolume1
leg.askVolume = tick.askVolume1
# 更新价差价格
spread = self.vtSymbolSpreadDict[tick.vtSymbol]
spread.calculatePrice()
# 发出事件
self.putSpreadTickEvent(spread)
#----------------------------------------------------------------------
def putSpreadTickEvent(self, spread):
"""发出价差行情更新事件"""
event1 = Event(EVENT_SPREADTRADING_TICK+spread.name)
event1.dict_['data'] = spread
self.eventEngine.put(event1)
event2 = Event(EVENT_SPREADTRADING_TICK)
event2.dict_['data'] = spread
self.eventEngine.put(event2)
#----------------------------------------------------------------------
def processTradeEvent(self, event):
"""处理成交推送"""
# 检查成交是否需要处理
trade = event.dict_['data']
if trade.vtSymbol not in self.legDict:
return
# 更新腿持仓
leg = self.legDict[trade.vtSymbol]
direction = trade.direction
offset = trade.offset
if direction == DIRECTION_LONG:
if offset == OFFSET_OPEN:
leg.longPos += trade.volume
else:
leg.shortPos -= trade.volume
else:
if offset == OFFSET_OPEN:
leg.shortPos += trade.volume
else:
leg.longPos -= trade.volume
leg.netPos = leg.longPos - leg.shortPos
# 更新价差持仓
spread = self.vtSymbolSpreadDict[trade.vtSymbol]
spread.calculatePos()
# 推送价差持仓更新
event1 = Event(EVENT_SPREADTRADING_POS+spread.name)
event1.dict_['data'] = spread
self.eventEngine.put(event1)
event2 = Event(EVENT_SPREADTRADING_POS)
event2.dict_['data'] = spread
self.eventEngine.put(event2)
#----------------------------------------------------------------------
def processPosEvent(self, event):
"""处理持仓推送"""
# 检查持仓是否需要处理
pos = event.dict_['data']
if pos.vtSymbol not in self.legDict:
return
# 更新腿持仓
leg = self.legDict[pos.vtSymbol]
direction = pos.direction
if direction == DIRECTION_LONG:
leg.longPos = pos.position
else:
leg.shortPos = pos.position
leg.netPos = leg.longPos - leg.shortPos
# 更新价差持仓
spread = self.vtSymbolSpreadDict[pos.vtSymbol]
spread.calculatePos()
# 推送价差持仓更新
self.putSpreadPosEvent(spread)
#----------------------------------------------------------------------
def putSpreadPosEvent(self, spread):
"""发出价差持仓事件"""
event1 = Event(EVENT_SPREADTRADING_POS+spread.name)
event1.dict_['data'] = spread
self.eventEngine.put(event1)
event2 = Event(EVENT_SPREADTRADING_POS)
event2.dict_['data'] = spread
self.eventEngine.put(event2)
#----------------------------------------------------------------------
def registerEvent(self):
""""""
self.eventEngine.register(EVENT_TICK, self.processTickEvent)
self.eventEngine.register(EVENT_TRADE, self.processTradeEvent)
self.eventEngine.register(EVENT_POSITION, self.processPosEvent)
#----------------------------------------------------------------------
def subscribeMarketData(self, vtSymbol):
"""订阅行情"""
contract = self.mainEngine.getContract(vtSymbol)
if not contract:
self.writeLog(u'订阅行情失败,找不到该合约%s' %vtSymbol)
return
req = VtSubscribeReq()
req.symbol = contract.symbol
req.exchange = contract.exchange
self.mainEngine.subscribe(req, contract.gatewayName)
#----------------------------------------------------------------------
def writeLog(self, content):
"""发出日志"""
log = VtLogData()
log.logContent = content
event = Event(EVENT_SPREADTRADING_LOG)
event.dict_['data'] = log
self.eventEngine.put(event)
#----------------------------------------------------------------------
def getAllSpreads(self):
"""获取所有的价差"""
return self.spreadDict.values()
########################################################################
class StAlgoEngine(object):
"""价差算法交易引擎"""
algoFileName = 'SpreadTradingAlgo.vt'
algoFilePath = getTempPath(algoFileName)
#----------------------------------------------------------------------
def __init__(self, dataEngine, mainEngine, eventEngine):
"""Constructor"""
self.dataEngine = dataEngine
self.mainEngine = mainEngine
self.eventEngine = eventEngine
self.algoDict = {} # spreadName:algo
self.vtSymbolAlgoDict = {} # vtSymbol:algo
self.registerEvent()
#----------------------------------------------------------------------
def registerEvent(self):
"""注册事件监听"""
self.eventEngine.register(EVENT_SPREADTRADING_TICK, self.processSpreadTickEvent)
self.eventEngine.register(EVENT_SPREADTRADING_POS, self.processSpreadPosEvent)
self.eventEngine.register(EVENT_TRADE, self.processTradeEvent)
self.eventEngine.register(EVENT_ORDER, self.processOrderEvent)
self.eventEngine.register(EVENT_TIMER, self.processTimerEvent)
#----------------------------------------------------------------------
def processSpreadTickEvent(self, event):
"""处理价差行情事件"""
spread = event.dict_['data']
algo = self.algoDict.get(spread.name, None)
if algo:
algo.updateSpreadTick(spread)
#----------------------------------------------------------------------
def processSpreadPosEvent(self, event):
"""处理价差持仓事件"""
spread = event.dict_['data']
algo = self.algoDict.get(spread.name, None)
if algo:
algo.updateSpreadPos(spread)
#----------------------------------------------------------------------
def processTradeEvent(self, event):
"""处理成交事件"""
trade = event.dict_['data']
algo = self.vtSymbolAlgoDict.get(trade.vtSymbol, None)
if algo:
algo.updateTrade(trade)
#----------------------------------------------------------------------
def processOrderEvent(self, event):
"""处理委托事件"""
order = event.dict_['data']
algo = self.vtSymbolAlgoDict.get(order.vtSymbol, None)
if algo:
algo.updateOrder(order)
#----------------------------------------------------------------------
def processTimerEvent(self, event):
""""""
for algo in self.algoDict.values():
algo.updateTimer()
#----------------------------------------------------------------------
def sendOrder(self, vtSymbol, direction, offset, price, volume, payup=0):
"""发单"""
contract = self.mainEngine.getContract(vtSymbol)
if not contract:
return ''
req = VtOrderReq()
req.symbol = contract.symbol
req.exchange = contract.exchange
req.direction = direction
req.offset = offset
req.volume = int(volume)
req.priceType = PRICETYPE_LIMITPRICE
if direction == DIRECTION_LONG:
req.price = price + payup * contract.priceTick
else:
req.price = price - payup * contract.priceTick
vtOrderID = self.mainEngine.sendOrder(req, contract.gatewayName)
return vtOrderID
#----------------------------------------------------------------------
def cancelOrder(self, vtOrderID):
"""撤单"""
order = self.mainEngine.getOrder(vtOrderID)
if not order:
return
req = VtCancelOrderReq()
req.symbol = order.symbol
req.exchange = order.exchange
req.frontID = order.frontID
req.sessionID = order.sessionID
req.orderID = order.orderID
self.mainEngine.cancelOrder(req, order.gatewayName)
#----------------------------------------------------------------------
def buy(self, vtSymbol, price, volume, payup=0):
"""买入"""
vtOrderID = self.sendOrder(vtSymbol, DIRECTION_LONG, OFFSET_OPEN, price, volume, payup)
l = []
if vtOrderID:
l.append(vtOrderID)
return l
#----------------------------------------------------------------------
def sell(self, vtSymbol, price, volume, payup=0):
"""卖出"""
vtOrderID = self.sendOrder(vtSymbol, DIRECTION_SHORT, OFFSET_CLOSE, price, volume, payup)
l = []
if vtOrderID:
l.append(vtOrderID)
return l
#----------------------------------------------------------------------
def short(self, vtSymbol, price, volume, payup=0):
"""卖空"""
vtOrderID = self.sendOrder(vtSymbol, DIRECTION_SHORT, OFFSET_OPEN, price, volume, payup)
l = []
if vtOrderID:
l.append(vtOrderID)
return l
#----------------------------------------------------------------------
def cover(self, vtSymbol, price, volume, payup=0):
"""平空"""
vtOrderID = self.sendOrder(vtSymbol, DIRECTION_LONG, OFFSET_CLOSE, price, volume, payup)
l = []
if vtOrderID:
l.append(vtOrderID)
return l
#----------------------------------------------------------------------
def putAlgoEvent(self, algo):
"""发出算法状态更新事件"""
event = Event(EVENT_SPREADTRADING_ALGO+algo.name)
self.eventEngine.put(event)
#----------------------------------------------------------------------
def writeLog(self, content):
"""输出日志"""
log = VtLogData()
log.logContent = content
event = Event(EVENT_SPREADTRADING_ALGOLOG)
event.dict_['data'] = log
self.eventEngine.put(event)
#----------------------------------------------------------------------
def saveSetting(self):
"""保存算法配置"""
setting = {}
for algo in self.algoDict.values():
setting[algo.spreadName] = algo.getAlgoParams()
f = shelve.open(self.algoFilePath)
f['setting'] = setting
f.close()
#----------------------------------------------------------------------
def loadSetting(self):
"""加载算法配置"""
# 创建算法对象
l = self.dataEngine.getAllSpreads()
for spread in l:
algo = SniperAlgo(self, spread)
self.algoDict[spread.name] = algo
# 保存腿代码和算法对象的映射
for leg in spread.allLegs:
self.vtSymbolAlgoDict[leg.vtSymbol] = algo
# 加载配置
f = shelve.open(self.algoFilePath)
setting = f.get('setting', None)
f.close()
if not setting:
return
for algo in self.algoDict.values():
if algo.spreadName in setting:
d = setting[algo.spreadName]
algo.setAlgoParams(d)
#----------------------------------------------------------------------
def stopAll(self):
"""停止全部算法"""
for algo in self.algoDict.values():
algo.stop()
#----------------------------------------------------------------------
def startAlgo(self, spreadName):
"""启动算法"""
algo = self.algoDict[spreadName]
algoActive = algo.start()
return algoActive
#----------------------------------------------------------------------
def stopAlgo(self, spreadName):
"""停止算法"""
algo = self.algoDict[spreadName]
algoActive = algo.stop()
return algoActive
#----------------------------------------------------------------------
def getAllAlgoParams(self):
"""获取所有算法的参数"""
return [algo.getAlgoParams() for algo in self.algoDict.values()]
#----------------------------------------------------------------------
def setAlgoBuyPrice(self, spreadName, buyPrice):
"""设置算法买开价格"""
algo = self.algoDict[spreadName]
algo.setBuyPrice(buyPrice)
#----------------------------------------------------------------------
def setAlgoSellPrice(self, spreadName, sellPrice):
"""设置算法卖平价格"""
algo = self.algoDict[spreadName]
algo.setSellPrice(sellPrice)
#----------------------------------------------------------------------
def setAlgoShortPrice(self, spreadName, shortPrice):
"""设置算法卖开价格"""
algo = self.algoDict[spreadName]
algo.setShortPrice(shortPrice)
#----------------------------------------------------------------------
def setAlgoCoverPrice(self, spreadName, coverPrice):
"""设置算法买平价格"""
algo = self.algoDict[spreadName]
algo.setCoverPrice(coverPrice)
#----------------------------------------------------------------------
def setAlgoMode(self, spreadName, mode):
"""设置算法工作模式"""
algo = self.algoDict[spreadName]
algo.setMode(mode)
#----------------------------------------------------------------------
def setAlgoMaxOrderSize(self, spreadName, maxOrderSize):
"""设置算法单笔委托限制"""
algo = self.algoDict[spreadName]
algo.setMaxOrderSize(maxOrderSize)
#----------------------------------------------------------------------
def setAlgoMaxPosSize(self, spreadName, maxPosSize):
"""设置算法持仓限制"""
algo = self.algoDict[spreadName]
algo.setMaxPosSize(maxPosSize)
########################################################################
class StEngine(object):
"""价差引擎"""
#----------------------------------------------------------------------
def __init__(self, mainEngine, eventEngine):
"""Constructor"""
self.mainEngine = mainEngine
self.eventEngine = eventEngine
self.dataEngine = StDataEngine(mainEngine, eventEngine)
self.algoEngine = StAlgoEngine(self.dataEngine, mainEngine, eventEngine)
#----------------------------------------------------------------------
def init(self):
"""初始化"""
self.dataEngine.loadSetting()
self.algoEngine.loadSetting()
#----------------------------------------------------------------------
def stop(self):
"""停止"""
self.dataEngine.saveSetting()
self.algoEngine.stopAll()
self.algoEngine.saveSetting()

View File

@ -0,0 +1,521 @@
# encoding: UTF-8
from collections import OrderedDict
from vnpy.event import Event
from vnpy.trader.uiQt import QtWidgets, QtCore
from vnpy.trader.uiBasicWidget import (BasicMonitor, BasicCell, PnlCell,
AskCell, BidCell, BASIC_FONT)
from .stBase import (EVENT_SPREADTRADING_TICK, EVENT_SPREADTRADING_POS,
EVENT_SPREADTRADING_LOG, EVENT_SPREADTRADING_ALGO,
EVENT_SPREADTRADING_ALGOLOG)
from .stAlgo import StAlgoTemplate
STYLESHEET_START = 'background-color: rgb(111,255,244); color: black'
STYLESHEET_STOP = 'background-color: rgb(255,201,111); color: black'
########################################################################
class StTickMonitor(BasicMonitor):
"""价差行情监控"""
#----------------------------------------------------------------------
def __init__(self, mainEngine, eventEngine, parent=None):
"""Constructor"""
super(StTickMonitor, self).__init__(mainEngine, eventEngine, parent)
d = OrderedDict()
d['name'] = {'chinese':u'价差名称', 'cellType':BasicCell}
d['bidPrice'] = {'chinese':u'买价', 'cellType':BidCell}
d['bidVolume'] = {'chinese':u'买量', 'cellType':BidCell}
d['askPrice'] = {'chinese':u'卖价', 'cellType':AskCell}
d['askVolume'] = {'chinese':u'卖量', 'cellType':AskCell}
d['time'] = {'chinese':u'时间', 'cellType':BasicCell}
d['symbol'] = {'chinese':u'价差公式', 'cellType':BasicCell}
self.setHeaderDict(d)
self.setDataKey('name')
self.setEventType(EVENT_SPREADTRADING_TICK)
self.setFont(BASIC_FONT)
self.initTable()
self.registerEvent()
########################################################################
class StPosMonitor(BasicMonitor):
"""价差持仓监控"""
#----------------------------------------------------------------------
def __init__(self, mainEngine, eventEngine, parent=None):
"""Constructor"""
super(StPosMonitor, self).__init__(mainEngine, eventEngine, parent)
d = OrderedDict()
d['name'] = {'chinese':u'价差名称', 'cellType':BasicCell}
d['netPos'] = {'chinese':u'净仓', 'cellType':PnlCell}
d['longPos'] = {'chinese':u'多仓', 'cellType':BasicCell}
d['shortPos'] = {'chinese':u'空仓', 'cellType':BasicCell}
d['symbol'] = {'chinese':u'代码', 'cellType':BasicCell}
self.setHeaderDict(d)
self.setDataKey('name')
self.setEventType(EVENT_SPREADTRADING_POS)
self.setFont(BASIC_FONT)
self.initTable()
self.registerEvent()
########################################################################
class StLogMonitor(QtWidgets.QTextEdit):
"""价差日志监控"""
signal = QtCore.pyqtSignal(type(Event()))
#----------------------------------------------------------------------
def __init__(self, mainEngine, eventEngine, parent=None):
"""Constructor"""
super(StLogMonitor, self).__init__(parent)
self.eventEngine = eventEngine
self.registerEvent()
#----------------------------------------------------------------------
def processLogEvent(self, event):
"""处理日志事件"""
log = event.dict_['data']
content = '%s:%s' %(log.logTime, log.logContent)
self.append(content)
#----------------------------------------------------------------------
def registerEvent(self):
"""注册事件监听"""
self.signal.connect(self.processLogEvent)
self.eventEngine.register(EVENT_SPREADTRADING_LOG, self.signal.emit)
########################################################################
class StAlgoLogMonitor(BasicMonitor):
"""价差日志监控"""
#----------------------------------------------------------------------
def __init__(self, mainEngine, eventEngine, parent=None):
"""Constructor"""
super(StAlgoLogMonitor, self).__init__(mainEngine, eventEngine, parent)
d = OrderedDict()
d['logTime'] = {'chinese':u'时间', 'cellType':BasicCell}
d['logContent'] = {'chinese':u'信息', 'cellType':BasicCell}
self.setHeaderDict(d)
self.setEventType(EVENT_SPREADTRADING_ALGOLOG)
self.setFont(BASIC_FONT)
self.initTable()
self.registerEvent()
########################################################################
class StBuyPriceSpinBox(QtWidgets.QDoubleSpinBox):
""""""
#----------------------------------------------------------------------
def __init__(self, algoEngine, spreadName, price, parent=None):
"""Constructor"""
super(StBuyPriceSpinBox, self).__init__(parent)
self.algoEngine = algoEngine
self.spreadName = spreadName
self.setDecimals(4)
self.setRange(-10000, 10000)
self.setValue(price)
self.valueChanged.connect(self.setPrice)
#----------------------------------------------------------------------
def setPrice(self, value):
"""设置价格"""
self.algoEngine.setAlgoBuyPrice(self.spreadName, value)
########################################################################
class StSellPriceSpinBox(QtWidgets.QDoubleSpinBox):
""""""
#----------------------------------------------------------------------
def __init__(self, algoEngine, spreadName, price, parent=None):
"""Constructor"""
super(StSellPriceSpinBox, self).__init__(parent)
self.algoEngine = algoEngine
self.spreadName = spreadName
self.setDecimals(4)
self.setRange(-10000, 10000)
self.setValue(price)
self.valueChanged.connect(self.setPrice)
#----------------------------------------------------------------------
def setPrice(self, value):
"""设置价格"""
self.algoEngine.setAlgoSellPrice(self.spreadName, value)
########################################################################
class StShortPriceSpinBox(QtWidgets.QDoubleSpinBox):
""""""
#----------------------------------------------------------------------
def __init__(self, algoEngine, spreadName, price, parent=None):
"""Constructor"""
super(StShortPriceSpinBox, self).__init__(parent)
self.algoEngine = algoEngine
self.spreadName = spreadName
self.setDecimals(4)
self.setRange(-10000, 10000)
self.setValue(price)
self.valueChanged.connect(self.setPrice)
#----------------------------------------------------------------------
def setPrice(self, value):
"""设置价格"""
self.algoEngine.setAlgoShortPrice(self.spreadName, value)
########################################################################
class StCoverPriceSpinBox(QtWidgets.QDoubleSpinBox):
""""""
#----------------------------------------------------------------------
def __init__(self, algoEngine, spreadName, price, parent=None):
"""Constructor"""
super(StCoverPriceSpinBox, self).__init__(parent)
self.algoEngine = algoEngine
self.spreadName = spreadName
self.setDecimals(4)
self.setRange(-10000, 10000)
self.setValue(price)
self.valueChanged.connect(self.setPrice)
#----------------------------------------------------------------------
def setPrice(self, value):
"""设置价格"""
self.algoEngine.setAlgoCoverPrice(self.spreadName, value)
########################################################################
class StMaxPosSizeSpinBox(QtWidgets.QSpinBox):
""""""
#----------------------------------------------------------------------
def __init__(self, algoEngine, spreadName, size, parent=None):
"""Constructor"""
super(StMaxPosSizeSpinBox, self).__init__(parent)
self.algoEngine = algoEngine
self.spreadName = spreadName
self.setRange(-10000, 10000)
self.setValue(size)
self.valueChanged.connect(self.setSize)
#----------------------------------------------------------------------
def setSize(self, size):
"""设置价格"""
self.algoEngine.setAlgoMaxPosSize(self.spreadName, size)
########################################################################
class StMaxOrderSizeSpinBox(QtWidgets.QSpinBox):
""""""
#----------------------------------------------------------------------
def __init__(self, algoEngine, spreadName, size, parent=None):
"""Constructor"""
super(StMaxOrderSizeSpinBox, self).__init__(parent)
self.algoEngine = algoEngine
self.spreadName = spreadName
self.setRange(-10000, 10000)
self.setValue(size)
self.valueChanged.connect(self.setSize)
#----------------------------------------------------------------------
def setSize(self, size):
"""设置价格"""
self.algoEngine.setAlgoMaxOrderSize(self.spreadName, size)
########################################################################
class StModeComboBox(QtWidgets.QComboBox):
""""""
#----------------------------------------------------------------------
def __init__(self, algoEngine, spreadName, mode, parent=None):
"""Constructor"""
super(StModeComboBox, self).__init__(parent)
self.algoEngine = algoEngine
self.spreadName = spreadName
l = [StAlgoTemplate.MODE_LONGSHORT,
StAlgoTemplate.MODE_LONGONLY,
StAlgoTemplate.MODE_SHORTONLY]
self.addItems(l)
self.setCurrentIndex(l.index(mode))
self.currentIndexChanged.connect(self.setMode)
#----------------------------------------------------------------------
def setMode(self):
"""设置模式"""
mode = unicode(self.currentText())
self.algoEngine.setAlgoMode(self.spreadName, mode)
#----------------------------------------------------------------------
def algoActiveChanged(self, active):
"""算法运行状态改变"""
# 只允许算法停止时修改运行模式
if active:
self.setEnabled(False)
else:
self.setEnabled(True)
########################################################################
class StActiveButton(QtWidgets.QPushButton):
""""""
signalActive = QtCore.pyqtSignal(bool)
#----------------------------------------------------------------------
def __init__(self, algoEngine, spreadName, parent=None):
"""Constructor"""
super(StActiveButton, self).__init__(parent)
self.algoEngine = algoEngine
self.spreadName = spreadName
self.active = False
self.setStopped()
self.clicked.connect(self.buttonClicked)
#----------------------------------------------------------------------
def buttonClicked(self):
"""改变运行模式"""
if self.active:
self.stop()
else:
self.start()
#----------------------------------------------------------------------
def stop(self):
"""停止"""
algoActive = self.algoEngine.stopAlgo(self.spreadName)
if not algoActive:
self.setStopped()
#----------------------------------------------------------------------
def start(self):
"""启动"""
algoActive = self.algoEngine.startAlgo(self.spreadName)
if algoActive:
self.setStarted()
#----------------------------------------------------------------------
def setStarted(self):
"""算法启动"""
self.setText(u'运行中')
self.setStyleSheet(STYLESHEET_START)
self.active = True
self.signalActive.emit(self.active)
#----------------------------------------------------------------------
def setStopped(self):
"""算法停止"""
self.setText(u'已停止')
self.setStyleSheet(STYLESHEET_STOP)
self.active = False
self.signalActive.emit(self.active)
########################################################################
class StAlgoManager(QtWidgets.QTableWidget):
"""价差算法管理组件"""
#----------------------------------------------------------------------
def __init__(self, stEngine, parent=None):
"""Constructor"""
super(StAlgoManager, self).__init__(parent)
self.algoEngine = stEngine.algoEngine
self.buttonActiveDict = {} # spreadName: buttonActive
self.initUi()
#----------------------------------------------------------------------
def initUi(self):
"""初始化表格"""
headers = [u'价差',
u'算法',
'BuyPrice',
'SellPrice',
'CoverPrice',
'ShortPrice',
u'委托上限',
u'持仓上限',
u'模式',
u'状态']
self.setColumnCount(len(headers))
self.setHorizontalHeaderLabels(headers)
self.horizontalHeader().setResizeMode(QtWidgets.QHeaderView.Stretch)
self.verticalHeader().setVisible(False)
self.setEditTriggers(self.NoEditTriggers)
#----------------------------------------------------------------------
def initCells(self):
"""初始化单元格"""
algoEngine = self.algoEngine
l = self.algoEngine.getAllAlgoParams()
self.setRowCount(len(l))
for row, d in enumerate(l):
cellSpreadName = QtWidgets.QTableWidgetItem(d['spreadName'])
cellAlgoName = QtWidgets.QTableWidgetItem(d['algoName'])
spinBuyPrice = StBuyPriceSpinBox(algoEngine, d['spreadName'], d['buyPrice'])
spinSellPrice = StSellPriceSpinBox(algoEngine, d['spreadName'], d['sellPrice'])
spinShortPrice = StShortPriceSpinBox(algoEngine, d['spreadName'], d['shortPrice'])
spinCoverPrice = StCoverPriceSpinBox(algoEngine, d['spreadName'], d['coverPrice'])
spinMaxOrderSize = StMaxOrderSizeSpinBox(algoEngine, d['spreadName'], d['maxOrderSize'])
spinMaxPosSize = StMaxPosSizeSpinBox(algoEngine, d['spreadName'], d['maxPosSize'])
comboMode = StModeComboBox(algoEngine, d['spreadName'], d['mode'])
buttonActive = StActiveButton(algoEngine, d['spreadName'])
self.setItem(row, 0, cellSpreadName)
self.setItem(row, 1, cellAlgoName)
self.setCellWidget(row, 2, spinBuyPrice)
self.setCellWidget(row, 3, spinSellPrice)
self.setCellWidget(row, 4, spinCoverPrice)
self.setCellWidget(row, 5, spinShortPrice)
self.setCellWidget(row, 6, spinMaxOrderSize)
self.setCellWidget(row, 7, spinMaxPosSize)
self.setCellWidget(row, 8, comboMode)
self.setCellWidget(row, 9, buttonActive)
buttonActive.signalActive.connect(comboMode.algoActiveChanged)
self.buttonActiveDict[d['spreadName']] = buttonActive
#----------------------------------------------------------------------
def stopAll(self):
"""停止所有算法"""
for button in self.buttonActiveDict.values():
button.stop()
########################################################################
class StGroup(QtWidgets.QGroupBox):
"""集合显示"""
#----------------------------------------------------------------------
def __init__(self, widget, title, parent=None):
"""Constructor"""
super(StGroup, self).__init__(parent)
self.setTitle(title)
vbox = QtWidgets.QVBoxLayout()
vbox.addWidget(widget)
self.setLayout(vbox)
########################################################################
class StManager(QtWidgets.QWidget):
""""""
#----------------------------------------------------------------------
def __init__(self, stEngine, eventEngine, parent=None):
"""Constructor"""
super(StManager, self).__init__(parent)
self.stEngine = stEngine
self.mainEngine = stEngine.mainEngine
self.eventEngine = eventEngine
self.initUi()
#----------------------------------------------------------------------
def initUi(self):
"""初始化界面"""
self.setWindowTitle(u'价差交易')
# 创建组件
tickMonitor = StTickMonitor(self.mainEngine, self.eventEngine)
posMonitor = StPosMonitor(self.mainEngine, self.eventEngine)
logMonitor = StLogMonitor(self.mainEngine, self.eventEngine)
self.algoManager = StAlgoManager(self.stEngine)
algoLogMonitor = StAlgoLogMonitor(self.mainEngine, self.eventEngine)
# 创建按钮
buttonInit = QtWidgets.QPushButton(u'初始化')
buttonInit.clicked.connect(self.init)
buttonStopAll = QtWidgets.QPushButton(u'全部停止')
buttonStopAll.clicked.connect(self.algoManager.stopAll)
# 创建集合
groupTick = StGroup(tickMonitor, u'价差行情')
groupPos = StGroup(posMonitor, u'价差持仓')
groupLog = StGroup(logMonitor, u'日志信息')
groupAlgo = StGroup(self.algoManager, u'价差算法')
groupAlgoLog = StGroup(algoLogMonitor, u'算法信息')
# 设置布局
hbox = QtWidgets.QHBoxLayout()
hbox.addWidget(buttonInit)
hbox.addStretch()
hbox.addWidget(buttonStopAll)
grid = QtWidgets.QGridLayout()
grid.addLayout(hbox, 0, 0, 1, 2)
grid.addWidget(groupTick, 1, 0)
grid.addWidget(groupPos, 1, 1)
grid.addWidget(groupAlgo, 2, 0, 1, 2)
grid.addWidget(groupLog, 3, 0)
grid.addWidget(groupAlgoLog, 3, 1)
self.setLayout(grid)
#----------------------------------------------------------------------
def show(self):
"""重载显示"""
self.showMaximized()
#----------------------------------------------------------------------
def init(self):
"""初始化"""
self.stEngine.init()
self.algoManager.initCells()

View File

@ -8,7 +8,7 @@ from time import sleep
from vnpy.api.cshshlp import CsHsHlp
from vnpy.api.ctp import MdApi
from vnpy.trader.vtGateway import *
from vnpy.trader.vtFunction import getTempPath
from vnpy.trader.vtFunction import getTempPath, getJsonPath
# 接口常量
@ -92,16 +92,15 @@ class CshshlpGateway(VtGateway):
self.qryEnabled = False # 是否要启动循环查询
self.fileName = self.gatewayName + '_connect.json'
self.filePath = getJsonPath(self.fileName, __file__)
#----------------------------------------------------------------------
def connect(self):
"""连接"""
# 载入json文件
fileName = self.gatewayName + '_connect.json'
path = os.path.abspath(os.path.dirname(__file__))
fileName = os.path.join(path, fileName)
try:
f = file(fileName)
f = file(self.filePath)
except IOError:
log = VtLogData()
log.gatewayName = self.gatewayName

View File

@ -15,9 +15,9 @@ from datetime import datetime
from vnpy.api.ctp import MdApi, TdApi, defineDict
from vnpy.trader.vtGateway import *
from vnpy.trader.vtFunction import getTempPath
from vnpy.trader.gateway.ctpGateway.language import text
from vnpy.trader.vtFunction import getJsonPath, getTempPath
from vnpy.trader.vtConstant import GATEWAYTYPE_FUTURES
from .language import text
# 以下为一些VT类型和CTP类型的映射字典
@ -91,19 +91,15 @@ class CtpGateway(VtGateway):
self.tdConnected = False # 交易API连接状态
self.qryEnabled = False # 循环查询
self.requireAuthentication = False
self.fileName = self.gatewayName + '_connect.json'
self.filePath = getJsonPath(self.fileName, __file__)
#----------------------------------------------------------------------
def connect(self):
"""连接"""
# 载入json文件
fileName = self.gatewayName + '_connect.json'
path = os.path.abspath(os.path.dirname(__file__))
fileName = os.path.join(path, fileName)
try:
f = file(fileName)
f = file(self.filePath)
except IOError:
log = VtLogData()
log.gatewayName = self.gatewayName
@ -331,10 +327,6 @@ class CtpMdApi(MdApi):
#----------------------------------------------------------------------
def onRtnDepthMarketData(self, data):
"""行情推送"""
# 忽略成交量为0的无效tick数据
if not data['Volume']:
return
# 创建对象
tick = VtTickData()
tick.gatewayName = self.gatewayName
@ -712,7 +704,7 @@ class CtpTdApi(TdApi):
pos.positionProfit += data['PositionProfit']
# 计算持仓均价
if pos.position:
if pos.position and pos.symbol in self.symbolSizeDict:
size = self.symbolSizeDict[pos.symbol]
pos.price = (cost + data['PositionCost']) / (pos.position * size)

View File

@ -11,7 +11,7 @@ import os
import json
from vnpy.api.femas import MdApi, TdApi, defineDict
from vnpy.trader.vtFunction import getTempPath
from vnpy.trader.vtFunction import getTempPath, getJsonPath
from vnpy.trader.vtGateway import *
# 以下为一些VT类型和CTP类型的映射字典
@ -72,16 +72,15 @@ class FemasGateway(VtGateway):
self.qryEnabled = False # 是否要启动循环查询
self.fileName = self.gatewayName + '_connect.json'
self.filePath = getJsonPath(self.fileName, __file__)
#----------------------------------------------------------------------
def connect(self):
"""连接"""
# 载入json文件
fileName = self.gatewayName + '_connect.json'
path = os.path.abspath(os.path.dirname(__file__))
fileName = os.path.join(path, fileName)
try:
f = file(fileName)
f = file(self.filePath)
except IOError:
log = VtLogData()
log.gatewayName = self.gatewayName

View File

@ -15,7 +15,7 @@ from threading import Thread
from vnpy.api.huobi import vnhuobi
from vnpy.trader.vtGateway import *
from vnpy.trader.vtFunction import getJsonPath
SYMBOL_BTCCNY = 'BTCCNY'
SYMBOL_LTCCNY = 'LTCCNY'
@ -60,16 +60,15 @@ class HuobiGateway(VtGateway):
self.tradeApi = HuobiTradeApi(self)
self.dataApi = HuobiDataApi(self)
self.fileName = self.gatewayName + '_connect.json'
self.filePath = getJsonPath(self.fileName, __file__)
#----------------------------------------------------------------------
def connect(self):
"""连接"""
# 载入json文件
fileName = self.gatewayName + '_connect.json'
path = os.path.abspath(os.path.dirname(__file__))
fileName = os.path.join(path, fileName)
try:
f = file(fileName)
f = file(self.filePath)
except IOError:
log = VtLogData()
log.gatewayName = self.gatewayName

View File

@ -19,7 +19,9 @@ from copy import copy
from vnpy.api.ib import *
from vnpy.trader.vtGateway import *
from vnpy.trader.gateway.ibGateway.language import text
from vnpy.trader.vtFunction import getJsonPath
from .language import text
# 以下为一些VT类型和CTP类型的映射字典
@ -136,17 +138,16 @@ class IbGateway(VtGateway):
self.connected = False # 连接状态
self.api = IbWrapper(self) # API接口
self.fileName = self.gatewayName + '_connect.json'
self.filePath = getJsonPath(self.fileName, __file__)
#----------------------------------------------------------------------
def connect(self):
"""连接"""
# 载入json文件
fileName = self.gatewayName + '_connect.json'
path = os.path.abspath(os.path.dirname(__file__))
fileName = os.path.join(path, fileName)
try:
f = file(fileName)
f = file(self.filePath)
except IOError:
log = VtLogData()
log.gatewayName = self.gatewayName

View File

@ -13,6 +13,7 @@ import time
from vnpy.api.ksgold import TdApi, defineDict
from vnpy.trader.vtGateway import *
from vnpy.trader.vtFunction import getJsonPath
# 以下类型映射参考的是原生API里的Constant.h
@ -45,16 +46,15 @@ class KsgoldGateway(VtGateway):
self.orderInited = False # 委托初始化查询
self.tradeInited = False # 成交初始化查询
self.fileName = self.gatewayName + '_connect.json'
self.filePath = getJsonPath(self.fileName, __file__)
#----------------------------------------------------------------------
def connect(self):
"""连接"""
# 载入json文件
fileName = self.gatewayName + '_connect.json'
path = os.path.abspath(os.path.dirname(__file__))
fileName = os.path.join(path, fileName)
try:
f = file(fileName)
f = file(self.filePath)
except IOError:
log = VtLogData()
log.gatewayName = self.gatewayName

View File

@ -9,7 +9,7 @@ import os
import json
from vnpy.api.ksotp import MdApi, TdApi, defineDict
from vnpy.trader.vtFunction import getTempPath
from vnpy.trader.vtFunction import getTempPath, getJsonPath
from vnpy.trader.vtGateway import *
# 以下为一些VT类型和CTP类型的映射字典
@ -68,16 +68,15 @@ class KsotpGateway(VtGateway):
self.qryEnabled = False # 是否要启动循环查询
self.fileName = self.gatewayName + '_connect.json'
self.filePath = getJsonPath(self.fileName, __file__)
#----------------------------------------------------------------------
def connect(self):
"""连接"""
# 载入json文件
fileName = self.gatewayName + '_connect.json'
path = os.path.abspath(os.path.dirname(__file__))
fileName = os.path.join(path, fileName)
try:
f = file(fileName)
f = file(self.filePath)
except IOError:
log = VtLogData()
log.gatewayName = self.gatewayName

View File

@ -12,6 +12,7 @@ from time import sleep
from vnpy.api.lhang import LhangApi
from vnpy.trader.vtGateway import *
from vnpy.trader.vtFunction import getJsonPath
SYMBOL_BTCCNY = 'BTCCNY'
@ -46,16 +47,15 @@ class LhangGateway(VtGateway):
self.api = LhangApi(self)
self.fileName = self.gatewayName + '_connect.json'
self.filePath = getJsonPath(self.fileName, __file__)
#----------------------------------------------------------------------
def connect(self):
"""连接"""
# 载入json文件
fileName = self.gatewayName + '_connect.json'
path = os.path.abspath(os.path.dirname(__file__))
fileName = os.path.join(path, fileName)
try:
f = file(fileName)
f = file(self.filePath)
except IOError:
log = VtLogData()
log.gatewayName = self.gatewayName

View File

@ -8,7 +8,7 @@ import os
import json
from vnpy.api.lts import MdApi, QryApi, TdApi, defineDict
from vnpy.trader.vtFunction import getTempPath
from vnpy.trader.vtFunction import getTempPath, getJsonPath
from vnpy.trader.vtGateway import *
@ -68,17 +68,16 @@ class LtsGateway(VtGateway):
self.qryConnected = False
self.qryEnabled = False # 是否要启动循环查询
self.fileName = self.gatewayName + '_connect.json'
self.filePath = getJsonPath(self.fileName, __file__)
#----------------------------------------------------------------------
def connect(self):
"""连接"""
# 载入json 文件
fileName = self.gatewayName + '_connect.json'
path = os.path.abspath(os.path.dirname(__file__))
fileName = os.path.join(path, fileName)
try:
f = file(fileName)
f = file(self.filePath)
except IOError:
log = VtLogData()
log.gatewayName = self.gatewayName

View File

@ -26,6 +26,7 @@ import datetime
from vnpy.api.oanda import OandaApi
from vnpy.trader.vtGateway import *
from vnpy.trader.vtFunction import getJsonPath
# 价格类型映射
priceTypeMap = {}
@ -53,16 +54,15 @@ class OandaGateway(VtGateway):
self.qryEnabled = False # 是否要启动循环查询
self.fileName = self.gatewayName + '_connect.json'
self.filePath = getJsonPath(self.fileName, __file__)
#----------------------------------------------------------------------
def connect(self):
"""连接"""
# 载入json文件
fileName = self.gatewayName + '_connect.json'
path = os.path.abspath(os.path.dirname(__file__))
fileName = os.path.join(path, fileName)
try:
f = file(fileName)
f = file(self.filePath)
except IOError:
log = VtLogData()
log.gatewayName = self.gatewayName

View File

@ -11,13 +11,16 @@ vn.okcoin的gateway接入
import os
import json
from datetime import datetime
from time import sleep
from copy import copy
from threading import Condition
from Queue import Queue
from threading import Thread
from time import sleep
from vnpy.api.okcoin import vnokcoin
from vnpy.trader.vtGateway import *
from vnpy.trader.vtFunction import getJsonPath
# 价格类型映射
priceTypeMap = {}
@ -54,16 +57,24 @@ LTC_USD_THISWEEK = 'LTC_USD_THISWEEK'
LTC_USD_NEXTWEEK = 'LTC_USD_NEXTWEEK'
LTC_USD_QUARTER = 'LTC_USD_QUARTER'
ETH_USD_SPOT = 'ETH_USD_SPOT'
ETH_USD_THISWEEK = 'ETH_USD_THISWEEK'
ETH_USD_NEXTWEEK = 'ETH_USD_NEXTWEEK'
ETH_USD_QUARTER = 'ETH_USD_QUARTER'
# CNY
BTC_CNY_SPOT = 'BTC_CNY_SPOT'
LTC_CNY_SPOT = 'LTC_CNY_SPOT'
ETH_CNY_SPOT = 'ETH_CNY_SPOT'
# 印射字典
spotSymbolMap = {}
spotSymbolMap['ltc_usd'] = LTC_USD_SPOT
spotSymbolMap['btc_usd'] = BTC_USD_SPOT
spotSymbolMap['ETH_usd'] = ETH_USD_SPOT
spotSymbolMap['ltc_cny'] = LTC_CNY_SPOT
spotSymbolMap['btc_cny'] = BTC_CNY_SPOT
spotSymbolMap['eth_cny'] = ETH_CNY_SPOT
spotSymbolMapReverse = {v: k for k, v in spotSymbolMap.items()}
@ -75,16 +86,20 @@ channelSymbolMap = {}
# USD
channelSymbolMap['ok_sub_spotusd_btc_ticker'] = BTC_USD_SPOT
channelSymbolMap['ok_sub_spotusd_ltc_ticker'] = LTC_USD_SPOT
channelSymbolMap['ok_sub_spotusd_eth_ticker'] = ETH_USD_SPOT
channelSymbolMap['ok_sub_spotusd_btc_depth_20'] = BTC_USD_SPOT
channelSymbolMap['ok_sub_spotusd_ltc_depth_20'] = LTC_USD_SPOT
channelSymbolMap['ok_sub_spotusd_eth_depth_20'] = ETH_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_eth_ticker'] = ETH_CNY_SPOT
channelSymbolMap['ok_sub_spotcny_btc_depth_20'] = BTC_CNY_SPOT
channelSymbolMap['ok_sub_spotcny_ltc_depth_20'] = LTC_CNY_SPOT
channelSymbolMap['ok_sub_spotcny_eth_depth_20'] = ETH_CNY_SPOT
@ -103,16 +118,15 @@ class OkcoinGateway(VtGateway):
self.leverage = 0
self.connected = False
self.fileName = self.gatewayName + '_connect.json'
self.filePath = getJsonPath(self.fileName, __file__)
#----------------------------------------------------------------------
def connect(self):
"""连接"""
# 载入json文件
fileName = self.gatewayName + '_connect.json'
path = os.path.abspath(os.path.dirname(__file__))
fileName = os.path.join(path, fileName)
try:
f = file(fileName)
f = file(self.filePath)
except IOError:
log = VtLogData()
log.gatewayName = self.gatewayName
@ -305,17 +319,20 @@ class Api(vnokcoin.OkCoinApi):
self.spotOrderInfo(vnokcoin.TRADING_SYMBOL_LTC, '-1')
self.spotOrderInfo(vnokcoin.TRADING_SYMBOL_BTC, '-1')
self.spotOrderInfo(vnokcoin.TRADING_SYMBOL_ETH, '-1')
# 连接后订阅现货的成交和账户数据
self.subscribeSpotTrades()
self.subscribeSpotUserInfo()
self.subscribeSpotTicker(vnokcoin.SYMBOL_BTC)
self.subscribeSpotTicker(vnokcoin.SYMBOL_LTC)
self.subscribeSpotTicker(vnokcoin.SYMBOL_ETH)
self.subscribeSpotDepth(vnokcoin.SYMBOL_BTC, vnokcoin.DEPTH_20)
self.subscribeSpotDepth(vnokcoin.SYMBOL_LTC, vnokcoin.DEPTH_20)
self.subscribeSpotDepth(vnokcoin.SYMBOL_ETH, vnokcoin.DEPTH_20)
# 如果连接的是USD网站则订阅期货相关回报数据
if self.currency == vnokcoin.CURRENCY_USD:
self.subscribeFutureTrades()
@ -346,10 +363,12 @@ class Api(vnokcoin.OkCoinApi):
# 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_eth_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_sub_spotusd_eth_depth_20'] = self.onDepth
self.cbDict['ok_spotusd_userinfo'] = self.onSpotUserInfo
self.cbDict['ok_spotusd_orderinfo'] = self.onSpotOrderInfo
@ -362,10 +381,12 @@ class Api(vnokcoin.OkCoinApi):
# 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_eth_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_sub_spotcny_eth_depth_20'] = self.onDepth
self.cbDict['ok_spotcny_userinfo'] = self.onSpotUserInfo
self.cbDict['ok_spotcny_orderinfo'] = self.onSpotOrderInfo
@ -452,7 +473,7 @@ class Api(vnokcoin.OkCoinApi):
funds = rawData['info']['funds']
# 持仓信息
for symbol in ['btc', 'ltc', self.currency]:
for symbol in ['btc', 'ltc','eth', self.currency]:
if symbol in funds['free']:
pos = VtPositionData()
pos.gatewayName = self.gatewayName
@ -485,7 +506,7 @@ class Api(vnokcoin.OkCoinApi):
info = rawData['info']
# 持仓信息
for symbol in ['btc', 'ltc', self.currency]:
for symbol in ['btc', 'ltc','eth', self.currency]:
if symbol in info['free']:
pos = VtPositionData()
pos.gatewayName = self.gatewayName
@ -616,7 +637,8 @@ class Api(vnokcoin.OkCoinApi):
contractList.append(self.generateSpecificContract(contract, BTC_CNY_SPOT))
contractList.append(self.generateSpecificContract(contract, LTC_CNY_SPOT))
contractList.append(self.generateSpecificContract(contract, ETH_CNY_SPOT))
return contractList
#----------------------------------------------------------------------
@ -633,7 +655,8 @@ class Api(vnokcoin.OkCoinApi):
contractList.append(self.generateSpecificContract(contract, BTC_USD_SPOT))
contractList.append(self.generateSpecificContract(contract, LTC_USD_SPOT))
contractList.append(self.generateSpecificContract(contract, ETH_USD_SPOT))
# 期货
contract.productClass = PRODUCT_FUTURES
@ -643,6 +666,9 @@ class Api(vnokcoin.OkCoinApi):
contractList.append(self.generateSpecificContract(contract, LTC_USD_THISWEEK))
contractList.append(self.generateSpecificContract(contract, LTC_USD_NEXTWEEK))
contractList.append(self.generateSpecificContract(contract, LTC_USD_QUARTER))
contractList.append(self.generateSpecificContract(contract, ETH_USD_THISWEEK))
contractList.append(self.generateSpecificContract(contract, ETH_USD_NEXTWEEK))
contractList.append(self.generateSpecificContract(contract, ETH_USD_QUARTER))
return contractList

View File

@ -15,6 +15,7 @@ from copy import copy
from vnpy.api.qdp import MdApi, TdApi, defineDict
from vnpy.trader.vtGateway import *
from vnpy.trader.vtFunction import getJsonPath
# 以下为一些VT类型和QDP类型的映射字典
@ -77,16 +78,14 @@ class QdpGateway(VtGateway):
self.qryEnabled = False # 是否要启动循环查询
self.fileName = self.gatewayName + '_connect.json'
self.filePath = getJsonPath(self.fileName, __file__)
#----------------------------------------------------------------------
def connect(self):
"""连接"""
# 载入json文件
fileName = self.gatewayName + '_connect.json'
path = os.path.abspath(os.path.dirname(__file__))
fileName = os.path.join(path, fileName)
"""连接"""
try:
f = file(fileName)
f = file(self.filePath)
except IOError:
log = VtLogData()
log.gatewayName = self.gatewayName

Some files were not shown because too many files have changed in this diff Show More