今天咱們來用WonderTrader的python
子框架wtpy
來實際編寫一個期貨日內交易的策略。而後咱們會先設定一組參數進行第一輪測試,再根據第一輪測試的結果,調整好參數之後,再進行第二輪測試。藉此來演示一下wtpy
中策略如何編寫以及回測。python
安裝wtpy
。在安裝了python3.6
以上的計算機上執行一下命令。git
$ pip install wtpy
或者直接下載whl
文件到本地進行安裝
阿里雲鏡像地址:https://mirrors.aliyun.com/py...pipy
地址:https://pypi.org/project/wtpy...github
github
複製demoCFFEX.IF.HOT_m5.csv
。爲了提升測試效率,咱們只選取最後近兩個月時間的數據進行回測,具體爲2019年9月10日到2019年10月31日。DualThrust
做爲咱們策略的算法。一方面DualThrust
流傳了好久了,曾經有不少大機構都用這個模型獲取到了足夠多的收益;另外一方面,DualThrust
的算法複雜度比較低,比較適合咱們做爲演示策略來使用。DualThrust
的算法邏輯以下
用MAX(HH-LC,HC-LL)
,做爲計算上下邊界的基準值,用今日開盤價
做爲基準價,而後用上邊界係數
和下邊界係數
,分別計算出上邊界的價格和下邊界的價格,當最新價突破上邊界或者下邊界的時候,就是咱們發出信號的時候。
可是在策略的實現中,咱們還須要考慮到已有持倉的時候如何處理,因此最終的策略邏輯以下:算法
當持倉爲0的時候,價格突破上邊界時,開多進場,價格突破下邊界時,開空進場
當持倉爲多的時候,價格突破上邊界時,保持倉位,價格突破下邊界時,多反空
當持倉爲空的時候,價格突破上邊界時,空反多,價格突破下邊界時,保持倉位
參數說明
肯定了策略的算法之後,咱們須要肯定策略模塊的參數。參數的設置,要綜合考慮策略自己的參數,以及模塊使用的參數。最終咱們肯定了以下的參數:json
name 策略實例名稱 code 回測使用的合約代碼 barCnt 要拉取的K線條數 period 要使用的K線週期,採用週期類型+週期倍數的形式,如m5表示5分鐘線,d3表示3日線 days 策略算法參數,算法引用的歷史數據條數 k1 策略算法參數,上邊界係數 k2 策略算法參數,下邊界係數 isForStk DualThrust策略用於控制交易品種的代碼
咱們還能夠將基本手數做爲參數傳遞給策略模型,這樣的話通用性更強。不過咱們這裏就再也不增設參數了,默認手數都是1手。api
最終策略源碼以下框架
from wtpy import BaseStrategy from wtpy import Context class StraDualThrust(BaseStrategy): def __init__(self, name:str, code:str, barCnt:int, period:str, days:int, k1:float, k2:float, isForStk:bool = False): BaseStrategy.__init__(self, name) self.__days__ = days self.__k1__ = k1 self.__k2__ = k2 self.__period__ = period self.__bar_cnt__ = barCnt self.__code__ = code self.__is_stk__ = isForStk def on_init(self, context:Context): code = self.__code__ #品種代碼 if self.__is_stk__: code = code + "Q" context.stra_get_bars(code, self.__period__, self.__bar_cnt__, isMain = True) context.stra_log_text("DualThrust inited") def on_calculate(self, context:Context): ''' 策略主調函數,全部的計算邏輯都在這裏完成 ''' code = self.__code__ #品種代碼 # 交易單位,主要用於股票的適配 trdUnit = 1 if self.__is_stk__: trdUnit = 100 #讀取最近50條1分鐘線(dataframe對象) theCode = code if self.__is_stk__: theCode = theCode + "Q" df_bars = context.stra_get_bars(theCode, self.__period__, self.__bar_cnt__, isMain = True) #把策略參數讀進來,做爲臨時變量,方便引用 days = self.__days__ k1 = self.__k1__ k2 = self.__k2__ #平倉價序列、最高價序列、最低價序列 closes = df_bars["close"] highs = df_bars["high"] lows = df_bars["low"] #讀取days天以前到上一個交易日位置的數據 hh = highs[-days:-1].max() hc = closes[-days:-1].max() ll = lows[-days:-1].min() lc = closes[-days:-1].min() #讀取今天的開盤價、最高價和最低價 lastBar = df_bars.iloc[-1] openpx = lastBar["open"] highpx = lastBar["high"] lowpx = lastBar["low"] ''' !!!!!這裏是重點 一、首先根據最後一條K線的時間,計算當前的日期 二、根據當前的日期,對日線進行切片,並截取所需條數 三、最後在最終切片內計算所需數據 ''' #肯定上軌和下軌 upper_bound = openpx + k1* max(hh-lc,hc-ll) lower_bound = openpx - k2* max(hh-lc,hc-ll) #讀取當前倉位 curPos = context.stra_get_position(code)/trdUnit if curPos == 0: if highpx >= upper_bound: context.stra_enter_long(code, 1*trdUnit, 'enterlong') context.stra_log_text("向上突破%.2f>=%.2f,多倉進場" % (highpx, upper_bound)) #修改並保存 self.xxx = 1 context.user_save_data('xxx', self.xxx) return if lowpx <= lower_bound and not self.__is_stk__: context.stra_enter_short(code, 1*trdUnit, 'entershort') context.stra_log_text("向下突破%.2f<=%.2f,空倉進場" % (lowpx, lower_bound)) return elif curPos > 0: if lowpx <= lower_bound: context.stra_exit_long(code, 1*trdUnit, 'exitlong') context.stra_log_text("向下突破%.2f<=%.2f,多倉出場" % (lowpx, lower_bound)) #raise Exception("except on purpose") return else: if highpx >= upper_bound and not self.__is_stk__: context.stra_exit_short(code, 1*trdUnit, 'exitshort') context.stra_log_text("向上突破%.2f>=%.2f,空倉出場" % (highpx, upper_bound)) return def on_tick(self, context:Context, stdCode:str, newTick:dict): return
修改runBT.py
中策略的參數,而後運行runBT.py
。函數
from wtpy import WtBtEngine from wtpy.backtest import WtBtAnalyst from Strategies.DualThrust import StraDualThrust if __name__ == "__main__": #建立一個運行環境,並加入策略 engine = WtBtEngine() engine.init('.\\Common\\', "configbt.json") engine.configBacktest(201909100930,201910311500) engine.configBTStorage(mode="csv", path=".\\storage\\") engine.commitBTConfig() #代碼裏的配置項,會覆蓋配置文件configbt.json裏的配置項 ''' 建立DualThrust策略的一個實例 name 策略實例名稱 code 回測使用的合約代碼 barCnt 要拉取的K線條數 period 要使用的K線週期,m表示分鐘線 days 策略算法參數,算法引用的歷史數據條數 k1 策略算法參數,上邊界係數 k2 策略算法參數,下邊界係數 isForStk DualThrust策略用於控制交易品種的代碼 ''' straInfo = StraDualThrust(name='pydt_IF', code="CFFEX.IF.HOT", barCnt=50, period="m5", days=30, k1=0.1, k2=0.1, isForStk=False) engine.set_strategy(straInfo) #開始運行回測 engine.run_backtest() #建立績效分析模塊 analyst = WtBtAnalyst() #將回測的輸出數據目錄傳遞給績效分析模塊 #init_capital爲初始資金規模 #rf爲無風險收益率 #annual_trading_days爲每一年的交易日天數,用於計算年化收益 analyst.add_strategy("pydt_IF", folder="./outputs_bt/pydt_IF/", init_capital=500000, rf=0.02, annual_trading_days=240) #運行績效模塊 analyst.run() kw = input('press any key to exit\n') engine.release_backtest()
修改runBT.py,而後運行runBT.py進行回測測試
straInfo = StraDualThrust(name='pydt_IF', code="CFFEX.IF.HOT", barCnt=50, period="m5", days=30, k1=0.5, k2=0.3, isForStk=False)
上面演示了在WonderTrader
上構建一個期貨日內交易策略的基本過程。回測穩定之後,策略就能夠不做任何修改的直接放到實盤裏去運行了。但願可以對你們有所啓發。
最後再打一波廣告:WonderTrader
的github
地址:https://github.com/wondertrad...WonderTrader
官網地址:https://wondertrader.github.io阿里雲