溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

VNPY中如何實(shí)現(xiàn)從發(fā)送交易指令到交易所的源代碼

發(fā)布時(shí)間:2021-11-20 17:24:15 來(lái)源:億速云 閱讀:146 作者:小新 欄目:編程語(yǔ)言

小編給大家分享一下VNPY中如何實(shí)現(xiàn)從發(fā)送交易指令到交易所的源代碼,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

  1. 在策略中,一般不直接調(diào)用sendOrder(), 而且用四個(gè)二次封裝函數(shù)函數(shù),這些都是在class      CtaTemplate中定義的,這里面主要區(qū)別就是sendorder()函數(shù)的中ordertype制定不一樣,用來(lái)區(qū)分是買開賣開等交易類型。

    返回一個(gè)vtOrderIDList, 這個(gè)list里面包含vtOrderID,這個(gè)是個(gè)內(nèi)部給號(hào),可以用做追蹤同一個(gè)order的狀態(tài)。

    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.  接下來(lái)我們看看那sendOrder()源碼,還在class CtaTemplate中定義;如果stop為True是本地停止單,這個(gè)停止單并沒(méi)有發(fā)送給交易所,而是存儲(chǔ)在內(nèi)部,使用ctaEngine.sendStopOrder()函數(shù); 否則這直接發(fā)送到交易所,使用ctaEngine.sendStopOrder函數(shù)。

      這里會(huì)返回一個(gè)vtOrderIDList, 這個(gè)list里面包含vtOrderID,然后在被上面返回。這里補(bǔ)充一下,對(duì)于StopOrder真正觸發(fā)的交易通常是漲停價(jià)或者跌停價(jià)發(fā)出的市價(jià)單(Market price),參數(shù)price只是觸發(fā)條件;而普通sendOrder是真正按照參數(shù)price的限價(jià)單(Limit price)

    def sendOrder(self, orderType, price, volume, stop=False):
        """發(fā)送委托"""
        if self.trading:
            # 如果stop為True,則意味著發(fā)本地停止單
            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:
            # 交易停止時(shí)發(fā)單返回空字符串
            return []

3. 這里我們首先看看ctaEngine.sendStopOrder()函數(shù),在class CtaEngine中定義的,首先實(shí)例初始化時(shí)候定義了兩個(gè)字典,用來(lái)存放stoporder,區(qū)別一個(gè)是停止單撤銷后刪除,一個(gè)不會(huì)刪除;還定義了一個(gè)字典,策略對(duì)應(yīng)的所有orderID。

def __init__(self, mainEngine, eventEngine):
   ………
       # 本地停止單字典
       # key為stopOrderID,value為stopOrder對(duì)象
       self.stopOrderDict = {}             # 停止單撤銷后不會(huì)從本字典中刪除
       self.workingStopOrderDict = {}      # 停止單撤銷后會(huì)從本字典中刪除
  
   # 保存策略名稱和委托號(hào)列表的字典
   # key為name,value為保存orderID(限價(jià)+本地停止)的集合
   self.strategyOrderDict = {}
    ………

然后在函數(shù)sendStopOrder中,首先記錄給本地停止單一個(gè)專門編號(hào),就是前綴加上順序編號(hào),其中STOPORDERPREFIX 是 'CtaStopOrder.',那么第一條本地編碼就是'CtaStopOrder.1'。 后面是這個(gè)單據(jù)信息;這里可以發(fā)現(xiàn)orderType其實(shí)是一個(gè)direction和offset的組合,交易方向direction有Long、short兩個(gè)情況,交易對(duì)offset有open和close兩個(gè)情況。組合就是上面買開,賣平等等。然后把這個(gè)stoporder放入字典,等待符合價(jià)格情況到達(dá)觸發(fā)真正的發(fā)單。這里返回本地編碼作為vtOrderIDList。

def sendStopOrder(self, vtSymbol, orderType, price, volume, strategy):
        """發(fā)停止單(本地實(shí)現(xiàn))"""
        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對(duì)象到字典中
        self.stopOrderDict[stopOrderID] = so
        self.workingStopOrderDict[stopOrderID] = so
        
        # 保存stopOrderID到策略委托號(hào)集合中
        self.strategyOrderDict[strategy.name].add(stopOrderID)
        
        # 推送停止單狀態(tài)
        strategy.onStopOrder(so)
        
        return [stopOrderID]

