Administrator
2025-12-24 81e84c3bb307e2a786d383fad7e14c0c2b4d897e
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
package com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy;
 
import com.xcong.excoin.modules.okxNewPrice.indicator.*;
import java.math.BigDecimal;
import java.util.List;
 
/**
 * MACD+MA复合交易策略实现类
 * 
 * 【策略核心思想】
 * 结合移动平均线(MA)的趋势判断能力和MACD指标的动量分析能力,构建一个兼顾趋势跟踪和入场时机的复合交易策略。
 * 
 * 【策略主要特点】
 * 1. **趋势导向**:以长期MA(100日)作为ETH市场趋势的主要判断依据
 * 2. **精确入场**:利用MACD指标的动量变化和短期MA(30日)的支撑/阻力作用确定最佳入场点
 * 3. **风险控制**:通过RSI和波动率指标过滤掉风险较高的交易信号,并设置明确的止损止盈
 * 4. **分层离场**:结合技术指标、移动平均线和风险控制构建多重离场机制
 * 5. **动态调整**:MACD周期根据市场波动率自动调整,适应ETH高波动特性
 * 
 * 【核心交易逻辑】
 * 1. **趋势判断**:当前价格高于长期MA(100日)判定为牛市,低于则为熊市
 * 2. **入场条件**:
 *    - 牛市:MACD多头信号(DIF>DEA) 且 价格>短期MA(30日) 且 MACD柱状图>0 且 RSI在合理区间
 *    - 熊市:MACD空头信号(DIF<DEA) 且 价格<短期MA(30日) 且 MACD柱状图<0 且 RSI在合理区间
 * 3. **离场条件**:
 *    - 牛市多头持仓:MACD空头信号(DIF<DEA) 或 价格跌破短期MA 或 触及止损/止盈
 *    - 熊市空头持仓:MACD多头信号(DIF>DEA) 或 价格突破短期MA 或 触及止损/止盈
 * 4. **过滤条件**:
 *    - 高风险过滤:RSI>65时不追多,RSI<35时不追空
 *    - 低波动过滤:波动率<0.5%或>5%时不进行交易
 * 
 * 【适用场景】
 * 专为ETH合约设计,适用于ETH高波动、24/7交易的市场环境,适合中短线趋势交易。
 * 
 * 【风险提示】
 * 1. 策略需要至少200个价格数据点才能有效运行
 * 2. 在极端市场条件下(如黑天鹅事件)可能会产生较大亏损
 * 3. 建议结合其他风险控制手段(如止损设置)使用
 */
public class MacdMaStrategy {
    /**
     * 策略使用的技术指标实例(适配ETH合约特点)
     */
    // MACD指标:用于判断价格动量和趋势变化
    private final MACD macd = new MACD();
    // 30日移动平均线:ETH波动较大,使用更短的周期捕捉趋势变化
    private final MovingAverage ma30 = new MovingAverage(30);
    // 100日移动平均线:ETH作为高波动资产,长期趋势判断使用更短周期
    private final MovingAverage ma100 = new MovingAverage(100);
    // RSI指标(10周期):ETH波动快,使用更短周期提高响应速度
    private final RSI rsi = new RSI(10);
    // 波动率指标(15周期):ETH波动频繁,使用更短周期捕捉市场变化
    private final Volatility volatility = new Volatility(15);
 
    /**
     * 策略配置参数(适配ETH合约特点)
     */
    // 止损比例(ETH波动较大,设置2.5%)
    private final BigDecimal stopLossRatio = new BigDecimal("0.025");
    // 止盈比例(ETH趋势明显,设置6%)
    private final BigDecimal takeProfitRatio = new BigDecimal("0.06");
    // 账户初始余额(USD)
    private BigDecimal accountBalance = new BigDecimal("10000");
    // 每次交易风险比例(ETH合约风险较高,设置0.8%)
    private final BigDecimal riskPerTrade = new BigDecimal("0.008");
    // 杠杆倍数(ETH合约建议使用3-5倍,这里使用4倍)
    private final int leverage = 4;
    
    /**
     * 策略运行状态变量
     */
    // 当前持仓状态:空仓/多头/空头
    private PositionStatus position = PositionStatus.FLAT;
    // 最新价格:用于策略判断
    private BigDecimal lastPrice;
    // 当前市场趋势:牛市/熊市
    private TrendDirection trend;
    // 持仓均价:用于计算盈亏和止损止盈
    private BigDecimal entryPrice;
    // 止损价格:根据止损比例计算
    private BigDecimal stopLossPrice;
    // 止盈价格:根据止盈比例计算
    private BigDecimal takeProfitPrice;
    // 当前持仓数量(ETH)
    private BigDecimal positionSize = BigDecimal.ZERO;
    // 已用保证金(USD)
    private BigDecimal usedMargin = BigDecimal.ZERO;
 
