vnpy 查询持仓量_VNPY,从发送交易指令到交易所的源代码分析
在尝试写tick级别的策略,由于交易反馈时间要求高,感觉需要对单个order的事有个全面了解,就花了些时间尝试性去分析了VNPY中 从发送交易指令(sendOrder())到交易所,和接收成交返回信息(onOrder()/onTrade())的代码。如果有错误或者遗漏,请指正。这里先将发送
在策略中,一般不直接调用sendOrder(), 而且用四个二次封装函数函数,这些都是在class CtaTemplate中定义的,这里面主要区别就是sendorder()函数的中ordertype制定不一样,用来区分是买开卖开等交易类型。
返回一个vtOrderIDList, 这个list里面包含vtOrderID,这个是个内部给号,可以用做追踪同一个order的状态。
def buy(self, price, volume, stop=False):
"""买开"""
return self.sendOrder(CTAORDER_BUY, price, volume, stop)
#----------------------------------------------------------------------
def sell(self, price, volume, stop=False):
"""卖平"""
return self.sendOrder(CTAORDER_SELL, price, volume, stop)
#----------------------------------------------------------------------
def short(self, price, volume, stop=False):
"""卖开"""
return self.sendOrder(CTAORDER_SHORT, price, volume, stop)
#----------------------------------------------------------------------
def cover(self, price, volume, stop=False):
"""买平"""
return self.sendOrder(CTAORDER_COVER, price, volume, stop)
2. 接下来我们看看那sendOrder()源码,还在class CtaTemplate中定义;如果stop为True是本地停止单,这个停止单并没有发送给交易所,而是存储在内部,使用ctaEngine.sendStopOrder()函数; 否则这直接发送到交易所,使用ctaEngine.sendStopOrder函数。
这里会返回一个vtOrderIDList, 这个list里面包含vtOrderID,然后在被上面返回。这里补充一下,对于StopOrder真正触发的交易通常是涨停价或者跌停价发出的市价单(Market price),参数price只是触发条件;而普通sendOrder是真正按照参数price的限价单(Limit price)
def sendOrder(self, orderType, price, volume, stop=False):
"""发送委托"""
if self.trading:
# 如果stop为True,则意味着发本地停止单
if stop:
vtOrderIDList = self.ctaEngine.sendStopOrder(self.vtSymbol, orderType, price, volume, self)
else:
vtOrderIDList = self.ctaEngine.sendOrder(self.vtSymbol, orderType, price, volume, self)
return vtOrderIDList
else:
# 交易停止时发单返回空字符串
return []
3. 这里我们首先看看ctaEngine.sendStopOrder()函数,在class CtaEngine中定义的,首先实例初始化时候定义了两个字典,用来存放stoporder,区别一个是停止单撤销后删除,一个不会删除;还定义了一个字典,策略对应的所有orderID。
def __init__(self, mainEngine, eventEngine):
………
# 本地停止单字典
# key为stopOrderID,value为stopOrder对象
self.stopOrderDict = {} # 停止单撤销后不会从本字典中删除
self.workingStopOrderDict = {} # 停止单撤销后会从本字典中删除
# 保存策略名称和委托号列表的字典
# key为name,value为保存orderID(限价+本地停止)的集合
self.strategyOrderDict = {}
………
然后在函数 sendStopOrder 中,首先记录给本地停止单一个专门编号,就是前缀加上顺序编号,其中STOPORDERPREFIX 是 ‘CtaStopOrder.’,那么第一条本地编码就是 ‘ CtaStopOrder. 1′ 。 后面是这个单据信息;这里可以发现 orderType 其实是一个 direction 和 offset 的组合,交易方向 direction 有 Long 、 short 两个情况,交易对 offset 有 open 和 close 两个情况。组合就是上面买开,卖平等等。然后把这个 stoporder 放入字典,等待符合价格情况到达触发真正的发单。这里返回本地编码作为 vtOrderIDList 。
def sendStopOrder(self, vtSymbol, orderType, price, volume, strategy):
"""发停止单(本地实现)"""
self.stopOrderCount += 1
stopOrderID = STOPORDERPREFIX + str(self.stopOrderCount)
so = StopOrder()
so.vtSymbol = vtSymbol
so.orderType = orderType
so.price = price
so.volume = volume
so.strategy = strategy
so.stopOrderID = stopOrderID
so.status = STOPORDER_WAITING
if orderType == CTAORDER_BUY:
so.direction = DIRECTION_LONG
so.offset = OFFSET_OPEN
elif orderType == CTAORDER_SELL:
so.direction = DIRECTION_SHORT
so.offset = OFFSET_CLOSE
elif orderType == CTAORDER_SHORT:
so.direction = DIRECTION_SHORT
so.offset = OFFSET_OPEN
elif orderType == CTAORDER_COVER:
so.direction = DIRECTION_LONG
so.offset = OFFSET_CLOSE
# 保存stopOrder对象到字典中
self.stopOrderDict[stopOrderID] = so
self.workingStopOrderDict[stopOrderID] = so
# 保存stopOrderID到策略委托号集合中
self.strategyOrderDict[strategy.name].add(stopOrderID)
# 推送停止单状态
strategy.onStopOrder(so)
return [stopOrderID]
4. 下面是processStopOrder () 函数,也在 class CtaEngine中定义的,主要是当行情符合时候如何发送真正交易指令,因为 stopOrderID 不是 tick 交易重点,这里简单讲讲,具体请看源码。
当接收到 tick 时候,会查看 tick.vtSymbol ,是不是存在 workingStopOrderDict 的 so .vtSymbol 有一样的,如果有,再看 tick.lastPrice 价格是否可以满足触发阈值,如果满足,根据原来 so 的交易 Direction , Long 按照涨停价, Short 按照跌停价发出委托。然后从 workingStopOrderDic 和strategyOrderDict移除该 so ,并更新 so 状态,并触发事件 onStopOrder(so).
这里发现,so只是只是按照涨停价发单给交易所,并没有确保成绩,而且市价委托的实际交易vtOrderID也没有返回;从tick交易角度,再收到tick后再发送交易,本事也是有了延迟一tick。所以一般tick级别交易不建议使用stoporder。
def processStopOrder(self, tick):
"""收到行情后处理本地停止单(检查是否要立即发出)"""
vtSymbol = tick.vtSymbol
# 首先检查是否有策略交易该合约
if vtSymbol in self.tickStrategyDict:
# 遍历等待中的停止单,检查是否会被触发
for so in self.workingStopOrderDict.values():
if so.vtSymbol == vtSymbol:
longTriggered = so.direction==DIRECTION_LONG and tick.lastPrice>=so.price # 多头停止单被触发
shortTriggered = so.direction==DIRECTION_SHORT and tick.lastPrice<=so.price # 空头停止单被触发
if longTriggered or shortTriggered:
# 买入和卖出分别以涨停跌停价发单(模拟市价单)
if so.direction==DIRECTION_LONG:
price = tick.upperLimit
else:
price = tick.lowerLimit
# 发出市价委托
self.sendOrder(so.vtSymbol, so.orderType, price, so.volume, so.strategy)
# 从活动停止单字典中移除该停止单
del self.workingStopOrderDict[so.stopOrderID]
# 从策略委托号集合中移除
s = self.strategyOrderDict[so.strategy.name]
if so.stopOrderID in s:
s.remove(so.stopOrderID)
# 更新停止单状态,并通知策略
so.status = STOPORDER_TRIGGERED
so.strategy.onStopOrder(so)
5. 前面说了这么多,终于到了正主 sendOrder(), 也在 class CtaEngine中定义的。代码较长,下面做了写缩减。
1 )通过mainEngine.getContract获得这个品种的合约的信息,包括这个合约的名称,接口名 gateway (国内期货就是 ctp ),交易所,最小价格变动等信息;
2 )创建一个 class VtOrderReq的对象 req ,在vtObject.py中,这个 py 包括很多事务类的定义;然后赋值,包括 contract 获得信息,交易手数,和 price ,和 priceType ,这里只有限价单。
3 )根据 orderType 赋值 direction 和 offset ,之前 sendStopOrder 中已经说了,就不重复。
4 )然后跳到 mainEngine.convertOrderReq(req) ,这里代码比较跳,分析下来,如果之前没有持仓,或者是直接返回 [req] ;如果有持仓就调用PositionDetail . convertOrderReq(req) ,这个时候如果是平仓操作,就分析持仓量,和平今和平昨等不同操作返回拆分的出来 [ reqTd , reqYd ] ,这里不展开。
5) 如果上一部没有返回 [req] ,则委托有问题,直接返回控制。如果有 [req] ,因为存在多个 req 情况,就遍历每个 req ,使用 mainEngine.sendOrder 发单,并保存返回的 vtOrderID 到 orderStrategyDict [], strategyOrderDict [] 两个字典;然后把 vtOrderIDList 返回。
def sendOrder(self, vtSymbol, orderType, price, volume, strategy):
"""发单"""
contract = self.mainEngine.getContract(vtSymbol)
req = VtOrderReq()
req.symbol = contract.symbol
……
# 设计为CTA引擎发出的委托只允许使用限价单
req.priceType = PRICETYPE_LIMITPRICE
# CTA委托类型映射
if orderType == CTAORDER_BUY:
req.direction = DIRECTION_LONG
req.offset = OFFSET_OPEN
……
# 委托转换
reqList = self.mainEngine.convertOrderReq(req)
vtOrderIDList = []
if not reqList:
return vtOrderIDList
for convertedReq in reqList:
vtOrderID = self.mainEngine.sendOrder(convertedReq, contract.gatewayName) # 发单
self.orderStrategyDict[vtOrderID] = strategy # 保存vtOrderID和策略的映射关系
self.strategyOrderDict[strategy.name].add(vtOrderID) # 添加到策略委托号集合中
vtOrderIDList.append(vtOrderID)
self.writeCtaLog(u'策略%s发送委托,%s,%s,%s@%s'
%(strategy.name, vtSymbol, req.direction, volume, price))
return vtOrderIDList
6. 在mainEngine.sendOrder中,这里不列举代码了,首先进行风控,如果到阈值就不发单,然后看 gateway 是否存在,如果存在,就调用 gateway. sendOrder(orderReq)方法;下面用 ctpgateway 说明。class CtpGateway(VtGateway)是 VtGateway 是继承,把主要发单,返回上面都实现,同时对于不同的接口,比如外汇,数字货币,只要用一套接口标准就可以,典型继承使用。
CtpGateway.sendOrder实际是调用class CtpTdApi(TdApi)的,这个就是一套ctp交易交口,代码很简单,最后是调用封装好C++的ctp接口reqOrderInsert()。最关键返回的vtOrderID是接口名+顺序数。
def sendOrder(self, orderReq):
"""发单"""
self.reqID += 1
self.orderRef += 1
req = {}
req['InstrumentID'] = orderReq.symbol
req['LimitPrice'] = orderReq.price
req['VolumeTotalOriginal'] = orderReq.volume
# 下面如果由于传入的类型本接口不支持,则会返回空字符串
req['OrderPriceType'] = priceTypeMap.get(orderReq.priceType, '')
.......
# 判断FAK和FOK
if orderReq.priceType == PRICETYPE_FAK:
req['OrderPriceType'] = defineDict["THOST_FTDC_OPT_LimitPrice"]
req['TimeCondition'] = defineDict['THOST_FTDC_TC_IOC']
req['VolumeCondition'] = defineDict['THOST_FTDC_VC_AV']
if orderReq.priceType == PRICETYPE_FOK:
req['OrderPriceType'] = defineDict["THOST_FTDC_OPT_LimitPrice"]
req['TimeCondition'] = defineDict['THOST_FTDC_TC_IOC']
req['VolumeCondition'] = defineDict['THOST_FTDC_VC_CV']
self.reqOrderInsert(req, self.reqID)
# 返回订单号(字符串),便于某些算法进行动态管理
vtOrderID = '.'.join([self.gatewayName, str(self.orderRef)])
return vtOrderID
整个流程下来,不考虑stoporder,是ctaTemplate -> CtaEngine ->mainEngine ->ctpgateway ->CtpTdApi, 传到C++封装的接口。返回的就是vtOrderID; 因为存在平昨,平今还有锁仓,反手等拆分情况,返回的可能是一组。
vnpy 查询持仓量_VNPY,从发送交易指令到交易所的源代码分析相关推荐
- vnpy 查询持仓量_VNPY源码(四)DataRecorder
VNPY源码学习系列文章: 一.源码 """ 注册EVENT_TICK.EVENT_CONTRACT,当有EVENT_TICK的时候,调用process_contract ...
- vnpy 查询持仓量_vn.py 数据入库
如何编写一个python脚本将本地.csv 文件导入数据库是vn.py论坛中新用户提问较多的问题之一.本文的主要目的是帮助新用户解决数据入库问题,以便用户可以快速进入量化策略的开发. 本文主要分为三大 ...
- vnpy 查询持仓量_持仓回报中的冻结量读取可能有误
环境 操作系统: Windows 7 Anaconda版本: Anaconda 4.0.0 Python 2.7 32位 vn.py版本: From 1.5 to latest Issue类型 Bug ...
- 使用掘金获取当前正在交易的合约并可视化价格和持仓量
记录一下常用的代码. 使用掘金获取当前正在交易的合约. 前提:安装有掘金的客户端和相应的库. 导入模块 如果仅仅是获取数据,那么只导入pandas和MyKlines(自定义的)模块就可以了. 其他模块 ...
- 期货市场持仓量交易法则(期货持仓量公式)
期货 中持仓量和成交量是怎么算的? 因为期货是T+0,所以交易量大于持仓量很正常.成交量就是当年成交的累计手数.持仓量是多空共同开仓数. 持仓量(inventory)是指买卖双方开立的还未实行反向平仓 ...
- 中用BBP公式计算_散户如何计算庄家的持仓量和持仓成本?
计算庄家的持仓量和持仓成本,可以帮助散户判断庄家目前的坐庄过程处于何种阶段.如果庄家处于建仓阶段,散户就应该找机会跟进:如果庄家处于出货阶段,散户就应该赶快抛出手中筹码. 一:计算庄家持仓量 在股市交 ...
- 成交量、持仓量与价格运动的关系
成交量是指某段时间内成交期货或期权合约的总数.通常以每个交易日来计算.一个成交清淡,持仓量微薄的市场很难吸引投资人入市.因为这样的市场会使你痴情空付,良机尽失.相应地,交投旺盛的市场也很难被大户所操纵 ...
- 期货成交量与持仓量(期货成交量与持仓量的秘密)
期货 中持仓量和成交量是怎么算的? 因为期货是T+0,所以交易量大于持仓量很正常.成交量就是当年成交的累计手数.持仓量是多空共同开仓数. 持仓量(inventory)是指买卖双方开立的还未实行反向平仓 ...
- 期货的交易量和持仓量(期货的持仓量和成交量)
期货的成交量,持仓量,日增仓都各代表什么意思 期货的成交量是指已经成交的合约数量:持仓量也称空盘量或未平仓合约量,是指买入或卖出后尚未对冲及进行实物交割的某种商品期货合约的数量:日增仓是当天的持仓的增 ...
最新文章
- 轻松构建复杂数据集,永洪自服务数据查询功能详解
- Python中实现ASCII码与字符相互转换
- Codeup-问题 B: 采药
- 第4章-机器学习基础
- php生成extjs下拉树json数据格式
- 网络知识 | 《图解HTTP》读书笔记(上)
- 单片机c语言模块化实例程序设计,单片机C语言模块化设计
- DPDK网络处理模块划分
- MySQL数据库最大连接数
- SpringBoot开发流程
- TakeColor取色器的使用
- 微软“断臂求生”,能实现绝地反击吗?
- 智点软件定制**服装工厂衣服加工计件工资软件的方案
- arm开发板无法使用dns解析问题
- gets与puts函数
- 第1章 Redis查阅官网和基本配置
- JAVA的一些学习方法
- 计算机开机怎么设置网络连接,电脑怎么设置开机自动连接宽带
- cinder云硬盘备份恢复
- 人脸识别: 人脸数据集大全
热门文章
- eclipse jsp 调用java_使用Eclipse开发JSP
- 基于idea-SSM的民宿预约网站客房预订管理系统-客户预订(javaweb-php-asp.netC#-j2ee-springboot)
- 零尽其用,尾随不落——探究力扣题目“移除字符串中的尾随零”的解题思路
- JAVA课程设计--图灵聊天机器人
- C语言32个关键字-最详解释
- 2018-2019-2 20165212《网络对抗技术》Exp2 后门原理与实践
- 计算机视觉 java 如何选择_计算机视觉中光源的选择
- Linux中$1、$@等含义是什么
- 医学健康数据分析与挖掘(一)—— R语言实战
- Tortoisegit小乌龟如何设置中文