4. 下面是processStopOrder()函數(shù),也在class CtaEngine中定義的,主要是當(dāng)行情符合時(shí)候如何發(fā)送真正交易指令,因?yàn)閟topOrderID不是tick交易重點(diǎn),這里簡(jiǎn)單講講,具體請(qǐng)看源碼。

當(dāng)接收到tick時(shí)候,會(huì)查看tick.vtSymbol,是不是存在workingStopOrderDict的so.vtSymbol有一樣的,如果有,再看tick.lastPrice價(jià)格是否可以滿足觸發(fā)閾值,如果滿足,根據(jù)原來(lái)so的交易Direction,Long按照漲停價(jià),Short按照跌停價(jià)發(fā)出委托。然后從workingStopOrderDic和strategyOrderDict移除該so,并更新so狀態(tài),并觸發(fā)事件onStopOrder(so).

這里發(fā)現(xiàn),so只是只是按照漲停價(jià)發(fā)單給交易所,并沒(méi)有確保成績(jī),而且市價(jià)委托的實(shí)際交易vtOrderID也沒(méi)有返回;從tick交易角度,再收到tick后再發(fā)送交易,本事也是有了延遲一tick。所以一般tick級(jí)別交易不建議使用stoporder。

   def processStopOrder(self, tick):
        """收到行情后處理本地停止單(檢查是否要立即發(fā)出)"""
        vtSymbol = tick.vtSymbol
        
        # 首先檢查是否有策略交易該合約
        if vtSymbol in self.tickStrategyDict:
            # 遍歷等待中的停止單,檢查是否會(huì)被觸發(fā)
            for so in self.workingStopOrderDict.values():
                if so.vtSymbol == vtSymbol:
                    longTriggered = so.direction==DIRECTION_LONG and tick.lastPrice>=so.price        # 多頭停止單被觸發(fā)
                    shortTriggered = so.direction==DIRECTION_SHORT and tick.lastPrice<=so.price     # 空頭停止單被觸發(fā)
                    
                    if longTriggered or shortTriggered:
                        # 買入和賣出分別以漲停跌停價(jià)發(fā)單(模擬市價(jià)單)
                        if so.direction==DIRECTION_LONG:
                            price = tick.upperLimit
                        else:
                            price = tick.lowerLimit
                        
                        # 發(fā)出市價(jià)委托
                        self.sendOrder(so.vtSymbol, so.orderType, price, so.volume, so.strategy)
                        
                        # 從活動(dòng)停止單字典中移除該停止單
                        del self.workingStopOrderDict[so.stopOrderID]
                        
                        # 從策略委托號(hào)集合中移除
                        s = self.strategyOrderDict[so.strategy.name]
                        if so.stopOrderID in s:
                            s.remove(so.stopOrderID)
                        
                        # 更新停止單狀態(tài),并通知策略
                        so.status = STOPORDER_TRIGGERED
                        so.strategy.onStopOrder(so)

5. 前面說(shuō)了這么多,終于到了正主sendOrder(),也在class CtaEngine中定義的。代碼較長(zhǎng),下面做了寫縮減。

1)通過(guò)mainEngine.getContract獲得這個(gè)品種的合約的信息,包括這個(gè)合約的名稱,接口名gateway(國(guó)內(nèi)期貨就是ctp),交易所,最小價(jià)格變動(dòng)等信息;

2)創(chuàng)建一個(gè)class VtOrderReq的對(duì)象req,在vtObject.py中,這個(gè)py包括很多事務(wù)類的定義;然后賦值,包括contract獲得信息,交易手?jǐn)?shù),和price,和priceType,這里只有限價(jià)單。

3)根據(jù)orderType賦值direction和offset,之前sendStopOrder中已經(jīng)說(shuō)了,就不重復(fù)。

