From a9402bbb84f6a521822fc8beb32056d0b2d4201f Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Fri, 26 Dec 2025 18:08:07 +0800
Subject: [PATCH] refactor(indicator): 重构MACD策略实现并优化交易逻辑
---
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MACD.java | 209 ++++++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 185 insertions(+), 24 deletions(-)
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MACD.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MACD.java
index 92c91b9..9b63ee2 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MACD.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MACD.java
@@ -2,7 +2,6 @@
import lombok.Getter;
import lombok.Setter;
-import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.math.RoundingMode;
@@ -12,69 +11,231 @@
/**
* MACD (Moving Average Convergence Divergence) 指标实现
* 计算逻辑:
- * 1. DIF = EMA(12) - EMA(26)
- * 2. DEA = EMA(DIF, 9)
- * 3. MACD柱状图 = (DIF - DEA) * 2
+ * 1. 快线DIFF = EMA(Close, 12) - EMA(Close, 26)
+ * 2. 慢线DEA = EMA(DIFF, 9)
+ * 3. MACD柱状图 = (DIFF - DEA) * macdBarsMultiplier
+ *
+ * 核心概念:
+ * 1. DIFF是EMA12与EMA26之间的距离,反映短期与长期趋势的差异
+ * 2. 当DIFF > 0表示EMA12在EMA26上方,代表多头趋势
+ * 3. 当DIFF < 0表示EMA12在EMA26下方,代表空头趋势
+ * 4. DEA是DIFF的EMA平滑线,用于过滤DIFF的波动
+ * 5. MACD柱状图通过放大倍数展示DIFF与DEA之间的关系,便于观察趋势变化
+ *
+ * 多空判断:
+ * - 多头机会:DIFF在0轴上且MACD柱状图向上,股价同步上涨
+ * - 空头机会:DIFF在0轴下且MACD柱状图向下,股价同步下跌
+ *
+ * 信号过滤:
+ * - 可通过设置macdBarsSmoothingPeriod启用MACD柱状图平滑处理
+ * - 平滑公式:MACD柱状图 = MA((DIFF - DEA) * macdBarsMultiplier, macdBarsSmoothingPeriod)
+ * - 作用:消除柱状图杂讯,使信号更加清晰
*/
-@Slf4j
@Getter
@Setter
public class MACD extends IndicatorBase {
- private static final int FAST_PERIOD = 12;
- private static final int SLOW_PERIOD = 26;
- private static final int SIGNAL_PERIOD = 9;
+ // 默认周期参数
+ public static final int DEFAULT_FAST_PERIOD = 12;
+ public static final int DEFAULT_SLOW_PERIOD = 26;
+ public static final int DEFAULT_SIGNAL_PERIOD = 9;
+
+ // 默认MACD柱状图放大倍数
+ public static final int DEFAULT_MACDBARS_MULTIPLIER = 2;
+ // 默认MACD柱状图平滑周期(0表示不平滑)
+ public static final int DEFAULT_MACDBARS_SMOOTHING_PERIOD = 0;
+ // 周期参数
+ private int fastPeriod;
+ private int slowPeriod;
+ private int signalPeriod;
+
+ // MACD柱状图参数
+ private int macdBarsMultiplier; // MACD柱状图放大倍数
+ private int macdBarsSmoothingPeriod; // MACD柱状图平滑周期(0表示不平滑)
+
+ // MACD计算结果
private BigDecimal dif = BigDecimal.ZERO;
private BigDecimal dea = BigDecimal.ZERO;
private BigDecimal macdBar = BigDecimal.ZERO;
+
+ // 历史值缓存
private BigDecimal prevFastEMA = null;
private BigDecimal prevSlowEMA = null;
private BigDecimal prevDea = null;
+ private List<BigDecimal> difHistory = new ArrayList<>(); // 保存历史DIF值,用于计算DEA
+ private List<BigDecimal> rawMacdBarHistory = new ArrayList<>(); // 保存原始MACD柱状图值,用于平滑处理
+
+ // 最大保存的历史值数量
+ private static final int MAX_HISTORY_SIZE = 50;
+
+ // 构造函数使用默认参数
+ public MACD() {
+ this.fastPeriod = DEFAULT_FAST_PERIOD;
+ this.slowPeriod = DEFAULT_SLOW_PERIOD;
+ this.signalPeriod = DEFAULT_SIGNAL_PERIOD;
+ this.macdBarsMultiplier = DEFAULT_MACDBARS_MULTIPLIER;
+ this.macdBarsSmoothingPeriod = DEFAULT_MACDBARS_SMOOTHING_PERIOD;
+ }
/**
- * 计算MACD指标
+ * 计算MACD指标(使用当前周期设置)
* @param prices 价格列表
*/
public void calculate(List<BigDecimal> prices) {
- if (prices == null || prices.size() < 2) {
+ calculate(prices, null);
+ }
+
+ /**
+ * 计算MACD指标,并支持动态周期调整
+ * @param prices 价格列表
+ * @param volatility 标准化波动率(百分比),用于动态调整周期
+ */
+ public void calculate(List<BigDecimal> prices, BigDecimal volatility) {
+ if (prices == null || prices.isEmpty()) {
return;
}
- // 计算快速EMA
- prevFastEMA = calculateEMA(prices, FAST_PERIOD, prevFastEMA);
+ // 如果提供了波动率,则动态调整周期
+ if (volatility != null) {
+ adjustPeriodsByVolatility(volatility);
+ }
+
- // 计算慢速EMA
- prevSlowEMA = calculateEMA(prices, SLOW_PERIOD, prevSlowEMA);
+
+ // 计算快速EMA (12日)
+ prevFastEMA = calculateEMA(prices, fastPeriod, prevFastEMA);
- // 计算DIF
+ // 计算慢速EMA (26日)
+ prevSlowEMA = calculateEMA(prices, slowPeriod, prevSlowEMA);
+
+ // 计算DIF = EMA(12) - EMA(26)
dif = prevFastEMA.subtract(prevSlowEMA).setScale(8, RoundingMode.HALF_UP);
- // 计算DEA
- List<BigDecimal> difList = new ArrayList<>();
- difList.add(dif);
- prevDea = calculateEMA(difList, SIGNAL_PERIOD, prevDea);
+ // 将新的DIF值添加到历史记录
+ difHistory.add(dif);
+ // 保持历史记录在合理范围内
+ if (difHistory.size() > MAX_HISTORY_SIZE) {
+ difHistory.remove(0);
+ }
+
+ // 计算DEA = EMA(DIFF, 9)
+ calculateDEA();
+
+ // 计算原始MACD柱状图值 = (DIF - DEA) * 放大倍数
+ BigDecimal rawMacdBar = dif.subtract(dea).multiply(new BigDecimal(macdBarsMultiplier)).setScale(8, RoundingMode.HALF_UP);
+
+ // 将原始MACD柱状图值添加到历史记录
+ rawMacdBarHistory.add(rawMacdBar);
+ // 保持历史记录在合理范围内
+ if (rawMacdBarHistory.size() > MAX_HISTORY_SIZE) {
+ rawMacdBarHistory.remove(0);
+ }
+
+ // 如果启用了平滑处理,则计算平滑后的MACD柱状图
+ if (macdBarsSmoothingPeriod > 0) {
+ macdBar = smoothMacdBars().setScale(8, RoundingMode.HALF_UP);
+ } else {
+ macdBar = rawMacdBar;
+ }
+
+
+ }
+
+ /**
+ * 计算DEA指标
+ */
+ private void calculateDEA() {
+ int difCount = difHistory.size();
+
+ // 如果没有足够的DIF历史值,无法计算有效的DEA
+ if (difCount == 0) {
+ dea = BigDecimal.ZERO;
+ prevDea = null;
+ return;
+ }
+
+ // 计算DEA = EMA(DIFF, signalPeriod)
+ // 使用所有DIF历史值来计算初始EMA,然后使用最新值更新
+ prevDea = calculateEMA(difHistory, signalPeriod, prevDea);
dea = prevDea.setScale(8, RoundingMode.HALF_UP);
+ }
+
+ /**
+ * 平滑MACD柱状图
+ * @return 平滑后的MACD柱状图值
+ */
+ private BigDecimal smoothMacdBars() {
+ int historyCount = rawMacdBarHistory.size();
- // 计算MACD柱状图
- macdBar = dif.subtract(dea).multiply(new BigDecimal(2)).setScale(8, RoundingMode.HALF_UP);
+ // 如果没有足够的历史数据,返回最新的原始值
+ if (historyCount < macdBarsSmoothingPeriod) {
+ return rawMacdBarHistory.get(historyCount - 1);
+ }
- log.debug("MACD计算结果 - DIF: {}, DEA: {}, MACD柱状图: {}", dif, dea, macdBar);
+ // 使用简单移动平均平滑MACD柱状图
+ List<BigDecimal> recentMacdBars = rawMacdBarHistory.subList(historyCount - macdBarsSmoothingPeriod, historyCount);
+ return calculateMA(recentMacdBars, macdBarsSmoothingPeriod);
+ }
+
+ /**
+ * 根据波动率调整MACD周期参数
+ * @param volatility 标准化波动率(百分比)
+ */
+ private void adjustPeriodsByVolatility(BigDecimal volatility) {
+ // 波动率阈值
+ BigDecimal volatilityThreshold = new BigDecimal(15);
+
+ // 根据波动率调整MACD参数
+ if (volatility.compareTo(volatilityThreshold) < 0) {
+ // 低波动率环境,使用默认参数
+ fastPeriod = DEFAULT_FAST_PERIOD;
+ slowPeriod = DEFAULT_SLOW_PERIOD;
+ signalPeriod = DEFAULT_SIGNAL_PERIOD;
+ } else {
+ // 高波动率环境,使用更灵敏的参数
+ fastPeriod = 8;
+ slowPeriod = 17;
+ signalPeriod = 5;
+ }
+
+
}
/**
* 判断金叉信号(DIF上穿DEA)
+ * @param previousDIF 上一个DIF值
+ * @param previousDEA 上一个DEA值
* @return 是否形成金叉
*/
public boolean isGoldenCross(BigDecimal previousDIF, BigDecimal previousDEA) {
- return previousDIF.compareTo(previousDEA) < 0 && dif.compareTo(dea) > 0;
+ return previousDIF != null && previousDEA != null &&
+ previousDIF.compareTo(previousDEA) < 0 && dif.compareTo(dea) > 0;
}
/**
* 判断死叉信号(DIF下穿DEA)
+ * @param previousDIF 上一个DIF值
+ * @param previousDEA 上一个DEA值
* @return 是否形成死叉
*/
public boolean isDeathCross(BigDecimal previousDIF, BigDecimal previousDEA) {
- return previousDIF.compareTo(previousDEA) > 0 && dif.compareTo(dea) < 0;
+ return previousDIF != null && previousDEA != null &&
+ previousDIF.compareTo(previousDEA) > 0 && dif.compareTo(dea) < 0;
+ }
+
+ /**
+ * 重置MACD指标状态
+ */
+ public void reset() {
+ dif = BigDecimal.ZERO;
+ dea = BigDecimal.ZERO;
+ macdBar = BigDecimal.ZERO;
+ prevFastEMA = null;
+ prevSlowEMA = null;
+ prevDea = null;
+ difHistory.clear();
+ rawMacdBarHistory.clear();
+
}
}
--
Gitblit v1.9.1