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<BigDecimal> 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<BigDecimal> 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<BigDecimal> 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<BigDecimal> getRecentPrices(List<BigDecimal> 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<BigDecimal> high, List<BigDecimal> low, List<BigDecimal> close, int period) {
|
if (high == null || low == null || close == null ||
|
high.size() < period || low.size() < period || close.size() < period) {
|
return BigDecimal.ZERO;
|
}
|
|
List<BigDecimal> 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<BigDecimal> 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));
|
}
|
}
|