package com.xcong.excoin.modules.okxNewPrice.indicator.strategy;
|
|
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 com.xcong.excoin.modules.okxNewPrice.utils.WsMapBuild;
|
import lombok.extern.slf4j.Slf4j;
|
|
import java.math.BigDecimal;
|
import java.util.List;
|
|
/**
|
* 综合技术指标策略实现类,整合MACD、KDJ、RSI、BOLL等指标生成交易信号
|
*/
|
@Slf4j
|
public class ComprehensiveTechnicalStrategy extends AbstractTechnicalIndicatorStrategy {
|
|
private final MACD macd;
|
private final KDJ kdj;
|
private final RSI rsi;
|
private final BOLL boll;
|
|
private BigDecimal prevDif;
|
private BigDecimal prevDea;
|
private BigDecimal prevK;
|
private BigDecimal prevD;
|
|
public ComprehensiveTechnicalStrategy() {
|
super();
|
this.strategyName = "综合技术指标策略";
|
this.macd = new MACD();
|
this.kdj = new KDJ();
|
this.rsi = new RSI();
|
this.boll = new BOLL();
|
this.prevDif = BigDecimal.ZERO;
|
this.prevDea = BigDecimal.ZERO;
|
this.prevK = new BigDecimal(50);
|
this.prevD = new BigDecimal(50);
|
}
|
|
@Override
|
public void initialize() {
|
super.initialize();
|
macd.setDif(BigDecimal.ZERO);
|
macd.setDea(BigDecimal.ZERO);
|
macd.setMacdBar(BigDecimal.ZERO);
|
macd.setPrevFastEMA(null);
|
macd.setPrevSlowEMA(null);
|
macd.setPrevDea(null);
|
|
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));
|
|
rsi.setRsi(BigDecimal.ZERO);
|
rsi.setPrevAvgGain(BigDecimal.ZERO);
|
rsi.setPrevAvgLoss(BigDecimal.ZERO);
|
|
boll.setMid(BigDecimal.ZERO);
|
boll.setUpper(BigDecimal.ZERO);
|
boll.setLower(BigDecimal.ZERO);
|
|
prevDif = BigDecimal.ZERO;
|
prevDea = BigDecimal.ZERO;
|
prevK = new BigDecimal(50);
|
prevD = 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);
|
|
// 计算所有技术指标
|
macd.calculate(priceHistory);
|
kdj.calculate(priceHistory);
|
rsi.calculate(priceHistory);
|
boll.calculate(priceHistory);
|
|
// 生成交易信号
|
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 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;
|
}
|
|
TradeSignal signal = TradeSignal.NO_SIGNAL;
|
|
// 根据多空方向分别分析信号
|
if (CoinEnums.POSSIDE_LONG.getCode().equals(posSide)) {
|
signal = analyzeLongSignal(currentPrice, posSide);
|
} else if (CoinEnums.POSSIDE_SHORT.getCode().equals(posSide)) {
|
signal = analyzeShortSignal(currentPrice, posSide);
|
} else {
|
// 如果没有指定仓位方向,同时分析多头和空头信号
|
TradeSignal longSignal = analyzeLongSignal(currentPrice, posSide);
|
TradeSignal shortSignal = analyzeShortSignal(currentPrice, posSide);
|
|
// 优先选择非NO_SIGNAL的信号
|
if (longSignal != TradeSignal.NO_SIGNAL) {
|
signal = longSignal;
|
} else {
|
signal = shortSignal;
|
}
|
}
|
|
log.info("账户: {}, 价格: {}, 方向: {}, 生成信号: {}",
|
accountName, markPx, posSide, signal.getName());
|
|
// 设置信号参数
|
setSignalParameters(param, signal);
|
|
return param;
|
}
|
|
/**
|
* 分析多头信号
|
*/
|
private TradeSignal analyzeLongSignal(BigDecimal currentPrice, String posSide) {
|
// 检查超卖条件
|
if (rsi.isOversold() || rsi.isExtremelyOversold()) {
|
// KDJ超卖且金叉
|
if ((kdj.isOversold() && kdj.isGoldenCross()) ||
|
(boll.isBreakLower(currentPrice) && macd.isGoldenCross(prevDif, prevDea))) {
|
// 如果当前没有仓位,则开多
|
if (posSide == null || posSide.isEmpty()) {
|
return TradeSignal.OPEN_LONG;
|
}
|
// 如果当前是空头仓位,则平空
|
if (CoinEnums.POSSIDE_SHORT.getCode().equals(posSide)) {
|
return TradeSignal.CLOSE_SHORT;
|
}
|
// 如果当前已经是多头仓位,则保持观望
|
return TradeSignal.NO_SIGNAL;
|
}
|
}
|
|
// 检查超买条件
|
if (rsi.isOverbought() || rsi.isExtremelyOverbought()) {
|
// KDJ超买且死叉
|
if ((kdj.isOverbought() && kdj.isDeathCross()) ||
|
(boll.isBreakUpper(currentPrice) && macd.isDeathCross(prevDif, prevDea))) {
|
// 如果当前是多头仓位,则平多
|
if (CoinEnums.POSSIDE_LONG.getCode().equals(posSide)) {
|
return TradeSignal.CLOSE_LONG;
|
}
|
// 如果当前没有仓位,则开空
|
if (posSide == null || posSide.isEmpty()) {
|
return TradeSignal.OPEN_SHORT;
|
}
|
// 如果当前已经是空头仓位,则保持观望
|
return TradeSignal.NO_SIGNAL;
|
}
|
}
|
|
// 检查MACD金叉死叉
|
if (macd.isGoldenCross(prevDif, prevDea)) {
|
// 如果当前没有仓位,则开多
|
if (posSide == null || posSide.isEmpty()) {
|
return TradeSignal.OPEN_LONG;
|
}
|
// 如果当前是空头仓位,则平空
|
if (CoinEnums.POSSIDE_SHORT.getCode().equals(posSide)) {
|
return TradeSignal.CLOSE_SHORT;
|
}
|
// 如果当前已经是多头仓位,则保持观望
|
return TradeSignal.NO_SIGNAL;
|
} else if (macd.isDeathCross(prevDif, prevDea)) {
|
// 如果当前是多头仓位,则平多
|
if (CoinEnums.POSSIDE_LONG.getCode().equals(posSide)) {
|
return TradeSignal.CLOSE_LONG;
|
}
|
// 如果当前没有仓位,则开空
|
if (posSide == null || posSide.isEmpty()) {
|
return TradeSignal.OPEN_SHORT;
|
}
|
// 如果当前已经是空头仓位,则保持观望
|
return TradeSignal.NO_SIGNAL;
|
}
|
|
return TradeSignal.NO_SIGNAL;
|
}
|
|
/**
|
* 分析空头信号
|
*/
|
private TradeSignal analyzeShortSignal(BigDecimal currentPrice, String posSide) {
|
// 检查超买条件
|
if (rsi.isOverbought() || rsi.isExtremelyOverbought()) {
|
// KDJ超买且死叉
|
if ((kdj.isOverbought() && kdj.isDeathCross()) ||
|
(boll.isBreakUpper(currentPrice) && macd.isDeathCross(prevDif, prevDea))) {
|
// 如果当前没有仓位,则开空
|
if (posSide == null || posSide.isEmpty()) {
|
return TradeSignal.OPEN_SHORT;
|
}
|
// 如果当前是多头仓位,则平多
|
if (CoinEnums.POSSIDE_LONG.getCode().equals(posSide)) {
|
return TradeSignal.CLOSE_LONG;
|
}
|
// 如果当前已经是空头仓位,则保持观望
|
return TradeSignal.NO_SIGNAL;
|
}
|
}
|
|
// 检查超卖条件
|
if (rsi.isOversold() || rsi.isExtremelyOversold()) {
|
// KDJ超卖且金叉
|
if ((kdj.isOversold() && kdj.isGoldenCross()) ||
|
(boll.isBreakLower(currentPrice) && macd.isGoldenCross(prevDif, prevDea))) {
|
// 如果当前是空头仓位,则平空
|
if (CoinEnums.POSSIDE_SHORT.getCode().equals(posSide)) {
|
return TradeSignal.CLOSE_SHORT;
|
}
|
// 如果当前没有仓位,则开多
|
if (posSide == null || posSide.isEmpty()) {
|
return TradeSignal.OPEN_LONG;
|
}
|
// 如果当前已经是多头仓位,则保持观望
|
return TradeSignal.NO_SIGNAL;
|
}
|
}
|
|
// 检查MACD金叉死叉
|
if (macd.isGoldenCross(prevDif, prevDea)) {
|
// 如果当前是空头仓位,则平空
|
if (CoinEnums.POSSIDE_SHORT.getCode().equals(posSide)) {
|
return TradeSignal.CLOSE_SHORT;
|
}
|
// 如果当前没有仓位,则开多
|
if (posSide == null || posSide.isEmpty()) {
|
return TradeSignal.OPEN_LONG;
|
}
|
// 如果当前已经是多头仓位,则保持观望
|
return TradeSignal.NO_SIGNAL;
|
} else if (macd.isDeathCross(prevDif, prevDea)) {
|
// 如果当前没有仓位,则开空
|
if (posSide == null || posSide.isEmpty()) {
|
return TradeSignal.OPEN_SHORT;
|
}
|
// 如果当前是多头仓位,则平多
|
if (CoinEnums.POSSIDE_LONG.getCode().equals(posSide)) {
|
return TradeSignal.CLOSE_LONG;
|
}
|
// 如果当前已经是空头仓位,则保持观望
|
return TradeSignal.NO_SIGNAL;
|
}
|
|
return TradeSignal.NO_SIGNAL;
|
}
|
|
/**
|
* 设置信号参数
|
*/
|
private void setSignalParameters(TradeRequestParam param, TradeSignal signal) {
|
String side = null;
|
|
switch (signal) {
|
case BUY:
|
side = CoinEnums.SIDE_BUY.getCode();
|
break;
|
case SELL:
|
side = CoinEnums.SIDE_SELL.getCode();
|
break;
|
case OPEN_LONG:
|
side = CoinEnums.SIDE_BUY.getCode();
|
break;
|
case CLOSE_LONG:
|
side = CoinEnums.SIDE_SELL.getCode();
|
break;
|
case OPEN_SHORT:
|
side = CoinEnums.SIDE_SELL.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);
|
}
|
|
/**
|
* 更新历史指标值
|
*/
|
private void updateHistoricalIndicatorValues() {
|
prevDif = macd.getDif();
|
prevDea = macd.getDea();
|
prevK = kdj.getK();
|
prevD = kdj.getD();
|
}
|
|
/**
|
* 获取账户配置信息
|
*/
|
private String getAccountConfig(String accountName, String key) {
|
try {
|
// 这里需要根据实际情况获取账户配置信息
|
// 暂时返回默认值,实际应该从InstrumentsWs或其他配置类中获取
|
return "NO";
|
} catch (Exception e) {
|
log.error("获取账户配置信息失败", e);
|
return "NO";
|
}
|
}
|
}
|