src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxKlineWebSocketClient.java
@@ -1,6 +1,7 @@ package com.xcong.excoin.modules.okxNewPrice; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.json.JSONException; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; @@ -344,8 +345,9 @@ .collect(Collectors.toList()); log.info("生成100个15分钟价格数据点成功!"); // 使用策略分析最新价格数据 MacdMaStrategy.TradingOrder tradingOrder = strategy.generateTradingOrder(historicalPrices); if (tradingOrder == null){ MacdMaStrategy.TradingOrder tradingOrderOpen = strategy.generateTradingOrder(historicalPrices,MacdMaStrategy.OperationType.open.name()); MacdMaStrategy.TradingOrder tradingOrderClose = strategy.generateTradingOrder(historicalPrices,MacdMaStrategy.OperationType.close.name()); if (tradingOrderOpen == null && tradingOrderClose == null){ return; } Collection<OkxQuantWebSocketClient> allClients = clientManager.getAllClients(); @@ -357,39 +359,38 @@ for (OkxQuantWebSocketClient client : clientManager.getAllClients()) { String accountName = client.getAccountName(); if (accountName != null) { if (ObjectUtil.isNotEmpty(tradingOrderOpen)){ // 根据信号执行交易操作 TradeRequestParam tradeRequestParam = new TradeRequestParam(); String posSide = tradingOrder.getPosSide(); String posSide = tradingOrderOpen.getPosSide(); tradeRequestParam.setPosSide(posSide); String currentPrice = String.valueOf(closePx); tradeRequestParam = caoZuoService.caoZuoStrategy(accountName, currentPrice, posSide); String side = tradingOrder.getSide(); String side = tradingOrderOpen.getSide(); tradeRequestParam.setSide(side); String clOrdId = WsParamBuild.getOrderNum(side); tradeRequestParam.setClOrdId(clOrdId); String sz = null; if ( (posSide == CoinEnums.POSSIDE_LONG.getCode() && side == CoinEnums.SIDE_BUY.getCode()) || (posSide == CoinEnums.POSSIDE_SHORT.getCode() && side == CoinEnums.SIDE_SELL.getCode()) ){ sz = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.BUY_CNT_INIT.name()); }else if ( (posSide == CoinEnums.POSSIDE_LONG.getCode() && side == CoinEnums.SIDE_SELL.getCode()) || (posSide == CoinEnums.POSSIDE_SHORT.getCode() && side == CoinEnums.SIDE_BUY.getCode()) ){ BigDecimal pos = PositionsWs.getAccountMap(PositionsWs.initAccountName(accountName, posSide)).get("pos"); sz = String.valueOf(pos); } String sz = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.BUY_CNT_INIT.name()); tradeRequestParam.setSz(sz); TradeOrderWs.orderEvent(client.getWebSocketClient(), tradeRequestParam); } if (ObjectUtil.isNotEmpty(tradingOrderClose)){ // 根据信号执行交易操作 TradeRequestParam tradeRequestParam = new TradeRequestParam(); String posSide = tradingOrderClose.getPosSide(); tradeRequestParam.setPosSide(posSide); String currentPrice = String.valueOf(closePx); tradeRequestParam = caoZuoService.caoZuoZhiSunEvent(accountName, currentPrice, posSide); TradeOrderWs.orderEvent(client.getWebSocketClient(), tradeRequestParam); } } } } } src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MacdMaStrategy.java
@@ -22,6 +22,13 @@ public class MacdMaStrategy { /** 持仓状态枚举 */ public enum OperationType { /** 开仓平仓 */ open, close } /** 持仓状态枚举 */ public enum PositionType { /** 多头持仓 */ LONG_BUY, @@ -40,10 +47,6 @@ private int volatilityPeriod; // 波动率计算周期 private BigDecimal stopLossRatio; // 止损比例 private BigDecimal takeProfitRatio; // 止盈比例 // 持仓信息 private BigDecimal entryPrice; // 开仓价格 private long entryTime; // 开仓时间戳 /** * 默认构造函数,使用标准MACD参数 @@ -73,9 +76,6 @@ this.stopLossRatio = stopLossRatio; this.takeProfitRatio = takeProfitRatio; // 初始化持仓状态为空仓 this.entryPrice = BigDecimal.ZERO; this.entryTime = 0; } /** @@ -84,7 +84,7 @@ * @param closePrices 收盘价序列 * @return 生成的交易信号(LONG、SHORT或NONE) */ public PositionType analyze(List<BigDecimal> closePrices) { public PositionType analyzeOpen(List<BigDecimal> closePrices) { // 数据检查:确保有足够的数据点进行计算 if (closePrices == null || closePrices.size() < 34) { return PositionType.NONE; // 数据不足,无法生成信号 @@ -109,23 +109,48 @@ // 多头开仓条件检查 if (isLongEntryCondition(macdResult, closePrices, volatility.getValue())) { // 执行开多 this.entryPrice = latestPrice; this.entryTime = System.currentTimeMillis(); log.info( "多头开仓信号,价格:{}", latestPrice); return PositionType.LONG_BUY; } // 空头开仓条件检查 if (isShortEntryCondition(macdResult, closePrices, volatility.getValue())) { // 执行开空 this.entryPrice = latestPrice; this.entryTime = System.currentTimeMillis(); log.info( "空头开仓信号,价格:{}", latestPrice); return PositionType.SHORT_SELL; } // 无信号 return PositionType.NONE; } /** * 分析最新价格数据并生成交易信号 * * @param closePrices 收盘价序列 * @return 生成的交易信号(LONG、SHORT或NONE) */ public PositionType analyzeClose(List<BigDecimal> closePrices) { // 数据检查:确保有足够的数据点进行计算 if (closePrices == null || closePrices.size() < 34) { return PositionType.NONE; // 数据不足,无法生成信号 } // 1. 计算MACD指标 MACDResult macdResult = MACDCalculator.calculateMACD( closePrices, shortPeriod, longPeriod, signalPeriod); // 最新收盘价 BigDecimal latestPrice = closePrices.get(closePrices.size() - 1); if (isLongExitCondition(macdResult, latestPrice)) { // 执行平多 log.info( "多头平仓信号,价格:{}", latestPrice); return PositionType.LONG_SELL; } if (isShortExitCondition(macdResult, latestPrice)) { // 执行平空 log.info( "空头平仓信号,价格:{}", latestPrice); return PositionType.SHORT_BUY; } @@ -166,9 +191,18 @@ * @param historicalPrices 历史价格序列 * @return 交易指令(包含side和posSide),如果没有交易信号则返回null */ public TradingOrder generateTradingOrder(List<BigDecimal> historicalPrices) { PositionType signal = analyze(historicalPrices); public TradingOrder generateTradingOrder(List<BigDecimal> historicalPrices,String operation) { PositionType signal = null; if ( operation == OperationType.open.name()){ signal = analyzeOpen(historicalPrices); }else if ( operation == OperationType.close.name()){ analyzeClose(historicalPrices); } // 根据信号和当前持仓状态生成交易指令 if (signal == PositionType.LONG_BUY) { // 开多:买入开多(side 填写 buy; posSide 填写 long ) @@ -319,7 +353,7 @@ previous.getMacdHist().abs().compareTo(latest.getMacdHist().abs()) < 0; // 金叉或柱状线扩张任一满足即可 return isGoldenCross || isExpanding; return isGoldenCross && isExpanding; } /** @@ -373,7 +407,7 @@ previous.getMacdHist().abs().compareTo(latest.getMacdHist().abs()) > 0; // 死叉或柱状线收缩任一满足即可 return isDeathCross || isContracting; return isDeathCross && isContracting; } /** @@ -439,22 +473,4 @@ volatility.compareTo(maxVolatility) <= 0; } /** * 获取开仓价格 * * @return 开仓价格 */ public BigDecimal getEntryPrice() { return entryPrice; } /** * 获取开仓时间戳 * * @return 开仓时间戳 */ public long getEntryTime() { return entryTime; } } src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MacdMaStrategyTest.java
File was deleted src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/TradeOrderWs.java
@@ -86,8 +86,12 @@ * 平多:卖出平多(side 填写 sell;posSide 填写 long ) * 平空:买入平空(side 填写 buy; posSide 填写 short ) 需要检验仓位通道是否准备就绪 */ //买入开多、卖出开空则验证仓位通道是否准备就绪 String positionAccountName = PositionsWs.initAccountName(accountName, posSide); boolean b = posSide.equals(CoinEnums.POSSIDE_LONG.getCode()) && side.equals(CoinEnums.SIDE_BUY.getCode()); boolean c = posSide.equals(CoinEnums.POSSIDE_SHORT.getCode()) && side.equals(CoinEnums.SIDE_SELL.getCode()); if ( b || c ){ BigDecimal positionsReadyState = PositionsWs.getAccountMap(positionAccountName).get(CoinEnums.READY_STATE.name()) == null ? BigDecimal.ZERO : PositionsWs.getAccountMap(positionAccountName).get(CoinEnums.READY_STATE.name()); if (WsMapBuild.parseBigDecimalSafe(CoinEnums.READY_STATE_YES.getCode()).compareTo(positionsReadyState) != 0) { @@ -99,6 +103,7 @@ log.info("账户通道未就绪,取消发送"); return; } } try { JSONArray argsArray = new JSONArray();