Administrator
2025-12-29 b22f1797312ac54028f24c50ef9277f75c8ef9fc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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<BigDecimal>
 * - 参数说明:需要至少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<BigDecimal> 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;
    }
}