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; /** * MA (Moving Average) 指标实现 * 支持不同周期的简单移动平均线(SMA)和指数移动平均线(EMA) * * 作用: * 1. 平滑价格波动,识别趋势方向 * 2. 短周期MA上穿长周期MA形成金叉,提示买入信号 * 3. 短周期MA下穿长周期MA形成死叉,提示卖出信号 * 4. 价格上穿/下穿MA线,也可作为买卖参考 * * 价格参数类型: * - 参数名称:prices * - 参数类型:List * - 参数说明:需要至少1个价格数据点用于计算,根据不同周期需求更多数据点 * * 推荐时间粒度及优缺点: * 1. 1分钟(1m): * - 优点:反应迅速,适合超短线交易 * - 缺点:噪音多,容易产生虚假信号 * 2. 5分钟(5m): * - 优点:平衡了反应速度和噪音过滤 * - 缺点:仍有一定噪音 * 3. 15分钟(15m): * - 优点:适合日内交易,信号相对可靠 * - 缺点:反应速度较慢 * 4. 1小时(1h)及以上: * - 优点:趋势信号明确,虚假信号少 * - 缺点:反应滞后,不适合短线交易 */ @Slf4j @Getter @Setter public class MA extends IndicatorBase { // 默认周期 public static final int DEFAULT_MA5 = 5; public static final int DEFAULT_MA10 = 10; public static final int DEFAULT_MA20 = 20; public static final int DEFAULT_MA30 = 30; public static final int DEFAULT_MA60 = 60; // 动态周期参数 private int ma5Period; private int ma10Period; private int ma20Period; private int ma30Period; private int ma60Period; private BigDecimal ma5 = BigDecimal.ZERO; private BigDecimal ma10 = BigDecimal.ZERO; private BigDecimal ma20 = BigDecimal.ZERO; private BigDecimal ma30 = BigDecimal.ZERO; private BigDecimal ma60 = BigDecimal.ZERO; private BigDecimal ema5 = BigDecimal.ZERO; private BigDecimal ema10 = BigDecimal.ZERO; private BigDecimal ema20 = BigDecimal.ZERO; private BigDecimal ema30 = BigDecimal.ZERO; private BigDecimal ema60 = BigDecimal.ZERO; private BigDecimal prevEma5 = null; private BigDecimal prevEma10 = null; private BigDecimal prevEma20 = null; private BigDecimal prevEma30 = null; private BigDecimal prevEma60 = null; // 构造函数使用默认周期 public MA() { this.ma5Period = DEFAULT_MA5; this.ma10Period = DEFAULT_MA10; this.ma20Period = DEFAULT_MA20; this.ma30Period = DEFAULT_MA30; this.ma60Period = DEFAULT_MA60; } /** * 计算所有周期的MA指标(使用当前周期设置) * @param prices 价格列表 */ public void calculate(List prices) { calculate(prices, null); } /** * 计算所有周期的MA指标,并支持动态周期调整 * @param prices 价格列表 * @param volatility 标准化波动率(ATR百分比),用于动态调整周期 */ public void calculate(List prices, BigDecimal volatility) { if (prices == null || prices.size() < 1) { return; } // 如果提供了波动率,则动态调整周期 if (volatility != null) { adjustPeriodsByVolatility(volatility); } // 计算SMA if (prices.size() >= ma5Period) { ma5 = calculateMA(prices, ma5Period); } if (prices.size() >= ma10Period) { ma10 = calculateMA(prices, ma10Period); } if (prices.size() >= ma20Period) { ma20 = calculateMA(prices, ma20Period); } if (prices.size() >= ma30Period) { ma30 = calculateMA(prices, ma30Period); } if (prices.size() >= ma60Period) { ma60 = calculateMA(prices, ma60Period); } // 计算EMA prevEma5 = calculateEMA(prices, ma5Period, prevEma5); ema5 = prevEma5; prevEma10 = calculateEMA(prices, ma10Period, prevEma10); ema10 = prevEma10; prevEma20 = calculateEMA(prices, ma20Period, prevEma20); ema20 = prevEma20; prevEma30 = calculateEMA(prices, ma30Period, prevEma30); ema30 = prevEma30; prevEma60 = calculateEMA(prices, ma60Period, prevEma60); ema60 = prevEma60; log.info("MA计算结果 - MA5({}): {}, MA10({}): {}, MA20({}): {}, MA30({}): {}, MA60({}): {}", ma5Period, ma5, ma10Period, ma10, ma20Period, ma20, ma30Period, ma30, ma60Period, ma60); log.info("EMA计算结果 - EMA5({}): {}, EMA10({}): {}, EMA20({}): {}, EMA30({}): {}, EMA60({}): {}", ma5Period, ema5, ma10Period, ema10, ma20Period, ema20, ma30Period, ema30, ma60Period, ema60); } /** * 根据波动率调整MA周期 * @param volatility 标准化波动率(ATR百分比) */ private void adjustPeriodsByVolatility(BigDecimal volatility) { // 根据波动率缩放均线周期 // 3%、5%、8%作为ATR阈值 BigDecimal lowVolatility = new BigDecimal(3); BigDecimal midVolatility = new BigDecimal(5); BigDecimal highVolatility = new BigDecimal(8); // 快速MA周期 (ma5) ma5Period = volatility.compareTo(lowVolatility) < 0 ? 10 : 6; // 中期MA周期 (ma10, ma20) ma10Period = volatility.compareTo(midVolatility) < 0 ? 10 : 8; ma20Period = volatility.compareTo(midVolatility) < 0 ? 21 : 13; // 长期MA周期 (ma30, ma60) ma30Period = volatility.compareTo(highVolatility) < 0 ? 30 : 24; ma60Period = volatility.compareTo(highVolatility) < 0 ? 50 : 34; log.info("根据波动率{}调整MA周期: ma5={}, ma10={}, ma20={}, ma30={}, ma60={}", volatility, ma5Period, ma10Period, ma20Period, ma30Period, ma60Period); } /** * 判断短期均线是否上穿长期均线(金叉) * @param shortMA 短期均线 * @param longMA 长期均线 * @param prevShortMA 前一期短期均线 * @param prevLongMA 前一期长期均线 * @return 是否形成金叉 */ public boolean isGoldenCross(BigDecimal shortMA, BigDecimal longMA, BigDecimal prevShortMA, BigDecimal prevLongMA) { return prevShortMA != null && prevLongMA != null && prevShortMA.compareTo(prevLongMA) < 0 && shortMA.compareTo(longMA) > 0; } /** * 判断短期均线是否下穿长期均线(死叉) * @param shortMA 短期均线 * @param longMA 长期均线 * @param prevShortMA 前一期短期均线 * @param prevLongMA 前一期长期均线 * @return 是否形成死叉 */ public boolean isDeathCross(BigDecimal shortMA, BigDecimal longMA, BigDecimal prevShortMA, BigDecimal prevLongMA) { return prevShortMA != null && prevLongMA != null && prevShortMA.compareTo(prevLongMA) > 0 && shortMA.compareTo(longMA) < 0; } /** * 判断价格是否上穿均线 * @param price 当前价格 * @param ma 均线值 * @param prevPrice 前一期价格 * @param prevMA 前一期均线值 * @return 是否上穿 */ public boolean isPriceCrossUp(BigDecimal price, BigDecimal ma, BigDecimal prevPrice, BigDecimal prevMA) { return prevPrice != null && prevMA != null && prevPrice.compareTo(prevMA) < 0 && price.compareTo(ma) > 0; } /** * 判断价格是否下穿均线 * @param price 当前价格 * @param ma 均线值 * @param prevPrice 前一期价格 * @param prevMA 前一期均线值 * @return 是否下穿 */ public boolean isPriceCrossDown(BigDecimal price, BigDecimal ma, BigDecimal prevPrice, BigDecimal prevMA) { return prevPrice != null && prevMA != null && prevPrice.compareTo(prevMA) > 0 && price.compareTo(ma) < 0; } }