Administrator
2025-12-26 32331e187236646996590cecac7f23cf19272d7c
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
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
/**
 * MACD和MA组合交易策略实现类
 * 基于15分钟K线数据生成交易信号并确定持仓方向
 *
 * 该策略综合考虑了EMA指标、MACD指标、价格突破信号和波动率因素,
 * 形成了一套完整的开仓、平仓和持仓管理机制。
 */
package com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy;
 
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
 
/**
 * MACD和MA组合交易策略实现
 * <p>
 * 该策略利用EMA交叉、MACD指标、价格突破信号和波动率过滤,
 * 为15分钟K线级别交易提供综合决策支持。
 */
public class MacdMaStrategy {
 
    /** 持仓状态枚举 */
    public enum PositionType {
        /** 多头持仓 */
        LONG,
        /** 空头持仓 */
        SHORT,
        /** 空仓 */
        NONE
    }
 
    // 策略参数
    private int shortPeriod;      // 短期EMA周期
    private int longPeriod;       // 长期EMA周期
    private int signalPeriod;     // MACD信号线周期
    private int volatilityPeriod; // 波动率计算周期
    private BigDecimal stopLossRatio;   // 止损比例
    private BigDecimal takeProfitRatio; // 止盈比例
 
    // 持仓信息
    private PositionType currentPosition; // 当前持仓状态
    private BigDecimal entryPrice;       // 开仓价格
    private long entryTime;              // 开仓时间戳
 
    /**
     * 默认构造函数,使用标准MACD参数
     * 短期周期=12, 长期周期=26, 信号线周期=9, 波动率周期=20
     * 止损比例=1%, 止盈比例=2%
     */
    public MacdMaStrategy() {
        this(12, 26, 9, 20, new BigDecimal("0.01"), new BigDecimal("0.02"));
    }
 
    /**
     * 自定义参数构造函数
     *
     * @param shortPeriod 短期EMA周期
     * @param longPeriod 长期EMA周期
     * @param signalPeriod MACD信号线周期
     * @param volatilityPeriod 波动率计算周期
     * @param stopLossRatio 止损比例
     * @param takeProfitRatio 止盈比例
     */
    public MacdMaStrategy(int shortPeriod, int longPeriod, int signalPeriod, int volatilityPeriod,
                          BigDecimal stopLossRatio, BigDecimal takeProfitRatio) {
        this.shortPeriod = shortPeriod;
        this.longPeriod = longPeriod;
        this.signalPeriod = signalPeriod;
        this.volatilityPeriod = volatilityPeriod;
        this.stopLossRatio = stopLossRatio;
        this.takeProfitRatio = takeProfitRatio;
 
        // 初始化持仓状态为空仓
        this.currentPosition = PositionType.NONE;
        this.entryPrice = BigDecimal.ZERO;
        this.entryTime = 0;
    }
 
    /**
     * 分析最新价格数据并生成交易信号
     *
     * @param closePrices 收盘价序列
     * @return 生成的交易信号(LONG、SHORT或NONE)
     */
    public PositionType analyze(List<BigDecimal> closePrices) {
        // 数据检查:确保有足够的数据点进行计算
        if (closePrices == null || closePrices.size() < 34) {
            return PositionType.NONE; // 数据不足,无法生成信号
        }
 
        // 1. 计算MACD指标
        MACDResult macdResult = MACDCalculator.calculateMACD(
                closePrices, shortPeriod, longPeriod, signalPeriod);
 
        // 2. 计算波动率
        Volatility volatility = new Volatility(volatilityPeriod);
        for (int i = Math.max(0, closePrices.size() - volatilityPeriod);
             i < closePrices.size(); i++) {
            volatility.addPrice(closePrices.get(i));
        }
        volatility.calculate();
 
        // 最新收盘价
        BigDecimal latestPrice = closePrices.get(closePrices.size() - 1);
 
        // 3. 检查开仓条件
        if (currentPosition == PositionType.NONE) {
            // 多头开仓条件检查
            if (isLongEntryCondition(macdResult, closePrices, volatility.getValue())) {
                // 执行开多
                this.currentPosition = PositionType.LONG;
                this.entryPrice = latestPrice;
                this.entryTime = System.currentTimeMillis();
                return PositionType.LONG;
            }
 
            // 空头开仓条件检查
            if (isShortEntryCondition(macdResult, closePrices, volatility.getValue())) {
                // 执行开空
                this.currentPosition = PositionType.SHORT;
                this.entryPrice = latestPrice;
                this.entryTime = System.currentTimeMillis();
                return PositionType.SHORT;
            }
 
            // 无信号
            return PositionType.NONE;
        } else {
            // 4. 检查平仓条件
            if (shouldClosePosition(macdResult, closePrices, latestPrice)) {
                // 执行平仓
                PositionType closedPosition = currentPosition;
                this.currentPosition = PositionType.NONE;
                this.entryPrice = BigDecimal.ZERO;
                this.entryTime = 0;
                return PositionType.NONE; // 返回空仓信号表示平仓
            }
 
            // 保持当前持仓
            return currentPosition;
        }
    }
 