    /**
     * 策略执行的主入口方法
     * 
     * @param prices 历史价格数据列表
     * @throws IllegalArgumentException 如果价格数据不足200个
     */
    public void execute(List<BigDecimal> prices) {
        // 验证输入数据完整性:策略需要至少200个价格数据点
        if (prices.size() < 200) {
            throw new IllegalArgumentException("至少需要200个价格数据点才能运行该策略");
        }
 
        // 第一步:更新所有技术指标
        updateIndicators(prices);
 
        // 第二步:确定当前市场趋势
        determineTrend(prices);
 
        // 第三步:检查是否需要跳过当前交易
        if (shouldSkipTrade()) {
            return;
        }
 
        // 第四步:根据持仓状态执行相应的交易逻辑
        if (position == PositionStatus.FLAT) {
            // 空仓状态下检查入场信号
            checkEntrySignal();
        } else {
            // 持仓状态下检查离场信号
            checkExitSignal();
        }
    }
 
    /**
     * 更新所有技术指标的计算结果
     * 
     * @param prices 历史价格数据列表
     */
    private void updateIndicators(List<BigDecimal> prices) {
        // 先计算波动率指标,因为MACD需要用它来动态调整周期
        volatility.calculate(prices);
        // 计算MACD指标并传入波动率参数,实现动态周期调整
        macd.calculate(prices, volatility.getValue());
        // 计算移动平均线指标
        ma30.calculate(prices);      // 计算30日移动平均线
        ma100.calculate(prices);     // 计算100日移动平均线
        // 计算RSI指标
        rsi.calculate(prices);
        // 更新最新价格
        lastPrice = prices.get(prices.size()-1);
    }
 
    /**
     * 确定当前市场趋势(牛市/熊市)
     * 
     * @param prices 历史价格数据列表
     */
    private void determineTrend(List<BigDecimal> prices) {
        BigDecimal currentMa100 = ma100.getMa(); // 获取当前100日移动平均线
        // 根据最新价格与100日MA的关系判断趋势:价格高于100日MA为牛市,否则为熊市
        trend = lastPrice.compareTo(currentMa100) > 0 ?
                TrendDirection.BULLISH : TrendDirection.BEARISH;
    }
 
    /**
     * 检查是否需要跳过当前交易
     * 
     * @return true表示需要跳过交易,false表示可以进行交易
     */
    private boolean shouldSkipTrade() {
        // 波动率过滤:当波动率小于1%时,市场活跃度不足,跳过交易
        if (volatility.getValue().compareTo(new BigDecimal("0.01")) < 0) {
            return true;
        }
 
        // RSI极端值过滤:
        // 1. 牛市中RSI>65表示超买,避免追高
        // 2. 熊市中RSI<35表示超卖,避免追空
        if (trend == TrendDirection.BULLISH && rsi.getRsi().compareTo(new BigDecimal(65)) > 0) {
            return true;
        }
        if (trend == TrendDirection.BEARISH && rsi.getRsi().compareTo(new BigDecimal(35)) < 0) {
            return true;
        }
        return false;
    }
 
