QuantBuddy · 宽宝
开始使用
量化方法论 · 教学复现

在噪声中寻找信号

市场每天抛出成千上万个数字,其中绝大多数是噪声。量化研究的全部手艺,是用一套可证伪、可复算的数理统计方法,从噪声里分离出一丝真实的异象(anomaly),再把它转化成可以下注的 alpha

本文以光大证券提出的 RSRS(阻力支撑相对强度,Resistance-Support Relative Strength)择时指标为标本,完整走一遍这条流水线:从一个关于「支撑与阻力」的朴素直觉出发,写成数学,交给 QuantBuddy 公式引擎实算二十一年的沪深300,再用《信号与噪声》(Nate Silver)的方法论逐项拷问——这究竟是信号,还是我们骗了自己?

标的 沪深300 区间 2005-04 → [[asof||'…']] 交易日 [[ndays||'…']] 实算引擎 QuantBuddy DSL 数据 实时取数 · 自动重算
实时取数失败:[[err]] (请稍后刷新;公式包凭证或网络可能临时不可用)
正在向 QuantBuddy 生产引擎实时取数并重算 …
⚠ 实时引擎此刻在你的网络下不可达([[snapMsg]]),下文分析已自动改用页面内嵌的数据快照(截至 [[asof]])渲染——内容与口径完全一致,只是不含快照之后的新交易日。待网络/防火墙恢复后刷新本页,即可切回实时取数。
原版 RSRS 择时 · 累计净值
[[fx(S.nav1,2)]]×
年化 [[fx(S.c1,1)]]% · 最大回撤 [[fx(S.dd1,1)]]%
优化版(+趋势确认)· 累计净值
[[fx(S.nav2,2)]]×
年化 [[fx(S.c2,1)]]% · 最大回撤 [[fx(S.dd2,1)]]%
买入持有沪深300 · 累计净值
[[fx(S.navb,2)]]×
年化 [[fx(S.cb,1)]]% · 最大回撤 [[fx(S.ddb,1)]]%
当前 RSRS 标准分信号
[[fx(S.curSig,2)]]
[[S.curSig>0.7?'≥0.7 → 偏多':(S.curSig<-0.7?'≤-0.7 → 偏空':'中性区 → 持仓不变')]]

上述数字由页面在你打开的此刻向生产引擎实时取回净值序列、在浏览器内重算得到;底层行情更新后会自动反映。下文逐段拆解它们从何而来、可信到什么程度。

1异象:把「支撑与阻力」翻译成一个数字

技术分析里最古老的两个词是支撑阻力:价格跌到某处总有人接、涨到某处总有人抛。它们通常只是图上随手画的线,主观、不可复算。RSRS 的聪明之处,是把这对概念压缩成一个可计算的数

做法极简:取最近 N=18 个交易日,每天有一个最高价(当日阻力被触及的高度)和最低价(当日支撑被触及的低度)。把这 18 组「最低价 → 最高价」点画在坐标系里,做一条最小二乘回归直线,它的斜率 β 就是当下「阻力相对支撑的强度」。

  • β 大(陡):支撑略抬,阻力就大幅抬升——买盘强势、上行弹性足;
  • β 小(平)甚至下行:支撑在动而阻力顶不上去——上方抛压沉重、动能枯竭。

把 β 读成一句话会更顺:「支撑每抬高一点,阻力会跟着抬高多少?」——它度量的是阻力对支撑的响应弹性。想象每根日 K 线都是一段从最低价(地板)到最高价(天花板)的竖条;18 天看下来,地板与天花板各自上下浮动。回归就在问:当买盘把地板一寸寸顶高时,天花板是顺势让开(β 大、阻力松动),还是被卖盘死死压住(β 小、抛压顽固)。

β 大 · 阻力松动(强势)
支撑抬高 1 元,阻力跟涨约 1.4 元——天花板比地板涨得还快
阻力 最高价 ↑ 支撑 最低价 → 支撑 +1 阻力 +1.4 β ≈ 1.4 → 偏多
β 小 · 抛压封顶(弱势)
支撑抬高 1 元,阻力只抬约 0.6 元——每次反弹都被卖盘按住
阻力 最高价 ↑ 支撑 最低价 → 支撑 +1 阻力 +0.6 β ≈ 0.6 → 偏空
同一种「最低价 → 最高价」散点,斜率却天差地别。β 把「天花板让不让路」这件事压缩成一个无量纲、可比较、可下注的数——这就是把支撑阻力这句技术分析黑话,翻译成统计量的全部秘密。

下图是活的:拖动滑块选任意一天,左侧实时对该天往前 18 日的最高/最低价做回归,右图显示这条 β 曲线在历史中的位置。这里画出的 β 与后文策略所用的,是同一个引擎算的同一个数(已逐点核对一致)。

交互 · 01 18 日 阻力—支撑 回归,斜率即 β
拖动滑块沿时间轴移动回归窗口;或点「播放」让它自己走。
[[fmtD(reg.date)]]
读图:左图每个点是某一天的(最低价, 最高价),斜直线是 18 日 OLS 拟合;标题给出当日 β 与拟合优度 R²。R² 越高,这条「支撑—阻力」关系越紧密、信号越可信——这正是后文「修正标准分」要利用的东西。
为什么是高—低回归,而非别的用「最高价对最低价」回归,刻意避开了收盘价的噪声与隔夜跳空;斜率天然无量纲、可跨时间比较。这是把一个语义(支撑阻力强弱)化为统计量的典型手法——异象研究的第一步,永远是先找到一个能被算出来、且有经济学故事支撑的数。

