《量化投資與策略》6.9節-vnpy實戰IX

本節繼續介紹CTA策略模組。

Part1:執行日誌

1。日誌內容

CTA策略模組UI介面上輸出的日誌有兩個來源,分別是CTA策略引擎和策略例項。

2.引擎日誌

CTA策略引擎一般輸出的是全域性資訊。下圖中除了策略例項名後加冒號的內容之外,都是CTA策略引擎輸出的日誌。

《量化投資與策略》6.9節-vnpy實戰IX

3.策略日誌

如果在策略中呼叫了write_log函式,那麼日誌內容就會透過策略日誌輸出。下圖紅框裡的內容分別是兩個不同的策略例項輸出的策略日誌。冒號前是策略例項的名稱,冒號後是write_log函式輸出的內容。

《量化投資與策略》6.9節-vnpy實戰IX

4.清空操作

如果想要清空CTA策略UI介面上的日誌輸出,可以點選右上角的【清空日誌】按鈕,則可一鍵清空該介面上已輸出的日誌。

點選【清空日誌】前,如下圖所示:

《量化投資與策略》6.9節-vnpy實戰IX

點選【清空日誌】後,如下圖所示:

《量化投資與策略》6.9節-vnpy實戰IX

Part2:停止單

圖形介面右上方區域的停止單監控元件,是用來跟蹤所有CTA引擎內本地停止單的狀態變化的。

因為不是所有介面都支援停止單,所以vn。py提供了本地停止單的功能。在交易介面不支援交易所停止單的前提下,使用者依然可以透過策略的下單函式(buy/sell/short/cover),把stop引數設定為True,啟用本地停止單功能。

vn。py的本地停止單有三個特點:

儲存在本地電腦上,關機後則失效;

只有交易員本人能夠看到,不必擔心洩露底牌;

停止單觸發有延時,導致一定的滑點。

1。停止單資訊

在發出本地停止單後,圖形介面右上方的監控元件就會顯示停止單的委託細節。本地停止單一共有【等待中】、【已觸發】和【已撤銷】三個狀態,如下圖所示:

《量化投資與策略》6.9節-vnpy實戰IX

停止單剛發出時是處於【等待中】的狀態。因為停止單的資訊記錄在本地,沒有發往交易所,所以此時主介面上【委託】欄不會有變化。

一旦該停止單的委託價格被觸發,為了實現立即成交的目的,CTA策略引擎會立即以漲跌停價或者盤口五檔的價格,去發出限價委託(所以建議本地停止單隻用於流動性較好的合約)。限價委託發出後,主介面上【委託】欄將更新該訂單的狀態,此時停止單狀態會變為【已觸發】,【限價委託號】欄下也會填入該訂單的限價委託號。

需注意,停止單介面顯示的價格是本地停止單的觸發價格,而不是發出限價單的價格。如果停止單在被觸發前就被策略取消了,那麼該訂單的狀態就會變為【已撤銷】。

Part3:批次操作

在策略經過充分測試,實盤執行較為穩定,不需要經常進行調整的情況下,如果有多個需要執行的CTA策略例項,可以使用介面右上角的【全部初始化】、【全部啟動】和【全部停止】功能來執行盤前批次初始化、啟動策略例項以及盤後批次停止策略例項的操作。

全部初始化

在所有策略例項建立成功後,點選右上角的【全部初始化】按鈕,則可批次初始化策略例項。

點選【全部初始化】前,如下圖所示:

《量化投資與策略》6.9節-vnpy實戰IX

點選【全部初始化】後,如下圖所示:

《量化投資與策略》6.9節-vnpy實戰IX

全部啟動

在所有策略例項初始化成功後,點選右上角的【全部啟動】按鈕,則可批次啟動策略例項,如下圖所示:

《量化投資與策略》6.9節-vnpy實戰IX

全部停止

在所有策略例項啟動成功後,點選右上角的【全部停止】按鈕,則可批次停止策略例項,如下圖所示:

《量化投資與策略》6.9節-vnpy實戰IX

Part4:CTA策略模板(CtaTemplate)

CTA策略模板提供完整的訊號生成和委託管理功能,使用者可以基於該模板(位於vnpy。app。cta_strategy。template中)自行開發CTA策略。

使用者自行開發的策略可以放在使用者執行資料夾下的strategies資料夾內。

