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;
|
}
|
}
|