2数学:从斜率到一个可下注的信号

原始 β 有两个毛病:它有量纲、随行情中枢漂移,无法直接定阈值。RSRS 用四步把它驯化成一个均值约 0、可比较的信号。

第一步 · 斜率 β(OLS)

对窗口内 $\{(L_i,H_i)\}_{i=1}^{N}$($L$ 最低价、$H$ 最高价),最小二乘斜率:

$$\beta_t=\frac{\sum_{i}(L_i-\bar L)(H_i-\bar H)}{\sum_{i}(L_i-\bar L)^2},\qquad R^2_t=\big[\mathrm{corr}(L,H)\big]^2$$
第一层 · 先看懂 β 和 R² 在量什么高中的回归直线就够用

直觉 1β 就是那条「回归直线」的斜率

先别碰任何高深的东西。把这 18 天里、每一天的(最低价,最高价)当成一个点,描在坐标纸上:横轴是当天最低价 L、纵轴是当天最高价 H。你会看到一串大致向右上方走的点。RSRS 第一步做的,就是你高中学过的那件事——拿尺子穿过这堆点,找一条最贴合的直线 H ≈ α + βL(课本叫「回归直线」「最小二乘」)。

这条线的斜率 β 回答一个特别朴素的问题:「今天最低价要是比平时高 1 块,最高价大约会跟着高几块?」β 越陡(大),说明低点稍微一抬、高点就冲很远 → 买盘强、上方阻力松动;β 越平(小),说明低点抬了、高点却纹丝不动 → 上方抛压沉重。一个斜率,就把「多空力量」量化成了一个数。

当天最低价 L → 当天最高价 H ↑ 回归直线 · 斜率 = β 最低价 +Δ 高价跟涨 ≈ β·Δ
把 18 天的(最低价, 最高价)描成散点,穿过去的那条直线就是回归直线;它的斜率 β 就是「最低价每抬高 1 块,最高价平均跟涨几块」。这正是高中统计里的最小二乘直线。

直觉 2斜率公式 = 「一起动的劲头 ÷ 自己晃的幅度」

课本里直线斜率是 Δy/Δx,可点云不是一条完美直线,怎么办?办法是对一堆点取平均。先在图上画一个十字:竖线穿过最低价的平均 L̄、横线穿过最高价的平均 H̄,把图切成四块。看每个点落在哪:

  • 落在右上 / 左下(都高 或 都低)→ L 与 H 同向偏离,记一笔 正账
  • 落在左上 / 右下(一个高一个低)→ 反向,记一笔负账

把所有点的账加总,就是公式分子 Σ(Lᵢ−L̄)(Hᵢ−H̄)——衡量 L、H 一起涨跌的劲头(统计里叫协方差)。分母 Σ(Lᵢ−L̄)² 则是 L 自己晃的幅度方差)。一除,就得到「L 每变 1 单位、H 平均变多少」= β。所以这条吓人的公式,本质就一句话:β = 一起动的劲头 ÷ 自己晃的幅度

L̄(最低价均值) + 同高 + 同低 - 反向 - 反向
蓝点几乎都落在「同高同低」的绿区(记正账),只有少数橙点落在红区(记负账)。绿区压倒红区 → 协方差为正 → β 为正。β 的公式,就是这场记账的总和除以「L 自己的晃动」。

直觉 3R² 就是「点贴不贴这条线」

光有斜率 β 还不够,得问一句:这条线靠不靠谱?同样一个 β,点紧贴着线,和点散成一团云,可信度天差地别。R²(取值 0~1)量的就是这个贴合度——其实它正是你高中学过的相关系数 r 的平方

  • R² → 1:点几乎全压在线上,「低→高」关系铁瓷,这个 β 可信
  • R² → 0:点乱成一团云,看不出什么关系,这个 β 基本是蒙的

这也解释了 RSRS 第三步为什么要乘 R²:给那些「蒙出来的 β」自动打个折,让看不清的窗口少说话。

点贴线 → R²≈0.9 可信 点散成云 → R²≈0.2 蒙的
两边斜率 β 一样,但左边点紧贴线(关系铁)、右边散成一团云(基本没关系)。R² 就量这个贴合度:1=全在线上,0=纯噪声。
第二层 · 再看深 用线性代数的眼睛重看一遍选读 · 为做量化打底

进阶 1先把一串数字「打包成一个向量」

上面三步用高中知识已经够用了。但要真正做量化、读懂论文,值得再换一副「线性代数的眼睛」看一遍——你会发现 β 和 R² 其实是同一张几何图上的两个量。第一步是换个角度看数据:把这 18 天的最高价按顺序排成一列,打包成一个向量 h=(H₁, H₂, …, H₁₈)。别被「18 维」吓到——二维向量是平面上一支箭头,三维是空间里一支箭头,18 维只是同一件事、分量多几个而已,加法、长度、夹角的规则全照搬。同理把 18 个最低价打包成 ,再把一列全是 1 的清单打包成 1(负责式子里的「截距」那一项)。

