Administrator
7 days ago 4dbaa914142b578be0756608d3cd23c328c3e4cd
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
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 + (平均上涨幅度 / 平均下跌幅度)))
 */
@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.debug("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;
    }
}