package com.xcong.excoin.modules.okxNewPrice.indicator;
|
|
import lombok.AllArgsConstructor;
|
import lombok.Getter;
|
import lombok.NoArgsConstructor;
|
import lombok.Setter;
|
import lombok.extern.slf4j.Slf4j;
|
|
import java.math.BigDecimal;
|
import java.util.ArrayList;
|
import java.util.List;
|
|
/**
|
* 交易策略实现
|
* 展示如何为ETH合约交易(开仓/平仓)组合所有指标
|
*/
|
@Slf4j
|
public class TradingStrategy extends IndicatorBase {
|
|
@Getter
|
@Setter
|
@AllArgsConstructor
|
@NoArgsConstructor
|
public static class StrategyConfig {
|
private int maShortPeriod = 5; // 短期移动平均周期
|
private int maLongPeriod = 20; // 长期移动平均周期
|
private int rsiPeriod = 14; // RSI指标周期
|
private int kdjPeriod = 9; // KDJ指标周期
|
private int bollPeriod = 20; // 布林带周期
|
private double bollK = 2.0; // 布林带标准差倍数
|
private int atrPeriod = 14; // ATR计算周期
|
private boolean enableDynamicParams = true; // 是否启用动态参数优化
|
private boolean enableMultiTimeframeConfirm = true; // 是否启用多周期确认
|
private int volumeMaPeriod = 20; // 成交量移动平均周期
|
private boolean enableVolumeConfirm = true; // 是否启用成交量验证
|
|
// 风险控制参数
|
private BigDecimal baseLeverage = new BigDecimal(3); // 基础杠杆倍数
|
private int volatilityThresholdPeriod = 30; // 波动率阈值计算周期(用于动态杠杆)
|
private boolean enableDynamicLeverage = true; // 是否启用动态杠杆
|
private boolean enableThreeStepProfitTaking = true; // 是否启用三段式止盈
|
private boolean enableBlackSwanFilter = true; // 是否启用黑天鹅事件过滤
|
}
|
|
public enum Direction {
|
LONG, // 做多方向信号
|
SHORT, // 做空方向信号
|
RANGING // 震荡市场
|
}
|
|
public enum SignalType {
|
NONE, // 无信号
|
BUY, // 开多信号
|
SELL, // 开空信号
|
CLOSE_BUY, // 平多信号
|
CLOSE_SELL // 平空信号
|
}
|
|
private final StrategyConfig config;
|
private final MA ma;
|
private final AdvancedMA advancedMA;
|
private final BOLL boll;
|
private final KDJ kdj;
|
private final MACD macd;
|
private final RSI rsi;
|
|
public TradingStrategy() {
|
this(new StrategyConfig());
|
}
|
|
public TradingStrategy(StrategyConfig config) {
|
this.config = config;
|
this.ma = new MA();
|
this.advancedMA = new AdvancedMA();
|
this.boll = new BOLL(config.getBollPeriod(), config.getBollK());
|
this.kdj = new KDJ(config.getKdjPeriod());
|
this.macd = new MACD();
|
this.rsi = new RSI(config.getRsiPeriod());
|
}
|
|
/**
|
* 计算所有指标并生成交易信号
|
* @param prices 价格数据
|
* @param high 最高价列表
|
* @param low 最低价列表
|
* @param close 收盘价列表
|
* @param volume 成交量列表
|
* @param currentPrice 当前价格
|
* @param hasLongPosition 是否当前持有做多仓位
|
* @param hasShortPosition 是否当前持有做空仓位
|
* @param fiveMinPrices 5分钟价格数据(多周期确认)
|
* @param oneHourPrices 1小时价格数据(多周期确认)
|
* @param fourHourPrices 4小时价格数据(多周期确认)
|
* @param fundingRate 当前资金费率(用于黑天鹅过滤)
|
* @param hasLargeTransfer 是否有大额转账(用于黑天鹅过滤)
|
* @param hasUpcomingEvent 是否有即将到来的重大事件(用于黑天鹅过滤)
|
* @return 交易信号
|
*/
|
public SignalType generateSignal(List<BigDecimal> prices, List<BigDecimal> high,
|
List<BigDecimal> low, List<BigDecimal> close,
|
List<BigDecimal> volume, BigDecimal currentPrice,
|
boolean hasLongPosition, boolean hasShortPosition,
|
List<BigDecimal> fiveMinPrices,
|
List<BigDecimal> oneHourPrices,
|
List<BigDecimal> fourHourPrices,
|
BigDecimal fundingRate,
|
boolean hasLargeTransfer,
|
boolean hasUpcomingEvent) {
|
// 计算所有指标
|
calculateIndicators(prices, high, low, close);
|
|
// 检查是否为震荡市场,如果是,则执行区间交易策略
|
if (isRangeMarket()) {
|
log.info("当前市场为震荡行情,执行区间交易策略");
|
return generateRangeTradingSignal(currentPrice, volume, hasLongPosition, hasShortPosition);
|
}
|
|
// 黑天鹅事件过滤
|
if (blackSwanFilter(fundingRate, hasLargeTransfer, hasUpcomingEvent)) {
|
log.info("黑天鹅事件过滤触发,不产生信号");
|
return SignalType.NONE;
|
}
|
|
// 开多信号
|
if (shouldOpenLong(currentPrice, prices, volume, fiveMinPrices, oneHourPrices, fourHourPrices) && !hasLongPosition && !hasShortPosition) {
|
log.info("生成买入信号");
|
return SignalType.BUY;
|
}
|
|
// 开空信号
|
if (shouldOpenShort(currentPrice, volume, fiveMinPrices, oneHourPrices, fourHourPrices) && !hasLongPosition && !hasShortPosition) {
|
log.info("生成卖出信号");
|
return SignalType.SELL;
|
}
|
|
// 平多信号
|
if (shouldCloseLong(currentPrice, volume, fiveMinPrices, oneHourPrices, fourHourPrices) && hasLongPosition) {
|
log.info("生成平多信号");
|
return SignalType.CLOSE_BUY;
|
}
|
|
// 平空信号
|
if (shouldCloseShort(currentPrice, volume, fiveMinPrices, oneHourPrices, fourHourPrices) && hasShortPosition) {
|
log.info("生成平空信号");
|
return SignalType.CLOSE_SELL;
|
}
|
|
log.info("未生成信号");
|
return SignalType.NONE;
|
}
|
|
/**
|
* 多周期确认辅助方法(看涨)
|
* @param fiveMinPrices 5分钟价格数据
|
* @param oneHourPrices 1小时价格数据
|
* @param fourHourPrices 4小时价格数据
|
* @return 是否有足够的多周期确认
|
*/
|
private boolean multiTimeframeConfirm(List<BigDecimal> fiveMinPrices,
|
List<BigDecimal> oneHourPrices,
|
List<BigDecimal> fourHourPrices) {
|
if (!config.isEnableMultiTimeframeConfirm()) {
|
return true; // 如果未启用多周期确认,则默认返回true
|
}
|
|
int confirmCount = 0;
|
|
// 检查5分钟周期
|
if (hasBullishTrend(fiveMinPrices)) {
|
confirmCount++;
|
}
|
|
// 检查1小时周期
|
if (hasBullishTrend(oneHourPrices)) {
|
confirmCount++;
|
}
|
|
// 检查4小时周期
|
if (hasBullishTrend(fourHourPrices)) {
|
confirmCount++;
|
}
|
|
// 至少需要2个周期确认
|
return confirmCount >= 2;
|
}
|
|
/**
|
* 多周期确认辅助方法(看跌)
|
* @param fiveMinPrices 5分钟价格数据
|
* @param oneHourPrices 1小时价格数据
|
* @param fourHourPrices 4小时价格数据
|
* @return 是否有足够的多周期确认
|
*/
|
private boolean multiTimeframeBearishConfirm(List<BigDecimal> fiveMinPrices,
|
List<BigDecimal> oneHourPrices,
|
List<BigDecimal> fourHourPrices) {
|
if (!config.isEnableMultiTimeframeConfirm()) {
|
return true; // 如果未启用多周期确认,则默认返回true
|
}
|
|
int confirmCount = 0;
|
|
// 检查5分钟周期
|
if (hasBearishTrend(fiveMinPrices)) {
|
confirmCount++;
|
}
|
|
// 检查1小时周期
|
if (hasBearishTrend(oneHourPrices)) {
|
confirmCount++;
|
}
|
|
// 检查4小时周期
|
if (hasBearishTrend(fourHourPrices)) {
|
confirmCount++;
|
}
|
|
// 至少需要2个周期确认
|
return confirmCount >= 2;
|
}
|
|
/**
|
* 检查指定周期是否有看涨趋势
|
* @param prices 价格数据
|
* @return 是否有看涨趋势
|
*/
|
private boolean hasBullishTrend(List<BigDecimal> prices) {
|
if (prices == null || prices.size() < 20) {
|
return false; // 数据不足
|
}
|
|
// 创建临时MA指标用于判断趋势
|
MA tempMA = new MA();
|
tempMA.calculate(prices);
|
|
// 简单的趋势判断:短期MA > 长期MA
|
return tempMA.getEma5().compareTo(tempMA.getEma20()) > 0;
|
}
|
|
/**
|
* 检查指定周期是否有看跌趋势
|
* @param prices 价格数据
|
* @return 是否有看跌趋势
|
*/
|
private boolean hasBearishTrend(List<BigDecimal> prices) {
|
if (prices == null || prices.size() < 20) {
|
return false; // 数据不足
|
}
|
|
// 创建临时MA指标用于判断趋势
|
MA tempMA = new MA();
|
tempMA.calculate(prices);
|
|
// 简单的趋势判断:短期MA < 长期MA
|
return tempMA.getEma5().compareTo(tempMA.getEma20()) < 0;
|
}
|
|
/**
|
* 成交量验证辅助方法
|
* 增强版:当前成交量需大于20周期均量的1.2倍,以过滤无量反弹/回调的假信号
|
* @param volume 成交量列表
|
* @return 是否通过成交量验证
|
*/
|
private boolean volumeConfirm(List<BigDecimal> volume) {
|
if (!config.isEnableVolumeConfirm() || volume == null || volume.size() < config.getVolumeMaPeriod()) {
|
return true; // 如果未启用成交量验证或数据不足,则默认返回true
|
}
|
|
// 计算成交量移动平均
|
BigDecimal volumeMA = calculateMA(volume, config.getVolumeMaPeriod());
|
BigDecimal currentVolume = volume.get(volume.size() - 1);
|
|
// 增强验证:成交量需要大于1.2倍均线
|
return currentVolume.compareTo(volumeMA.multiply(new BigDecimal("1.2"))) > 0;
|
}
|
|
/**
|
* 量价背离检测
|
* 检测价格上涨/下跌但成交量萎缩的情况,或价格和成交量趋势不一致
|
* @param prices 价格列表
|
* @param volume 成交量列表
|
* @return 是否存在量价背离
|
*/
|
private boolean hasPriceVolumeDivergence(List<BigDecimal> prices, List<BigDecimal> volume) {
|
if (!config.isEnableVolumeConfirm() || prices == null || volume == null || prices.size() < 3 || volume.size() < 3) {
|
return false; // 如果未启用成交量验证或数据不足,则默认返回false
|
}
|
|
// 获取最近3个周期的价格和成交量
|
BigDecimal currentPrice = prices.get(prices.size() - 1);
|
BigDecimal prevPrice1 = prices.get(prices.size() - 2);
|
BigDecimal prevPrice2 = prices.get(prices.size() - 3);
|
|
BigDecimal currentVolume = volume.get(volume.size() - 1);
|
BigDecimal prevVolume1 = volume.get(volume.size() - 2);
|
BigDecimal prevVolume2 = volume.get(volume.size() - 3);
|
|
// 计算价格趋势
|
boolean priceTrendUp = currentPrice.compareTo(prevPrice1) > 0 && prevPrice1.compareTo(prevPrice2) > 0;
|
boolean priceTrendDown = currentPrice.compareTo(prevPrice1) < 0 && prevPrice1.compareTo(prevPrice2) < 0;
|
|
// 计算成交量趋势
|
boolean volumeTrendUp = currentVolume.compareTo(prevVolume1) > 0 && prevVolume1.compareTo(prevVolume2) > 0;
|
boolean volumeTrendDown = currentVolume.compareTo(prevVolume1) < 0 && prevVolume1.compareTo(prevVolume2) < 0;
|
|
// 检测量价背离
|
// 价格上涨但成交量萎缩
|
boolean bullishDivergence = priceTrendUp && volumeTrendDown;
|
// 价格下跌但成交量萎缩(通常是强势信号,不视为背离)
|
// 价格下跌但成交量放大(可能是恐慌性抛售,视为背离)
|
boolean bearishDivergence = priceTrendDown && volumeTrendUp;
|
|
return bullishDivergence || bearishDivergence;
|
}
|
|
/**
|
* 计算所有指标
|
* @param prices 价格数据
|
* @param high 最高价列表
|
* @param low 最低价列表
|
* @param close 收盘价列表
|
*/
|
private void calculateIndicators(List<BigDecimal> prices, List<BigDecimal> high,
|
List<BigDecimal> low, List<BigDecimal> close) {
|
// 计算ATR和波动率
|
BigDecimal atr = calculateATR(high, low, close, config.getAtrPeriod());
|
BigDecimal volatility = normalizeVolatility(close, atr);
|
|
// 使用动态参数计算指标
|
if (config.isEnableDynamicParams()) {
|
log.info("使用动态参数计算指标,波动率: {}", volatility);
|
ma.calculate(prices, volatility);
|
macd.calculate(prices, volatility);
|
} else {
|
ma.calculate(prices);
|
macd.calculate(prices);
|
}
|
|
// 其他指标计算
|
advancedMA.calculateTripleEMA(prices);
|
boll.calculate(prices);
|
kdj.calculate(prices);
|
rsi.calculate(prices);
|
}
|
|
/**
|
* 检查是否为震荡市场
|
* @return 是否为震荡市场
|
*/
|
private boolean isRangeMarket() {
|
// 高级MA线收敛 + RSI(40-60) + 布林带收窄
|
boolean isMaConverged = advancedMA.calculatePercent().compareTo(new BigDecimal(2)) < 0;
|
boolean isRsiNeutral = rsi.getRsi().compareTo(new BigDecimal(40)) > 0 &&
|
rsi.getRsi().compareTo(new BigDecimal(60)) < 0;
|
boolean isBollNarrow = boll.calculateBandWidth().compareTo(new BigDecimal(0.05)) < 0;
|
|
return isMaConverged && isRsiNeutral && isBollNarrow;
|
}
|
|
|
|
/**
|
* 根据15分钟时间框架指标确定市场方向(做多/做空/震荡)
|
* @param prices 价格数据列表
|
* @param high 最高价列表
|
* @param low 最低价列表
|
* @param close 收盘价列表
|
* @param currentPrice 当前价格
|
* @return 市场方向
|
*/
|
public Direction getDirection(List<BigDecimal> prices, List<BigDecimal> high,
|
List<BigDecimal> low, List<BigDecimal> close,
|
BigDecimal currentPrice) {
|
// 计算所有指标
|
calculateIndicators(prices, high, low, close);
|
|
// 检查是否为震荡市场
|
if (isRangeMarket()) {
|
return Direction.RANGING;
|
}
|
|
// 检查做多方向条件:MA多头排列 + MACD金叉 + RSI中性(30-70) + BOLL价格在上轨和中轨之间
|
boolean isMaBullish = ma.getEma5().compareTo(ma.getEma10()) > 0 &&
|
ma.getEma10().compareTo(ma.getEma20()) > 0;
|
boolean isMacdGoldenCross = macd.getDif().compareTo(macd.getDea()) > 0;
|
boolean isRsiNeutral = rsi.getRsi().compareTo(new BigDecimal(30)) > 0 &&
|
rsi.getRsi().compareTo(new BigDecimal(70)) < 0;
|
boolean isPriceInUpperMid = currentPrice.compareTo(boll.getMid()) > 0 &&
|
currentPrice.compareTo(boll.getUpper()) < 0;
|
|
if (isMaBullish && isMacdGoldenCross && isRsiNeutral && isPriceInUpperMid) {
|
return Direction.LONG;
|
}
|
|
// 检查做空方向条件:MA空头排列 + MACD死叉 + RSI中性(30-70) + BOLL价格在下轨和中轨之间
|
boolean isMaBearish = ma.getEma5().compareTo(ma.getEma10()) < 0 &&
|
ma.getEma10().compareTo(ma.getEma20()) < 0;
|
boolean isMacdDeathCross = macd.getDif().compareTo(macd.getDea()) < 0;
|
boolean isPriceInLowerMid = currentPrice.compareTo(boll.getLower()) > 0 &&
|
currentPrice.compareTo(boll.getMid()) < 0;
|
|
if (isMaBearish && isMacdDeathCross && isRsiNeutral && isPriceInLowerMid) {
|
return Direction.SHORT;
|
}
|
|
// 如果没有明确方向,默认为震荡
|
return Direction.RANGING;
|
}
|
|
/**
|
* 根据用户要求检查是否应该开多仓位
|
* 条件:MA金叉 + MACD金叉 + KDJ金叉 + RSI中性 + 价格高于BOLL中轨 + 多周期确认 + 成交量验证
|
* @param currentPrice 当前价格
|
* @param prices 价格数据
|
* @param volume 成交量列表
|
* @param fiveMinPrices 5分钟价格数据
|
* @param oneHourPrices 1小时价格数据
|
* @param fourHourPrices 4小时价格数据
|
* @return 是否应该开多
|
*/
|
private boolean shouldOpenLong(BigDecimal currentPrice, List<BigDecimal> prices, List<BigDecimal> volume,
|
List<BigDecimal> fiveMinPrices,
|
List<BigDecimal> oneHourPrices,
|
List<BigDecimal> fourHourPrices) {
|
// MA金叉 (5EMA > 20EMA)
|
boolean isMaGoldenCross = ma.getEma5().compareTo(ma.getEma20()) > 0;
|
// MACD金叉 (DIF > DEA)
|
boolean isMacdGoldenCross = macd.getDif().compareTo(macd.getDea()) > 0;
|
// KDJ金叉 (K线上穿D线)
|
boolean isKdjGoldenCross = kdj.isGoldenCross();
|
// RSI中性 (30-70)
|
boolean isRsiNeutral = rsi.getRsi().compareTo(new BigDecimal(30)) > 0 &&
|
rsi.getRsi().compareTo(new BigDecimal(70)) < 0;
|
// 价格高于BOLL中轨
|
boolean isPriceAboveBollMid = currentPrice.compareTo(boll.getMid()) > 0;
|
// 成交量验证
|
boolean isVolumeConfirmed = volumeConfirm(volume);
|
// 量价背离检测
|
boolean isPriceVolumeDivergence = hasPriceVolumeDivergence(prices, volume);
|
// 多周期确认
|
boolean isMultiTimeframeConfirmed = multiTimeframeConfirm(fiveMinPrices, oneHourPrices, fourHourPrices);
|
|
return isMaGoldenCross && isMacdGoldenCross && isKdjGoldenCross &&
|
isRsiNeutral && isPriceAboveBollMid && isVolumeConfirmed &&
|
!isPriceVolumeDivergence && isMultiTimeframeConfirmed;
|
}
|
|
/**
|
* 根据用户要求检查是否应该开空仓位
|
* 条件:MA死叉 + MACD死叉 + KDJ死叉 + RSI中性 + 价格低于BOLL中轨 + 多周期确认 + 成交量验证
|
* @param currentPrice 当前价格
|
* @param volume 成交量列表
|
* @param fiveMinPrices 5分钟价格数据
|
* @param oneHourPrices 1小时价格数据
|
* @param fourHourPrices 4小时价格数据
|
* @return 是否应该开空
|
*/
|
private boolean shouldOpenShort(BigDecimal currentPrice, List<BigDecimal> volume,
|
List<BigDecimal> fiveMinPrices,
|
List<BigDecimal> oneHourPrices,
|
List<BigDecimal> fourHourPrices) {
|
// MA死叉 (5EMA < 20EMA)
|
boolean isMaDeathCross = ma.getEma5().compareTo(ma.getEma20()) < 0;
|
// MACD死叉 (DIF < DEA)
|
boolean isMacdDeathCross = macd.getDif().compareTo(macd.getDea()) < 0;
|
// KDJ死叉 (K线下穿D线)
|
boolean isKdjDeathCross = kdj.isDeathCross();
|
// RSI中性 (30-70)
|
boolean isRsiNeutral = rsi.getRsi().compareTo(new BigDecimal(30)) > 0 &&
|
rsi.getRsi().compareTo(new BigDecimal(70)) < 0;
|
// 价格低于BOLL中轨
|
boolean isPriceBelowBollMid = currentPrice.compareTo(boll.getMid()) < 0;
|
// 成交量验证
|
boolean isVolumeConfirmed = volumeConfirm(volume);
|
// 多周期确认(看跌)
|
boolean isMultiTimeframeConfirmed = multiTimeframeBearishConfirm(fiveMinPrices, oneHourPrices, fourHourPrices);
|
|
return isMaDeathCross && isMacdDeathCross && isKdjDeathCross &&
|
isRsiNeutral && isPriceBelowBollMid && isVolumeConfirmed && isMultiTimeframeConfirmed;
|
}
|
|
/**
|
* 根据用户要求检查是否应该平多仓位
|
* 条件:MA死叉 + MACD死叉 + RSI超买 + 价格低于BOLL中轨 + 多周期确认
|
* @param currentPrice 当前价格
|
* @param volume 成交量列表
|
* @param fiveMinPrices 5分钟价格数据
|
* @param oneHourPrices 1小时价格数据
|
* @param fourHourPrices 4小时价格数据
|
* @return 是否应该平多
|
*/
|
private boolean shouldCloseLong(BigDecimal currentPrice, List<BigDecimal> volume,
|
List<BigDecimal> fiveMinPrices,
|
List<BigDecimal> oneHourPrices,
|
List<BigDecimal> fourHourPrices) {
|
// MA死叉 (5EMA < 20EMA)
|
boolean isMaDeathCross = ma.getEma5().compareTo(ma.getEma20()) < 0;
|
// MACD死叉 (DIF < DEA)
|
boolean isMacdDeathCross = macd.getDif().compareTo(macd.getDea()) < 0;
|
// RSI超买 (>70)
|
boolean isRsiOverbought = rsi.isOverbought();
|
// 价格低于BOLL中轨
|
boolean isPriceBelowBollMid = currentPrice.compareTo(boll.getMid()) < 0;
|
// 多周期确认(看跌)
|
boolean isMultiTimeframeConfirmed = multiTimeframeBearishConfirm(fiveMinPrices, oneHourPrices, fourHourPrices);
|
|
return isMaDeathCross && isMacdDeathCross && isRsiOverbought && isPriceBelowBollMid && isMultiTimeframeConfirmed;
|
}
|
|
/**
|
* 根据用户要求检查是否应该平空仓位
|
* 条件:MA金叉 + MACD金叉 + RSI超卖 + 价格高于BOLL中轨 + 多周期确认
|
* @param currentPrice 当前价格
|
* @param volume 成交量列表
|
* @param fiveMinPrices 5分钟价格数据
|
* @param oneHourPrices 1小时价格数据
|
* @param fourHourPrices 4小时价格数据
|
* @return 是否应该平空
|
*/
|
private boolean shouldCloseShort(BigDecimal currentPrice, List<BigDecimal> volume,
|
List<BigDecimal> fiveMinPrices,
|
List<BigDecimal> oneHourPrices,
|
List<BigDecimal> fourHourPrices) {
|
// MA金叉 (5EMA > 20EMA)
|
boolean isMaGoldenCross = ma.getEma5().compareTo(ma.getEma20()) > 0;
|
// MACD金叉 (DIF > DEA)
|
boolean isMacdGoldenCross = macd.getDif().compareTo(macd.getDea()) > 0;
|
// RSI超卖 (<30)
|
boolean isRsiOversold = rsi.isOversold();
|
// 价格高于BOLL中轨
|
boolean isPriceAboveBollMid = currentPrice.compareTo(boll.getMid()) > 0;
|
// 多周期确认(看涨)
|
boolean isMultiTimeframeConfirmed = multiTimeframeConfirm(fiveMinPrices, oneHourPrices, fourHourPrices);
|
|
return isMaGoldenCross && isMacdGoldenCross && isRsiOversold && isPriceAboveBollMid && isMultiTimeframeConfirmed;
|
}
|
|
/**
|
* 获取所有指标的当前状态
|
* @return 指标状态字符串
|
*/
|
public String getIndicatorStatus() {
|
return String.format("MA5: %s, MA20: %s, ", ma.getEma5(), ma.getEma20()) +
|
String.format("MACD-DIF: %s, MACD-DEA: %s, MACD-BAR: %s, ", macd.getDif(), macd.getDea(), macd.getMacdBar()) +
|
String.format("KDJ-K: %s, KDJ-D: %s, KDJ-J: %s, ", kdj.getK(), kdj.getD(), kdj.getJ()) +
|
String.format("RSI: %s, ", rsi.getRsi()) +
|
String.format("BOLL-MID: %s, BOLL-UP: %s, BOLL-DN: %s, ", boll.getMid(), boll.getUpper(), boll.getLower()) +
|
String.format("AdvancedMA-Bullish: %s, Bearish: %s, Percent: %s",
|
advancedMA.isBullish(), advancedMA.isBearish(), advancedMA.calculatePercent());
|
}
|
|
/**
|
* 生成区间交易信号
|
* 在震荡行情下,使用BOLL通道作为区间边界,结合KDJ指标生成交易信号
|
* @param currentPrice 当前价格
|
* @param volume 成交量列表
|
* @param hasLongPosition 是否当前持有做多仓位
|
* @param hasShortPosition 是否当前持有做空仓位
|
* @return 交易信号
|
*/
|
private SignalType generateRangeTradingSignal(BigDecimal currentPrice, List<BigDecimal> volume,
|
boolean hasLongPosition, boolean hasShortPosition) {
|
// 区间交易策略逻辑:
|
// 1. 价格触及BOLL下轨且KDJ超卖 → 买入信号
|
// 2. 价格触及BOLL上轨且KDJ超买 → 卖出信号
|
// 3. 价格回归BOLL中轨 → 平仓信号
|
|
// 价格触及BOLL下轨
|
boolean isPriceNearBollLower = currentPrice.compareTo(boll.getLower()) >= 0 &&
|
currentPrice.compareTo(boll.getLower().multiply(new BigDecimal("1.005"))) <= 0;
|
|
// 价格触及BOLL上轨
|
boolean isPriceNearBollUpper = currentPrice.compareTo(boll.getUpper()) <= 0 &&
|
currentPrice.compareTo(boll.getUpper().multiply(new BigDecimal("0.995"))) >= 0;
|
|
// 价格回归BOLL中轨附近
|
boolean isPriceNearBollMid = currentPrice.compareTo(boll.getMid().multiply(new BigDecimal("0.998"))) >= 0 &&
|
currentPrice.compareTo(boll.getMid().multiply(new BigDecimal("1.002"))) <= 0;
|
|
// KDJ超卖(使用调整后的阈值)
|
boolean isKdjOversold = kdj.getJ().compareTo(new BigDecimal(15)) < 0;
|
|
// KDJ超买(使用调整后的阈值)
|
boolean isKdjOverbought = kdj.getJ().compareTo(new BigDecimal(85)) > 0;
|
|
// 成交量验证(当前成交量大于20周期均值的1.2倍)
|
boolean isVolumeValid = volumeConfirm(volume) &&
|
volume.get(volume.size() - 1).compareTo(calculateMA(volume, config.getVolumeMaPeriod()).multiply(new BigDecimal("1.2"))) > 0;
|
|
// 开多逻辑:价格触及BOLL下轨且KDJ超卖且有成交量支持
|
if (isPriceNearBollLower && isKdjOversold && isVolumeValid && !hasLongPosition && !hasShortPosition) {
|
log.info("区间交易:价格触及BOLL下轨({}), KDJ-J值({})超卖,生成买入信号", boll.getLower(), kdj.getJ());
|
return SignalType.BUY;
|
}
|
|
// 开空逻辑:价格触及BOLL上轨且KDJ超买且有成交量支持
|
if (isPriceNearBollUpper && isKdjOverbought && isVolumeValid && !hasLongPosition && !hasShortPosition) {
|
log.info("区间交易:价格触及BOLL上轨({}), KDJ-J值({})超买,生成卖出信号", boll.getUpper(), kdj.getJ());
|
return SignalType.SELL;
|
}
|
|
// 平多逻辑:价格回归BOLL中轨
|
if (isPriceNearBollMid && hasLongPosition) {
|
log.info("区间交易:价格回归BOLL中轨({}),生成平多信号", boll.getMid());
|
return SignalType.CLOSE_BUY;
|
}
|
|
// 平空逻辑:价格回归BOLL中轨
|
if (isPriceNearBollMid && hasShortPosition) {
|
log.info("区间交易:价格回归BOLL中轨({}),生成平空信号", boll.getMid());
|
return SignalType.CLOSE_SELL;
|
}
|
|
return SignalType.NONE;
|
}
|
|
/**
|
* 计算动态杠杆倍数
|
* 杠杆倍数 = 基础杠杆 * (波动率阈值/当前波动率)
|
* @param high 最高价列表
|
* @param low 最低价列表
|
* @param close 收盘价列表
|
* @return 动态杠杆倍数
|
*/
|
public BigDecimal calculateDynamicLeverage(List<BigDecimal> high, List<BigDecimal> low, List<BigDecimal> close) {
|
if (!config.isEnableDynamicLeverage()) {
|
return config.getBaseLeverage();
|
}
|
|
// 计算当前ATR和波动率
|
BigDecimal currentAtr = calculateATR(high, low, close, config.getAtrPeriod());
|
BigDecimal currentVolatility = normalizeVolatility(close, currentAtr);
|
|
// 计算30日ATR移动中位数作为波动率阈值
|
BigDecimal volatilityThreshold = calculateVolatilityThreshold(high, low, close);
|
|
// 动态计算杠杆倍数
|
BigDecimal leverage = config.getBaseLeverage().multiply(volatilityThreshold).divide(currentVolatility, 2, BigDecimal.ROUND_HALF_UP);
|
|
// 限制杠杆范围在1x-10x之间
|
leverage = leverage.min(new BigDecimal(10)).max(BigDecimal.ONE);
|
|
log.info("动态杠杆计算 - 基础杠杆: {}, 波动率阈值: {}, 当前波动率: {}, 计算杠杆: {}",
|
config.getBaseLeverage(), volatilityThreshold, currentVolatility, leverage);
|
|
return leverage;
|
}
|
|
/**
|
* 计算波动率阈值(30日ATR移动中位数)
|
* @param high 最高价列表
|
* @param low 最低价列表
|
* @param close 收盘价列表
|
* @return 波动率阈值
|
*/
|
private BigDecimal calculateVolatilityThreshold(List<BigDecimal> high, List<BigDecimal> low, List<BigDecimal> close) {
|
if (high == null || low == null || close == null || close.size() < config.getVolatilityThresholdPeriod()) {
|
return new BigDecimal(5); // 默认阈值
|
}
|
|
List<BigDecimal> volatilityList = new ArrayList<>();
|
for (int i = close.size() - config.getVolatilityThresholdPeriod(); i < close.size(); i++) {
|
List<BigDecimal> recentHigh = high.subList(Math.max(0, i - config.getAtrPeriod()), i + 1);
|
List<BigDecimal> recentLow = low.subList(Math.max(0, i - config.getAtrPeriod()), i + 1);
|
List<BigDecimal> recentClose = close.subList(Math.max(0, i - config.getAtrPeriod()), i + 1);
|
|
BigDecimal atr = calculateATR(recentHigh, recentLow, recentClose, config.getAtrPeriod());
|
BigDecimal volatility = normalizeVolatility(recentClose, atr);
|
volatilityList.add(volatility);
|
}
|
|
// 计算中位数
|
volatilityList.sort(BigDecimal::compareTo);
|
int midIndex = volatilityList.size() / 2;
|
return volatilityList.get(midIndex);
|
}
|
|
/**
|
* 三段式止盈策略
|
* 第一目标:BOLL上轨(30%仓位)
|
* 第二目标:斐波那契161.8%(50%仓位)
|
* 第三目标:趋势线破位(20%仓位)
|
* @param entryPrice 入场价格
|
* @param currentPrice 当前价格
|
* @param direction 交易方向
|
* @param positionSize 当前仓位大小
|
* @return 止盈信号和应该平仓的仓位比例
|
*/
|
public ProfitTakingResult calculateThreeStepProfitTaking(BigDecimal entryPrice, BigDecimal currentPrice,
|
Direction direction, BigDecimal positionSize) {
|
if (!config.isEnableThreeStepProfitTaking()) {
|
return new ProfitTakingResult(SignalType.NONE, BigDecimal.ZERO);
|
}
|
|
// 计算三个止盈目标
|
BigDecimal firstTarget = calculateFirstProfitTarget(entryPrice, currentPrice, direction);
|
BigDecimal secondTarget = calculateSecondProfitTarget(entryPrice, direction);
|
BigDecimal thirdTarget = calculateThirdProfitTarget(currentPrice, direction);
|
|
// 判断当前价格是否达到目标
|
if (direction == Direction.LONG) {
|
if (currentPrice.compareTo(thirdTarget) >= 0) {
|
// 达到第三目标,平全部仓位
|
return new ProfitTakingResult(SignalType.CLOSE_BUY, positionSize);
|
} else if (currentPrice.compareTo(secondTarget) >= 0) {
|
// 达到第二目标,平50%仓位
|
return new ProfitTakingResult(SignalType.CLOSE_BUY, positionSize.multiply(new BigDecimal("0.5")));
|
} else if (currentPrice.compareTo(firstTarget) >= 0) {
|
// 达到第一目标,平30%仓位
|
return new ProfitTakingResult(SignalType.CLOSE_BUY, positionSize.multiply(new BigDecimal("0.3")));
|
}
|
} else if (direction == Direction.SHORT) {
|
if (currentPrice.compareTo(thirdTarget) <= 0) {
|
// 达到第三目标,平全部仓位
|
return new ProfitTakingResult(SignalType.CLOSE_SELL, positionSize);
|
} else if (currentPrice.compareTo(secondTarget) <= 0) {
|
// 达到第二目标,平50%仓位
|
return new ProfitTakingResult(SignalType.CLOSE_SELL, positionSize.multiply(new BigDecimal("0.5")));
|
} else if (currentPrice.compareTo(firstTarget) <= 0) {
|
// 达到第一目标,平30%仓位
|
return new ProfitTakingResult(SignalType.CLOSE_SELL, positionSize.multiply(new BigDecimal("0.3")));
|
}
|
}
|
|
return new ProfitTakingResult(SignalType.NONE, BigDecimal.ZERO);
|
}
|
|
/**
|
* 计算第一止盈目标:BOLL上轨
|
*/
|
private BigDecimal calculateFirstProfitTarget(BigDecimal entryPrice, BigDecimal currentPrice, Direction direction) {
|
return direction == Direction.LONG ? boll.getUpper() : boll.getLower();
|
}
|
|
/**
|
* 计算第二止盈目标:斐波那契161.8%
|
*/
|
private BigDecimal calculateSecondProfitTarget(BigDecimal entryPrice, Direction direction) {
|
BigDecimal fibonacciRatio = new BigDecimal("1.618");
|
if (direction == Direction.LONG) {
|
return entryPrice.multiply(BigDecimal.ONE.add(fibonacciRatio.divide(new BigDecimal(100))));
|
} else {
|
return entryPrice.multiply(BigDecimal.ONE.subtract(fibonacciRatio.divide(new BigDecimal(100))));
|
}
|
}
|
|
/**
|
* 计算第三止盈目标:简单趋势线破位(这里简化为MA5下穿MA20)
|
*/
|
private BigDecimal calculateThirdProfitTarget(BigDecimal currentPrice, Direction direction) {
|
// 这里使用简化的趋势线破位判断
|
// 实际应用中可以使用更复杂的趋势线计算
|
return direction == Direction.LONG ? ma.getEma20() : ma.getEma20();
|
}
|
|
|
|
/**
|
* 黑天鹅事件过滤
|
* 规避重大事件前后30分钟、链上大额转账、异常资金费率
|
* @param fundingRate 当前资金费率
|
* @param hasLargeTransfer 是否有大额转账
|
* @param hasUpcomingEvent 是否有即将到来的重大事件
|
* @return 是否应该规避交易
|
*/
|
public boolean blackSwanFilter(BigDecimal fundingRate, boolean hasLargeTransfer, boolean hasUpcomingEvent) {
|
if (!config.isEnableBlackSwanFilter()) {
|
return false;
|
}
|
|
// 资金费率绝对值大于0.2%
|
boolean isAbnormalFundingRate = fundingRate != null &&
|
fundingRate.abs().compareTo(new BigDecimal("0.002")) > 0;
|
|
// 大额转账监控(这里简化为外部传入)
|
// 重大事件监控(这里简化为外部传入)
|
|
boolean shouldAvoid = isAbnormalFundingRate || hasLargeTransfer || hasUpcomingEvent;
|
|
if (shouldAvoid) {
|
log.info("黑天鹅事件过滤触发 - 资金费率异常: {}, 大额转账: {}, 即将发生重大事件: {}",
|
isAbnormalFundingRate, hasLargeTransfer, hasUpcomingEvent);
|
}
|
|
return shouldAvoid;
|
}
|
|
/**
|
* 止盈结果类
|
*/
|
public static class ProfitTakingResult {
|
private SignalType signal;
|
private BigDecimal closePositionSize;
|
|
public ProfitTakingResult(SignalType signal, BigDecimal closePositionSize) {
|
this.signal = signal;
|
this.closePositionSize = closePositionSize;
|
}
|
|
public SignalType getSignal() {
|
return signal;
|
}
|
|
public BigDecimal getClosePositionSize() {
|
return closePositionSize;
|
}
|
}
|
}
|