| MacdMaStrategy_Analysis.md | ●●●●● patch | view | raw | blame | history | |
| src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxKlineWebSocketClient.java | ●●●●● patch | view | raw | blame | history | |
| src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoServiceImpl.java | ●●●●● patch | view | raw | blame | history | |
| src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MacdMaStrategy.java | ●●●●● patch | view | raw | blame | history |
MacdMaStrategy_Analysis.md
New file @@ -0,0 +1,224 @@ # MacdMaStrategy.java 类逻辑梳理 ## 1. 类概述 MacdMaStrategy.java是一个专为ETH合约设计的MACD+MA复合交易策略实现类,结合了移动平均线(MA)的趋势判断能力和MACD指标的动量分析能力,通过RSI和波动率指标进行风险控制和信号过滤。 ## 2. 核心组成部分 ### 2.1 技术指标实例 类中初始化了以下技术指标: ```java // 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); ``` ### 2.2 状态变量 ```java // 最新价格:用于策略判断 private BigDecimal lastPrice; // 当前市场趋势:牛市/熊市 private TrendDirection trend; ``` ### 2.3 枚举类型 定义了两个内部枚举类型用于状态表示: ```java enum PositionStatus { FLAT, LONG, SHORT } // 持仓状态:空仓/多头/空头 enum TrendDirection { BULLISH, BEARISH } // 趋势方向:牛市/熊市 ``` ## 3. 策略执行流程 ### 3.1 主执行入口 ```java public void execute(List<BigDecimal> prices) { // 验证输入数据完整性:策略需要至少200个价格数据点 if (prices.size() < 200) { throw new IllegalArgumentException("至少需要200个价格数据点才能运行该策略"); } updateIndicators(prices); // 更新所有技术指标 getTrend(); // 确定当前市场趋势 } ``` ### 3.2 指标更新 ```java 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); } ``` ### 3.3 趋势判断 ```java public TrendDirection getTrend() { return determineTrend(); } private TrendDirection determineTrend() { BigDecimal currentMa100 = ma100.getMa(); // 获取当前100日移动平均线 // 根据最新价格与100日MA的关系判断趋势:价格高于100日MA为牛市,否则为熊市 return lastPrice.compareTo(currentMa100) > 0 ? TrendDirection.BULLISH : TrendDirection.BEARISH; } ``` ## 4. 交易信号生成 ### 4.1 入场信号检查 ```java public TradeRequestParam getOrderParamOpen(TradeRequestParam tradeRequestParam) { return checkEntrySignal(tradeRequestParam); } private TradeRequestParam checkEntrySignal(TradeRequestParam tradeRequestParam) { String poSide = null; BigDecimal currentMa30 = ma30.getMa(); BigDecimal currentDif = macd.getDif(); BigDecimal currentDea = macd.getDea(); BigDecimal macdBar = macd.getMacdBar(); BigDecimal currentRsi = rsi.getRsi(); BigDecimal currentVolatility = volatility.getValue(); if (trend == TrendDirection.BULLISH) { // 牛市入场条件:MACD多头信号、MACD柱状图为正、价格在30日MA之上、RSI合理、波动率适中 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(); } } else if (trend == TrendDirection.BEARISH) { // 熊市入场条件:MACD空头信号、MACD柱状图为负、价格在30日MA之下、RSI合理、波动率适中 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(); } } tradeRequestParam.setPosSide(poSide); return tradeRequestParam; } ``` ### 4.2 离场信号检查 ```java public TradeRequestParam getOrderParamClose(TradeRequestParam tradeRequestParam) { return checkExitSignal(tradeRequestParam); } private TradeRequestParam checkExitSignal(TradeRequestParam tradeRequestParam) { BigDecimal currentMa30 = ma30.getMa(); BigDecimal currentDif = macd.getDif(); BigDecimal currentDea = macd.getDea(); String posSide = tradeRequestParam.getPosSide(); if (posSide == CoinEnums.POSSIDE_LONG.getCode()) { // 多头持仓离场条件:MACD转为空头信号且价格跌破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()) { // 空头持仓离场条件:MACD转为多头信号且价格突破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; } ``` ## 5. 交易过滤机制 ### 5.1 交易跳过检查 ```java public boolean getSkip() { return shouldSkipTrade(); } 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; } ``` ## 6. 策略核心特点 1. **动态参数调整**:MACD周期根据市场波动率自动调整,适应ETH高波动特性 2. **多重指标验证**:结合MACD、MA、RSI和波动率指标进行综合判断 3. **严格的风险控制**:通过波动率过滤和RSI极端值过滤降低交易风险 4. **精确的入场时机**:要求价格在MA之上/之下有一定偏离,避免假突破 5. **明确的离场条件**:MACD信号反转且价格突破MA时离场,确保及时止盈止损 ## 7. 代码优化建议 1. **完善日志记录**:在关键决策点添加日志记录,便于策略调试和分析 2. **增加止盈止损机制**:当前代码缺少明确的止盈止损价格设置和判断 3. **仓位管理**:建议添加基于风险的仓位计算逻辑 4. **策略参数外部化**:将策略参数(如周期、阈值)配置为外部参数,便于调整 5. **结果返回优化**:execute方法可以返回策略执行结果,方便调用方获取交易信号 ## 8. 总结 MacdMaStrategy.java实现了一个完整的MACD+MA复合交易策略,专为ETH合约设计。该策略通过结合多种技术指标,实现了趋势判断、入场时机选择和风险控制的功能。策略执行流程清晰,从指标更新到趋势判断,再到交易信号生成和过滤,形成了一个完整的交易决策系统。 该策略的核心优势在于动态参数调整和多重指标验证,能够较好地适应ETH高波动的市场特性,同时通过严格的过滤机制降低交易风险。 src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxKlineWebSocketClient.java
@@ -7,6 +7,7 @@ import com.alibaba.fastjson.JSONObject; import com.xcong.excoin.modules.okxNewPrice.celue.CaoZuoService; import com.xcong.excoin.modules.okxNewPrice.indicator.TradingStrategy; import com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy.MacdMaStrategy; import com.xcong.excoin.modules.okxNewPrice.okxWs.*; import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.CoinEnums; import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.OrderParamEnums; @@ -256,10 +257,156 @@ log.debug("收到pong响应"); cancelPongTimeout(); } else { processPushData(response); // processPushData(response); processPushDataV2(response); } } catch (Exception e) { log.error("处理WebSocket消息失败: {}", message, e); } } /** * 解析并处理价格推送数据。 * 将最新的标记价格存入 Redis 并触发后续业务逻辑比较处理。 * 当价格变化时,调用CaoZuoService的caoZuo方法,触发所有账号的量化操作 * * @param response 包含价格数据的 JSON 对象 */ private void processPushDataV2(JSONObject response) { try { /** * { * "arg": { * "channel": "candle1D", * "instId": "BTC-USDT" * }, * "data": [ * [ * "1629993600000", * "42500", * "48199.9", * "41006.1", * "41006.1", * "3587.41204591", * "166741046.22583129", * "166741046.22583129", * "0" * ] * ] * } */ JSONObject arg = response.getJSONObject("arg"); if (arg == null) { log.warn("{}: 无效的推送数据,缺少 'arg' 字段", response); return; } String channel = arg.getString("channel"); if (channel == null) { log.warn("{}: 无效的推送数据,缺少 'channel' 字段", response); return; } String instId = arg.getString("instId"); if (instId == null) { log.warn("{}: 无效的推送数据,缺少 'instId' 字段", response); return; } if (CHANNEL.equals(channel) && CoinEnums.HE_YUE.getCode().equals(instId)) { JSONArray dataArray = response.getJSONArray("data"); if (dataArray == null || dataArray.isEmpty()) { log.warn("K线频道数据为空"); return; } JSONArray data = dataArray.getJSONArray(0); BigDecimal openPx = new BigDecimal(data.getString(1)); BigDecimal highPx = new BigDecimal(data.getString(2)); BigDecimal lowPx = new BigDecimal(data.getString(3)); BigDecimal closePx = new BigDecimal(data.getString(4)); BigDecimal vol = new BigDecimal(data.getString(5)); /** * K线状态 * 0:K线未完结 * 1:K线已完结 */ String confirm = data.getString(8); if ("1".equals(confirm)){ //调用策略 // 创建策略实例 MacdMaStrategy strategy = new MacdMaStrategy(); // 生成100个15分钟价格数据点 List<Kline> kline15MinuteData = getKlineDataByInstIdAndBar(instId, "1D"); List<BigDecimal> fiveMinPrices = kline15MinuteData.stream() .map(Kline::getC) .collect(Collectors.toList()); try { // 执行策略 strategy.execute(fiveMinPrices); boolean skip = strategy.getSkip(); if (skip){ log.info("跳过"); return; } System.out.println("策略初始化成功!"); } catch (Exception e) { System.err.println("策略初始化失败:" + e.getMessage()); e.printStackTrace(); } Collection<OkxQuantWebSocketClient> allClients = clientManager.getAllClients(); //如果为空,则直接返回 if (allClients.isEmpty()) { return; } // 获取所有OkxQuantWebSocketClient实例 for (OkxQuantWebSocketClient client : clientManager.getAllClients()) { String accountName = client.getAccountName(); if (accountName != null) { TradeRequestParam tradeRequestParam = new TradeRequestParam(); tradeRequestParam = strategy.getOrderParamOpen(tradeRequestParam); String posSide = tradeRequestParam.getPosSide(); String side = tradeRequestParam.getSide(); BigDecimal posHold = PositionsWs.getAccountMap(PositionsWs.initAccountName(accountName, posSide)).get("pos"); if (posHold.compareTo(BigDecimal.ZERO) > 0){ tradeRequestParam = strategy.getOrderParamClose(tradeRequestParam); } String currentPrice = String.valueOf(closePx); tradeRequestParam = caoZuoService.caoZuoStrategy(accountName, currentPrice, posSide); String clOrdId = WsParamBuild.getOrderNum(side); tradeRequestParam.setClOrdId(clOrdId); String sz = null; if (posSide == CoinEnums.POSSIDE_LONG.getCode()){ if (side == CoinEnums.SIDE_BUY.getCode()){ sz = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.BUY_CNT_INIT.name()); }else if (side == CoinEnums.SIDE_SELL.getCode()){ BigDecimal pos = PositionsWs.getAccountMap(PositionsWs.initAccountName(accountName, CoinEnums.POSSIDE_LONG.getCode())).get("pos"); if (BigDecimal.ZERO.compareTo( pos) >= 0) { tradeRequestParam.setTradeType(OrderParamEnums.TRADE_NO.getValue()); } sz = String.valueOf( pos); } }else if (posSide == CoinEnums.POSSIDE_SHORT.getCode()){ if (side == CoinEnums.SIDE_BUY.getCode()){ BigDecimal pos = PositionsWs.getAccountMap(PositionsWs.initAccountName(accountName, CoinEnums.POSSIDE_SHORT.getCode())).get("pos"); if (BigDecimal.ZERO.compareTo( pos) >= 0) { tradeRequestParam.setTradeType(OrderParamEnums.TRADE_NO.getValue()); } sz = String.valueOf( pos); }else if (side == CoinEnums.SIDE_SELL.getCode()){ sz = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.BUY_CNT_INIT.name()); } } tradeRequestParam.setSz(sz); TradeOrderWs.orderEvent(client.getWebSocketClient(), tradeRequestParam); } } } } } catch (Exception e) { log.error("处理 K线频道推送数据失败", e); } } @@ -498,7 +645,7 @@ LinkedHashMap<String, Object> requestParam = new LinkedHashMap<>(); requestParam.put("instId", instId); requestParam.put("bar", bar); requestParam.put("limit", "100"); requestParam.put("limit", "200"); String result = ExchangeLoginService.getInstance(ExchangeInfoEnum.OKX_UAT.name()).lineHistory(requestParam); log.info("加载OKX-KLINE,{}", result); src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoServiceImpl.java
@@ -43,6 +43,7 @@ public TradeRequestParam caoZuoStrategy(String accountName, String markPx, String posSide) { TradeRequestParam tradeRequestParam = new TradeRequestParam(); tradeRequestParam.setAccountName(accountName); tradeRequestParam.setMarkPx(markPx); tradeRequestParam.setInstId(CoinEnums.HE_YUE.getCode()); tradeRequestParam.setTdMode(CoinEnums.CROSS.getCode()); tradeRequestParam.setPosSide(posSide); src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MacdMaStrategy.java
@@ -1,6 +1,10 @@ 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; @@ -51,43 +55,15 @@ 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个 @@ -97,31 +73,44 @@ if (prices.size() < 200) { throw new IllegalArgumentException("至少需要200个价格数据点才能运行该策略"); } // 第一步:更新所有技术指标 updateIndicators(prices); // 第二步:确定当前市场趋势 determineTrend(prices); trend = getTrend(); // 第三步:检查是否需要跳过当前交易 if (shouldSkipTrade()) { return; } } // 第四步:根据持仓状态执行相应的交易逻辑 if (position == PositionStatus.FLAT) { // 空仓状态下检查入场信号 checkEntrySignal(); } else { // 持仓状态下检查离场信号 checkExitSignal(); } /** * 第二步:确定当前市场趋势 */ 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) { @@ -138,15 +127,15 @@ lastPrice = prices.get(prices.size()-1); } /** * 确定当前市场趋势(牛市/熊市) * * @param prices 历史价格数据列表 */ private void determineTrend(List<BigDecimal> prices) { private TrendDirection determineTrend() { BigDecimal currentMa100 = ma100.getMa(); // 获取当前100日移动平均线 // 根据最新价格与100日MA的关系判断趋势:价格高于100日MA为牛市,否则为熊市 trend = lastPrice.compareTo(currentMa100) > 0 ? return lastPrice.compareTo(currentMa100) > 0 ? TrendDirection.BULLISH : TrendDirection.BEARISH; } @@ -176,7 +165,9 @@ /** * 检查是否满足入场信号条件 */ private void checkEntrySignal() { private TradeRequestParam checkEntrySignal(TradeRequestParam tradeRequestParam) { String poSide = null; String side = null; BigDecimal currentMa30 = ma30.getMa(); // 获取当前30日移动平均线 // 获取MACD的最新值用于判断动量方向 @@ -206,9 +197,10 @@ currentVolatility.compareTo(new BigDecimal("0.05")) < 0; if (macdBull && macdBarPositive && priceAboveMAWithStrength && rsiInRange && volatilityModerate) { enterLong(); poSide = CoinEnums.POSSIDE_LONG.getCode(); side = CoinEnums.SIDE_BUY.getCode(); } } else { } else if (trend == TrendDirection.BEARISH){ // 熊市入场条件增强: // 1. MACD空头信号(DIF<DEA) // 2. MACD柱状图为负(确认空头力量) @@ -224,152 +216,49 @@ currentVolatility.compareTo(new BigDecimal("0.05")) < 0; if (macdBear && macdBarNegative && priceBelowMAWithStrength && rsiInRange && volatilityModerate) { enterShort(); poSide = CoinEnums.POSSIDE_SHORT.getCode(); side = CoinEnums.SIDE_SELL.getCode(); } } tradeRequestParam.setPosSide(poSide); tradeRequestParam.setSide(side); return tradeRequestParam; } /** * 检查是否满足离场信号条件 */ private void checkExitSignal() { private TradeRequestParam checkExitSignal(TradeRequestParam tradeRequestParam) { BigDecimal currentMa30 = ma30.getMa(); // 获取当前30日移动平均线 // 获取MACD的最新值用于判断动量变化 BigDecimal currentDif = macd.getDif(); BigDecimal currentDea = macd.getDea(); if (position == PositionStatus.LONG) { String posSide = tradeRequestParam.getPosSide(); if (posSide == CoinEnums.POSSIDE_LONG.getCode()) { // 多头持仓离场条件: // 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(); if (macdExit && priceExit) { tradeRequestParam.setSide(CoinEnums.SIDE_SELL.getCode()); } } else { } else if (posSide == CoinEnums.POSSIDE_SHORT.getCode()){ // 空头持仓离场条件: // 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(); if (macdExit && priceExit) { tradeRequestParam.setSide(CoinEnums.SIDE_BUY.getCode()); } } return tradeRequestParam; } /** * 计算基于风险的仓位大小 * * @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 }