    /**
     * 交易指令类,封装side和posSide的组合
     */
    public static class TradingOrder {
        private String side;    // buy或sell
        private String posSide; // long或short
 
        public TradingOrder(String side, String posSide) {
            this.side = side;
            this.posSide = posSide;
        }
 
        public String getSide() {
            return side;
        }
 
        public String getPosSide() {
            return posSide;
        }
 
        @Override
        public String toString() {
            return String.format("TradingOrder{side='%s', posSide='%s'}", side, posSide);
        }
    }
 
    /**
     * 分析历史价格数据并生成交易指令
     *
     * @param historicalPrices 历史价格序列
     * @return 交易指令(包含side和posSide),如果没有交易信号则返回null
     */
    public TradingOrder generateTradingOrder(List<BigDecimal> historicalPrices) {
        PositionType signal = analyze(historicalPrices);
 
        // 根据信号和当前持仓状态生成交易指令
        if (signal == PositionType.LONG) {
            // 开多:买入开多(side 填写 buy; posSide 填写 long )
            return new TradingOrder("buy", "long");
        } else if (signal == PositionType.SHORT) {
            // 开空:卖出开空(side 填写 sell; posSide 填写 short )
            return new TradingOrder("sell", "short");
        } else if (signal == PositionType.NONE && currentPosition != PositionType.NONE) {
            // 平仓操作
            if (currentPosition == PositionType.LONG) {
                // 平多:卖出平多(side 填写 sell;posSide 填写 long )
                return new TradingOrder("sell", "long");
            } else if (currentPosition == PositionType.SHORT) {
                // 平空:买入平空(side 填写 buy; posSide 填写 short )
                return new TradingOrder("buy", "short");
            }
        }
 
        // 没有交易信号
        return null;
    }
 
    /**
     * 多头开仓条件检查
     *
     * @param macdResult MACD计算结果
     * @param closePrices 收盘价序列
     * @param volatility 当前波动率
     * @return 是否满足多头开仓条件
     */
    private boolean isLongEntryCondition(MACDResult macdResult, List<BigDecimal> closePrices,
                                         BigDecimal volatility) {
        // 1. EMA金叉检查(短期EMA > 长期EMA)
        boolean emaGoldenCross = isEmaGoldenCross(macdResult);
 
        // 2. MACD柱状线扩张+金叉检查
        boolean macdGoldenCross = isMacdGoldenCrossAndExpanding(macdResult);
 
        // 3. 价格突破前高检查
        boolean priceBreakout = BullishSignalDetector.isBullishSignalFormed(macdResult, closePrices);
 
        // 4. 波动率过滤检查(0.5% ~ 5%)
        boolean volatilityFilter = isVolatilityInRange(volatility);
 
        // 所有条件必须同时满足
        return emaGoldenCross && macdGoldenCross && priceBreakout && volatilityFilter;
    }
 