請注意,策略檔案命名採用下劃線模式,如boll_channel_strategy。py,而策略類命名採用駝峰式,如BollChannelStrategy。

下面透過BollChannelStrategy策略示例,來展示策略開發的具體步驟:

在基於CTA策略模板編寫策略邏輯之前,需要在策略檔案的頂部載入需要用到的內部元件,如下方程式碼所示:

from vnpy。app。cta_strategy import ( CtaTemplate, StopOrder, TickData, BarData, TradeData, OrderData, BarGenerator, ArrayManager,)

CtaTemplate是CTA策略模板,StopOrder、TickData、BarData、TradeData和OrderData都是儲存對應資訊的資料容器,BarGenerator是K線生成模組,ArrayManager是K線時間序列管理模組。

1。策略引數與變數

在策略類的下方,可以設定策略的作者(author),引數(parameters)以及變數(variables),如下方程式碼所示:

author = “用Python的交易員” boll_window = 18 boll_dev = 3。4 cci_window = 10 atr_window = 30 sl_multiplier = 5。2 fixed_size = 1 boll_up = 0 boll_down = 0 cci_value = 0 atr_value = 0 intra_trade_high = 0 intra_trade_low = 0 long_stop = 0 short_stop = 0 parameters = [ “boll_window”, “boll_dev”, “cci_window”, “atr_window”, “sl_multiplier”, “fixed_size” ] variables = [ “boll_up”, “boll_down”, “cci_value”, “atr_value”, “intra_trade_high”, “intra_trade_low”, “long_stop”, “short_stop” ]

雖然策略的引數和變數都從屬於策略類,但策略引數是固定的(由交易員從外部指定),而策略變數則在交易的過程中隨著策略的狀態而變化,所以策略變數一開始只需要初始化為對應的基礎型別。例如:整數設為0,浮點數設為0。0,而字串則設為””。

如果需要CTA引擎在執行過程中,將策略引數和變數顯示在UI介面上,並在資料重新整理、停止策略時儲存其數值,則需把引數和變數的名字(以字串的資料型別)新增進parameters和variables列表裡。

請注意,該列表只能接受引數或變數以str、int、float和bool四種資料型別傳入。如果策略裡需要用到其他資料型別的引數與變數,請把該引數或變數的定義放到__init__函式下。

2。類的初始化

入參:cta_engine: Any, strategy_name: str, vt_symbol: str, setting: dict

出參:無

__init__函式是策略類的建構函式,需要與繼承的CtaTemplate保持一致。在這個繼承的策略類裡,初始化一般分三步,如下方程式碼所示:

def __init__(self, cta_engine, strategy_name, vt_symbol, setting): “”“”“” super()。__init__(cta_engine, strategy_name, vt_symbol, setting) self。bg = BarGenerator(self。on_bar, 15, self。on_15min_bar) self。am = ArrayManager()

1 。 透過super( )的方法繼承CTA策略模板,在__init__( )函式中傳入CTA引擎、策略名稱、vt_symbol以及引數設定。注意其中的CTA引擎,可以是實盤引擎或者回測引擎,這樣可以方便地實現同一套程式碼同時跑回測和實盤(以上引數均由策略引擎在使用策略類建立策略例項時自動傳入,使用者無需進行設定)。

2 。 呼叫K線生成模組(BarGenerator):透過時間切片將Tick資料合成1分鐘K線資料。如有需求,還可合成更長的時間週期數據,如15分鐘K線。

如果只基於on_bar進行交易,這裡程式碼可以寫成:

self。bg = BarGenerator(self。on_bar)

而不用給bg例項傳入需要基於on_bar週期合成的更長K線週期,以及接收更長K線週期的函式名。

請注意,合成X分鐘線時,X必須設為能被60整除的數(60除外)。對於小時線的合成沒有這個限制。

BarGenerator預設的基於on_bar函式合成長週期K線的資料頻率是分鐘級別,如果需要基於合成的小時線或者更長週期的K線交易,請在策略檔案頂部匯入Interval,並傳入對應的資料頻率給bg例項。如下方程式碼所示:

檔案頂部匯入Interval:

from vnpy。trader。constant import Interval

__init__函式建立bg例項時傳入資料頻率:

self。bg = BarGenerator(self。on_bar, 2, self。on_2hour_bar, Interval。HOUR)