    /**
     * 检查是否满足入场信号条件
     */
    private void checkEntrySignal() {
        BigDecimal currentMa30 = ma30.getMa(); // 获取当前30日移动平均线
 
        // 获取MACD的最新值用于判断动量方向
        BigDecimal currentDif = macd.getDif();
        BigDecimal currentDea = macd.getDea();
        BigDecimal macdBar = macd.getMacdBar(); // 获取MACD柱状图值
        
        // 获取RSI的最新值
        BigDecimal currentRsi = rsi.getRsi();
        // 获取波动率的最新值
        BigDecimal currentVolatility = volatility.getValue();
 
        // 根据市场趋势判断入场条件
        if (trend == TrendDirection.BULLISH) {
            // 牛市入场条件增强:
            // 1. MACD多头信号(DIF>DEA)
            // 2. MACD柱状图为正(确认多头力量)
            // 3. 价格在30日MA之上且有一定偏离(避免假突破)
            // 4. RSI处于合理区间(40-65),避免在超买区域入场
            // 5. 波动率适中(>0.5%且<5%),避免极端波动环境
            boolean macdBull = currentDif.compareTo(currentDea) > 0;
            boolean macdBarPositive = macdBar.compareTo(BigDecimal.ZERO) > 0;
            BigDecimal priceMaDiff = lastPrice.subtract(currentMa30);
            boolean priceAboveMAWithStrength = priceMaDiff.compareTo(currentMa30.multiply(new BigDecimal("0.005"))) > 0;
            boolean rsiInRange = currentRsi.compareTo(new BigDecimal(40)) > 0 && currentRsi.compareTo(new BigDecimal(65)) < 0;
            boolean volatilityModerate = currentVolatility.compareTo(new BigDecimal("0.005")) > 0 && 
                                         currentVolatility.compareTo(new BigDecimal("0.05")) < 0;
            
            if (macdBull && macdBarPositive && priceAboveMAWithStrength && rsiInRange && volatilityModerate) {
                enterLong();
            }
        } else {
            // 熊市入场条件增强:
            // 1. MACD空头信号(DIF<DEA)
            // 2. MACD柱状图为负(确认空头力量)
            // 3. 价格在30日MA之下且有一定偏离(避免假突破)
            // 4. RSI处于合理区间(35-60),避免在超卖区域入场
            // 5. 波动率适中(>0.5%且<5%),避免极端波动环境
            boolean macdBear = currentDif.compareTo(currentDea) < 0;
            boolean macdBarNegative = macdBar.compareTo(BigDecimal.ZERO) < 0;
            BigDecimal priceMaDiff = currentMa30.subtract(lastPrice);
            boolean priceBelowMAWithStrength = priceMaDiff.compareTo(currentMa30.multiply(new BigDecimal("0.005"))) > 0;
            boolean rsiInRange = currentRsi.compareTo(new BigDecimal(35)) > 0 && currentRsi.compareTo(new BigDecimal(60)) < 0;
            boolean volatilityModerate = currentVolatility.compareTo(new BigDecimal("0.005")) > 0 && 
                                         currentVolatility.compareTo(new BigDecimal("0.05")) < 0;
            
            if (macdBear && macdBarNegative && priceBelowMAWithStrength && rsiInRange && volatilityModerate) {
                enterShort();
            }
        }
    }
 
    /**
     * 检查是否满足离场信号条件
     */
    private void checkExitSignal() {
        BigDecimal currentMa30 = ma30.getMa(); // 获取当前30日移动平均线
 
        // 获取MACD的最新值用于判断动量变化
        BigDecimal currentDif = macd.getDif();
        BigDecimal currentDea = macd.getDea();
 
        if (position == PositionStatus.LONG) {
            // 多头持仓离场条件:
            // 1. MACD转为空头信号(DIF<DEA)
            // 2. 价格跌破30日MA
            // 3. 价格触及止损价格
            // 4. 价格触及止盈价格
            boolean macdExit = currentDif.compareTo(currentDea) < 0;
            boolean priceExit = lastPrice.compareTo(currentMa30) < 0;
            boolean stopLossExit = lastPrice.compareTo(stopLossPrice) <= 0;
            boolean takeProfitExit = lastPrice.compareTo(takeProfitPrice) >= 0;
            
            if (macdExit || priceExit || stopLossExit || takeProfitExit) {
                if (stopLossExit) {
                    System.out.println("触发止损 - ");
                } else if (takeProfitExit) {
                    System.out.println("触发止盈 - ");
                }
                exitPosition();
            }
        } else {
            // 空头持仓离场条件:
            // 1. MACD转为多头信号(DIF>DEA)
            // 2. 价格突破30日MA
            // 3. 价格触及止损价格
            // 4. 价格触及止盈价格
            boolean macdExit = currentDif.compareTo(currentDea) > 0;
            boolean priceExit = lastPrice.compareTo(currentMa30) > 0;
            boolean stopLossExit = lastPrice.compareTo(stopLossPrice) >= 0;
            boolean takeProfitExit = lastPrice.compareTo(takeProfitPrice) <= 0;
            
            if (macdExit || priceExit || stopLossExit || takeProfitExit) {
                if (stopLossExit) {
                    System.out.println("触发止损 - ");
                } else if (takeProfitExit) {
                    System.out.println("触发止盈 - ");
                }
                exitPosition();
            }
        }
    }
 
