feat(indicator): 优化交易策略并调整日志级别
- 将多个指标类中的日志级别从debug调整为info
- 修改KDJ指标的超买超卖判断逻辑,使用J值替代K值
- 为成交量验证增加更严格的阈值条件
- 在震荡市场情况下实现区间交易策略
- 添加generateRangeTradingSignal方法处理震荡行情交易信号
- 调整根日志级别从debug到info
- 增强黑天鹅事件过滤的日志输出
| | |
| | | prevEma55 = calculateEMA(prices, EMA55, prevEma55); |
| | | ema55 = prevEma55; |
| | | |
| | | log.debug("三重EMA计算结果 - EMA9: {}, EMA21: {}, EMA55: {}", |
| | | log.info("三重EMA计算结果 - EMA9: {}, EMA21: {}, EMA55: {}", |
| | | ema9, ema21, ema55); |
| | | } |
| | | |
| | |
| | | upper = mid.add(bandWidth).setScale(8, RoundingMode.HALF_UP); |
| | | lower = mid.subtract(bandWidth).setScale(8, RoundingMode.HALF_UP); |
| | | |
| | | log.debug("BOLL计算结果 - 中轨: {}, 上轨: {}, 下轨: {}", mid, upper, lower); |
| | | log.info("BOLL计算结果 - 中轨: {}, 上轨: {}, 下轨: {}", mid, upper, lower); |
| | | } |
| | | |
| | | /** |
| | |
| | | .subtract(d.multiply(new BigDecimal(2))) |
| | | .setScale(8, RoundingMode.HALF_UP); |
| | | |
| | | log.debug("KDJ计算结果 - K: {}, D: {}, J: {}", k, d, j); |
| | | log.info("KDJ计算结果 - K: {}, D: {}, J: {}", k, d, j); |
| | | } |
| | | |
| | | /** |
| | | * 判断超买(K > 80) |
| | | * 判断超买(J > 85) |
| | | * @return 是否超买 |
| | | */ |
| | | public boolean isOverbought() { |
| | | return k.compareTo(new BigDecimal(80)) > 0; |
| | | return j.compareTo(new BigDecimal(85)) > 0; |
| | | } |
| | | |
| | | /** |
| | | * 判断超卖(K < 20) |
| | | * 判断超卖(J < 15) |
| | | * @return 是否超卖 |
| | | */ |
| | | public boolean isOversold() { |
| | | return k.compareTo(new BigDecimal(20)) < 0; |
| | | return j.compareTo(new BigDecimal(15)) < 0; |
| | | } |
| | | |
| | | /** |
| | |
| | | prevEma60 = calculateEMA(prices, ma60Period, prevEma60); |
| | | ema60 = prevEma60; |
| | | |
| | | log.debug("MA计算结果 - MA5({}): {}, MA10({}): {}, MA20({}): {}, MA30({}): {}, MA60({}): {}", |
| | | log.info("MA计算结果 - MA5({}): {}, MA10({}): {}, MA20({}): {}, MA30({}): {}, MA60({}): {}", |
| | | ma5Period, ma5, ma10Period, ma10, ma20Period, ma20, ma30Period, ma30, ma60Period, ma60); |
| | | log.debug("EMA计算结果 - EMA5({}): {}, EMA10({}): {}, EMA20({}): {}, EMA30({}): {}, EMA60({}): {}", |
| | | log.info("EMA计算结果 - EMA5({}): {}, EMA10({}): {}, EMA20({}): {}, EMA30({}): {}, EMA60({}): {}", |
| | | ma5Period, ema5, ma10Period, ema10, ma20Period, ema20, ma30Period, ema30, ma60Period, ema60); |
| | | } |
| | | |
| | |
| | | ma30Period = volatility.compareTo(highVolatility) < 0 ? 30 : 24; |
| | | ma60Period = volatility.compareTo(highVolatility) < 0 ? 50 : 34; |
| | | |
| | | log.debug("根据波动率{}调整MA周期: ma5={}, ma10={}, ma20={}, ma30={}, ma60={}", |
| | | log.info("根据波动率{}调整MA周期: ma5={}, ma10={}, ma20={}, ma30={}, ma60={}", |
| | | volatility, ma5Period, ma10Period, ma20Period, ma30Period, ma60Period); |
| | | } |
| | | |
| | |
| | | // 计算MACD柱状图 |
| | | macdBar = dif.subtract(dea).multiply(new BigDecimal(2)).setScale(8, RoundingMode.HALF_UP); |
| | | |
| | | log.debug("MACD计算结果 - DIF: {}, DEA: {}, MACD柱状图: {}, 参数: fast={}, slow={}, signal={}", |
| | | log.info("MACD计算结果 - DIF: {}, DEA: {}, MACD柱状图: {}, 参数: fast={}, slow={}, signal={}", |
| | | dif, dea, macdBar, fastPeriod, slowPeriod, signalPeriod); |
| | | } |
| | | |
| | |
| | | signalPeriod = 5; |
| | | } |
| | | |
| | | log.debug("根据波动率{}调整MACD周期: fast={}, slow={}, signal={}", |
| | | log.info("根据波动率{}调整MACD周期: fast={}, slow={}, signal={}", |
| | | volatility, fastPeriod, slowPeriod, signalPeriod); |
| | | } |
| | | |
| | |
| | | .setScale(8, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | log.debug("RSI计算结果 - RSI({}): {}", period, rsi); |
| | | log.info("RSI计算结果 - RSI({}): {}", period, rsi); |
| | | } |
| | | |
| | | /** |
| | |
| | | // 计算所有指标 |
| | | calculateIndicators(prices, high, low, close); |
| | | |
| | | // 检查是否为震荡市场,如果是,则无信号 |
| | | // 检查是否为震荡市场,如果是,则执行区间交易策略 |
| | | if (isRangeMarket()) { |
| | | log.debug("当前市场为震荡行情,不产生信号"); |
| | | return SignalType.NONE; |
| | | 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("生成卖出信号"); |
| | | 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; |
| | | } |
| | | |
| | |
| | | |
| | | /** |
| | | * 成交量验证辅助方法 |
| | | * 增强版:当前成交量需大于20周期均量的1.2倍,以过滤无量反弹/回调的假信号 |
| | | * @param volume 成交量列表 |
| | | * @return 是否通过成交量验证 |
| | | */ |
| | |
| | | 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; |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | // 使用动态参数计算指标 |
| | | if (config.isEnableDynamicParams()) { |
| | | log.debug("使用动态参数计算指标,波动率: {}", volatility); |
| | | log.info("使用动态参数计算指标,波动率: {}", volatility); |
| | | ma.calculate(prices, volatility); |
| | | macd.calculate(prices, volatility); |
| | | } else { |
| | |
| | | |
| | | return isMaConverged && isRsiNeutral && isBollNarrow; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * 根据15分钟时间框架指标确定市场方向(做多/做空/震荡) |
| | |
| | | } |
| | | |
| | | /** |
| | | * 生成区间交易信号 |
| | | * 在震荡行情下,使用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中轨 → 平仓信号 |
| | | |
| | | // 价格触及BOLL下轨 |
| | | boolean isPriceNearBollLower = currentPrice.compareTo(boll.getLower()) >= 0 && |
| | | currentPrice.compareTo(boll.getLower().multiply(new BigDecimal("1.005"))) <= 0; |
| | | |
| | | // 价格触及BOLL上轨 |
| | | boolean isPriceNearBollUpper = currentPrice.compareTo(boll.getUpper()) <= 0 && |
| | | currentPrice.compareTo(boll.getUpper().multiply(new BigDecimal("0.995"))) >= 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; |
| | | |
| | | // 成交量验证(当前成交量大于20周期均值的1.2倍) |
| | | boolean isVolumeValid = volumeConfirm(volume) && |
| | | volume.get(volume.size() - 1).compareTo(calculateMA(volume, config.getVolumeMaPeriod()).multiply(new BigDecimal("1.2"))) > 0; |
| | | |
| | | // 开多逻辑:价格触及BOLL下轨且KDJ超卖且有成交量支持 |
| | | if (isPriceNearBollLower && isKdjOversold && isVolumeValid && !hasLongPosition && !hasShortPosition) { |
| | | log.info("区间交易:价格触及BOLL下轨({}), KDJ-J值({})超卖,生成买入信号", boll.getLower(), kdj.getJ()); |
| | | return SignalType.BUY; |
| | | } |
| | | |
| | | // 开空逻辑:价格触及BOLL上轨且KDJ超买且有成交量支持 |
| | | if (isPriceNearBollUpper && isKdjOverbought && isVolumeValid && !hasLongPosition && !hasShortPosition) { |
| | | log.info("区间交易:价格触及BOLL上轨({}), KDJ-J值({})超买,生成卖出信号", boll.getUpper(), kdj.getJ()); |
| | | 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; |
| | | } |
| | | |
| | | /** |
| | | * 计算动态杠杆倍数 |
| | | * 杠杆倍数 = 基础杠杆 * (波动率阈值/当前波动率) |
| | | * @param high 最高价列表 |
| | |
| | | // 限制杠杆范围在1x-10x之间 |
| | | leverage = leverage.min(new BigDecimal(10)).max(BigDecimal.ONE); |
| | | |
| | | log.debug("动态杠杆计算 - 基础杠杆: {}, 波动率阈值: {}, 当前波动率: {}, 计算杠杆: {}", |
| | | log.info("动态杠杆计算 - 基础杠杆: {}, 波动率阈值: {}, 当前波动率: {}, 计算杠杆: {}", |
| | | config.getBaseLeverage(), volatilityThreshold, currentVolatility, leverage); |
| | | |
| | | return leverage; |
| | |
| | | return direction == Direction.LONG ? ma.getEma20() : ma.getEma20(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * 黑天鹅事件过滤 |
| | | * 规避重大事件前后30分钟、链上大额转账、异常资金费率 |
| | |
| | | boolean shouldAvoid = isAbnormalFundingRate || hasLargeTransfer || hasUpcomingEvent; |
| | | |
| | | if (shouldAvoid) { |
| | | log.debug("黑天鹅事件过滤触发 - 资金费率异常: {}, 大额转账: {}, 即将发生重大事件: {}", |
| | | log.info("黑天鹅事件过滤触发 - 资金费率异常: {}, 大额转账: {}, 即将发生重大事件: {}", |
| | | isAbnormalFundingRate, hasLargeTransfer, hasUpcomingEvent); |
| | | } |
| | | |
| | |
| | | <logger name="java.sql" level="DEBUG" /> |
| | | </springProfile> |
| | | |
| | | <root level="debug"> |
| | | <root level="info"> |
| | | <appender-ref ref="CONSOLE" /> |
| | | <appender-ref ref="DEBUG_FILE" /> |
| | | <appender-ref ref="INFO_FILE" /> |