【Elite量化策略实验室】RUMI策略 - 1
发布于VeighNa社区公众号【vnpy-community】
原文作者:用Python的交易员 | 发布时间:2023-3-1
失踪系列回归
VeighNa平台面向专业量化交易用户的Elite(菁英版)从2022年10月开始内测以来,经过5个月时间的迭代目前已经到了0.9.0版本,预计将在3月底之前正式上线发布。
前期VeighNa Elite在开发上主要专注于交易系统性能方面的优化,如:
- 低延时事件引擎:实现更高速率的系统事件处理;
- 前后端分离架构:分离前端UI界面的运行时开销;
- 多进程策略并发:突破GIL带来的CPU单核算力上限。
后续根据内测用户的反馈,开始增加交易和策略相关的功能扩展,交易方面的内容包括:市场深度委托、多账户组合交易、标准化执行算法等。策略方面推出的则是更加针对量化私募机构策略开发需求的EliteCtaStrategy和ElitePortfolioStrategy模块。
翻了下公众号的历史记录,【VeighNa量化策略实验室】系列的上一篇文章更新已经是2021年9月的事情。时隔一年半再次回归,升级为基于VeighNa Elite版的【Elite量化策略实验室】,同样希望能够在量化策略研究方面给大家带来更多的灵感。
本文分享的RUMI策略来自于VeighNa社区用户推荐的互联网资料,原作者已经很难确定,甚至我们连RUMI具体是什么的缩写也不清楚(如果有知道的欢迎评论区留言)。
为了吊起大家的胃口,先来看一下螺纹钢期货上经过各种细节优化的结果:
策略基本信息
策略来源 | 互联网资料 |
---|---|
策略类型 | CTA策略 |
核心思路 | 均线偏离度平滑识别 |
策略核心原理
移动平均线(Moving Average)作为一种大家非常熟悉的技术指标,由于其非平稳性时间序列的特征(即均值并不围绕固定中枢波动),本身并不适合作为CTA策略的核心信号使用。
而简单的使用双均线金死叉作为交易信号,尽管能够在大趋势中捕捉相当一部分行情,但在均线缠绕出现的震荡类行情中则会遭遇来回打脸的问题。
因此RUMI策略采用了对均线偏离度平滑处理的方式,来更好的识别趋势机会:
- 快速均线:使用SMA(简单移动平均)计算
- 慢速均线:使用WMA(加权移动平均)计算
- DIFF:计算快慢均线的偏离度(两者相减)
- RUMI:对偏离度使用SMA进行平滑处理
有了RUMI指标后,交易信号则是十分简单:
- RUMI上穿0轴,买入做多
- RUMI下穿0轴,卖出做空
看到这里不难发现,本质上RUMI策略是对传统均线金死叉(即DIFF上下穿0轴)信号的一种优化,通过平滑处理的方式降低DIFF的敏感度来解决均线缠绕的问题。
策略代码实现
策略参数定义
本文中的RumiStrategy采用EliteCtaStrategy模块下的策略模板类EliteCtaTemplate开发,用户可以直接通过基础参数来配置策略运行的K线数据周期(无需再创建和维护BarGenerator进行合成):
class RumiStrategy(EliteCtaTemplate):
"""RUMI策略"""
author = "VeighNa菁英版"
# 基础参数(必填)
bar_window: int = Parameter(1) # K线窗口
bar_interval: int = Parameter("1m") # K线级别
bar_buffer: int = Parameter(100) # K线缓存
在定义策略的参数和变量时,可以分别使用Parameter和Variable辅助类来创建,无需再手动添加字段名称到parameters和variables列表中:
# 策略参数(可选)
fast_window: int = Parameter(3) # 快速均线窗口
slow_window: int = Parameter(50) # 慢速均线窗口
rumi_window: int = Parameter(30) # 均线偏差窗口
max_holding: int = Parameter(100) # 持仓周期上限
stop_percent: float = Parameter(0.03) # 保守止损比例
risk_window: int = Parameter(10) # 风险计算窗口
risk_capital: int = Parameter(1_000_000) # 交易风险投入
price_add: int = Parameter(5) # 委托下单超价
# 策略变量
trading_size: int = Variable(1) # 当前委托数量
rumi_0: float = Variable(0.0) # RUMI当前数值
rumi_1: float = Variable(0.0) # RUMI上期数值
信号指标计算
EliteCtaTemplate提供了新的on_history回调函数,用于推送已经合成完毕且缓存为时间序列的K线数据容器HistoryManager:
def on_history(self, hm: HistoryManager) -> None:
"""K线推送"""
# 计算均线数组
fast_array: ndarray = sma(hm.close, self.fast_window)
slow_array: ndarray = wma(hm.close, self.slow_window)
# 计算均线差值
diff_array: ndarray = fast_array - slow_array
rumi_array: ndarray = sma(diff_array, self.rumi_window)
self.rumi_0 = rumi_array[-1]
self.rumi_1 = rumi_array[-2]
# 判断上下穿
long_signal: bool = cross_over(rumi_array, 0)
short_signal: bool = cross_below(rumi_array, 0)
由于采用了连续内存区域分配,HistoryManager可以提供比开源版ArrayManager更快的数据更新和计算效率,且支持快速转换为Pandas的DataFrame对象。当用户收到on_history推送时,HistoryManager对象中的K线缓存数据固定为bar_buffer长度,用户无需再额外进行条件判断检查数据满足长度要求。
除了封装TA-Lib中的指标计算函数外,EliteCtaStrategy模块中还额外提供了类似cross_over/cross_below的常用条件判断和逻辑运算函数。
动态仓位计算
根据市场波动对交易仓位进行动态管理的技术,能够有效平滑资金曲线波动和提高整体夏普比率,随着海龟策略体系的普及在CTA策略中被广泛应用(具体细节在《超越海龟策略精析》课程中有详细讲解)。
# 计算交易数量
self.trading_size = self.calculate_volume(self.risk_capital, self.risk_window, 1000, 1)
这里通过调用EliteCtaTemplate所提供的calculate_volume函数,传入交易风险资金risk_capital、风险评估窗口risk_window、委托数量上下限(1000、1)参数,快速计算当前市场情况下合适的交易委托数量。
目标交易执行
考虑到机构投资者普遍金额和数量较大的交易执行需求,由用户在策略中直接实现具体的买卖委托操作可能较为麻烦,因此EliteCtaTemplate提供了目标仓位交易执行功能:
# 获取当前目标
last_target: int = self.get_target()
# 初始化新一轮目标(默认不变)
new_target: int = last_target
# 执行开仓信号
if long_signal:
new_target = self.trading_size
elif short_signal:
new_target = -self.trading_size
# 持仓时间平仓
if self.bar_since_entry() >= self.max_holding:
new_target = 0
# 保护止损平仓
close_price = hm.close[-1]
if last_target > 0:
stop_price: float = self.long_average_price() * (1 - self.stop_percent)
if close_price <= stop_price:
new_target = 0
elif last_target < 0:
stop_price: float = self.short_average_price() * (1 + self.stop_percent)
if close_price >= stop_price:
new_target = 0
# 设置新一轮目标
self.set_target(new_target)
# 执行目标交易
self.execute_trading(self.price_add)
# 推送UI更新
self.put_event()
在上文代码中,交易执行的步骤可以分解为:
- 通过get_target函数,获取策略当前T时刻的目标仓位数值last_target;
- 结合策略核心逻辑判断,计算生成新的目标仓位数值new_target;
- 调用set_target函数,设置策略T+1时刻的目标仓位数值;
- 调用execute_trading函数,计算目标持仓target和实际持仓pos的差值执行委托交易;
同时除了RUMI策略本身的多空交易逻辑外,我们出于实盘交易中的谨慎风险管理原则,额外添加了两块平仓出场逻辑:
-
总持仓时间限制的出场:
-
使用bar_since_entry函数,获取开仓以来经过的K线总数量;
-
当总数量超过一个上限值,认为趋势已经衰减结束,平仓离场;
-
保护性止损位平仓出场:
-
在多空持仓场景下,分别使用long_average_price和short_average_price函数获取开仓成本价;
-
以做多为例:当价格跌破多头开仓成本价一个固定百分比,则止损离场
在实盘交易中,目标交易执行模式的主要优势包括:
- 策略逻辑围绕目标持仓展开,开盘初始化时可以直接通过历史数据回放来恢复正确的策略状态,无需再依赖昨日收盘时的策略变量缓存(如基于实际仓位跟踪的移动止损高低点),减少在实盘运维中的出错风险;
- 用户不再需要为繁琐的委托下单代码头疼(尤其是某些比较复杂策略中的交易状态机管理),当执行数量较大时可以由策略引擎自动调用平台内置的执行算法(TWAP、Sniper、BestLimit等)来进行拆单交易,降低整体的交易成本。
回测结果
回测数据上,本文中选择使用米筐RQData提供的rb99连续指数合约数据(rb888连续主力合约数据由于长期的换月升贴水平滑,较早时期的价格已变为负数),在后续的篇幅中我们会尝试更多的品种,回测配置如下:
- 本地代码:rb99.SHFE
- K线周期:1分钟
- 开始日期:2010-1-1
- 结束日期:2023-2-3
- 手续费率:0.0001
- 交易滑点:1
- 合约乘数:10
- 价格跳动:1
- 回测资金:1000W
作为原始版本策略的源码复现,初步回测结果勉强过得去:
策略回测的关键统计结果:
- 总交易日:3226
- 盈利交易日:1425
- 总收益率:338.81%
- 年化收益:25.21%
- 百分比最大回撤:-32.80%
下一篇文章中,将会详细讲解如何使用EliteCtaStrategy实现基于稳健统计指标R-Cubed的参数优化,以及分享RumiStrategy在更多品种上的回测绩效。
当前VeighNa Elite版仍处于免费内测阶段,内测账号可以通过提供量化私募机构的投研人员名片申请,感兴趣的同学请扫码添加小助手:
免责声明
文章中的信息或观点仅供参考,作者不对其准确性或完整性做出任何保证。读者应以其独立判断做出投资决策,作者不对因使用本报告的内容而引致的损失承担任何责任。