/**
* 指数移动平均线(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);
}
}