From b6b63e31d3f25784a4f4e0add7045a3d724d22fb Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Sun, 04 Jan 2026 13:09:38 +0800
Subject: [PATCH] refactor(indicator): 重构MACD指标计算工具类
---
/dev/null | 87 ------------
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/IndicatorUtils.java | 154 ++++++++++++++++++++++
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MACDCalculator.java | 111 +--------------
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MacdMaStrategy.java | 18 +-
4 files changed, 171 insertions(+), 199 deletions(-)
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/BearishSignalDetector.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/BearishSignalDetector.java
deleted file mode 100644
index c537e84..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/BearishSignalDetector.java
+++ /dev/null
@@ -1,132 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy;
-
-import java.math.BigDecimal;
-import java.util.List;
-
-/**
- * 示例用法
- * // 计算MACD(获取包含起始索引的结果)
- * MACDCalculator.MACDResult macdResult = MACDCalculator.calculateMACD(closePrices, 12, 26, 9);
- *
- * // 检测做空信号
- * boolean bearishSignal = BearishSignalDetector.isBearishSignalFormed(macdResult, closePrices);
- */
-public class BearishSignalDetector {
- /**
- * 判断是否形成做空信号
- *
- * @param macdResult 包含MACD数据和有效起始索引的结果对象
- * @param closePrices 收盘价列表
- * @return 是否形成信号
- */
- public static boolean isBearishSignalFormed(MACDResult macdResult, List<BigDecimal> closePrices) {
- List<PriceData> macdData = macdResult.getMacdData();
- int startIdx = macdResult.getStartIndex();
-
- // 校验数据长度(至少需要两个有效DIF点)
- if (macdData.size() < 2 || closePrices.size() < startIdx + macdData.size()) {
- return false;
- }
-
- int currentMacdIndex = macdData.size() - 1;
- int currentCloseIndex = startIdx + currentMacdIndex;
-
- // 1. 判断顶背离
- boolean isTopDivergence = isTopDivergence(macdData, closePrices, currentMacdIndex, startIdx);
-
- // 2. 判断死叉
- boolean isDeathCross = isDeathCross(macdData, currentMacdIndex);
-
- return isTopDivergence && isDeathCross;
- }
-
- /**
- * 判断顶背离(需确保earlierHigh在recentHigh之前)
- */
- private static boolean isTopDivergence(List<PriceData> macdData, List<BigDecimal> closePrices,
- int currentMacdIndex, int startIdx) {
- // 计算对应closePrices的索引
- int currentCloseIndex = startIdx + currentMacdIndex;
- if (currentCloseIndex < 10 || macdData.size() < 5) {
- return false;
- }
-
- // 寻找最近的价格高点(至少在当前点的前2根K线范围内)
- int recentHighCloseIndex = findRecentHighWithRetrace(closePrices, currentCloseIndex - 5, currentCloseIndex);
- if (recentHighCloseIndex == -1) {
- return false;
- }
-
- // 寻找更早的高点(确保在recentHigh之前)
- int earlierHighCloseIndex = findRecentHighWithRetrace(closePrices, startIdx, recentHighCloseIndex - 1);
- if (earlierHighCloseIndex == -1) {
- return false;
- }
-
- // 转换为macdData的索引
- int recentHighMacdIndex = recentHighCloseIndex - startIdx;
- int earlierHighMacdIndex = earlierHighCloseIndex - startIdx;
-
- // 判断价格创新高但DIF走低
- BigDecimal recentHighPrice = closePrices.get(recentHighCloseIndex);
- BigDecimal earlierHighPrice = closePrices.get(earlierHighCloseIndex);
- boolean isPriceHigher = recentHighPrice.compareTo(earlierHighPrice) > 0;
-
- BigDecimal recentDif = macdData.get(recentHighMacdIndex).getDif();
- BigDecimal earlierDif = macdData.get(earlierHighMacdIndex).getDif();
- boolean isDifLower = recentDif.compareTo(earlierDif) < 0;
-
- return isPriceHigher && isDifLower;
- }
-
- /**
- * 寻找有效的高点(高点后需有至少1根K线下跌)
- */
- private static int findRecentHighWithRetrace(List<BigDecimal> prices, int startIndex, int endIndex) {
- if (startIndex < 0 || endIndex >= prices.size() || startIndex >= endIndex) {
- return -1;
- }
-
- int highIndex = -1;
- BigDecimal highPrice = BigDecimal.ZERO;
-
- // 从右向左搜索,找到第一个有效高点
- for (int i = endIndex; i >= startIndex; i--) {
- if (prices.get(i).compareTo(highPrice) > 0) {
- highPrice = prices.get(i);
- highIndex = i;
- }
-
- // 检查高点后是否有回调
- if (highIndex != -1 && i < endIndex) {
- if (prices.get(i + 1).compareTo(highPrice) < 0) {
- return highIndex; // 找到确认回调的高点
- }
- }
- }
-
- return highIndex;
- }
-
-
- /**
- * 判断是否形成死叉(DIF下穿DEA)
- *
- * @param macdData MACD计算结果数据列表
- * @param currentIndex 当前数据点索引
- * @return 是否形成死叉
- */
- private static boolean isDeathCross(List<PriceData> macdData, int currentIndex) {
- if (currentIndex < 1) {
- return false;
- }
-
- PriceData current = macdData.get(currentIndex);
- PriceData previous = macdData.get(currentIndex - 1);
-
- // 前一期DIF >= DEA,当前期DIF < DEA
- return previous.getDif().compareTo(previous.getDea()) >= 0 &&
- current.getDif().compareTo(current.getDea()) < 0;
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/BullishSignalDetector.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/BullishSignalDetector.java
deleted file mode 100644
index b6265e6..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/BullishSignalDetector.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy;
-
-import java.math.BigDecimal;
-import java.util.List;
-
-/**
- * 示例用法
- * // 计算MACD结果(包含起始索引)
- * MACDCalculator.MACDResult macdResult = MACDCalculator.calculateMACD(closePrices, 12, 26, 9);
- *
- * // 检测做多信号
- * boolean bullishSignal = BullishSignalDetector.isBullishSignalFormed(macdResult, closePrices);
- */
-public class BullishSignalDetector {
- public static boolean isBullishSignalFormed(MACDResult macdResult, List<BigDecimal> closePrices) {
- List<PriceData> macdData = macdResult.getMacdData();
- int startIdx = macdResult.getStartIndex();
-
- // 校验MACD数据和收盘价长度合法性
- if (closePrices.size() < 34 || macdData.size() < 2 || startIdx + macdData.size() > closePrices.size()) {
- return false;
- }
-
- int currentMacdIndex = macdData.size() - 1;
- int currentCloseIndex = startIdx + currentMacdIndex;
-
- // 1. 金叉判断
- boolean isGoldenCross = isGoldenCross(macdData, currentMacdIndex);
-
- // 2. MACD柱动量增强
- boolean isMacdHistExpanding = isMacdHistExpanding(macdData, currentMacdIndex);
-
- // 3. 价格突破前15周期高点
- boolean isPriceBreakout = isPriceBreakout(closePrices, currentCloseIndex, 15);
-
- return isGoldenCross && isMacdHistExpanding && isPriceBreakout;
- }
-
- private static boolean isGoldenCross(List<PriceData> macdData, int currentMacdIndex) {
- if (currentMacdIndex < 1 || currentMacdIndex >= macdData.size()) {
- return false;
- }
-
- PriceData current = macdData.get(currentMacdIndex);
- PriceData previous = macdData.get(currentMacdIndex - 1);
-
- return previous.getDif().compareTo(previous.getDea()) <= 0 &&
- current.getDif().compareTo(current.getDea()) > 0;
- }
-
- private static boolean isMacdHistExpanding(List<PriceData> macdData, int currentMacdIndex) {
- if (currentMacdIndex < 2 || currentMacdIndex >= macdData.size()) {
- return false;
- }
-
- BigDecimal currentHist = macdData.get(currentMacdIndex).getMacdHist();
- BigDecimal prevHist = macdData.get(currentMacdIndex - 1).getMacdHist();
- BigDecimal prevPrevHist = macdData.get(currentMacdIndex - 2).getMacdHist();
-
- boolean isPositive = currentHist.compareTo(BigDecimal.ZERO) > 0;
- boolean isExpanding = prevPrevHist.compareTo(prevHist) <= 0 &&
- prevHist.compareTo(currentHist) < 0;
-
- return isPositive && isExpanding;
- }
-
- private static boolean isPriceBreakout(List<BigDecimal> closePrices, int currentCloseIndex, int period) {
- if (currentCloseIndex < period || period <= 0) {
- return false;
- }
-
- int startSearchIdx = Math.max(currentCloseIndex - period, 0);
- BigDecimal maxPreviousPrice = closePrices.get(startSearchIdx);
-
- for (int i = startSearchIdx + 1; i < currentCloseIndex; i++) {
- if (i >= closePrices.size()) {
- break;
- }
- BigDecimal price = closePrices.get(i);
- if (price.compareTo(maxPreviousPrice) > 0) {
- maxPreviousPrice = price;
- }
- }
-
- return closePrices.get(currentCloseIndex).compareTo(maxPreviousPrice) > 0;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/IndicatorUtils.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/IndicatorUtils.java
new file mode 100644
index 0000000..a3cbec7
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/IndicatorUtils.java
@@ -0,0 +1,154 @@
+/**
+ * 指标计算工具类
+ * 封装MACD策略中常用的通用功能,如高低点查找等
+ */
+package com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 指标计算工具类,提供MACD策略中常用的通用功能
+ */
+public class IndicatorUtils {
+
+ /**
+ * 找到最近的价格高点索引
+ *
+ * @param prices 价格列表
+ * @param startIdx 起始索引
+ * @return 最近的价格高点索引
+ */
+ public static int findRecentHighIndex(List<BigDecimal> prices, int startIdx) {
+ if (prices == null || startIdx < 0 || startIdx >= prices.size()) {
+ return -1;
+ }
+
+ int highIdx = startIdx;
+ BigDecimal highPrice = prices.get(startIdx);
+
+ for (int i = startIdx + 1; i < prices.size(); i++) {
+ BigDecimal currentPrice = prices.get(i);
+ if (currentPrice.compareTo(highPrice) > 0) {
+ highPrice = currentPrice;
+ highIdx = i;
+ }
+ }
+
+ return highIdx;
+ }
+
+ /**
+ * 找到最近的价格低点索引
+ *
+ * @param prices 价格列表
+ * @param startIdx 起始索引
+ * @return 最近的价格低点索引
+ */
+ public static int findRecentLowIndex(List<BigDecimal> prices, int startIdx) {
+ if (prices == null || startIdx < 0 || startIdx >= prices.size()) {
+ return -1;
+ }
+
+ int lowIdx = startIdx;
+ BigDecimal lowPrice = prices.get(startIdx);
+
+ for (int i = startIdx + 1; i < prices.size(); i++) {
+ BigDecimal currentPrice = prices.get(i);
+ if (currentPrice.compareTo(lowPrice) < 0) {
+ lowPrice = currentPrice;
+ lowIdx = i;
+ }
+ }
+
+ return lowIdx;
+ }
+
+ /**
+ * 找到最近价格高点之前的价格高点索引
+ *
+ * @param prices 价格列表
+ * @param startIdx 起始索引
+ * @param recentHighIdx 最近的价格高点索引
+ * @return 之前的价格高点索引
+ */
+ public static int findPreviousHighIndex(List<BigDecimal> prices, int startIdx, int recentHighIdx) {
+ if (prices == null || startIdx < 0 || recentHighIdx <= startIdx || recentHighIdx >= prices.size()) {
+ return -1;
+ }
+
+ int highIdx = startIdx;
+ BigDecimal highPrice = prices.get(startIdx);
+
+ for (int i = startIdx + 1; i < recentHighIdx; i++) {
+ BigDecimal currentPrice = prices.get(i);
+ if (currentPrice.compareTo(highPrice) > 0) {
+ highPrice = currentPrice;
+ highIdx = i;
+ }
+ }
+
+ return highIdx;
+ }
+
+ /**
+ * 找到最近价格低点之前的价格低点索引
+ *
+ * @param prices 价格列表
+ * @param startIdx 起始索引
+ * @param recentLowIdx 最近的价格低点索引
+ * @return 之前的价格低点索引
+ */
+ public static int findPreviousLowIndex(List<BigDecimal> prices, int startIdx, int recentLowIdx) {
+ if (prices == null || startIdx < 0 || recentLowIdx <= startIdx || recentLowIdx >= prices.size()) {
+ return -1;
+ }
+
+ int lowIdx = startIdx;
+ BigDecimal lowPrice = prices.get(startIdx);
+
+ for (int i = startIdx + 1; i < recentLowIdx; i++) {
+ BigDecimal currentPrice = prices.get(i);
+ if (currentPrice.compareTo(lowPrice) < 0) {
+ lowPrice = currentPrice;
+ lowIdx = i;
+ }
+ }
+
+ return lowIdx;
+ }
+
+ /**
+ * 寻找最近的价格高点(带有回调确认)
+ *
+ * @param prices 价格列表
+ * @param startIndex 起始索引
+ * @param endIndex 结束索引
+ * @return 符合条件的价格高点索引,未找到则返回-1
+ */
+ public static int findRecentHighWithRetrace(List<BigDecimal> prices, int startIndex, int endIndex) {
+ if (prices == null || startIndex < 0 || endIndex >= prices.size() || startIndex >= endIndex) {
+ return -1;
+ }
+
+ int highIndex = -1;
+ BigDecimal highPrice = BigDecimal.ZERO;
+
+ // 从右向左搜索,找到第一个有效高点
+ for (int i = endIndex; i >= startIndex; i--) {
+ if (prices.get(i).compareTo(highPrice) > 0) {
+ highPrice = prices.get(i);
+ highIndex = i;
+ }
+
+ // 检查高点后是否有回调
+ if (highIndex != -1 && i < endIndex) {
+ if (prices.get(i + 1).compareTo(highPrice) < 0) {
+ return highIndex; // 找到确认回调的高点
+ }
+ }
+ }
+
+ return highIndex;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MACDCalculator.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MACDCalculator.java
index a70869e..37c0ec0 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MACDCalculator.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MACDCalculator.java
@@ -85,7 +85,7 @@
result.add(data);
}
- System.out.println(result.get(result.size() -1));
+
return new MACDResult(result, startIdx);
}
@@ -123,14 +123,14 @@
}
// 找到最近的价格高点和对应的DIF值
- int recentPriceHighIdx = findRecentHighIndex(closePrices, startIdx);
- if (recentPriceHighIdx < startIdx + 2) {
+ int recentPriceHighIdx = IndicatorUtils.findRecentHighIndex(closePrices, startIdx);
+ if (recentPriceHighIdx < startIdx + 2 || recentPriceHighIdx == -1) {
return false;
}
// 找到之前的价格高点和对应的DIF值
- int previousPriceHighIdx = findPreviousHighIndex(closePrices, startIdx, recentPriceHighIdx);
- if (previousPriceHighIdx < startIdx) {
+ int previousPriceHighIdx = IndicatorUtils.findPreviousHighIndex(closePrices, startIdx, recentPriceHighIdx);
+ if (previousPriceHighIdx < startIdx || previousPriceHighIdx == -1) {
return false;
}
@@ -173,14 +173,14 @@
}
// 找到最近的价格低点和对应的DIF值
- int recentPriceLowIdx = findRecentLowIndex(closePrices, startIdx);
- if (recentPriceLowIdx < startIdx + 2) {
+ int recentPriceLowIdx = IndicatorUtils.findRecentLowIndex(closePrices, startIdx);
+ if (recentPriceLowIdx < startIdx + 2 || recentPriceLowIdx == -1) {
return false;
}
// 找到之前的价格低点和对应的DIF值
- int previousPriceLowIdx = findPreviousLowIndex(closePrices, startIdx, recentPriceLowIdx);
- if (previousPriceLowIdx < startIdx) {
+ int previousPriceLowIdx = IndicatorUtils.findPreviousLowIndex(closePrices, startIdx, recentPriceLowIdx);
+ if (previousPriceLowIdx < startIdx || previousPriceLowIdx == -1) {
return false;
}
@@ -202,95 +202,4 @@
return recentPrice.compareTo(previousPrice) < 0 &&
recentDif.compareTo(previousDif) > 0;
}
-
- /**
- * 找到最近的价格高点索引
- *
- * @param prices 价格列表
- * @param startIdx 起始索引
- * @return 最近的价格高点索引
- */
- private static int findRecentHighIndex(List<BigDecimal> prices, int startIdx) {
- int highIdx = startIdx;
- BigDecimal highPrice = prices.get(startIdx);
-
- for (int i = startIdx + 1; i < prices.size(); i++) {
- BigDecimal currentPrice = prices.get(i);
- if (currentPrice.compareTo(highPrice) > 0) {
- highPrice = currentPrice;
- highIdx = i;
- }
- }
-
- return highIdx;
- }
-
- /**
- * 找到最近的价格低点索引
- *
- * @param prices 价格列表
- * @param startIdx 起始索引
- * @return 最近的价格低点索引
- */
- private static int findRecentLowIndex(List<BigDecimal> prices, int startIdx) {
- int lowIdx = startIdx;
- BigDecimal lowPrice = prices.get(startIdx);
-
- for (int i = startIdx + 1; i < prices.size(); i++) {
- BigDecimal currentPrice = prices.get(i);
- if (currentPrice.compareTo(lowPrice) < 0) {
- lowPrice = currentPrice;
- lowIdx = i;
- }
- }
-
- return lowIdx;
- }
-
- /**
- * 找到最近价格高点之前的价格高点索引
- *
- * @param prices 价格列表
- * @param startIdx 起始索引
- * @param recentHighIdx 最近的价格高点索引
- * @return 之前的价格高点索引
- */
- private static int findPreviousHighIndex(List<BigDecimal> prices, int startIdx, int recentHighIdx) {
- int highIdx = startIdx;
- BigDecimal highPrice = prices.get(startIdx);
-
- for (int i = startIdx + 1; i < recentHighIdx; i++) {
- BigDecimal currentPrice = prices.get(i);
- if (currentPrice.compareTo(highPrice) > 0) {
- highPrice = currentPrice;
- highIdx = i;
- }
- }
-
- return highIdx;
- }
-
- /**
- * 找到最近价格低点之前的价格低点索引
- *
- * @param prices 价格列表
- * @param startIdx 起始索引
- * @param recentLowIdx 最近的价格低点索引
- * @return 之前的价格低点索引
- */
- private static int findPreviousLowIndex(List<BigDecimal> prices, int startIdx, int recentLowIdx) {
- int lowIdx = startIdx;
- BigDecimal lowPrice = prices.get(startIdx);
-
- for (int i = startIdx + 1; i < recentLowIdx; i++) {
- BigDecimal currentPrice = prices.get(i);
- if (currentPrice.compareTo(lowPrice) < 0) {
- lowPrice = currentPrice;
- lowIdx = i;
- }
- }
-
- return lowIdx;
- }
-}
-
+}
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MacdMaStrategy.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MacdMaStrategy.java
index be65136..fee2ab4 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MacdMaStrategy.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MacdMaStrategy.java
@@ -357,15 +357,13 @@
boolean isGoldenCross = prevPrev.getDif().compareTo(prevPrev.getDea()) <= 0 &&
previous.getDif().compareTo(previous.getDea()) > 0;
- boolean isUp = latest.getDif().compareTo(BigDecimal.ZERO) > 0 && latest.getDea().compareTo(BigDecimal.ZERO) > 0;
-
// 柱状线扩张判断:连续正值且绝对值增大
boolean isExpanding = latest.getMacdHist().compareTo(BigDecimal.ZERO) > 0 &&
previous.getMacdHist().compareTo(BigDecimal.ZERO) > 0 &&
previous.getMacdHist().abs().compareTo(latest.getMacdHist().abs()) < 0;
- // 金叉或柱状线扩张任一满足即可
- return isGoldenCross && isExpanding && isUp;
+ // 金叉柱状线扩张满足
+ return isGoldenCross && isExpanding;
}
/**
@@ -390,14 +388,14 @@
}
/**
- * MACD死叉且柱状线收缩检查
+ * MACD死叉且柱状线扩张检查
* <p>
* 条件:
* 1. DIF线从上往下穿过DEA线(死叉)
- * 2. MACD柱状线绝对值减小且为负值(动量减弱)
+ * 2. MACD柱状线绝对值增大且为负值(动量增强)
*
* @param macdResult MACD计算结果
- * @return 是否形成MACD死叉或柱状线收缩
+ * @return 是否形成MACD死叉且柱状线扩张
*/
private boolean isMacdDeathCrossAndContracting(MACDResult macdResult) {
List<PriceData> macdData = macdResult.getMacdData();
@@ -413,15 +411,13 @@
boolean isDeathCross = prevPrev.getDif().compareTo(prevPrev.getDea()) >= 0 &&
previous.getDif().compareTo(previous.getDea()) < 0;
- boolean isDown = latest.getDif().compareTo(BigDecimal.ZERO) < 0 && latest.getDea().compareTo(BigDecimal.ZERO) < 0;
-
// 优化后的死叉柱状线条件:空头趋势中,死叉应伴随柱状线扩张(绝对值增大)
boolean isExpanding = latest.getMacdHist().compareTo(BigDecimal.ZERO) < 0 &&
previous.getMacdHist().compareTo(BigDecimal.ZERO) < 0 &&
previous.getMacdHist().abs().compareTo(latest.getMacdHist().abs()) < 0;
- // 死叉或柱状线收缩任一满足即可
- return isDeathCross && isExpanding && isDown;
+ // 死叉且柱状线扩张
+ return isDeathCross && isExpanding;
}
/**
--
Gitblit v1.9.1