    /**
     * 计算基于风险的仓位大小
     * 
     * @return 计算得到的仓位大小(ETH数量)
     */
    private BigDecimal calculatePositionSize() {
        // 计算每次交易可承受的最大风险金额
        BigDecimal maxRiskAmount = accountBalance.multiply(riskPerTrade);
        // 计算单合约风险金额(价格波动 * 数量)
        BigDecimal priceRisk = entryPrice.subtract(stopLossPrice).abs();
        // 计算基础仓位大小(不考虑杠杆)
        BigDecimal basePositionSize = maxRiskAmount.divide(priceRisk, 6, BigDecimal.ROUND_HALF_UP);
        // 应用杠杆计算实际仓位大小
        BigDecimal leveragedPositionSize = basePositionSize.multiply(new BigDecimal(leverage))
                                                           .setScale(4, BigDecimal.ROUND_DOWN);
        return leveragedPositionSize;
    }
 
    /**
     * 执行开多仓操作
     */
    private void enterLong() {
        position = PositionStatus.LONG; // 更新持仓状态为多头
        entryPrice = lastPrice; // 记录入场价格
        // 多头止损价格 = 入场价格 * (1 - 止损比例)
        stopLossPrice = entryPrice.multiply(BigDecimal.ONE.subtract(stopLossRatio))
                                  .setScale(2, BigDecimal.ROUND_HALF_UP);
        // 多头止盈价格 = 入场价格 * (1 + 止盈比例)
        takeProfitPrice = entryPrice.multiply(BigDecimal.ONE.add(takeProfitRatio))
                                    .setScale(2, BigDecimal.ROUND_HALF_UP);
        
        // 计算仓位大小
        positionSize = calculatePositionSize();
        // 计算已用保证金(不考虑手续费)
        usedMargin = positionSize.multiply(entryPrice).divide(new BigDecimal(leverage), 2, BigDecimal.ROUND_HALF_UP);
        
        // 实际交易中,这里会执行买入操作
        System.out.println("开多仓 @ " + lastPrice + ", 止损价格: " + stopLossPrice + ", 止盈价格: " + takeProfitPrice);
        System.out.println("仓位大小: " + positionSize + " ETH, 已用保证金: " + usedMargin + " USD, 账户余额: " + accountBalance + " USD");
    }
 
    /**
     * 执行开空仓操作
     */
    private void enterShort() {
        position = PositionStatus.SHORT; // 更新持仓状态为空头
        entryPrice = lastPrice; // 记录入场价格
        // 空头止损价格 = 入场价格 * (1 + 止损比例)
        stopLossPrice = entryPrice.multiply(BigDecimal.ONE.add(stopLossRatio))
                                  .setScale(2, BigDecimal.ROUND_HALF_UP);
        // 空头止盈价格 = 入场价格 * (1 - 止盈比例)
        takeProfitPrice = entryPrice.multiply(BigDecimal.ONE.subtract(takeProfitRatio))
                                    .setScale(2, BigDecimal.ROUND_HALF_UP);
        
        // 计算仓位大小
        positionSize = calculatePositionSize();
        // 计算已用保证金(不考虑手续费)
        usedMargin = positionSize.multiply(entryPrice).divide(new BigDecimal(leverage), 2, BigDecimal.ROUND_HALF_UP);
        
        // 实际交易中,这里会执行卖出操作
        System.out.println("开空仓 @ " + lastPrice + ", 止损价格: " + stopLossPrice + ", 止盈价格: " + takeProfitPrice);
        System.out.println("仓位大小: " + positionSize + " ETH, 已用保证金: " + usedMargin + " USD, 账户余额: " + accountBalance + " USD");
    }
 
    /**
     * 执行平仓操作
     */
    private void exitPosition() {
        // 计算交易盈亏
        BigDecimal profitLoss;
        if (position == PositionStatus.LONG) {
            // 多头盈亏 = (平仓价格 - 入场价格) * 持仓数量
            profitLoss = lastPrice.subtract(entryPrice).multiply(positionSize);
        } else {
            // 空头盈亏 = (入场价格 - 平仓价格) * 持仓数量
            profitLoss = entryPrice.subtract(lastPrice).multiply(positionSize);
        }
        
        // 更新账户余额
        accountBalance = accountBalance.add(profitLoss).setScale(2, BigDecimal.ROUND_HALF_UP);
        
        // 实际交易中,这里会执行平仓操作
        System.out.println("平仓 @ " + lastPrice + ", 盈亏: " + profitLoss.setScale(2, BigDecimal.ROUND_HALF_UP) + " USD");
        System.out.println("最新账户余额: " + accountBalance + " USD");
        
        // 重置持仓状态
        position = PositionStatus.FLAT;
        positionSize = BigDecimal.ZERO;
        usedMargin = BigDecimal.ZERO;
    }
 
    // 枚举定义
    enum PositionStatus { FLAT, LONG, SHORT }
    enum TrendDirection { BULLISH, BEARISH }
}