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

使用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迭代」的规则:

    1. 先计算前14个周期增益/损失的简单平均值作为初始RMA值
    2. 后续周期用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)
}

关键修改说明

  1. 修正差值计算逻辑:通过索引遍历收盘价数组,直接计算相邻两个收盘价的差值,彻底解决prev_close未更新的问题。
  2. 初始化RMA为SMA:先计算前14个增益/损失的简单平均值,完全匹配TradingView的RSI计算规则。
  3. 添加数据量校验与极端情况处理:确保K线数据量满足计算要求,同时避免除数为零的panic。
  4. 优化数据处理逻辑:将收盘价收集为Vec,通过索引访问更清晰,便于后续调试与扩展。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 13:29:54