4)然后跳到mainEngine.convertOrderReq(req),這里代碼比較跳,分析下來(lái),如果之前沒(méi)有持倉(cāng),或者是直接返回[req];如果有持倉(cāng)就調(diào)用PositionDetail.convertOrderReq(req),這個(gè)時(shí)候如果是平倉(cāng)操作,就分析持倉(cāng)量,和平今和平昨等不同操作返回拆分的出來(lái)[reqTd,reqYd],這里不展開。

5) 如果上一部沒(méi)有返回[req],則委托有問(wèn)題,直接返回控制。如果有[req],因?yàn)榇嬖诙鄠€(gè)req情況,就遍歷每個(gè)req,使用mainEngine.sendOrder發(fā)單,并保存返回的vtOrderID到orderStrategyDict[],strategyOrderDict[]兩個(gè)字典;然后把vtOrderIDList返回。

    def sendOrder(self, vtSymbol, orderType, price, volume, strategy):
        """發(fā)單"""
        contract = self.mainEngine.getContract(vtSymbol)
        
        req = VtOrderReq()
        req.symbol = contract.symbol
    ……
        
        # 設(shè)計(jì)為CTA引擎發(fā)出的委托只允許使用限價(jià)單
        req.priceType = PRICETYPE_LIMITPRICE    
        
        # CTA委托類型映射
        if orderType == CTAORDER_BUY:
            req.direction = DIRECTION_LONG
            req.offset = OFFSET_OPEN
        ……
            
        # 委托轉(zhuǎn)換
        reqList = self.mainEngine.convertOrderReq(req)
        vtOrderIDList = []
        
        if not reqList:
            return vtOrderIDList
        
        for convertedReq in reqList:
            vtOrderID = self.mainEngine.sendOrder(convertedReq, contract.gatewayName)    # 發(fā)單
            self.orderStrategyDict[vtOrderID] = strategy                                 # 保存vtOrderID和策略的映射關(guān)系
            self.strategyOrderDict[strategy.name].add(vtOrderID)                         # 添加到策略委托號(hào)集合中
            vtOrderIDList.append(vtOrderID)
            
        self.writeCtaLog(u'策略%s發(fā)送委托,%s,%s,%s@%s' 
                         %(strategy.name, vtSymbol, req.direction, volume, price))
        
        return vtOrderIDList

6.   在mainEngine.sendOrder中,這里不列舉代碼了,首先進(jìn)行風(fēng)控,如果到閾值就不發(fā)單,然后看gateway是否存在,如果存在,就調(diào)用gateway.sendOrder(orderReq)方法;下面用ctpgateway說(shuō)明。class      CtpGateway(VtGateway)是VtGateway是繼承,把主要發(fā)單,返回上面都實(shí)現(xiàn),同時(shí)對(duì)于不同的接口,比如外匯,數(shù)字貨幣,只要用一套接口標(biāo)準(zhǔn)就可以,典型繼承使用。   

CtpGateway.sendOrder實(shí)際是調(diào)用class CtpTdApi(TdApi)的,這個(gè)就是一套ctp交易交口,代碼很簡(jiǎn)單,最后是調(diào)用封裝好C++的ctp接口reqOrderInsert()。最關(guān)鍵返回的vtOrderID是接口名+順序數(shù)。

    def sendOrder(self, orderReq):
        """發(fā)單"""
        self.reqID += 1
        self.orderRef += 1
       
        req = {}
       
        req['InstrumentID'] = orderReq.symbol
        req['LimitPrice'] = orderReq.price
        req['VolumeTotalOriginal'] = orderReq.volume
       
        # 下面如果由于傳入的類型本接口不支持,則會(huì)返回空字符串
        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)
       
        # 返回訂單號(hào)(字符串),便于某些算法進(jìn)行動(dòng)態(tài)管理
        vtOrderID = '.'.join([self.gatewayName, str(self.orderRef)])
        return vtOrderID

整個(gè)流程下來(lái),不考慮stoporder,是ctaTemplate -> CtaEngine ->mainEngine ->ctpgateway ->CtpTdApi, 傳到C++封裝的接口。返回的就是vtOrderID; 因?yàn)榇嬖谄阶颍浇襁€有鎖倉(cāng),反手等拆分情況,返回的可能是一組。

以上是“VNPY中如何實(shí)現(xiàn)從發(fā)送交易指令到交易所的源代碼”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI