前面兩篇有了基礎知識的準備,這一篇講透過CTP API獲取實時行情,錄入csv,實時合成k線。github上開源了錄入csv及合成k線程式碼,後臺回覆pyctp可獲取。先上兩張效果圖:

CTP程式化交易入門系列之三:獲取實時行情及k線合成

圖1 csv資料

CTP程式化交易入門系列之三:獲取實時行情及k線合成

圖2 1分鐘K線圖

一、CTP行情API介紹

CTP API分為行情和交易兩類,兩者是完全獨立的,所以如果只對行情感興趣的話,只用如下行情API相關部分三個檔案就可以。

thosttraderapi。py

_thostmduserapi。pyd

thostmduserapi_se。dll

API中的重點函式如下,請求:

//登入函式,確認連上CTP後首先需要登入

def ReqUserLogin(self, pReqUserLoginField: ‘CThostFtdcReqUserLoginField’, nRequestID: ‘int’) -> “int”:

//訂閱函式,即透過這個函式來向CTP請求訂閱哪些合約的實時行情

//第一個引數型別為list,寫成[“au1912”,“IC1909”]的形式,第二個引數必須為前面list的長度

def SubscribeMarketData(self, ppInstrumentID: ‘char *[]’, nCount: ‘int’) -> “int”:

回報:

//行情回報函式,其中pDepthMarketData類內即為每次實時行情的相關資料

def OnRtnDepthMarketData(self, pDepthMarketData: ‘CThostFtdcDepthMarketDataField’) -> “void”:

那麼究竟可以獲取到合約的哪些行情資料呢?從型別CThostFtdcDepthMarketDataField 中的欄位就可以看出來,在thosttraderapi。py檔案中搜CThostFtdcDepthMarketDataField型別即可看出有哪些欄位,主要有更新時間UpdateTime,最新成交價LastPrice ,買賣一檔的價格及數量BidPrice1,BidVolume1,AskPrice1,AskVolume1,累計成交量Volume等。

二、訂閱獲取行情的步驟

程式碼非常簡單,50行內即可訂閱全市場行情。透過上一章的學習應該知道CTP的API是非同步回撥的機制,底層dll在客戶訂閱成功後會自動推送訂閱合約的實時行情。程式碼邏輯時序圖如下:

CTP程式化交易入門系列之三:獲取實時行情及k線合成

圖3 訂閱行情時序圖

對應的主函式如下:

def main():

mduserapi=mdapi。CThostFtdcMdApi_CreateFtdcMdApi() #第1步

mduserspi=CFtdcMdSpi(mduserapi) #第2步

‘’‘以下是生產環境’‘’

#mduserapi。RegisterFront(“tcp://180。168。146。187:10101”) #第3步

‘’‘以下是7*24小時環境’‘’