3 。 呼叫K線時間序列管理模組(ArrayManager):基於K線資料,如1分鐘、15分鐘, 將其轉化為便於向量化計算的時間序列資料結構,並在內部支援使用talib庫來計算相應的技術指標。

3。CTA策略引擎呼叫的函式

CtaTemplate中的update_setting函式和該函式後面四個以get開頭的函式,都是CTA策略引擎去負責呼叫的函式,一般在策略編寫的時候是不需要呼叫的。

策略的回撥函式

CtaTemplate中以on開頭的函式稱為回撥函式,在編寫策略的過程中能夠用來接收資料或者接收狀態更新。回撥函式的作用是當某一個事件發生的時候,策略裡的這類函式會被CTA策略引擎自動呼叫(無需在策略中主動操作)。回撥函式按其功能可分為以下三類:

####策略例項狀態控制(所有策略都需要)

on_init

入參:無

出參:無

初始化策略時on_init函式會被呼叫,預設寫法是先呼叫write_log函式輸出“策略初始化”日誌,再呼叫load_bar函式載入歷史資料,如下方程式碼所示:

def on_init(self): “”“ Callback when strategy is inited。 ”“” self。write_log(“策略初始化”) self。load_bar(10)

請注意,如果是基於Tick資料回測,請在此處呼叫load_tick函式。

策略初始化時,策略的inited和trading狀態都為【False】,此時只是呼叫ArrayManager計算並快取相關的計算指標,不能發出交易訊號。呼叫完on_init函式之後,策略的inited狀態才變為【True】,策略初始化才完成。

on_start

入參:無

出參:無

啟動策略時on_start函式會被呼叫,預設寫法是呼叫write_log函式輸出“策略啟動”日誌,如下方程式碼所示:

def on_start(self): “”“ Callback when strategy is started。 ”“” self。write_log(“策略啟動”)

呼叫策略的on_start函式啟動策略後,策略的trading狀態變為【True】,此時策略才能夠發出交易訊號。

on_stop

入參:無

出參:無

停止策略時on_stop函式會被呼叫,預設寫法是呼叫write_log函式輸出“策略停止”日誌,如下方程式碼所示:

def on_stop(self): “”“ Callback when strategy is stopped。 ”“” self。write_log(“策略停止”)

呼叫策略的on_stop函式停止策略後,策略的trading狀態變為【False】,此時策略就不會發出交易訊號了。

接收資料、計算指標、發出交易訊號

on_tick

入參:tick: TickData

出參:無

絕大部分交易系統都只提供Tick資料的推送。即使一部分數字貨幣交易平臺或者外匯平臺可以提供K線資料的推送,但是這些資料到達本地電腦的速度也會慢於Tick資料的推送,因為也需要平臺合成之後才能推送過來。所以實盤的時候,vn。py裡所有的策略的K線都是由收到的Tick資料合成的。

當策略收到最新的Tick資料的行情推送時,on_tick函式會被呼叫。預設寫法是透過BarGenerator的update_tick函式把收到的Tick資料推進前面建立的bg例項中以便合成1分鐘的K線,如下方程式碼所示:

def on_tick(self, tick: TickData): “”“ Callback of new tick data update。 ”“” self。bg。update_tick(tick)

on_bar

入參:bar: BarData

出參:無

當策略收到最新的K線資料時(實盤時預設推進來的是基於Tick合成的一分鐘的K線,回測時則取決於選擇引數時填入的K線資料頻率),on_bar函式就會被呼叫。示例策略裡出現過的寫法有兩種:

1 。 如果策略基於on_bar推進來的K線交易,那麼請把交易請求類函式都寫在on_bar函式下(因本次示例策略類BollChannelStrategy不是基於on_bar交易,故不作示例講解。基於on_bar交易的示例程式碼可參考其他示例策略);

2 。 如果策略需要基於on_bar推進來的K線資料透過BarGenerator合成更長時間週期的K線來交易,那麼請在on_bar中呼叫BarGenerator的update_bar函式,把收到的這個bar推進前面建立的bg例項中即可,如下方程式碼所示:

def on_bar(self, bar: BarData): “”“ Callback of new bar data update。 ”“” self。bg。update_bar(bar)

示例策略類BollChannelStrategy是透過15分鐘K線資料回報來生成CTA訊號的。一共有三部分,如下方程式碼所示:

