commit
007f6297ed
183
README.md
183
README.md
@ -2,9 +2,78 @@
|
||||
|
||||
|
||||
---
|
||||
|
||||
### 简介
|
||||
|
||||
vn.py是一套基于Python的开源量化交易程序开发框架,起源于国内私募的自主量化交易系统。2015年初项目启动时只是单纯的交易API接口的Python封装。随着业内关注度的上升和社区不断的贡献,目前已经一步步成长为一套全面的交易程序开发框架,用户群体也日渐多样化,包括私募基金、券商自营和资管、期货资管和子公司、高校研究机构和专业个人投资者等。
|
||||
vn.py是基于Python的开源量化交易程序开发框架,起源于国内私募的自主量化交易系统。2015年初项目启动时只是单纯的交易API接口的Python封装。随着业内关注度的上升和社区不断的贡献,目前已经成长为一套全功能的交易程序开发框架,用户群体也日渐多样化,包括私募基金、券商自营和资管、期货资管和子公司、高校研究机构和专业个人投资者等。
|
||||
|
||||
---
|
||||
|
||||
### 项目结构
|
||||
|
||||
1. 丰富的Python交易和数据API接口(vnpy.api),基本覆盖了国内外所有常规交易品种(股票、期货、期权、外汇、外盘、比特币),具体包括:
|
||||
|
||||
- CTP(ctp)
|
||||
|
||||
- 飞马(femas)
|
||||
|
||||
- 中泰证券XTP(xtp)
|
||||
|
||||
- 中信证券期权(cshshlp)
|
||||
|
||||
- 金仕达黄金(ksgold)
|
||||
|
||||
- 金仕达期权(ksotp)
|
||||
|
||||
- 飞鼠(sgit)
|
||||
|
||||
- 飞创(xspeed)
|
||||
|
||||
- QDP(qdp)
|
||||
|
||||
- 上海直达期货(shzd)
|
||||
|
||||
- Interactive Brokers(ib)
|
||||
|
||||
- OANDA(oanda)
|
||||
|
||||
- OKCOIN(okcoin)
|
||||
|
||||
- 火币(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),基本覆盖了国内外所有常规交易品种(股票、期货、期权、外汇、外盘、比特币),具体包括:
|
||||
|
||||
- CTP(ctp)
|
||||
|
||||
- 飞马(femas)
|
||||
|
||||
- LTS(lts)
|
||||
|
||||
- 中信证券期权(cshshlp)
|
||||
|
||||
- 金仕达黄金(ksgold)
|
||||
|
||||
- 金仕达期权(ksotp)
|
||||
|
||||
- 飞鼠(sgit)
|
||||
|
||||
- 飞创(xspeed)
|
||||
|
||||
- QDP(qdp)
|
||||
|
||||
- 上海直达期货(shzd)
|
||||
|
||||
- Interactive Brokers(ib)
|
||||
|
||||
- OANDA(oanda)
|
||||
|
||||
- OKCOIN(okcoin)
|
||||
|
||||
- 火币(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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
@ -12,5 +12,7 @@
|
||||
"logActive": false,
|
||||
"logLevel": "debug",
|
||||
"logConsole": true,
|
||||
"logFile": true
|
||||
"logFile": true,
|
||||
|
||||
"tdPenalty": ["IF", "IH", "IC"]
|
||||
}
|
84
examples/DataRecording/runDataCleaning.py
Normal file
84
examples/DataRecording/runDataCleaning.py
Normal 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()
|
@ -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"
|
||||
}
|
||||
}
|
@ -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:
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
||||
|
||||
########################################################################
|
||||
|
@ -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):
|
||||
|
@ -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()
|
||||
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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 = ''
|
||||
|
||||
# 价格类型常量
|
||||
|
@ -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())
|
||||
|
@ -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)
|
||||
|
@ -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 # 报单号
|
||||
|
Loading…
Reference in New Issue
Block a user