Administrator
8 days ago 212e20fa6e9d0cc69d7f70339ecd47d4ec286533
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
package com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy;
 
import java.math.BigDecimal;
import java.util.List;
 
/**
 * 示例用法
 * // 计算MACD(获取包含起始索引的结果)
 * MACDCalculator.MACDResult macdResult = MACDCalculator.calculateMACD(closePrices, 12, 26, 9);
 *
 * // 检测做空信号
 * boolean bearishSignal = BearishSignalDetector.isBearishSignalFormed(macdResult, closePrices);
 */
public class BearishSignalDetector {
    /**
     * 判断是否形成做空信号
     *
     * @param macdResult 包含MACD数据和有效起始索引的结果对象
     * @param closePrices 收盘价列表
     * @return 是否形成信号
     */
    public static boolean isBearishSignalFormed(MACDResult macdResult, List<BigDecimal> closePrices) {
        List<PriceData> macdData = macdResult.getMacdData();
        int startIdx = macdResult.getStartIndex();
 
        // 校验数据长度(至少需要两个有效DIF点)
        if (macdData.size() < 2 || closePrices.size() < startIdx + macdData.size()) {
            return false;
        }
 
        int currentMacdIndex = macdData.size() - 1;
        int currentCloseIndex = startIdx + currentMacdIndex;
 
        // 1. 判断顶背离
        boolean isTopDivergence = isTopDivergence(macdData, closePrices, currentMacdIndex, startIdx);
 
        // 2. 判断死叉
        boolean isDeathCross = isDeathCross(macdData, currentMacdIndex);
 
        return isTopDivergence && isDeathCross;
    }
 
    /**
     * 判断顶背离(需确保earlierHigh在recentHigh之前)
     */
    private static boolean isTopDivergence(List<PriceData> macdData, List<BigDecimal> closePrices,
                                           int currentMacdIndex, int startIdx) {
        // 计算对应closePrices的索引
        int currentCloseIndex = startIdx + currentMacdIndex;
        if (currentCloseIndex < 10 || macdData.size() < 5) {
            return false;
        }
 
        // 寻找最近的价格高点(至少在当前点的前2根K线范围内)
        int recentHighCloseIndex = findRecentHighWithRetrace(closePrices, currentCloseIndex - 5, currentCloseIndex);
        if (recentHighCloseIndex == -1) {
            return false;
        }
 
        // 寻找更早的高点(确保在recentHigh之前)
        int earlierHighCloseIndex = findRecentHighWithRetrace(closePrices, startIdx, recentHighCloseIndex - 1);
        if (earlierHighCloseIndex == -1) {
            return false;
        }
 
        // 转换为macdData的索引
        int recentHighMacdIndex = recentHighCloseIndex - startIdx;
        int earlierHighMacdIndex = earlierHighCloseIndex - startIdx;
 
        // 判断价格创新高但DIF走低
        BigDecimal recentHighPrice = closePrices.get(recentHighCloseIndex);
        BigDecimal earlierHighPrice = closePrices.get(earlierHighCloseIndex);
        boolean isPriceHigher = recentHighPrice.compareTo(earlierHighPrice) > 0;
 
        BigDecimal recentDif = macdData.get(recentHighMacdIndex).getDif();
        BigDecimal earlierDif = macdData.get(earlierHighMacdIndex).getDif();
        boolean isDifLower = recentDif.compareTo(earlierDif) < 0;
 
        return isPriceHigher && isDifLower;
    }
 
    /**
     * 寻找有效的高点(高点后需有至少1根K线下跌)
     */
    private static int findRecentHighWithRetrace(List<BigDecimal> prices, int startIndex, int endIndex) {
        if (startIndex < 0 || endIndex >= prices.size() || startIndex >= endIndex) {
            return -1;
        }
 
        int highIndex = -1;
        BigDecimal highPrice = BigDecimal.ZERO;
 
        // 从右向左搜索,找到第一个有效高点
        for (int i = endIndex; i >= startIndex; i--) {
            if (prices.get(i).compareTo(highPrice) > 0) {
                highPrice = prices.get(i);
                highIndex = i;
            }
 
            // 检查高点后是否有回调
            if (highIndex != -1 && i < endIndex) {
                if (prices.get(i + 1).compareTo(highPrice) < 0) {
                    return highIndex; // 找到确认回调的高点
                }
            }
        }
 
        return highIndex;
    }
 
 
    /**
     * 判断是否形成死叉(DIF下穿DEA)
     *
     * @param macdData MACD计算结果数据列表
     * @param currentIndex 当前数据点索引
     * @return 是否形成死叉
     */
    private static boolean isDeathCross(List<PriceData> macdData, int currentIndex) {
        if (currentIndex < 1) {
            return false;
        }
 
        PriceData current = macdData.get(currentIndex);
        PriceData previous = macdData.get(currentIndex - 1);
 
        // 前一期DIF >= DEA,当前期DIF < DEA
        return previous.getDif().compareTo(previous.getDea()) >= 0 &&
                current.getDif().compareTo(current.getDea()) < 0;
    }
 
}