Administrator
2025-12-24 eaa37652d5f03e4b36cb53d9d3e2e13312af3d78
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
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.ArrayList;
import java.util.List;
 
/**
 * MACD (Moving Average Convergence Divergence) 指标实现
 * 计算逻辑:
 * 1. DIF = EMA(12) - EMA(26)
 * 2. DEA = EMA(DIF, 9)
 * 3. MACD柱状图 = (DIF - DEA) * 2
 * 
 * 作用:
 * 1. 识别趋势方向和动能变化
 * 2. DIF上穿DEA形成金叉,提示买入信号
 * 3. DIF下穿DEA形成死叉,提示卖出信号
 * 4. MACD柱状图由负转正,提示多头力量增强
 * 5. MACD柱状图由正转负,提示空头力量增强
 * 
 * 价格参数类型:
 * - 参数名称:prices
 * - 参数类型:List<BigDecimal>
 * - 参数说明:需要至少2个价格数据点用于计算,数据越多计算越准确
 * 
 * 推荐时间粒度及优缺点:
 * 1. 1分钟(1m):
 *    - 优点:反应迅速,适合超短线交易
 *    - 缺点:MACD柱状图波动剧烈,信号频繁
 * 2. 5分钟(5m):
 *    - 优点:平衡了反应速度和信号可靠性
 *    - 缺点:仍有一定噪音
 * 3. 15分钟(15m):
 *    - 优点:适合日内交易,信号较为可靠
 *    - 缺点:反应速度较慢
 * 4. 1小时(1h)及以上:
 *    - 优点:趋势信号明确,MACD柱状图变化稳定
 *    - 缺点:反应滞后,不适合短线交易
 */
@Slf4j
@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;
 
    // 动态周期参数
    private int fastPeriod;
    private int slowPeriod;
    private int signalPeriod;
 
    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;
 
    // 构造函数使用默认周期
    public MACD() {
        this.fastPeriod = DEFAULT_FAST_PERIOD;
        this.slowPeriod = DEFAULT_SLOW_PERIOD;
        this.signalPeriod = DEFAULT_SIGNAL_PERIOD;
    }
 
    /**
     * 计算MACD指标(使用当前周期设置)
     * @param prices 价格列表
     */
    public void calculate(List<BigDecimal> prices) {
        calculate(prices, null);
    }
 
    /**
     * 计算MACD指标,并支持动态周期调整
     * @param prices 价格列表
     * @param volatility 标准化波动率(百分比),用于动态调整周期
     */
    public void calculate(List<BigDecimal> prices, BigDecimal volatility) {
        if (prices == null || prices.size() < 2) {
            return;
        }
 
        // 如果提供了波动率,则动态调整周期
        if (volatility != null) {
            adjustPeriodsByVolatility(volatility);
        }
 
        // 计算快速EMA
        prevFastEMA = calculateEMA(prices, fastPeriod, prevFastEMA);
        
        // 计算慢速EMA
        prevSlowEMA = calculateEMA(prices, slowPeriod, prevSlowEMA);
        
        // 计算DIF
        dif = prevFastEMA.subtract(prevSlowEMA).setScale(8, RoundingMode.HALF_UP);
        
        // 计算DEA
        List<BigDecimal> difList = new ArrayList<>();
        difList.add(dif);
        prevDea = calculateEMA(difList, signalPeriod, prevDea);
        dea = prevDea.setScale(8, RoundingMode.HALF_UP);
        
        // 计算MACD柱状图
        macdBar = dif.subtract(dea).multiply(new BigDecimal(2)).setScale(8, RoundingMode.HALF_UP);
        
        log.info("MACD计算结果 - DIF: {}, DEA: {}, MACD柱状图: {}, 参数: fast={}, slow={}, signal={}",
                dif, dea, macdBar, fastPeriod, slowPeriod, signalPeriod);
    }
 
    /**
     * 根据波动率调整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;
        }
 
        log.info("根据波动率{}调整MACD周期: fast={}, slow={}, signal={}",
                volatility, fastPeriod, slowPeriod, signalPeriod);
    }
 
    /**
     * 判断金叉信号(DIF上穿DEA)
     * @return 是否形成金叉
     */
    public boolean isGoldenCross(BigDecimal previousDIF, BigDecimal previousDEA) {
        return previousDIF.compareTo(previousDEA) < 0 && dif.compareTo(dea) > 0;
    }
 
    /**
     * 判断死叉信号(DIF下穿DEA)
     * @return 是否形成死叉
     */
    public boolean isDeathCross(BigDecimal previousDIF, BigDecimal previousDEA) {
        return previousDIF.compareTo(previousDEA) > 0 && dif.compareTo(dea) < 0;
    }
}