Administrator
2025-12-22 7e5aeb4270fa91096410e66542273def7beaea5d
feat(trading): 实现综合技术指标交易策略

- 新增抽象技术指标策略基类,提供通用功能
- 实现高级移动平均线(AdvancedMA)指标,支持三重EMA交叉系统
- 实现布林带(BOLL)指标,支持动态标准差倍数调整
- 开发综合技术指标策略,整合MACD、KDJ、RSI、BOLL等指标
- 开发核心技术指标策略,包含三重EMA、波动率自适应布林带等
- 添加技术指标计算和交易信号生成功能
- 实现多空头信号分析和风险控制机制
15 files added
2574 ■■■■■ changed files
CoreTechnicalStrategy_Usage_Guide.md 150 ●●●●● patch | view | raw | blame | history
indicator_package_documentation.md 288 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/AdvancedMA.java 104 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/BOLL.java 143 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/IndicatorBase.java 94 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/KDJ.java 120 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MA.java 151 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MACD.java 80 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/RSI.java 119 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/AbstractTechnicalIndicatorStrategy.java 89 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/ComprehensiveTechnicalStrategy.java 351 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/CoreTechnicalStrategy.java 589 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/CoreTechnicalStrategyUsageGuide.md 198 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/TechnicalIndicatorStrategy.java 44 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/TradeSignal.java 54 ●●●●● patch | view | raw | blame | history
CoreTechnicalStrategy_Usage_Guide.md
New file
@@ -0,0 +1,150 @@
# CoreTechnicalStrategy 使用指南
## 功能概述
CoreTechnicalStrategy 是一个集成了多种技术指标的交易策略类,用于生成做多、做空、买入和卖出信号。它整合了以下核心指标:
- 三重 EMA 交叉系统 (9/21/55 周期)
- 波动率自适应布林带
- MACD 能量柱分级策略
- RSI (相对强弱指标)
- KDJ 指标
## 所需参数
### 1. 初始化参数
```java
CoreTechnicalStrategy strategy = new CoreTechnicalStrategy();
```
### 2. 初始化价格历史
在使用策略之前,需要先初始化价格历史数据:
```java
List<BigDecimal> historicalPrices = Arrays.asList(
    new BigDecimal(30000),
    new BigDecimal(30100),
    new BigDecimal(30200),
    // 添加更多历史价格数据,建议至少包含 60 个数据点
);
strategy.init(historicalPrices);
```
### 3. 获取交易信号参数
调用 `getSignal` 方法获取交易信号,需要传入以下参数:
| 参数名 | 类型 | 描述 | 示例值 |
|--------|------|------|--------|
| accountName | String | 账户名称 | "main_account" |
| markPx | String | 当前标记价格 | "30500.50" |
| posSide | String | 当前持仓方向 | "long" 或 "short" 或 "net" |
### 4. 持仓方向说明
- `"long"`: 多头持仓
- `"short"`: 空头持仓
- `"net"`: 净持仓(无方向)
## 使用示例
```java
import com.xcong.excoin.modules.okxNewPrice.indicator.strategy.CoreTechnicalStrategy;
import com.xcong.excoin.modules.okxNewPrice.okxWs.param.TradeRequestParam;
import com.xcong.excoin.modules.okxNewPrice.okxWs.param.TradeSignal;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CoreTechnicalStrategyExample {
    public static void main(String[] args) {
        // 1. 创建策略实例
        CoreTechnicalStrategy strategy = new CoreTechnicalStrategy();
        // 2. 准备历史价格数据
        List<BigDecimal> historicalPrices = new ArrayList<>();
        // 假设我们有过去 100 个价格数据
        for (int i = 0; i < 100; i++) {
            historicalPrices.add(new BigDecimal(30000 + i * 10));
        }
        // 3. 初始化策略
        strategy.initialize();
        // 4. 更新初始价格历史
        strategy.updatePrices(historicalPrices);
        // 5. 定期更新价格(例如每分钟获取一次最新价格)
        // 这里的价格是指交易资产的当前市场价格,如加密货币的标记价格或股票的实时价格
        BigDecimal newMarketPrice = new BigDecimal("31050.75");
        List<BigDecimal> newPrices = Collections.singletonList(newMarketPrice);
        strategy.updatePrices(newPrices);
        // 6. 获取交易信号
        String accountName = "trading_account";
        String currentPrice = newMarketPrice.toString();
        String currentPosition = "net"; // 当前无持仓
        TradeRequestParam tradeParam = strategy.getSignal(accountName, currentPrice, currentPosition);
        // 7. 处理交易信号
        if (tradeParam.getSignal() == TradeSignal.BUY) {
            System.out.println("执行买入操作");
            // 调用交易API执行买入
        } else if (tradeParam.getSignal() == TradeSignal.SELL) {
            System.out.println("执行卖出操作");
            // 调用交易API执行卖出
        } else {
            System.out.println("无交易信号");
        }
    }
}
## 返回值说明
`getSignal` 方法返回 `TradeRequestParam` 对象,包含以下主要属性:
| 属性名 | 类型 | 描述 |
|--------|------|------|
| accountName | String | 账户名称 |
| markPx | String | 当前标记价格 |
| posSide | String | 持仓方向 |
| signal | TradeSignal | 交易信号:BUY、SELL 或 NO_SIGNAL |
| side | String | 交易方向:"buy" 或 "sell" |
| tradeType | String | 交易类型:"open"(开仓)或 "close"(平仓) |
## 核心指标逻辑说明
### 1. 三重 EMA 交叉系统
- **多头排列**:9EMA > 21EMA > 55EMA
- **空头排列**:9EMA < 21EMA < 55EMA
- **震荡过滤**:当三线粘合度 < 2% 时暂停交易
### 2. 波动率自适应布林带
- **动态标准差倍数**:根据 ATR(平均真实范围)自动调整
  - ATR < 0.5% 时,使用 2 倍标准差
  - 0.5% ≤ ATR < 1% 时,使用 2.5 倍标准差
  - ATR ≥ 1% 时,使用 3 倍标准差
- **做空信号**:价格突破上轨 + 成交量放大
- **做多信号**:价格触及下轨 + 正资金费率
### 3. MACD 能量柱分级策略
- **多头入场**:当前能量柱 > 0 且面积增速 > 前周期 20%
- **空头反转**:能量柱顶背离 + RSI > 70 区域死叉
## 注意事项
1. **数据充足性**:确保提供足够的历史价格数据(至少 60 个数据点),以便所有指标能准确计算
2. **价格更新**:策略不会自动更新价格历史,需要外部定期调用 `updatePrices` 方法添加新价格。这里的价格是指交易资产的市场价格(如加密货币的当前标记价格、股票的实时价格等),用于计算技术指标。
3. **参数调整**:可以根据市场情况调整指标参数,如 EMA 周期、布林带参数等
4. **风险控制**:本策略仅提供交易信号,实际交易时应结合风险控制措施
## 错误处理
- 如果策略未初始化或价格历史为空,将返回 NO_SIGNAL
- 如果计算过程中发生异常,将返回 NO_SIGNAL 并记录错误日志
## 依赖关系
- Java 8 或更高版本
- Lombok 库(用于自动生成 getter/setter 方法)
- Apache Commons Math 库(用于数学计算)
indicator_package_documentation.md
New file
@@ -0,0 +1,288 @@
# com.xcong.excoin.modules.okxNewPrice.indicator 包文档
## 1. 包概述
该包实现了一套完整的技术指标分析系统,包含基础指标计算、高级指标策略以及信号生成功能。主要用于加密货币交易中的技术分析和策略决策,支持三重EMA交叉系统、波动率自适应布林带、MACD能量柱分级策略等高级分析方法。
## 2. 类层次结构
```
├── IndicatorBase (抽象基类)
│   ├── MA (简单移动平均线)
│   ├── AdvancedMA (三重EMA交叉系统)
│   ├── BOLL (波动率自适应布林带)
│   ├── MACD (移动平均线收敛发散)
│   ├── RSI (相对强弱指标)
│   └── KDJ (随机指标)
└── strategy/
    ├── TechnicalIndicatorStrategy (策略接口)
    ├── AbstractTechnicalIndicatorStrategy (策略抽象基类)
    ├── CoreTechnicalStrategy (核心技术指标策略)
    ├── ComprehensiveTechnicalStrategy (综合技术指标策略)
    └── TradeSignal (交易信号枚举)
