| | |
| | | /** |
| | | * MA (Moving Average) 指标实现 |
| | | * 支持不同周期的简单移动平均线(SMA)和指数移动平均线(EMA) |
| | | * |
| | | * 作用: |
| | | * 1. 平滑价格波动,识别趋势方向 |
| | | * 2. 短周期MA上穿长周期MA形成金叉,提示买入信号 |
| | | * 3. 短周期MA下穿长周期MA形成死叉,提示卖出信号 |
| | | * 4. 价格上穿/下穿MA线,也可作为买卖参考 |
| | | * |
| | | * 价格参数类型: |
| | | * - 参数名称:prices |
| | | * - 参数类型:List<BigDecimal> |
| | | * - 参数说明:需要至少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 MA5 = 5; |
| | | public static final int MA10 = 10; |
| | | public static final int MA20 = 20; |
| | | public static final int MA30 = 30; |
| | | public static final int MA60 = 60; |
| | | // 默认周期 |
| | | 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 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指标 |
| | | * 计算所有周期的MA指标(使用当前周期设置) |
| | | * @param prices 价格列表 |
| | | */ |
| | | public void calculate(List<BigDecimal> prices) { |
| | | calculate(prices, null); |
| | | } |
| | | |
| | | /** |
| | | * 计算所有周期的MA指标,并支持动态周期调整 |
| | | * @param prices 价格列表 |
| | | * @param volatility 标准化波动率(ATR百分比),用于动态调整周期 |
| | | */ |
| | | public void calculate(List<BigDecimal> prices, BigDecimal volatility) { |
| | | if (prices == null || prices.size() < 1) { |
| | | return; |
| | | } |
| | | |
| | | // 如果提供了波动率,则动态调整周期 |
| | | if (volatility != null) { |
| | | adjustPeriodsByVolatility(volatility); |
| | | } |
| | | |
| | | // 计算SMA |
| | | if (prices.size() >= MA5) { |
| | | ma5 = calculateMA(prices, MA5); |
| | | if (prices.size() >= ma5Period) { |
| | | ma5 = calculateMA(prices, ma5Period); |
| | | } |
| | | if (prices.size() >= MA10) { |
| | | ma10 = calculateMA(prices, MA10); |
| | | if (prices.size() >= ma10Period) { |
| | | ma10 = calculateMA(prices, ma10Period); |
| | | } |
| | | if (prices.size() >= MA20) { |
| | | ma20 = calculateMA(prices, MA20); |
| | | if (prices.size() >= ma20Period) { |
| | | ma20 = calculateMA(prices, ma20Period); |
| | | } |
| | | if (prices.size() >= MA30) { |
| | | ma30 = calculateMA(prices, MA30); |
| | | if (prices.size() >= ma30Period) { |
| | | ma30 = calculateMA(prices, ma30Period); |
| | | } |
| | | if (prices.size() >= MA60) { |
| | | ma60 = calculateMA(prices, MA60); |
| | | if (prices.size() >= ma60Period) { |
| | | ma60 = calculateMA(prices, ma60Period); |
| | | } |
| | | |
| | | // 计算EMA |
| | | prevEma5 = calculateEMA(prices, MA5, prevEma5); |
| | | prevEma5 = calculateEMA(prices, ma5Period, prevEma5); |
| | | ema5 = prevEma5; |
| | | |
| | | prevEma10 = calculateEMA(prices, MA10, prevEma10); |
| | | prevEma10 = calculateEMA(prices, ma10Period, prevEma10); |
| | | ema10 = prevEma10; |
| | | |
| | | prevEma20 = calculateEMA(prices, MA20, prevEma20); |
| | | prevEma20 = calculateEMA(prices, ma20Period, prevEma20); |
| | | ema20 = prevEma20; |
| | | |
| | | prevEma30 = calculateEMA(prices, MA30, prevEma30); |
| | | prevEma30 = calculateEMA(prices, ma30Period, prevEma30); |
| | | ema30 = prevEma30; |
| | | |
| | | prevEma60 = calculateEMA(prices, MA60, prevEma60); |
| | | prevEma60 = calculateEMA(prices, ma60Period, prevEma60); |
| | | ema60 = prevEma60; |
| | | |
| | | log.debug("MA计算结果 - MA5: {}, MA10: {}, MA20: {}, MA30: {}, MA60: {}", |
| | | ma5, ma10, ma20, ma30, ma60); |
| | | log.debug("EMA计算结果 - EMA5: {}, EMA10: {}, EMA20: {}, EMA30: {}, EMA60: {}", |
| | | ema5, ema10, ema20, ema30, ema60); |
| | | log.debug("MA计算结果 - MA5({}): {}, MA10({}): {}, MA20({}): {}, MA30({}): {}, MA60({}): {}", |
| | | ma5Period, ma5, ma10Period, ma10, ma20Period, ma20, ma30Period, ma30, ma60Period, ma60); |
| | | log.debug("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.debug("根据波动率{}调整MA周期: ma5={}, ma10={}, ma20={}, ma30={}, ma60={}", |
| | | volatility, ma5Period, ma10Period, ma20Period, ma30Period, ma60Period); |
| | | } |
| | | |
| | | /** |