今天咱们来用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阿里云