| 2026-01-05 | Administrator | ![]() |
| 2026-01-05 | Administrator | ![]() |
| 2026-01-05 | Administrator | ![]() |
| 2026-01-05 | Administrator | ![]() |
| 2026-01-05 | Administrator | ![]() |
| 2026-01-05 | Administrator | ![]() |
| 2026-01-05 | Administrator | ![]() |
| 2026-01-05 | Administrator | ![]() |
| 2026-01-05 | Administrator | ![]() |
| 2026-01-05 | Administrator | ![]() |
| 2026-01-05 | Administrator | ![]() |
| 2026-01-05 | Administrator | ![]() |
| 2026-01-05 | Administrator | ![]() |
| 2026-01-05 | Administrator | ![]() |
| 2026-01-05 | Administrator | ![]() |
| 2026-01-05 | Administrator | ![]() |
src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxKlineWebSocketClient.java
@@ -3,9 +3,11 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.json.JSONException; import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.xcong.excoin.modules.blackchain.service.DateUtil; 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; @@ -332,6 +334,8 @@ BigDecimal lowPx = new BigDecimal(data.getString(3)); BigDecimal closePx = new BigDecimal(data.getString(4)); BigDecimal vol = new BigDecimal(data.getString(5)); //ts String 开始时间,Unix时间戳的毫秒数格式,如 1597026383085 转日期:2020-08-07 15:13:03.085 String time = DateUtil.TimeStampToDateTime(Long.parseLong(data.getString(0))); /** * K线状态 * 0:K线未完结 @@ -339,37 +343,28 @@ */ String confirm = data.getString(8); if ("1".equals(confirm)){ log.info("{}开仓{}:{}",time,closePx,instId); //调用策略 // 创建策略实例 MacdMaStrategy strategy = new MacdMaStrategy(); // 生成100个15分钟价格数据点 // 生成200个1m价格数据点 List<Kline> kline1MinuteData = getKlineDataByInstIdAndBar(instId, "1m"); List<BigDecimal> historicalPrices1M = kline1MinuteData.stream() .map(Kline::getC) .collect(Collectors.toList()); // 生成200个1D价格数据点 List<Kline> kline1DayData = getKlineDataByInstIdAndBar(instId, "1D"); List<BigDecimal> historicalPrices1D = kline1DayData.stream() .map(Kline::getC) .collect(Collectors.toList()); log.info("1D:{}", JSONUtil.parse( historicalPrices1D)); // 使用策略分析最新价格数据 MacdMaStrategy.TradingOrder tradingOrderOpen1M = strategy.generateTradingOrder(historicalPrices1M,MacdMaStrategy.OperationType.open.name()); if (tradingOrderOpen1M == null ){ MacdMaStrategy.TradingOrder tradingOrderOpenOpen = strategy.generateTradingOrder(historicalPrices1M,historicalPrices1D,MacdMaStrategy.OperationType.open.name()); if (tradingOrderOpenOpen == null){ return; } // List<Kline> kline15MinuteData = getKlineDataByInstIdAndBar(instId, "15m"); // List<BigDecimal> historicalPrices15M = kline15MinuteData.stream() // .map(Kline::getC) // .collect(Collectors.toList()); // // 使用策略分析最新价格数据 // MacdMaStrategy.TradingOrder tradingOrderOpen15M = strategy.generateTradingOrder(historicalPrices15M,MacdMaStrategy.OperationType.open.name()); // if (tradingOrderOpen15M == null ){ // return; // } // // if (!tradingOrderOpen1M.getPosSide().equals(tradingOrderOpen15M.getPosSide())){ // return; // } // log.info("1分钟和15分钟K线方向一致,开始执行交易操作!"); Collection<OkxQuantWebSocketClient> allClients = clientManager.getAllClients(); //如果为空,则直接返回 @@ -380,16 +375,16 @@ for (OkxQuantWebSocketClient client : clientManager.getAllClients()) { String accountName = client.getAccountName(); if (accountName != null) { if (ObjectUtil.isNotEmpty(tradingOrderOpen1M)){ if (ObjectUtil.isNotEmpty(tradingOrderOpenOpen)){ // 根据信号执行交易操作 TradeRequestParam tradeRequestParam = new TradeRequestParam(); String posSide = tradingOrderOpen1M.getPosSide(); String posSide = tradingOrderOpenOpen.getPosSide(); tradeRequestParam.setPosSide(posSide); String currentPrice = String.valueOf(closePx); tradeRequestParam = caoZuoService.caoZuoStrategy(accountName, currentPrice, posSide); String side = tradingOrderOpen1M.getSide(); String side = tradingOrderOpenOpen.getSide(); tradeRequestParam.setSide(side); String clOrdId = WsParamBuild.getOrderNum(side); @@ -398,6 +393,63 @@ String sz = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.BUY_CNT_INIT.name()); tradeRequestParam.setSz(sz); TradeOrderWs.orderEvent(client.getWebSocketClient(), tradeRequestParam); } } } }else{ log.info("{}平仓{}:{}",time,closePx,instId); //调用策略 // 创建策略实例 MacdMaStrategy strategy = new MacdMaStrategy(); // 生成200个1m价格数据点 List<Kline> kline1MinuteData = getKlineDataByInstIdAndBar(instId, "1m"); List<BigDecimal> historicalPrices1M = kline1MinuteData.stream() .map(Kline::getC) .collect(Collectors.toList()); // 生成200个1D价格数据点 List<Kline> kline1DayData = getKlineDataByInstIdAndBar(instId, "1D"); List<BigDecimal> historicalPrices1D = kline1DayData.stream() .map(Kline::getC) .collect(Collectors.toList()); // 使用策略分析最新价格数据 MacdMaStrategy.TradingOrder tradingOrderOpenClose = strategy.generateTradingOrder(historicalPrices1M,historicalPrices1D,MacdMaStrategy.OperationType.close.name()); if (tradingOrderOpenClose == null){ return; } Collection<OkxQuantWebSocketClient> allClients = clientManager.getAllClients(); //如果为空,则直接返回 if (allClients.isEmpty()) { return; } // 获取所有OkxQuantWebSocketClient实例 for (OkxQuantWebSocketClient client : clientManager.getAllClients()) { String accountName = client.getAccountName(); if (accountName != null) { if (ObjectUtil.isNotEmpty(tradingOrderOpenClose)){ // 根据信号执行交易操作 TradeRequestParam tradeRequestParam = new TradeRequestParam(); tradeRequestParam.setAccountName(accountName); tradeRequestParam.setMarkPx(String.valueOf(closePx)); tradeRequestParam.setInstId(CoinEnums.HE_YUE.getCode()); tradeRequestParam.setTdMode(CoinEnums.CROSS.getCode()); tradeRequestParam.setOrdType(CoinEnums.ORDTYPE_MARKET.getCode()); String posSide = tradingOrderOpenClose.getPosSide(); tradeRequestParam.setPosSide(posSide); String side = tradingOrderOpenClose.getSide(); tradeRequestParam.setSide(side); String clOrdId = WsParamBuild.getOrderNum(side); tradeRequestParam.setClOrdId(clOrdId); String positionAccountName = PositionsWs.initAccountName(accountName, posSide); BigDecimal pos = PositionsWs.getAccountMap(positionAccountName).get("pos"); tradeRequestParam.setSz(String.valueOf(pos)); TradeOrderWs.orderZhiYingZhiSunEventNoState(client.getWebSocketClient(), tradeRequestParam); } } } @@ -425,16 +477,19 @@ for (String[] s : klinesList) { // 确保数组有足够的元素 if (s != null && s.length >= 9) { String s1 = s[8]; try { Kline kline = new Kline(); kline.setTs(s[0]); kline.setO(new BigDecimal(s[1])); kline.setH(new BigDecimal(s[2])); kline.setL(new BigDecimal(s[3])); kline.setC(new BigDecimal(s[4])); kline.setVol(new BigDecimal(s[5])); kline.setConfirm(s[8]); klineList.add(kline); if ("1".equals(s1)){ Kline kline = new Kline(); kline.setTs(s[0]); kline.setO(new BigDecimal(s[1])); kline.setH(new BigDecimal(s[2])); kline.setL(new BigDecimal(s[3])); kline.setC(new BigDecimal(s[4])); kline.setVol(new BigDecimal(s[5])); kline.setConfirm(s[8]); klineList.add(kline); } } catch (NumberFormatException e) { log.error("K线数据转换为BigDecimal失败: {}", Arrays.toString(s), e); } src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxQuantWebSocketClient.java
@@ -383,8 +383,9 @@ // 注意:当前实现中,OrderInfoWs等类使用静态Map存储数据 // 这会导致多账号之间的数据冲突。需要进一步修改这些类的设计,让数据存储与特定账号关联 if (OrderInfoWs.ORDERINFOWS_CHANNEL.equals(channel)) { TradeRequestParam tradeRequestParam = OrderInfoWs.handleEvent(response, redisUtils, account.name()); TradeOrderWs.orderEvent(webSocketClient, tradeRequestParam); OrderInfoWs.handleEvent(response, redisUtils, account.name()); // TradeRequestParam tradeRequestParam = OrderInfoWs.handleEvent(response, redisUtils, account.name()); // TradeOrderWs.orderZhiYingZhiSunEventNoState(webSocketClient, tradeRequestParam); }else if (AccountWs.ACCOUNTWS_CHANNEL.equals(channel)) { AccountWs.handleEvent(response, account.name()); } else if (PositionsWs.POSITIONSWS_CHANNEL.equals(channel)) { src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/EMACalculator.java
@@ -86,4 +86,22 @@ public static List<BigDecimal> calculateEMA(List<BigDecimal> prices, int period) { return calculateEMA(prices, period, true); } /** * 计算单个EMA值(递归计算方式) * * @param currentPrice 当前价格 * @param prevEMA 前一个EMA值 * @param period EMA周期 * @return 当前EMA值 */ public static BigDecimal calculateSingleEMA(BigDecimal currentPrice, BigDecimal prevEMA, int period) { // 计算权重因子alpha = 2 / (period + 1) BigDecimal alpha = BigDecimal.valueOf(2.0).divide(BigDecimal.valueOf(period + 1), 10, RoundingMode.HALF_UP); // EMA(today) = Price(today) * alpha + EMA(yesterday) * (1 - alpha) return currentPrice.multiply(alpha) .add(prevEMA.multiply(BigDecimal.ONE.subtract(alpha))) .setScale(10, RoundingMode.HALF_UP); } } src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MACDCalculator.java
@@ -2,6 +2,7 @@ import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** @@ -20,74 +21,101 @@ * 计算MACD指标 * * @param closePrices 收盘价列表(使用BigDecimal确保计算精度) * @param shortPeriod 短期EMA周期(通常为12) * @param longPeriod 长期EMA周期(通常为26) * @param signalPeriod DEA的周期(通常为9) * @param fastlen 短期EMA周期(通常为12) * @param slowlen 长期EMA周期(通常为26) * @param siglen DEA的周期(通常为9) * @return 包含MACD各部分数据的PriceData列表 * @throws IllegalArgumentException 如果数据点不足或参数无效 */ public static MACDResult calculateMACD(List<BigDecimal> closePrices, int shortPeriod, int longPeriod, int signalPeriod) { public static MACDResult calculateMACD(List<BigDecimal> closePrices, int fastlen, int slowlen, int siglen) { // 参数校验:确保数据点足够 if (closePrices == null || closePrices.isEmpty()) { throw new IllegalArgumentException("Close prices list cannot be null or empty."); } if (shortPeriod <= 0 || longPeriod <= 0 || signalPeriod <= 0) { if (fastlen <= 0 || slowlen <= 0 || siglen <= 0) { throw new IllegalArgumentException("All periods must be positive integers."); } if (shortPeriod >= longPeriod) { throw new IllegalArgumentException("Short period must be less than long period."); if (fastlen >= slowlen) { throw new IllegalArgumentException("Fast period must be less than slow period."); } if (closePrices.size() < Math.max(shortPeriod, longPeriod)) { if (closePrices.size() < Math.max(fastlen, slowlen)) { throw new IllegalArgumentException("Insufficient data points for the specified periods."); } // 1. 计算短期和长期EMA(使用SMA作为初始值,提高计算准确性) List<BigDecimal> emaShort = EMACalculator.calculateEMA(closePrices, shortPeriod, true); List<BigDecimal> emaLong = EMACalculator.calculateEMA(closePrices, longPeriod, true); // 反转数据,确保从旧到新处理(因为用户提供的数据是从新到旧) List<BigDecimal> prices = new ArrayList<>(closePrices); Collections.reverse(prices); // 2. 确定公共有效起始点(从较长周期的EMA开始计算) int startIdx = Math.max(shortPeriod, longPeriod) - 1; // 因为EMA从第period个数据点开始有效 int validLength = closePrices.size() - startIdx; // 有效数据点数量 // 1. 计算快速EMA和慢速EMA,使用SMA作为初始值 // 当initialSMA=true时,EMA列表长度为prices.size() - period + 1 List<BigDecimal> fastEma = EMACalculator.calculateEMA(prices, fastlen, true); List<BigDecimal> slowEma = EMACalculator.calculateEMA(prices, slowlen, true); // 3. 计算DIF(仅在有效区间内计算) List<BigDecimal> difValues = new ArrayList<>(validLength); for (int i = 0; i < validLength; i++) { // 计算EMA的有效索引 int idxEmaShort = startIdx - shortPeriod + 1 + i; // 短期EMA的当前索引 int idxEmaLong = startIdx - longPeriod + 1 + i; // 长期EMA的当前索引 // DIF = 短期EMA - 长期EMA BigDecimal dif = emaShort.get(idxEmaShort).subtract(emaLong.get(idxEmaLong)); difValues.add(dif); // 2. 计算MACD线(快速EMA减去慢速EMA) List<BigDecimal> macdLine = new ArrayList<>(); // EMA列表的起始索引与价格列表的对应关系 int slowEmaStartIdx = slowlen - 1; // slowEma中第一个有效值对应的价格索引 int fastEmaStartIdx = fastlen - 1; // fastEma中第一个有效值对应的价格索引 for (int i = 0; i < prices.size(); i++) { if (i < slowEmaStartIdx) { // 在慢速EMA开始有效之前,MACD线值为0 macdLine.add(BigDecimal.ZERO); } else { // MACD线 = 快速EMA - 慢速EMA // 需要将价格索引转换为EMA列表索引 int slowEmaIdx = i - slowEmaStartIdx; int fastEmaIdx = i - fastEmaStartIdx; BigDecimal macdValue = fastEma.get(fastEmaIdx).subtract(slowEma.get(slowEmaIdx)); macdLine.add(macdValue); } } // 4. 计算DEA(基于有效DIF数据的EMA) List<BigDecimal> deaValues = EMACalculator.calculateEMA(difValues, signalPeriod, false); // 3. 计算信号线(MACD线的siglen周期EMA),使用SMA作为初始值 List<BigDecimal> signalLine = EMACalculator.calculateEMA(macdLine, siglen, true); // 5. 构建并填充结果(包含所有MACD数据) List<PriceData> result = new ArrayList<>(deaValues.size()); // 4. 计算柱状图(MACD线与信号线的差值) List<BigDecimal> histogram = new ArrayList<>(); int signalLineStartIdx = siglen - 1; // signalLine中第一个有效值对应的macdLine索引 for (int i = 0; i < macdLine.size(); i++) { if (i < slowEmaStartIdx + signalLineStartIdx) { // 在信号线开始有效之前,柱状图值为0 histogram.add(BigDecimal.ZERO); } else { // 将macdLine索引转换为signalLine索引 int signalLineIdx = i - signalLineStartIdx; // 柱状图 = (MACD线 - 信号线) * 2(放大信号) BigDecimal histValue = macdLine.get(i).subtract(signalLine.get(signalLineIdx)).multiply(new BigDecimal("2")); histogram.add(histValue); } } // 从第一个DEA值开始构建结果 for (int i = 0; i < deaValues.size(); i++) { int closeIdx = startIdx + i; // 对应原收盘价列表的索引 // 创建价格数据对象 PriceData data = new PriceData(closePrices.get(closeIdx)); // 设置EMA(使用正确的偏移位置) data.setEmaShort(emaShort.get(closeIdx - shortPeriod + 1)); data.setEmaLong(emaLong.get(closeIdx - longPeriod + 1)); // 设置DIF、DEA和MACD柱状图 data.setDif(difValues.get(i)); data.setDea(deaValues.get(i)); // DEA索引直接对应 data.setMacdHist(data.getDif().subtract(data.getDea())); // MACD柱状图 = DIF - DEA // 5. 构建结果数据 List<PriceData> result = new ArrayList<>(); int startIndex = slowEmaStartIdx + signalLineStartIdx; // 从信号线开始有效的位置开始 for (int i = startIndex; i < prices.size(); i++) { PriceData data = new PriceData(prices.get(i)); // 设置EMA值(需要转换索引) int fastEmaIdx = i - fastEmaStartIdx; int slowEmaIdx = i - slowEmaStartIdx; data.setEmaShort(fastEma.get(fastEmaIdx)); data.setEmaLong(slowEma.get(slowEmaIdx)); // 设置MACD指标值 data.setDif(macdLine.get(i)); data.setDea(signalLine.get(i - signalLineStartIdx)); data.setMacdHist(histogram.get(i)); result.add(data); } // 反转结果列表,恢复为从新到旧的顺序 Collections.reverse(result); return new MACDResult(result, startIdx); return new MACDResult(result, startIndex); } /** @@ -99,7 +127,7 @@ * @return 包含MACD各部分数据的PriceData列表 */ public static MACDResult calculateMACD(List<BigDecimal> closePrices) { // 默认参数:短期周期12,长期周期26,信号周期9 // 默认参数:快速周期12,慢速周期26,信号周期9 return calculateMACD(closePrices, 12, 26, 9); } @@ -122,14 +150,18 @@ return false; } // 反转原始价格列表,确保从旧到新处理 List<BigDecimal> prices = new ArrayList<>(closePrices); Collections.reverse(prices); // 找到最近的价格高点和对应的DIF值 int recentPriceHighIdx = IndicatorUtils.findRecentHighIndex(closePrices, startIdx); int recentPriceHighIdx = IndicatorUtils.findRecentHighIndex(prices, startIdx); if (recentPriceHighIdx < startIdx + 2 || recentPriceHighIdx == -1) { return false; } // 找到之前的价格高点和对应的DIF值 int previousPriceHighIdx = IndicatorUtils.findPreviousHighIndex(closePrices, startIdx, recentPriceHighIdx); int previousPriceHighIdx = IndicatorUtils.findPreviousHighIndex(prices, startIdx, recentPriceHighIdx); if (previousPriceHighIdx < startIdx || previousPriceHighIdx == -1) { return false; } @@ -143,8 +175,8 @@ return false; } BigDecimal recentPrice = closePrices.get(recentPriceHighIdx); BigDecimal previousPrice = closePrices.get(previousPriceHighIdx); BigDecimal recentPrice = prices.get(recentPriceHighIdx); BigDecimal previousPrice = prices.get(previousPriceHighIdx); BigDecimal recentDif = macdData.get(recentDifIdx).getDif(); BigDecimal previousDif = macdData.get(previousDifIdx).getDif(); @@ -172,14 +204,18 @@ return false; } // 反转原始价格列表,确保从旧到新处理 List<BigDecimal> prices = new ArrayList<>(closePrices); Collections.reverse(prices); // 找到最近的价格低点和对应的DIF值 int recentPriceLowIdx = IndicatorUtils.findRecentLowIndex(closePrices, startIdx); int recentPriceLowIdx = IndicatorUtils.findRecentLowIndex(prices, startIdx); if (recentPriceLowIdx < startIdx + 2 || recentPriceLowIdx == -1) { return false; } // 找到之前的价格低点和对应的DIF值 int previousPriceLowIdx = IndicatorUtils.findPreviousLowIndex(closePrices, startIdx, recentPriceLowIdx); int previousPriceLowIdx = IndicatorUtils.findPreviousLowIndex(prices, startIdx, recentPriceLowIdx); if (previousPriceLowIdx < startIdx || previousPriceLowIdx == -1) { return false; } @@ -193,8 +229,8 @@ return false; } BigDecimal recentPrice = closePrices.get(recentPriceLowIdx); BigDecimal previousPrice = closePrices.get(previousPriceLowIdx); BigDecimal recentPrice = prices.get(recentPriceLowIdx); BigDecimal previousPrice = prices.get(previousPriceLowIdx); BigDecimal recentDif = macdData.get(recentDifIdx).getDif(); BigDecimal previousDif = macdData.get(previousDifIdx).getDif(); @@ -202,4 +238,4 @@ return recentPrice.compareTo(previousPrice) < 0 && recentDif.compareTo(previousDif) > 0; } } } src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MacdMaStrategy.java
@@ -1,161 +1,56 @@ /** * MACD和MA组合交易策略实现类 * 基于15分钟K线数据生成交易信号并确定持仓方向 * 基于多时间粒度K线数据生成交易信号并确定持仓方向 * * 该策略综合考虑了EMA指标、MACD指标、价格突破信号和波动率因素, * 形成了一套完整的开仓、平仓和持仓管理机制。 * 支持1分钟(K线)和日线(K线)级别的数据输入,数据顺序要求从新到旧排列。 */ package com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy; import lombok.extern.slf4j.Slf4j; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * MACD和MA组合交易策略实现 * <p> * 该策略利用EMA交叉、MACD指标、价格突破信号和波动率过滤, * 为15分钟K线级别交易提供综合决策支持。 * 为多时间粒度K线级别交易提供综合决策支持。 * <p> * 数据输入要求: * - historicalPrices1M:1分钟K线收盘价列表,顺序从新到旧 * - historicalPrices1D:日线K线收盘价列表,顺序从新到旧 */ @Slf4j public class MacdMaStrategy { /** 持仓状态枚举 */ /** 操作类型枚举 */ public enum OperationType { /** 开仓平仓 */ /** 开仓 */ open, /** 平仓 */ close } /** 持仓状态枚举 */ public enum PositionType { /** 多头持仓 */ /** 多头开仓 */ LONG_BUY, /** 多头平仓 */ LONG_SELL, /** 空头持仓 */ /** 空头开仓 */ SHORT_SELL, /** 空头平仓 */ SHORT_BUY, /** 空仓 */ NONE } // 策略参数 private int shortPeriod; // 短期EMA周期 private int longPeriod; // 长期EMA周期 private int signalPeriod; // MACD信号线周期 private int volatilityPeriod; // 波动率计算周期 private BigDecimal stopLossRatio; // 止损比例 private BigDecimal takeProfitRatio; // 止盈比例 /** * 默认构造函数,使用标准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; } /** * 分析最新价格数据并生成交易信号 * * @param closePrices 收盘价序列 * @return 生成的交易信号(LONG、SHORT或NONE) */ public PositionType analyzeOpen(List<BigDecimal> closePrices) { // 数据检查:确保有足够的数据点进行计算 if (closePrices == null || closePrices.size() < 34) { return PositionType.NONE; // 数据不足,无法生成信号 } // 1. 计算MACD指标 MACDResult macdResult = MACDCalculator.calculateMACD( closePrices, shortPeriod, longPeriod, signalPeriod); log.info( "MACD计算结果:{}", macdResult.getMacdData().get( macdResult.getMacdData().size() -1)); // 最新收盘价 BigDecimal latestPrice = closePrices.get(closePrices.size() - 1); // 3. 检查开平仓条件 // 多头开仓条件检查 if (isLongEntryCondition(macdResult, closePrices)) { // 执行开多 log.info( "多头开仓信号,价格:{}", latestPrice); return PositionType.LONG_BUY; } // 空头开仓条件检查 if (isShortEntryCondition(macdResult, closePrices)) { // 执行开空 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; } // 无信号 return PositionType.NONE; } /** * 交易指令类,封装side和posSide的组合 */ /** 交易指令类,封装side和posSide的组合 */ public static class TradingOrder { private String side; // buy或sell private String posSide; // long或short @@ -179,93 +74,264 @@ } } // 策略参数 private int shortPeriod; // 短期EMA周期 private int longPeriod; // 长期EMA周期 private int signalPeriod; // MACD信号线周期 private int volatilityPeriod; // 波动率计算周期 private int trendPeriod = 200; // 趋势过滤EMA周期(200日) private BigDecimal stopLossRatio; // 止损比例 private BigDecimal takeProfitRatio; // 止盈比例 /** * 默认构造函数,使用标准MACD参数 * 短期周期=12, 长期周期=26, 信号线周期=9, 波动率周期=20 * 止损比例=1%, 止盈比例=2% */ public MacdMaStrategy() { this(12, 26, 9, 20, new BigDecimal("0.01"), new BigDecimal("0.02")); } /** * 自定义参数构造函数,使用默认趋势周期200 * * @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, longPeriod, signalPeriod, volatilityPeriod, 200, stopLossRatio, takeProfitRatio); } /** * 自定义参数构造函数 * * @param shortPeriod 短期EMA周期 * @param longPeriod 长期EMA周期 * @param signalPeriod MACD信号线周期 * @param volatilityPeriod 波动率计算周期 * @param trendPeriod 趋势过滤EMA周期(200日) * @param stopLossRatio 止损比例 * @param takeProfitRatio 止盈比例 */ public MacdMaStrategy(int shortPeriod, int longPeriod, int signalPeriod, int volatilityPeriod, int trendPeriod, BigDecimal stopLossRatio, BigDecimal takeProfitRatio) { this.shortPeriod = shortPeriod; this.longPeriod = longPeriod; this.signalPeriod = signalPeriod; this.volatilityPeriod = volatilityPeriod; this.trendPeriod = trendPeriod; this.stopLossRatio = stopLossRatio; this.takeProfitRatio = takeProfitRatio; } // 主流程方法 /** * 分析历史价格数据并生成交易指令 * * @param historicalPrices 历史价格序列 * @param historicalPrices 历史价格序列(1分钟K线收盘价),顺序从新到旧 * @param historical1DayPrices 日线历史价格序列,顺序从新到旧 * @param operation 操作类型(open/close) * @return 交易指令(包含side和posSide),如果没有交易信号则返回null */ public TradingOrder generateTradingOrder(List<BigDecimal> historicalPrices,String operation) { PositionType signal = null; public TradingOrder generateTradingOrder(List<BigDecimal> historicalPrices, List<BigDecimal> historical1DayPrices, String operation) { PositionType signal = null; if ( operation == OperationType.open.name()){ signal = analyzeOpen(historicalPrices); }else if ( operation == OperationType.close.name()){ signal = analyzeClose(historicalPrices); if (OperationType.open.name().equals(operation)) { signal = analyzeOpen(historicalPrices, historical1DayPrices); } else if (OperationType.close.name().equals(operation)) { signal = analyzeClose(historicalPrices, historical1DayPrices); } // 根据信号和当前持仓状态生成交易指令 if (signal == PositionType.LONG_BUY) { // 开多:买入开多(side 填写 buy; posSide 填写 long ) return new TradingOrder("buy", "long"); } else if (signal == PositionType.LONG_SELL) { // 开空:卖出开空(side 填写 sell; posSide 填写 short ) return new TradingOrder("sell", "long"); } else if (signal == PositionType.SHORT_SELL) { // 开空:卖出开空(side 填写 sell; posSide 填写 short ) return new TradingOrder("sell", "short"); } else if (signal == PositionType.SHORT_BUY) { // 开空:卖出开空(side 填写 sell; posSide 填写 short ) return new TradingOrder("buy", "short"); } // 没有交易信号 return null; // 根据信号生成交易指令 return convertSignalToTradingOrder(signal); } /** * 分析最新价格数据并生成开仓信号 * * @param closePrices 1分钟K线收盘价序列,顺序从新到旧 * @param close1DPrices 日线K线收盘价序列,顺序从新到旧 * @return 生成的交易信号(LONG_BUY、SHORT_SELL或NONE) */ public PositionType analyzeOpen(List<BigDecimal> closePrices, List<BigDecimal> close1DPrices) { // 数据检查:确保有足够的数据点进行计算(需要足够数据计算200日EMA) if (closePrices == null || closePrices.size() < Math.max(34, trendPeriod) || close1DPrices == null || close1DPrices.size() < Math.max(34, trendPeriod)) { return PositionType.NONE; // 数据不足,无法生成信号 } // 计算MACD指标 MACDResult macdResult = MACDCalculator.calculateMACD( closePrices, shortPeriod, longPeriod, signalPeriod); log.info("MACD计算结果:{}", macdResult.getMacdData().get(0)); // 多头开仓条件检查 if (isLongEntryCondition(macdResult, closePrices, close1DPrices)) { log.info("多头开仓信号,价格:{}", closePrices.get(0)); return PositionType.LONG_BUY; } // 空头开仓条件检查 if (isShortEntryCondition(macdResult, closePrices, close1DPrices)) { log.info("空头开仓信号,价格:{}", closePrices.get(0)); return PositionType.SHORT_SELL; } // 无信号 return PositionType.NONE; } /** * 分析最新价格数据并生成平仓信号 * * @param closePrices 1分钟K线收盘价序列,顺序从新到旧 * @param close1DPrices 日线K线收盘价序列,顺序从新到旧 * @return 生成的交易信号(LONG_SELL、SHORT_BUY或NONE) */ public PositionType analyzeClose(List<BigDecimal> closePrices, List<BigDecimal> close1DPrices) { // 数据检查:确保有足够的数据点进行计算 if (closePrices == null || closePrices.size() < Math.max(34, trendPeriod) || close1DPrices == null || close1DPrices.size() < Math.max(34, trendPeriod)) { return PositionType.NONE; // 数据不足,无法生成信号 } // 计算MACD指标 MACDResult macdResult = MACDCalculator.calculateMACD( closePrices, shortPeriod, longPeriod, signalPeriod); // 最新收盘价 BigDecimal latestPrice = closePrices.get(0); if (isLongExitCondition(macdResult, latestPrice)) { log.info("多头平仓信号,价格:{}", latestPrice); return PositionType.LONG_SELL; } if (isShortExitCondition(macdResult, latestPrice)) { log.info("空头平仓信号,价格:{}", latestPrice); return PositionType.SHORT_BUY; } // 无信号 return PositionType.NONE; } // 信号转换方法 /** * 将持仓信号转换为交易指令 * * @param signal 持仓信号 * @return 交易指令,无信号则返回null */ private TradingOrder convertSignalToTradingOrder(PositionType signal) { if (signal == null) { return null; } switch (signal) { case LONG_BUY: // 开多:买入开多(side 填写 buy; posSide 填写 long ) return new TradingOrder("buy", "long"); case LONG_SELL: // 平多:卖出平多(side 填写 sell; posSide 填写 long ) return new TradingOrder("sell", "long"); case SHORT_SELL: // 开空:卖出开空(side 填写 sell; posSide 填写 short ) return new TradingOrder("sell", "short"); case SHORT_BUY: // 平空:买入平空(side 填写 buy; posSide 填写 short ) return new TradingOrder("buy", "short"); default: // 无信号 return null; } } // 开仓条件检查方法 /** * 多头开仓条件检查 * * @param macdResult MACD计算结果 * @param closePrices 收盘价序列 * @param closePrices 1分钟K线收盘价序列,顺序从新到旧 * @param close1DPrices 日线K线收盘价序列,顺序从新到旧 * @return 是否满足多头开仓条件 */ private boolean isLongEntryCondition(MACDResult macdResult, List<BigDecimal> closePrices) { // 2. MACD金叉且柱状线扩张检查 boolean isMacdFavorable = isMacdGoldenCrossAndExpanding(macdResult); private boolean isLongEntryCondition(MACDResult macdResult, List<BigDecimal> closePrices, List<BigDecimal> close1DPrices) { // 1. 计算200日EMA(趋势过滤) // 复制并反转日线数据,确保从旧到新计算EMA List<BigDecimal> reversed1DPrices = new ArrayList<>(close1DPrices); Collections.reverse(reversed1DPrices); List<BigDecimal> trendEma = EMACalculator.calculateEMA(reversed1DPrices, trendPeriod, true); BigDecimal latestTrendEma = trendEma.get(trendEma.size() - 1); BigDecimal latestPrice = closePrices.get(0); log.info( "200日EMA:{}, 最新价格:{}", latestTrendEma, latestPrice); // 3. MACD柱状线必须为正 boolean macdPositive = isMacdPositive(macdResult); // 2. 价格必须位于200日EMA上方(多头趋势确认) boolean isAboveTrend = latestPrice.compareTo(latestTrendEma) > 0; // 3. MACD金叉检查 boolean isGoldenCross = isGoldenCross(macdResult); // 4. MACD柱状线由负转正(动量转变) boolean isMacdHistTurningPositive = isMacdHistTurningPositive(macdResult); // 5. 底背离检查(增强多头信号可靠性) boolean isBottomDivergence = MACDCalculator.isBottomDivergence(closePrices, macdResult); log.info("多头信号形成, MACD有利状态: {}, 柱状线为正: {}, 底背离: {}", isMacdFavorable, macdPositive, isBottomDivergence); log.info("多头信号检查, 价格位于200日EMA上方: {}, 金叉: {}, MACD柱状线由负转正: {}, 底背离: {}", isAboveTrend, isGoldenCross, isMacdHistTurningPositive, isBottomDivergence); // 所有条件必须同时满足 boolean result = macdPositive && (isMacdFavorable || isBottomDivergence); return result; // 多头开仓条件:趋势向上 + 金叉 + (柱状线转强或底背离) return isAboveTrend && isGoldenCross && (isMacdHistTurningPositive || isBottomDivergence); } /** * 空头开仓条件检查 * * @param macdResult MACD计算结果 * @param closePrices 收盘价序列 * @param closePrices 1分钟K线收盘价序列,顺序从新到旧 * @param close1DPrices 日线K线收盘价序列,顺序从新到旧 * @return 是否满足空头开仓条件 */ private boolean isShortEntryCondition(MACDResult macdResult, List<BigDecimal> closePrices) { // 2. MACD死叉且柱状线收缩检查 boolean isMacdFavorable = isMacdDeathCrossAndContracting(macdResult); private boolean isShortEntryCondition(MACDResult macdResult, List<BigDecimal> closePrices, List<BigDecimal> close1DPrices) { // 1. 计算200日EMA(趋势过滤) // 复制并反转日线数据,确保从旧到新计算EMA List<BigDecimal> reversed1DPrices = new ArrayList<>(close1DPrices); Collections.reverse(reversed1DPrices); List<BigDecimal> trendEma = EMACalculator.calculateEMA(reversed1DPrices, trendPeriod, true); BigDecimal latestTrendEma = trendEma.get(trendEma.size() - 1); BigDecimal latestPrice = closePrices.get(0); log.info( "200日EMA:{}, 最新价格:{}", latestTrendEma, latestPrice); // 3. MACD柱状线必须为负 boolean macdNegative = isMacdNegative(macdResult); // 2. 价格必须位于200日EMA下方(空头趋势确认) boolean isBelowTrend = latestPrice.compareTo(latestTrendEma) < 0; // 3. MACD死叉检查 boolean isDeathCross = isDeathCross(macdResult); // 4. MACD柱状线由正转负(动量转变) boolean isMacdHistTurningNegative = isMacdHistTurningNegative(macdResult); // 5. 顶背离检查(增强空头信号可靠性) boolean isTopDivergence = MACDCalculator.isTopDivergence(closePrices, macdResult); log.info("空头信号形成, MACD有利状态: {}, 柱状线为负: {}, 顶背离: {}", isMacdFavorable, macdNegative, isTopDivergence); // 所有条件必须同时满足 boolean result = macdNegative && (isMacdFavorable || isTopDivergence); log.info("空头信号检查, 价格位于200日EMA下方: {}, 死叉: {}, MACD柱状线由正转负: {}, 顶背离: {}", isBelowTrend, isDeathCross, isMacdHistTurningNegative, isTopDivergence); return result; // 空头开仓条件:趋势向下 + 死叉 + (柱状线转弱或顶背离) return isBelowTrend && isDeathCross && (isMacdHistTurningNegative || isTopDivergence); } // 平仓条件检查方法 /** * 多头平仓条件检查 @@ -275,17 +341,17 @@ * @return 是否满足多头平仓条件 */ private boolean isLongExitCondition(MACDResult macdResult, BigDecimal currentPrice) { // 1. MACD柱状线由正转负(动量转变) // 多头平仓条件:MACD柱状线动量减弱(由正转弱) List<PriceData> macdData = macdResult.getMacdData(); if (macdData.size() >= 2) { PriceData latest = macdData.get(macdData.size() - 1); PriceData previous = macdData.get(macdData.size() - 2); PriceData latest = macdData.get(0); // 最新数据 PriceData previous = macdData.get(1); // 前一个数据 boolean momentumShift = previous.getMacdHist().compareTo(BigDecimal.ZERO) >= 0 && latest.getMacdHist().abs().compareTo(previous.getMacdHist().abs()) < 0; if (momentumShift) { return true; } // 柱状线由正转弱:前一根为正,当前绝对值减小 boolean momentumWeakening = previous.getMacdHist().compareTo(BigDecimal.ZERO) >= 0 && latest.getMacdHist().abs().compareTo(previous.getMacdHist().abs()) < 0; return momentumWeakening; } return false; } @@ -298,170 +364,110 @@ * @return 是否满足空头平仓条件 */ private boolean isShortExitCondition(MACDResult macdResult, BigDecimal currentPrice) { // 1. MACD柱状线由负转正(动量转变) // 空头平仓条件:MACD柱状线动量减弱(由负转弱) List<PriceData> macdData = macdResult.getMacdData(); if (macdData.size() >= 2) { PriceData latest = macdData.get(macdData.size() - 1); PriceData previous = macdData.get(macdData.size() - 2); PriceData latest = macdData.get(0); // 最新数据 PriceData previous = macdData.get(1); // 前一个数据 boolean momentumShift = previous.getMacdHist().compareTo(BigDecimal.ZERO) <= 0 && latest.getMacdHist().abs().compareTo(previous.getMacdHist().abs()) < 0; if (momentumShift) { return true; } // 柱状线由负转弱:前一根为负,当前绝对值减小 boolean momentumWeakening = previous.getMacdHist().compareTo(BigDecimal.ZERO) <= 0 && latest.getMacdHist().abs().compareTo(previous.getMacdHist().abs()) < 0; return momentumWeakening; } return false; } // MACD信号辅助方法 /** * MACD金叉且柱状线扩张检查 * 简单金叉判断 * <p> * 条件: * 1. DIF线从下往上穿过DEA线(金叉) * 2. MACD柱状线绝对值增大且为正值(动量增强) * 条件:DIF线从下往上穿过DEA线 * * @param macdResult MACD计算结果 * @return 是否形成MACD金叉或柱状线扩张 * @return 是否形成金叉 */ private boolean isMacdGoldenCrossAndExpanding(MACDResult macdResult) { private boolean isGoldenCross(MACDResult macdResult) { List<PriceData> macdData = macdResult.getMacdData(); if (macdData.size() < 3) { if (macdData.size() < 2) { return false; } PriceData latest = macdData.get(macdData.size() - 1); PriceData previous = macdData.get(macdData.size() - 2); PriceData prevPrev = macdData.get(macdData.size() - 3); PriceData latest = macdData.get(0); PriceData previous = macdData.get(1); // 金叉判断:DIF从下往上穿过DEA boolean isGoldenCross = prevPrev.getDif().compareTo(prevPrev.getDea()) <= 0 && previous.getDif().compareTo(previous.getDea()) > 0; // 柱状线扩张判断:连续正值且绝对值增大 boolean isExpanding = latest.getMacdHist().compareTo(BigDecimal.ZERO) > 0 && previous.getMacdHist().compareTo(BigDecimal.ZERO) > 0 && previous.getMacdHist().abs().compareTo(latest.getMacdHist().abs()) < 0; // 金叉柱状线扩张满足 return isGoldenCross && isExpanding; return previous.getDif().compareTo(previous.getDea()) < 0 && latest.getDif().compareTo(latest.getDea()) > 0; } /** * MACD柱状线扩张检查 * 简单死叉判断 * <p> * 条件:当前MACD柱状线绝对值大于前一根 * 条件:DIF线从上往下穿过DEA线 * * @param macdResult MACD计算结果 * @return MACD柱状线是否扩张 * @return 是否形成死叉 */ private boolean isExpanding(MACDResult macdResult) { private boolean isDeathCross(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); // MACD柱状线扩张:当前绝对值大于前一根 return latest.getMacdHist().abs().compareTo(previous.getMacdHist().abs()) > 0; } /** * MACD死叉且柱状线扩张检查 * <p> * 条件: * 1. DIF线从上往下穿过DEA线(死叉) * 2. 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); PriceData latest = macdData.get(0); PriceData previous = macdData.get(1); // 死叉判断:DIF从上往下穿过DEA boolean isDeathCross = prevPrev.getDif().compareTo(prevPrev.getDea()) >= 0 && previous.getDif().compareTo(previous.getDea()) < 0; // 优化后的死叉柱状线条件:空头趋势中,死叉应伴随柱状线扩张(绝对值增大) boolean isExpanding = latest.getMacdHist().compareTo(BigDecimal.ZERO) < 0 && previous.getMacdHist().compareTo(BigDecimal.ZERO) < 0 && previous.getMacdHist().abs().compareTo(latest.getMacdHist().abs()) < 0; // 死叉且柱状线扩张 return isDeathCross && isExpanding; return previous.getDif().compareTo(previous.getDea()) > 0 && latest.getDif().compareTo(latest.getDea()) < 0; } /** * MACD柱状线收缩检查 * MACD柱状线由负转正判断 * <p> * 条件:前一根MACD柱状线绝对值大于当前 * 条件:前一根柱状线为负,当前柱状线为正 * * @param macdResult MACD计算结果 * @return MACD柱状线是否收缩 * @return 是否由负转正 */ private boolean isContracting(MACDResult macdResult) { private boolean isMacdHistTurningPositive(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); PriceData latest = macdData.get(0); // 最新数据 PriceData previous = macdData.get(1); // 前一个数据 // MACD柱状线收缩:前一根绝对值大于当前 return previous.getMacdHist().abs().compareTo(latest.getMacdHist().abs()) > 0; // 柱状线由负转正:前一根为负,当前为正 return previous.getMacdHist().compareTo(BigDecimal.ZERO) <= 0 && latest.getMacdHist().compareTo(BigDecimal.ZERO) > 0; } /** * 检查MACD柱状线是否为正值 * MACD柱状线由正转负判断 * <p> * 条件:前一根柱状线为正,当前柱状线为负 * * @param macdResult MACD计算结果 * @return MACD柱状线是否为正值 * @return 是否由正转负 */ private boolean isMacdPositive(MACDResult macdResult) { private boolean isMacdHistTurningNegative(MACDResult macdResult) { List<PriceData> macdData = macdResult.getMacdData(); if (macdData.isEmpty()) { if (macdData.size() < 2) { return false; } return macdData.get(macdData.size() - 1).getMacdHist().compareTo(BigDecimal.ZERO) > 0; } /** * 检查MACD柱状线是否为负值 * * @param macdResult MACD计算结果 * @return MACD柱状线是否为负值 */ private boolean isMacdNegative(MACDResult macdResult) { List<PriceData> macdData = macdResult.getMacdData(); if (macdData.isEmpty()) { return false; } return macdData.get(macdData.size() - 1).getMacdHist().compareTo(BigDecimal.ZERO) < 0; } /** * 波动率过滤检查 * * @param volatility 当前波动率 * @return 波动率是否在0.5%~5%范围内 */ private boolean isVolatilityInRange(BigDecimal volatility) { BigDecimal minVolatility = new BigDecimal("0.1"); // 降低最小波动率阈值 BigDecimal maxVolatility = new BigDecimal("5.0"); return volatility.compareTo(minVolatility) >= 0 && volatility.compareTo(maxVolatility) <= 0; PriceData latest = macdData.get(0); // 最新数据 PriceData previous = macdData.get(1); // 前一个数据 // 柱状线由正转负:前一根为正,当前为负 return previous.getMacdHist().compareTo(BigDecimal.ZERO) >= 0 && latest.getMacdHist().compareTo(BigDecimal.ZERO) < 0; } } src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/OrderInfoWs.java
@@ -107,7 +107,7 @@ String stateStr = TradeOrderWs.getAccountMap(accountName).get("state"); if (StrUtil.isNotBlank(stateStr) && state.equals(stateStr)){ // 使用账号特定的Map String positionAccountName = PositionsWs.initAccountName(accountName, side); String positionAccountName = PositionsWs.initAccountName(accountName, posSide); Map<String, BigDecimal> positionsMap = PositionsWs.getAccountMap(positionAccountName); WsMapBuild.saveBigDecimalToMap(positionsMap, CoinEnums.READY_STATE.name(), WsMapBuild.parseBigDecimalSafe(CoinEnums.READY_STATE_NO.getCode())); src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/TradeOrderWs.java
@@ -1,6 +1,7 @@ package com.xcong.excoin.modules.okxNewPrice.okxWs; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.CoinEnums; @@ -36,7 +37,7 @@ public static void orderEvent(WebSocketClient webSocketClient, TradeRequestParam tradeRequestParam) { log.info("开始执行TradeOrderWs......"); log.info("开始执行TradeOrderWs{}......", JSONUtil.parse(tradeRequestParam)); String accountName = tradeRequestParam.getAccountName(); String markPx = tradeRequestParam.getMarkPx(); String instId = tradeRequestParam.getInstId(); @@ -49,8 +50,8 @@ String clOrdId = tradeRequestParam.getClOrdId(); String side = tradeRequestParam.getSide(); String sz = tradeRequestParam.getSz(); log.info("账户:{},触发价格:{},币种:{},方向:{},买卖:{},数量:{},是否允许下单:{},编号:{},", accountName, markPx, instId, posSide,side, sz, tradeType, clOrdId); log.info("账户:{},类型:{},触发价格:{},币种:{},方向:{},买卖:{},数量:{},是否允许下单:{},编号:{},", accountName,ordType, markPx, instId, posSide,side, sz, tradeType, clOrdId); //验证是否允许下单 if (StrUtil.isNotEmpty(tradeType) && OrderParamEnums.TRADE_NO.getValue().equals(tradeType)) { log.warn("账户{}不允许下单,取消发送", accountName); @@ -175,8 +176,8 @@ log.warn("下单参数缺失,取消发送"); return; } log.info("账户:{},触发价格:{},币种:{},方向:{},买卖:{},数量:{},是否允许下单:{},编号:{},", accountName, markPx, instId, posSide,side, sz, tradeType, clOrdId); log.info("账户:{},类型:{},触发价格:{},币种:{},方向:{},买卖:{},数量:{},是否允许下单:{},编号:{},", accountName,ordType, markPx, instId, posSide,side, sz, tradeType, clOrdId); //验证是否允许下单 if (StrUtil.isNotEmpty(tradeType) && OrderParamEnums.TRADE_NO.getValue().equals(tradeType)) { log.warn("账户{}不允许下单,取消发送", accountName); src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/ExchangeInfoEnum.java
@@ -13,10 +13,14 @@ * 模拟盘账户信息 * 存储了模拟盘交易所需的API密钥、秘钥和通过码 */ OKX_UAT("ffb4e79f-fcf5-4afb-82c5-2fbb64123f61", "AA06C5ED1D7C7F5AFE6484052E231C55", OKX_UAT("f512673b-2685-4fcb-9bb1-2ae8db745d62", "B0C1CC8F39625B41140D93DC25039E33", "Aa12345678@", false); true); // OKX_UAT("ffb4e79f-fcf5-4afb-82c5-2fbb64123f61", // "AA06C5ED1D7C7F5AFE6484052E231C55", // "Aa12345678@", // false); // /** // * 模拟盘账户信息 src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/enums/DefaultUrls.java
@@ -5,7 +5,7 @@ public static final String USDM_UAT_WSS_URL = "wss://wspap.okx.com:8443"; //public static final String USDM_UAT_WSS_URL = "wss://ws.okx.com:8443"; //USD-M Futures public static final String USDM_PROD_URL = "https://aws.okx.com"; public static final String USDM_PROD_URL = "https://www.okx.com"; public static final String USDM_PROD_WS_URL = "wss://ws.okx.com:8443"; //比特币买入数量 public static final String BTC_BUYNUMBER = "0.001";