    /**
     * 空头开仓条件检查
     *
     * @param macdResult MACD计算结果
     * @param closePrices 收盘价序列
     * @param volatility 当前波动率
     * @return 是否满足空头开仓条件
     */
    private boolean isShortEntryCondition(MACDResult macdResult, List<BigDecimal> closePrices,
                                          BigDecimal volatility) {
        // 1. EMA死叉检查(短期EMA < 长期EMA)
        boolean emaDeathCross = isEmaDeathCross(macdResult);
 
        // 2. MACD柱状线收缩+死叉检查
        boolean macdDeathCross = isMacdDeathCrossAndContracting(macdResult);
 
        // 3. 价格跌破前低检查
        boolean priceBreakdown = BearishSignalDetector.isBearishSignalFormed(macdResult, closePrices);
 
        // 4. 波动率过滤检查(0.5% ~ 5%)
        boolean volatilityFilter = isVolatilityInRange(volatility);
 
        // 所有条件必须同时满足
        return emaDeathCross && macdDeathCross && priceBreakdown && volatilityFilter;
    }
 
    /**
     * 平仓条件检查
     *
     * @param macdResult MACD计算结果
     * @param closePrices 收盘价序列
     * @param currentPrice 当前价格
     * @return 是否应该平仓
     */
    private boolean shouldClosePosition(MACDResult macdResult, List<BigDecimal> closePrices,
                                        BigDecimal currentPrice) {
        // 1. 检查止损条件
        if (isStopLossTriggered(currentPrice)) {
            return true;
        }
 
        // 2. 检查止盈条件
        if (isTakeProfitTriggered(currentPrice)) {
            return true;
        }
 
        // 3. 检查MACD反向信号
        if (isMacdReversalSignal(macdResult)) {
            return true;
        }
 
        return false;
    }
 
    /**
     * EMA金叉检查
     *
     * @param macdResult MACD计算结果
     * @return 是否形成EMA金叉
     */
    private boolean isEmaGoldenCross(MACDResult macdResult) {
        List<PriceData> macdData = macdResult.getMacdData();
        if (macdData.size() < 2) {
            return false;
        }
 
        PriceData latest = macdData.get(macdData.size() - 1);
        PriceData previous = macdData.get(macdData.size() - 2);
 
        // 当前短期EMA > 当前长期EMA,并且前一期短期EMA <= 前一期长期EMA
        return latest.getEmaShort().compareTo(latest.getEmaLong()) > 0 &&
                previous.getEmaShort().compareTo(previous.getEmaLong()) <= 0;
    }
 
    /**
     * EMA死叉检查
     *
     * @param macdResult MACD计算结果
     * @return 是否形成EMA死叉
     */
    private boolean isEmaDeathCross(MACDResult macdResult) {
        List<PriceData> macdData = macdResult.getMacdData();
        if (macdData.size() < 2) {
            return false;
        }
 
        PriceData latest = macdData.get(macdData.size() - 1);
        PriceData previous = macdData.get(macdData.size() - 2);
 
        // 当前短期EMA < 当前长期EMA,并且前一期短期EMA >= 前一期长期EMA
        return latest.getEmaShort().compareTo(latest.getEmaLong()) < 0 &&
                previous.getEmaShort().compareTo(previous.getEmaLong()) >= 0;
    }
 
    /**
     * MACD金叉且柱状线扩张检查
     *
     * @param macdResult MACD计算结果
     * @return 是否形成MACD金叉且柱状线扩张
     */
    private boolean isMacdGoldenCrossAndExpanding(MACDResult macdResult) {
        List<PriceData> macdData = macdResult.getMacdData();
        if (macdData.size() < 3) {
            return false;
        }
 
        PriceData latest = macdData.get(macdData.size() - 1);
        PriceData previous = macdData.get(macdData.size() - 2);
        PriceData prevPrev = macdData.get(macdData.size() - 3);
 
        // 1. MACD金叉检查(DIF上穿DEA)
        boolean goldenCross = previous.getDif().compareTo(previous.getDea()) <= 0 &&
                latest.getDif().compareTo(latest.getDea()) > 0;
 
        // 2. MACD柱状线扩张检查
        boolean histogramExpanding = prevPrev.getMacdHist().compareTo(previous.getMacdHist()) <= 0 &&
                previous.getMacdHist().compareTo(latest.getMacdHist()) < 0 &&
                latest.getMacdHist().compareTo(BigDecimal.ZERO) > 0;
 
        return goldenCross && histogramExpanding;
    }
 
