package com.xcong.excoin.modules.okxNewPrice.indicator;
|
|
import lombok.Getter;
|
import lombok.Setter;
|
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.util.ArrayList;
|
import java.util.List;
|
|
/**
|
* MACD (Moving Average Convergence Divergence) 指标实现
|
* 计算逻辑:
|
* 1. 快线DIFF = EMA(Close, 12) - EMA(Close, 26)
|
* 2. 慢线DEA = EMA(DIFF, 9)
|
* 3. MACD柱状图 = (DIFF - DEA) * macdBarsMultiplier
|
*
|
* 核心概念:
|
* 1. DIFF是EMA12与EMA26之间的距离,反映短期与长期趋势的差异
|
* 2. 当DIFF > 0表示EMA12在EMA26上方,代表多头趋势
|
* 3. 当DIFF < 0表示EMA12在EMA26下方,代表空头趋势
|
* 4. DEA是DIFF的EMA平滑线,用于过滤DIFF的波动
|
* 5. MACD柱状图通过放大倍数展示DIFF与DEA之间的关系,便于观察趋势变化
|
*
|
* 多空判断:
|
* - 多头机会:DIFF在0轴上且MACD柱状图向上,股价同步上涨
|
* - 空头机会:DIFF在0轴下且MACD柱状图向下,股价同步下跌
|
*
|
* 信号过滤:
|
* - 可通过设置macdBarsSmoothingPeriod启用MACD柱状图平滑处理
|
* - 平滑公式:MACD柱状图 = MA((DIFF - DEA) * macdBarsMultiplier, macdBarsSmoothingPeriod)
|
* - 作用:消除柱状图杂讯,使信号更加清晰
|
*/
|
@Getter
|
@Setter
|
public class MACD extends IndicatorBase {
|
|
// 默认周期参数
|
public static final int DEFAULT_FAST_PERIOD = 12;
|
public static final int DEFAULT_SLOW_PERIOD = 26;
|
public static final int DEFAULT_SIGNAL_PERIOD = 9;
|
|
// 默认MACD柱状图放大倍数
|
public static final int DEFAULT_MACDBARS_MULTIPLIER = 2;
|
// 默认MACD柱状图平滑周期(0表示不平滑)
|
public static final int DEFAULT_MACDBARS_SMOOTHING_PERIOD = 0;
|
|
// 周期参数
|
private int fastPeriod;
|
private int slowPeriod;
|
private int signalPeriod;
|
|
// MACD柱状图参数
|
private int macdBarsMultiplier; // MACD柱状图放大倍数
|
private int macdBarsSmoothingPeriod; // MACD柱状图平滑周期(0表示不平滑)
|
|
// MACD计算结果
|
private BigDecimal dif = BigDecimal.ZERO;
|
private BigDecimal dea = BigDecimal.ZERO;
|
private BigDecimal macdBar = BigDecimal.ZERO;
|
|
// 历史值缓存
|
private BigDecimal prevFastEMA = null;
|
private BigDecimal prevSlowEMA = null;
|
private BigDecimal prevDea = null;
|
private List<BigDecimal> difHistory = new ArrayList<>(); // 保存历史DIF值,用于计算DEA
|
private List<BigDecimal> rawMacdBarHistory = new ArrayList<>(); // 保存原始MACD柱状图值,用于平滑处理
|
|
// 最大保存的历史值数量
|
private static final int MAX_HISTORY_SIZE = 50;
|
|
// 构造函数使用默认参数
|
public MACD() {
|
this.fastPeriod = DEFAULT_FAST_PERIOD;
|
this.slowPeriod = DEFAULT_SLOW_PERIOD;
|
this.signalPeriod = DEFAULT_SIGNAL_PERIOD;
|
this.macdBarsMultiplier = DEFAULT_MACDBARS_MULTIPLIER;
|
this.macdBarsSmoothingPeriod = DEFAULT_MACDBARS_SMOOTHING_PERIOD;
|
}
|
|
/**
|
* 计算MACD指标(使用当前周期设置)
|
* @param prices 价格列表
|
*/
|
public void calculate(List<BigDecimal> prices) {
|
calculate(prices, null);
|
}
|
|
/**
|
* 计算MACD指标,并支持动态周期调整
|
* @param prices 价格列表
|
* @param volatility 标准化波动率(百分比),用于动态调整周期
|
*/
|
public void calculate(List<BigDecimal> prices, BigDecimal volatility) {
|
if (prices == null || prices.isEmpty()) {
|
return;
|
}
|
|
// 如果提供了波动率,则动态调整周期
|
if (volatility != null) {
|
adjustPeriodsByVolatility(volatility);
|
}
|
|
|
|
// 计算快速EMA (12日)
|
prevFastEMA = calculateEMA(prices, fastPeriod, prevFastEMA);
|
|
// 计算慢速EMA (26日)
|
prevSlowEMA = calculateEMA(prices, slowPeriod, prevSlowEMA);
|
|
// 计算DIF = EMA(12) - EMA(26)
|
dif = prevFastEMA.subtract(prevSlowEMA).setScale(8, RoundingMode.HALF_UP);
|
|
// 将新的DIF值添加到历史记录
|
difHistory.add(dif);
|
// 保持历史记录在合理范围内
|
if (difHistory.size() > MAX_HISTORY_SIZE) {
|
difHistory.remove(0);
|
}
|
|
// 计算DEA = EMA(DIFF, 9)
|
calculateDEA();
|
|
// 计算原始MACD柱状图值 = (DIF - DEA) * 放大倍数
|
BigDecimal rawMacdBar = dif.subtract(dea).multiply(new BigDecimal(macdBarsMultiplier)).setScale(8, RoundingMode.HALF_UP);
|
|
// 将原始MACD柱状图值添加到历史记录
|
rawMacdBarHistory.add(rawMacdBar);
|
// 保持历史记录在合理范围内
|
if (rawMacdBarHistory.size() > MAX_HISTORY_SIZE) {
|
rawMacdBarHistory.remove(0);
|
}
|
|
// 如果启用了平滑处理,则计算平滑后的MACD柱状图
|
if (macdBarsSmoothingPeriod > 0) {
|
macdBar = smoothMacdBars().setScale(8, RoundingMode.HALF_UP);
|
} else {
|
macdBar = rawMacdBar;
|
}
|
|
|
}
|
|
/**
|
* 计算DEA指标
|
*/
|
private void calculateDEA() {
|
int difCount = difHistory.size();
|
|
// 如果没有足够的DIF历史值,无法计算有效的DEA
|
if (difCount == 0) {
|
dea = BigDecimal.ZERO;
|
prevDea = null;
|
return;
|
}
|
|
// 计算DEA = EMA(DIFF, signalPeriod)
|
// 使用所有DIF历史值来计算初始EMA,然后使用最新值更新
|
prevDea = calculateEMA(difHistory, signalPeriod, prevDea);
|
dea = prevDea.setScale(8, RoundingMode.HALF_UP);
|
}
|
|
/**
|
* 平滑MACD柱状图
|
* @return 平滑后的MACD柱状图值
|
*/
|
private BigDecimal smoothMacdBars() {
|
int historyCount = rawMacdBarHistory.size();
|
|
// 如果没有足够的历史数据,返回最新的原始值
|
if (historyCount < macdBarsSmoothingPeriod) {
|
return rawMacdBarHistory.get(historyCount - 1);
|
}
|
|
// 使用简单移动平均平滑MACD柱状图
|
List<BigDecimal> recentMacdBars = rawMacdBarHistory.subList(historyCount - macdBarsSmoothingPeriod, historyCount);
|
return calculateMA(recentMacdBars, macdBarsSmoothingPeriod);
|
}
|
|
/**
|
* 根据波动率调整MACD周期参数
|
* @param volatility 标准化波动率(百分比)
|
*/
|
private void adjustPeriodsByVolatility(BigDecimal volatility) {
|
// 波动率阈值
|
BigDecimal volatilityThreshold = new BigDecimal(15);
|
|
// 根据波动率调整MACD参数
|
if (volatility.compareTo(volatilityThreshold) < 0) {
|
// 低波动率环境,使用默认参数
|
fastPeriod = DEFAULT_FAST_PERIOD;
|
slowPeriod = DEFAULT_SLOW_PERIOD;
|
signalPeriod = DEFAULT_SIGNAL_PERIOD;
|
} else {
|
// 高波动率环境,使用更灵敏的参数
|
fastPeriod = 8;
|
slowPeriod = 17;
|
signalPeriod = 5;
|
}
|
|
|
}
|
|
/**
|
* 判断金叉信号(DIF上穿DEA)
|
* @param previousDIF 上一个DIF值
|
* @param previousDEA 上一个DEA值
|
* @return 是否形成金叉
|
*/
|
public boolean isGoldenCross(BigDecimal previousDIF, BigDecimal previousDEA) {
|
return previousDIF != null && previousDEA != null &&
|
previousDIF.compareTo(previousDEA) < 0 && dif.compareTo(dea) > 0;
|
}
|
|
/**
|
* 判断死叉信号(DIF下穿DEA)
|
* @param previousDIF 上一个DIF值
|
* @param previousDEA 上一个DEA值
|
* @return 是否形成死叉
|
*/
|
public boolean isDeathCross(BigDecimal previousDIF, BigDecimal previousDEA) {
|
return previousDIF != null && previousDEA != null &&
|
previousDIF.compareTo(previousDEA) > 0 && dif.compareTo(dea) < 0;
|
}
|
|
/**
|
* 重置MACD指标状态
|
*/
|
public void reset() {
|
dif = BigDecimal.ZERO;
|
dea = BigDecimal.ZERO;
|
macdBar = BigDecimal.ZERO;
|
prevFastEMA = null;
|
prevSlowEMA = null;
|
prevDea = null;
|
difHistory.clear();
|
rawMacdBarHistory.clear();
|
|
}
|
}
|