def on_15min_bar(self, bar: BarData): “”“”“” self。cancel_all() am = self。am am。update_bar(bar) if not am。inited: return self。boll_up, self。boll_down = am。boll(self。boll_window, self。boll_dev) self。cci_value = am。cci(self。cci_window) self。atr_value = am。atr(self。atr_window) if self。pos == 0: self。intra_trade_high = bar。high_price self。intra_trade_low = bar。low_price if self。cci_value > 0: self。buy(self。boll_up, self。fixed_size, True) elif self。cci_value < 0: self。short(self。boll_down, self。fixed_size, True) elif self。pos > 0: self。intra_trade_high = max(self。intra_trade_high, bar。high_price) self。intra_trade_low = bar。low_price self。long_stop = self。intra_trade_high - self。atr_value * self。sl_multiplier self。sell(self。long_stop, abs(self。pos), True) elif self。pos < 0: self。intra_trade_high = bar。high_price self。intra_trade_low = min(self。intra_trade_low, bar。low_price) self。short_stop = self。intra_trade_low + self。atr_value * self。sl_multiplier self。cover(self。short_stop, abs(self。pos), True) self。put_event()

清空未成交委託:為了防止之前下的單子在上一個15分鐘沒有成交,但是下一個15分鐘可能已經調整了價格,就用cancel_all()方法立刻撤銷之前未成交的所有委託,保證策略在當前這15分鐘開始時的整個狀態是清晰和唯一的;

呼叫K線時間序列管理模組:基於最新的15分鐘K線資料來計算相應的技術指標,如布林帶通道上下軌、CCI指標、ATR指標等。首先獲取ArrayManager物件,然後將收到的K線推送進去,檢查ArrayManager的初始化狀態,如果還沒初始化成功就直接返回,沒有必要去進行後續的交易相關的邏輯判斷。因為很多技術指標計算對最少K線數量有要求,如果數量不夠的話計算出來的指標會出現錯誤或無意義。反之,如果沒有return,就可以開始計算技術指標了;

訊號計算:透過持倉的判斷以及結合CCI指標、布林帶通道、ATR指標在通道突破點掛出停止單委託(buy/sell),同時設定離場點(short/cover)。請注意,如果需要在圖形介面重新整理指標數值,請不要忘記呼叫put_event()函式。

委託狀態更新

以下函式在策略中可以直接pass,其具體邏輯應用交給回測/實盤引擎負責。

on_trade

入參:bar: TradeData

出參:無

收到策略成交回報時on_trade函式會被呼叫。

on_order

入參:bar: OrderData

出參:無

收到策略委託回報時on_order函式會被呼叫。

on_stop_order

入參:bar: StopOrder

出參:無

收到策略停止單回報時on_stop_order函式會被呼叫。

策略的主動函式

策略內

buy:買入開倉(Direction:LONG,Offset:OPEN)

sell:賣出平倉(Direction:SHORT,Offset:CLOSE)

short:賣出開倉(Direction:SHORT,Offset:OPEN)

cover:買入平倉(Direction:LONG,Offset:CLOSE)

入參:price: float, volume: float, stop: bool = False, lock: bool = False, net: bool = False

出參:vt_orderids: List[vt_orderid] / 無

buy/sell/short/cover都是策略內部的負責發單的交易請求類函式。策略可以透過這些函式給CTA策略引擎傳送交易訊號來達到下單的目的。

以下方buy函式的程式碼為例,可以看到,價格和數量是必填的引數,停止單轉換、鎖倉轉換和淨倉轉換則預設為False。也可以看到,函式內部收到傳進來的引數之後就呼叫了CtaTemplate裡的send_order函式來發單(因為是buy指令,則自動把方向填成了LONG,開平填成了OPEN)

如果stop設定為True,那麼該筆訂單則會自動轉成停止單,如果介面支援交易所停止單則會轉成交易所停止單,如果介面不支援交易所停止單則會轉換成vn。py的本地停止單。

如果lock設定為True,那麼該筆訂單則會進行鎖倉委託轉換(在有今倉的情況下,如果想平倉,則會先平掉所有的昨倉,然後剩下的部分都進行反向開倉來代替平今倉,以避免平今的手續費懲罰)。

