使用Rust+MEXC API计算RSI与TradingView结果不符求助
RSI指标计算错误排查与修正
我尝试用Rust结合MEXC API复现TradingView平台的RSI指标数值,但计算结果不正确,相关代码如下:
async fn get_symbol_rsi(client: Client, symbol: &str) -> AppResult<f64> { let candles: Klines = client .get(format!( "https://contract.mexc.com/api/v1/contract/kline/{symbol}?interval=Min15" )) .send() .await? .json() .await?; let mut closes = candles .data .close .into_iter() .rev() // So it shows from new candles .take(15) // take the last 15 new candles (1 more because of the first one?) .rev(); // reverse it back to the original order older to newer // Get the gains and losses percentages let mut gains = Vec::new(); let mut losses = Vec::new(); let prev_close = closes.next().unwrap(); for close in closes { let diff = close - prev_close; if diff > 0.0 { gains.push(diff); losses.push(0.0); } else { gains.push(0.0); losses.push(diff.abs()); } } // Calculate the RSI based on RMA let alpha = 1.0 / 14.0; let mut rma_gains = gains[0]; let mut rma_losses = losses[0]; for i in 1..14 { rma_gains = alpha * gains[i] + (1.0 - alpha) * rma_gains; rma_losses = alpha * losses[i] + (1.0 - alpha) * rma_losses; } let rs = rma_gains / rma_losses; let rsi = 100.0 - (100.0 / (1.0 + rs)); Ok(rsi) }
问题排查
相邻收盘价差值计算错误
原代码中prev_close仅初始化一次,循环中始终用第一个收盘价与后续收盘价计算差值,完全违背了RSI计算「相邻周期收盘价变动」的核心逻辑。正确做法是每次循环结束后更新prev_close为当前收盘价。RMA初始值不符合标准逻辑
TradingView的RSI采用「先简单平均(SMA)初始化,后续用RMA迭代」的规则:- 先计算前14个周期增益/损失的简单平均值作为初始RMA值
- 后续周期用RMA公式迭代更新
原代码直接将第一个增益/损失值作为初始RMA,会导致计算结果大幅偏差。
蜡烛数据顺序的潜在风险
代码通过两次反转获取最新15个收盘价,但需确认MEXC合约API的K线返回顺序:若API默认返回「旧→新」的K线,当前逻辑正确;若返回「新→旧」,两次反转会导致拿到最旧的15个数据,直接导致RSI计算基于错误样本。未处理除数为零的极端情况
当所有周期均为上涨(losses全为0)时,rma_losses为0,计算rs = rma_gains / rma_losses会触发panic,需添加防御性处理。
修正后的代码
async fn get_symbol_rsi(client: Client, symbol: &str) -> AppResult<f64> { let candles: Klines = client .get(format!( "https://contract.mexc.com/api/v1/contract/kline/{symbol}?interval=Min15" )) .send() .await? .json() .await?; // 获取最新15个收盘价(假设API返回K线顺序为旧→新) let closes = candles .data .close .into_iter() .rev() .take(15) .rev() .collect::<Vec<f64>>(); // 检查数据量是否满足RSI(14)计算要求 if closes.len() != 15 { return Err(AppError::Custom("需要至少15根K线才能计算RSI(14)".into())); } // 计算相邻收盘价的差值,生成14组gain/loss let mut gains = Vec::with_capacity(14); let mut losses = Vec::with_capacity(14); for i in 1..15 { let diff = closes[i] - closes[i-1]; if diff > 0.0 { gains.push(diff); losses.push(0.0); } else { gains.push(0.0); losses.push(diff.abs()); } } // 用SMA计算初始RMA值,符合TradingView标准 let initial_avg_gain = gains.iter().sum::<f64>() / 14.0; let initial_avg_loss = losses.iter().sum::<f64>() / 14.0; // 处理全上涨无损失的极端情况 if initial_avg_loss == 0.0 { return Ok(100.0); } let alpha = 1.0 / 14.0; let mut rma_gains = initial_avg_gain; let mut rma_losses = initial_avg_loss; // 若有更多历史数据,可在此处继续迭代更新RMA;当前仅用最新14个差值,初始SMA即为最终RMA let rs = rma_gains / rma_losses; let rsi = 100.0 - (100.0 / (1.0 + rs)); Ok(rsi) }
关键修改说明
- 修正差值计算逻辑:通过索引遍历收盘价数组,直接计算相邻两个收盘价的差值,彻底解决
prev_close未更新的问题。 - 初始化RMA为SMA:先计算前14个增益/损失的简单平均值,完全匹配TradingView的RSI计算规则。
- 添加数据量校验与极端情况处理:确保K线数据量满足计算要求,同时避免除数为零的panic。
- 优化数据处理逻辑:将收盘价收集为Vec,通过索引访问更清晰,便于后续调试与扩展。
内容的提问来源于stack exchange,提问作者NightmareXD
相关产品推荐
相关产品推荐

