Administrator
8 days ago 17ad4d11755c11f63768b84932e42805388f91ee
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
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<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指标(使用当前周期设置)
     * @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.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<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 != 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();
 
    }
}