[Merge] PR #551 from vnpy/dev,v1.7.2 beta2

v1.7.2 beta2
This commit is contained in:
vn.py 2017-10-18 14:01:34 +08:00 committed by GitHub
commit 007f6297ed
16 changed files with 405 additions and 279 deletions

183
README.md
View File

@ -2,9 +2,78 @@
---
### 简介
vn.py是一套基于Python的开源量化交易程序开发框架起源于国内私募的自主量化交易系统。2015年初项目启动时只是单纯的交易API接口的Python封装。随着业内关注度的上升和社区不断的贡献目前已经一步步成长为一套全面的交易程序开发框架用户群体也日渐多样化包括私募基金、券商自营和资管、期货资管和子公司、高校研究机构和专业个人投资者等。
vn.py是基于Python的开源量化交易程序开发框架起源于国内私募的自主量化交易系统。2015年初项目启动时只是单纯的交易API接口的Python封装。随着业内关注度的上升和社区不断的贡献目前已经成长为一套全功能的交易程序开发框架用户群体也日渐多样化包括私募基金、券商自营和资管、期货资管和子公司、高校研究机构和专业个人投资者等。
---
### 项目结构
1. 丰富的Python交易和数据API接口vnpy.api基本覆盖了国内外所有常规交易品种股票、期货、期权、外汇、外盘、比特币具体包括
- CTPctp
- 飞马femas
- 中泰证券XTPxtp
- 中信证券期权cshshlp
- 金仕达黄金ksgold
- 金仕达期权ksotp
- 飞鼠sgit
- 飞创xspeed
- QDPqdp
- 上海直达期货shzd
- Interactive Brokersib
- OANDAoanda
- OKCOINokcoin
- 火币huobi
- 链行lhang
- 通联数据datayes
2. 简洁易用的事件驱动引擎vnpy.event作为事件驱动型交易程序的核心
3. 支持服务器端数据推送的RPC框架vnpy.rpc用于实现多进程分布式架构的交易系统
4. 开箱即用的实盘交易平台框架vnpy.trader整合了多种交易接口并针对具体策略算法和功能开发提供了简洁易用的API用于快速构建交易员所需的量化交易程序应用举例
* 同时登录多个交易接口,在一套界面上监控多种市场的行情和多种资产账户的资金、持仓、委托、成交情况
* 支持跨市场套利CTP期货和LTS证券、境内外套利CTP期货和IB外盘、多市场数据整合实时预测走势CTP的股指期货数据、IB的外盘A50数据、Wind的行业指数数据等策略应用
* CTA策略引擎模块在保持易用性的同时允许用户针对CTA类策略运行过程中委托的报撤行为进行细粒度控制降低交易滑点、实现高频策略
* 实盘行情记录支持Tick和K线数据的落地用于策略开发回测以及实盘运行初始化
5. 数据相关的API接口vnpy.data用于构建和更新历史行情数据库目前包括
* 上海中期历史行情服务shcifco
* 通联数据API下载服务datayes
* 天勤行情数据接口tq
6. 关于vn.py项目的应用演示examples对于新手而言可以从这里开始学习vn.py项目的使用方式
8. vn.py项目的Docker镜像docker目前尚未完成
9. [官网](http://vnpy.org)和[知乎专栏](http://zhuanlan.zhihu.com/vn-py)内容包括vn.py项目的开发教程和Python在量化交易领域的应用研究等内容
10. 官方交流QQ群262656087管理较严格定期清除长期潜水的成员
---
### 环境准备
@ -55,9 +124,9 @@ sudo /home/vnpy/anaconda2/bin/conda install -c quantopian ta-lib=0.4.9
1. 在[SimNow](http://simnow.com.cn/)注册CTP仿真账号记下你的**账号、密码、经纪商编号**,然后下载快期查询你的**交易和行情服务器地址**
2. 找到你的Anaconda安装目录打开Anaconda2\Lib\site-packages\vnpy-1.6.2b0-py2.7.egg\vnpy\trader\gateway\ctpGateway\CTP_connect.json修改账号、密码、服务器等为上一步注册完成后你的信息注意使用专门的编程编辑器如Sublime Text等防止json编码出错
2. 找到vn.py应用示例目录examples打开examples\VnTrader\CTP_connect.json修改账号、密码、服务器等为上一步注册完成后你的信息注意使用专门的编程编辑器如Sublime Text等防止json编码出错
3. 在任意目录将以下内容保存为run.py并双击运行若无法双击则在当前目录按住Shift点鼠标右键打开cmd输入python run.py运行
3. 找到VnTrader的启动入口run.py并双击运行若无法双击则在当前目录按住Shift点鼠标右键打开cmd输入python run.py运行run.py内容如下
```
# encoding: UTF-8
@ -78,12 +147,14 @@ from vnpy.trader.uiQt import createQApp
from vnpy.trader.uiMainWindow import MainWindow
# 加载底层接口
from vnpy.trader.gateway import (ctpGateway, oandaGateway, ibGateway,
from vnpy.trader.gateway import (ctpGateway, oandaGateway, ibGateway,
huobiGateway, okcoinGateway)
if system == 'Windows':
from vnpy.trader.gateway import (femasGateway, xspeedGateway,
sgitGateway, shzdGateway)
from vnpy.trader.gateway import femasGateway, xspeedGateway
if system == 'Linux':
from vnpy.trader.gateway import xtpGateway
# 加载上层应用
from vnpy.trader.app import (riskManager, ctaStrategy, spreadTrading)
@ -94,44 +165,46 @@ def main():
"""主程序入口"""
# 创建Qt应用对象
qApp = createQApp()
# 创建事件引擎
ee = EventEngine()
# 创建主引擎
me = MainEngine(ee)
# 添加交易接口
me.addGateway(ctpGateway)
me.addGateway(oandaGateway)
me.addGateway(ibGateway)
me.addGateway(huobiGateway)
me.addGateway(okcoinGateway)
if system == 'Windows':
me.addGateway(femasGateway)
me.addGateway(xspeedGateway)
me.addGateway(sgitGateway)
me.addGateway(shzdGateway)
if system == 'Linux':
me.addGateway(xtpGateway)
# 添加上层应用
me.addApp(riskManager)
me.addApp(ctaStrategy)
me.addApp(spreadTrading)
# 创建主窗口
mw = MainWindow(me, ee)
mw.showMaximized()
# 在主线程中启动Qt事件循环
sys.exit(qApp.exec_())
if __name__ == '__main__':
main()
```
以上run.py脚本位于examples\VnTrader目录下更多使用方法方法请参考examples下的其他目录。
更多使用方法方法请参考examples下的其他目录。
---
@ -154,75 +227,6 @@ if __name__ == '__main__':
* [Visual Studio 2013](https://www.visualstudio.com/en-us/downloads/download-visual-studio-vs.aspx)这个就不多说了作者编译API封装用的是2013版本
---
### 项目结构
1. 丰富的Python交易和数据API接口vnpy.api基本覆盖了国内外所有常规交易品种股票、期货、期权、外汇、外盘、比特币具体包括
- CTPctp
- 飞马femas
- LTSlts
- 中信证券期权cshshlp
- 金仕达黄金ksgold
- 金仕达期权ksotp
- 飞鼠sgit
- 飞创xspeed
- QDPqdp
- 上海直达期货shzd
- Interactive Brokersib
- OANDAoanda
- OKCOINokcoin
- 火币huobi
- 链行lhang
- 通联数据datayes
2. 简洁易用的事件驱动引擎vnpy.event作为事件驱动型交易程序的核心
3. 支持服务器端数据推送的RPC框架vnpy.rpc用于实现多进程分布式架构的交易系统
4. 开箱即用的实盘交易平台框架vnpy.trader整合了多种交易接口并针对具体策略算法和功能开发提供了简洁易用的API用于快速构建交易员所需的量化交易程序应用举例
* 同时登录多个交易接口,在一套界面上监控多种市场的行情和多种资产账户的资金、持仓、委托、成交情况
* 支持跨市场套利CTP期货和LTS证券、境内外套利CTP期货和IB外盘、多市场数据整合实时预测走势CTP的股指期货数据、IB的外盘A50数据、Wind的行业指数数据等策略应用
* CTA策略引擎模块在保持易用性的同时允许用户针对CTA类策略运行过程中委托的报撤行为进行细粒度控制降低交易滑点、实现高频策略
* 实盘行情记录支持Tick和K线数据的落地用于策略开发回测以及实盘运行初始化
5. 数据相关的API接口vnpy.data用于构建和更新历史行情数据库目前包括
* 上海中期历史行情服务shcifco
* 通联数据API下载服务datayes
5. 关于vn.py项目的应用演示examples对于新手而言可以从这里开始学习vn.py项目的使用方式
5. 关于项目在实盘交易中的一些使用指南tutorial
6. vn.py项目的Docker镜像docker目前尚未完成
7. [官网](http://vnpy.org)和[知乎专栏](http://zhuanlan.zhihu.com/vn-py)内容包括vn.py项目的开发教程和Python在量化交易领域的应用研究等内容
7. 官方交流QQ群262656087管理较严格定期清除长期潜水的成员
---
### 贡献代码
@ -256,8 +260,13 @@ vn.py使用github托管其源代码如果希望贡献代码请使用github的
---
### 联系作者
作者知乎名用python的交易员想要联系作者可以通过知乎私信
### 其他内容
* [获取帮助](https://github.com/vnpy/vnpy/blob/dev/docs/SUPPORT.md)
* [社区行为准侧](https://github.com/vnpy/vnpy/blob/dev/docs/CODE_OF_CONDUCT.md)
* [Issue模板](https://github.com/vnpy/vnpy/blob/dev/docs/ISSUE_TEMPLATE.md)
* [PR模板](https://github.com/vnpy/vnpy/blob/dev/docs/PULL_REQUEST_TEMPLATE.md)
---
### License

View File

@ -1,18 +1,8 @@
# 获取帮助
* 操作系统: 如Windows 7或者Ubuntu 16.04
* Anaconda版本: 如Anaconda 4.0.0 Python 2.7 32位
* vn.py版本: 如v1.7发行版或者dev branch 20170920下载日期
在开发和使用vn.py项目的过程中遇到问题时获取帮助的渠道包括
## Issue类型
三选一Bug/Enhancement/Question
## 预期程序行为
## 实际程序行为
## 重现步骤
针对Bug类型Issue请提供具体重现步骤以及报错截图
* Github Issues[Issues页面](https://github.com/vnpy/vnpy/issues)
* 官方QQ群: 262656087
* 项目论坛:[维恩的派](http://www.vnpie.com))
* 项目邮箱: vn.py@foxmail.com

View File

@ -3,16 +3,31 @@
"tick":
[
["rb1801", "CTP"],
["m1801", "CTP"],
["SR801", "CTP"],
["IF1712", "CTP"],
["IH1712", "CTP"],
["IC1712", "CTP"]
],
"bar":
[
["BTC_CNY_SPOT", "OKCOIN"],
["LTC_CNY_SPOT", "OKCOIN"],
["ETH_CNY_SPOT", "OKCOIN"]
["rb1801", "CTP"],
["m1801", "CTP"],
["SR801", "CTP"],
["IF1712", "CTP"],
["IH1712", "CTP"],
["IC1712", "CTP"]
],
"active":
{
"rb.HOT": "rb1801",
"m.HOT": "m1801",
"SR.HOT": "SR801",
"IF.HOT": "IF1712",
"IH.HOT": "IH1712",
"IC.HOT": "IC1712"
}
}

View File

@ -12,5 +12,7 @@
"logActive": false,
"logLevel": "debug",
"logConsole": true,
"logFile": true
"logFile": true,
"tdPenalty": ["IF", "IH", "IC"]
}

View File

@ -0,0 +1,84 @@
# encoding: UTF-8
import json
from datetime import datetime, timedelta, time
from pymongo import MongoClient
from vnpy.trader.app.ctaStrategy.ctaBase import MINUTE_DB_NAME, TICK_DB_NAME
# 这里以商品期货为例
MORNING_START = time(9, 0)
MORNING_REST = time(10, 15)
MORNING_RESTART = time(10, 30)
MORNING_END = time(11, 30)
AFTERNOON_START = time(13, 30)
AFTERNOON_END = time(15, 0)
NIGHT_START = time(21, 0)
NIGHT_END = time(2, 30)
#----------------------------------------------------------------------
def cleanData(dbName, collectionName, start):
"""清洗数据"""
print u'\n清洗数据库:%s, 集合:%s, 起始日:%s' %(dbName, collectionName, start)
mc = MongoClient('localhost', 27017) # 创建MongoClient
cl = mc[dbName][collectionName] # 获取数据集合
d = {'datetime':{'$gte':start}} # 只过滤从start开始的数据
cx = cl.find(d) # 获取数据指针
# 遍历数据
for data in cx:
# 获取时间戳对象
dt = data['datetime'].time()
# 默认需要清洗
cleanRequired = True
# 如果在交易事件内,则为有效数据,无需清洗
if ((MORNING_START <= dt < MORNING_REST) or
(MORNING_RESTART <= dt < MORNING_END) or
(AFTERNOON_START <= dt < AFTERNOON_END) or
(dt >= NIGHT_START) or
(dt < NIGHT_END)):
cleanRequired = False
# 如果需要清洗
if cleanRequired:
print u'删除无效数据,时间戳:%s' %data['datetime']
cl.delete_one(data)
print u'清洗完成,数据库:%s, 集合:%s' %(dbName, collectionName)
#----------------------------------------------------------------------
def runDataCleaning():
"""运行数据清洗"""
print u'开始数据清洗工作'
# 加载配置
setting = {}
with open("DR_setting.json") as f:
setting = json.load(f)
# 遍历执行清洗
today = datetime.now()
start = today - timedelta(10) # 清洗过去10天数据
start.replace(hour=0, minute=0, second=0, microsecond=0)
for l in setting['tick']:
symbol = l[0]
cleanData(TICK_DB_NAME, symbol, start)
for l in setting['bar']:
symbol = l[0]
cleanData(MINUTE_DB_NAME, symbol, start)
print u'数据清洗工作完成'
if __name__ == '__main__':
runDataCleaning()

View File

@ -3,16 +3,31 @@
"tick":
[
["rb1801", "CTP"],
["m1801", "CTP"],
["SR801", "CTP"],
["IF1712", "CTP"],
["IH1712", "CTP"],
["IC1712", "CTP"]
],
"bar":
[
["BTC_CNY_SPOT", "OKCOIN"],
["LTC_CNY_SPOT", "OKCOIN"],
["ETH_CNY_SPOT", "OKCOIN"]
["rb1801", "CTP"],
["m1801", "CTP"],
["SR801", "CTP"],
["IF1712", "CTP"],
["IH1712", "CTP"],
["IC1712", "CTP"]
],
"active":
{
"rb.HOT": "rb1801",
"m.HOT": "m1801",
"SR.HOT": "SR801",
"IF.HOT": "IF1712",
"IH.HOT": "IH1712",
"IC.HOT": "IC1712"
}
}

View File

@ -219,7 +219,9 @@ class CtaEngine(object):
del self.workingStopOrderDict[stopOrderID]
# 从策略委托号集合中移除
self.strategyOrderDict[strategy.name].remove(stopOrderID)
s = self.strategyOrderDict[strategy.name]
if stopOrderID in s:
s.remove(stopOrderID)
# 通知策略
strategy.onStopOrder(so)
@ -251,7 +253,9 @@ class CtaEngine(object):
del self.workingStopOrderDict[so.stopOrderID]
# 从策略委托号集合中移除
self.strategyOrderDict[so.strategy.name].remove(so.stopOrderID)
s = self.strategyOrderDict[so.strategy.name]
if so.stopOrderID in s:
s.remove(so.stopOrderID)
# 更新停止单状态,并通知策略
so.status = STOPORDER_TRIGGERED
@ -285,12 +289,16 @@ class CtaEngine(object):
"""处理委托推送"""
order = event.dict_['data']
if order.vtOrderID in self.orderStrategyDict:
strategy = self.orderStrategyDict[order.vtOrderID]
vtOrderID = order.vtOrderID
if vtOrderID in self.orderStrategyDict:
strategy = self.orderStrategyDict[vtOrderID]
# 如果委托已经完成(拒单、撤销、全成),则从活动委托集合中移除
if order.status in self.STATUS_FINISHED:
self.strategyOrderDict[strategy.name].remove(order.vtOrderID)
s = self.strategyOrderDict[strategy.name]
if vtOrderID in s:
s.remove(vtOrderID)
self.callStrategyFunc(strategy, strategy.onOrder, order)
@ -613,8 +621,9 @@ class CtaEngine(object):
"""全部撤单"""
s = self.strategyOrderDict[name]
# 遍历集合,全部撤单
for orderID in s:
# 遍历列表,全部撤单
# 这里不能直接遍历集合s因为撤单时会修改s中的内容导致出错
for orderID in list(s):
if STOPORDERPREFIX in orderID:
self.cancelStopOrder(orderID)
else:

View File

@ -430,6 +430,44 @@ def loadTbCsv(fileName, dbName, symbol):
print bar.date, bar.time
print u'插入完毕,耗时:%s' % (time()-start)
#----------------------------------------------------------------------
def loadTbPlusCsv(fileName, dbName, symbol):
"""将TB极速版导出的csv格式的历史分钟数据插入到Mongo数据库中"""
import csv
start = time()
print u'开始读取CSV文件%s中的数据插入到%s%s' %(fileName, dbName, symbol)
# 锁定集合,并创建索引
client = pymongo.MongoClient(globalSetting['mongoHost'], globalSetting['mongoPort'])
collection = client[dbName][symbol]
collection.ensure_index([('datetime', pymongo.ASCENDING)], unique=True)
# 读取数据和插入到数据库
reader = csv.reader(file(fileName, 'r'))
for d in reader:
bar = VtBarData()
bar.vtSymbol = symbol
bar.symbol = symbol
bar.open = float(d[2])
bar.high = float(d[3])
bar.low = float(d[4])
bar.close = float(d[5])
bar.date = str(d[0])
tempstr=str(round(float(d[1])*10000)).split(".")[0].zfill(4)
bar.time = tempstr[:2]+":"+tempstr[2:4]+":00"
bar.datetime = datetime.strptime(bar.date + ' ' + bar.time, '%Y%m%d %H:%M:%S')
bar.volume = d[6]
bar.openInterest = d[7]
flt = {'datetime': bar.datetime}
collection.update_one(flt, {'$set':bar.__dict__}, upsert=True)
print bar.date, bar.time
print u'插入完毕,耗时:%s' % (time()-start)
#----------------------------------------------------------------------
def loadTdxCsv(fileName, dbName, symbol):

View File

@ -286,10 +286,10 @@ class TargetPosTemplate(CtaTemplate):
# 回测模式下,采用合并平仓和反向开仓委托的方式
if self.getEngineType() == ENGINETYPE_BACKTESTING:
if posChange > 0:
vtOrderID = self.buy(longPrice, abs(posChange))
l = self.buy(longPrice, abs(posChange))
else:
vtOrderID = self.short(shortPrice, abs(posChange))
self.orderList.append(vtOrderID)
l = self.short(shortPrice, abs(posChange))
self.orderList.extend(l)
# 实盘模式下,首先确保之前的委托都已经结束(全成、撤销)
# 然后先发平仓委托,等待成交后,再发送新的开仓委托
@ -301,16 +301,16 @@ class TargetPosTemplate(CtaTemplate):
# 买入
if posChange > 0:
if self.pos < 0:
vtOrderID = self.cover(longPrice, abs(self.pos))
l = self.cover(longPrice, abs(self.pos))
else:
vtOrderID = self.buy(longPrice, abs(posChange))
l = self.buy(longPrice, abs(posChange))
# 卖出
else:
if self.pos > 0:
vtOrderID = self.sell(shortPrice, abs(self.pos))
l = self.sell(shortPrice, abs(self.pos))
else:
vtOrderID = self.short(shortPrice, abs(posChange))
self.orderList.append(vtOrderID)
l = self.short(shortPrice, abs(posChange))
self.orderList.extend(l)
########################################################################

View File

@ -19,9 +19,10 @@ from vnpy.event import Event
from vnpy.trader.vtEvent import *
from vnpy.trader.vtFunction import todayDate, getJsonPath
from vnpy.trader.vtObject import VtSubscribeReq, VtLogData, VtBarData, VtTickData
from vnpy.trader.app.ctaStrategy.ctaTemplate import BarManager
from vnpy.trader.app.dataRecorder.drBase import *
from vnpy.trader.app.dataRecorder.language import text
from .drBase import *
from .language import text
########################################################################
@ -44,10 +45,10 @@ class DrEngine(object):
self.activeSymbolDict = {}
# Tick对象字典
self.tickDict = {}
self.tickSymbolSet = set()
# K线对象字典
self.barDict = {}
# K线合成器字典
self.bmDict = {}
# 配置字典
self.settingDict = OrderedDict()
@ -77,11 +78,13 @@ class DrEngine(object):
if not working:
return
# Tick记录配置
if 'tick' in drSetting:
l = drSetting['tick']
for setting in l:
symbol = setting[0]
gateway = setting[1]
vtSymbol = symbol
req = VtSubscribeReq()
@ -97,16 +100,17 @@ class DrEngine(object):
req.currency = setting[3]
req.productClass = setting[4]
self.mainEngine.subscribe(req, setting[1])
self.mainEngine.subscribe(req, gateway)
tick = VtTickData() # 该tick实例可以用于缓存部分数据目前未使用
self.tickDict[vtSymbol] = tick
#tick = VtTickData() # 该tick实例可以用于缓存部分数据目前未使用
#self.tickDict[vtSymbol] = tick
self.tickSymbolSet.add(vtSymbol)
# 保存到配置字典中
if vtSymbol not in self.settingDict:
d = {
'symbol': symbol,
'gateway': setting[1],
'gateway': gateway,
'tick': True
}
self.settingDict[vtSymbol] = d
@ -114,11 +118,13 @@ class DrEngine(object):
d = self.settingDict[vtSymbol]
d['tick'] = True
# 分钟线记录配置
if 'bar' in drSetting:
l = drSetting['bar']
for setting in l:
symbol = setting[0]
gateway = setting[1]
vtSymbol = symbol
req = VtSubscribeReq()
@ -132,158 +138,85 @@ class DrEngine(object):
req.currency = setting[3]
req.productClass = setting[4]
self.mainEngine.subscribe(req, setting[1])
bar = VtBarData()
self.barDict[vtSymbol] = bar
self.mainEngine.subscribe(req, gateway)
# 保存到配置字典中
if vtSymbol not in self.settingDict:
d = {
'symbol': symbol,
'gateway': setting[1],
'gateway': gateway,
'bar': True
}
self.settingDict[vtSymbol] = d
else:
d = self.settingDict[vtSymbol]
d['bar'] = True
d['bar'] = True
# 创建BarManager对象
self.bmDict[vtSymbol] = BarManager(self.onBar)
# 主力合约记录配置
if 'active' in drSetting:
d = drSetting['active']
# 注意这里的vtSymbol对于IB和LTS接口应该后缀.交易所
for activeSymbol, vtSymbol in d.items():
self.activeSymbolDict[vtSymbol] = activeSymbol
# 保存到配置字典中
if vtSymbol not in self.settingDict:
d = {
'symbol': symbol,
'gateway': setting[1],
'active': True
}
self.settingDict[vtSymbol] = d
else:
d = self.settingDict[vtSymbol]
d['active'] = True
self.activeSymbolDict = {vtSymbol:activeSymbol for activeSymbol, vtSymbol in d.items()}
##----------------------------------------------------------------------
#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
return self.settingDict, self.activeSymbolDict
#----------------------------------------------------------------------
def procecssTickEvent(self, event):
"""处理行情推送"""
"""处理行情事件"""
tick = event.dict_['data']
vtSymbol = tick.vtSymbol
# 转化Tick格式
# 生成datetime对象
if not tick.datetime:
tick.datetime = datetime.strptime(' '.join([tick.date, tick.time]), '%Y%m%d %H:%M:%S.%f')
self.onTick(tick)
# 更新Tick数据
if vtSymbol in self.tickDict:
bm = self.bmDict.get(vtSymbol, None)
if bm:
bm.updateTick(tick)
#----------------------------------------------------------------------
def onTick(self, tick):
"""Tick更新"""
vtSymbol = tick.vtSymbol
if vtSymbol in self.tickSymbolSet:
self.insertData(TICK_DB_NAME, vtSymbol, tick)
if vtSymbol in self.activeSymbolDict:
activeSymbol = self.activeSymbolDict[vtSymbol]
self.insertData(TICK_DB_NAME, activeSymbol, tick)
# 发出日志
self.writeDrLog(text.TICK_LOGGING_MESSAGE.format(symbol=tick.vtSymbol,
time=tick.time,
last=tick.lastPrice,
bid=tick.bidPrice1,
ask=tick.askPrice1))
# 更新分钟线数据
if vtSymbol in self.barDict:
bar = self.barDict[vtSymbol]
# 如果第一个TICK或者新的一分钟
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)
if vtSymbol in self.activeSymbolDict:
activeSymbol = self.activeSymbolDict[vtSymbol]
self.insertData(MINUTE_DB_NAME, activeSymbol, newBar)
self.writeDrLog(text.BAR_LOGGING_MESSAGE.format(symbol=bar.vtSymbol,
time=bar.time,
open=bar.open,
high=bar.high,
low=bar.low,
close=bar.close))
bar.vtSymbol = tick.vtSymbol
bar.symbol = tick.symbol
bar.exchange = tick.exchange
bar.open = tick.lastPrice
bar.high = tick.lastPrice
bar.low = tick.lastPrice
bar.close = tick.lastPrice
bar.date = tick.date
bar.time = tick.time
bar.datetime = tick.datetime.replace(second=0, microsecond=0)
bar.volume = tick.volume
bar.openInterest = tick.openInterest
# 否则继续累加新的K线
else:
bar.high = max(bar.high, tick.lastPrice)
bar.low = min(bar.low, tick.lastPrice)
bar.close = tick.lastPrice
#----------------------------------------------------------------------
def onBar(self, bar):
"""分钟线更新"""
vtSymbol = bar.vtSymbol
self.insertData(MINUTE_DB_NAME, vtSymbol, bar)
if vtSymbol in self.activeSymbolDict:
activeSymbol = self.activeSymbolDict[vtSymbol]
self.insertData(MINUTE_DB_NAME, activeSymbol, bar)
self.writeDrLog(text.BAR_LOGGING_MESSAGE.format(symbol=bar.vtSymbol,
time=bar.time,
open=bar.open,
high=bar.high,
low=bar.low,
close=bar.close))
#----------------------------------------------------------------------
def registerEvent(self):

View File

@ -115,7 +115,7 @@ class DrEngineManager(QtWidgets.QWidget):
#----------------------------------------------------------------------
def updateSetting(self):
"""显示引擎行情记录配置"""
setting = self.drEngine.getSetting()
setting, activeSetting = self.drEngine.getSetting()
for d in setting.values():
if 'tick' in d and d['tick']:
@ -128,14 +128,14 @@ class DrEngineManager(QtWidgets.QWidget):
self.barTable.setItem(0, 0, TableCell(d['symbol']))
self.barTable.setItem(0, 1, TableCell(d['gateway']))
if 'active'in d and d['active']:
self.activeTable.insertRow(0)
self.activeTable.setItem(0, 0, TableCell(d['active']))
self.activeTable.setItem(0, 1, TableCell(d['symbol']))
for vtSymbol, activeSymbol in activeSetting.items():
self.activeTable.insertRow(0)
self.activeTable.setItem(0, 0, TableCell(activeSymbol))
self.activeTable.setItem(0, 1, TableCell(vtSymbol))
self.tickTable.resizeColumnsToContents()
self.barTable.resizeColumnsToContents()
self.activeTable.resizeColumnsToContents()
self.tickTable.resizeColumnsToContents()
self.barTable.resizeColumnsToContents()
self.activeTable.resizeColumnsToContents()

View File

@ -356,6 +356,7 @@ class StAlgoEngine(object):
req = VtOrderReq()
req.symbol = contract.symbol
req.exchange = contract.exchange
req.vtSymbol = contract.vtSymbol
req.direction = direction
req.offset = offset
req.volume = int(volume)

View File

@ -41,6 +41,9 @@ PRODUCT_FOREX = u'外汇'
PRODUCT_UNKNOWN = u'未知'
PRODUCT_SPOT = u'现货'
PRODUCT_DEFER = u'延期'
PRODUCT_ETF = u'ETF'
PRODUCT_WARRANT = u'权证'
PRODUCT_BOND = u'债券'
PRODUCT_NONE = ''
# 价格类型常量

View File

@ -1046,6 +1046,7 @@ class TradingWidget(QtWidgets.QFrame):
req = VtOrderReq()
req.symbol = symbol
req.exchange = exchange
req.vtSymbol = contract.vtSymbol
req.price = self.spinPrice.value()
req.volume = self.spinVolume.value()
req.direction = unicode(self.comboDirection.currentText())

View File

@ -477,8 +477,10 @@ class DataEngine(object):
contract = self.getContract(vtSymbol)
if contract:
detail.exchange = contract.exchange
# 上期所合约
if contract.exchange is EXCHANGE_SHFE:
if contract.exchange == EXCHANGE_SHFE:
detail.mode = detail.MODE_SHFE
# 检查是否有平今惩罚
@ -491,10 +493,7 @@ class DataEngine(object):
#----------------------------------------------------------------------
def updateOrderReq(self, req, vtOrderID):
"""委托请求更新"""
if req.exchange:
vtSymbol = '.'.join([req.symbol, req.exchange])
else:
vtSymbol = req.symbol
vtSymbol = req.vtSymbol
detail = self.getPositionDetail(vtSymbol)
detail.updateOrderReq(req, vtOrderID)
@ -519,6 +518,16 @@ class LogEngine(object):
LEVEL_WARN = logging.WARN
LEVEL_ERROR = logging.ERROR
LEVEL_CRITICAL = logging.CRITICAL
# 单例对象
instance = None
#----------------------------------------------------------------------
def __new__(cls, *args, **kwargs):
"""创建对象,保证单例"""
if not cls.instance:
cls.instance = super(LogEngine, cls).__new__(cls, *args, **kwargs)
return cls.instance
#----------------------------------------------------------------------
def __init__(self):
@ -637,6 +646,7 @@ class PositionDetail(object):
self.shortTdFrozen = EMPTY_INT
self.mode = self.MODE_NORMAL
self.exchange = EMPTY_STRING
self.workingOrderDict = {}
@ -654,14 +664,18 @@ class PositionDetail(object):
# 平昨
elif trade.offset is OFFSET_CLOSEYESTERDAY:
self.shortYd -= trade.volume
# 平仓(非上期所,优先平今)
# 平仓
elif trade.offset is OFFSET_CLOSE:
self.shortTd -= trade.volume
if self.shortTd < 0:
self.shortYd += self.shortTd
self.shortTd = 0
# 上期所等同于平昨
if self.exchange is EXCHANGE_SHFE:
self.shortYd -= trade.volume
# 非上期所,优先平今
else:
self.shortTd -= trade.volume
if self.shortTd < 0:
self.shortYd += self.shortTd
self.shortTd = 0
# 空头
elif trade.direction is DIRECTION_SHORT:
# 开仓
@ -675,11 +689,16 @@ class PositionDetail(object):
self.longYd -= trade.volume
# 平仓
elif trade.offset is OFFSET_CLOSE:
self.longTd -= trade.volume
if self.longTd < 0:
self.longYd += self.longTd
self.longTd = 0
# 上期所等同于平昨
if self.exchange is EXCHANGE_SHFE:
self.longYd -= trade.volume
# 非上期所,优先平今
else:
self.longTd -= trade.volume
if self.longTd < 0:
self.longYd += self.longTd
self.longTd = 0
# 汇总今昨
self.calculatePosition()
@ -710,14 +729,13 @@ class PositionDetail(object):
self.shortPos = pos.position
self.shortYd = pos.ydPosition
self.shortTd = self.shortPos - self.shortYd
self.output()
#----------------------------------------------------------------------
def updateOrderReq(self, req, vtOrderID):
"""发单更新"""
if req.exchange:
vtSymbol = '.'.join([req.symbol, req.exchange])
else:
vtSymbol = req.symbol
vtSymbol = req.vtSymbol
# 基于请求生成委托对象
order = VtOrderData()
@ -867,12 +885,18 @@ class PositionDetail(object):
return [req]
# 如果平仓量小于昨可用,全部平昨
elif req.volume <= ydAvailable:
req.offset = OFFSET_CLOSE # OFFSET_CLOSE在上期所等于平昨
if self.exchange is EXCHANGE_SHFE:
req.offset = OFFSET_CLOSEYESTERDAY
else:
req.offset = OFFSET_CLOSE
return [req]
# 平仓量大于昨可用,平仓再反向开仓
else:
reqClose = copy(req)
reqClose.offset = OFFSET_CLOSE
if self.exchange is EXCHANGE_SHFE:
req.offset = OFFSET_CLOSEYESTERDAY
else:
req.offset = OFFSET_CLOSE
reqClose.volume = ydAvailable
reqOpen = copy(req)

View File

@ -292,6 +292,7 @@ class VtOrderReq(object):
"""Constructor"""
self.symbol = EMPTY_STRING # 代码
self.exchange = EMPTY_STRING # 交易所
self.vtSymbol = EMPTY_STRING # VT合约代码
self.price = EMPTY_FLOAT # 价格
self.volume = EMPTY_INT # 数量
@ -318,6 +319,7 @@ class VtCancelOrderReq(object):
"""Constructor"""
self.symbol = EMPTY_STRING # 代码
self.exchange = EMPTY_STRING # 交易所
self.vtSymbol = EMPTY_STRING # VT合约代码
# 以下字段主要和CTP、LTS类接口相关
self.orderID = EMPTY_STRING # 报单号