于是「用最低价去解释最高价」就变成一道向量问题:能不能用 ℓ 和 1 这两支箭头,拼出最接近 h 的那支箭头?

h(18 个最高价) H₁H₂H₁₇H₁₈ 打包 2维:平面上一支箭头 3维:空间里一支箭头 18维:同一件事,只是 18 个分量 「加法 / 长度 / 夹角」规则全照搬
把一串有序的数当成一支「高维箭头」——这一步什么都没改变,只是换个角度,让下面的「投影」「夹角」能登场。

进阶 2拟合 = 在「够得到的平面」里找最近的影子

把所有可能的组合 a·1 + β·ℓ 收集起来,它们铺成一张平面(数学上叫 1 与 ℓ 张成的子空间)——这就是「只靠 ℓ 和 1,我们能拼出的全部箭头」的范围。真实的 h 通常不在这张平面上(数据带噪声,最高价不可能被最低价完美解释)。那么「最好的拟合」是谁?就是平面上离 h 最近的那一点 ĥ,也就是 h 投在平面上的影子(正交投影)。从 h 到它影子的那截差距 e = h − ĥ,就是残差

平面:所有 a·1 + β·ℓ 能拼出的箭头 O h 真实最高价向量 e 残差(最短) ĥ 影子=最佳拟合
核心图。蓝箭头 h 是真实数据;绿箭头 ĥ 是它落在平面上的影子(最佳拟合);金色虚线 e 是两者之差,垂直于平面、长度最短。最小二乘做的,就是找这个影子。

进阶 3为什么「最近」一定意味着「垂直」

正午把光从头顶打下来,你的影子落在脚边,你和影子的连线垂直于地面——这不是巧合,而是「最近」的必然结果。几何事实是:点到平面的最短距离,就是那条垂线段。任何别的拟合点 h′,它到 h 的连线都是一个直角三角形的斜边(一条直角边是垂线 e,另一条躺在平面里),而斜边永远比直角边长。所以「残差最短」⇔「残差垂直于平面」——这就是 Least Squares(最小二乘)里那个「Least」的几何含义。

拟合平面(侧视) h ĥ 最短残差(垂线) h′ 换个拟合点 → 斜边,更长
侧视图:垂线(金)比任何斜线(红)都短。所以最佳影子 ĥ 一定落在 h 正下方、残差垂直于平面。

进阶 4把「垂直」解出来,斜率就是 Cov / Var

「残差垂直于平面」翻成式子,就是 e 同时垂直于 ℓ 和垂直于 1(与它们的内积都为 0)。其中「垂直于 1」恰好等价于残差的平均值为 0;利用这一条,可以先把 h、ℓ 各自减去自己的均值(中心化)得到 $\tilde h,\tilde\ell$,把碍事的截距 a 一笔消掉。剩下「$e\perp\tilde\ell$」这一个条件解出来,就是那条课本公式:

$$\beta=\frac{\langle\tilde h,\tilde\ell\rangle}{\langle\tilde\ell,\tilde\ell\rangle}=\frac{\operatorname{Cov}(H,L)}{\operatorname{Var}(L)}$$

它一点都不神秘:分子 $\langle\tilde h,\tilde\ell\rangle=\sum_i\tilde H_i\tilde L_i$ 正是协方差(最高价与最低价「一起波动」的程度),分母 $\langle\tilde\ell,\tilde\ell\rangle=\sum_i\tilde L_i^2$ 正是方差(最低价自己波动的程度)。所以「协方差 ÷ 方差」这句口诀,背后就是上面那张投影图的代数答案——β 衡量的是:最低价每动一个单位,最高价平均跟动几个单位

进阶 5R² 就是两支箭头夹角的「余弦平方」

同一张图还能直接读出拟合优度 R²。中心化后的 $\tilde h$ 和 $\tilde\ell$ 是两支从原点出发的箭头,它们之间有一个夹角 θ。把 $\tilde h$ 投影到 $\tilde\ell$ 方向上,投影长度占 $\tilde h$ 全长的比例,正好是 cos θ。再平方,就是大名鼎鼎的:

$$R^2=\cos^2\theta=\frac{\langle\tilde h,\tilde\ell\rangle^2}{\lVert\tilde h\rVert^2\,\lVert\tilde\ell\rVert^2}$$
ℓ̃ θ 投影长度 = |h̃|·cos θ(沿 ℓ̃ 方向的影子)
cos θ = 影子长度 ÷ 箭头全长。θ 越小,两支箭头越同向,cos θ 越接近 1。
  • θ 很小(几乎同向):cos θ → 1,R² → 1。最低价一动,最高价几乎同步跟动,「支撑—阻力」关系紧、这条 β 可信
  • θ ≈ 90°(几乎正交):cos θ → 0,R² → 0。最高价对最低价基本没有线性响应,这条 β 是噪声拼出来的、不可信。

这下就懂了第三步为什么要乘 R²:R² 是这条「支撑—阻力」关系方向有多一致的可信度分;用它给 β 打折,等于让「看不清的窗口」自动少说话。

