Administrator
2025-12-23 2ef04f9d1fba9e4a92508dd72fd8e7430c300f22
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
package com.xcong.excoin.modules.okxNewPrice.indicator;
 
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
 
import java.math.BigDecimal;
import java.util.List;
 
/**
 * MA (Moving Average) 指标实现
 * 支持不同周期的简单移动平均线(SMA)和指数移动平均线(EMA)
 */
@Slf4j
@Getter
@Setter
public class MA extends IndicatorBase {
 
    // 常用周期
    public static final int MA5 = 5;
    public static final int MA10 = 10;
    public static final int MA20 = 20;
    public static final int MA30 = 30;
    public static final int MA60 = 60;
 
    private BigDecimal ma5 = BigDecimal.ZERO;
    private BigDecimal ma10 = BigDecimal.ZERO;
    private BigDecimal ma20 = BigDecimal.ZERO;
    private BigDecimal ma30 = BigDecimal.ZERO;
    private BigDecimal ma60 = BigDecimal.ZERO;
 
    private BigDecimal ema5 = BigDecimal.ZERO;
    private BigDecimal ema10 = BigDecimal.ZERO;
    private BigDecimal ema20 = BigDecimal.ZERO;
    private BigDecimal ema30 = BigDecimal.ZERO;
    private BigDecimal ema60 = BigDecimal.ZERO;
 
    private BigDecimal prevEma5 = null;
    private BigDecimal prevEma10 = null;
    private BigDecimal prevEma20 = null;
    private BigDecimal prevEma30 = null;
    private BigDecimal prevEma60 = null;
 
    /**
     * 计算所有周期的MA指标
     * @param prices 价格列表
     */
    public void calculate(List<BigDecimal> prices) {
        if (prices == null || prices.size() < 1) {
            return;
        }
 
        // 计算SMA
        if (prices.size() >= MA5) {
            ma5 = calculateMA(prices, MA5);
        }
        if (prices.size() >= MA10) {
            ma10 = calculateMA(prices, MA10);
        }
        if (prices.size() >= MA20) {
            ma20 = calculateMA(prices, MA20);
        }
        if (prices.size() >= MA30) {
            ma30 = calculateMA(prices, MA30);
        }
        if (prices.size() >= MA60) {
            ma60 = calculateMA(prices, MA60);
        }
 
        // 计算EMA
        prevEma5 = calculateEMA(prices, MA5, prevEma5);
        ema5 = prevEma5;
        
        prevEma10 = calculateEMA(prices, MA10, prevEma10);
        ema10 = prevEma10;
        
        prevEma20 = calculateEMA(prices, MA20, prevEma20);
        ema20 = prevEma20;
        
        prevEma30 = calculateEMA(prices, MA30, prevEma30);
        ema30 = prevEma30;
        
        prevEma60 = calculateEMA(prices, MA60, prevEma60);
        ema60 = prevEma60;
 
        log.debug("MA计算结果 - MA5: {}, MA10: {}, MA20: {}, MA30: {}, MA60: {}", 
                ma5, ma10, ma20, ma30, ma60);
        log.debug("EMA计算结果 - EMA5: {}, EMA10: {}, EMA20: {}, EMA30: {}, EMA60: {}", 
                ema5, ema10, ema20, ema30, ema60);
    }
 
    /**
     * 判断短期均线是否上穿长期均线(金叉)
     * @param shortMA 短期均线
     * @param longMA 长期均线
     * @param prevShortMA 前一期短期均线
     * @param prevLongMA 前一期长期均线
     * @return 是否形成金叉
     */
    public boolean isGoldenCross(BigDecimal shortMA, BigDecimal longMA, 
                                BigDecimal prevShortMA, BigDecimal prevLongMA) {
        return prevShortMA != null && prevLongMA != null &&
                prevShortMA.compareTo(prevLongMA) < 0 && 
                shortMA.compareTo(longMA) > 0;
    }
 
    /**
     * 判断短期均线是否下穿长期均线(死叉)
     * @param shortMA 短期均线
     * @param longMA 长期均线
     * @param prevShortMA 前一期短期均线
     * @param prevLongMA 前一期长期均线
     * @return 是否形成死叉
     */
    public boolean isDeathCross(BigDecimal shortMA, BigDecimal longMA, 
                               BigDecimal prevShortMA, BigDecimal prevLongMA) {
        return prevShortMA != null && prevLongMA != null &&
                prevShortMA.compareTo(prevLongMA) > 0 && 
                shortMA.compareTo(longMA) < 0;
    }
 
    /**
     * 判断价格是否上穿均线
     * @param price 当前价格
     * @param ma 均线值
     * @param prevPrice 前一期价格
     * @param prevMA 前一期均线值
     * @return 是否上穿
     */
    public boolean isPriceCrossUp(BigDecimal price, BigDecimal ma, 
                                 BigDecimal prevPrice, BigDecimal prevMA) {
        return prevPrice != null && prevMA != null &&
                prevPrice.compareTo(prevMA) < 0 && 
                price.compareTo(ma) > 0;
    }
 
    /**
     * 判断价格是否下穿均线
     * @param price 当前价格
     * @param ma 均线值
     * @param prevPrice 前一期价格
     * @param prevMA 前一期均线值
     * @return 是否下穿
     */
    public boolean isPriceCrossDown(BigDecimal price, BigDecimal ma, 
                                   BigDecimal prevPrice, BigDecimal prevMA) {
        return prevPrice != null && prevMA != null &&
                prevPrice.compareTo(prevMA) > 0 && 
                price.compareTo(ma) < 0;
    }
}