| src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/IndicatorBase.java | ●●●●● patch | view | raw | blame | history | |
| src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MACD.java | ●●●●● patch | view | raw | blame | history | |
| src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MACDTest.java | ●●●●● patch | view | raw | blame | history |
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/IndicatorBase.java
@@ -6,31 +6,31 @@ import java.util.List; /** * 技术指标基础类,提供通用计算方法 * Technical indicators base class, provides common calculation methods * * 指标组合策略: * 1. 趋势判断(MA/AdvancedMA/MACD):判断价格的整体走势方向 * 2. 动量确认(RSI/KDJ):确认当前趋势的强度和可持续性 * 3. 波动参考(BOLL):确定价格的合理波动范围和突破时机 * Indicator combination strategy: * 1. Trend judgment (MA/AdvancedMA/MACD): Determine the overall trend direction of prices * 2. Momentum confirmation (RSI/KDJ): Confirm the strength and sustainability of the current trend * 3. Volatility reference (BOLL): Determine reasonable price volatility range and breakthrough timing * * 多空方向选择逻辑: * - 多头信号:MA多头排列 + MACD金叉 + RSI(30-70) + BOLL价格在上轨与中轨之间 * - 空头信号:MA空头排列 + MACD死叉 + RSI(30-70) + BOLL价格在下轨与中轨之间 * - 震荡信号:AdvancedMA三线粘合 + RSI(40-60) + BOLL带宽收窄 * Long/Short direction selection logic: * - Long signal: MA bullish arrangement + MACD golden cross + RSI(30-70) + BOLL price between upper and middle band * - Short signal: MA bearish arrangement + MACD death cross + RSI(30-70) + BOLL price between lower and middle band * - Consolidation signal: AdvancedMA three-line convergence + RSI(40-60) + BOLL bandwidth narrowing * * 开仓和平仓策略: * - 开多:MA金叉 + MACD金叉 + KDJ金叉 + RSI(30-70) + 价格突破BOLL中轨 * - 开空:MA死叉 + MACD死叉 + KDJ死叉 + RSI(30-70) + 价格跌破BOLL中轨 * - 平多:MA死叉 + MACD死叉 + RSI超买(>70) + 价格跌破BOLL中轨 * - 平空:MA金叉 + MACD金叉 + RSI超卖(<30) + 价格突破BOLL中轨 * Open and close position strategies: * - Open long: MA golden cross + MACD golden cross + KDJ golden cross + RSI(30-70) + price breaks through BOLL middle band * - Open short: MA death cross + MACD death cross + KDJ death cross + RSI(30-70) + price breaks below BOLL middle band * - Close long: MA death cross + MACD death cross + RSI overbought(>70) + price breaks below BOLL middle band * - Close short: MA golden cross + MACD golden cross + RSI oversold(<30) + price breaks through BOLL middle band */ public abstract class IndicatorBase { /** * 计算移动平均值 * @param prices 价格列表 * @param period 周期 * @return 移动平均值 * Calculate moving average * @param prices Price list * @param period Period * @return Moving average value */ protected BigDecimal calculateMA(List<BigDecimal> prices, int period) { if (prices == null || prices.size() < period) { @@ -44,11 +44,11 @@ } /** * 计算指数移动平均值 * @param prices 价格列表 * @param period 周期 * @param prevEMA 前一个EMA值 * @return 指数移动平均值 * Calculate exponential moving average * @param prices Price list * @param period Period * @param prevEMA Previous EMA value * @return Exponential moving average value */ protected BigDecimal calculateEMA(List<BigDecimal> prices, int period, BigDecimal prevEMA) { if (prices == null || prices.size() == 0) { @@ -63,10 +63,10 @@ } /** * 计算标准差 * @param prices 价格列表 * @param period 周期 * @return 标准差 * Calculate standard deviation * @param prices Price list * @param period Period * @return Standard deviation */ protected BigDecimal calculateStdDev(List<BigDecimal> prices, int period) { if (prices == null || prices.size() < period) { @@ -83,9 +83,9 @@ } /** * 计算平方根(简化实现) * @param value 输入值 * @return 平方根 * Calculate square root (simplified implementation) * @param value Input value * @return Square root */ protected BigDecimal sqrt(BigDecimal value) { if (value.compareTo(BigDecimal.ZERO) < 0) { @@ -95,10 +95,10 @@ } /** * 获取最近的价格数据 * @param prices 所有价格数据 * @param period 周期 * @return 最近period个价格数据 * Get recent price data * @param prices All price data * @param period Period * @return Recent period price data */ protected List<BigDecimal> getRecentPrices(List<BigDecimal> prices, int period) { if (prices == null || prices.size() == 0) { @@ -109,12 +109,12 @@ } /** * 计算ATR(Average True Range) * @param high 最高价列表 * @param low 最低价列表 * @param close 收盘价列表 * @param period 周期 * @return ATR值 * Calculate ATR (Average True Range) * @param high High price list * @param low Low price list * @param close Close price list * @param period Period * @return ATR value */ protected BigDecimal calculateATR(List<BigDecimal> high, List<BigDecimal> low, List<BigDecimal> close, int period) { if (high == null || low == null || close == null || @@ -128,16 +128,16 @@ trList.add(trueRange); } // 使用简单移动平均计算ATR // Use simple moving average to calculate ATR return calculateMA(trList, Math.min(period, trList.size())); } /** * 计算真实波幅(True Range) * @param high 当前最高价 * @param low 当前最低价 * @param prevClose 前收盘价 * @return 真实波幅 * Calculate True Range * @param high Current high price * @param low Current low price * @param prevClose Previous close price * @return True range */ protected BigDecimal calculateTrueRange(BigDecimal high, BigDecimal low, BigDecimal prevClose) { BigDecimal h1 = high.subtract(low); @@ -147,10 +147,10 @@ } /** * 计算标准化波动率(基于ATR) * @param close 收盘价列表 * @param atr ATR值 * @return 标准化波动率(百分比) * Calculate normalized volatility (based on ATR) * @param close Close price list * @param atr ATR value * @return Normalized volatility (percentage) */ protected BigDecimal normalizeVolatility(List<BigDecimal> close, BigDecimal atr) { if (close == null || close.size() == 0 || atr.compareTo(BigDecimal.ZERO) == 0) { src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MACD.java
@@ -2,48 +2,35 @@ import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * MACD (Moving Average Convergence Divergence) 指标实现 * 计算逻辑: * 1. DIF = EMA(12) - EMA(26) * 2. DEA = EMA(DIF, 9) * 3. MACD柱状图 = (DIF - DEA) * 2 * 1. 快线DIFF = EMA(Close, 12) - EMA(Close, 26) * 2. 慢线DEA = EMA(DIFF, 9) * 3. MACD柱状图 = (DIFF - DEA) * macdBarsMultiplier * * 作用: * 1. 识别趋势方向和动能变化 * 2. DIF上穿DEA形成金叉,提示买入信号 * 3. DIF下穿DEA形成死叉,提示卖出信号 * 4. MACD柱状图由负转正,提示多头力量增强 * 5. MACD柱状图由正转负,提示空头力量增强 * 核心概念: * 1. DIFF是EMA12与EMA26之间的距离,反映短期与长期趋势的差异 * 2. 当DIFF > 0表示EMA12在EMA26上方,代表多头趋势 * 3. 当DIFF < 0表示EMA12在EMA26下方,代表空头趋势 * 4. DEA是DIFF的EMA平滑线,用于过滤DIFF的波动 * 5. MACD柱状图通过放大倍数展示DIFF与DEA之间的关系,便于观察趋势变化 * * 价格参数类型: * - 参数名称:prices * - 参数类型:List<BigDecimal> * - 参数说明:需要至少2个价格数据点用于计算,数据越多计算越准确 * 多空判断: * - 多头机会:DIFF在0轴上且MACD柱状图向上,股价同步上涨 * - 空头机会:DIFF在0轴下且MACD柱状图向下,股价同步下跌 * * 推荐时间粒度及优缺点: * 1. 1分钟(1m): * - 优点:反应迅速,适合超短线交易 * - 缺点:MACD柱状图波动剧烈,信号频繁 * 2. 5分钟(5m): * - 优点:平衡了反应速度和信号可靠性 * - 缺点:仍有一定噪音 * 3. 15分钟(15m): * - 优点:适合日内交易,信号较为可靠 * - 缺点:反应速度较慢 * 4. 1小时(1h)及以上: * - 优点:趋势信号明确,MACD柱状图变化稳定 * - 缺点:反应滞后,不适合短线交易 * 信号过滤: * - 可通过设置macdBarsSmoothingPeriod启用MACD柱状图平滑处理 * - 平滑公式:MACD柱状图 = MA((DIFF - DEA) * macdBarsMultiplier, macdBarsSmoothingPeriod) * - 作用:消除柱状图杂讯,使信号更加清晰 */ @Slf4j @Getter @Setter public class MACD extends IndicatorBase { @@ -52,28 +39,43 @@ 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; // 用于保存历史DIF值,用于计算DEA private List<BigDecimal> difHistory = new ArrayList<>(); // 最大保存的DIF历史值数量,至少为signalPeriod private static final int MAX_DIF_HISTORY = 30; 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; } /** @@ -90,7 +92,7 @@ * @param volatility 标准化波动率(百分比),用于动态调整周期 */ public void calculate(List<BigDecimal> prices, BigDecimal volatility) { if (prices == null || prices.size() < 2) { if (prices == null || prices.isEmpty()) { return; } @@ -99,45 +101,81 @@ adjustPeriodsByVolatility(volatility); } // 计算快速EMA // 计算快速EMA (12日) prevFastEMA = calculateEMA(prices, fastPeriod, prevFastEMA); // 计算慢速EMA // 计算慢速EMA (26日) prevSlowEMA = calculateEMA(prices, slowPeriod, prevSlowEMA); // 计算DIF // 计算DIF = EMA(12) - EMA(26) dif = prevFastEMA.subtract(prevSlowEMA).setScale(8, RoundingMode.HALF_UP); // 将新的DIF值添加到历史记录 difHistory.add(dif); // 保持历史记录在合理范围内 if (difHistory.size() > MAX_DIF_HISTORY) { if (difHistory.size() > MAX_HISTORY_SIZE) { difHistory.remove(0); } // 计算DEA:使用足够数量的历史DIF值 // 如果历史DIF值不足,使用当前可用的所有值 int difCount = difHistory.size(); List<BigDecimal> deaCalculationList; if (difCount >= signalPeriod) { // 使用最近的signalPeriod个DIF值 deaCalculationList = difHistory.subList(difCount - signalPeriod, difCount); } else if (difCount > 0) { // 使用所有可用的DIF值 deaCalculationList = new ArrayList<>(difHistory); } else { // 没有DIF历史值,使用当前DIF deaCalculationList = Collections.singletonList(dif); // 计算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); } prevDea = calculateEMA(deaCalculationList, signalPeriod, prevDea); // 如果启用了平滑处理,则计算平滑后的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(); // 计算MACD柱状图 macdBar = dif.subtract(dea).multiply(new BigDecimal(2)).setScale(8, RoundingMode.HALF_UP); // 如果没有足够的历史数据,返回最新的原始值 if (historyCount < macdBarsSmoothingPeriod) { return rawMacdBarHistory.get(historyCount - 1); } log.info("MACD计算结果 - DIF: {}, DEA: {}, MACD柱状图: {}, 参数: fast={}, slow={}, signal={}", dif, dea, macdBar, fastPeriod, slowPeriod, signalPeriod); // 使用简单移动平均平滑MACD柱状图 List<BigDecimal> recentMacdBars = rawMacdBarHistory.subList(historyCount - macdBarsSmoothingPeriod, historyCount); return calculateMA(recentMacdBars, macdBarsSmoothingPeriod); } /** @@ -161,23 +199,43 @@ signalPeriod = 5; } log.info("根据波动率{}调整MACD周期: fast={}, slow={}, signal={}", volatility, fastPeriod, slowPeriod, signalPeriod); } /** * 判断金叉信号(DIF上穿DEA) * @param previousDIF 上一个DIF值 * @param previousDEA 上一个DEA值 * @return 是否形成金叉 */ public boolean isGoldenCross(BigDecimal previousDIF, BigDecimal previousDEA) { return previousDIF.compareTo(previousDEA) < 0 && dif.compareTo(dea) > 0; 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.compareTo(previousDEA) > 0 && dif.compareTo(dea) < 0; 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(); } } src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MACDTest.java
New file @@ -0,0 +1,118 @@ package com.xcong.excoin.modules.okxNewPrice.indicator; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; /** * MACD Indicator Test Class * Used to verify the correctness of MACD calculation logic */ public class MACDTest { public static void main(String[] args) { // Create MACD instance MACD macd = new MACD(); // Set MACD bars parameters (optional, using default values here) macd.setMacdBarsMultiplier(2); // Default multiplier macd.setMacdBarsSmoothingPeriod(0); // No smoothing // Generate test price data (simple upward trend) List<BigDecimal> prices = generateTestPrices(30); System.out.println("=== MACD Indicator Test Start ==="); System.out.println("Price count: " + prices.size()); System.out.println("MACD parameters: fast=" + macd.getFastPeriod() + ", slow=" + macd.getSlowPeriod() + ", signal=" + macd.getSignalPeriod()); System.out.println("MACD bars parameters: multiplier=" + macd.getMacdBarsMultiplier() + ", smoothing=" + macd.getMacdBarsSmoothingPeriod()); System.out.println(); // Calculate MACD macd.calculate(prices); // Output results System.out.println("=== MACD Calculation Results ==="); System.out.println("DIF: " + macd.getDif()); System.out.println("DEA: " + macd.getDea()); System.out.println("MACD Bars: " + macd.getMacdBar()); System.out.println(); // Trend judgment System.out.println("=== Trend Judgment ==="); if (macd.getDif().compareTo(BigDecimal.ZERO) > 0) { System.out.println("DIFF > 0: Bullish trend"); } else if (macd.getDif().compareTo(BigDecimal.ZERO) < 0) { System.out.println("DIFF < 0: Bearish trend"); } else { System.out.println("DIFF = 0: No trend"); } // Test smoothing function testSmoothingFunction(); System.out.println("=== MACD Indicator Test End ==="); } /** * Generate test price data * @param count Number of data points * @return Price list */ private static List<BigDecimal> generateTestPrices(int count) { List<BigDecimal> prices = new ArrayList<>(); // Start from 100, simple upward trend with some random fluctuations BigDecimal basePrice = new BigDecimal(100); for (int i = 0; i < count; i++) { // Add random fluctuation between 0.1 and 0.5 BigDecimal price = basePrice.add(new BigDecimal(Math.random() * 0.4 + 0.1)); prices.add(price.setScale(2, BigDecimal.ROUND_HALF_UP)); basePrice = price; } return prices; } /** * Test MACD bars smoothing function */ private static void testSmoothingFunction() { System.out.println("=== MACD Bars Smoothing Function Test ==="); MACD macd = new MACD(); macd.setMacdBarsMultiplier(2); macd.setMacdBarsSmoothingPeriod(3); // 3-day smoothing // Generate test price data with more fluctuations List<BigDecimal> prices = generateVolatileTestPrices(30); macd.calculate(prices); System.out.println("Price count: " + prices.size()); System.out.println("MACD parameters: fast=" + macd.getFastPeriod() + ", slow=" + macd.getSlowPeriod() + ", signal=" + macd.getSignalPeriod()); System.out.println("MACD bars parameters: multiplier=" + macd.getMacdBarsMultiplier() + ", smoothing=" + macd.getMacdBarsSmoothingPeriod()); System.out.println(); System.out.println("Smoothed MACD Results:"); System.out.println("DIF: " + macd.getDif()); System.out.println("DEA: " + macd.getDea()); System.out.println("MACD Bars: " + macd.getMacdBar()); System.out.println(); } /** * Generate test price data with more fluctuations * @param count Number of data points * @return Price list */ private static List<BigDecimal> generateVolatileTestPrices(int count) { List<BigDecimal> prices = new ArrayList<>(); // Start from 100 with more random fluctuations BigDecimal basePrice = new BigDecimal(100); for (int i = 0; i < count; i++) { // Add random fluctuation between -1.0 and 1.0 BigDecimal price = basePrice.add(new BigDecimal(Math.random() * 2 - 1)); prices.add(price.setScale(2, BigDecimal.ROUND_HALF_UP)); basePrice = price; } return prices; } }