想把「投影即拟合」这套直觉吃到骨子里,强烈推荐周志成的《線代啟示錄》——〈从线性变换解释最小平方近似〉与〈正交投影:威力强大的线代工具〉两篇,把上面每一步的几何来龙去脉讲得极透。ccjou.wordpress.com ↗
随堂自测 · 点选项即判分
[[ 'Q'+(qi+1)+'. '+qz.q ]]
[[ picked('lsq',qi)===qz.a?'✓ 答对了。':'✗ 再想想。' ]] [[ qz.why ]]

第二步 · 标准分(滚动 z 标准化)

用过去 $M=600$ 天的 β 均值与标准差,把当前 β 转成 z 分数,消除量纲与漂移:

$$z_t=\frac{\beta_t-\mu_{t}(\beta,M)}{\sigma_{t}(\beta,M)},\qquad M=600$$

直觉 1β 的「零刻度」会偷偷移动,没法直接画线

想象你用一把尺子量身高,但这把尺子的零刻度每年都悄悄往上挪一点。那「175」今年和十年前就不是一回事了。原始 β 正有这个毛病:它带着价格的量纲,而且「常态水平」会随行情中枢慢慢漂移。如果直接拿一个固定的 0.7 当买卖线,就等于用一把会移动的尺子去量东西——同一个 β=1.2,在这个年代可能很高、在那个年代却很平常。

① 原始 β:常态基线在漂移(虚线) 基线一路抬高 → 阈值没法固定 ② 标准分 z:钉在 0 附近、刻度统一 +0.7 −0.7 0
同一段 β,标准化前基线一路漂移(上),标准化后被「钉」回 0 附近、幅度也归一(下)——这才敢用一条固定的 ±0.7 去切。

直觉 2两步校准尺子:减均值、除标准差

公式 z=(β − μ)/σ 看着抽象,其实就是两个动作:

  • 减去最近 600 天的均值 μ:把「当前的常态」对齐到 0,漂移被自动抵消(μ 自己也在逐日滚动更新,永远代表「最近一年多的常态」)。
  • 除以最近 600 天的标准差 σ:把波动幅度换算成「几个标准差」。从此不同年代、不同波动环境,全部换成同一把无量纲的尺子。

于是 z 有了一个非常好读的含义:「现在的 β,比最近 600 天的常态高(或低)了几个标准差」。z=+2 就是「比常态高 2 个标准差,相当罕见的强」;z=0 就是「正处在常态当中」。

μ(z=0 常态) z < −0.7 强空 z > +0.7 强多 常态区
把 β 摊在「标准差」这把尺子上:中间是常态,越往两侧越罕见。±0.7 就是落在两侧、值得动作的偏离。

直觉 3为什么偏偏是 0.7

在标准化后的刻度上,0.7 大约就是「比常态偏离 0.7 个标准差」。它不大不小,是一个工程折中:阈值太小(比如 0.2),信号会频繁触发、被日常噪声反复打脸;太大(比如 2),信号又太稀、白白错过大段行情。0.7 是原研报在历史数据上权衡「灵敏 vs 稳健」挑的折中点。

但请记住:0.7 是一个可以被质疑、被回测检验的选择,不是天降神谕。到了第 7、8 节,我们就会把阈值和窗口长度铺成一张网格,亲眼看看这个选择到底有多稳、换个数会怎样——这正是《信号与噪声》教我们的态度:对每一个参数都保持怀疑,让数据说话。

阈值越大 →(越严格) ← 越小(越灵敏) 太小(0.2) 信号乱跳、被噪声 反复打脸、来回交易 ≈0.7 折中 灵敏与稳健的 平衡点 太大(2.0) 信号太稀、迟钝, 白白错过大段行情 谷底≈最划算
把阈值从小往大调:太小被噪声打脸、太大错过行情,「代价」曲线呈 U 形。0.7 落在谷底附近——一个被历史数据选出、且应当被回测检验的折中,不是魔法数字。
随堂自测 · 点选项即判分
[[ 'Q'+(qi+1)+'. '+qz.q ]]
[[ picked('zsc',qi)===qz.a?'✓ 答对了。':'✗ 再想想。' ]] [[ qz.why ]]

这就是 RSRS 标准分。它在 0 附近波动,大于 +0.7 视为强多、小于 −0.7 视为强空——阈值 0.7 是原研报的设定。

第三步 · 修正标准分(用 R² 加权)

低 R² 的窗口意味着「支撑—阻力」关系松散、这个 β 不可信。乘上 R² 给低质量信号自动降权

$$z^{\mathrm{rev}}_t = z_t \cdot R^2_t$$

第四步 · 右偏标准分(再乘 β)

再乘原始 β,让指标在趋势方向上更「敢于」表态(右偏):

$$z^{\mathrm{right}}_t = z^{\mathrm{rev}}_t \cdot \beta_t$$

直觉 1退一步:从脏信号里把「异常」滤出来

把镜头拉远,前面四步连起来其实就是工程师天天在做的事:从一段又脏又吵的信号里,把真正有用的那一点点「异常」提炼出来。原始价格是混着趋势、噪声、隔夜跳空的脏信号;β 像一个特征提取器,把每 18 天的形态压成一个标量;后面几步则一站一站地把它「洗干净、调好刻度、按可信度加权、最后设闸」。

