← 返回编程题库
coding-online-cumulative-bollinger-bands中等免费版2000ms未尝试

在线累计 Bollinger 带:Welford 递推的带对序列

Online Cumulative Bollinger Bands: Welford-Recurrence Band-Pair Stream

开始编码

实现 solution(x: list[float], k_sigma: float) -> list[list[float]]。给定长度为 N 的逐期观测数组 x 与非负带宽倍数 k_sigma,输出长度为 N 的列表,第 k 项(1-indexed)为二元列表 [lower_band_k, upper_band_k],定义为

lower_band_k = mean_k - k_sigma * sd_k
upper_band_k = mean_k + k_sigma * sd_k

其中 mean_ksd_kx[0..k-1]累计总体均值与标准差(分母 k,不是 k-1),通过 Welford 递推

delta    = x[k-1] - mean_old
mean_new = mean_old + delta / k
M2_new   = M2_old + delta * (x[k-1] - mean_new)
sd_k     = sqrt(M2_new / k)

mean_0 = 0M2_0 = 0 起步。这道题的合约是先折入再读出:每个 tick 先把当前观测吸收进运行状态,再从那个状态读 meansd 来生成带对——不存在 prior-only z 分数那种"自污染分母"问题,因为第 k 个带对就是定义在包含当前观测的 x[0..k-1] 上。

关键工程要点是总体方差 vs 样本方差的分母选择。本题用分母 k,所以 k=1 时方差正好是 0,两条带都退化到 x[0];改用 statistics.stdev(分母 k-1)会在 k=1 时除零,并在其它 k 处统一偏出 sqrt(k/(k-1)) 倍。Welford 是单趟 O(N)、O(1) 状态,且在大均值-小方差体制下保持数值稳定;教科书的两趟 sum(x^2)/n − (sum(x)/n)^2 写法在该体制下会因为两个相近正数的相消而输出 0 或负方差,让 sqrt 在合法输入上报错。

solution([1.0, 2.0, 3.0, 4.0, 5.0], 2.0) 返回

[
  [1.0,                  1.0                ],
  [0.5,                  2.5                ],
  [0.3670068381445479,   3.632993161855452  ],
  [0.2639320225002102,   4.736067977499790  ],
  [0.1715728752538097,   5.828427124746190  ],
]

逐步:k=1 时窗口里只有一个样本,mean=1sd=0,两条带都是 1。k=2 时前缀 [1, 2],累计均值 1.5、总体方差 ((1-1.5)^2 + (2-1.5)^2)/2 = 0.25sd = 0.5,带对 1.5 ± 2*0.5 = [0.5, 2.5]。k=3 时前缀 [1, 2, 3],均值 2、总体方差 ((-1)^2 + 0 + 1^2)/3 = 2/3sd = sqrt(2/3) ≈ 0.81650,带对 2 ± 2*sqrt(2/3) ≈ [0.367, 3.633]。k=5 时前缀 [1..5],均值 3、总体方差 (4+1+0+1+4)/5 = 2sd = sqrt(2),带对 3 ± 2*sqrt(2) ≈ [0.172, 5.828]。如果用样本方差(分母 k-1),k=5 时会变成 3 ± 2*sqrt(2.5) ≈ [-0.162, 6.162]——判别测试会捕获这个漂移。

需明确若干细节:solution([], 1.5) 返回 [];常数流如 [3.5, 3.5, 3.5, 3.5] 输出 [[3.5, 3.5], [3.5, 3.5], [3.5, 3.5], [3.5, 3.5]](sd 全程为 0);k_sigma == 0 时每个 k 的带对都坍缩到累计均值;k_sigma 最大可达 100x 量级可至 ±1e6——Welford 保数值稳定,朴素累加器不行。比较器使用绝对容差 1e-9,因此任何合理的 Welford 或等价补偿和实现都通过。

实现细节由 stubs/stub.py 提供。

实践背景

