vnpy怎么创建策略并回测_vn.py社区精选20 - 做不出好策略?试试这3个新的风险分析指标!...

1fcc2db8f968e32ebcfafd6c5b44d225.png

发布于vn.py社区公众号【vnpy-community】

原文作者:KeKe | 发布时间:2020-03-08

传统的回撤分析

在对CTA策略的历史数据回测研究中,除了直接观察资金曲线的形状外,更多会基于各类围绕资金曲线的统计指标,来对策略的业绩表现进行评估,例如:

  • 年化收益率
  • 收益率标准差
  • 夏普比率
  • 历史最大回撤
  • 最长回撤时间
  • 等等

其中历史最大回撤和最长回撤时间两个统计指标,描述的是在最坏情况下(即连续亏损时)策略的损失程度。但在任何给定的样本数据上跑完回测,得到的回撤指标都分别只有一个点的估计值(最坏情况),同时其对策略参数变化的敏感性非常高。

因此在对策略的整体风险评估上,如果仅仅使用以上两个回撤指标,可能使得我们分关注极端情况。尤其是在参数优化时,导致我们选择一些仅仅只是幸运避开了个别几笔亏损交易,但整体预测效果未必最佳的参数组合。

好在,最近一篇新的论文提供了一个可靠的研究改进方向。

一篇新的研究论文

2019年德国科隆大学金融研究中心的Korn, Moller和Schwehm合作发布了一篇研究量化策略回撤分析的文章:

《Drawdown Measures: Are They All the Same?》

1) 在文章中,他们首先创建了6个不同的回撤指标:

  • Average Drawdown, 平均回撤(ADD)
  • Linearly Weighted Drawdown, 线性加权回撤(lwDD)
  • Average Squared Drawdown, 均方回撤(ADD^2)
  • Trend Weighted Drawdown, 趋势加权回撤(twDD)
  • Maximum Drawdown, 最大回撤(MDD)
  • End-of-period Drawdown, 期末回撤(eopDD)

指标的公式和图形如下:

b2641ec9ddfd2197dbda382372ac621f.png

2)然后做了一个对比分析,用不同回撤指标来识别:

  • 随机策略(hit ratio 0.5)
  • 具有正数学期望的策略(hit ratio 0.6)

发现平均回撤(ADD)、线性加权回撤(lwDD)以及均方回撤(ADD^2)三者的效果最为接近:

e3ecc3759c745fbb99c90459518f446d.png

3)除了相似性以外,这3个指标的识别效果也更佳,即相比于最大回撤 MDD,这些回撤指标能够更有效的区分随机和正期望策略:

30dd8beb2a09f74d3dc1a8ea87f69fd0.png

基于vn.py的代码实现

首先,我们需要在Jupyter Notebook中,以命令行CLI交互的模式调用CtaStrategy模块的回测功能(不会的请戳这里),对一个策略执行历史数据回测,得到资金曲线图和百分比回撤图:

c306841692039ad3a2381a4d8d8def07.png

此时可以访问BacktestingEngine对象实例的daily_df(DataFrame格式)数据,获取基于逐日盯市规则计算的每日策略统计数据(如当日盈亏、累计盈亏、成交笔数、百分比最大回撤等):

1ffb94198f501370d8de85636e5d34a6.png

由于百分比回撤是负数,为了后续研究的时候方便观察,先创建一个新的列【ddpercent_justed】,这是调整为正数后的百分比最大回撤,然后画图显示出来:

4b193c08155de15a4cd0dd2c6ffb17f4.png

最大回撤可以通过对百分比回撤序列调用max函数方法,得到结果为1.4%:

b92b31e650aad5e7d17a60385477bad0.png

有了参考对象,接下来我们开始计算以上论文中提到的3个更为有效回撤指标。

平均回撤(ADD)

平均回撤就是每日百分比回撤的算数平均,直接调用mean方法得到结果为0.41%,即采用简单的算术平均计算策略整体每日亏损的风险为0.41%:

d5a2e35c5c563fce36b7d1ade36dfa38.png

线性加权回撤(lwDD)

线性加权回撤就是使用最小二乘法(OLS)对回撤曲线进行线性回归,从而得到一条回撤的趋势线。最小化误差平方和的方法有利于避免极端情况的影响,让我们把关注点更多集中在策略的整体风险水平上。一个好的策略,其回撤的回归直线的斜率应该尽可能的小。

由于这里要求的只是回归线的终值,而不需要过程中的其他信息,所以无需动用那些巨型的数据分析库,如sklearn、statsmodels、scipy等等,这里我们选择更加轻量级的talib库(没错,算技术指标的那个)。

在交互式模式下执行命令:help(talib.LINEARREG),我们可以知道线性回归函数的入参有2个:

0994d7708b808eded0fbb8ac652c6759.png

其中:

  • real:需要被回归的数据,即每天的百分比回撤数据;
  • tiemperiod:回望周期,我们需要最整体样本进行回归,故回望周期等于数据的样本数(在逐日盯市统计下,为策略回测的天数)。

这样,我们得到线性加权回撤的结果为0.31%,即通过最小二乘法去噪声后,策略整体每日亏损的风险为0.31%:

022db672da970a4f5356a26e3811e8df.png

均方回撤(ADD^2)

均方回撤就是每日百分比回撤的平方的期望值,采用这种计量方法的原因在于认为策略样本内回测属于小样本评估,属于有偏估算。

均方回撤的计算方法同样简单,只需要将百分比回撤这一列的数据平方后,再求平均数即可,最终得到均方回撤的结果为0.32%:

9e9b8559481abfd7341e80a523214f9b.png

回测引擎扩展