脏信号 · 价格(高 / 低 / 收) 混着趋势、噪声与隔夜跳空 β · 特征提取 18 天高~低回归,把一段形态压成一个斜率标量 z · 去直流 + 白化 减 μ(高通,去慢漂移)÷ σ(归一),留下「偏离常态」 ×R² · 信噪比加权 估计越干净(R² 越大)权重越大,纯噪声自动压低 ±0.7 · 门限检测 + 迟滞 穿过才动作、闸内维持现状 → 输出仓位 0 / 1
同一段价格,依次经过「特征提取 → 去直流白化 → 信噪比加权 → 门限检测」四级处理,最后落成一个干脆的多/空仓位。

直觉 2逐站对照:每一步对应一个经典器件

  • z 标准化=去直流(高通)+ 白化:减掉缓慢漂移的均值 μ,相当于一道高通滤波,滤去随行情中枢的慢漂移、只留真正有信息的偏离;除以 σ 是一维白化(whitening),让不同年代、不同波动环境的幅度可比——这才敢用一条固定 ±0.7 去切。
  • ×R²=信噪比加权:R²=cos²θ 衡量这次回归有多干净,乘上它就是按信噪比加权,可信的偏离被放大、噪声被抑制。
  • ×β=方向增益:在趋势方向上放大响应,让指标更「敢表态」(右偏)。
  • ±0.7=带迟滞的门限检测器:在白化后的信号上设一道闸,穿过才动作、闸内保持,避免在阈值附近来回抖动打脸。

直觉 3这正是《信号与噪声》的方法论

一句话——RSRS 做的,正是「用一个科学的、数理统计的方法,来捕捉市场的某种『异常』,然后转化成 alpha」。这也是本页(与一切量化策略)贯穿始终的方法论:先有可证伪的假设,再用数据审判它。Nate Silver 在《信号与噪声》里反复强调的,正是把信号从噪声里分离出来,并对每一个判断保持谦逊与怀疑。

标准化/白化、内积与夹角这些信号与线代的共同语言,《線代啟示錄》(周志成)有极清楚的几何讲法;配合 Nate Silver《信号与噪声》一起读,能同时长出「数学的手」与「怀疑的脑」。ccjou.wordpress.com ↗
随堂自测 · 点选项即判分
[[ 'Q'+(qi+1)+'. '+qz.q ]]
[[ picked('sig',qi)===qz.a?'✓ 答对了。':'✗ 再想想。' ]] [[ qz.why ]]

下图把三条信号叠在一起。注意它们的形状差异:标准分最「跳」,修正分在震荡市被 R² 压扁,右偏分在大趋势里被放大。哪一个更好?——先别急着回答,这正是第 7 节要用数据审判的问题。

交互 · 02 RSRS 信号阶梯
切换查看三种信号,虚线是 ±0.7 阈值。
区间可在图上框选缩放
读图:信号穿越 +0.7(绿虚线)触发满仓、跌破 −0.7(红虚线)触发空仓;位于阈值之间则维持现状(迟滞,减少来回打脸)。纵轴固定在 ±4:极端行情下信号偶尔会冲到 ±5~6(如 2019 年 7 月一度达 −6.2),这类离群点会被裁到边缘,以免日常区间被压扁看不清。

3QuantBuddy 公式引擎:把数学写成会自己重算的代码

上面的公式若用 Python+pandas 手写,要操心数据对齐、滚动窗口、缺失值、复权、回测状态机……几百行,且每次数据更新都得重跑。QuantBuddy 的思路不同:你声明式地写公式,引擎负责取数、对齐、滚动与重算。整套 RSRS 策略就是下面这二十几行——没有循环、没有 for,每一行是一个具名序列。

# —— 原始量:沪深300 的日内高/低/收 ——
HI = 最高价(沪深300)
LO = 最低价(沪深300)
PRICE = 收盘价(沪深300)
# —— 斜率 β、拟合优度 R² ——
BETA  = 回归系数("HI","LO",18)
R2    = 次方(相关系数("HI","LO",18),2)
# —— 标准分 → 修正 → 右偏 ——
RSRS_STD     = z分数("BETA",600)
RSRS_REVISED = "RSRS_STD" * "R2"
RSRS_RIGHT   = "RSRS_REVISED" * "BETA"
# —— 择时:>0.7 满仓 / <-0.7 空仓,进出场区间是个持仓状态机 ——
POS      = 进出场区间(("RSRS_STD" > 0.7), ("RSRS_STD" < -0.7))
DAILY_RET= 缺失填零(涨跌幅("PRICE",1))
STRAT_NAV= 累乘(1 + 昨天("POS") * "DAILY_RET")   # 昨天持仓×今日收益→净值
BENCH_NAV= 累乘(1 + "DAILY_RET")
# —— 优化版:叠加 250 日趋势确认(见 §8)——
MA250   = 平均("BENCH_NAV",250)
POS_OPT = "POS" * ("BENCH_NAV" > "MA250")
OPT_NAV = 累乘(1 + 昨天("POS_OPT") * "DAILY_RET")
      