mduserapi。RegisterFront(“tcp://180。168。146。187:10131”)

mduserapi。RegisterSpi(mduserspi) #第4步

mduserapi。Init() #第5步,API正式啟動,dll底層會自動去連上面註冊的地址

mduserapi。Join() #join的目的是為了阻塞主執行緒,可以用sleep代替

回撥例項類CFtdcMdSpi如下:

import thostmduserapi as mdapi

‘’‘需要訂閱的合約list’‘’

subID=[“au1912”,“IC1909”,“i2001”,“TA001”]

#繼承自spi基類mdapi。CThostFtdcMdSpi

class CFtdcMdSpi(mdapi。CThostFtdcMdSpi):

def __init__(self,tapi):

mdapi。CThostFtdcMdSpi。__init__(self)

self。tapi=tapi

def OnFrontConnected(self) -> “void”:

print (“OnFrontConnected”)

loginfield = mdapi。CThostFtdcReqUserLoginField()

loginfield。BrokerID=“8000”

loginfield。UserID=“000005”

loginfield。Password=“123456”

loginfield。UserProductInfo=“python dll”

#實現onfrontconnect函式,在裡面呼叫登入,第7步

self。tapi。ReqUserLogin(loginfield,0)

def OnRspUserLogin(self, pRspUserLogin: ‘CThostFtdcRspUserLoginField’, pRspInfo: ‘CThostFtdcRspInfoField’, nRequestID: ‘int’, bIsLast: ‘bool’) -> “void”:

print (f“OnRspUserLogin, SessionID={pRspUserLogin。SessionID},ErrorID={pRspInfo。ErrorID},ErrorMsg={pRspInfo。ErrorMsg}”)

#繼承實現登入回撥,登入成功後去訂閱,第9步

ret=self。tapi。SubscribeMarketData([id。encode(‘utf-8’) for id in subID],len(subID))

def OnRtnDepthMarketData(self, pDepthMarketData: ‘CThostFtdcDepthMarketDataField’) -> “void”:

#繼承收取訂閱行情,第11步,在這裡將pDepthMarketData資料存入csv即可錄得資料

print(f“InstrumentID={pDepthMarketData。InstrumentID},LastPrice={pDepthMarketData。LastPrice}”)

看總共就30+行程式碼,就完成了訂閱收取行情的工作。如果將subID列表中填入入全市場合約,就能訂閱得到全市場的行情,是不是很簡單?

四、由CTP API得到K線資料

首先需要區分下tick資料和切片(快照)資料有什麼區別。

tick資料一般是指市場上的逐筆資料,例如一筆委託會產生一筆行情,一筆成交也會產生一筆行情。目前國內期貨交易所還不支援推送這種逐筆的資料,只推送切片(快照)資料。

切片資料是指將一定時間內的逐筆資料統計成一個快照發出,一般是1秒2筆。但鄭商所有點特殊,可能是1s多筆,就不展開來講了。

CTP發出的行情正是轉發的交易所的行情,所以也是500ms一次快照。一般業內也將這個快照資料稱之為tick,雖然這不是真正的tick,但我們依照慣例,下面都稱之為tick資料。

很多客戶做交易更關心K線資料,用K線資料計算訊號。CTP不推送K線資料,所以需要客戶自己根據tick資料計算得出。

K線資料的基本要素有

Time、Volume、Price、Open、High、Low、Close

這6個值,可以根據這個週期內CTP的tick資料中的

UpdateTime、 LastPrice、 Volume

三個欄位算出。我們以1分鐘K線為例,邏輯如下:

#根據行情中的UpdateTime欄位判斷是否為新1分鐘

st= pDepthMarketData。UpdateTime。split(‘:’)

if not self。bar:

newMinitue = True

else:

if int(st[1]) == self。bar。updateTime。minute :

newMinitue = False

else:

newMinitue = True

#如果是新1分鐘,生成一個新k線變數,CBarData結構體中有OHLC,time等K線欄位

if newMinitue :

self。bar = CBarData()

self。bar。instrumentID = pDepthMarketData。InstrumentID

self。bar。exchangeID = pDepthMarketData。ExchangeID

self。bar。updateTime = time(int(st[0]),int(st[1]),0,0)

self。bar。volume = 0

self。bar。openInterest = pDepthMarketData。OpenInterest

self。bar。openPrice = pDepthMarketData。LastPrice

self。bar。highPrice = pDepthMarketData。LastPrice

self。bar。lowPrice = pDepthMarketData。LastPrice

self。bar。closePrice = pDepthMarketData。LastPrice

else :

#如果不是新1分鐘,將最新價與HL價相比然後更新,跟新C價

self。bar。highPrice = max(self。bar。highPrice, pDepthMarketData。LastPrice)

self。bar。lowPrice = min(self。bar。lowPrice, pDepthMarketData。LastPrice)

self。bar。closePrice = pDepthMarketData。LastPrice

self。bar。openInterest = pDepthMarketData。OpenInterest

#注意Volume欄位是累計成交量,所以這個時間段內成交量為該值與上一時間段末成交量的差值

if not self。lastVolume:

self。bar。volume += max(pDepthMarketData。Volume-self。lastVolume,0)

self。lastVolume = pDepthMarketData。Volume

#列印實時k線資料

print(f“{bar update[pDepthMarketData。UpdateTime],O[self。bar。openPrice],H[self。bar。highPrice],L[self。bar。lowPrice],C[self。bar。closePrice]}”)

有這一段程式碼加入到上面的OnRtnDepthMarketData函式中,就能獲得1分鐘K線資料了。其餘的3、5、10、15、30分鐘這類的K線資料獲取方式原理也相似。

當然要得到令自己滿意的k線資料還是有很多坑要自己踩過才知道,每個人對K線的要求也不一樣,這裡提幾點思考,就不一一列舉解答了。

根據最新價LastPrice更新得到的highPrice一定是真的最高價嗎?

上下午收盤分別是11:30和15:00,那收到11:30:00。500和15:30:00。500ms的行情如何處理?

非主力合約有的很長時間才來一個tick,如何處理?

建議大家可以一邊做一邊對應快期等終端對比,得到自己滿意的k線資料。一般有k線資料就可以直接計算指標得到訊號量便於交易,為了更直觀地看到K線,這裡也提供下PyQt + PyQtGraph實現的K線圖,原始碼一樣提供在github上。程式碼參考了github上uiKLine和vnpy兩個開源專案,大家可以看我github上fork的這兩個專案。感謝兩位作者!

往期推薦

● CTP程式化交易入門系列之一:準備

● CTP程式化交易入門系列之二:API基本架構及初始化

● 什麼是穿透式監管,需要投資者做什麼?

下節預告

● CTP API獲取行情常見問題及解答

關注我們 ,一起學習程式化交易吧!

CTP程式化交易入門系列之三:獲取實時行情及k線合成