发布于VeighNa社区公众号【vnpy-community】
原文作者: 李思佳 | 发布时间:2024-04-02
此前【Elite量化策略实验室】的期货类策略分享主要围绕商品期货展开,本篇文章要分享的Efficiency策略则基于捕捉国债期货趋势的思路构造。
原策略研报发表于2019年7月,距今已有快5年时间,但思路仍可借鉴。本文通过VeighNa Elite平台对其进行了代码上的实现与改进,让Efficiency指标以另一种方式构成了有效信号。
策略基本信息
策略核心原理
Efficiency,即效率,定义为回看t期总体涨跌幅与每期涨跌幅绝对值之和的比值。假设p0表示当期价格,pi表示过去i期的价格,t表示回看期,则Efficiency的计算公式如下:
上式从曲线形态上理解就是位移除以路程:从t时刻到当前时刻的价格变化量(价格的位移)除以该段时间窗口价格曲线轨迹的路程。Efficiency取值位于-1到1之间,绝对值越接近于1就说明趋势越明显。
由于国债期货的价格波动相对其他期货品种微弱,更多的策略逻辑只会减少交易机会,所以策略没有增加复杂的调仓和平仓方法。那么,基于Efficiency指标,可以构建一个简单的趋势策略:
- Efficiency绝对值大于阈值,则做多平空
- Efficiency绝对值小于阈值,则做空平多
策略在开平仓上只有两个参数,即用于计算Efficiency的回看窗口参数efficiency_window和指标阈值efficiency_limit。
原策略中,当Efficiency高于设定阈值时,策略会发出做多信号;而当Efficiency低于负阈值时,策略则发出做空信号。而在本策略的calculate_efficiency函数中实际计算的是原文章Efficiency的绝对值,因此,策略信号均发生了改变:
- 只要Efficiency的绝对值高于阈值,无论趋势方向如何,均视为强劲趋势并发出做多信号;
- 相反,当Efficiency的绝对值低于阈值时,则视为市场处于震荡状态,发出做空信号。
策略代码实现
Efficiency计算函数定义
def calculate_efficiency(
data: np.ndarray,
n: int
) -> float:
"""
计算效率的绝对值
n参数代表时间窗口
返回值:t时刻的效率绝对值,取值在0到1间,效率比的值越接近1噪声越低,趋势越明显
"""
# 计算价格曲线轨迹的位移
x: float = abs(data[-1] - data[-n])
# 计算价格净变化,即价格的路程
diff_array: np.ndarray = np.diff(data)
abs_diff: np.ndarray = np.abs(diff_array)
s: float = np.sum(abs_diff[-n:])
return x / s
策略参数定义
class EfficiencyStrategy(EliteCtaTemplate):
"""效率趋势策略"""
author = "VeighNa Elite"
# 基础参数(必填)
bar_window: int = Parameter(5) # K线窗口
bar_interval: int = Parameter("1h") # K线级别
bar_buffer: int = Parameter(100) # K线缓存
# 策略参数(可选)
efficiency_window: int = Parameter(85) # 效率窗口
efficiency_limit: int = Parameter(0.02) # 效率比阈值
risk_window: int = Parameter(10) # 风险窗口
risk_capital: int = Parameter(1_000_000) # 风险敞口
price_add: int = Parameter(0.05) # 委托下单超价
# 策略变量
trading_size: int = Variable(1) # 当前委托数量
信号指标计算
在on_init回调函数中初始化一个用于缓存efficiency指标的numpy数组:
def on_init(self) -> None:
"""初始化"""
self.write_log("策略初始化")
self.efficiency_arr = np.zeros(self.bar_buffer)
self.load_bar(100)
在on_history回调函数中使用前文已经定义好的calculate_efficiency函数计算efficiency值,并将其缓存在efficiency_arr中:
def on_history(self, hm: HistoryManager) -> None:
"""K线推送"""
# 计算效率比
efficiency: float = calculate_efficiency(hm.close,
self.efficiency_window)
# 缓存效率比值
self.efficiency_arr[:-1] = self.efficiency_arr[1:]
self.efficiency_arr[-1] = efficiency
# 判断交易信号
long_signal: bool = self.efficiency_arr[-1] >= self.efficiency_limit
short_signal: bool = self.efficiency_arr[-1] <= self.efficiency_limit
动态仓位计算
# 计算交易数量
self.trading_size = self.calculate_volume(self.risk_capital, self.risk_window, 1000, 1)
目标交易执行
# 获取当前目标
last_target: int = self.get_target()
# 初始化新一轮目标(默认不变)
new_target: int = last_target
# 执行开仓信号
if not last_target:
if long_signal:
new_target = self.trading_size
elif short_signal:
new_target = -self.trading_size
# 信号反转平仓
if last_target > 0 and short_signal:
new_target = 0
if not last_target:
new_target = -self.trading_size
elif last_target < 0 and long_signal:
new_target = 0
if not last_target:
new_target = self.trading_size
# 设置新一轮目标
self.set_target(new_target)
# 执行目标交易
self.execute_trading(self.price_add)
# 推送UI更新
self.put_event()
回测结果
回测数据上,本文选择使用米筐RQData提供的五年期期货TF99连续指数合约数据,在后续篇幅中还将使用T99十年期国债期货和TS99两年期国债期货数据进行回测,回测配置如下:
- 本地代码:TF99.CFFEX
- K线周期:1分钟
- 开始日期:2021-1-1
- 结束日期:2024-3-1
- 手续费率:0
- 交易滑点:0.005 + 3e-06
- 合约乘数:1000000
- 价格跳动:0.005
- 回测资金:1000000
由于国债期货的交易手续费均为3元,是固定比手续费,所以此处将手续费除以合约乘数后在滑点中体现。
原文基于2014年10月至2019年6月的数据所得参数显然对于现在不完全适用,不过其优化参数集合仍可作为借鉴。设定”efficiency_window”在10到140的区间以10的步长、“efficiency_limit”在0.01到0.1的区间以0.01为步长进行调整,并使用2021年1月到2024年3月的数据作为总数据集,选取2021年1月到2023年1月作为样本内进行参数优化,得到以下结果:
策略参数
- bar_window: 5
- bar_interval: "1h"
- bar_buffer: 100
- efficiency_window: 40
- efficiency_limit: 0.08
其他国债期货效果
使用与前文五年期国债期货相同的回测配置和优化方法,得到以下结果:
TS99.CFFEX(两年期国债期货)
策略参数
- bar_window: 5
- bar_interval: "1h"
- bar_buffer: 100
- fast_window: 40
- slow_window: 0.01
T99.CFFEX(十年期国债期货)
策略参数
- bar_window: 5
- bar_interval: "1h"
- bar_buffer: 100
- fast_window: 60
- slow_window: 0.08
免责声明
文章中的信息或观点仅供参考,作者不对其准确性或完整性做出任何保证。读者应以其独立判断做出投资决策,作者不对因使用本报告的内容而引致的损失承担任何责任。