计算加密货币RSI的Python转JS代码结果不符原因排查
RSI计算代码:Python与JavaScript结果不一致的原因及修正
核心差异原因
两段代码的本质区别在于平均计算方式,这是导致结果不同的关键:
- Python代码使用指数加权移动平均(EWMA):通过
up.ewm(com=(period - 1), min_periods=period).mean()计算平均涨幅,down.abs().ewm(...)计算平均跌幅。这里的com=(period-1)对应指数平滑系数α=1/period,会给近期数据更高的权重,是RSI标准计算中常用的方式。 - JavaScript代码使用简单算术平均(SMA):直接取前
period个涨幅/跌幅的总和除以period,没有考虑数据的时间权重,属于最基础的平均方式,和Python的逻辑完全不符。
除此之外还有两处细节差异:
- Python代码返回的是指定索引
st位置的RSI值,而JS代码只计算了第一个完整周期的RSI,没有处理后续数据的迭代更新。 - Python的
min_periods=period确保只有在积累了足够多的数据后才开始计算,JS代码虽然做了长度判断,但后续逻辑没有对应这种延迟计算的逻辑。
修正后的JavaScript代码
以下是和Python逻辑完全对齐的JS实现,采用指数加权移动平均计算RSI,并支持返回指定位置的结果:
export function getRSI(ohlcv, period, st) { if (ohlcv.length <= st) { throw new Error("Index st exceeds data length"); } if (st < period) { throw new Error("st must be >= period to calculate valid RSI"); } let deltas = []; // 计算收盘价差值 for (let i = 1; i <= st; i++) { deltas.push(ohlcv[i].close - ohlcv[i - 1].close); } let up = []; let down = []; deltas.forEach(d => { up.push(d > 0 ? d : 0); down.push(d < 0 ? Math.abs(d) : 0); }); // 计算指数加权平均涨幅和跌幅,对应Python的ewm(com=period-1) let alpha = 1 / period; let avgGain = 0; let avgLoss = 0; // 先计算前period个数据的简单平均作为初始值 let initialGainSum = up.slice(0, period).reduce((acc, cur) => acc + cur, 0); let initialLossSum = down.slice(0, period).reduce((acc, cur) => acc + cur, 0); avgGain = initialGainSum / period; avgLoss = initialLossSum / period; // 从第period+1个数据开始迭代更新加权平均 for (let i = period; i < up.length; i++) { avgGain = alpha * up[i] + (1 - alpha) * avgGain; avgLoss = alpha * down[i] + (1 - alpha) * avgLoss; } let RS = avgGain / avgLoss; return 100 - (100 / (1 + RS)); }
验证说明
- 这段代码完全复刻了Python版本的逻辑:先计算初始周期的简单平均,之后用指数加权公式迭代更新平均涨幅/跌幅。
- 传入参数
st和Python版本的st含义一致,返回对应位置的RSI值。
内容的提问来源于stack exchange,提问作者jiyong Yoon
相关产品推荐
相关产品推荐

