/** * 指数移动平均线(EMA)计算器 *

* EMA(Exponential Moving Average)是一种加权移动平均线,对近期价格赋予更高权重, * 对远期价格赋予较低权重,能够更敏感地反映价格变化趋势。 * 本计算器提供了EMA的多种计算方式,支持使用SMA作为初始值或使用第一个价格作为初始值。 */ package com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.ArrayList; import java.util.List; /** * 指数移动平均线(EMA)计算器 * *

计算公式:EMA(today) = Price(today) * k + EMA(yesterday) * (1 - k)

*

其中:k = 2 / (period + 1),period为EMA的周期

*/ public class EMACalculator { /** * 计算价格序列的指数移动平均线(EMA) * * @param prices 价格序列,使用BigDecimal确保计算精度 * @param period EMA计算周期 * @param initialSMA 是否使用SMA(简单移动平均线)作为初始值 * @return 计算得到的EMA序列,与输入价格序列一一对应 * @throws IllegalArgumentException 当输入参数无效时抛出异常 */ public static List calculateEMA(List prices, int period, boolean initialSMA) { if (prices == null || prices.isEmpty() || period <= 0) { throw new IllegalArgumentException("Invalid input parameters."); } if (initialSMA && prices.size() < period) { throw new IllegalArgumentException("Prices list too short for initial SMA."); } // 计算权重因子k = 2 / (period + 1) BigDecimal alpha = BigDecimal.valueOf(2.0).divide(BigDecimal.valueOf(period + 1), 10, RoundingMode.HALF_UP); List ema = new ArrayList<>(); if (initialSMA) { // 使用SMA作为初始EMA值(前period个价格的平均值) BigDecimal sum = BigDecimal.ZERO; for (int i = 0; i < period; i++) { sum = sum.add(prices.get(i)); } BigDecimal sma = sum.divide(BigDecimal.valueOf(period), 10, RoundingMode.HALF_UP); ema.add(sma); // 从第period+1个数据点开始计算后续EMA值 for (int i = period; i < prices.size(); i++) { BigDecimal price = prices.get(i); BigDecimal prevEMA = ema.get(ema.size() - 1); // EMA计算公式:Price(today) * alpha + EMA(yesterday) * (1 - alpha) BigDecimal emaToday = price.multiply(alpha) .add(prevEMA.multiply(BigDecimal.ONE.subtract(alpha))) .setScale(10, RoundingMode.HALF_UP); ema.add(emaToday); } } else { // 使用第一个价格作为初始EMA值,并从第二个数据点开始计算 ema.add(prices.get(0)); for (int i = 1; i < prices.size(); i++) { BigDecimal price = prices.get(i); BigDecimal prevEMA = ema.get(ema.size() - 1); // EMA计算公式:Price(today) * alpha + EMA(yesterday) * (1 - alpha) BigDecimal emaToday = price.multiply(alpha) .add(prevEMA.multiply(BigDecimal.ONE.subtract(alpha))) .setScale(10, RoundingMode.HALF_UP); ema.add(emaToday); } } return ema; } /** * 计算价格序列的指数移动平均线(EMA),默认使用SMA作为初始值 * * @param prices 价格序列 * @param period EMA计算周期 * @return 计算得到的EMA序列 */ public static List calculateEMA(List prices, int period) { return calculateEMA(prices, period, true); } /** * 计算单个EMA值(递归计算方式) * * @param currentPrice 当前价格 * @param prevEMA 前一个EMA值 * @param period EMA周期 * @return 当前EMA值 */ public static BigDecimal calculateSingleEMA(BigDecimal currentPrice, BigDecimal prevEMA, int period) { // 计算权重因子alpha = 2 / (period + 1) BigDecimal alpha = BigDecimal.valueOf(2.0).divide(BigDecimal.valueOf(period + 1), 10, RoundingMode.HALF_UP); // EMA(today) = Price(today) * alpha + EMA(yesterday) * (1 - alpha) return currentPrice.multiply(alpha) .add(prevEMA.multiply(BigDecimal.ONE.subtract(alpha))) .setScale(10, RoundingMode.HALF_UP); } }