| | |
| | | /** |
| | | * 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 { |
| | |
| | | /** |
| | | * 分析历史价格数据并生成交易指令 |
| | | * |
| | | * @param historicalPrices 历史价格序列 |
| | | * @param historical1DayPrices 日线历史价格序列 |
| | | * @param historicalPrices 历史价格序列(1分钟K线收盘价),顺序从新到旧 |
| | | * @param historical1DayPrices 日线历史价格序列,顺序从新到旧 |
| | | * @param operation 操作类型(open/close) |
| | | * @return 交易指令(包含side和posSide),如果没有交易信号则返回null |
| | | */ |
| | |
| | | /** |
| | | * 分析最新价格数据并生成开仓信号 |
| | | * |
| | | * @param closePrices 收盘价序列 |
| | | * @param close1DPrices 日线收盘价序列 |
| | | * @return 生成的交易信号(LONG、SHORT或NONE) |
| | | * @param closePrices 1分钟K线收盘价序列,顺序从新到旧 |
| | | * @param close1DPrices 日线K线收盘价序列,顺序从新到旧 |
| | | * @return 生成的交易信号(LONG_BUY、SHORT_SELL或NONE) |
| | | */ |
| | | public PositionType analyzeOpen(List<BigDecimal> closePrices, List<BigDecimal> close1DPrices) { |
| | | // 数据检查:确保有足够的数据点进行计算(需要足够数据计算200日EMA) |
| | |
| | | // 计算MACD指标 |
| | | MACDResult macdResult = MACDCalculator.calculateMACD( |
| | | closePrices, shortPeriod, longPeriod, signalPeriod); |
| | | log.info("MACD计算结果:{}", macdResult.getMacdData().get(macdResult.getMacdData().size() - 1)); |
| | | log.info("MACD计算结果:{}", macdResult.getMacdData().get(0)); |
| | | |
| | | // 多头开仓条件检查 |
| | | if (isLongEntryCondition(macdResult, closePrices, close1DPrices)) { |
| | | log.info("多头开仓信号,价格:{}", closePrices.get(closePrices.size() - 1)); |
| | | log.info("多头开仓信号,价格:{}", closePrices.get(0)); |
| | | return PositionType.LONG_BUY; |
| | | } |
| | | |
| | | // 空头开仓条件检查 |
| | | if (isShortEntryCondition(macdResult, closePrices, close1DPrices)) { |
| | | log.info("空头开仓信号,价格:{}", closePrices.get(closePrices.size() - 1)); |
| | | log.info("空头开仓信号,价格:{}", closePrices.get(0)); |
| | | return PositionType.SHORT_SELL; |
| | | } |
| | | |
| | |
| | | /** |
| | | * 分析最新价格数据并生成平仓信号 |
| | | * |
| | | * @param closePrices 收盘价序列 |
| | | * @param close1DPrices 日线收盘价序列 |
| | | * @param closePrices 1分钟K线收盘价序列,顺序从新到旧 |
| | | * @param close1DPrices 日线K线收盘价序列,顺序从新到旧 |
| | | * @return 生成的交易信号(LONG_SELL、SHORT_BUY或NONE) |
| | | */ |
| | | public PositionType analyzeClose(List<BigDecimal> closePrices, List<BigDecimal> close1DPrices) { |
| | |
| | | closePrices, shortPeriod, longPeriod, signalPeriod); |
| | | |
| | | // 最新收盘价 |
| | | BigDecimal latestPrice = closePrices.get(closePrices.size() - 1); |
| | | BigDecimal latestPrice = closePrices.get(0); |
| | | |
| | | if (isLongExitCondition(macdResult, latestPrice)) { |
| | | log.info("多头平仓信号,价格:{}", latestPrice); |
| | |
| | | * 多头开仓条件检查 |
| | | * |
| | | * @param macdResult MACD计算结果 |
| | | * @param closePrices 收盘价序列 |
| | | * @param close1DPrices 日线收盘价序列 |
| | | * @param closePrices 1分钟K线收盘价序列,顺序从新到旧 |
| | | * @param close1DPrices 日线K线收盘价序列,顺序从新到旧 |
| | | * @return 是否满足多头开仓条件 |
| | | */ |
| | | private boolean isLongEntryCondition(MACDResult macdResult, List<BigDecimal> closePrices, List<BigDecimal> close1DPrices) { |
| | | // 1. 计算200日EMA(趋势过滤) |
| | | List<BigDecimal> trendEma = EMACalculator.calculateEMA(close1DPrices, trendPeriod, true); |
| | | // 复制并反转日线数据,确保从旧到新计算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(closePrices.size() - 1); |
| | | BigDecimal latestPrice = closePrices.get(0); |
| | | log.info( "200日EMA:{}, 最新价格:{}", latestTrendEma, latestPrice); |
| | | |
| | | // 2. 价格必须位于200日EMA上方(多头趋势确认) |
| | | boolean isAboveTrend = latestPrice.compareTo(latestTrendEma) > 0; |
| | |
| | | * 空头开仓条件检查 |
| | | * |
| | | * @param macdResult MACD计算结果 |
| | | * @param closePrices 收盘价序列 |
| | | * @param close1DPrices 日线收盘价序列 |
| | | * @param closePrices 1分钟K线收盘价序列,顺序从新到旧 |
| | | * @param close1DPrices 日线K线收盘价序列,顺序从新到旧 |
| | | * @return 是否满足空头开仓条件 |
| | | */ |
| | | private boolean isShortEntryCondition(MACDResult macdResult, List<BigDecimal> closePrices, List<BigDecimal> close1DPrices) { |
| | | // 1. 计算200日EMA(趋势过滤) |
| | | List<BigDecimal> trendEma = EMACalculator.calculateEMA(close1DPrices, trendPeriod, true); |
| | | // 复制并反转日线数据,确保从旧到新计算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(closePrices.size() - 1); |
| | | BigDecimal latestPrice = closePrices.get(0); |
| | | |
| | | log.info( "200日EMA:{}, 最新价格:{}", latestTrendEma, latestPrice); |
| | | |
| | | // 2. 价格必须位于200日EMA下方(空头趋势确认) |
| | | boolean isBelowTrend = latestPrice.compareTo(latestTrendEma) < 0; |
| | |
| | | // 多头平仓条件: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 momentumWeakening = previous.getMacdHist().compareTo(BigDecimal.ZERO) >= 0 && |
| | |
| | | // 空头平仓条件: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 momentumWeakening = previous.getMacdHist().compareTo(BigDecimal.ZERO) <= 0 && |
| | |
| | | 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); |
| | | |
| | | // 金叉判断:DIF从下往上穿过DEA |
| | | return previous.getDif().compareTo(previous.getDea()) < 0 && |
| | |
| | | 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); |
| | | |
| | | // 死叉判断:DIF从上往下穿过DEA |
| | | return previous.getDif().compareTo(previous.getDea()) > 0 && |
| | |
| | | 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); // 前一个数据 |
| | | |
| | | // 柱状线由负转正:前一根为负,当前为正 |
| | | return previous.getMacdHist().compareTo(BigDecimal.ZERO) <= 0 && |
| | |
| | | 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); // 前一个数据 |
| | | |
| | | // 柱状线由正转负:前一根为正,当前为负 |
| | | return previous.getMacdHist().compareTo(BigDecimal.ZERO) >= 0 && |