```
## 3. 核心类详解
### 3.1 IndicatorBase (指标基类)
**功能**:提供所有技术指标的通用计算方法
**核心方法**:
- `calculateMA(List<BigDecimal> prices, int period)` - 计算移动平均值
- `calculateEMA(List<BigDecimal> prices, int period, BigDecimal prevEMA)` - 计算指数移动平均值
- `calculateStdDev(List<BigDecimal> prices, int period)` - 计算标准差
- `getRecentPrices(List<BigDecimal> prices, int period)` - 获取最近N个价格数据
**使用场景**:作为所有指标类的父类,提供基础计算功能
### 3.2 MA (移动平均线)
**功能**:实现标准周期的移动平均线指标
**支持的周期**:
- MA5/MA10/MA20/MA30/MA60 (简单移动平均线)
- EMA5/EMA10/EMA20/EMA30/EMA60 (指数移动平均线)
**核心方法**:
- `calculate(List<BigDecimal> prices)` - 计算所有周期的移动平均线
- `isGoldenCross()` - 判断金叉信号
- `isDeathCross()` - 判断死叉信号
**使用场景**:用于基本趋势判断和交叉信号分析
### 3.3 AdvancedMA (高级移动平均线)
**功能**:实现三重EMA交叉系统,用于高级趋势分析
**支持的周期**:EMA9/EMA21/EMA55 (三重EMA交叉系统)
**核心方法**:
- `calculateTripleEMA(List<BigDecimal> prices)` - 计算三重EMA指标
- `isBullish()` - 判断多头排列 (9EMA > 21EMA > 55EMA)
- `isBearish()` - 判断空头排列 (9EMA < 21EMA < 55EMA)
- `calculatePercent()` - 计算三线粘合度
- `isUpAndDown()` - 判断震荡行情 (粘合度 < 2%)
**使用场景**:用于判断市场趋势强度和过滤震荡行情
### 3.4 BOLL (布林带)
**功能**:实现波动率自适应布林带指标
**计算逻辑**:
- 中轨(MB)= N日移动平均线
- 上轨(UP)= 中轨 + K倍标准差
- 下轨(DN)= 中轨 - K倍标准差
**核心方法**:
- `calculate(List<BigDecimal> prices)` - 计算布林带指标
- `isBreakUpper()` - 判断价格突破上轨
- `isBreakLower()` - 判断价格跌破下轨
- `isReturnFromUpper()` - 判断价格回归上轨下方
- `isReturnFromLower()` - 判断价格回归下轨上方
- `calculateBandWidth()` - 计算布林带宽度
**使用场景**:用于判断价格波动范围和突破信号
### 3.5 MACD (移动平均线收敛发散)
**功能**:实现MACD指标,包括DIF、DEA和柱状图
**计算逻辑**:
- DIF = EMA(12) - EMA(26)
- DEA = EMA(DIF, 9)
- MACD柱状图 = (DIF - DEA) * 2
**核心方法**:
- `calculate(List<BigDecimal> prices)` - 计算MACD指标
- `isGoldenCross()` - 判断金叉信号(DIF上穿DEA)
- `isDeathCross()` - 判断死叉信号(DIF下穿DEA)
**使用场景**:用于判断价格动量和趋势强度
### 3.6 RSI (相对强弱指标)
**功能**:实现相对强弱指标,用于判断超买超卖
**计算逻辑**:
- RSI = 100 - (100 / (1 + (平均上涨幅度 / 平均下跌幅度)))
**核心方法**:
- `calculate(List<BigDecimal> prices)` - 计算RSI指标
- `isOverbought()` - 判断超买 (RSI > 70)
- `isOversold()` - 判断超卖 (RSI < 30)
- `isExtremelyOverbought()` - 判断严重超买 (RSI > 80)
- `isExtremelyOversold()` - 判断严重超卖 (RSI < 20)
**使用场景**:用于判断市场情绪和价格反转点
### 3.7 KDJ (随机指标)
**功能**:实现KDJ指标,用于判断价格反转和超买超卖
**计算逻辑**:
- RSV = (收盘价 - N日内最低价) / (N日内最高价 - N日内最低价) * 100
- K = 2/3 * 前一日K值 + 1/3 * 当日RSV
- D = 2/3 * 前一日D值 + 1/3 * 当日K值
- J = 3*K - 2*D
**核心方法**:
- `calculate(List<BigDecimal> prices)` - 计算KDJ指标
- `isOverbought()` - 判断超买 (K > 80)
- `isOversold()` - 判断超卖 (K < 20)
- `isGoldenCross()` - 判断金叉信号(K线上穿D线)
- `isDeathCross()` - 判断死叉信号(K线下穿D线)
**使用场景**:用于判断价格短期反转和超买超卖
## 4. 策略相关类
### 4.1 TechnicalIndicatorStrategy (策略接口)
**功能**:定义技术指标策略的基本方法
**核心方法**:
- `initialize()` - 初始化策略
- `updatePrices(List<BigDecimal> prices)` - 更新价格数据
- `getSignal()` - 获取交易信号
- `getStrategyName()` - 获取策略名称
- `isValid()` - 判断策略是否有效
**使用场景**:作为所有技术指标策略的接口,定义统一的方法规范
### 4.2 AbstractTechnicalIndicatorStrategy (策略抽象基类)
**功能**:提供技术指标策略的通用功能
**核心功能**:
- 价格历史管理(最多保存100条记录)
- 策略初始化
- 价格更新
- 交易请求参数创建
**使用场景**:作为所有具体策略的父类,提供基础功能
### 4.3 TradeSignal (交易信号枚举)
**功能**:定义所有可能的交易信号类型
**信号类型**:
- NO_SIGNAL (无信号)
- BUY (买入)
- SELL (卖出)
- OPEN_LONG (开多)
- CLOSE_LONG (平多)
- OPEN_SHORT (开空)
- CLOSE_SHORT (平空)
- STOP_LOSS (止损)
- TAKE_PROFIT (止盈)
**使用场景**:用于策略生成和传递交易信号
### 4.4 CoreTechnicalStrategy (核心技术指标策略)
**功能**:整合三重EMA交叉系统、波动率自适应布林带、MACD能量柱分级策略等高级指标
**核心特点**:
- 波动率自适应布林带(根据ATR动态调整标准差倍数)
- MACD能量柱面积指标(累计过去5根柱体积分)
- 多重指标综合分析(EMA、MACD、RSI、KDJ、BOLL)
- 震荡行情过滤(三线粘合度 < 2%)
**交易信号生成规则**:
- 多头入场:当前柱体>0且面积增速>前周期20%
- 空头入场:当前柱体<0且面积增速>前周期20%
- 突破上轨+成交量放大3倍=做空信号
- 触及下轨+期货资金费率转正=做多信号
- 空头反转:柱体顶背离+RSI>70区域死叉
- 多头反转:柱体底背离+RSI<30区域金叉
**使用场景**:用于复杂的技术分析和交易决策
### 4.5 ComprehensiveTechnicalStrategy (综合技术指标策略)
**功能**:整合MACD、KDJ、RSI、BOLL等指标生成交易信号
**核心特点**:
- 基于多空方向分别分析信号
- 结合超买超卖条件和指标交叉信号
- 简化的信号生成逻辑
**交易信号生成规则**:
- 多头方向:RSI超卖+KDJ金叉或布林带下轨突破+MACD金叉=买入信号
- 多头方向:RSI超买+KDJ死叉或布林带上轨突破+MACD死叉=卖出信号
- 空头方向:RSI超买+KDJ死叉或布林带上轨突破+MACD死叉=卖出信号
- 空头方向:RSI超卖+KDJ金叉或布林带下轨突破+MACD金叉=买入信号
**使用场景**:用于较为简单的技术分析和交易决策
## 5. 工作流程
```
1. 初始化策略(initialize())
2. 更新价格数据(updatePrices())
3. 计算技术指标(calculate())
4. 分析交易信号(analyzeSignal())
5. 生成交易请求参数(createTradeRequestParam())
6. 执行交易操作
```
## 6. 如何使用
### 6.1 基本使用示例
```java
// 创建策略实例
CoreTechnicalStrategy strategy = new CoreTechnicalStrategy();
// 初始化策略
strategy.initialize();
// 更新价格数据(需要外部定期调用)
List<BigDecimal> prices = new ArrayList<>();
// 添加价格数据...
strategy.updatePrices(prices);
// 获取交易信号
TradeRequestParam param = strategy.getSignal("account1", "45000.0", "long");
// 处理交易信号
if (TradeSignal.BUY.equals(param.getSignal())) {
    // 执行买入操作
} else if (TradeSignal.SELL.equals(param.getSignal())) {
    // 执行卖出操作
}
```
### 6.2 参数说明
- `accountName`:账户名称,用于标识交易账户
- `markPx`:当前标记价格,用于计算指标和生成信号
- `posSide`:仓位方向,取值为"long"(多头)或"short"(空头)
### 6.3 价格更新说明
策略不会自动更新价格历史,需要外部定期调用`updatePrices()`方法添加新价格。价格历史最多保存100条记录,超过时会自动移除最旧的价格记录。
## 7. 代码优化建议
1. **性能优化**:
   - 考虑使用缓存机制存储已计算的指标值
   - 对于高频更新的场景,可实现增量计算
2. **功能扩展**:
   - 添加更多技术指标(如ATR、OBV等)
   - 实现回测功能,支持历史数据验证
3. **鲁棒性提升**:
   - 增加异常处理和参数验证
   - 实现策略监控和告警机制
4. **可维护性**:
   - 提取常量到配置文件
   - 增强日志记录,便于调试和分析
## 8. 总结
该包提供了一套完整的技术指标分析系统,支持从基础指标计算到高级策略分析的全流程。通过整合多种技术指标,实现了复杂的交易信号生成逻辑,可用于加密货币交易中的技术分析和策略决策。系统设计遵循面向对象原则,具有良好的可扩展性和可维护性。
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/AdvancedMA.java
New file
@@ -0,0 +1,104 @@
package com.xcong.excoin.modules.okxNewPrice.indicator;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
/**
 * Advanced MA (Moving Average) 指标实现
 * 支持扩展周期的指数移动平均线(EMA),用于三重EMA交叉系统
 */
@Slf4j
@Getter
@Setter
public class AdvancedMA extends IndicatorBase {
    // 扩展周期 - 三重EMA交叉系统
    public static final int EMA9 = 9;
    public static final int EMA21 = 21;
    public static final int EMA55 = 55;
    private BigDecimal ema9 = BigDecimal.ZERO;
    private BigDecimal ema21 = BigDecimal.ZERO;
    private BigDecimal ema55 = BigDecimal.ZERO;
    private BigDecimal prevEma9 = null;
    private BigDecimal prevEma21 = null;
    private BigDecimal prevEma55 = null;
    /**
     * 计算三重EMA交叉系统的指标
     * @param prices 价格列表
     */
    public void calculateTripleEMA(List<BigDecimal> prices) {
        if (prices == null || prices.isEmpty()) {
            return;
        }
        // 计算三重EMA
        prevEma9 = calculateEMA(prices, EMA9, prevEma9);
        ema9 = prevEma9;
        prevEma21 = calculateEMA(prices, EMA21, prevEma21);
        ema21 = prevEma21;
        prevEma55 = calculateEMA(prices, EMA55, prevEma55);
        ema55 = prevEma55;
        log.debug("三重EMA计算结果 - EMA9: {}, EMA21: {}, EMA55: {}",
                ema9, ema21, ema55);
    }
    /**
     * 判断三重EMA多头排列
     * 当9EMA > 21EMA > 55EMA时触发多头条件
     * @return 是否形成多头排列
     */
    public boolean isBullish() {
        return ema9.compareTo(ema21) > 0 && ema21.compareTo(ema55) > 0;
    }
    /**
     * 判断三重EMA空头排列
     * 当9EMA < 21EMA < 55EMA时触发空头条件
     * @return 是否形成空头排列
     */
    public boolean isBearish() {
        return ema9.compareTo(ema21) < 0 && ema21.compareTo(ema55) < 0;
    }
    /**
     * 计算三线粘合度
     * 用于自动过滤震荡行情
     * @return 粘合度百分比,值越小表示越粘合
     */
    public BigDecimal calculatePercent() {
        // 计算最大值和最小值
        BigDecimal max = ema9.max(ema21).max(ema55);
        BigDecimal min = ema9.min(ema21).min(ema55);
        // 计算平均价
        BigDecimal avg = ema9.add(ema21).add(ema55).divide(new BigDecimal(3), 8, RoundingMode.HALF_UP);
        // 计算粘合度: (最大值 - 最小值) / 平均值 * 100%
        if (avg.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        return max.subtract(min).divide(avg, 8, RoundingMode.HALF_UP).multiply(new BigDecimal(100));
    }
    /**
     * 检查是否处于震荡行情(三线粘合)
     * 当三线粘合度 < 2% 时判定为震荡行情
     * @return 是否处于震荡行情
     */
    public boolean isUpAndDown() {
        BigDecimal bigDecimal = calculatePercent();
        return bigDecimal.compareTo(new BigDecimal(2)) < 0;
    }
}
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/BOLL.java
New file
@@ -0,0 +1,143 @@
package com.xcong.excoin.modules.okxNewPrice.indicator;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
/**
 * BOLL (Bollinger Bands) 指标实现
 * 计算逻辑:
 * 1. 中轨(MB)= N日移动平均线
 * 2. 上轨(UP)= 中轨 + K倍标准差
 * 3. 下轨(DN)= 中轨 - K倍标准差
 */
@Slf4j
@Getter
@Setter
public class BOLL extends IndicatorBase {
    private static final int DEFAULT_PERIOD = 20;
    private static final double DEFAULT_K = 2.0;
    private int period = DEFAULT_PERIOD;
    private BigDecimal k = new BigDecimal(DEFAULT_K);
    private BigDecimal mid = BigDecimal.ZERO;
    private BigDecimal upper = BigDecimal.ZERO;
    private BigDecimal lower = BigDecimal.ZERO;
    public BOLL() {}
    public BOLL(int period, double k) {
        this.period = period;
        this.k = new BigDecimal(k);
    }
    /**
     * 计算BOLL指标
     * @param prices 价格列表
     */
    public void calculate(List<BigDecimal> prices) {
        if (prices == null || prices.size() < period) {
            return;
        }
        // 计算中轨(MB)= N日移动平均线
        mid = calculateMA(prices, period);
        // 计算标准差
        BigDecimal stdDev = calculateStdDev(prices, period);
        // 计算上轨(UP)和下轨(DN)
        BigDecimal bandWidth = k.multiply(stdDev);
        upper = mid.add(bandWidth).setScale(8, RoundingMode.HALF_UP);
        lower = mid.subtract(bandWidth).setScale(8, RoundingMode.HALF_UP);
        log.debug("BOLL计算结果 - 中轨: {}, 上轨: {}, 下轨: {}", mid, upper, lower);
    }
    /**
     * 判断价格是否突破上轨
     * @param price 当前价格
     * @return 是否突破上轨
     */
    public boolean isBreakUpper(BigDecimal price) {
        return price.compareTo(upper) > 0;
    }
    /**
     * 判断价格是否跌破下轨
     * @param price 当前价格
     * @return 是否跌破下轨
     */
    public boolean isBreakLower(BigDecimal price) {
        return price.compareTo(lower) < 0;
    }
    /**
     * 判断价格是否回归上轨下方
     * @param price 当前价格
     * @param prevPrice 前一期价格
     * @return 是否回归上轨下方
     */
    public boolean isReturnFromUpper(BigDecimal price, BigDecimal prevPrice) {
        return prevPrice.compareTo(upper) > 0 && price.compareTo(upper) <= 0;
    }
    /**
     * 判断价格是否回归下轨上方
     * @param price 当前价格
     * @param prevPrice 前一期价格
     * @return 是否回归下轨上方
     */
    public boolean isReturnFromLower(BigDecimal price, BigDecimal prevPrice) {
        return prevPrice.compareTo(lower) < 0 && price.compareTo(lower) >= 0;
    }
    /**
     * 判断价格是否在中轨上方
     * @param price 当前价格
     * @return 是否在中轨上方
     */
    public boolean isAboveMid(BigDecimal price) {
        return price.compareTo(mid) > 0;
    }
    /**
     * 判断价格是否在中轨下方
     * @param price 当前价格
     * @return 是否在中轨下方
     */
    public boolean isBelowMid(BigDecimal price) {
        return price.compareTo(mid) < 0;
    }
    /**
     * 计算布林带宽度
     * @return 布林带宽度
     */
    public BigDecimal calculateBandWidth() {
        if (mid.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        return upper.subtract(lower).divide(mid, 8, RoundingMode.HALF_UP);
    }
    /**
     * 计算价格相对于布林带的位置
     * @param price 当前价格
     * @return 价格位置指标 (-1: 下轨外, 0: 轨道内, 1: 上轨外)
     */
    public int getPricePosition(BigDecimal price) {
        if (price.compareTo(upper) > 0) {
            return 1;
        } else if (price.compareTo(lower) < 0) {
            return -1;
        } else {
            return 0;
        }
    }
}
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/IndicatorBase.java
New file
@@ -0,0 +1,94 @@
package com.xcong.excoin.modules.okxNewPrice.indicator;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
/**
 * 技术指标基础类,提供通用计算方法
 */
public abstract class IndicatorBase {
    /**
     * 计算移动平均值
     * @param prices 价格列表
     * @param period 周期
     * @return 移动平均值
     */
    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);
    }
    /**
     * 计算指数移动平均值
     * @param prices 价格列表
     * @param period 周期
     * @param prevEMA 前一个EMA值
     * @return 指数移动平均值
     */
    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)));
    }
    /**
     * 计算标准差
     * @param prices 价格列表
     * @param period 周期
     * @return 标准差
     */
    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);
    }
    /**
     * 计算平方根(简化实现)
     * @param value 输入值
     * @return 平方根
     */
    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);
    }
    /**
     * 获取最近的价格数据
     * @param prices 所有价格数据
     * @param period 周期
     * @return 最近period个价格数据
     */
    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());
    }
}
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/KDJ.java
New file
@@ -0,0 +1,120 @@
package com.xcong.excoin.modules.okxNewPrice.indicator;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
/**
 * KDJ (Stochastic Oscillator) 指标实现
 * 计算逻辑:
 * 1. RSV = (收盘价 - N日内最低价) / (N日内最高价 - N日内最低价) * 100
 * 2. K = 2/3 * 前一日K值 + 1/3 * 当日RSV
 * 3. D = 2/3 * 前一日D值 + 1/3 * 当日K值
 * 4. J = 3*K - 2*D
 */
@Slf4j
@Getter
@Setter
public class KDJ extends IndicatorBase {
    private static final int DEFAULT_PERIOD = 9;
    private static final int K_PERIOD = 3;
    private static final int D_PERIOD = 3;
    private int period = DEFAULT_PERIOD;
    private BigDecimal k = new BigDecimal(50);
    private BigDecimal d = new BigDecimal(50);
    private BigDecimal j = new BigDecimal(50);
    private BigDecimal prevK = new BigDecimal(50);
    private BigDecimal prevD = new BigDecimal(50);
    public KDJ() {}
    public KDJ(int period) {
        this.period = period;
    }
    /**
     * 计算KDJ指标
     * @param prices 价格列表
     */
    public void calculate(List<BigDecimal> prices) {
        if (prices == null || prices.size() < period) {
            return;
        }
        // 获取最近N天的价格
        List<BigDecimal> recentPrices = getRecentPrices(prices, period);
        // 计算最高价和最低价
        BigDecimal high = recentPrices.stream().max(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
        BigDecimal low = recentPrices.stream().min(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
        BigDecimal close = recentPrices.get(recentPrices.size() - 1);
        // 计算RSV
        BigDecimal rsv;
        if (high.compareTo(low) == 0) {
            rsv = new BigDecimal(50);
        } else {
            rsv = close.subtract(low)
                    .divide(high.subtract(low), 8, RoundingMode.HALF_UP)
                    .multiply(new BigDecimal(100))
                    .setScale(8, RoundingMode.HALF_UP);
        }
        // 计算K值
        prevK = k;
        k = new BigDecimal(2).multiply(prevK)
                .add(rsv)
                .divide(new BigDecimal(3), 8, RoundingMode.HALF_UP);
        // 计算D值
        prevD = d;
        d = new BigDecimal(2).multiply(prevD)
                .add(k)
                .divide(new BigDecimal(3), 8, RoundingMode.HALF_UP);
        // 计算J值
        j = k.multiply(new BigDecimal(3))
                .subtract(d.multiply(new BigDecimal(2)))
                .setScale(8, RoundingMode.HALF_UP);
        log.debug("KDJ计算结果 - K: {}, D: {}, J: {}", k, d, j);
    }
    /**
     * 判断超买(K > 80)
     * @return 是否超买
     */
    public boolean isOverbought() {
        return k.compareTo(new BigDecimal(80)) > 0;
    }
    /**
     * 判断超卖(K < 20)
     * @return 是否超卖
     */
    public boolean isOversold() {
        return k.compareTo(new BigDecimal(20)) < 0;
    }
    /**
     * 判断金叉信号(K线上穿D线)
     * @return 是否形成金叉
     */
    public boolean isGoldenCross() {
        return prevK.compareTo(prevD) < 0 && k.compareTo(d) > 0;
    }
    /**
     * 判断死叉信号(K线下穿D线)
     * @return 是否形成死叉
     */
    public boolean isDeathCross() {
        return prevK.compareTo(prevD) > 0 && k.compareTo(d) < 0;
    }
}
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MA.java
New file
@@ -0,0 +1,151 @@
package com.xcong.excoin.modules.okxNewPrice.indicator;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.util.List;
/**
 * MA (Moving Average) 指标实现
 * 支持不同周期的简单移动平均线(SMA)和指数移动平均线(EMA)
 */
@Slf4j
@Getter
@Setter
public class MA extends IndicatorBase {
    // 常用周期
    public static final int MA5 = 5;
    public static final int MA10 = 10;
    public static final int MA20 = 20;
    public static final int MA30 = 30;
    public static final int MA60 = 60;
    private BigDecimal ma5 = BigDecimal.ZERO;
    private BigDecimal ma10 = BigDecimal.ZERO;
    private BigDecimal ma20 = BigDecimal.ZERO;
    private BigDecimal ma30 = BigDecimal.ZERO;
    private BigDecimal ma60 = BigDecimal.ZERO;
    private BigDecimal ema5 = BigDecimal.ZERO;
    private BigDecimal ema10 = BigDecimal.ZERO;
    private BigDecimal ema20 = BigDecimal.ZERO;
    private BigDecimal ema30 = BigDecimal.ZERO;
    private BigDecimal ema60 = BigDecimal.ZERO;
    private BigDecimal prevEma5 = null;
    private BigDecimal prevEma10 = null;
    private BigDecimal prevEma20 = null;
    private BigDecimal prevEma30 = null;
    private BigDecimal prevEma60 = null;
    /**
     * 计算所有周期的MA指标
     * @param prices 价格列表
     */
    public void calculate(List<BigDecimal> prices) {
        if (prices == null || prices.size() < 1) {
            return;
        }
        // 计算SMA
        if (prices.size() >= MA5) {
            ma5 = calculateMA(prices, MA5);
        }
        if (prices.size() >= MA10) {
            ma10 = calculateMA(prices, MA10);
        }
        if (prices.size() >= MA20) {
            ma20 = calculateMA(prices, MA20);
        }
        if (prices.size() >= MA30) {
            ma30 = calculateMA(prices, MA30);
        }
        if (prices.size() >= MA60) {
            ma60 = calculateMA(prices, MA60);
        }
        // 计算EMA
        prevEma5 = calculateEMA(prices, MA5, prevEma5);
        ema5 = prevEma5;
        prevEma10 = calculateEMA(prices, MA10, prevEma10);
        ema10 = prevEma10;
        prevEma20 = calculateEMA(prices, MA20, prevEma20);
        ema20 = prevEma20;
        prevEma30 = calculateEMA(prices, MA30, prevEma30);
        ema30 = prevEma30;
        prevEma60 = calculateEMA(prices, MA60, prevEma60);
        ema60 = prevEma60;
        log.debug("MA计算结果 - MA5: {}, MA10: {}, MA20: {}, MA30: {}, MA60: {}",
                ma5, ma10, ma20, ma30, ma60);
        log.debug("EMA计算结果 - EMA5: {}, EMA10: {}, EMA20: {}, EMA30: {}, EMA60: {}",
                ema5, ema10, ema20, ema30, ema60);
    }
    /**
     * 判断短期均线是否上穿长期均线(金叉)
     * @param shortMA 短期均线
     * @param longMA 长期均线
     * @param prevShortMA 前一期短期均线
     * @param prevLongMA 前一期长期均线
     * @return 是否形成金叉
     */
    public boolean isGoldenCross(BigDecimal shortMA, BigDecimal longMA,
                                BigDecimal prevShortMA, BigDecimal prevLongMA) {
        return prevShortMA != null && prevLongMA != null &&
                prevShortMA.compareTo(prevLongMA) < 0 &&
                shortMA.compareTo(longMA) > 0;
    }
    /**
     * 判断短期均线是否下穿长期均线(死叉)
     * @param shortMA 短期均线
     * @param longMA 长期均线
     * @param prevShortMA 前一期短期均线
     * @param prevLongMA 前一期长期均线
     * @return 是否形成死叉
     */
    public boolean isDeathCross(BigDecimal shortMA, BigDecimal longMA,
                               BigDecimal prevShortMA, BigDecimal prevLongMA) {
        return prevShortMA != null && prevLongMA != null &&
                prevShortMA.compareTo(prevLongMA) > 0 &&
                shortMA.compareTo(longMA) < 0;
    }
    /**
     * 判断价格是否上穿均线
     * @param price 当前价格
     * @param ma 均线值
     * @param prevPrice 前一期价格
     * @param prevMA 前一期均线值
     * @return 是否上穿
     */
    public boolean isPriceCrossUp(BigDecimal price, BigDecimal ma,
                                 BigDecimal prevPrice, BigDecimal prevMA) {
        return prevPrice != null && prevMA != null &&
                prevPrice.compareTo(prevMA) < 0 &&
                price.compareTo(ma) > 0;
    }
    /**
     * 判断价格是否下穿均线
     * @param price 当前价格
     * @param ma 均线值
     * @param prevPrice 前一期价格
     * @param prevMA 前一期均线值
     * @return 是否下穿
     */
    public boolean isPriceCrossDown(BigDecimal price, BigDecimal ma,
                                   BigDecimal prevPrice, BigDecimal prevMA) {
        return prevPrice != null && prevMA != null &&
                prevPrice.compareTo(prevMA) > 0 &&
                price.compareTo(ma) < 0;
    }
}
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MACD.java
New file
@@ -0,0 +1,80 @@
package com.xcong.excoin.modules.okxNewPrice.indicator;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
/**
 * MACD (Moving Average Convergence Divergence) 指标实现
 * 计算逻辑:
 * 1. DIF = EMA(12) - EMA(26)
 * 2. DEA = EMA(DIF, 9)
 * 3. MACD柱状图 = (DIF - DEA) * 2
 */
@Slf4j
@Getter
@Setter
public class MACD extends IndicatorBase {
    private static final int FAST_PERIOD = 12;
    private static final int SLOW_PERIOD = 26;
    private static final int SIGNAL_PERIOD = 9;
    private BigDecimal dif = BigDecimal.ZERO;
    private BigDecimal dea = BigDecimal.ZERO;
    private BigDecimal macdBar = BigDecimal.ZERO;
    private BigDecimal prevFastEMA = null;
    private BigDecimal prevSlowEMA = null;
    private BigDecimal prevDea = null;
    /**
     * 计算MACD指标
     * @param prices 价格列表
     */
    public void calculate(List<BigDecimal> prices) {
        if (prices == null || prices.size() < 2) {
            return;
        }
        // 计算快速EMA
        prevFastEMA = calculateEMA(prices, FAST_PERIOD, prevFastEMA);
        // 计算慢速EMA
        prevSlowEMA = calculateEMA(prices, SLOW_PERIOD, prevSlowEMA);
        // 计算DIF
        dif = prevFastEMA.subtract(prevSlowEMA).setScale(8, RoundingMode.HALF_UP);
        // 计算DEA
        List<BigDecimal> difList = new ArrayList<>();
        difList.add(dif);
        prevDea = calculateEMA(difList, SIGNAL_PERIOD, prevDea);
        dea = prevDea.setScale(8, RoundingMode.HALF_UP);
        // 计算MACD柱状图
        macdBar = dif.subtract(dea).multiply(new BigDecimal(2)).setScale(8, RoundingMode.HALF_UP);
        log.debug("MACD计算结果 - DIF: {}, DEA: {}, MACD柱状图: {}", dif, dea, macdBar);
    }
    /**
     * 判断金叉信号(DIF上穿DEA)
     * @return 是否形成金叉
     */
    public boolean isGoldenCross(BigDecimal previousDIF, BigDecimal previousDEA) {
        return previousDIF.compareTo(previousDEA) < 0 && dif.compareTo(dea) > 0;
    }
    /**
     * 判断死叉信号(DIF下穿DEA)
     * @return 是否形成死叉
     */
    public boolean isDeathCross(BigDecimal previousDIF, BigDecimal previousDEA) {
        return previousDIF.compareTo(previousDEA) > 0 && dif.compareTo(dea) < 0;
    }
}
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/RSI.java
New file
@@ -0,0 +1,119 @@
package com.xcong.excoin.modules.okxNewPrice.indicator;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
/**
 * RSI (Relative Strength Index) 指标实现
 * 计算逻辑:
 * 1. 计算N天内的上涨幅度和下跌幅度
 * 2. 计算平均上涨幅度和平均下跌幅度
 * 3. RSI = 100 - (100 / (1 + (平均上涨幅度 / 平均下跌幅度)))
 */
@Slf4j
@Getter
@Setter
public class RSI extends IndicatorBase {
    private static final int DEFAULT_PERIOD = 14;
    private int period = DEFAULT_PERIOD;
    private BigDecimal rsi = BigDecimal.ZERO;
    private BigDecimal prevAvgGain = BigDecimal.ZERO;
    private BigDecimal prevAvgLoss = BigDecimal.ZERO;
    public RSI() {}
    public RSI(int period) {
        this.period = period;
    }
    /**
     * 计算RSI指标
     * @param prices 价格列表
     */
    public void calculate(List<BigDecimal> prices) {
        if (prices == null || prices.size() < period + 1) {
            return;
        }
        if (prevAvgGain.compareTo(BigDecimal.ZERO) == 0 && prevAvgLoss.compareTo(BigDecimal.ZERO) == 0) {
            // 首次计算
            BigDecimal totalGain = BigDecimal.ZERO;
            BigDecimal totalLoss = BigDecimal.ZERO;
            for (int i = prices.size() - period; i < prices.size(); i++) {
                BigDecimal change = prices.get(i).subtract(prices.get(i - 1));
                if (change.compareTo(BigDecimal.ZERO) > 0) {
                    totalGain = totalGain.add(change);
                } else {
                    totalLoss = totalLoss.add(change.abs());
                }
            }
            prevAvgGain = totalGain.divide(new BigDecimal(period), 8, RoundingMode.HALF_UP);
            prevAvgLoss = totalLoss.divide(new BigDecimal(period), 8, RoundingMode.HALF_UP);
        } else {
            // 后续计算
            BigDecimal change = prices.get(prices.size() - 1).subtract(prices.get(prices.size() - 2));
            BigDecimal gain = change.compareTo(BigDecimal.ZERO) > 0 ? change : BigDecimal.ZERO;
            BigDecimal loss = change.compareTo(BigDecimal.ZERO) < 0 ? change.abs() : BigDecimal.ZERO;
            prevAvgGain = prevAvgGain.multiply(new BigDecimal(period - 1))
                    .add(gain)
                    .divide(new BigDecimal(period), 8, RoundingMode.HALF_UP);
            prevAvgLoss = prevAvgLoss.multiply(new BigDecimal(period - 1))
                    .add(loss)
                    .divide(new BigDecimal(period), 8, RoundingMode.HALF_UP);
        }
        // 计算RSI
        if (prevAvgLoss.compareTo(BigDecimal.ZERO) == 0) {
            rsi = new BigDecimal(100);
        } else {
            BigDecimal rs = prevAvgGain.divide(prevAvgLoss, 8, RoundingMode.HALF_UP);
            rsi = new BigDecimal(100)
                    .subtract(new BigDecimal(100).divide(BigDecimal.ONE.add(rs), 8, RoundingMode.HALF_UP))
                    .setScale(8, RoundingMode.HALF_UP);
        }
        log.debug("RSI计算结果 - RSI({}): {}", period, rsi);
    }
    /**
     * 判断超买(RSI > 70)
     * @return 是否超买
     */
    public boolean isOverbought() {
        return rsi.compareTo(new BigDecimal(70)) > 0;
    }
    /**
     * 判断超卖(RSI < 30)
     * @return 是否超卖
     */
    public boolean isOversold() {
        return rsi.compareTo(new BigDecimal(30)) < 0;
    }
    /**
     * 判断超买(RSI > 80)
     * @return 是否严重超买
     */
    public boolean isExtremelyOverbought() {
        return rsi.compareTo(new BigDecimal(80)) > 0;
    }
    /**
     * 判断超卖(RSI < 20)
     * @return 是否严重超卖
     */
    public boolean isExtremelyOversold() {
        return rsi.compareTo(new BigDecimal(20)) < 0;
    }
}
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/AbstractTechnicalIndicatorStrategy.java
New file
@@ -0,0 +1,89 @@
package com.xcong.excoin.modules.okxNewPrice.indicator.strategy;
import com.xcong.excoin.modules.okxNewPrice.okxWs.param.TradeRequestParam;
import com.xcong.excoin.modules.okxNewPrice.utils.WsMapBuild;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
/**
 * 技术指标策略抽象基类,提供通用功能
 */
@Slf4j
@Getter
@Setter
public abstract class AbstractTechnicalIndicatorStrategy implements TechnicalIndicatorStrategy {
    protected static final int MAX_PRICE_HISTORY = 100; // 最大价格历史记录数量
    protected List<BigDecimal> priceHistory = new ArrayList<>();
    protected boolean initialized = false;
    protected String strategyName = ""; // 策略名称
    @Override
    public void initialize() {
        priceHistory.clear();
        initialized = true;
        log.info("策略初始化完成: {}", strategyName);
    }
    @Override
    public void updatePrices(List<BigDecimal> prices) {
        if (!initialized) {
            initialize();
        }
        if (prices == null || prices.isEmpty()) {
            return;
        }
        // 更新价格历史记录
        priceHistory.addAll(prices);
        // 限制价格历史记录数量
        if (priceHistory.size() > MAX_PRICE_HISTORY) {
            priceHistory = priceHistory.subList(priceHistory.size() - MAX_PRICE_HISTORY, priceHistory.size());
        }
        log.debug("价格历史记录更新完成,当前数量: {}", priceHistory.size());
    }
    @Override
    public boolean isValid() {
        return initialized;
    }
    /**
     * 创建交易请求参数
     * @param accountName 账户名称
     * @param markPx 当前标记价格
     * @param posSide 仓位方向
     * @param signal 交易信号
     * @return 交易请求参数
     */
    protected TradeRequestParam createTradeRequestParam(String accountName, String markPx, String posSide, TradeSignal signal) {
        TradeRequestParam param = new TradeRequestParam();
        param.setAccountName(accountName);
        param.setMarkPx(markPx);
        param.setPosSide(posSide);
        log.info("账户: {}, 价格: {}, 仓位方向: {}, 信号: {}",
                accountName, markPx, posSide, signal.getName());
        return param;
    }
    /**
     * 日志记录交易信号
     * @param accountName 账户名称
     * @param markPx 当前标记价格
     * @param signal 交易信号
     */
    protected void logSignal(String accountName, String markPx, TradeSignal signal) {
        log.info("策略: {}, 账户: {}, 价格: {}, 信号: {}",
                strategyName, accountName, markPx, signal.getName());
    }
}
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/ComprehensiveTechnicalStrategy.java
New file
@@ -0,0 +1,351 @@
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";
        }
    }
}
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/CoreTechnicalStrategy.java
New file
@@ -0,0 +1,589 @@
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();
    }
}
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/CoreTechnicalStrategyUsageGuide.md
New file
@@ -0,0 +1,198 @@
# CoreTechnicalStrategy 使用指南
## 1. 概述
CoreTechnicalStrategy 是一个综合技术指标策略类,整合了以下核心技术指标:
- 三重EMA交叉系统(9/21/55周期)
- 波动率自适应布林带
- MACD能量柱分级策略
## 2. 类结构
### 2.1 AdvancedMA 类
用于计算三重EMA交叉系统的指标。
**主要方法:**
- `calculateTripleEMA(List<BigDecimal> prices)`: 计算9EMA、21EMA、55EMA
- `isBullish排列()`: 判断是否多头排列(9EMA>21EMA>55EMA)
- `isBearish排列()`: 判断是否空头排列(9EMA<21EMA<55EMA)
- `calculate粘合度()`: 计算三线粘合度
- `is震荡行情()`: 判断是否处于震荡行情(粘合度<2%)
### 2.2 CoreTechnicalStrategy 类
综合所有技术指标生成交易信号的策略类。
**主要方法:**
- `initialize()`: 初始化策略
- `updatePrices(List<BigDecimal> prices)`: 更新价格数据
- `getSignal(String accountName, String markPx, String posSide)`: 获取交易信号
## 3. 使用步骤
### 3.1 初始化策略
```java
// 创建策略实例
CoreTechnicalStrategy strategy = new CoreTechnicalStrategy();
// 初始化策略
strategy.initialize();
```
### 3.2 更新价格数据
```java
// 准备价格数据列表
List<BigDecimal> prices = new ArrayList<>();
prices.add(new BigDecimal(30000));
prices.add(new BigDecimal(30500));
prices.add(new BigDecimal(31000));
// 添加更多价格数据...
// 更新价格数据
strategy.updatePrices(prices);
```
### 3.3 获取交易信号
```java
// 调用getSignal方法获取交易信号
TradeRequestParam param = strategy.getSignal(
    "account1",           // 账户名称
    "31500",              // 当前标记价格
    "long"                // 当前仓位方向("long"或"short")
);
// 获取交易方向
String side = param.getSide(); // "buy"或"sell"
String tradeType = param.getTradeType(); // "yes"或"no"
```
## 4. 参数说明
### 4.1 getSignal 方法参数
| 参数名 | 类型 | 说明 | 示例 |
|-------|------|------|------|
| accountName | String | 账户名称,用于日志记录和配置获取 | "account1", "myAccount" |
| markPx | String | 当前标记价格,作为交易决策的基础价格 | "31500", "32000.5" |
| posSide | String | 当前仓位方向,决定多空信号的生成逻辑 | "long"(多头), "short"(空头) |
### 4.2 返回值 TradeRequestParam 说明
| 属性名 | 类型 | 说明 |
|-------|------|------|
| accountName | String | 账户名称 |
| markPx | String | 当前标记价格 |
| posSide | String | 仓位方向 |
| side | String | 交易方向:"buy"(买入/做多)或"sell"(卖出/做空) |
| tradeType | String | 交易类型:"yes"(执行交易)或"no"(不执行交易) |
| instId | String | 交易对ID |
| tdMode | String | 交易模式 |
| ordType | String | 订单类型 |
## 5. 核心指标逻辑
### 5.1 三重EMA交叉系统
- **多头条件**:9EMA > 21EMA > 55EMA
- **空头条件**:9EMA < 21EMA < 55EMA
- **震荡过滤**:三线粘合度 < 2% 时暂停交易
### 5.2 波动率自适应布林带
- **动态通道宽度**:标准差倍数根据ATR调整
- **做空信号**:突破上轨 + 成交量放大3倍
- **做多信号**:触及下轨 + 期货资金费率转正
### 5.3 MACD能量柱分级策略
- **能量柱面积**:累计过去5根柱体积分
- **多头入场**:当前柱体 > 0 且面积增速 > 前周期20%
- **空头反转**:柱体顶背离 + RSI > 70区域死叉
## 6. 示例代码
```java
public class StrategyExample {
    public static void main(String[] args) {
        // 初始化策略
        CoreTechnicalStrategy strategy = new CoreTechnicalStrategy();
        strategy.initialize();
        // 准备历史价格数据
        List<BigDecimal> historicalPrices = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            // 模拟价格数据
            BigDecimal price = new BigDecimal(30000 + i * 100 + Math.random() * 500);
            historicalPrices.add(price);
        }
        // 更新价格数据
        strategy.updatePrices(historicalPrices);
        // 获取当前价格
        String currentPrice = "35000";
        // 获取多头信号
        TradeRequestParam longSignal = strategy.getSignal("myAccount", currentPrice, "long");
        System.out.println("多头信号: " + longSignal.getSide() + ", 交易类型: " + longSignal.getTradeType());
        // 获取空头信号
        TradeRequestParam shortSignal = strategy.getSignal("myAccount", currentPrice, "short");
        System.out.println("空头信号: " + shortSignal.getSide() + ", 交易类型: " + shortSignal.getTradeType());
    }
}
```
## 7. 注意事项
1. **价格数据长度**:建议至少提供55个以上的价格数据点,以确保所有指标都能正确计算
2. **初始化顺序**:必须先调用`initialize()`方法,再调用`updatePrices()`和`getSignal()`
3. **仓位方向**:传入的`posSide`参数会影响信号生成逻辑,请确保传入正确的当前仓位方向
4. **日志记录**:策略会输出详细的日志信息,可通过日志级别控制输出内容
5. **震荡过滤**:当处于震荡行情时,`tradeType`会返回"no",建议暂停交易
## 8. 错误处理
策略内部包含完善的错误处理机制:
- 价格数据为空或无效时,会返回`NO_SIGNAL`
- 计算过程中发生异常时,会捕获并记录日志,返回`NO_SIGNAL`
- 冷静期内会返回`tradeType="no"`
- 震荡行情会返回`tradeType="no"`
## 9. 扩展与定制
### 9.1 修改指标参数
可以在`CoreTechnicalStrategy`类中修改以下常量来调整策略参数:
```java
// MACD能量柱计算周期
private static final int MACD_HISTOGRAM_PERIOD = 5;
// 成交量放大倍数
private static final BigDecimal VOLUME_MULTIPLIER = new BigDecimal(3);
// 三线粘合度阈值(%)
private static final BigDecimal GLUE_THRESHOLD = new BigDecimal(2);
```
### 9.2 自定义信号逻辑
可以重写`analyzeCoreSignal`方法来自定义信号生成逻辑:
```java
@Override
protected TradeSignal analyzeCoreSignal(BigDecimal currentPrice) {
    // 自定义信号逻辑
    // ...
}
```
## 10. 整合到现有系统
要将CoreTechnicalStrategy整合到现有系统中,只需将其替换或补充现有的策略类即可:
```java
// 在OkxWebSocketClientManager或其他适当位置
CoreTechnicalStrategy coreStrategy = new CoreTechnicalStrategy();
coreStrategy.initialize();
// 在价格更新时
coreStrategy.updatePrices(priceList);
// 在需要交易信号时
TradeRequestParam signal = coreStrategy.getSignal(accountName, markPx, posSide);
```
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/TechnicalIndicatorStrategy.java
New file
@@ -0,0 +1,44 @@
package com.xcong.excoin.modules.okxNewPrice.indicator.strategy;
import com.xcong.excoin.modules.okxNewPrice.okxWs.param.TradeRequestParam;
import java.math.BigDecimal;
import java.util.List;
/**
 * 技术指标策略接口,定义技术指标策略的基本方法
 */
public interface TechnicalIndicatorStrategy {
    /**
     * 初始化策略
     */
    void initialize();
    /**
     * 更新价格数据
     * @param prices 价格列表
     */
    void updatePrices(List<BigDecimal> prices);
    /**
     * 获取交易信号
     * @param accountName 账户名称
     * @param markPx 当前标记价格
     * @param posSide 仓位方向
     * @return 交易请求参数,包含交易信号和相关信息
     */
    TradeRequestParam getSignal(String accountName, String markPx, String posSide);
    /**
     * 获取策略名称
     * @return 策略名称
     */
    String getStrategyName();
    /**
     * 判断策略是否有效
     * @return 是否有效
     */
    boolean isValid();
}
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/TradeSignal.java
New file
@@ -0,0 +1,54 @@
package com.xcong.excoin.modules.okxNewPrice.indicator.strategy;
import lombok.Getter;
/**
 * 交易信号枚举
 */
@Getter
public enum TradeSignal {
    /** 无信号,保持观望 */
    NO_SIGNAL("NO_SIGNAL", "无信号"),
    /** 买入信号 */
    BUY("BUY", "买入"),
    /** 卖出信号 */
    SELL("SELL", "卖出"),
    /** 开多信号 */
    OPEN_LONG("OPEN_LONG", "开多"),
    /** 平多信号 */
    CLOSE_LONG("CLOSE_LONG", "平多"),
    /** 开空信号 */
    OPEN_SHORT("OPEN_SHORT", "开空"),
    /** 平空信号 */
    CLOSE_SHORT("CLOSE_SHORT", "平空"),
    /** 止损信号 */
    STOP_LOSS("STOP_LOSS", "止损"),
    /** 止盈信号 */
    TAKE_PROFIT("TAKE_PROFIT", "止盈");
    private final String value;
    private final String name;
    TradeSignal(String value, String name) {
        this.value = value;
        this.name = name;
    }
    public static TradeSignal fromValue(String value) {
        for (TradeSignal signal : values()) {
            if (signal.getValue().equals(value)) {
                return signal;
            }
        }
        return NO_SIGNAL;
    }
}