You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
108 lines
3.9 KiB
108 lines
3.9 KiB
package com.deepchart.utils;
|
|
|
|
import com.deepchart.entity.KDJData;
|
|
import com.deepchart.entity.StockDailyData;
|
|
|
|
import java.util.*;
|
|
|
|
/**
|
|
* KD指标分析工具类
|
|
*/
|
|
public class KDUtil {
|
|
|
|
private static final int N = 9; // 默认周期长度
|
|
private static final double SMOOTH_K = 1.0 / 3; // K 平滑系数
|
|
private static final double SMOOTH_D = 1.0 / 3; // D 平滑系数
|
|
|
|
private static List<KDJData> calculateKD(List<StockDailyData> dataList) {
|
|
if (dataList == null || dataList.size() < N) {
|
|
throw new IllegalArgumentException("数据不足,至少需要" + N + "条记录");
|
|
}
|
|
|
|
List<KDJData> kdList = new ArrayList<>();
|
|
Deque<Double> highs = new ArrayDeque<>(N);
|
|
Deque<Double> lows = new ArrayDeque<>(N);
|
|
|
|
Double prevK = null;
|
|
Double prevD = null;
|
|
|
|
for (int i = 0; i < dataList.size(); i++) {
|
|
StockDailyData data = dataList.get(i);
|
|
highs.offerLast(data.getHighPrice());
|
|
lows.offerLast(data.getLowPrice());
|
|
|
|
if (highs.size() > N) {
|
|
highs.pollFirst();
|
|
lows.pollFirst();
|
|
}
|
|
|
|
if (i >= N - 1) {
|
|
double hhv = Collections.max(highs);
|
|
double llv = Collections.min(lows);
|
|
double rsv = ((data.getClosePrice() - llv) / (hhv - llv)) * 100;
|
|
|
|
double kValue = (prevK == null ? rsv : prevK * (1 - SMOOTH_K) + rsv * SMOOTH_K);
|
|
double dValue = (prevD == null ? kValue : prevD * (1 - SMOOTH_D) + kValue * SMOOTH_D);
|
|
|
|
kdList.add(new KDJData(data.getDate(), rsv, kValue, dValue, 0));
|
|
prevK = kValue;
|
|
prevD = dValue;
|
|
} else {
|
|
kdList.add(new KDJData(data.getDate(), 0, 0, 0, 0)); // 占位符
|
|
}
|
|
}
|
|
|
|
return kdList;
|
|
}
|
|
|
|
public static String generateReport(List<StockDailyData> stockDataList) {
|
|
List<KDJData> kdList = calculateKD(stockDataList);
|
|
StringBuilder report = new StringBuilder();
|
|
|
|
report.append("📈 股票KD指标分析报告\n");
|
|
report.append("=======================\n");
|
|
|
|
if (kdList.isEmpty()) {
|
|
report.append("❌ 数据不足,无法生成分析。\n");
|
|
return report.toString();
|
|
}
|
|
|
|
KDJData latest = kdList.get(kdList.size() - 1);
|
|
KDJData previous = kdList.size() >= 2 ? kdList.get(kdList.size() - 2) : null;
|
|
|
|
double k = latest.getK();
|
|
double d = latest.getD();
|
|
|
|
// 判断超买或超卖
|
|
if (k > 80 && d > 80) {
|
|
report.append("🔴 当前处于【超买】状态,注意回调风险。\n");
|
|
} else if (k < 20 && d < 20) {
|
|
report.append("🟢 当前处于【超卖】状态,关注反弹机会。\n");
|
|
} else {
|
|
report.append("🟡 当前处于中性区域,暂无明显买卖信号。\n");
|
|
}
|
|
|
|
// 判断金叉/死叉
|
|
if (previous != null) {
|
|
boolean isGoldenCross = previous.getK() <= previous.getD() && k > d;
|
|
boolean isDeathCross = previous.getK() >= previous.getD() && k < d;
|
|
|
|
if (isGoldenCross && d < 30) {
|
|
report.append("✨ 出现金叉(K线上穿D线),特别是在低位,是潜在买入信号。\n");
|
|
} else if (isDeathCross && d > 70) {
|
|
report.append("⚠️ 出现死叉(K线下穿D线),尤其是在高位,建议谨慎卖出。\n");
|
|
}
|
|
}
|
|
|
|
// 多空趋势判断
|
|
if (k > 50 && d > 50 && k > d) {
|
|
report.append("⬆️ 当前为多头趋势,可考虑持股观望。\n");
|
|
} else if (k < 50 && d < 50 && k < d) {
|
|
report.append("⬇️ 当前为空头趋势,宜控制仓位。\n");
|
|
} else {
|
|
report.append("🔄 当前为震荡行情,短线操作更适宜。\n");
|
|
}
|
|
|
|
return report.toString();
|
|
}
|
|
}
|