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<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 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<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() >= 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;
|
}
|
}
|