3. 增量更新算法基础

逐笔特征计算的核心矛盾在于:行情流是持续到达的,而特征往往需要“最近一段时间”的统计量。如果每来一笔都从头重算窗口内全部数据,计算量会随窗口长度线性增长,在毫秒级延迟约束下不可接受。增量更新的思路是:让每一笔新数据只做常数级或对数级的局部修正,把历史计算结果复用起来。本节围绕四种基础构件展开——滑动窗口聚合、指数加权移动平均、增量方差/协方差、环形缓冲区,并对比它们与全量重算的复杂度差异。

一、滑动窗口聚合:从“重扫”到“加减”

设窗口长度为 N,窗口内需要维护求和 S。全量重算每来一笔都要遍历 N 个元素,单笔复杂度 O(N);增量做法是维护一个累加器,新数据进入时 S += x_new,最老数据离开时 S -= x_old,单笔复杂度降为 O(1)。均值、最大值、最小值同理,但最大值/最小值不能简单相减,需要配合单调队列,单笔摊还复杂度仍为 O(1)。

以成交额窗口均值特征为例,假设窗口为最近 50 笔成交,初始累加器为 0。第 51 笔到达时,先减去第 1 笔的成交额,再加上第 51 笔的成交额,然后除以 50 得到新均值。整个过程只涉及两次浮点加减和一次除法,与窗口长度无关。需要注意浮点累加误差:长时间运行后 S 可能产生漂移,工程上可定期用一次全量重算做校准,或改用 Kahan 求和补偿。

二、指数加权移动平均:用衰减因子替代窗口

EWMA 不保存历史样本,只维护一个状态值:EWMA_t = α · x_t + (1 − α) · EWMA_{t−1},其中 α ∈ (0,1) 为平滑系数。单笔更新为常数次乘加,复杂度 O(1),内存 O(1)。它与滑动窗口的区别在于:窗口聚合有明确的“遗忘边界”,第 N+1 笔之前的样本权重瞬间归零;EWMA 则是渐进遗忘,越老的数据权重按 (1−α)^k 指数衰减。

实际参数选择上,若希望等效观察长度约为 N 笔,常用近似 α ≈ 2/(N+1)。例如 N=20 时取 α≈0.095。EWMA 对突发行情反应更平滑,但也意味着旧状态会“拖尾”,在剧烈跳变时不如硬窗口灵敏。逐笔特征中,EWMA 常用于买卖压力、短期波动率的平滑估计。

三、增量方差与协方差:Welford 式递推

方差若用 Σx² − (Σx)²/N 直接增量维护,数值稳定性差,大均值小方差时容易产生灾难性抵消。更稳妥的是维护均值与二阶中心矩的递推形式。对固定窗口,进入新值 x_new、离开旧值 x_old 时,可先更新和与平方和,再计算方差;若追求数值稳定,可采用“先移除旧值、再加入新值”的两步 Welford 更新,每步都是常数次运算。

协方差用于刻画两个逐笔序列的联动,例如成交额与价格变化的协方差。维护 Σx、Σy、Σxy 三个累加器,窗口滑动时同步加减,单笔 O(1)。但同样存在数值稳定性问题,工程上可对数据做中心化预处理,或定期重算校准。对比全量重算的 O(N),增量协方差在 N=200 的窗口下可将单笔计算量降低两个数量级。

四、环形缓冲区:让“最老元素”可 O(1) 定位

增量聚合的前提是能快速找到并移除窗口中最老的元素,环形缓冲区正是为此设计。它用固定长度数组加头尾指针实现:写入时尾指针前进并覆盖最老位置,读取最老元素时直接访问头指针。入队、出队、访问最老元素均为 O(1),内存预分配、无动态扩容,避免了链表节点分配带来的抖动,非常适合低延迟场景。

设计要点包括:容量取 2 的幂以便用位掩码替代取模;区分“满”与“空”状态(可用计数器或预留一个空位);多线程环境下写指针与读指针的可见性需用内存序或原子操作保证。逐笔特征引擎通常为每个特征维护独立的环形缓冲区,缓冲区长度即窗口长度。

增量更新 vs 全量重算:单笔处理路径与复杂度 全量重算(每笔 O(N)) 新逐笔 遍历窗口 N 项 输出特征 N=200 时单笔约 200 次累加,延迟随窗口线性上升 内存 O(N),无状态复用,实现简单但难满足低延迟 增量更新(每笔 O(1)) 新逐笔 加新值 / 减旧值 输出特征 单笔仅常数次运算,延迟与窗口长度解耦 需维护累加器状态,注意浮点漂移与校准 环形缓冲区:固定容量、头尾指针、O(1) 定位最老元素 x₄₇ x₄₈ x₄₉ x₅₀ x₅₁ 空位 空位 头指针(最老) 尾指针(最新写入) 窗口滑动一步: S ← S + x₅₁ − x₄₇(移除最老、加入最新) 头指针后移一格,尾指针后移一格,容量不变 容量取 2 的幂,用位掩码替代取模

五、复杂度对比与选型判断

把四种构件放在同一张表里看,差异一目了然。下表为示例性对比,N 表示窗口长度,d 表示协方差维度。

构件单笔时间复杂度内存主要风险典型用途
全量重算窗口聚合O(N)O(N)延迟随窗口线性增长离线校验、基准对照
增量滑动窗口聚合O(1)O(N)浮点累加漂移窗口均值、成交额和
EWMAO(1)O(1)旧状态拖尾、α 难调买卖压力、波动率平滑
增量方差/协方差O(1) / O(d²)O(N) / O(d²)数值抵消、需中心化波动率、量价协方差
环形缓冲区O(1)O(N) 预分配满/空判定、并发可见性为增量聚合提供窗口底座

选型时先问三个问题:窗口是否有明确边界?若需要“只看最近 N 笔”,用环形缓冲区加增量聚合;若更看重平滑与低内存,用 EWMA。统计量是否涉及二阶矩?方差、协方差必须用数值稳定的递推形式,不能图省事直接维护平方和。窗口长度是否频繁变化?环形缓冲区容量固定,变长窗口需要重建缓冲区,这一点要在特征配置阶段就确定。

工程实践中,建议为每个增量特征保留一个“影子全量重算”通道,按固定间隔(例如每 1000 笔)抽样比对一次,偏差超过阈值即触发校准。这既能发现浮点漂移,也能在算法改动后快速定位回归。

最后强调一个容易忽略的点:增量更新的正确性依赖于“进入与离开严格配对”。逐笔流中若出现乱序、重复或丢包,累加器会与真实窗口脱节。因此环形缓冲区写入前应做序列号校验,发现缺口时标记该特征为“待重建”,而不是继续在错误状态上累加。低延迟不等于牺牲正确性,增量算法的价值恰恰在于用可控的状态维护换取稳定的常数级延迟。