    /**
     * MACD死叉且柱状线收缩检查
     *
     * @param macdResult MACD计算结果
     * @return 是否形成MACD死叉且柱状线收缩
     */
    private boolean isMacdDeathCrossAndContracting(MACDResult macdResult) {
        List<PriceData> macdData = macdResult.getMacdData();
        if (macdData.size() < 3) {
            return false;
        }
 
        PriceData latest = macdData.get(macdData.size() - 1);
        PriceData previous = macdData.get(macdData.size() - 2);
        PriceData prevPrev = macdData.get(macdData.size() - 3);
 
        // 1. MACD死叉检查(DIF下穿DEA)
        boolean deathCross = previous.getDif().compareTo(previous.getDea()) >= 0 &&
                latest.getDif().compareTo(latest.getDea()) < 0;
 
        // 2. MACD柱状线收缩检查(绝对值减小)
        boolean histogramContracting = prevPrev.getMacdHist().abs().compareTo(
                previous.getMacdHist().abs()) >= 0 &&
                previous.getMacdHist().abs().compareTo(
                        latest.getMacdHist().abs()) > 0 &&
                latest.getMacdHist().compareTo(BigDecimal.ZERO) < 0;
 
        return deathCross && histogramContracting;
    }
 
    /**
     * 波动率过滤检查
     *
     * @param volatility 当前波动率
     * @return 波动率是否在0.5%~5%范围内
     */
    private boolean isVolatilityInRange(BigDecimal volatility) {
        BigDecimal minVolatility = new BigDecimal("0.5");
        BigDecimal maxVolatility = new BigDecimal("5.0");
 
        return volatility.compareTo(minVolatility) >= 0 &&
                volatility.compareTo(maxVolatility) <= 0;
    }
 
    /**
     * 止损触发检查
     *
     * @param currentPrice 当前价格
     * @return 是否触发止损
     */
    private boolean isStopLossTriggered(BigDecimal currentPrice) {
        if (entryPrice.compareTo(BigDecimal.ZERO) == 0) {
            return false;
        }
 
        if (currentPosition == PositionType.LONG) {
            // 多头持仓:价格下跌超过止损比例
            BigDecimal stopLossPrice = entryPrice.multiply(
                    BigDecimal.ONE.subtract(stopLossRatio));
            return currentPrice.compareTo(stopLossPrice) < 0;
        } else if (currentPosition == PositionType.SHORT) {
            // 空头持仓:价格上涨超过止损比例
            BigDecimal stopLossPrice = entryPrice.multiply(
                    BigDecimal.ONE.add(stopLossRatio));
            return currentPrice.compareTo(stopLossPrice) > 0;
        }
 
        return false;
    }
 
    /**
     * 止盈触发检查
     *
     * @param currentPrice 当前价格
     * @return 是否触发止盈
     */
    private boolean isTakeProfitTriggered(BigDecimal currentPrice) {
        if (entryPrice.compareTo(BigDecimal.ZERO) == 0) {
            return false;
        }
 
        if (currentPosition == PositionType.LONG) {
            // 多头持仓:价格上涨超过止盈比例
            BigDecimal takeProfitPrice = entryPrice.multiply(
                    BigDecimal.ONE.add(takeProfitRatio));
            return currentPrice.compareTo(takeProfitPrice) > 0;
        } else if (currentPosition == PositionType.SHORT) {
            // 空头持仓:价格下跌超过止盈比例
            BigDecimal takeProfitPrice = entryPrice.multiply(
                    BigDecimal.ONE.subtract(takeProfitRatio));
            return currentPrice.compareTo(takeProfitPrice) < 0;
        }
 
        return false;
    }
 