监控台想在某条收益 / 信号流上挂一个最简单的"当 tick 是否落在累计历史均值 ± k_sigma 包络外"的判断,最常见的就是 Bollinger 带几何。野外存在两种口味:滑动窗口形(取最近 W 期的均值与 sd,零售看图常用)和这里的累计历史形(在整段历史上累计均值与 sd)。当桌子希望整段回测——包括深尾——都参与到带的形状里、而不被一个本身可能并不代表性的近期窗口主导时,累计形是首选;它也是流式统计约束下的自然选择——例如低延迟的 on-ack 事后监控对每笔成交相对其全生命周期的统计打分时,维持滚动缓冲是昂贵的。把分母固定为 k(总体)而不是 k-1(样本)这一个细节是下游读者真正在意的:当带对后续要与 stack 别处按总体 sd 标定的预交易阈值比对时,两种分母约定会悄悄差出 sqrt(k/(k-1)),这个差在低 k 时会演变成系统性的阈值漂移。Welford 是承重递推,因为另一种选项——累加 sum(x)sum(x*x) 再用 E[X^2] - E[X]^2 读方差——在 mid 报价、FX 汇率这种大均值-小离散度的流上会失败:两个接近的正数相消可以输出零或负方差,让 sqrt 在合法输入上报错。Welford 累加的是相对运行均值的平方偏差、而不是相对零的,从而避开相消。

约束条件

  • 0 <= len(x) <= 5000,其中 x 是 solution(x, k_sigma) 的逐期观测数组
  • k_sigma 为非负有限 float,取值在 [0, 100]
  • x[i] 为有限带符号 float,绝对值不超过 1e6
  • 输出为长度 N 的二元 float 列表 [lower_band, upper_band];空输入返回 []
  • 比较器为 float 绝对容差 1e-9(任意数值稳定的 Welford 或等价补偿和实现都通过)

样例

Case 1 · statement-example: rising 5-tick stream with k_sigma=2.0

输入: [[1,2,3,4,5],2]

期望: [[1,1],[0.5,2.5],[0.36700683814454793,3.632993161855452],[0.2639320225002102,4.73606797749979],[0.1715728752538097,5.82842712474619]]

k=1 时 sd=0 故 [1,1];之后随累计样本 mean 与 population sd 演化,输出每一步的 [mean - 2*sd, mean + 2*sd]。

Case 2 · visible: empty stream returns []

输入: [[],1.5]

期望: []

空输入直接返回空列表。

Case 3 · visible: constant stream emits flat bands

输入: [[3.5,3.5,3.5,3.5],2.5]

期望: [[3.5,3.5],[3.5,3.5],[3.5,3.5],[3.5,3.5]]

全等流的 sd 恒为 0,每一步都是 [3.5, 3.5]。

Case 4 · visible: k_sigma=0 collapses both bands onto the mean

输入: [[2,4,6,8],0]

期望: [[2,2],[3,3],[4,4],[5,5]]

k_sigma=0 时带宽为零,每一步两条带都退化为该步 cumulative mean。

Case 5 · visible: single tick emits [v, v]

输入: [[7.25],3]

期望: [[7.25,7.25]]

单元素 N=1 时 sd_1=0,输出 [[7.25, 7.25]]。

最近提交

还没有提交记录。

编码区

实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。

加载编辑器...
计时0:00

默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。

Case 1 · statement-example: rising 5-tick stream with k_sigma=2.0

输入: [[1,2,3,4,5],2]

期望: [[1,1],[0.5,2.5],[0.36700683814454793,3.632993161855452],[0.2639320225002102,4.73606797749979],[0.1715728752538097,5.82842712474619]]

k=1 时 sd=0 故 [1,1];之后随累计样本 mean 与 population sd 演化,输出每一步的 [mean - 2*sd, mean + 2*sd]。

Case 2 · visible: empty stream returns []

输入: [[],1.5]

期望: []

空输入直接返回空列表。

Case 3 · visible: constant stream emits flat bands

输入: [[3.5,3.5,3.5,3.5],2.5]

期望: [[3.5,3.5],[3.5,3.5],[3.5,3.5],[3.5,3.5]]

全等流的 sd 恒为 0,每一步都是 [3.5, 3.5]。

Case 4 · visible: k_sigma=0 collapses both bands onto the mean

输入: [[2,4,6,8],0]

期望: [[2,2],[3,3],[4,4],[5,5]]

k_sigma=0 时带宽为零,每一步两条带都退化为该步 cumulative mean。

Case 5 · visible: single tick emits [v, v]

输入: [[7.25],3]

期望: [[7.25,7.25]]

单元素 N=1 时 sd_1=0,输出 [[7.25, 7.25]]。