You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实时计算时间序列变量?滚动方差实现方法问询

滚动计算时间序列波动率(方差)实现方案

现有计算逻辑

你当前用于计算fv和指数移动平均ma的代码如下:

// 计算当前fv值
fv = exactMath.add(exactMath.mul(busdVolume, fv_busd), exactMath.div(exactMath.mul(fv_usdt, usdtVolume), busd_price));

// 存储fv到时间序列数组
fvs[n] = fv;

// 计算指数移动平均ma
if(n == 0){
    ma[n] = fvs[n];
}else if(n > 0){
    ma[n] = exactMath.add(exactMath.mul(0.1, fv), exactMath.mul(0.9, ma[n-1]));
}

需求说明

你需要实现滚动窗口式的波动率(方差)计算:

  • 当索引n=200时,用前200个fvs值(索引0~199)计算方差
  • 当索引n=400时,用索引200~399的200个fvs值计算方差
  • 本质是固定窗口大小(比如200),每新增一个数据就滑动窗口,丢弃最旧的一个值,用最新的窗口数据计算方差

滚动方差实现方案

直接遍历窗口内所有元素计算会浪费算力(尤其是链上环境),推荐用递推方式维护窗口的总和、平方和,从而实现O(1)复杂度的滚动计算。

核心思路

方差公式可以变形为:

方差 = (窗口内元素平方和 - (窗口内元素总和² / 窗口大小)) / (窗口大小 - 1)

我们只需要维护三个变量,每次新增数据时更新它们,就能快速计算当前窗口的方差:

  • window_sum:窗口内所有fvs值的总和
  • window_sq_sum:窗口内所有fvs值的平方和
  • window_size:固定的窗口长度(比如你需要的200)

代码实现

// 定义固定窗口大小,比如200
uint256 constant WINDOW_SIZE = 200;

// 维护全局状态变量,用于滚动计算
uint256 window_sum;
uint256 window_sq_sum;

// 在每次计算fv后,执行滚动方差计算
function calculateRollingVariance(uint256 n, uint256 fv) returns (uint256 var_fv) {
    // 1. 更新窗口的总和与平方和
    if (n < WINDOW_SIZE) {
        // 窗口还没填满,直接累加当前值
        window_sum = exactMath.add(window_sum, fv);
        window_sq_sum = exactMath.add(window_sq_sum, exactMath.mul(fv, fv));
    } else {
        // 窗口已满,移除最旧的元素(n-WINDOW_SIZE位置的值),加入当前值
        uint256 oldest_fv = fvs[n - WINDOW_SIZE];
        window_sum = exactMath.sub(window_sum, oldest_fv);
        window_sum = exactMath.add(window_sum, fv);
        
        window_sq_sum = exactMath.sub(window_sq_sum, exactMath.mul(oldest_fv, oldest_fv));
        window_sq_sum = exactMath.add(window_sq_sum, exactMath.mul(fv, fv));
    }

    // 2. 计算当前窗口的方差
    uint256 window_count = n < WINDOW_SIZE ? n + 1 : WINDOW_SIZE;
    if (window_count < 2) {
        // 元素不足2个时,方差无意义,返回0或自定义值
        return 0;
    }

    // 计算(总和²)/窗口大小
    uint256 sum_squared = exactMath.mul(window_sum, window_sum);
    uint256 avg_sq_term = exactMath.div(sum_squared, window_count);
    
    // 分子:平方和 - (总和²/窗口大小)
    uint256 numerator = exactMath.sub(window_sq_sum, avg_sq_term);
    
    // 方差 = 分子 / (窗口大小-1)
    var_fv = exactMath.div(numerator, exactMath.sub(window_count, 1));
    
    // 存储当前方差到数组
    var_fvs[n] = var_fv;
    return var_fv;
}

说明

  • 这个方案不需要每次遍历窗口内的所有元素,每次更新只做几次加减乘除,计算效率极高
  • 如果你的窗口大小不是固定值(比如随n变化),只需要调整WINDOW_SIZE的逻辑,比如当n=200时窗口200,n=400时窗口200,本质还是固定窗口滑动
  • 注意exactMath的精度问题,确保除法操作不会丢失精度(如果是整数运算,可能需要用乘法逆元或者定点数处理)

内容的提问来源于stack exchange,提问作者Alexey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 16:57:22