Administrator
2025-12-29 d802aae20f21f05e15c8ba8e9e2148fc7b6a0028
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
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;
 
/**
 * 15分钟时间粒度的交易策略实现
 * 专门针对100个15分钟数据点设计的策略,包含明确的多空方向选择和开仓平仓方法
 */
@Slf4j
public class FifteenMinuteTradingStrategy {
 
    @Getter
    @Setter
    public static class TradingResult {
        private Direction direction;      // 多空方向
        private PositionSignal signal;    // 开仓平仓信号
        private String indicatorStatus;   // 指标状态
        
        public TradingResult(Direction direction, PositionSignal signal, String indicatorStatus) {
            this.direction = direction;
            this.signal = signal;
            this.indicatorStatus = indicatorStatus;
        }
    }
 
    public enum Direction {
        LONG,    // 多头方向
        SHORT,   // 空头方向
        RANGING  // 震荡行情
    }
 
    public enum PositionSignal {
        OPEN_LONG,    // 开多仓
        OPEN_SHORT,   // 开空仓
        CLOSE_LONG,   // 平多仓
        CLOSE_SHORT,  // 平空仓
        HOLD,         // 持有
        STAY_OUT      // 观望
    }
 
    private final MA ma;
    private final AdvancedMA advancedMA;
    private final BOLL boll;
    private final KDJ kdj;
    private final MACD macd;
    private final RSI rsi;
 
    public FifteenMinuteTradingStrategy() {
        // 15分钟数据优化的参数配置
        this.ma = new MA();
        this.advancedMA = new AdvancedMA();
        this.boll = new BOLL(20, 2.0);  // BOLL使用默认20周期
        this.kdj = new KDJ(9);          // KDJ使用默认9周期
        this.macd = new MACD();         // MACD使用默认12/26/9周期
        this.rsi = new RSI(14);         // RSI使用默认14周期
    }
 
    /**
     * 计算所有指标
     * @param prices 15分钟价格数据(至少100个数据点)
     */
    private void calculateIndicators(List<BigDecimal> prices) {
        ma.calculate(prices);
        advancedMA.calculateTripleEMA(prices);
        boll.calculate(prices);
        kdj.calculate(prices);
        macd.calculate(prices);
        rsi.calculate(prices);
    }
 
    /**
     * 判断市场是否处于震荡行情
     * @return 是否为震荡行情
     */
    private boolean isRangeMarket() {
        // AdvancedMA三线粘合 + RSI(40-60) + BOLL带宽收窄
        boolean isMaConverged = advancedMA.calculatePercent().compareTo(new BigDecimal(2)) < 0;
        boolean isRsiNeutral = rsi.getRsi().compareTo(new BigDecimal(40)) > 0 && 
                              rsi.getRsi().compareTo(new BigDecimal(60)) < 0;
        boolean isBollNarrow = boll.calculateBandWidth().compareTo(new BigDecimal(0.05)) < 0;
 
        return isMaConverged && isRsiNeutral && isBollNarrow;
    }
 
    /**
     * 获取多空方向选择
     * @param prices 15分钟价格数据(100个数据点)
     * @return 多空方向
     */
    public Direction getDirection(List<BigDecimal> prices) {
        if (prices == null || prices.size() < 100) {
            throw new IllegalArgumentException("需要至少100个15分钟价格数据点");
        }
 
        calculateIndicators(prices);
 
        // 震荡过滤
        if (isRangeMarket()) {
            return Direction.RANGING;
        }
 
        BigDecimal currentPrice = prices.get(prices.size() - 1);
 
        // 多头信号判断:MA多头排列 + MACD金叉 + RSI(30-70) + BOLL价格在上轨与中轨之间
        boolean isLongSignal = 
            ma.getEma5().compareTo(ma.getEma10()) > 0 &&  // MA5 > MA10
            ma.getEma10().compareTo(ma.getEma20()) > 0 && // MA10 > MA20
            macd.getDif().compareTo(macd.getDea()) > 0 && // MACD金叉
            rsi.getRsi().compareTo(new BigDecimal(30)) > 0 && rsi.getRsi().compareTo(new BigDecimal(70)) < 0 && // RSI(30-70)
            currentPrice.compareTo(boll.getMid()) > 0 && currentPrice.compareTo(boll.getUpper()) < 0; // BOLL价格在上轨与中轨之间
 
        // 空头信号判断:MA空头排列 + MACD死叉 + RSI(30-70) + BOLL价格在下轨与中轨之间
        boolean isShortSignal = 
            ma.getEma5().compareTo(ma.getEma10()) < 0 &&  // MA5 < MA10
            ma.getEma10().compareTo(ma.getEma20()) < 0 && // MA10 < MA20
            macd.getDif().compareTo(macd.getDea()) < 0 && // MACD死叉
            rsi.getRsi().compareTo(new BigDecimal(30)) > 0 && rsi.getRsi().compareTo(new BigDecimal(70)) < 0 && // RSI(30-70)
            currentPrice.compareTo(boll.getMid()) < 0 && currentPrice.compareTo(boll.getLower()) > 0; // BOLL价格在下轨与中轨之间
 
        if (isLongSignal) {
            return Direction.LONG;
        } else if (isShortSignal) {
            return Direction.SHORT;
        } else {
            return Direction.RANGING;
        }
    }
 