    /**
     * MACD反向信号检查
     *
     * @param macdResult MACD计算结果
     * @return 是否出现MACD反向信号
     */
    private boolean isMacdReversalSignal(MACDResult macdResult) {
        if (currentPosition == PositionType.LONG) {
            // 多头持仓:检查MACD死叉信号
            return isMacdDeathCrossAndContracting(macdResult);
        } else if (currentPosition == PositionType.SHORT) {
            // 空头持仓:检查MACD金叉信号
            return isMacdGoldenCrossAndExpanding(macdResult);
        }
 
        return false;
    }
 
    /**
     * 获取当前持仓状态
     *
     * @return 当前持仓状态
     */
    public PositionType getCurrentPosition() {
        return currentPosition;
    }
 
    /**
     * 获取开仓价格
     *
     * @return 开仓价格
     */
    public BigDecimal getEntryPrice() {
        return entryPrice;
    }
 
    /**
     * 获取开仓时间戳
     *
     * @return 开仓时间戳
     */
    public long getEntryTime() {
        return entryTime;
    }
 
    /**
     * 重置策略状态(清空持仓)
     */
    public void reset() {
        this.currentPosition = PositionType.NONE;
        this.entryPrice = BigDecimal.ZERO;
        this.entryTime = 0;
    }
 
    /**
     * 示例主方法,展示如何使用MACD和MA组合交易策略
     *
     * @param args 命令行参数(未使用)
     */
    public static void main(String[] args) {
        // 创建策略实例(使用默认参数)
        MacdMaStrategy strategy = new MacdMaStrategy();
 
        // 示例:模拟历史价格数据(实际应用中应从数据源获取)
        List<BigDecimal> historicalPrices = new ArrayList<>();
 
        // 生成一些示例价格数据(这里仅作演示)
        // 实际应用中应替换为真实的历史K线数据
        for (int i = 0; i < 50; i++) {
            // 模拟价格数据(示例中使用简单递增的价格)
            historicalPrices.add(new BigDecimal("100.00").add(new BigDecimal(i * 0.5)));
        }
 
        // 模拟实时价格流处理
        System.out.println("===== MACD和MA组合交易策略示例 =====");
        System.out.println("开始处理价格数据并生成交易信号...");
 
        // 模拟实时数据流(假设我们有更多的价格数据)
        for (int i = 0; i < 20; i++) {
            // 添加新的价格数据(这里仅作演示)
            BigDecimal newPrice = new BigDecimal("125.00").add(new BigDecimal(i * 0.2));
            historicalPrices.add(newPrice);
 
            // 使用策略分析最新价格数据并生成交易指令
            TradingOrder order = strategy.generateTradingOrder(historicalPrices);
 
            // 输出交易信号和指令
            System.out.printf("价格: %s, 当前持仓: %s, 交易指令: %s\n",
                    newPrice.setScale(2, RoundingMode.HALF_UP),
                    strategy.getCurrentPosition().name(),
                    order != null ? order.toString() : "无交易指令");
 
            // 示例:在实际应用中,你可能需要根据指令执行交易操作
            if (order != null) {
                if (order.getSide().equals("buy") && order.getPosSide().equals("long")) {
                    System.out.println("[交易操作] 买入开多");
                } else if (order.getSide().equals("sell") && order.getPosSide().equals("short")) {
                    System.out.println("[交易操作] 卖出开空");
                } else if (order.getSide().equals("sell") && order.getPosSide().equals("long")) {
                    System.out.println("[交易操作] 卖出平多");
                } else if (order.getSide().equals("buy") && order.getPosSide().equals("short")) {
                    System.out.println("[交易操作] 买入平空");
                }
            }
        }
 
        // 打印策略最终状态
        System.out.println("\n===== 策略最终状态 =====");
        System.out.println("当前持仓: " + strategy.getCurrentPosition().name());
        System.out.println("开仓价格: " + strategy.getEntryPrice());
        System.out.println("开仓时间戳: " + strategy.getEntryTime());
    }
}