package com.xcong.excoin.modules.okxNewPrice.indicator.strategy;
|
|
import com.xcong.excoin.modules.okxNewPrice.indicator.AdvancedMA;
|
import com.xcong.excoin.modules.okxNewPrice.indicator.BOLL;
|
import com.xcong.excoin.modules.okxNewPrice.indicator.KDJ;
|
import com.xcong.excoin.modules.okxNewPrice.indicator.MACD;
|
import com.xcong.excoin.modules.okxNewPrice.indicator.RSI;
|
import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.CoinEnums;
|
import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.OrderParamEnums;
|
import com.xcong.excoin.modules.okxNewPrice.okxWs.param.TradeRequestParam;
|
import lombok.extern.slf4j.Slf4j;
|
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.util.ArrayList;
|
import java.util.List;
|
|
/**
|
* 核心技术指标策略实现类
|
* 整合三重EMA交叉系统、波动率自适应布林带、MACD能量柱分级策略等核心指标
|
*/
|
@Slf4j
|
public class CoreTechnicalStrategy extends AbstractTechnicalIndicatorStrategy {
|
|
private static final int MACD_HISTOGRAM_PERIOD = 5; // MACD能量柱计算周期
|
private static final BigDecimal VOLUME_MULTIPLIER = new BigDecimal(3); // 成交量放大倍数
|
private static final BigDecimal GLUE_THRESHOLD = new BigDecimal(2); // 三线粘合度阈值(%)
|
|
private final AdvancedMA advancedMA;
|
private final BOLL boll;
|
private final MACD macd;
|
private final RSI rsi;
|
private final KDJ kdj;
|
|
private List<BigDecimal> macdHistogramHistory; // MACD能量柱历史
|
private BigDecimal prevEma9;
|
private BigDecimal prevEma21;
|
private BigDecimal prevEma55;
|
private BigDecimal prevDif;
|
private BigDecimal prevDea;
|
private BigDecimal prevK;
|
private BigDecimal prevD;
|
private BigDecimal prevJ;
|
|
public CoreTechnicalStrategy() {
|
super();
|
this.strategyName = "核心技术指标策略";
|
this.advancedMA = new AdvancedMA();
|
this.boll = new BOLL();
|
this.macd = new MACD();
|
this.rsi = new RSI();
|
this.kdj = new KDJ();
|
this.macdHistogramHistory = new ArrayList<>();
|
this.prevEma9 = BigDecimal.ZERO;
|
this.prevEma21 = BigDecimal.ZERO;
|
this.prevEma55 = BigDecimal.ZERO;
|
this.prevDif = BigDecimal.ZERO;
|
this.prevDea = BigDecimal.ZERO;
|
this.prevK = new BigDecimal(50);
|
this.prevD = new BigDecimal(50);
|
this.prevJ = new BigDecimal(50);
|
}
|
|
@Override
|
public void initialize() {
|
super.initialize();
|
macdHistogramHistory.clear();
|
|
// 初始化技术指标
|
advancedMA.setPrevEma9(null);
|
advancedMA.setPrevEma21(null);
|
advancedMA.setPrevEma55(null);
|
|
macd.setDif(BigDecimal.ZERO);
|
macd.setDea(BigDecimal.ZERO);
|
macd.setMacdBar(BigDecimal.ZERO);
|
macd.setPrevFastEMA(null);
|
macd.setPrevSlowEMA(null);
|
macd.setPrevDea(null);
|
|
rsi.setRsi(BigDecimal.ZERO);
|
rsi.setPrevAvgGain(BigDecimal.ZERO);
|
rsi.setPrevAvgLoss(BigDecimal.ZERO);
|
|
kdj.setK(new BigDecimal(50));
|
kdj.setD(new BigDecimal(50));
|
kdj.setJ(new BigDecimal(50));
|
kdj.setPrevK(new BigDecimal(50));
|
kdj.setPrevD(new BigDecimal(50));
|
|
boll.setMid(BigDecimal.ZERO);
|
boll.setUpper(BigDecimal.ZERO);
|
boll.setLower(BigDecimal.ZERO);
|
|
prevEma9 = BigDecimal.ZERO;
|
prevEma21 = BigDecimal.ZERO;
|
prevEma55 = BigDecimal.ZERO;
|
prevDif = BigDecimal.ZERO;
|
prevDea = BigDecimal.ZERO;
|
prevK = new BigDecimal(50);
|
prevD = new BigDecimal(50);
|
prevJ = new BigDecimal(50);
|
|
log.info("核心技术指标策略初始化完成");
|
}
|
|
@Override
|
public TradeRequestParam getSignal(String accountName, String markPx, String posSide) {
|
if (!initialized || priceHistory.isEmpty()) {
|
log.warn("策略未初始化或价格历史为空");
|
return createTradeRequestParam(accountName, markPx, posSide, TradeSignal.NO_SIGNAL);
|
}
|
|
try {
|
BigDecimal currentPrice = new BigDecimal(markPx);
|
|
// 计算所有技术指标
|
calculateIndicators(currentPrice);
|
|
// 生成交易信号
|
TradeRequestParam param = analyzeSignal(accountName, markPx, posSide, currentPrice);
|
|
// 更新历史指标值
|
updateHistoricalIndicatorValues();
|
|
return param;
|
} catch (Exception e) {
|
log.error("计算交易信号时发生异常", e);
|
return createTradeRequestParam(accountName, markPx, posSide, TradeSignal.NO_SIGNAL);
|
}
|
}
|
|
/**
|
* 计算所有技术指标
|
*/
|
private void calculateIndicators(BigDecimal currentPrice) {
|
// 计算三重EMA交叉系统
|
advancedMA.calculateTripleEMA(priceHistory);
|
|
// 计算MACD
|
macd.calculate(priceHistory);
|
|
// 维护MACD能量柱历史
|
updateMacdHistogramHistory();
|
|
// 计算RSI
|
rsi.calculate(priceHistory);
|
|
// 计算KDJ
|
kdj.calculate(priceHistory);
|
|
// 计算波动率自适应布林带
|
calculateAdaptiveBollingerBands();
|
}
|
|
/**
|
* 计算波动率自适应布林带
|
*/
|
private void calculateAdaptiveBollingerBands() {
|
// 动态调整布林带的标准差倍数
|
BigDecimal atr = calculateATR(priceHistory, 14);
|
|
// 根据ATR动态计算标准差倍数
|
BigDecimal stdDevMultiplier;
|
if (atr.compareTo(new BigDecimal(0.5)) < 0) {
|
stdDevMultiplier = new BigDecimal(2);
|
} else if (atr.compareTo(new BigDecimal(1)) < 0) {
|
stdDevMultiplier = new BigDecimal(2.5);
|
} else {
|
stdDevMultiplier = new BigDecimal(3);
|
}
|
|
boll.setK(stdDevMultiplier);
|
boll.calculate(priceHistory);
|
|
log.debug("ATR: {}, 布林带标准差倍数: {}", atr, stdDevMultiplier);
|
}
|
|
/**
|
* 计算ATR (平均真实范围)
|
*/
|
private BigDecimal calculateATR(List<BigDecimal> prices, int period) {
|
if (prices == null || prices.size() < period + 1) {
|
return new BigDecimal(0.5); // 默认值
|
}
|
|
List<BigDecimal> trList = new ArrayList<>();
|
|
for (int i = 1; i < prices.size(); i++) {
|
BigDecimal high = prices.get(i);
|
BigDecimal low = prices.get(i);
|
BigDecimal prevClose = prices.get(i - 1);
|
|
BigDecimal tr1 = high.subtract(low);
|
BigDecimal tr2 = high.subtract(prevClose).abs();
|
BigDecimal tr3 = prevClose.subtract(low).abs();
|
|
trList.add(tr1.max(tr2).max(tr3));
|
}
|
|
// 计算ATR
|
BigDecimal sum = trList.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
|
return sum.divide(new BigDecimal(trList.size()), 8, RoundingMode.HALF_UP);
|
}
|
|
/**
|
* 更新MACD能量柱历史
|
*/
|
private void updateMacdHistogramHistory() {
|
macdHistogramHistory.add(macd.getMacdBar());
|
|
// 限制历史记录数量
|
if (macdHistogramHistory.size() > MACD_HISTOGRAM_PERIOD) {
|
macdHistogramHistory = macdHistogramHistory.subList(
|
macdHistogramHistory.size() - MACD_HISTOGRAM_PERIOD,
|
macdHistogramHistory.size());
|
}
|
}
|
|
/**
|
* 计算MACD能量柱面积指标(累计过去5根柱体积分)
|
*/
|
private BigDecimal calculateMacdHistogramArea() {
|
return macdHistogramHistory.stream()
|
.reduce(BigDecimal.ZERO, BigDecimal::add)
|
.abs(); // 使用绝对值表示面积
|
}
|
|
/**
|
* 分析技术指标生成交易信号
|
*/
|
private TradeRequestParam analyzeSignal(String accountName, String markPx, String posSide, BigDecimal currentPrice) {
|
TradeRequestParam param = createTradeRequestParam(accountName, markPx, posSide, TradeSignal.NO_SIGNAL);
|
param.setTradeType(OrderParamEnums.TRADE_YES.getValue());
|
param.setInstId(CoinEnums.HE_YUE.getCode());
|
param.setTdMode(CoinEnums.CROSS.getCode());
|
param.setOrdType(CoinEnums.ORDTYPE_MARKET.getCode());
|
|
// 检查冷静期
|
String outStr = getAccountConfig(accountName, CoinEnums.OUT.name());
|
if (OrderParamEnums.OUT_YES.getValue().equals(outStr)) {
|
log.error("冷静中,不允许下单......");
|
param.setTradeType(OrderParamEnums.TRADE_NO.getValue());
|
return param;
|
}
|
|
// 检查震荡行情
|
if (advancedMA.isUpAndDown()) {
|
log.info("处于震荡行情(三线粘合度<{}%),暂停交易", GLUE_THRESHOLD);
|
param.setTradeType(OrderParamEnums.TRADE_NO.getValue());
|
return param;
|
}
|
|
TradeSignal signal = TradeSignal.NO_SIGNAL;
|
|
// 分析多空信号
|
signal = analyzeCoreSignal(currentPrice, posSide);
|
|
log.info("账户: {}, 价格: {}, 方向: {}, 生成信号: {}",
|
accountName, markPx, posSide, signal.getName());
|
|
// 设置信号参数
|
setSignalParameters(param, signal);
|
|
return param;
|
}
|
|
/**
|
* 分析核心技术指标信号
|
*/
|
private TradeSignal analyzeCoreSignal(BigDecimal currentPrice, String posSide) {
|
// 计算MACD能量柱面积
|
BigDecimal macdArea = calculateMacdHistogramArea();
|
BigDecimal prevMacdArea = calculateMacdHistogramAreaPrevious();
|
|
// 多头入场条件:当前柱体>0且面积增速>前周期20%
|
if (isBullishEntry(macdArea, prevMacdArea)) {
|
// 如果当前没有仓位或仓位为空头,则开多
|
if (posSide == null || posSide.isEmpty() || CoinEnums.POSSIDE_SHORT.getCode().equals(posSide)) {
|
return posSide == null || posSide.isEmpty() ? TradeSignal.OPEN_LONG : TradeSignal.CLOSE_SHORT;
|
}
|
// 如果当前已经是多头仓位,则保持观望
|
return TradeSignal.NO_SIGNAL;
|
}
|
|
// 空头入场条件:当前柱体<0且面积增速>前周期20%
|
if (isBearishEntry(macdArea, prevMacdArea)) {
|
// 如果当前没有仓位或仓位为多头,则开空
|
if (posSide == null || posSide.isEmpty() || CoinEnums.POSSIDE_LONG.getCode().equals(posSide)) {
|
return posSide == null || posSide.isEmpty() ? TradeSignal.OPEN_SHORT : TradeSignal.CLOSE_LONG;
|
}
|
// 如果当前已经是空头仓位,则保持观望
|
return TradeSignal.NO_SIGNAL;
|
}
|
|
// 突破上轨+成交量放大3倍=做空信号
|
if (isBollingerUpperBreak(currentPrice) && isVolumeIncreased()) {
|
// 如果当前没有仓位或仓位为多头,则开空
|
if (posSide == null || posSide.isEmpty() || CoinEnums.POSSIDE_LONG.getCode().equals(posSide)) {
|
return posSide == null || posSide.isEmpty() ? TradeSignal.OPEN_SHORT : TradeSignal.CLOSE_LONG;
|
}
|
// 如果当前已经是空头仓位,则保持观望
|
return TradeSignal.NO_SIGNAL;
|
}
|
|
// 触及下轨+期货资金费率转正=做多信号
|
if (isBollingerLowerTouch(currentPrice) && isFundingRatePositive()) {
|
// 如果当前没有仓位或仓位为空头,则开多
|
if (posSide == null || posSide.isEmpty() || CoinEnums.POSSIDE_SHORT.getCode().equals(posSide)) {
|
return posSide == null || posSide.isEmpty() ? TradeSignal.OPEN_LONG : TradeSignal.CLOSE_SHORT;
|
}
|
// 如果当前已经是多头仓位,则保持观望
|
return TradeSignal.NO_SIGNAL;
|
}
|
|
// 空头反转:柱体顶背离+RSI>70区域死叉
|
if (isBearishReversal(currentPrice)) {
|
// 如果当前没有仓位或仓位为多头,则开空或平多
|
if (posSide == null || posSide.isEmpty() || CoinEnums.POSSIDE_LONG.getCode().equals(posSide)) {
|
return posSide == null || posSide.isEmpty() ? TradeSignal.OPEN_SHORT : TradeSignal.CLOSE_LONG;
|
}
|
// 如果当前已经是空头仓位,则保持观望
|
return TradeSignal.NO_SIGNAL;
|
}
|
|
// 多头反转:柱体底背离+RSI<30区域金叉
|
if (isBullishReversal(currentPrice)) {
|
// 如果当前没有仓位或仓位为空头,则开多或平空
|
if (posSide == null || posSide.isEmpty() || CoinEnums.POSSIDE_SHORT.getCode().equals(posSide)) {
|
return posSide == null || posSide.isEmpty() ? TradeSignal.OPEN_LONG : TradeSignal.CLOSE_SHORT;
|
}
|
// 如果当前已经是多头仓位,则保持观望
|
return TradeSignal.NO_SIGNAL;
|
}
|
|
return TradeSignal.NO_SIGNAL;
|
}
|
|
/**
|
* 多头入场条件判断
|
*/
|
private boolean isBullishEntry(BigDecimal currentArea, BigDecimal prevArea) {
|
// 当前柱体>0
|
boolean currentBarPositive = macd.getMacdBar().compareTo(BigDecimal.ZERO) > 0;
|
|
// 面积增速>前周期20%
|
boolean areaGrowth = prevArea != null && prevArea.compareTo(BigDecimal.ZERO) > 0 &&
|
currentArea.divide(prevArea, 8, RoundingMode.HALF_UP)
|
.compareTo(new BigDecimal(1.2)) > 0;
|
|
// 三重EMA多头排列
|
boolean emaBullish = advancedMA.isBullish();
|
|
return currentBarPositive && areaGrowth && emaBullish;
|
}
|
|
/**
|
* 空头入场条件判断
|
*/
|
private boolean isBearishEntry(BigDecimal currentArea, BigDecimal prevArea) {
|
// 当前柱体<0
|
boolean currentBarNegative = macd.getMacdBar().compareTo(BigDecimal.ZERO) < 0;
|
|
// 面积增速>前周期20%
|
boolean areaGrowth = prevArea != null && prevArea.compareTo(BigDecimal.ZERO) > 0 &&
|
currentArea.divide(prevArea, 8, RoundingMode.HALF_UP)
|
.compareTo(new BigDecimal(1.2)) > 0;
|
|
// 三重EMA空头排列
|
boolean emaBearish = advancedMA.isBearish();
|
|
return currentBarNegative && areaGrowth && emaBearish;
|
}
|
|
/**
|
* 突破上轨判断
|
*/
|
private boolean isBollingerUpperBreak(BigDecimal currentPrice) {
|
return currentPrice.compareTo(boll.getUpper()) > 0;
|
}
|
|
/**
|
* 触及下轨判断
|
*/
|
private boolean isBollingerLowerTouch(BigDecimal currentPrice) {
|
return currentPrice.compareTo(boll.getLower()) < 0;
|
}
|
|
/**
|
* 成交量放大判断(模拟)
|
*/
|
private boolean isVolumeIncreased() {
|
// 这里使用价格波动率模拟成交量
|
return calculateVolatility(priceHistory, 5)
|
.compareTo(calculateVolatility(priceHistory, 20).multiply(VOLUME_MULTIPLIER)) > 0;
|
}
|
|
/**
|
* 期货资金费率转正判断(模拟)
|
*/
|
private boolean isFundingRatePositive() {
|
// 这里使用MACD柱状图模拟资金费率
|
return macd.getMacdBar().compareTo(BigDecimal.ZERO) > 0;
|
}
|
|
/**
|
* 计算价格波动率
|
*/
|
private BigDecimal calculateVolatility(List<BigDecimal> prices, int period) {
|
if (prices.size() < period) {
|
return BigDecimal.ZERO;
|
}
|
|
List<BigDecimal> returns = new ArrayList<>();
|
for (int i = 1; i < prices.size(); i++) {
|
BigDecimal current = prices.get(i);
|
BigDecimal previous = prices.get(i - 1);
|
returns.add(current.divide(previous, 8, RoundingMode.HALF_UP).subtract(BigDecimal.ONE));
|
}
|
|
return calculateStandardDeviation(returns);
|
}
|
|
/**
|
* 计算标准差
|
*/
|
private BigDecimal calculateStandardDeviation(List<BigDecimal> values) {
|
if (values.isEmpty()) {
|
return BigDecimal.ZERO;
|
}
|
|
// 计算平均值
|
BigDecimal sum = values.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
|
BigDecimal mean = sum.divide(new BigDecimal(values.size()), 8, RoundingMode.HALF_UP);
|
|
// 计算方差
|
BigDecimal variance = values.stream()
|
.map(val -> val.subtract(mean).pow(2))
|
.reduce(BigDecimal.ZERO, BigDecimal::add)
|
.divide(new BigDecimal(values.size()), 8, RoundingMode.HALF_UP);
|
|
// 计算标准差
|
return sqrt(variance);
|
}
|
|
/**
|
* 计算平方根
|
*/
|
private BigDecimal sqrt(BigDecimal value) {
|
return new BigDecimal(Math.sqrt(value.doubleValue()));
|
}
|
|
/**
|
* 空头反转判断
|
*/
|
private boolean isBearishReversal(BigDecimal currentPrice) {
|
// 柱体顶背离
|
boolean topDivergence = isMacdTopDivergence(currentPrice);
|
|
// RSI>70区域死叉
|
boolean rsiOverbought = rsi.getRsi().compareTo(new BigDecimal(70)) > 0;
|
boolean kdjDeathCross = kdj.isDeathCross();
|
|
return topDivergence && rsiOverbought && kdjDeathCross;
|
}
|
|
/**
|
* 多头反转判断
|
*/
|
private boolean isBullishReversal(BigDecimal currentPrice) {
|
// 柱体底背离
|
boolean bottomDivergence = isMacdBottomDivergence(currentPrice);
|
|
// RSI<30区域金叉
|
boolean rsiOversold = rsi.getRsi().compareTo(new BigDecimal(30)) < 0;
|
boolean kdjGoldenCross = kdj.isGoldenCross();
|
|
return bottomDivergence && rsiOversold && kdjGoldenCross;
|
}
|
|
/**
|
* 判断MACD顶背离
|
*/
|
private boolean isMacdTopDivergence(BigDecimal currentPrice) {
|
// 简化的顶背离判断:价格创新高但MACD未创新高
|
if (priceHistory.size() < 2) {
|
return false;
|
}
|
|
BigDecimal previousPrice = priceHistory.get(priceHistory.size() - 2);
|
return currentPrice.compareTo(previousPrice) > 0 &&
|
macd.getMacdBar().compareTo(prevDea) < 0;
|
}
|
|
/**
|
* 判断MACD底背离
|
*/
|
private boolean isMacdBottomDivergence(BigDecimal currentPrice) {
|
// 简化的底背离判断:价格创新低但MACD未创新低
|
if (priceHistory.size() < 2) {
|
return false;
|
}
|
|
BigDecimal previousPrice = priceHistory.get(priceHistory.size() - 2);
|
return currentPrice.compareTo(previousPrice) < 0 &&
|
macd.getMacdBar().compareTo(prevDea) > 0;
|
}
|
|
/**
|
* 计算前一期MACD能量柱面积
|
*/
|
private BigDecimal calculateMacdHistogramAreaPrevious() {
|
if (macdHistogramHistory.size() < 2) {
|
return BigDecimal.ZERO;
|
}
|
|
List<BigDecimal> prevHistory = macdHistogramHistory.subList(
|
0, macdHistogramHistory.size() - 1);
|
|
return prevHistory.stream()
|
.reduce(BigDecimal.ZERO, BigDecimal::add)
|
.abs();
|
}
|
|
/**
|
* 设置信号参数
|
*/
|
private void setSignalParameters(TradeRequestParam param, TradeSignal signal) {
|
String side = null;
|
|
switch (signal) {
|
case BUY:
|
side = CoinEnums.SIDE_BUY.getCode();
|
param.setPosSide(CoinEnums.POSSIDE_LONG.getCode());
|
break;
|
case SELL:
|
side = CoinEnums.SIDE_SELL.getCode();
|
param.setPosSide(CoinEnums.POSSIDE_SHORT.getCode());
|
break;
|
case OPEN_LONG:
|
side = CoinEnums.SIDE_BUY.getCode();
|
param.setPosSide(CoinEnums.POSSIDE_LONG.getCode());
|
break;
|
case CLOSE_LONG:
|
side = CoinEnums.SIDE_SELL.getCode();
|
break;
|
case OPEN_SHORT:
|
side = CoinEnums.SIDE_SELL.getCode();
|
param.setPosSide(CoinEnums.POSSIDE_SHORT.getCode());
|
break;
|
case CLOSE_SHORT:
|
side = CoinEnums.SIDE_BUY.getCode();
|
break;
|
case STOP_LOSS:
|
// 止损操作
|
side = CoinEnums.POSSIDE_LONG.getCode().equals(param.getPosSide()) ?
|
CoinEnums.SIDE_SELL.getCode() : CoinEnums.SIDE_BUY.getCode();
|
break;
|
default:
|
param.setTradeType(OrderParamEnums.TRADE_NO.getValue());
|
return;
|
}
|
|
param.setSide(side);
|
log.info("设置交易方向: {}, 仓位方向: {}", side, param.getPosSide());
|
}
|
|
/**
|
* 更新历史指标值
|
*/
|
private void updateHistoricalIndicatorValues() {
|
prevEma9 = advancedMA.getEma9();
|
prevEma21 = advancedMA.getEma21();
|
prevEma55 = advancedMA.getEma55();
|
prevDif = macd.getDif();
|
prevDea = macd.getDea();
|
prevK = kdj.getK();
|
prevD = kdj.getD();
|
prevJ = kdj.getJ();
|
}
|
|
/**
|
* 获取账户配置
|
*/
|
private String getAccountConfig(String accountName, String key) {
|
// 模拟获取账户配置
|
return OrderParamEnums.OUT_NO.getValue();
|
}
|
}
|