package com.xcong.excoin.modules.okxNewPrice.indicator; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.ArrayList; import java.util.List; /** * 技术指标基础类,提供通用计算方法 * * 指标组合策略: * 1. 趋势判断(MA/AdvancedMA/MACD):判断价格的整体走势方向 * 2. 动量确认(RSI/KDJ):确认当前趋势的强度和可持续性 * 3. 波动参考(BOLL):确定价格的合理波动范围和突破时机 * * 多空方向选择逻辑: * - 多头信号:MA多头排列 + MACD金叉 + RSI(30-70) + BOLL价格在上轨与中轨之间 * - 空头信号:MA空头排列 + MACD死叉 + RSI(30-70) + BOLL价格在下轨与中轨之间 * - 震荡信号:AdvancedMA三线粘合 + RSI(40-60) + BOLL带宽收窄 * * 开仓和平仓策略: * - 开多:MA金叉 + MACD金叉 + KDJ金叉 + RSI(30-70) + 价格突破BOLL中轨 * - 开空:MA死叉 + MACD死叉 + KDJ死叉 + RSI(30-70) + 价格跌破BOLL中轨 * - 平多:MA死叉 + MACD死叉 + RSI超买(>70) + 价格跌破BOLL中轨 * - 平空:MA金叉 + MACD金叉 + RSI超卖(<30) + 价格突破BOLL中轨 */ public abstract class IndicatorBase { /** * 计算移动平均值 * @param prices 价格列表 * @param period 周期 * @return 移动平均值 */ protected BigDecimal calculateMA(List prices, int period) { if (prices == null || prices.size() < period) { return BigDecimal.ZERO; } BigDecimal sum = BigDecimal.ZERO; for (int i = prices.size() - period; i < prices.size(); i++) { sum = sum.add(prices.get(i)); } return sum.divide(new BigDecimal(period), 8, RoundingMode.HALF_UP); } /** * 计算指数移动平均值 * @param prices 价格列表 * @param period 周期 * @param prevEMA 前一个EMA值 * @return 指数移动平均值 */ protected BigDecimal calculateEMA(List prices, int period, BigDecimal prevEMA) { if (prices == null || prices.size() == 0) { return BigDecimal.ZERO; } if (prevEMA == null || prevEMA.compareTo(BigDecimal.ZERO) == 0) { return calculateMA(prices, Math.min(period, prices.size())); } BigDecimal k = new BigDecimal(2).divide(new BigDecimal(period + 1), 8, RoundingMode.HALF_UP); BigDecimal currentPrice = prices.get(prices.size() - 1); return currentPrice.multiply(k).add(prevEMA.multiply(BigDecimal.ONE.subtract(k))); } /** * 计算标准差 * @param prices 价格列表 * @param period 周期 * @return 标准差 */ protected BigDecimal calculateStdDev(List prices, int period) { if (prices == null || prices.size() < period) { return BigDecimal.ZERO; } BigDecimal ma = calculateMA(prices, period); BigDecimal sumSquares = BigDecimal.ZERO; for (int i = prices.size() - period; i < prices.size(); i++) { BigDecimal diff = prices.get(i).subtract(ma); sumSquares = sumSquares.add(diff.multiply(diff)); } BigDecimal variance = sumSquares.divide(new BigDecimal(period), 8, RoundingMode.HALF_UP); return sqrt(variance); } /** * 计算平方根(简化实现) * @param value 输入值 * @return 平方根 */ protected BigDecimal sqrt(BigDecimal value) { if (value.compareTo(BigDecimal.ZERO) < 0) { return BigDecimal.ZERO; } return new BigDecimal(Math.sqrt(value.doubleValue())).setScale(8, RoundingMode.HALF_UP); } /** * 获取最近的价格数据 * @param prices 所有价格数据 * @param period 周期 * @return 最近period个价格数据 */ protected List getRecentPrices(List prices, int period) { if (prices == null || prices.size() == 0) { return new ArrayList<>(); } int startIndex = Math.max(0, prices.size() - period); return prices.subList(startIndex, prices.size()); } /** * 计算ATR(Average True Range) * @param high 最高价列表 * @param low 最低价列表 * @param close 收盘价列表 * @param period 周期 * @return ATR值 */ protected BigDecimal calculateATR(List high, List low, List close, int period) { if (high == null || low == null || close == null || high.size() < period || low.size() < period || close.size() < period) { return BigDecimal.ZERO; } List trList = new ArrayList<>(); for (int i = 1; i < high.size(); i++) { BigDecimal trueRange = calculateTrueRange(high.get(i), low.get(i), close.get(i - 1)); trList.add(trueRange); } // 使用简单移动平均计算ATR return calculateMA(trList, Math.min(period, trList.size())); } /** * 计算真实波幅(True Range) * @param high 当前最高价 * @param low 当前最低价 * @param prevClose 前收盘价 * @return 真实波幅 */ protected BigDecimal calculateTrueRange(BigDecimal high, BigDecimal low, BigDecimal prevClose) { BigDecimal h1 = high.subtract(low); BigDecimal h2 = high.subtract(prevClose).abs(); BigDecimal h3 = low.subtract(prevClose).abs(); return h1.max(h2).max(h3); } /** * 计算标准化波动率(基于ATR) * @param close 收盘价列表 * @param atr ATR值 * @return 标准化波动率(百分比) */ protected BigDecimal normalizeVolatility(List close, BigDecimal atr) { if (close == null || close.size() == 0 || atr.compareTo(BigDecimal.ZERO) == 0) { return BigDecimal.ZERO; } return atr.divide(close.get(close.size() - 1), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100)); } }