package com.xcong.excoin.modules.okxNewPrice.indicator; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.List; /** * RSI (Relative Strength Index) 指标实现 * 计算逻辑: * 1. 计算N天内的上涨幅度和下跌幅度 * 2. 计算平均上涨幅度和平均下跌幅度 * 3. RSI = 100 - (100 / (1 + (平均上涨幅度 / 平均下跌幅度))) * * 作用: * 1. 衡量市场的相对强弱程度(0-100) * 2. 超买信号:RSI>70表示市场超买,可能回调 * 3. 超卖信号:RSI<30表示市场超卖,可能反弹 * 4. 极端超买:RSI>80表示市场极度超买 * 5. 极端超卖:RSI<20表示市场极度超卖 * * 价格参数类型: * - 参数名称:prices * - 参数类型:List * - 参数说明:需要至少15个(默认周期+1)价格数据点用于计算 * * 推荐时间粒度及优缺点: * 1. 1分钟(1m): * - 优点:反应迅速,适合超短线交易 * - 缺点:RSI波动剧烈,频繁进入超买超卖区域 * 2. 5分钟(5m): * - 优点:RSI波动相对稳定,适合短线交易 * - 缺点:仍有一定虚假超买超卖信号 * 3. 15分钟(15m): * - 优点:超买超卖信号较为可靠,适合日内交易 * - 缺点:反应速度较慢 * 4. 1小时(1h)及以上: * - 优点:超买超卖信号明确,适合中期交易 * - 缺点:反应滞后,不适合短线交易 */ @Slf4j @Getter @Setter public class RSI extends IndicatorBase { private static final int DEFAULT_PERIOD = 14; private int period = DEFAULT_PERIOD; private BigDecimal rsi = BigDecimal.ZERO; private BigDecimal prevAvgGain = BigDecimal.ZERO; private BigDecimal prevAvgLoss = BigDecimal.ZERO; public RSI() {} public RSI(int period) { this.period = period; } /** * 计算RSI指标 * @param prices 价格列表 */ public void calculate(List prices) { if (prices == null || prices.size() < period + 1) { return; } if (prevAvgGain.compareTo(BigDecimal.ZERO) == 0 && prevAvgLoss.compareTo(BigDecimal.ZERO) == 0) { // 首次计算 BigDecimal totalGain = BigDecimal.ZERO; BigDecimal totalLoss = BigDecimal.ZERO; for (int i = prices.size() - period; i < prices.size(); i++) { BigDecimal change = prices.get(i).subtract(prices.get(i - 1)); if (change.compareTo(BigDecimal.ZERO) > 0) { totalGain = totalGain.add(change); } else { totalLoss = totalLoss.add(change.abs()); } } prevAvgGain = totalGain.divide(new BigDecimal(period), 8, RoundingMode.HALF_UP); prevAvgLoss = totalLoss.divide(new BigDecimal(period), 8, RoundingMode.HALF_UP); } else { // 后续计算 BigDecimal change = prices.get(prices.size() - 1).subtract(prices.get(prices.size() - 2)); BigDecimal gain = change.compareTo(BigDecimal.ZERO) > 0 ? change : BigDecimal.ZERO; BigDecimal loss = change.compareTo(BigDecimal.ZERO) < 0 ? change.abs() : BigDecimal.ZERO; prevAvgGain = prevAvgGain.multiply(new BigDecimal(period - 1)) .add(gain) .divide(new BigDecimal(period), 8, RoundingMode.HALF_UP); prevAvgLoss = prevAvgLoss.multiply(new BigDecimal(period - 1)) .add(loss) .divide(new BigDecimal(period), 8, RoundingMode.HALF_UP); } // 计算RSI if (prevAvgLoss.compareTo(BigDecimal.ZERO) == 0) { rsi = new BigDecimal(100); } else { BigDecimal rs = prevAvgGain.divide(prevAvgLoss, 8, RoundingMode.HALF_UP); rsi = new BigDecimal(100) .subtract(new BigDecimal(100).divide(BigDecimal.ONE.add(rs), 8, RoundingMode.HALF_UP)) .setScale(8, RoundingMode.HALF_UP); } log.info("RSI计算结果 - RSI({}): {}", period, rsi); } /** * 判断超买(RSI > 70) * @return 是否超买 */ public boolean isOverbought() { return rsi.compareTo(new BigDecimal(70)) > 0; } /** * 判断超卖(RSI < 30) * @return 是否超卖 */ public boolean isOversold() { return rsi.compareTo(new BigDecimal(30)) < 0; } /** * 判断超买(RSI > 80) * @return 是否严重超买 */ public boolean isExtremelyOverbought() { return rsi.compareTo(new BigDecimal(80)) > 0; } /** * 判断超卖(RSI < 20) * @return 是否严重超卖 */ public boolean isExtremelyOversold() { return rsi.compareTo(new BigDecimal(20)) < 0; } }