关键函数各司其职:回归系数 / 相关系数 是滚动 OLS 与相关,z分数 是滚动标准化,进出场区间 是「入场置 1、出场置 0、其余保持」的持仓状态机,累乘 把日收益复利成净值,昨天 实现 T+1 防未来函数。

声明式 = 可信注册成「公式包」后,得到一对 package_id + signature 凭证;本页就是凭它在你眼前实时取数。底层行情每更新一天,引擎自动增量重算,无需改一行代码——这让「复现」不再是一次性的截图,而是一个始终鲜活、可被任何人重新核验的对象。

4从信号到策略:一条信号怎样变成净值

先把两件最容易含糊的事说死:交易什么、怎么交易。

  • 标的=沪深300 指数本身。复现时直接采用指数的日收益;现实里它对应「买一只跟踪沪深300 的 ETF / 指数基金」。本策略只做择时、不选股——买卖的永远是这一篮子,信号也只来自指数自己的日内最高价 / 最低价。
  • 方向只有「持有」或「空仓」两种(不做空、不加杠杆)。所谓持仓,就是「这一篮子,今天拿着还是不拿」。

先说总账:策略收益 = 「持仓」与「收益」的内积

回测看似是一台复杂机器,可它的总账能用第 2 节那把尺子——内积——一句话写完。把每个交易日的指数涨跌幅 $r_t$ 排成一支收益向量 $\mathbf r$,把每天的仓位(昨天定下、今天生效)排成一支只含 0/1 的持仓向量 $\mathbf w$,那么整条策略的累计(对数)收益,就是这两支向量的内积:

$$\mathbf r=(r_1,\dots,r_T),\quad w_t=\mathrm{POS}_{t-1}\in\{0,1\};\qquad \ln\mathrm{NAV}_T=\sum_{t}\ln(1+w_t r_t)\;\approx\;\langle\,\mathbf w,\,\mathbf r\,\rangle=\sum_{t} w_t\,r_t$$

因为 $\mathbf w$ 每个分量非 0 即 1,这里的内积退化成最朴素的一件事——挑日子求和:$\mathbf w$ 像一张 0/1 掩码,把收益向量里持有的那些天留下相加,空仓日乘 0、自动丢弃。买入持有则是掩码全 1、一天不落。两者一减,「择时」就被单独逼了出来:

$$R_{\text{买入持有}}=\langle\,\mathbf 1,\,\mathbf r\,\rangle=\sum_t r_t,\qquad \underbrace{\langle\mathbf w,\mathbf r\rangle-\langle\mathbf 1,\mathbf r\rangle}_{\text{择时超额}}=-\,\langle\,\mathbf 1-\mathbf w,\,\mathbf r\,\rangle=-\!\!\!\sum_{t:\,w_t=0}\!\!\! r_t$$

这条等式把择时的全部价值压成一句话:超额 = 减去「空仓那些天的收益之和」。所以一个择时策略能不能赚钱,取决于一件事——它躲开的日子,整体是不是在跌:躲对下跌日,超额为正;误躲上涨日,超额为负。下面这张小图,就是这支内积的「逐项明细」。

每天:指数收益 r × 持仓掩码 w(深=留下 / 浅=丢弃) r w 1 1 1 0 0 1 1 1 1 0 1 1 ⟨ w , r ⟩ = 深色条之代数和 = 策略相对买入持有, 唯一多做的「挑日子」
深色条($w=1$)当天收益被留下计入策略;浅色条($w=0$)被乘 0、丢弃。内积 $\langle\mathbf w,\mathbf r\rangle$ 就是这些深色条的代数和。本例特意空仓了三根最深的红条(下跌日),于是「被丢弃项之和」为负,按上面的等式,超额便为正——这正是 RSRS 在真实历史里赚到钱的全部秘密:躲对了大跌

再拆机械步骤:每天其实只重复四步

「总账」是一支内积;落到每天的算法,不过是同一件事的四个小步骤:

1
RSRS 标准分 → 持仓指令
每天看连续信号:穿上 +0.7 满仓(w=1)、跌破 −0.7 空仓(w=0)、落在 ±0.7 之间维持昨天(迟滞,不来回折腾)。代码即 进出场区间(…),把连续信号翻译成一串 0/1 掩码 $\mathbf w$。
2
用「昨天」的仓位(T+1 防未来)
今天能动用的,是昨天收盘才算得出的仓位 $w_t=\mathrm{POS}_{t-1}$。信号要等收盘才成形,不能拿今天的信号去赚今天的钱——这正是 昨天("POS") 的职责。
3
当日盈亏 = 仓位 × 当日涨跌
单日贡献 $w_t\,r_t$:持有日如实承接指数涨跌,空仓日乘 0、贡献为零,自动躲过当天行情。把这些日贡献沿时间加总,正是上面那支内积 $\langle\mathbf w,\mathbf r\rangle$。
4
复利累乘 → 净值曲线
把每天 $(1+w_t r_t)$ 连乘(即 累乘(1 + 昨天(POS)·收益))长成净值曲线;对照组「买入持有」永远满仓(掩码全 1),用来量出择时究竟有没有加分(见 §5)。

