package com.xcong.excoin.modules.okxNewPrice.indicator; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.math.BigDecimal; import java.util.List; /** * 15分钟时间粒度的交易策略实现 * 专门针对100个15分钟数据点设计的策略,包含明确的多空方向选择和开仓平仓方法 */ @Slf4j public class FifteenMinuteTradingStrategy { @Getter @Setter public static class TradingResult { private Direction direction; // 多空方向 private PositionSignal signal; // 开仓平仓信号 private String indicatorStatus; // 指标状态 public TradingResult(Direction direction, PositionSignal signal, String indicatorStatus) { this.direction = direction; this.signal = signal; this.indicatorStatus = indicatorStatus; } } public enum Direction { LONG, // 多头方向 SHORT, // 空头方向 RANGING // 震荡行情 } public enum PositionSignal { OPEN_LONG, // 开多仓 OPEN_SHORT, // 开空仓 CLOSE_LONG, // 平多仓 CLOSE_SHORT, // 平空仓 HOLD, // 持有 STAY_OUT // 观望 } private final MA ma; private final AdvancedMA advancedMA; private final BOLL boll; private final KDJ kdj; private final MACD macd; private final RSI rsi; public FifteenMinuteTradingStrategy() { // 15分钟数据优化的参数配置 this.ma = new MA(); this.advancedMA = new AdvancedMA(); this.boll = new BOLL(20, 2.0); // BOLL使用默认20周期 this.kdj = new KDJ(9); // KDJ使用默认9周期 this.macd = new MACD(); // MACD使用默认12/26/9周期 this.rsi = new RSI(14); // RSI使用默认14周期 } /** * 计算所有指标 * @param prices 15分钟价格数据(至少100个数据点) */ private void calculateIndicators(List prices) { ma.calculate(prices); advancedMA.calculateTripleEMA(prices); boll.calculate(prices); kdj.calculate(prices); macd.calculate(prices); rsi.calculate(prices); } /** * 判断市场是否处于震荡行情 * @return 是否为震荡行情 */ private boolean isRangeMarket() { // AdvancedMA三线粘合 + RSI(40-60) + BOLL带宽收窄 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; } /** * 获取多空方向选择 * @param prices 15分钟价格数据(100个数据点) * @return 多空方向 */ public Direction getDirection(List prices) { if (prices == null || prices.size() < 100) { throw new IllegalArgumentException("需要至少100个15分钟价格数据点"); } calculateIndicators(prices); // 震荡过滤 if (isRangeMarket()) { return Direction.RANGING; } BigDecimal currentPrice = prices.get(prices.size() - 1); // 多头信号判断:MA多头排列 + MACD金叉 + RSI(30-70) + BOLL价格在上轨与中轨之间 boolean isLongSignal = ma.getEma5().compareTo(ma.getEma10()) > 0 && // MA5 > MA10 ma.getEma10().compareTo(ma.getEma20()) > 0 && // MA10 > MA20 macd.getDif().compareTo(macd.getDea()) > 0 && // MACD金叉 rsi.getRsi().compareTo(new BigDecimal(30)) > 0 && rsi.getRsi().compareTo(new BigDecimal(70)) < 0 && // RSI(30-70) currentPrice.compareTo(boll.getMid()) > 0 && currentPrice.compareTo(boll.getUpper()) < 0; // BOLL价格在上轨与中轨之间 // 空头信号判断:MA空头排列 + MACD死叉 + RSI(30-70) + BOLL价格在下轨与中轨之间 boolean isShortSignal = ma.getEma5().compareTo(ma.getEma10()) < 0 && // MA5 < MA10 ma.getEma10().compareTo(ma.getEma20()) < 0 && // MA10 < MA20 macd.getDif().compareTo(macd.getDea()) < 0 && // MACD死叉 rsi.getRsi().compareTo(new BigDecimal(30)) > 0 && rsi.getRsi().compareTo(new BigDecimal(70)) < 0 && // RSI(30-70) currentPrice.compareTo(boll.getMid()) < 0 && currentPrice.compareTo(boll.getLower()) > 0; // BOLL价格在下轨与中轨之间 if (isLongSignal) { return Direction.LONG; } else if (isShortSignal) { return Direction.SHORT; } else { return Direction.RANGING; } } /** * 获取开仓平仓策略信号 * @param prices 15分钟价格数据(100个数据点) * @param hasLongPosition 当前是否持有多仓 * @param hasShortPosition 当前是否持有空仓 * @return 开仓平仓信号 */ public PositionSignal getPositionSignal(List prices, boolean hasLongPosition, boolean hasShortPosition) { if (prices == null || prices.size() < 100) { throw new IllegalArgumentException("需要至少100个15分钟价格数据点"); } calculateIndicators(prices); // 震荡过滤 if (isRangeMarket()) { return PositionSignal.STAY_OUT; } BigDecimal currentPrice = prices.get(prices.size() - 1); // 开多信号:MA金叉 + MACD金叉 + KDJ金叉 + RSI中性 + 价格在BOLL中轨上方 boolean shouldOpenLong = ma.getEma5().compareTo(ma.getEma20()) > 0 && // MA金叉(5日EMA上穿20日EMA) macd.getDif().compareTo(macd.getDea()) > 0 && macd.getMacdBar().compareTo(BigDecimal.ZERO) > 0 && // MACD金叉且柱状图为正 kdj.isGoldenCross() && // KDJ金叉 rsi.getRsi().compareTo(new BigDecimal(30)) > 0 && rsi.getRsi().compareTo(new BigDecimal(70)) < 0 && // RSI中性 currentPrice.compareTo(boll.getMid()) > 0; // 价格在BOLL中轨上方 // 开空信号:MA死叉 + MACD死叉 + KDJ死叉 + RSI中性 + 价格在BOLL中轨下方 boolean shouldOpenShort = ma.getEma5().compareTo(ma.getEma20()) < 0 && // MA死叉(5日EMA下穿20日EMA) macd.getDif().compareTo(macd.getDea()) < 0 && macd.getMacdBar().compareTo(BigDecimal.ZERO) < 0 && // MACD死叉且柱状图为负 kdj.isDeathCross() && // KDJ死叉 rsi.getRsi().compareTo(new BigDecimal(30)) > 0 && rsi.getRsi().compareTo(new BigDecimal(70)) < 0 && // RSI中性 currentPrice.compareTo(boll.getMid()) < 0; // 价格在BOLL中轨下方 // 平多信号:MA死叉 + MACD死叉 + RSI超买 + 价格跌破BOLL中轨 boolean shouldCloseLong = (ma.getEma5().compareTo(ma.getEma20()) < 0 && // MA死叉 macd.getDif().compareTo(macd.getDea()) < 0 && // MACD死叉 (rsi.isOverbought() || rsi.isExtremelyOverbought())) || // RSI超买 currentPrice.compareTo(boll.getMid()) < 0; // 价格跌破BOLL中轨 // 平空信号:MA金叉 + MACD金叉 + RSI超卖 + 价格突破BOLL中轨 boolean shouldCloseShort = (ma.getEma5().compareTo(ma.getEma20()) > 0 && // MA金叉 macd.getDif().compareTo(macd.getDea()) > 0 && // MACD金叉 (rsi.isOversold() || rsi.isExtremelyOversold())) || // RSI超卖 currentPrice.compareTo(boll.getMid()) > 0; // 价格突破BOLL中轨 // 确定开仓信号 if (shouldOpenLong && !hasLongPosition && !hasShortPosition) { return PositionSignal.OPEN_LONG; } else if (shouldOpenShort && !hasLongPosition && !hasShortPosition) { return PositionSignal.OPEN_SHORT; } // 确定平仓信号 if (shouldCloseLong && hasLongPosition) { return PositionSignal.CLOSE_LONG; } else if (shouldCloseShort && hasShortPosition) { return PositionSignal.CLOSE_SHORT; } // 无信号 return hasLongPosition || hasShortPosition ? PositionSignal.HOLD : PositionSignal.STAY_OUT; } /** * 综合获取交易结果 * @param prices 15分钟价格数据(100个数据点) * @param hasLongPosition 当前是否持有多仓 * @param hasShortPosition 当前是否持有空仓 * @return 包含多空方向和开仓平仓信号的完整交易结果 */ public TradingResult getTradingResult(List prices, boolean hasLongPosition, boolean hasShortPosition) { Direction direction = getDirection(prices); PositionSignal signal = getPositionSignal(prices, hasLongPosition, hasShortPosition); String indicatorStatus = getIndicatorStatus(); return new TradingResult(direction, signal, indicatorStatus); } /** * 获取当前指标状态 * @return 指标状态字符串 */ private String getIndicatorStatus() { return String.format("MA5: %s, MA20: %s, " + "MACD-DIF: %s, MACD-DEA: %s, MACD-BAR: %s, " + "KDJ-K: %s, KDJ-D: %s, KDJ-J: %s, " + "RSI: %s, " + "BOLL-UP: %s, BOLL-MID: %s, BOLL-DN: %s, " + "AdvancedMA-Bullish: %s, Bearish: %s, Percent: %s", ma.getEma5(), ma.getEma20(), macd.getDif(), macd.getDea(), macd.getMacdBar(), kdj.getK(), kdj.getD(), kdj.getJ(), rsi.getRsi(), boll.getUpper(), boll.getMid(), boll.getLower(), advancedMA.isBullish(), advancedMA.isBearish(), advancedMA.calculatePercent()); } }