Administrator
2025-12-25 5a53a21e97311d239f57266d58d2d7b6f55c42a6
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
package com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy;
 
import com.xcong.excoin.modules.okxNewPrice.indicator.*;
import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.CoinEnums;
import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.OrderParamEnums;
import com.xcong.excoin.modules.okxNewPrice.okxWs.param.TradeRequestParam;
 
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);
    // 最新价格:用于策略判断
    private BigDecimal lastPrice;
    // 当前市场趋势:牛市/熊市
    private TrendDirection trend;
 
    /**
     * 策略执行的主入口方法
     *
     * 第一步:更新所有技术指标
     * 
     * @param prices 历史价格数据列表
     * @throws IllegalArgumentException 如果价格数据不足200个
     */
    public void execute(List<BigDecimal> prices) {
        // 验证输入数据完整性:策略需要至少200个价格数据点
        if (prices.size() < 200) {
            throw new IllegalArgumentException("至少需要200个价格数据点才能运行该策略");
        }
        updateIndicators(prices);
 
        trend = getTrend();
 
    }
 
    /**
     * 第二步:确定当前市场趋势
      */
    public TrendDirection getTrend(){
        return determineTrend();
    }
 
 
    /**
     * 第三步:检查是否需要跳过当前交易
     */
    public boolean getSkip(){
        return shouldSkipTrade();
    }
 
    /**
     * 第四步:是否允许开仓
     */
    public TradeRequestParam getOrderParamOpen(TradeRequestParam tradeRequestParam){
        return checkEntrySignal(tradeRequestParam);
    }
 
    /**
     * 第五步:是否允许平仓
     */
    public TradeRequestParam getOrderParamClose(TradeRequestParam tradeRequestParam){
        return checkExitSignal(tradeRequestParam);
    }
 
    /**
     * 更新所有技术指标的计算结果
     *
     * @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);
    }
 
 
    /**
     * 确定当前市场趋势(牛市/熊市)
     * 
     */
    private TrendDirection determineTrend() {
        BigDecimal currentMa100 = ma100.getMa(); // 获取当前100日移动平均线
        // 根据最新价格与100日MA的关系判断趋势:价格高于100日MA为牛市,否则为熊市
        return 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 TradeRequestParam checkEntrySignal(TradeRequestParam tradeRequestParam) {
        String poSide = null;
        String side = null;
        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) {
                poSide = CoinEnums.POSSIDE_LONG.getCode();
                side = CoinEnums.SIDE_BUY.getCode();
            }
        } else if (trend == TrendDirection.BEARISH){
            // 熊市入场条件增强:
            // 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) {
                poSide = CoinEnums.POSSIDE_SHORT.getCode();
                side = CoinEnums.SIDE_SELL.getCode();
            }
        }
        tradeRequestParam.setPosSide(poSide);
        tradeRequestParam.setSide(side);
        return tradeRequestParam;
    }
 
    /**
     * 检查是否满足离场信号条件
     */
    private TradeRequestParam checkExitSignal(TradeRequestParam tradeRequestParam) {
        BigDecimal currentMa30 = ma30.getMa(); // 获取当前30日移动平均线
 
        // 获取MACD的最新值用于判断动量变化
        BigDecimal currentDif = macd.getDif();
        BigDecimal currentDea = macd.getDea();
        String posSide = tradeRequestParam.getPosSide();
        if (posSide == CoinEnums.POSSIDE_LONG.getCode()) {
            // 多头持仓离场条件:
            // 1. MACD转为空头信号(DIF<DEA)
            // 2. 价格跌破30日MA
            boolean macdExit = currentDif.compareTo(currentDea) < 0;
            boolean priceExit = lastPrice.compareTo(currentMa30) < 0;
 
            if (macdExit && priceExit) {
                tradeRequestParam.setSide(CoinEnums.SIDE_SELL.getCode());
            }
        } else if (posSide == CoinEnums.POSSIDE_SHORT.getCode()){
            // 空头持仓离场条件:
            // 1. MACD转为多头信号(DIF>DEA)
            // 2. 价格突破30日MA
            boolean macdExit = currentDif.compareTo(currentDea) > 0;
            boolean priceExit = lastPrice.compareTo(currentMa30) > 0;
 
            if (macdExit && priceExit) {
                tradeRequestParam.setSide(CoinEnums.SIDE_BUY.getCode());
            }
        }
        return tradeRequestParam;
    }
 
 
    // 枚举定义
    enum PositionStatus { FLAT, LONG, SHORT }
    enum TrendDirection { BULLISH, BEARISH }
}