package com.xcong.excoin.modules.okxNewPrice.indicator; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.ArrayList; import java.util.List; /** * Technical indicators base class, provides common calculation methods * * 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 * * 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 * * 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 { /** * Calculate moving average * @param prices Price list * @param period Period * @return Moving average value */ 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); } /** * 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 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))); } /** * Calculate standard deviation * @param prices Price list * @param period Period * @return Standard deviation */ 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); } /** * Calculate square root (simplified implementation) * @param value Input value * @return Square root */ 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); } /** * Get recent price data * @param prices All price data * @param period Period * @return Recent period price data */ 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()); } /** * 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 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); } // Use simple moving average to calculate ATR return calculateMA(trList, Math.min(period, trList.size())); } /** * 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); BigDecimal h2 = high.subtract(prevClose).abs(); BigDecimal h3 = low.subtract(prevClose).abs(); return h1.max(h2).max(h3); } /** * Calculate normalized volatility (based on ATR) * @param close Close price list * @param atr ATR value * @return Normalized volatility (percentage) */ 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)); } }