如果net設定為True,那麼該筆訂單則會進行淨倉委託轉換(基於整體賬戶的所有倉位,根據淨倉持有方式來對策略下單的開平方向進行轉換)。但是淨倉交易模式與鎖倉交易模式互斥,因此net設定為True時,lock必須設定為False。

def buy(self, price: float, volume: float, stop: bool = False, lock: bool = False, net: bool = False): “”“ Send buy order to open a long position。 ”“” return self。send_order(Direction。LONG, Offset。OPEN, price, volume, stop, lock, net)

請注意,國內期貨有開平倉的概念,例如買入操作要區分為買入開倉和買入平倉;但對於股票、外盤期貨和絕大部分數字貨幣都是淨持倉模式,沒有開倉和平倉概念,所以只需使用買入(buy)和賣出(sell)這兩個指令就可以了。

cancel_order

入參:vt_orderid: str

出參:無

cancel_all

入參:無

出參:無

cancel_order和cancel_all都是策略內部的負責撤單的交易請求類函式。cancel_order是撤掉策略內指定的活動委託,cancel_all是撤掉策略所有的活動委託。

請注意,要在策略啟動之後,也就是策略的trading狀態變為【True】之後,才能撤單。

CTA策略引擎

send_order

入參:price: float, volume: float, stop: bool = False, lock: bool = False, net: bool = False

出參:vt_orderids / 無

send_order函式是CTA策略引擎呼叫的傳送委託的函式。一般在策略編寫的時候不需要單獨呼叫,透過buy/sell/short/cover函式傳送委託即可。

實盤的時候,收到傳進來的引數後,會呼叫round_to函式基於合約的pricetick和min_volume對委託的價格和數量進行處理。

請注意,要在策略啟動之後,也就是策略的trading狀態變為【True】之後,才能發出交易委託。如果策略的Trading狀態為【False】時呼叫了該函式,只會返回[]。

功能函式¶

以下為策略以外的功能函式:

write_log

入參:msg: str

出參:無

在策略中呼叫write_log函式,可以進行指定內容的日誌輸出。

get_engine_type

入參:無

出參:engine_type: EngineType

如果策略對於回測和實盤時有不同的邏輯處理,可以呼叫get_engine_type函式獲取當下使用的引擎型別來進行邏輯判斷。

請注意,如果要呼叫該函式進行邏輯判斷,請在策略檔案頂部匯入“EngineType”。

get_pricetick

入參:無

出參:pricetick: float / None

在策略中呼叫get_pricetick函式,可以獲取交易合約的最小价格跳動。

load_bar

入參:days: int, interval: Interval = Interval。MINUTE, callback: Callable = None, use_database: bool = False

出參:無

在策略中呼叫load_bar函式,可以在策略初始化時載入K線資料。

如下方程式碼所示,load_bar函式呼叫時,預設載入的天數是10,頻率是一分鐘,對應也就是載入10天的1分鐘K線資料。在回測時,10天指的是10個交易日,而在實盤時,10天則是指的是自然日,因此建議載入的天數寧可多一些也不要太少。

def load_bar( self, days: int, interval: Interval = Interval。MINUTE, callback: Callable = None, use_database: bool = False ): “”“ Load historical bar data for initializing strategy。 ”“” if not callback: callback = self。on_bar self。cta_engine。load_bar( self。vt_symbol, days, interval, callback, use_database )

load_tick

入參:days: int

出參:無

在策略中呼叫load_tick函式,可以在策略初始化時載入Tick資料。

put_event

入參:無

出參:無

在策略中呼叫put_event函式,可以透過圖形介面重新整理策略狀態相關顯示。

請注意,要策略初始化完成,inited狀態變為【True】之後,才能重新整理介面。

send_email

入參:msg: str

出參:無

配置好郵箱相關資訊之後(配置方法詳見基本使用篇的全域性配置部分),在策略中呼叫send_email函式,可以傳送指定內容的郵件到自己的郵箱。

請注意,要策略初始化完成,inited狀態變為【True】之後,才能傳送郵件。

sync_data

入參:無

出參:無

在策略中呼叫sync_data函式,可以在實盤的時候,每次停止或成交時都同步策略變數進json檔案中進行本地快取,方便第二天初始化時再進行讀取還原(CTA策略引擎會去呼叫,在策略裡無需主動呼叫)。

請注意,要在策略啟動之後,也就是策略的trading狀態變為【True】之後,才能同步策略資訊。