本節繼續介紹CTA策略模組。
Part1:執行日誌
1。日誌內容
CTA策略模組UI介面上輸出的日誌有兩個來源,分別是CTA策略引擎和策略例項。
2.引擎日誌
CTA策略引擎一般輸出的是全域性資訊。下圖中除了策略例項名後加冒號的內容之外,都是CTA策略引擎輸出的日誌。
3.策略日誌
如果在策略中呼叫了write_log函式,那麼日誌內容就會透過策略日誌輸出。下圖紅框裡的內容分別是兩個不同的策略例項輸出的策略日誌。冒號前是策略例項的名稱,冒號後是write_log函式輸出的內容。
4.清空操作
如果想要清空CTA策略UI介面上的日誌輸出,可以點選右上角的【清空日誌】按鈕,則可一鍵清空該介面上已輸出的日誌。
點選【清空日誌】前,如下圖所示:
點選【清空日誌】後,如下圖所示:
Part2:停止單
圖形介面右上方區域的停止單監控元件,是用來跟蹤所有CTA引擎內本地停止單的狀態變化的。
因為不是所有介面都支援停止單,所以vn。py提供了本地停止單的功能。在交易介面不支援交易所停止單的前提下,使用者依然可以透過策略的下單函式(buy/sell/short/cover),把stop引數設定為True,啟用本地停止單功能。
vn。py的本地停止單有三個特點:
儲存在本地電腦上,關機後則失效;
只有交易員本人能夠看到,不必擔心洩露底牌;
停止單觸發有延時,導致一定的滑點。
1。停止單資訊
在發出本地停止單後,圖形介面右上方的監控元件就會顯示停止單的委託細節。本地停止單一共有【等待中】、【已觸發】和【已撤銷】三個狀態,如下圖所示:
停止單剛發出時是處於【等待中】的狀態。因為停止單的資訊記錄在本地,沒有發往交易所,所以此時主介面上【委託】欄不會有變化。
一旦該停止單的委託價格被觸發,為了實現立即成交的目的,CTA策略引擎會立即以漲跌停價或者盤口五檔的價格,去發出限價委託(所以建議本地停止單隻用於流動性較好的合約)。限價委託發出後,主介面上【委託】欄將更新該訂單的狀態,此時停止單狀態會變為【已觸發】,【限價委託號】欄下也會填入該訂單的限價委託號。
需注意,停止單介面顯示的價格是本地停止單的觸發價格,而不是發出限價單的價格。如果停止單在被觸發前就被策略取消了,那麼該訂單的狀態就會變為【已撤銷】。
Part3:批次操作
在策略經過充分測試,實盤執行較為穩定,不需要經常進行調整的情況下,如果有多個需要執行的CTA策略例項,可以使用介面右上角的【全部初始化】、【全部啟動】和【全部停止】功能來執行盤前批次初始化、啟動策略例項以及盤後批次停止策略例項的操作。
全部初始化
在所有策略例項建立成功後,點選右上角的【全部初始化】按鈕,則可批次初始化策略例項。
點選【全部初始化】前,如下圖所示:
點選【全部初始化】後,如下圖所示:
全部啟動
在所有策略例項初始化成功後,點選右上角的【全部啟動】按鈕,則可批次啟動策略例項,如下圖所示:
全部停止
在所有策略例項啟動成功後,點選右上角的【全部停止】按鈕,則可批次停止策略例項,如下圖所示:
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】之後,才能同步策略資訊。