From f81e196ce000319117e268edc3e67f6e75d953c3 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Sat, 27 Dec 2025 20:22:05 +0800
Subject: [PATCH] fix(okxNewPrice): 修复交易参数设置问题
---
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/TradingStrategy.java | 487 +++++++++++++++++++++++++++++++++++++++++++----------
1 files changed, 389 insertions(+), 98 deletions(-)
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/TradingStrategy.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/TradingStrategy.java
index 0605d0f..558ad79 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/TradingStrategy.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/TradingStrategy.java
@@ -7,6 +7,7 @@
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
+import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
@@ -109,43 +110,43 @@
// 计算所有指标
calculateIndicators(prices, high, low, close);
- // 检查是否为震荡市场,如果是,则无信号
- if (isRangeMarket()) {
- log.debug("当前市场为震荡行情,不产生信号");
- return SignalType.NONE;
+ // 检查是否为震荡市场,如果是,则执行区间交易策略
+ if (isRangeMarket(prices)) {
+ log.info("当前市场为震荡行情,执行区间交易策略");
+ return generateRangeTradingSignal(currentPrice, volume, hasLongPosition, hasShortPosition);
}
// 黑天鹅事件过滤
if (blackSwanFilter(fundingRate, hasLargeTransfer, hasUpcomingEvent)) {
- log.debug("黑天鹅事件过滤触发,不产生信号");
+ log.info("黑天鹅事件过滤触发,不产生信号");
return SignalType.NONE;
}
// 开多信号
if (shouldOpenLong(currentPrice, prices, volume, fiveMinPrices, oneHourPrices, fourHourPrices) && !hasLongPosition && !hasShortPosition) {
- log.debug("生成买入信号");
+ log.info("生成买入信号");
return SignalType.BUY;
}
// 开空信号
- if (shouldOpenShort(currentPrice, volume, fiveMinPrices, oneHourPrices, fourHourPrices) && !hasLongPosition && !hasShortPosition) {
- log.debug("生成卖出信号");
+ if (shouldOpenShort(currentPrice, prices, volume, fiveMinPrices, oneHourPrices, fourHourPrices) && !hasLongPosition && !hasShortPosition) {
+ log.info("生成卖出信号");
return SignalType.SELL;
}
// 平多信号
if (shouldCloseLong(currentPrice, volume, fiveMinPrices, oneHourPrices, fourHourPrices) && hasLongPosition) {
- log.debug("生成平多信号");
+ log.info("生成平多信号");
return SignalType.CLOSE_BUY;
}
// 平空信号
if (shouldCloseShort(currentPrice, volume, fiveMinPrices, oneHourPrices, fourHourPrices) && hasShortPosition) {
- log.debug("生成平空信号");
+ log.info("生成平空信号");
return SignalType.CLOSE_SELL;
}
- log.debug("未生成信号");
+ log.info("未生成信号");
return SignalType.NONE;
}
@@ -231,10 +232,28 @@
// 创建临时MA指标用于判断趋势
MA tempMA = new MA();
+ MACD tempMACD = new MACD();
+
+ // 计算指标
tempMA.calculate(prices);
+ tempMACD.calculate(prices);
- // 简单的趋势判断:短期MA > 长期MA
- return tempMA.getEma5().compareTo(tempMA.getEma20()) > 0;
+ // 优化后的多头趋势判断:
+ // 1. MA多头排列:短期MA > 中期MA > 长期MA
+ // 2. 所有均线向上发散
+ // 3. MACD趋势:DIFF线在DEA线之上
+ boolean isMaBullish = tempMA.getEma5().compareTo(tempMA.getEma10()) > 0 &&
+ tempMA.getEma10().compareTo(tempMA.getEma20()) > 0;
+
+ // 判断均线向上发散(简单方法:近期均线斜率为正)
+ boolean isMaDivergingUp = calculateMaSlope(prices, 5) > 0 &&
+ calculateMaSlope(prices, 10) > 0 &&
+ calculateMaSlope(prices, 20) > 0;
+
+ // MACD趋势判断
+ boolean isMacdBullish = tempMACD.getDif().compareTo(tempMACD.getDea()) > 0;
+
+ return isMaBullish && isMaDivergingUp && isMacdBullish;
}
/**
@@ -249,14 +268,59 @@
// 创建临时MA指标用于判断趋势
MA tempMA = new MA();
+ MACD tempMACD = new MACD();
+
+ // 计算指标
tempMA.calculate(prices);
+ tempMACD.calculate(prices);
- // 简单的趋势判断:短期MA < 长期MA
- return tempMA.getEma5().compareTo(tempMA.getEma20()) < 0;
+ // 优化后的空头趋势判断:
+ // 1. MA空头排列:短期MA < 中期MA < 长期MA
+ // 2. 所有均线向下发散
+ // 3. MACD趋势:DIFF线在DEA线之下
+ boolean isMaBearish = tempMA.getEma5().compareTo(tempMA.getEma10()) < 0 &&
+ tempMA.getEma10().compareTo(tempMA.getEma20()) < 0;
+
+ // 判断均线向下发散(简单方法:近期均线斜率为负)
+ boolean isMaDivergingDown = calculateMaSlope(prices, 5) < 0 &&
+ calculateMaSlope(prices, 10) < 0 &&
+ calculateMaSlope(prices, 20) < 0;
+
+ // MACD趋势判断
+ boolean isMacdBearish = tempMACD.getDif().compareTo(tempMACD.getDea()) < 0;
+
+ return isMaBearish && isMaDivergingDown && isMacdBearish;
}
/**
+ * 计算均线斜率
+ * @param prices 价格数据
+ * @param period 均线周期
+ * @return 均线斜率(正数表示向上,负数表示向下)
+ */
+ private double calculateMaSlope(List<BigDecimal> prices, int period) {
+ if (prices == null || prices.size() < period * 2) {
+ return 0; // 数据不足
+ }
+
+ // 获取最近两个周期的均线值
+ int endIndex = prices.size() - 1;
+ int startIndex = endIndex - period + 1;
+
+ // 计算当前周期的均线
+ BigDecimal currentMa = calculateMA(prices.subList(startIndex, endIndex + 1), period);
+
+ // 计算前一个周期的均线
+ int prevStartIndex = startIndex - period;
+ BigDecimal prevMa = calculateMA(prices.subList(prevStartIndex, startIndex), period);
+
+ // 计算斜率(简单差值法)
+ return currentMa.subtract(prevMa).doubleValue();
+ }
+
+ /**
* 成交量验证辅助方法
+ * 增强版:当前成交量需大于20周期均量的1.2倍,以过滤无量反弹/回调的假信号
* @param volume 成交量列表
* @return 是否通过成交量验证
*/
@@ -269,8 +333,8 @@
BigDecimal volumeMA = calculateMA(volume, config.getVolumeMaPeriod());
BigDecimal currentVolume = volume.get(volume.size() - 1);
- // 成交量需要大于均线
- return currentVolume.compareTo(volumeMA) > 0;
+ // 增强验证:成交量需要大于1.2倍均线
+ return currentVolume.compareTo(volumeMA.multiply(new BigDecimal("1.2"))) > 0;
}
/**
@@ -327,7 +391,7 @@
// 使用动态参数计算指标
if (config.isEnableDynamicParams()) {
- log.debug("使用动态参数计算指标,波动率: {}", volatility);
+ log.info("使用动态参数计算指标,波动率: {}", volatility);
ma.calculate(prices, volatility);
macd.calculate(prices, volatility);
} else {
@@ -344,17 +408,83 @@
/**
* 检查是否为震荡市场
+ * @param prices 价格数据列表
* @return 是否为震荡市场
*/
- private boolean isRangeMarket() {
- // 高级MA线收敛 + RSI(40-60) + 布林带收窄
- boolean isMaConverged = advancedMA.calculatePercent().compareTo(new BigDecimal(2)) < 0;
- boolean isRsiNeutral = rsi.getRsi().compareTo(new BigDecimal(40)) > 0 &&
- rsi.getRsi().compareTo(new BigDecimal(60)) < 0;
- boolean isBollNarrow = boll.calculateBandWidth().compareTo(new BigDecimal(0.05)) < 0;
-
- return isMaConverged && isRsiNeutral && isBollNarrow;
+ private boolean isRangeMarket(List<BigDecimal> prices) {
+ // 优化的震荡市场判断条件,放宽标准以减少RANGING信号频率
+
+ // 1. 高级MA三线收敛(小于3%,放宽条件)
+ boolean isMaConverged = advancedMA.calculatePercent().compareTo(new BigDecimal(3)) < 0;
+
+ // 2. RSI中性区间(35-65,放宽条件)
+ boolean isRsiNeutral = rsi.getRsi().compareTo(new BigDecimal(35)) > 0 &&
+ rsi.getRsi().compareTo(new BigDecimal(65)) < 0;
+
+ // 3. BOLL带宽收窄(小于0.06,放宽条件)
+ boolean isBollNarrow = boll.calculateBandWidth().compareTo(new BigDecimal(0.06)) < 0;
+
+ // 4. MACD柱状图趋近于0(多空力量平衡,放宽条件)
+ boolean isMacdBalanced = macd.getMacdBar().abs().compareTo(new BigDecimal(0.02)) < 0;
+
+ // 5. KDJ在中间区域波动(25-75,放宽条件)
+ boolean isKdjNeutral = kdj.getK().compareTo(new BigDecimal(25)) > 0 &&
+ kdj.getK().compareTo(new BigDecimal(75)) < 0 &&
+ kdj.getD().compareTo(new BigDecimal(25)) > 0 &&
+ kdj.getD().compareTo(new BigDecimal(75)) < 0 &&
+ kdj.getJ().compareTo(new BigDecimal(20)) > 0 &&
+ kdj.getJ().compareTo(new BigDecimal(80)) < 0;
+
+ // 6. 价格波动范围较小(最近20根K线最高价与最低价的比值小于1.06,放宽条件)
+ boolean isPriceVolatilityLow = calculatePriceVolatility(prices).compareTo(new BigDecimal(1.06)) < 0;
+
+ // 综合判断:只需要满足部分条件即可,增加趋势信号的机会
+ int rangeConditionsMet = 0;
+ if (isMaConverged) {
+ rangeConditionsMet++;
+ }
+ if (isRsiNeutral) {
+ rangeConditionsMet++;
+ }
+ if (isBollNarrow) {
+ rangeConditionsMet++;
+ }
+ if (isMacdBalanced) {
+ rangeConditionsMet++;
+ }
+ if (isKdjNeutral) {
+ rangeConditionsMet++;
+ }
+ if (isPriceVolatilityLow) {
+ rangeConditionsMet++;
+ }
+
+ // 只有满足4个或以上条件才判定为震荡市场
+ return rangeConditionsMet >= 4;
}
+
+ /**
+ * 计算价格波动率(最近20根K线最高价与最低价的比值)
+ * @param prices 价格数据列表
+ * @return 价格波动率
+ */
+ private BigDecimal calculatePriceVolatility(List<BigDecimal> prices) {
+ if (prices == null || prices.isEmpty()) {
+ return BigDecimal.ONE;
+ }
+
+ List<BigDecimal> recentPrices = prices.subList(Math.max(0, prices.size() - 20), prices.size());
+ if (recentPrices.isEmpty()) {
+ return BigDecimal.ONE;
+ }
+
+ BigDecimal highest = recentPrices.stream().max(BigDecimal::compareTo).orElse(BigDecimal.ONE);
+ BigDecimal lowest = recentPrices.stream().min(BigDecimal::compareTo).orElse(BigDecimal.ONE);
+
+ return highest.divide(lowest, 10, RoundingMode.HALF_UP);
+ }
+
+
/**
* 根据15分钟时间框架指标确定市场方向(做多/做空/震荡)
@@ -365,38 +495,48 @@
* @param currentPrice 当前价格
* @return 市场方向
*/
- public Direction getDirection(List<BigDecimal> prices, List<BigDecimal> high,
+ public Direction getDirection(List<BigDecimal> prices, List<BigDecimal> high,
List<BigDecimal> low, List<BigDecimal> close,
BigDecimal currentPrice) {
// 计算所有指标
calculateIndicators(prices, high, low, close);
// 检查是否为震荡市场
- if (isRangeMarket()) {
+ if (isRangeMarket(prices)) {
return Direction.RANGING;
}
- // 检查做多方向条件:MA多头排列 + MACD金叉 + RSI中性(30-70) + BOLL价格在上轨和中轨之间
+ // 优化后的多头趋势条件:
+ // 1. MA多头排列(短期MA > 中期MA > 长期MA)
+ // 2. MACD趋势(DIFF在DEA之上,代表多头趋势)
+ // 3. 价格在BOLL中轨上方
+ // 4. RSI(50-65) 为健康多头区间
boolean isMaBullish = ma.getEma5().compareTo(ma.getEma10()) > 0 &&
ma.getEma10().compareTo(ma.getEma20()) > 0;
- boolean isMacdGoldenCross = macd.getDif().compareTo(macd.getDea()) > 0;
- boolean isRsiNeutral = rsi.getRsi().compareTo(new BigDecimal(30)) > 0 &&
- rsi.getRsi().compareTo(new BigDecimal(70)) < 0;
- boolean isPriceInUpperMid = currentPrice.compareTo(boll.getMid()) > 0 &&
- currentPrice.compareTo(boll.getUpper()) < 0;
-
- if (isMaBullish && isMacdGoldenCross && isRsiNeutral && isPriceInUpperMid) {
+ boolean isMacdBullish = macd.getDif().compareTo(macd.getDea()) > 0;
+ boolean isPriceAboveBollMid = currentPrice.compareTo(boll.getMid()) > 0;
+ boolean isRsiBullish = rsi.getRsi().compareTo(new BigDecimal(50)) > 0 &&
+ rsi.getRsi().compareTo(new BigDecimal(65)) < 0;
+
+ // 检查多头信号(MA多头 + MACD金叉 + 价格在BOLL中轨上方 + RSI(50-65))
+ if (isMaBullish && isMacdBullish && isPriceAboveBollMid && isRsiBullish) {
return Direction.LONG;
}
- // 检查做空方向条件:MA空头排列 + MACD死叉 + RSI中性(30-70) + BOLL价格在下轨和中轨之间
+ // 优化后的空头趋势条件:
+ // 1. MA空头排列(短期MA < 中期MA < 长期MA)
+ // 2. MACD趋势(DIFF在DEA之下,代表空头趋势)
+ // 3. 价格在BOLL中轨下方
+ // 4. RSI(35-50) 为健康空头区间
boolean isMaBearish = ma.getEma5().compareTo(ma.getEma10()) < 0 &&
ma.getEma10().compareTo(ma.getEma20()) < 0;
- boolean isMacdDeathCross = macd.getDif().compareTo(macd.getDea()) < 0;
- boolean isPriceInLowerMid = currentPrice.compareTo(boll.getLower()) > 0 &&
- currentPrice.compareTo(boll.getMid()) < 0;
-
- if (isMaBearish && isMacdDeathCross && isRsiNeutral && isPriceInLowerMid) {
+ boolean isMacdBearish = macd.getDif().compareTo(macd.getDea()) < 0;
+ boolean isPriceBelowBollMid = currentPrice.compareTo(boll.getMid()) < 0;
+ boolean isRsiBearish = rsi.getRsi().compareTo(new BigDecimal(35)) > 0 &&
+ rsi.getRsi().compareTo(new BigDecimal(50)) < 0;
+
+ // 检查空头信号(MA空头 + MACD死叉 + 价格在BOLL中轨下方 + RSI(35-50))
+ if (isMaBearish && isMacdBearish && isPriceBelowBollMid && isRsiBearish) {
return Direction.SHORT;
}
@@ -405,8 +545,10 @@
}
/**
- * 根据用户要求检查是否应该开多仓位
- * 条件:MA金叉 + MACD金叉 + KDJ金叉 + RSI中性 + 价格高于BOLL中轨 + 多周期确认 + 成交量验证
+ * 根据优化建议检查是否应该开多仓位
+ * 使用主次结合法:
+ * - 主信号(趋势):MA多头排列 + MACD金叉
+ * - 辅信号(入场点):价格回调至BOLL中轨附近获得支撑,且出现KDJ金叉或RSI从低位回升至50以上
* @param currentPrice 当前价格
* @param prices 价格数据
* @param volume 成交量列表
@@ -419,17 +561,40 @@
List<BigDecimal> fiveMinPrices,
List<BigDecimal> oneHourPrices,
List<BigDecimal> fourHourPrices) {
- // MA金叉 (5EMA > 20EMA)
- boolean isMaGoldenCross = ma.getEma5().compareTo(ma.getEma20()) > 0;
- // MACD金叉 (DIF > DEA)
- boolean isMacdGoldenCross = macd.getDif().compareTo(macd.getDea()) > 0;
- // KDJ金叉 (K线上穿D线)
- boolean isKdjGoldenCross = kdj.isGoldenCross();
- // RSI中性 (30-70)
- boolean isRsiNeutral = rsi.getRsi().compareTo(new BigDecimal(30)) > 0 &&
+ // 主信号:趋势判断
+ // MA多头排列(短期MA > 中期MA > 长期MA)
+ boolean isMaBullish = ma.getEma5().compareTo(ma.getEma10()) > 0 &&
+ ma.getEma10().compareTo(ma.getEma20()) > 0;
+ // MACD趋势(DIFF在DEA之上)
+ boolean isMacdBullish = macd.getDif().compareTo(macd.getDea()) > 0;
+
+ // 如果主信号不满足,直接返回false
+ if (!(isMaBullish && isMacdBullish)) {
+ return false;
+ }
+
+ // 辅信号:入场点判断
+ // 价格在BOLL中轨上方,且未触及上轨(避免追高)
+ boolean isPriceInSafeZone = currentPrice.compareTo(boll.getMid()) > 0 &&
+ currentPrice.compareTo(boll.getUpper()) < 0;
+ // 价格靠近BOLL中轨
+ boolean isPriceNearBollMid = currentPrice.compareTo(boll.getMid().multiply(new BigDecimal(0.98))) > 0 &&
+ currentPrice.compareTo(boll.getMid().multiply(new BigDecimal(1.02))) < 0;
+
+ // RSI:健康区间30-70,刚从50中线向上
+ boolean isRsiHealthy = rsi.getRsi().compareTo(new BigDecimal(30)) > 0 &&
rsi.getRsi().compareTo(new BigDecimal(70)) < 0;
- // 价格高于BOLL中轨
- boolean isPriceAboveBollMid = currentPrice.compareTo(boll.getMid()) > 0;
+ boolean isRsiAboveMid = rsi.getRsi().compareTo(new BigDecimal(50)) > 0;
+
+ // KDJ:金叉或超卖区域
+ boolean isKdjGoldenCross = kdj.isGoldenCross();
+ boolean isKdjOversold = kdj.getJ().compareTo(new BigDecimal(15)) < 0;
+
+ // 入场点条件:价格在安全区域或靠近中轨,且动量健康
+ boolean isEntryPointValid = (isPriceInSafeZone || isPriceNearBollMid) &&
+ (isRsiHealthy && isRsiAboveMid) &&
+ (isKdjGoldenCross || isKdjOversold);
+
// 成交量验证
boolean isVolumeConfirmed = volumeConfirm(volume);
// 量价背离检测
@@ -437,48 +602,76 @@
// 多周期确认
boolean isMultiTimeframeConfirmed = multiTimeframeConfirm(fiveMinPrices, oneHourPrices, fourHourPrices);
- return isMaGoldenCross && isMacdGoldenCross && isKdjGoldenCross &&
- isRsiNeutral && isPriceAboveBollMid && isVolumeConfirmed &&
- !isPriceVolumeDivergence && isMultiTimeframeConfirmed;
+ return isEntryPointValid && isVolumeConfirmed && !isPriceVolumeDivergence && isMultiTimeframeConfirmed;
}
/**
- * 根据用户要求检查是否应该开空仓位
- * 条件:MA死叉 + MACD死叉 + KDJ死叉 + RSI中性 + 价格低于BOLL中轨 + 多周期确认 + 成交量验证
+ * 根据优化建议检查是否应该开空仓位
+ * 使用主次结合法:
+ * - 主信号(趋势):MA空头排列 + MACD死叉
+ * - 辅信号(入场点):价格反弹至BOLL中轨附近遇阻,且出现KDJ死叉或RSI从高位回落至50以下
* @param currentPrice 当前价格
+ * @param prices 价格数据
* @param volume 成交量列表
* @param fiveMinPrices 5分钟价格数据
* @param oneHourPrices 1小时价格数据
* @param fourHourPrices 4小时价格数据
* @return 是否应该开空
*/
- private boolean shouldOpenShort(BigDecimal currentPrice, List<BigDecimal> volume,
+ private boolean shouldOpenShort(BigDecimal currentPrice, List<BigDecimal> prices, List<BigDecimal> volume,
List<BigDecimal> fiveMinPrices,
List<BigDecimal> oneHourPrices,
List<BigDecimal> fourHourPrices) {
- // MA死叉 (5EMA < 20EMA)
- boolean isMaDeathCross = ma.getEma5().compareTo(ma.getEma20()) < 0;
- // MACD死叉 (DIF < DEA)
- boolean isMacdDeathCross = macd.getDif().compareTo(macd.getDea()) < 0;
- // KDJ死叉 (K线下穿D线)
- boolean isKdjDeathCross = kdj.isDeathCross();
- // RSI中性 (30-70)
- boolean isRsiNeutral = rsi.getRsi().compareTo(new BigDecimal(30)) > 0 &&
+ // 主信号:趋势判断
+ // MA空头排列(短期MA < 中期MA < 长期MA)
+ boolean isMaBearish = ma.getEma5().compareTo(ma.getEma10()) < 0 &&
+ ma.getEma10().compareTo(ma.getEma20()) < 0;
+ // MACD趋势(DIFF在DEA之下)
+ boolean isMacdBearish = macd.getDif().compareTo(macd.getDea()) < 0;
+
+ // 如果主信号不满足,直接返回false
+ if (!(isMaBearish && isMacdBearish)) {
+ return false;
+ }
+
+ // 辅信号:入场点判断
+ // 价格在BOLL中轨下方,且未触及下轨(避免追空)
+ boolean isPriceInSafeZone = currentPrice.compareTo(boll.getMid()) < 0 &&
+ currentPrice.compareTo(boll.getLower()) > 0;
+ // 价格靠近BOLL中轨
+ boolean isPriceNearBollMid = currentPrice.compareTo(boll.getMid().multiply(new BigDecimal(0.98))) > 0 &&
+ currentPrice.compareTo(boll.getMid().multiply(new BigDecimal(1.02))) < 0;
+
+ // RSI:健康区间30-70,刚从50中线向下
+ boolean isRsiHealthy = rsi.getRsi().compareTo(new BigDecimal(30)) > 0 &&
rsi.getRsi().compareTo(new BigDecimal(70)) < 0;
- // 价格低于BOLL中轨
- boolean isPriceBelowBollMid = currentPrice.compareTo(boll.getMid()) < 0;
+ boolean isRsiBelowMid = rsi.getRsi().compareTo(new BigDecimal(50)) < 0;
+
+ // KDJ:死叉或超买区域
+ boolean isKdjDeathCross = kdj.isDeathCross();
+ boolean isKdjOverbought = kdj.getJ().compareTo(new BigDecimal(85)) > 0;
+
+ // 入场点条件:价格在安全区域或靠近中轨,且动量健康
+ boolean isEntryPointValid = (isPriceInSafeZone || isPriceNearBollMid) &&
+ (isRsiHealthy && isRsiBelowMid) &&
+ (isKdjDeathCross || isKdjOverbought);
+
// 成交量验证
boolean isVolumeConfirmed = volumeConfirm(volume);
+ // 量价背离检测
+ boolean isPriceVolumeDivergence = hasPriceVolumeDivergence(prices, volume);
// 多周期确认(看跌)
boolean isMultiTimeframeConfirmed = multiTimeframeBearishConfirm(fiveMinPrices, oneHourPrices, fourHourPrices);
- return isMaDeathCross && isMacdDeathCross && isKdjDeathCross &&
- isRsiNeutral && isPriceBelowBollMid && isVolumeConfirmed && isMultiTimeframeConfirmed;
+ return isEntryPointValid && isVolumeConfirmed && !isPriceVolumeDivergence && isMultiTimeframeConfirmed;
}
/**
- * 根据用户要求检查是否应该平多仓位
- * 条件:MA死叉 + MACD死叉 + RSI超买 + 价格低于BOLL中轨 + 多周期确认
+ * 根据优化建议检查是否应该平多仓位
+ * 采用分层止盈止损策略:
+ * - 保护止损:价格跌破BOLL中轨
+ * - 跟踪止损:价格有效跌破移动平均线
+ * - 最终平仓:趋势反转信号(MA空头排列 + MACD死叉)
* @param currentPrice 当前价格
* @param volume 成交量列表
* @param fiveMinPrices 5分钟价格数据
@@ -490,23 +683,31 @@
List<BigDecimal> fiveMinPrices,
List<BigDecimal> oneHourPrices,
List<BigDecimal> fourHourPrices) {
- // MA死叉 (5EMA < 20EMA)
- boolean isMaDeathCross = ma.getEma5().compareTo(ma.getEma20()) < 0;
- // MACD死叉 (DIF < DEA)
- boolean isMacdDeathCross = macd.getDif().compareTo(macd.getDea()) < 0;
- // RSI超买 (>70)
- boolean isRsiOverbought = rsi.isOverbought();
- // 价格低于BOLL中轨
- boolean isPriceBelowBollMid = currentPrice.compareTo(boll.getMid()) < 0;
+ // 保护止损:价格跌破BOLL中轨(关键支撑位)
+ boolean isStopLossTriggered = currentPrice.compareTo(boll.getMid()) < 0;
+
+ // 跟踪止损:价格有效跌破短期均线(5EMA)
+ boolean isTrailingStopTriggered = currentPrice.compareTo(ma.getEma5()) < 0;
+
+ // 趋势反转信号:MA空头排列 + MACD死叉
+ boolean isMaBearish = ma.getEma5().compareTo(ma.getEma10()) < 0 &&
+ ma.getEma10().compareTo(ma.getEma20()) < 0;
+ boolean isMacdBearish = macd.getDif().compareTo(macd.getDea()) < 0;
+ boolean isTrendReversed = isMaBearish && isMacdBearish;
+
// 多周期确认(看跌)
boolean isMultiTimeframeConfirmed = multiTimeframeBearishConfirm(fiveMinPrices, oneHourPrices, fourHourPrices);
-
- return isMaDeathCross && isMacdDeathCross && isRsiOverbought && isPriceBelowBollMid && isMultiTimeframeConfirmed;
+
+ // 平多条件:保护止损触发 或 跟踪止损触发 或 (趋势反转且多周期确认)
+ return isStopLossTriggered || isTrailingStopTriggered || (isTrendReversed && isMultiTimeframeConfirmed);
}
/**
- * 根据用户要求检查是否应该平空仓位
- * 条件:MA金叉 + MACD金叉 + RSI超卖 + 价格高于BOLL中轨 + 多周期确认
+ * 根据优化建议检查是否应该平空仓位
+ * 采用分层止盈止损策略:
+ * - 保护止损:价格突破BOLL中轨
+ * - 跟踪止损:价格有效突破移动平均线
+ * - 最终平仓:趋势反转信号(MA多头排列 + MACD金叉)
* @param currentPrice 当前价格
* @param volume 成交量列表
* @param fiveMinPrices 5分钟价格数据
@@ -518,18 +719,23 @@
List<BigDecimal> fiveMinPrices,
List<BigDecimal> oneHourPrices,
List<BigDecimal> fourHourPrices) {
- // MA金叉 (5EMA > 20EMA)
- boolean isMaGoldenCross = ma.getEma5().compareTo(ma.getEma20()) > 0;
- // MACD金叉 (DIF > DEA)
- boolean isMacdGoldenCross = macd.getDif().compareTo(macd.getDea()) > 0;
- // RSI超卖 (<30)
- boolean isRsiOversold = rsi.isOversold();
- // 价格高于BOLL中轨
- boolean isPriceAboveBollMid = currentPrice.compareTo(boll.getMid()) > 0;
+ // 保护止损:价格突破BOLL中轨(关键阻力位)
+ boolean isStopLossTriggered = currentPrice.compareTo(boll.getMid()) > 0;
+
+ // 跟踪止损:价格有效突破短期均线(5EMA)
+ boolean isTrailingStopTriggered = currentPrice.compareTo(ma.getEma5()) > 0;
+
+ // 趋势反转信号:MA多头排列 + MACD金叉
+ boolean isMaBullish = ma.getEma5().compareTo(ma.getEma10()) > 0 &&
+ ma.getEma10().compareTo(ma.getEma20()) > 0;
+ boolean isMacdBullish = macd.getDif().compareTo(macd.getDea()) > 0;
+ boolean isTrendReversed = isMaBullish && isMacdBullish;
+
// 多周期确认(看涨)
boolean isMultiTimeframeConfirmed = multiTimeframeConfirm(fiveMinPrices, oneHourPrices, fourHourPrices);
-
- return isMaGoldenCross && isMacdGoldenCross && isRsiOversold && isPriceAboveBollMid && isMultiTimeframeConfirmed;
+
+ // 平空条件:保护止损触发 或 跟踪止损触发 或 (趋势反转且多周期确认)
+ return isStopLossTriggered || isTrailingStopTriggered || (isTrendReversed && isMultiTimeframeConfirmed);
}
/**
@@ -544,6 +750,89 @@
String.format("BOLL-MID: %s, BOLL-UP: %s, BOLL-DN: %s, ", boll.getMid(), boll.getUpper(), boll.getLower()) +
String.format("AdvancedMA-Bullish: %s, Bearish: %s, Percent: %s",
advancedMA.isBullish(), advancedMA.isBearish(), advancedMA.calculatePercent());
+ }
+
+ /**
+ * 生成区间交易信号
+ * 在震荡行情下,使用BOLL通道作为区间边界,结合KDJ指标生成交易信号
+ * @param currentPrice 当前价格
+ * @param volume 成交量列表
+ * @param hasLongPosition 是否当前持有做多仓位
+ * @param hasShortPosition 是否当前持有做空仓位
+ * @return 交易信号
+ */
+ private SignalType generateRangeTradingSignal(BigDecimal currentPrice, List<BigDecimal> volume,
+ boolean hasLongPosition, boolean hasShortPosition) {
+ // 区间交易策略逻辑:
+ // 1. 价格触及BOLL下轨且KDJ超卖 → 买入信号
+ // 2. 价格触及BOLL上轨且KDJ超买 → 卖出信号
+ // 3. 价格回归BOLL中轨 → 平仓信号
+
+ // 检查KDJ极端超买超卖情况
+ boolean isKdjJExtremeOverbought = kdj.getJ().compareTo(new BigDecimal("100")) > 0;
+ boolean isKdjJExtremeOversold = kdj.getJ().compareTo(new BigDecimal("10")) < 0;
+
+ // 价格触及BOLL下轨(基础条件)
+ boolean isPriceNearBollLower = currentPrice.compareTo(boll.getLower()) >= 0 &&
+ currentPrice.compareTo(boll.getLower().multiply(new BigDecimal("1.01"))) <= 0;
+
+ // 价格触及BOLL上轨(基础条件)
+ boolean isPriceNearBollUpper = currentPrice.compareTo(boll.getUpper()) <= 0 &&
+ currentPrice.compareTo(boll.getUpper().multiply(new BigDecimal("0.99"))) >= 0;
+
+ // 当KDJ-J极度超买/超卖时,放宽BOLL边界要求
+ if (isKdjJExtremeOverbought) {
+ isPriceNearBollUpper = currentPrice.compareTo(boll.getUpper().multiply(new BigDecimal("0.985"))) >= 0;
+ }
+ if (isKdjJExtremeOversold) {
+ isPriceNearBollLower = currentPrice.compareTo(boll.getLower().multiply(new BigDecimal("1.015"))) <= 0;
+ }
+
+ // 价格回归BOLL中轨附近
+ boolean isPriceNearBollMid = currentPrice.compareTo(boll.getMid().multiply(new BigDecimal("0.998"))) >= 0 &&
+ currentPrice.compareTo(boll.getMid().multiply(new BigDecimal("1.002"))) <= 0;
+
+ // KDJ超卖(使用调整后的阈值)
+ boolean isKdjOversold = kdj.getJ().compareTo(new BigDecimal(15)) < 0;
+
+ // KDJ超买(使用调整后的阈值)
+ boolean isKdjOverbought = kdj.getJ().compareTo(new BigDecimal(85)) > 0;
+
+ // RSI超卖(<30)
+ boolean isRsiOversold = rsi.getRsi().compareTo(new BigDecimal(30)) < 0;
+
+ // RSI超买(>70)
+ boolean isRsiOverbought = rsi.getRsi().compareTo(new BigDecimal(70)) > 0;
+
+ // 成交量验证(当前成交量大于20周期均值的1.1倍)
+ boolean isVolumeValid = volumeConfirm(volume) &&
+ volume.get(volume.size() - 1).compareTo(calculateMA(volume, config.getVolumeMaPeriod()).multiply(new BigDecimal("1.1"))) > 0;
+
+ // 开多逻辑:价格触及BOLL下轨且KDJ超卖且RSI超卖且有成交量支持
+ if (isPriceNearBollLower && isKdjOversold && isRsiOversold && isVolumeValid && !hasLongPosition && !hasShortPosition) {
+ log.info("区间交易:价格触及BOLL下轨({}), KDJ-J值({})超卖,RSI({})超卖,生成买入信号", boll.getLower(), kdj.getJ(), rsi.getRsi());
+ return SignalType.BUY;
+ }
+
+ // 开空逻辑:价格触及BOLL上轨且KDJ超买且RSI超买且有成交量支持
+ if (isPriceNearBollUpper && isKdjOverbought && isRsiOverbought && isVolumeValid && !hasLongPosition && !hasShortPosition) {
+ log.info("区间交易:价格触及BOLL上轨({}), KDJ-J值({})超买,RSI({})超买,生成卖出信号", boll.getUpper(), kdj.getJ(), rsi.getRsi());
+ return SignalType.SELL;
+ }
+
+ // 平多逻辑:价格回归BOLL中轨
+ if (isPriceNearBollMid && hasLongPosition) {
+ log.info("区间交易:价格回归BOLL中轨({}),生成平多信号", boll.getMid());
+ return SignalType.CLOSE_BUY;
+ }
+
+ // 平空逻辑:价格回归BOLL中轨
+ if (isPriceNearBollMid && hasShortPosition) {
+ log.info("区间交易:价格回归BOLL中轨({}),生成平空信号", boll.getMid());
+ return SignalType.CLOSE_SELL;
+ }
+
+ return SignalType.NONE;
}
/**
@@ -572,7 +861,7 @@
// 限制杠杆范围在1x-10x之间
leverage = leverage.min(new BigDecimal(10)).max(BigDecimal.ONE);
- log.debug("动态杠杆计算 - 基础杠杆: {}, 波动率阈值: {}, 当前波动率: {}, 计算杠杆: {}",
+ log.info("动态杠杆计算 - 基础杠杆: {}, 波动率阈值: {}, 当前波动率: {}, 计算杠杆: {}",
config.getBaseLeverage(), volatilityThreshold, currentVolatility, leverage);
return leverage;
@@ -685,6 +974,8 @@
return direction == Direction.LONG ? ma.getEma20() : ma.getEma20();
}
+
+
/**
* 黑天鹅事件过滤
* 规避重大事件前后30分钟、链上大额转账、异常资金费率
@@ -708,7 +999,7 @@
boolean shouldAvoid = isAbnormalFundingRate || hasLargeTransfer || hasUpcomingEvent;
if (shouldAvoid) {
- log.debug("黑天鹅事件过滤触发 - 资金费率异常: {}, 大额转账: {}, 即将发生重大事件: {}",
+ log.info("黑天鹅事件过滤触发 - 资金费率异常: {}, 大额转账: {}, 即将发生重大事件: {}",
isAbnormalFundingRate, hasLargeTransfer, hasUpcomingEvent);
}
--
Gitblit v1.9.1