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;
|
|
/**
|
* KDJ (Stochastic Oscillator) 指标实现
|
* 计算逻辑:
|
* 1. RSV = (收盘价 - N日内最低价) / (N日内最高价 - N日内最低价) * 100
|
* 2. K = 2/3 * 前一日K值 + 1/3 * 当日RSV
|
* 3. D = 2/3 * 前一日D值 + 1/3 * 当日K值
|
* 4. J = 3*K - 2*D
|
*/
|
@Slf4j
|
@Getter
|
@Setter
|
public class KDJ extends IndicatorBase {
|
|
private static final int DEFAULT_PERIOD = 9;
|
private static final int K_PERIOD = 3;
|
private static final int D_PERIOD = 3;
|
|
private int period = DEFAULT_PERIOD;
|
private BigDecimal k = new BigDecimal(50);
|
private BigDecimal d = new BigDecimal(50);
|
private BigDecimal j = new BigDecimal(50);
|
private BigDecimal prevK = new BigDecimal(50);
|
private BigDecimal prevD = new BigDecimal(50);
|
|
public KDJ() {}
|
|
public KDJ(int period) {
|
this.period = period;
|
}
|
|
/**
|
* 计算KDJ指标
|
* @param prices 价格列表
|
*/
|
public void calculate(List<BigDecimal> prices) {
|
if (prices == null || prices.size() < period) {
|
return;
|
}
|
|
// 获取最近N天的价格
|
List<BigDecimal> recentPrices = getRecentPrices(prices, period);
|
|
// 计算最高价和最低价
|
BigDecimal high = recentPrices.stream().max(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
|
BigDecimal low = recentPrices.stream().min(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
|
BigDecimal close = recentPrices.get(recentPrices.size() - 1);
|
|
// 计算RSV
|
BigDecimal rsv;
|
if (high.compareTo(low) == 0) {
|
rsv = new BigDecimal(50);
|
} else {
|
rsv = close.subtract(low)
|
.divide(high.subtract(low), 8, RoundingMode.HALF_UP)
|
.multiply(new BigDecimal(100))
|
.setScale(8, RoundingMode.HALF_UP);
|
}
|
|
// 计算K值
|
prevK = k;
|
k = new BigDecimal(2).multiply(prevK)
|
.add(rsv)
|
.divide(new BigDecimal(3), 8, RoundingMode.HALF_UP);
|
|
// 计算D值
|
prevD = d;
|
d = new BigDecimal(2).multiply(prevD)
|
.add(k)
|
.divide(new BigDecimal(3), 8, RoundingMode.HALF_UP);
|
|
// 计算J值
|
j = k.multiply(new BigDecimal(3))
|
.subtract(d.multiply(new BigDecimal(2)))
|
.setScale(8, RoundingMode.HALF_UP);
|
|
log.debug("KDJ计算结果 - K: {}, D: {}, J: {}", k, d, j);
|
}
|
|
/**
|
* 判断超买(K > 80)
|
* @return 是否超买
|
*/
|
public boolean isOverbought() {
|
return k.compareTo(new BigDecimal(80)) > 0;
|
}
|
|
/**
|
* 判断超卖(K < 20)
|
* @return 是否超卖
|
*/
|
public boolean isOversold() {
|
return k.compareTo(new BigDecimal(20)) < 0;
|
}
|
|
/**
|
* 判断金叉信号(K线上穿D线)
|
* @return 是否形成金叉
|
*/
|
public boolean isGoldenCross() {
|
return prevK.compareTo(prevD) < 0 && k.compareTo(d) > 0;
|
}
|
|
/**
|
* 判断死叉信号(K线下穿D线)
|
* @return 是否形成死叉
|
*/
|
public boolean isDeathCross() {
|
return prevK.compareTo(prevD) > 0 && k.compareTo(d) < 0;
|
}
|
}
|