最后我们可以将上述3个新的回撤统计指标,添加到回测引擎的统计指标计算函数中。打开位于C:vnstudioLibsite-packagesvnpyappcta_strategy目录中的backtesting.py文件,找到其中的calculate_statistics函数,修改的内容分为三步:

  1. 初始化新的回撤指标数据;
  2. 计算3个新的回撤指标数值;
  3. 打印输出新的回撤指标;

新增的代码如下:

...            
         average_drawdown = 0            
         lw_drawdown = 0            
         average_square_drawdown = 0   
...
         average_drawdown = df["ddpercent"].mean()            
         lw_drawdown = talib.LINEARREG(df["ddpercent"], total_days)[-1]

         df["ddpercent^2"] = df["ddpercent"] * df["ddpercent"]                             
         average_square_drawdown = - df["ddpercent^2"].mean()
...           
         self.output(f"百分比平均回撤: {average_drawdown:,.2f}%")                           
         self.output(f"百分比线性加权回撤: {lw_drawdown:,.2f}%")                           
         self.output(f"百分比均方回撤: {average_square_drawdown:,.2f}%")

完成修改后,再次回到Jupyter Notebook中执行回测,已经可以在打印输出的回测结果中找到三个新增的回撤指标:

23c9eedbf87156f44d213caa5a674b74.png

同样在CLI命令行模式下,我们也可以很方便的这3个新的回撤指标(average_drawdown、lw_drawdown、average_square_drawdown),作为CTA策略参数优化的目标函数,筛选出稳健性更好的参数组合。

《vn.py全实战进阶 - 期权零基础入门》课程已经更新过半!内容专门面向从未接触过期权交易的新手,共计30节课程带你一步步掌握期权的基础知识、了解合约特征和品种细节、学习方向交易和套利组合等各种常用期权交易策略,详细内容请戳新课上线:《期权零基础入门》

首先,需要安装好vnpy库,并在vnpy的安装目录下找到vn.trader\app\cta_strategy\strategy_template.py文件,这是vnpy策略模板文件。 接下来,我们根据期货套利策略的需求,对策略模板进行修改。具体而言,我们需要重写以下方法: 1. on_init(self): 用于策略初始化,包括设置参数、订阅合约等。 2. on_start(self): 策略启动时执行的方法。 3. on_stop(self): 策略停止时执行的方法。 4. on_tick(self, tick: TickData): 处理行情数据的方法,用于更策略的状态。 5. on_trade(self, trade: TradeData): 处理成交数据的方法。 6. on_order(self, order: OrderData): 处理委托数据的方法。 7. on_bar(self, bar: BarData): 处理K线数据的方法,用于实现基于K线的策略。 下面是一个简单的期货套利策略示例,以两个相同品种但不同到期日的期货合约为例。假设当前时间为t,合约A的到期日为t1,合约B的到期日为t2,我们的策略目标是通过买入A合约,卖出B合约来实现套利。当t1-t2的时间差小于某个阈值时,我们就认为A和B之间存在套利机会,此时我们就执行套利交易。 ```python from vnpy.app.cta_strategy import ( CtaTemplate, StopOrder, TickData, BarData, TradeData, OrderData, Direction ) class FuturesArbitrageStrategy(CtaTemplate): """""" author = "Your name" strategy_name = "FuturesArbitrageStrategy" # 策略参数 spread_threshold = 5 # 时间差阈值,以分钟为单位 # 策略变量 long_pos = 0 # 多头持仓 short_pos = 0 # 空头持仓 last_tick_A = None # A合约最tick数据 last_tick_B = None # B合约最tick数据 last_trade_A = None # A合约最成交数据 last_trade_B = None # B合约最成交数据 parameters = ["spread_threshold"] variables = [ "long_pos", "short_pos", "last_tick_A", "last_tick_B", "last_trade_A", "last_trade_B" ] def on_init(self): """ 策略初始化 """ self.write_log("策略初始化") # 订阅合约 self.subscribe(self.symbol_A, "") self.subscribe(self.symbol_B, "") def on_start(self): """ 策略启动 """ self.write_log("策略启动") def on_stop(self): """ 策略停止 """ self.write_log("策略停止") def on_tick(self, tick: TickData): """ 处理行情数据 """ if tick.vt_symbol == self.symbol_A: self.last_tick_A = tick else: self.last_tick_B = tick # 如果A和B的最tick数据都存在,就进行套利判断 if self.last_tick_A is not None and self.last_tick_B is not None: t1 = self.last_tick_A.datetime t2 = self.last_tick_B.datetime if abs((t1 - t2).total_seconds() / 60) < self.spread_threshold: self.arbitrage() def on_trade(self, trade: TradeData): """ 处理成交数据 """ if trade.vt_symbol == self.symbol_A: self.last_trade_A = trade else: self.last_trade_B = trade def on_order(self, order: OrderData): """ 处理委托数据 """ pass def on_bar(self, bar: BarData): """ 处理K线数据 """ pass def arbitrage(self): """ 套利交易 """ # 如果A和B的最成交数据都存在,就进行套利交易 if self.last_trade_A is not None and self.last_trade_B is not None: price_A = self.last_trade_A.price price_B = self.last_trade_B.price if price_A > price_B: # A合约价格高于B合约,执行买入A合约,卖出B合约 self.buy(self.symbol_A, 1, price_A) self.short(self.symbol_B, 1, price_B) else: # A合约价格低于B合约,执行卖出A合约,买入B合约 self.short(self.symbol_A, 1, price_A) self.buy(self.symbol_B, 1, price_B) ``` 以上就是一个简单的期货套利策略的实现,可以使用vnpy回测引擎进行回测。需要注意的是,这个策略只是一个示例,实际的套利策略需要根据具体的市场情况和交易规则进行设计。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值