下图三条线:原版 RSRS优化版买入持有。底部阴影标出策略实际持仓的时段——空白处即空仓避险。

交互 · 03 策略净值(默认线性轴;切「对数轴」更宜看长期复利)
读图:默认线性轴直观看绝对增长;切到对数轴时,等斜率=等复合增速。原版与优化版长期增速高于买入持有,且在 2008、2015 两次熊市里走平(空仓)而非俯冲。

把上面的内积摊开看,更清楚:下图把每天的指数对数收益 $\ln(1+r_t)$ 按当天是否持仓,分别累加成两条线——持有日累计(计入策略的项,即 $\langle\mathbf w,\mathbf r\rangle$,因仓位无杠杆,它恰等于 $\ln$ 策略净值)与 空仓日累计(被掩码丢弃的项,即 $\langle\mathbf 1-\mathbf w,\mathbf r\rangle$)。二者之和恰是买入持有;而 超额 = −(空仓日累计)——空仓日那条线压得越深,说明躲开的越是下跌,超额越厚。

交互 · 04 内积分解:超额究竟来自哪些天(累计对数收益)
读图:持有日累计一路上行,终值 = $\ln$ 策略净值,是策略真正吃到的部分;空仓日累计长期向下、终值约 [[fx(S.ipCash,2)]](对数收益)——这正是被躲开的下跌,取相反数即择时超额 ≈ +[[fx(-S.ipCash,2)]]。换算成倍数:你躲过的那些日子合计把指数乘了约 [[fx(S.ipCashX,2)]]×(<1 即净在跌)。两条相加恰为买入持有;它们的差,就是 §5 要单独放大的 alpha。

体检:那些「净值突变」是 bug 还是真事?

肉眼扫净值曲线,会看到一些天净值陡跳(比如 2024 年 9~10 月那一段)。量化里这往往是数据脏点的警报,必须查。我把开关打开、逐日核对了日间净值变动:全样本单日波动 ≥6% 的共 20 天(图中已用绿/圆点标出,悬停可看日期与幅度),每一天都能对应一段真实历史——股灾、涨停式反弹、政策驱动的暴涨暴跌——没有一个是脏数据。下表是其中最具代表性的几天:

日期当日指数涨跌策略当时持仓?真实事件
2005-06-08+8.2%满仓股改启动+「千点」政策底,报复性反弹
2007-02-27−9.2%满仓「2·27」全球股灾,沪深300 单日重挫
2008-09-19+9.3%满仓印花税单边征收+汇金增持,全线涨停式反弹
2015-01-19−7.7%满仓券商两融检查,蓝筹集体跳水
2024-09-30+8.5%满仓「9·24」一揽子货币+稳增长政策,节前最后一日暴涨
2024-10-09−7.0%满仓节后预期落差(发布会不及预期),高位回吐

其余十余天集中在 2008 金融海啸前后那串「涨跌停式」交易日。结论:它们不是脏数据,而是一段段真实历史。指数那几天本身就是这个幅度,策略恰好持仓,于是如实承接(含 2024 年 9 月的政策驱动暴涨——RSRS 当时早已满仓,完整吃到)。这条曲线通过了体检:它没有偷偷平滑掉风险,也没有虚构收益。能区分「真实的极端」与「错误的极端」,是回测可信度的底线。

方法论提醒若曾看到「滚动夏普是一条水平直线」之类的图,那才是真 bug——某些库的 夏普 函数会返回全样本单值再广播到每一天。本页的滚动夏普(见 §6)是在浏览器内按真窗口逐日重算的,会随市况起伏。先怀疑工具,再怀疑市场。

5超额收益:把择时单独拎出来看

净值好看,可能只是因为指数本身涨了。要看择时本身有没有创造价值,得看相对净值(策略净值 ÷ 基准净值):曲线向上,就是这段时间跑赢了基准;走平或向下,则是在白费力气甚至拖后腿。

交互 · 05 相对基准的超额净值(>1 即跑赢)
读图:超额几乎全部来自 20082015 两次空仓避险——台阶式跃升。其余漫长岁月里超额曲线接近走平:这个策略的本事是「躲大跌」,不是「天天比指数聪明」。认清 alpha 的来源,比知道它有多大更重要。

6风险:回撤与滚动夏普

真正稳健的择时,价值往往不在更高的收益,而在更浅的回撤。下图是水下回撤曲线(离前高的距离)。买入持有在 2008 一度回撤逾 −70%;两个策略把最深伤口收窄到约 −50%/−30%。

图 · 06 水下回撤曲线
读图:越接近 0 越好。优化版(绿)几乎从不深潜,代价是牛市偶尔踏空——风险与收益的老实交换。

再看滚动一年夏普比率(过去 244 个交易日的风险调整后收益,逐日重算并年化)。这条线会随市况上下起伏——若它是一条水平直线,那一定是算错了。

图 · 07 滚动 1 年 夏普比率(已修正为真·滚动窗口)
读图:三条线都在 −2 至 +3.5 间起伏,牛市高、熊市负。全样本夏普:原版≈[[fx(S.sh1,2)]]、优化版≈[[fx(S.sh2,2)]]、买入持有≈[[fx(S.shb,2)]]。

