当前位置: 首页 > 后端技术 > Python

10分钟在WonderTrader上创建一个期货盘中交易策略

时间:2023-03-25 21:54:22 Python

今天我们将使用WonderTrader的python子框架wtpy来实际编写一个期货盘中交易策略。那么我们会先为第一轮测试设置一组参数,然后根据第一轮测试的结果调整参数,然后进行第二轮测试。用它来演示如何在wtpy中编写和回测策略。准备安装wtpy。在安装了python3.6或以上版本的电脑上执行命令。$pipinstallwtpy或者直接下载whl文件安装到本地阿里云镜像地址:https://mirrors.aliyun.com/py...pipy地址:https://pypi.org/project/wtpy...来自github复制demo,我们使用期货回测demo作为基准demo进行修改。期货回测demo下载地址:https://github.com/wondertrad...准备历史回测数据我们直接使用demo自带的主力股指期货5分钟数据进行回测,文件名为中金所。HOT_m5.csv。为了提高测试效率,我们只选取最近两个月的数据进行回测,具体为2019年9月10日至2019年10月31日,股指期货主力合约2019年9月10日至10月31日的走势,20192019年9月10日开盘价3976.2,2019年10月31日收盘价3879.0,涨幅-2.44%。确定策略算法和选择策略算法我们选择比较经典的DualThrust作为我们的策略算法。一方面,DualThrust已经存在了很长时间,许多大型组织已经使用这种模式获得了足够的收益;另一方面,DualThrust的算法复杂度比较低,更适合我们作为演示策略使用。DualThrust的算法逻辑如下以MAX(HH-LC,HC-LL)为计算上下边界的参考值,以今日开盘价为参考价,再以上边界系数和下边界系数边界系数分别计算上边界的价格和下边界的价格,当最新价格突破上边界或下边界时,就是我们发出信号的时候。但是在策略的实现中,我们还需要考虑如何处理已有的仓位,所以最终的策略逻辑是:当仓位为0时,当价格突破上边界时,开多进场行情,当价格突破下边界时,开空进场。多头时,当价格突破上边界时,持仓。当价格突破下边界时,做多做空。,持仓策略的实现参数说明在确定了策略的算法之后,我们需要确定策略模块的参数。参数的设置要综合考虑策略本身的参数和模块使用的参数。最后我们确定了以下参数:namestrategyinstancenamecode用于回测的合约代码barCnt拉取K线的数量period需要使用的K线周期,形式为周期类型+周期倍数,如m5表示5-分钟线,d3代表3日线天数策略算法参数,k1策略算法参数算法参考的历史数据数,k2策略算法参数的上边界系数,下边界系数是ForStkDualThrust策略用来控制交易品种代码。我们还可以将基本手数作为参数传递给策略模型,这样更加通用。不过这里我们不再添加参数,默认手数为1手。最终策略源码如下:fromwtpyimportBaseStrategyfromwtpyimportContextclassStraDualThrust(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__=daysself.__k1__=k1self.__k2__=k2self.__period__=periodself.__bar_cnt__=barCntself.__code__=代码self.__is_stk__=isForStkdefon_init(self,context:Context):code=self.__code__#varietycodeifself.__is_stk__:code=code+"Q"context.stra_get_bars(code,self.__period__,self.__bar_cnt__,isMain=True)context.stra_log_text("DualThrustinited")defon_calculate(self,context:Context):'''策略的主要调用函数,所有的计算逻辑都在这里完成'''code=self.__code__#speciescode#交易单位,主要用于股票适配trdUnit=1ifself.__is_stk__:trdUnit=100#Readthelast501-minutelines(dataframeobject)theCode=codeifself.__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__#平仓顺序,最高价顺序,最低价顺序"high"]lows=df_bars["low"]#读取前几天到前一个交易日的数据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"]'''!!!!!!这里是重点1.首先根据最后一根K线时间,计算出当前日期2.根据当前日期,对日线进行切片,截取需要的数3.最后,在最后计算出需要的数据slice'''#确定上下轨upper_bound=openpx+k1*max(hh-lc,hc-ll)lower_bound=openpx-k2*max(hh-lc,hc-ll)#读取当前位置curPos=context.stra_get_position(code)/trdUnitifcurPos==0:ifhighpx>=upper_bound:context.stra_enter_long(code,1*trdUnit,'enterlong')context.stra_log_text("向上突破%.2f>=%.2f,多头入场"%(highpx,upper_bound))#修改保存self.xxx=1context.user_save_data('xxx',self.xxx)如果lowpx<=lower_bound而不是self.__is_stk__则返回:context.stra_enter_short(code,1*trdUnit,'entershort')context.stra_log_text("Breakthrough%.2f<=%.2f,shortpositionEntry"%(lowpx,lower_bound))returnelifcurPos>0:iflowpx<=lower_bound:context.stra_exit_long(code,1*trdUnit,'exitlong')context.stra_log_text("突破%.2f<=%.2f,多头仓退出"%(lowpx,lower_bound))#raiseException("exceptonpurpose")returnelse:ifhighpx>=upper_boundandnotself.__is_stk__:context.stra_exit_short(code,1*trdUnit,'exitshort')context.stra_log_text("突破%.2f>=%.2f,空头头寸Appearance"%(highpx,upper_bound))returndefon_tick(self,context:Context,stdCode:str,newTick:dict):return第一轮回测确定参数,我们使用主力的5分钟K线回测股指期货合约,一次读取50条历史K线,利用最新的30条K线计算上下突破边界,上边界系数初始设置为0.1,下边界系数为同样初始设置为0.1,修改runBT.py中策略的参数,然后运行runBT.py。fromwtpyimportWtBtEnginefromwtpy.backtestimportWtBtAnalystfromStrategies.DualThrustimportStraDualThrustif__name__=="__main__":#创建运行环境并添加策略engine=WtBtEngine()engine.init('.\\Common\\',"configbt.json")engine.configBacktest(201909100930,201910311500)engine.configBTStorage(mode="csv",path=".\\storage\\")engine.commitBTConfig()#代码中的配置项会覆盖配置文件configbt.json中的配置项'''创建DualThrust策略名称策略实例名称code回测用的合约代码barCnt要拉的K线数period要使用的K线周期,m代表分钟线天数strategyalgorithmparameters,算法引用的历史数据数k1o控制交易品种'''strInfo=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('按任意键退出\n')engine.release_backtest()回测执行完成后,打开此后生成的性能分析报告文件,查看性能分析结果,回测性能概览然后打开交易日志文件,查看交易详情...回测结果分析从上面的性能报告我们可以看到,在这组参数下,从2019年9月10日到2019年10月31日下午收盘,也就是一个完整的开平370手,换算成成交量,是740手。虽然交易多,但收益并不理想。不到2个月的时间,50万资金累计损失近10万,约20%。从上面的截图我们可以看到最后一次进场时的总盈亏是4,980.00,也就是说策略的逻辑最终是盈利的,但是盈利的量很少。我们再看看每天结算的资金。从上图可以看出,2个月的手续费共花费了104,775.82,所以账户的总盈亏为-99,795.82。这样,我们大致可以得出一个结论:由于上下边界不够宽,噪声信号较多,也触发了买卖逻辑,导致买卖频繁,佣金高,最终导致亏损。第二轮回测重新调整参数。根据上一轮的结果分析,我们需要加宽上下边界,以过滤掉一些噪声波动,减少信号数量。所以在我们第二轮回测中,我们将上限系数改为0.5,下限系数改为0.3。修改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)再次查看业绩报告回测业绩概览交易详情每日资金结算重分析结果从第二轮业绩报告可以看出,当边界被拉宽时,交易信号减少到14几轮下来,交易收益达到了24060.00,约为第一轮收益的5倍,而佣金仅为1453.02元,比第一轮佣金低了两个数量级。最终累计收益也从第一轮的-19.96%上升到4.31%的盈利。与-2.44%的基准收益率相比,相对收益率达到了6.75%。(这个算法对吗?:orz:)结论以上演示了在WonderTrader上构建期货盘中交易策略的基本过程。回测稳定后,无需任何修改即可直接将策略放入真盘运行。希望能够对大家有所启发。最后再来一波广告:WonderTrader的github地址:https://github.com/wondertrad...WonderTrader官网地址:https://wondertrader.github.io