Administrator
6 days ago 97b7f914d060b2a052299416384fc80e061f4b1d
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MacdMaStrategy.java
@@ -1,22 +1,29 @@
/**
 * 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 {
@@ -82,7 +89,7 @@
     * 止损比例=1%, 止盈比例=2%
     */
    public MacdMaStrategy() {
        this(5, 10, 9, 20, new BigDecimal("0.01"), new BigDecimal("0.02"));
        this(12, 26, 9, 20, new BigDecimal("0.01"), new BigDecimal("0.02"));
    }
    /**
@@ -127,8 +134,8 @@
    /**
     * 分析历史价格数据并生成交易指令
     *
     * @param historicalPrices 历史价格序列
     * @param historical1DayPrices 日线历史价格序列
     * @param historicalPrices 历史价格序列(1分钟K线收盘价),顺序从新到旧
     * @param historical1DayPrices 日线历史价格序列,顺序从新到旧
     * @param operation 操作类型(open/close)
     * @return 交易指令(包含side和posSide),如果没有交易信号则返回null
     */
@@ -148,9 +155,9 @@
    /**
     * 分析最新价格数据并生成开仓信号
     *
     * @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)
@@ -162,17 +169,17 @@
        // 计算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;
        }
        
@@ -183,8 +190,8 @@
    /**
     * 分析最新价格数据并生成平仓信号
     *
     * @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) {
@@ -199,7 +206,7 @@
                closePrices, shortPeriod, longPeriod, signalPeriod);
        // 最新收盘价
        BigDecimal latestPrice = closePrices.get(closePrices.size() - 1);
        BigDecimal latestPrice = closePrices.get(0);
        if (isLongExitCondition(macdResult, latestPrice)) {
            log.info("多头平仓信号,价格:{}", latestPrice);
@@ -253,15 +260,19 @@
     * 多头开仓条件检查
     *
     * @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;
@@ -286,15 +297,20 @@
     * 空头开仓条件检查
     * 
     * @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;
@@ -328,8 +344,8 @@
        // 多头平仓条件: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 && 
@@ -351,8 +367,8 @@
        // 空头平仓条件: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 &&
@@ -380,11 +396,11 @@
            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 previous.getDif().compareTo(previous.getDea()) < 0 &&
               latest.getDif().compareTo(latest.getDea()) > 0;
    }
    
@@ -402,11 +418,11 @@
            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 previous.getDif().compareTo(previous.getDea()) > 0 &&
               latest.getDif().compareTo(latest.getDea()) < 0;
    }
    
@@ -424,8 +440,8 @@
            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 && 
@@ -446,12 +462,12 @@
            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 && 
               latest.getMacdHist().compareTo(BigDecimal.ZERO) < 0;
    }
}
}