资讯详情

资讯详情

深度解析Dream-RSI:从固定阈值到市场状态自适应的指标进化

用RSI的人十有八九都经历过这种憋屈时刻价格在底部来回磨RSI却早早翘起来给你发“超卖”信号等你真金白银杀进去它又继续趴十天半个月。我以前总以为是参数没调好后来做量化时间长了才明白这不是你运气差是RSI这种固定周期、固定阈值的静态指标在结构上就不适应会切换状态的市场。这次谷歌DeepMind放出的Dream-RSI研究恰好就是在动这一刀——把传统RSI从“一把尺子量到底”改造成能自己判断市场环境、自己调参数的动态系统。这篇文章我会从三件事展开第一传统RSI到底痛在哪里为什么它不是算错而是“状态不敏感”第二Dream-RSI的设计思路和技术路线我会把我看到的设计意图逐层拆开第三基于公开信息和业界惯例给出一套可以落地的复现方案和回测坑点。如果你平时用RSI做过交易信号、或者想把手里的指标改造成自适应版本这篇应该能帮你少走不少弯路。1. 传统RSI的三个“难言之隐”为什么老指标越用越别扭1.1 滞后不是玄学是Wilder平滑的数学宿命先快速回顾一下RSI的计算方式。标准的14日RSI核心是算“上涨均值”和“下跌均值”然后求比值RS 平均上涨幅度 / 平均下跌幅度 RSI 100 - 100 / (1 RS)关键在于“平均”这两个字。Wilder当年用的不是简单移动平均而是一种递归平滑今日平滑值 昨日平滑值 × (n-1) / n 今日原始值 × 1 / n拿14日窗格来说n 14相当于给昨天的平滑值打了13/14的折扣再乘以当日涨跌。懂信号处理的朋友一眼就能看出来这就是个一阶IIR低通滤波器截止频率取决于n的大小。低通滤波的含义是什么它天生就是为了“压制噪声”而设计的。但你把它用在行情数据上代价就是相位滞后——波动越大、滤波越强、滞后越狠。很多教材里会用“RSI钝化”这个词来描述其实说白了就是14天的记忆太长当市场已经拐头的时候RSI还记着前面那波趋势的旧账。这块有个天然的物理约束任何线性滤波器都无法同时做到“对突变响应快”和“对噪声不敏感”。你要提高RSI的反应速度就得缩小周期比如改成6日但周期一小噪声就大指标频繁钝化翻转假信号也跟着变多。传统RSI在这个两难问题上没有还手之力因为它根本不看市场状态。1.2 固定阈值70/30默认了一个根本不成立的前提另一个隐蔽的问题藏在阈值里。为什么大家普遍用70/30因为这个数值来自一个隐含假设市场收益率近似正态分布且分布是平稳的那RSI在70以上的概率就大约对应某类极端行情。这套逻辑在理想化的随机游走模型里说得通但真实市场不是这样的。真实市场的收益率有明显厚尾特征突发事件、跳空、流动性枯竭都会让价格在短时间内剧烈抖动。你用固定阈值去衡量这样一个过程结果就是——震荡市里RSI很容易触到70/30发出买卖信号可一旦进入强趋势价格可以沿着一个方向连续走RSI在70以上待好几天甚至好几周如果你按“超买就卖”的规则操作就会被趋势反复打脸。我做过一个简单的统计实验拿一段典型的单边趋势行情用标准RSI(14)和固定阈值去跑事后你会看到超买信号一个一个出现但如果全按这些信号反向操作收益曲线会很难看。问题不在RSI公式而在“无论行情什么样我都用同一个周期、同一对阈值”这个使用方法上。1.3 “Dream”这个词暗示了不一样的技术路线看到“Dream-RSI”这个名字我第一反应是谷歌DeepMind把做世界模型World Model的那套东西搬到指标研究上来了。了解DeepMind近年工作的人应该记得他们在Dreamer系列里提出过一个思路让模型在潜在空间里“想象”未来的多条轨迹再基于想象结果做规划或决策。RSI研究如果也走这个路子思路就通透了不再用14天这种拍脑袋的固定窗口而是让模型先在历史序列上学会“现在是震荡状态还是趋势状态”然后根据状态去生成合适的RSI周期和阈值。换句话说过去是“你用RSI去适应行情”现在变成“行情先决定RSI怎么算”。这正好补上了前面说的两个结构短板。2. Dream-RSI的设计推理从“固定阈值”到“状态自适应”的进化路2.1 市场状态识别数据驱动比人工分型更可靠要把RSI变成自适应指标第一个问题是怎么判断当前市场处于什么状态传统做法有很多比如用ADX平均趋向指数判断趋势强度用布林带宽度判断波动率甚至有人手工划线分震荡和趋势。这些方法都能用但边界模糊参数敏感而且本质上还是人先定义“什么叫趋势”、再拿规则去套并没有让数据自己说话。Dream-RSI大概率换了个方式用神经网络对时间序列窗口做特征编码输出的是“当前处于趋势状态的概率”和“震荡状态的概率”。这个做法在深度学习里很成熟本质上是一个序列分类问题。输入可以是过去若干根K线的开高低收、成交量、收益率序列输出就是状态概率。我推算它的训练思路大概是这样的先用大量历史行情数据按统计方法比如收益率均值回归程度、趋势斜率显著性、波动率集中度等自动打标签划分成趋势段、震荡段、高波动段然后让编码器网络去拟合这些标签。等模型学会之后你给它任意一个实时窗口它就能输出一个平滑的状态概率。这样做的好处是状态识别是连续的不是非黑即白。比如模型输出0.75的震荡概率系统就会在动态调整参数时给“震荡风格”的配置分配更高的权重。这就比硬切分“当前是趋势”要顺滑很多。2.2 动态参数生成把RSI的公式变成参数可调的函数一旦有了状态识别结果下一步就是让参数跟着状态走。我们可以把传统的RSI公式改造成一个带参数表的函数window f(震荡概率, 趋势概率) threshold_up g(震荡概率, 趋势概率) threshold_down h(震荡概率, 趋势概率)具体怎么映射呢按我的实践经验至少有两种可取的方向第一种是连续映射。离震荡中心越近window越短阈值越接近传统的70/30离趋势越近window越长阈值向外偏移。比如震荡状态用RSI(9)、阈值75/25趋势状态用RSI(21)、阈值85/15。这样做的目的是震荡市里用小周期和中等阈值抓反转趋势市里用大周期和极端阈值过滤噪声只捕捉真正的趋势衰竭。第二种是直接学习映射把状态概率向量扔给一个小型神经网络输出一组可以后续组装的参数。这个更黑盒但灵活性更高可以同时输出长短期两组RSI参数。我在自己的项目里试过类似的思路连续映射的方式更容易调试因为中间每个环节都能解释清楚。出问题的时候你可以直接看“模型是不是把状态识别错了”而不是面对一个完全无法解释的黑盒。这点在指标类研究里非常关键毕竟指标本身是用来辅助决策的如果它自己变成一个不可解释的模型那交易者很难信任它。2.3 多尺度特征融合单周期RSI的信号噪声比太弱如果你只把14改成9或者21本质上还是单周期指标很多信息仍然被丢掉。Dream-RSI更值得参考的地方我认为是把多周期RSI做了一次融合。举个具体的例子你可以同时计算RSI(5)、RSI(14)、RSI(21)三档数值。正常情况下三档数值会呈现“短周期加快、长周期稳”的特征。如果RSI(5)先突破80但RSI(21)还在65附近徘徊说明这波拉升可能只是短期情绪爆点趋势未确认如果RSI(21)也同步走高还突破了85那大概率是趋势性行情。把这些多周期数值连同状态概率一起喂给决策层就形成了一个比单一RSI丰富得多的信号空间。这和很多交易员看盘时“同时盯几个周期的RSI”是一样的道理只是Dream-RSI能把这个过程自动化、参数化并且根据市场状态动态调整各周期的权重。3. 复现管线把Dream-RSI从论文拆到可运行的Python代码3.1 数据准备清洗排序是第一道坎别在这翻车不管论文里用了多复杂的模型落到代码上第一步永远是数据。你需要准备的是标准OHLCV数据也就是开盘价、最高价、最低价、收盘价、成交量。数据质量直接影响后面所有环节尤其是序列方向和时间戳对齐问题很多人在这一步就踩坑了。要点有三个时间戳必须转成UTC标准时间并且按时间升序排序不能有缺失或重复。缺失K线要显式处理可以直接剔除也可以用前值填充但不能让索引里出现空洞而浑然不知。对可能有拆分、分红的价格序列建议使用复权后的价格否则RSI的涨跌幅计算会出现虚假的跳变。推荐直接用pandas读取CSV或者从数据库取数然后做一次基础清洗import pandas as pd def load_data(filepath: str) - pd.DataFrame: df pd.read_csv(filepath) df[timestamp] pd.to_datetime(df[timestamp], utcTrue) df df.sort_values(timestamp).drop_duplicates(subsettimestamp) df df.set_index(timestamp) df[return] df[close].pct_change() df df.dropna() return df这里我给一个提醒pct_change()默认用前一个交易日做分母但如果你的数据本身跨了不同的交易时段比如某些品种周末停盘、部分外汇交易所有周末缺口直接用pct_change可能算出隔夜跳跃。更稳妥的做法是让特征工程部分感知时间间隔比如把“距上一根K线的时间差”也作为一个特征喂给模型。3.2 构建经典RSI基准先用Wilder平滑打底在实现任何动态版本之前先把标准RSI(14)实现出来作为对照基准。这里建议用Wilder平滑方式也就是前面提到的递归平滑而不是简单移动平均。pandas里的ewm可以非常干净地实现这一点def rsi_wilder(close: pd.Series, window: int 14) - pd.Series: delta close.diff() up delta.clip(lower0) down -delta.clip(upper0) # Wilder平滑等价于 alpha1/window 的 EMA avg_up up.ewm(alpha1 / window, adjustFalse).mean() avg_down down.ewm(alpha1 / window, adjustFalse).mean() rs avg_up / avg_down return 100 - 100 / (1 rs)为什么用ewm一遍过很多人在算RSI时习惯先算简单移动平均当初值再手动迭代递推。其实ewm(alpha1/window, adjustFalse)在数学上和Wilder递归完全等价代码还短。这里唯一的差别是初始值处理pandas里第一种ewm用序列首个非空值作为起点这对长序列影响极小。拿到基准RSI之后你会发现后面任何动态版本的改进都最好拿这个基准做差异对比而不是只看绝对收益。3.3 特征构建让模型能看到“结构”而不是一根光秃秃的RSI如果直接把RSI序列丢给分类器信息维度太窄了。我建议至少构建以下几类特征收益率统计特征过去N根K线的均值、标准差、偏度、峰度。相对位置特征当前收盘价在过去N根K线区间中的百分位比如0.9就代表价格处于区间偏高位。波动率结构特征已经实现波动率比如ATR归一化、波动率的变化率。成交量特征成交量与过去20日成交量的比值用来辅助判断突破的有效性。多周期RSI特征RSI(5)、RSI(14)、RSI(21)的数值以及它们之间的差值。构建完特征后还需要做一次标准化处理。注意这里只能用训练集的数据来拟合标准化参数测试集或实时数据必须沿用训练时的参数不能用“看到未来”的方式重新标准化。3.4 状态标签构造没有公开标注就用统计规则先近似没有现成的“趋势/震荡”标注怎么办我的做法是用一套透明的统计规则生成伪标签。比如如果过去60根K线的线性回归斜率显著大于0且R²足够高则标记为“趋势态”。如果价格多次穿越均值、波动率稳定但方向收益不明显则标记为“震荡态”。如果波动率处于历史高位且方向不明标记为“高波动态”。这个过程写在代码里就是一组规则不需要很复杂它的作用只是给模型提供原始的监督信号。之后可以把真实标签和人工抽检结合起来修正。注意一点不要试图用“未来才知道”的信息去构造标签比如用未来一段时间的涨跌幅来判断当前是不是趋势起点这会引入未来函数。具体构造时我用过一个比较顺手的做法用滚动窗口均线偏离度加波动率聚类。先把收益率序列按20日滚动计算均值再按波动率大小分成高、中、低三档最后把“波动中低趋势明显”归为趋势态“波动中低趋势不明显”归为震荡态“波动高”归为高波动态。这是一种粗糙但有效的分桶方式。3.5 训练一个轻量分类器不用一上来就堆Transformer状态识别模型不见得非得是大模型。我在标准数据量下试过梯度提升树比如LightGBM在这个任务上表现已经足够好训练快、可解释性也强。神经网络更适合你后续想把整个流程端到端训练的场景。from sklearn.model_selection import train_test_split from sklearn.ensemble import GradientBoostingClassifier # feature_df: 3.3 构建的特征 # label_series: 3.4 构建的状态标签 X_train, X_test, y_train, y_test train_test_split( feature_df, label_series, test_size0.2, shuffleFalse ) model GradientBoostingClassifier( n_estimators200, max_depth4, learning_rate0.05, subsample0.8, random_state42, ) model.fit(X_train, y_train)注意这里我用了shuffleFalse。时间序列数据千万不要随机打乱否则会破坏时序依赖让模型“偷看”未来。训练集和测试集必须按时间前后切分。模型训练完之后你可以输出每个样本的状态概率。比如对最新窗口预测得到“震荡概率0.78、趋势概率0.15、高波动概率0.07”那就可以进入动态参数生成阶段。3.6 动态参数输出与信号合成把概率翻译成RSI阈值状态概率有了接下来就是把它们翻译成RSI参数。我给出一个可用的线性映射版本方便你直接跑通def dynamic_params(state_probs: dict, base_window: int 14) - dict: trend_p state_probs.get(trend, 0.0) oscill_p state_probs.get(oscillating, 0.0) # 震荡概率高 - 窗口缩短提高灵敏度 # 趋势概率高 - 窗口加长过滤噪声 window base_window int((trend_p - 0.5) * 8) # 范围约 10 ~ 18 # 趋势越强阈值越外扩减少假突破信号 upper 80 - 10 * trend_p lower 20 10 * trend_p return {window: window, upper: upper, lower: lower}这样写出来的映射比较直观你可以根据自己交易的品种和市场风格微调系数。比如数字货币这种波动大的我习惯把阈值外扩更多一些upper直接拉到88外汇直盘波动相对小upper设82附近就已经足够。信号合成时我会把动态RSI和原始RSI做对比只有两种情况下才触发信号从下向上穿越dynamic_lower且原RSI(14)也处于低位小于40判定为潜在买入。从上向下穿越dynamic_upper且原RSI(14)也处于高位大于60判定为潜在卖出。为什么要加这个“双重确认”因为我发现纯动态阈值在市场状态判断失误时容易产生悬崖式翻转加上基准RSI的过滤可以降低假信号率。4. 回测最容易翻车的地方未来函数、滑点与“看着很美”的收益曲线4.1 未来函数听起来低级错起来神不知鬼不觉回测里最打击信心的事不是策略亏钱而是策略在回测里稳定赚钱、实盘一跑就崩。大部分情况下它不是运气问题而是回测代码里埋了未来函数。最经典的一个坑是滑窗标准化。假设你要用过去60根的走势特征来预测状态概率如果你在整个数据集上直接算均值、标准差去做标准化那你已经把未来信息“泄露”给过去了。正确做法是在训练集上计算scaler参数然后应用到测试集或者对推理阶段使用扩展窗口且只用当前时刻之前的数据。这个错我踩过不止一次。尤其在使用sklearn的StandardScaler时很多人习惯先在全数据集上fit再做时序交叉验证。这样得到的回测结果往往好得吓人但一到实盘就失灵。请务必把这个检查写进你的回测流程里。另外还有一个隐含版本用全样本的波动率分位数来做状态标签也会引入未来信息。比如你用整个数据集的80%分位去切分“高波动态”那高波动阈值本身已经包含未来波动水平。我在3.4中已经提醒过构造标签时尽量用滚动窗口。4.2 手续费和滑点没有这两项回测收益曲线会骗人RSI类策略本质上是中高频反转/趋势跟踪策略交易次数不低。如果你把手续费和滑点设为零收益曲线一定会比你实盘结果好看一个数量级。我的经验值国内商品期货每手交易成本至少按万分之三到万分之五算股票加印花税后双边成本更高数字货币T0永续合约虽然名义手续费低但资金费率、滑点和强平影响不能忽略。滑点建议至少按每笔一跳来估算流动性差的时段可以按两跳到三跳。import vectorbt as vbt pf vbt.Portfolio.from_signals( close, entrieslong_signal, exitsexit_signal, init_cash100_000, fees0.0005, # 单边万五 slippage0.0005, # 单边万五滑点 freq15min, )上面是vectorbt的示例一行就能把手续费和滑点塞进去。回测时我还会额外打印“单笔最大亏损”“连续亏损次数”这类压力指标因为它们比总收益更能说明策略的韧性。4.3 分行情评估别用一年平均收益遮盖结构性问题很多时候一个策略在一年维度上收益不错但你把它按月拆开看会发现它只是在某两个月的趋势行情里赚了大钱其余月份都在反复磨损。RSI策略尤其如此因为它本质上是吃“震荡均值回归”和“趋势衰竭反转”这两碗饭不同市场环境下的表现差异很大。我的建议是把回测期按状态概率划分成趋势段、震荡段、高波动段分别统计收益、回撤、胜率。如果Dream-RSI真的起到了自适应效果你应该看到它在震荡段的胜率明显高于传统RSI同时趋势段的亏损不会因为参数飘移而失控。这块我实践的流程是回测完成后把所有交易信号标记上那一刻的状态概率再按概率区间分组统计盈亏。如果“高趋势概率下做逆势单”的亏损占比特别高就对那部分信号做额外过滤。5. 落地实践中的边界判断哪些品种适合哪些场景别用5.1 适合的品种和周期流动性就是生命线从我做过的测试看Dream-RSI这类自适应指标最适合两类场景一是流动性好、趋势与震荡切换频繁的品种比如股指期货、活跃外汇对、主流数字资产合约二是15分钟到4小时级别的K线周期因为太短的周期噪声太大模型来不及形成有效状态判断太长的周期信号样本太少统计上不可靠。以国内商品期货为例螺纹钢、甲醇这类成交活跃的品种表现通常比冷门品种好得多。原因很简单冷门品种的报价跳动本身就含有大量做市噪声状态识别的信噪比太低你再怎么自适应输入都不可靠输出自然难有起色。5.2 别把模型做重指标场景讲的是稳定不是炫技有一点我特别想泼冷水像Dream-RSI这种研究性项目落地的时候千万不要照搬完整的大型模型架构。先不说算力成本单说可维护性——指标本身就是你的决策辅助工具它应该稳定、可解释、快。如果为了一个RSI研究引入巨大的Transformer每天推理几百个品种那反而是过度工程化。我建议落地版本控制在“规则轻量模型”的组合用规则做状态初筛用轻量GBDT或极小MLP做状态概率输出用线性映射做参数生成。整套代码几百行就能写完部署在任何云函数或交易服务器上都没有压力。5.3 后续可以扩展的方向多品种共享状态、多周期共振如果你跑通了基础版本下一步可以做两件延伸。第一件是跨品种状态共享。比如做螺纹钢时可以同时参考铁矿石、热卷的状态特征因为黑色系板块内部联动强状态判断会更稳定。第二件是多周期共振。把15分钟和4小时两个级别的状态概率同时输入信号层只允许两层状态同向时才发信号能显著降低震荡期里的重复开仓。另外如果你有精力可以把状态概率做成一个公开的接口定期输出给下游的仓位管理系统使用。我从实践里的体会是Dream-RSI这类研究最有价值的不是那一个指标数值而是“让指标根据市场环境自我进化”的思路。这种思路一旦跑通不光RSI能用布林带、MACD、动量因子这些传统工具都可以照方抓药挨个做一遍自适应改造。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →