VNPY中如何实现从发送交易指令到交易所的源代码

技术VNPY中如何实现从发送交易指令到交易所的源代码小编给大家分享一下VNPY中如何实现从发送交易指令到交易所的源代码,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让

边肖将与您分享如何在VNPY中实现从发送交易指令到交易所的源代码。相信大部分人还不太了解,所以分享这篇文章给大家参考。希望你看完这篇文章会有很多收获。我们一起来看看吧!

在策略中,sendOrder()一般不直接调用,

此外,使用了四个二级封装函数,它们都在类CtaTemplate中定义。主要区别是sendorder()函数中的ordertype不同,用于区分买入、卖出、开仓等交易类型。

返回一个vtOrderIDList。此列表包含vtOrderID。这是一个内部号码,可用于跟踪同一订单的状态。

defbuy(自我,价格,成交量,停止=假):

“买吧”

returnself . send order(CTAORDER _ BUY,价格,成交量,止损)

# -

defsell(自我,价格,数量,停止=假):

“出售公寓”

returnself . send order(CTAORDER _ SELL,价格,成交量,止损)

# -

defshort(自我,价格,数量,停止=假):

“出售”

returnself . send order(CTAORDER _ SHORT,价格,成交量,止损)

# -

defcover(自我,价格,成交量,停止=假):

“买套公寓”

回归自我。sendOrder (CTA order _ cover,价格,成交量,止损)2。接下来,让我们看看sendorder()源代码,它也是在类CtaTemplate中定义的;如果stop是本地止损单,这个止损单不会发送到交易所,而是使用ctaEngine.sendStopOrder()函数存储在内部;否则,将使用ctaEngine.sendStopOrder函数直接将此消息发送到交换机。

这里将返回一个vtOrderIDList。该列表包含vtOrderID,然后将在其上返回。这里需要补充的是,StopOrder真正触发的交易,通常是每日限价或每日限价(马

rket 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中如何实现从发送交易指令到交易所的源代码”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/107924.html

(0)

相关推荐

  • mysql5.7无法开启二进制日志的示例分析

    技术mysql5.7无法开启二进制日志的示例分析小编给大家分享一下mysql5.7无法开启二进制日志的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起

    攻略 2021年11月6日
  • python二叉树遍历教程(二叉树python修改指定结点算法)

    技术怎么返回python二叉树的中序遍历今天就跟大家聊聊有关怎么返回python二叉树的中序遍历,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。1.给定一个二叉树,

    攻略 2021年12月13日
  • C语言再续编译执行分析

    技术C语言再续编译执行分析这篇文章主要讲解了“C语言再续编译执行分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C语言再续编译执行分析”吧!从键盘上读取a.out命令当

    攻略 2021年11月18日
  • asic计算方法(asic最简指令集)

    技术如何浅析ASIC物理实现概念中的Milkyway database如何浅析ASIC物理实现概念中的Milkyway database,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇

    攻略 2021年12月18日
  • SpringCloud分布式微服务b2b2c电子商务中docker-feign-hystrix-ribbon的示例分析

    技术SpringCloud分布式微服务b2b2c电子商务中docker-feign-hystrix-ribbon的示例分析小编给大家分享一下SpringCloud分布式微服务b2b2c电子商务中docker-feign-

    攻略 2021年11月19日
  • 怎么搭建Solr8与ikanalyzer

    技术怎么搭建Solr8与ikanalyzer今天就跟大家聊聊有关怎么搭建Solr8与ikanalyzer,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。服务安装并启

    攻略 2021年10月19日