package com.xcong.excoin.modules.okxNewPrice.indicator; import lombok.Getter; import lombok.Setter; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.ArrayList; import java.util.List; /** * MACD (Moving Average Convergence Divergence) 指标实现 * 计算逻辑: * 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) * - 作用:消除柱状图杂讯,使信号更加清晰 */ @Getter @Setter public class MACD extends IndicatorBase { // 默认周期参数 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 difHistory = new ArrayList<>(); // 保存历史DIF值,用于计算DEA private List 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指标(使用当前周期设置) * @param prices 价格列表 */ public void calculate(List prices) { calculate(prices, null); } /** * 计算MACD指标,并支持动态周期调整 * @param prices 价格列表 * @param volatility 标准化波动率(百分比),用于动态调整周期 */ public void calculate(List prices, BigDecimal volatility) { if (prices == null || prices.isEmpty()) { return; } // 如果提供了波动率,则动态调整周期 if (volatility != null) { adjustPeriodsByVolatility(volatility); } // 计算快速EMA (12日) prevFastEMA = calculateEMA(prices, fastPeriod, prevFastEMA); // 计算慢速EMA (26日) prevSlowEMA = calculateEMA(prices, slowPeriod, prevSlowEMA); // 计算DIF = EMA(12) - EMA(26) dif = prevFastEMA.subtract(prevSlowEMA).setScale(8, RoundingMode.HALF_UP); // 将新的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(); // 如果没有足够的历史数据,返回最新的原始值 if (historyCount < macdBarsSmoothingPeriod) { return rawMacdBarHistory.get(historyCount - 1); } // 使用简单移动平均平滑MACD柱状图 List 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 != 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 != 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(); } }