7信号,还是噪声?——一次过拟合审判

「最危险的不是我们不知道的事,而是我们自以为知道、其实并不成立的事。」— Nate Silver,《信号与噪声》(释义)

Silver 反复强调:一个模型在历史上拟合得越完美,越要警惕它只是记住了噪声。回测里最常见的自欺,是把参数(窗口 M、阈值 S、信号变体)调到历史最优,然后惊叹于那条完美的净值。

所以我做了两件事来拷问 RSRS。其一,把参数铺成一张网格全部跑一遍,看好成绩是一整片高原还是一根孤立的尖峰。下面这张三维曲面,是「标准分」策略在不同 M(标准化窗口)× S(阈值)下的全样本夏普——你可以旋转它。

交互 · 08 参数稳健性曲面:夏普 = f(窗口 M, 阈值 S)
3D:拖动旋转 · 滚轮缩放 ⚠ 当前浏览器未启用 WebGL,已自动用 2D 热图(数据完全相同,仅少了立体视角)
读图:曲面平缓、连成一片高原——在很宽的参数范围内夏普都稳定在 0.5–0.7,没有「只有某一组参数才灵」的孤峰。这是稳健的标志:说明 β 标准化择时是个真实存在的弱信号,而非拟合出来的幻象。

其二,更严格的检验——样本外。把历史切成两半:用 2005–2015 当「样本内」(你能看到的过去),2016 至今 当「样本外」(假装的未来)。如果一组参数只是过拟合,它的样本内夏普会很漂亮、样本外却会坍塌。下图每个点是一组参数:横轴样本内夏普、纵轴样本外夏普。

交互 · 09 样本内 vs 样本外:过拟合无所遁形
读图:点若落在对角线附近,说明样本内外一致、可信;越靠右下,越是「样本内华丽、样本外打回原形」的过拟合陷阱。全网格样本外夏普普遍从约 0.8 回落到 0.2–0.4——这才是这个信号在「未来」里的真实成色。买入持有的样本外夏普仅 [[robust.bench.test_sharpe]]。

诚实的结论:开头那串「八九倍 vs 五倍」大部分是样本内的运气(恰好躲过 2008、2015)。样本外,RSRS 仍能跑赢买入持有、并显著降低回撤,但 alpha 是温和而非惊艳的。承认这一点,正是把研究和营销区分开的地方。

8稳健地优化:一个先验的、而非挖来的改进

既然要优化,就得避免掉进第 7 节的陷阱。规矩有三:(1) 不动 RSRS 本身的参数(M=600、阈值 0.7 照旧);(2) 新增的部件必须有独立的经济学逻辑,而不是为了好看挑出来的;(3) 参数取行业公认的先验值,绝不在网格里挑极值。

改进只有一处:趋势确认。RSRS 擅长抓拐点,但在结构性熊市里它也会频繁给出「抄底」信号——结果接到下落的飞刀。于是加一道闸:只有当 RSRS 看多、指数本身站上其 250 日(约一年)均线时,才允许持仓。两个相互独立的确认同时点头,才下注。250 日是「一年趋势」的标准取值,不是调出来的。

POS_OPT = "POS"  ×  ( 指数净值 > 平均(指数净值, 250) )   # RSRS看多 且 站上年线

效果(全样本,实时计算):夏普 [[fx(S.sh1,2)]] → [[fx(S.sh2,2)]],最大回撤 [[fx(S.dd1,1)]]% → [[fx(S.dd2,1)]]%,净值 [[fx(S.nav1,2)]]× → [[fx(S.nav2,2)]]×,而持仓时间反而从 [[fx(S.hold1,0)]]% 降到 [[fx(S.hold2,0)]]%——用更少的暴露拿到更稳的收益。下面这张「参数阶梯」是关键的反过拟合证据(同样在浏览器内实算):

图 · 10 趋势确认:均线窗口的稳健性阶梯
读图:横轴是均线窗口(120/200/250 日),柱为样本内夏普、点为样本外夏普。越短的窗口越容易样本内偏高、样本外回落(过拟合气味);MA250 在样本内外都站得住——所以选 250,是因为它落在稳健的一侧,而非因为它某个指标最高。
这才叫「不偏离原方法的优化」没有动 RSRS 的任何核心参数,只叠加了一个有独立逻辑、用公认窗口的趋势闸。它带来的主要是风险的下降(而非收益的暴涨),且样本外不退化——这是稳健改进的指纹。任何「样本内收益翻倍、样本外查无此事」的优化,都该被扔掉。

把方法装进口袋

RSRS 这趟流水线,浓缩了量化研究的全部要义:

  • 异象要有故事——支撑/阻力强弱,先有经济学直觉,再有统计量;
  • 把直觉写成可复算的数学,交给会自动重算的引擎,让结论始终鲜活、可被他人核验;
  • 用样本外和参数高原拷问自己,把运气和信号分开;
  • 优化要先验、要有逻辑、要看样本外——稳健的改进通常体现为风险下降,而非收益神话。

最终我们得到的不是一台印钞机,而是一个诚实的弱信号:它真实存在、能降低回撤、样本外仍站得住。在充满噪声的市场里,能稳稳抓住一丝真信号,已经是 alpha 的全部含义。