    /**
     * 获取开仓平仓策略信号
     * @param prices 15分钟价格数据(100个数据点)
     * @param hasLongPosition 当前是否持有多仓
     * @param hasShortPosition 当前是否持有空仓
     * @return 开仓平仓信号
     */
    public PositionSignal getPositionSignal(List<BigDecimal> prices, boolean hasLongPosition, boolean hasShortPosition) {
        if (prices == null || prices.size() < 100) {
            throw new IllegalArgumentException("需要至少100个15分钟价格数据点");
        }
 
        calculateIndicators(prices);
 
        // 震荡过滤
        if (isRangeMarket()) {
            return PositionSignal.STAY_OUT;
        }
 
        BigDecimal currentPrice = prices.get(prices.size() - 1);
 
        // 开多信号:MA金叉 + MACD金叉 + KDJ金叉 + RSI中性 + 价格在BOLL中轨上方
        boolean shouldOpenLong = 
            ma.getEma5().compareTo(ma.getEma20()) > 0 &&  // MA金叉(5日EMA上穿20日EMA)
            macd.getDif().compareTo(macd.getDea()) > 0 && macd.getMacdBar().compareTo(BigDecimal.ZERO) > 0 && // MACD金叉且柱状图为正
            kdj.isGoldenCross() && // KDJ金叉
            rsi.getRsi().compareTo(new BigDecimal(30)) > 0 && rsi.getRsi().compareTo(new BigDecimal(70)) < 0 && // RSI中性
            currentPrice.compareTo(boll.getMid()) > 0; // 价格在BOLL中轨上方
 
        // 开空信号:MA死叉 + MACD死叉 + KDJ死叉 + RSI中性 + 价格在BOLL中轨下方
        boolean shouldOpenShort = 
            ma.getEma5().compareTo(ma.getEma20()) < 0 &&  // MA死叉(5日EMA下穿20日EMA)
            macd.getDif().compareTo(macd.getDea()) < 0 && macd.getMacdBar().compareTo(BigDecimal.ZERO) < 0 && // MACD死叉且柱状图为负
            kdj.isDeathCross() && // KDJ死叉
            rsi.getRsi().compareTo(new BigDecimal(30)) > 0 && rsi.getRsi().compareTo(new BigDecimal(70)) < 0 && // RSI中性
            currentPrice.compareTo(boll.getMid()) < 0; // 价格在BOLL中轨下方
 
        // 平多信号:MA死叉 + MACD死叉 + RSI超买 + 价格跌破BOLL中轨
        boolean shouldCloseLong = 
            (ma.getEma5().compareTo(ma.getEma20()) < 0 && // MA死叉
            macd.getDif().compareTo(macd.getDea()) < 0 && // MACD死叉
            (rsi.isOverbought() || rsi.isExtremelyOverbought())) || // RSI超买
            currentPrice.compareTo(boll.getMid()) < 0; // 价格跌破BOLL中轨
 
        // 平空信号:MA金叉 + MACD金叉 + RSI超卖 + 价格突破BOLL中轨
        boolean shouldCloseShort = 
            (ma.getEma5().compareTo(ma.getEma20()) > 0 && // MA金叉
            macd.getDif().compareTo(macd.getDea()) > 0 && // MACD金叉
            (rsi.isOversold() || rsi.isExtremelyOversold())) || // RSI超卖
            currentPrice.compareTo(boll.getMid()) > 0; // 价格突破BOLL中轨
 
        // 确定开仓信号
        if (shouldOpenLong && !hasLongPosition && !hasShortPosition) {
            return PositionSignal.OPEN_LONG;
        } else if (shouldOpenShort && !hasLongPosition && !hasShortPosition) {
            return PositionSignal.OPEN_SHORT;
        }
 
        // 确定平仓信号
        if (shouldCloseLong && hasLongPosition) {
            return PositionSignal.CLOSE_LONG;
        } else if (shouldCloseShort && hasShortPosition) {
            return PositionSignal.CLOSE_SHORT;
        }
 
        // 无信号
        return hasLongPosition || hasShortPosition ? PositionSignal.HOLD : PositionSignal.STAY_OUT;
    }
 
    /**
     * 综合获取交易结果
     * @param prices 15分钟价格数据(100个数据点)
     * @param hasLongPosition 当前是否持有多仓
     * @param hasShortPosition 当前是否持有空仓
     * @return 包含多空方向和开仓平仓信号的完整交易结果
     */
    public TradingResult getTradingResult(List<BigDecimal> prices, boolean hasLongPosition, boolean hasShortPosition) {
        Direction direction = getDirection(prices);
        PositionSignal signal = getPositionSignal(prices, hasLongPosition, hasShortPosition);
        String indicatorStatus = getIndicatorStatus();
        
        return new TradingResult(direction, signal, indicatorStatus);
    }
 
    /**
     * 获取当前指标状态
     * @return 指标状态字符串
     */
    private String getIndicatorStatus() {
        return String.format("MA5: %s, MA20: %s, " +
                        "MACD-DIF: %s, MACD-DEA: %s, MACD-BAR: %s, " +
                        "KDJ-K: %s, KDJ-D: %s, KDJ-J: %s, " +
                        "RSI: %s, " +
                        "BOLL-UP: %s, BOLL-MID: %s, BOLL-DN: %s, " +
                        "AdvancedMA-Bullish: %s, Bearish: %s, Percent: %s",
                ma.getEma5(), ma.getEma20(),
                macd.getDif(), macd.getDea(), macd.getMacdBar(),
                kdj.getK(), kdj.getD(), kdj.getJ(),
                rsi.getRsi(),
                boll.getUpper(), boll.getMid(), boll.getLower(),
                advancedMA.isBullish(), advancedMA.isBearish(), advancedMA.calculatePercent());
    }
 
}