From 7e5aeb4270fa91096410e66542273def7beaea5d Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Mon, 22 Dec 2025 11:55:52 +0800
Subject: [PATCH] feat(trading): 实现综合技术指标交易策略
---
indicator_package_documentation.md | 288 ++++++
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/TradeSignal.java | 54 +
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/TechnicalIndicatorStrategy.java | 44 +
CoreTechnicalStrategy_Usage_Guide.md | 150 +++
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MA.java | 151 +++
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/BOLL.java | 143 +++
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/RSI.java | 119 ++
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/KDJ.java | 120 ++
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/ComprehensiveTechnicalStrategy.java | 351 ++++++++
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MACD.java | 80 +
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/AbstractTechnicalIndicatorStrategy.java | 89 ++
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/CoreTechnicalStrategy.java | 589 +++++++++++++
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/AdvancedMA.java | 104 ++
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/CoreTechnicalStrategyUsageGuide.md | 198 ++++
src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/IndicatorBase.java | 94 ++
15 files changed, 2,574 insertions(+), 0 deletions(-)
diff --git a/CoreTechnicalStrategy_Usage_Guide.md b/CoreTechnicalStrategy_Usage_Guide.md
new file mode 100644
index 0000000..ace716c
--- /dev/null
+++ b/CoreTechnicalStrategy_Usage_Guide.md
@@ -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 库(用于数学计算)
\ No newline at end of file
diff --git a/indicator_package_documentation.md b/indicator_package_documentation.md
new file mode 100644
index 0000000..280a23a
--- /dev/null
+++ b/indicator_package_documentation.md
@@ -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. 总结
+
+该包提供了一套完整的技术指标分析系统,支持从基础指标计算到高级策略分析的全流程。通过整合多种技术指标,实现了复杂的交易信号生成逻辑,可用于加密货币交易中的技术分析和策略决策。系统设计遵循面向对象原则,具有良好的可扩展性和可维护性。
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/AdvancedMA.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/AdvancedMA.java
new file mode 100644
index 0000000..a7e3aad
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/AdvancedMA.java
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/BOLL.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/BOLL.java
new file mode 100644
index 0000000..8f00e0c
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/BOLL.java
@@ -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;
+ }
+ }
+}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/IndicatorBase.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/IndicatorBase.java
new file mode 100644
index 0000000..6872bfd
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/IndicatorBase.java
@@ -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());
+ }
+}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/KDJ.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/KDJ.java
new file mode 100644
index 0000000..6651871
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/KDJ.java
@@ -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;
+ }
+}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MA.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MA.java
new file mode 100644
index 0000000..25cba55
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MA.java
@@ -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;
+ }
+}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MACD.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MACD.java
new file mode 100644
index 0000000..92c91b9
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MACD.java
@@ -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;
+ }
+}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/RSI.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/RSI.java
new file mode 100644
index 0000000..b289d1c
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/RSI.java
@@ -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;
+ }
+}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/AbstractTechnicalIndicatorStrategy.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/AbstractTechnicalIndicatorStrategy.java
new file mode 100644
index 0000000..a853473
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/AbstractTechnicalIndicatorStrategy.java
@@ -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());
+ }
+}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/ComprehensiveTechnicalStrategy.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/ComprehensiveTechnicalStrategy.java
new file mode 100644
index 0000000..a6c320c
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/ComprehensiveTechnicalStrategy.java
@@ -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";
+ }
+ }
+}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/CoreTechnicalStrategy.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/CoreTechnicalStrategy.java
new file mode 100644
index 0000000..61469c5
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/CoreTechnicalStrategy.java
@@ -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();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/CoreTechnicalStrategyUsageGuide.md b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/CoreTechnicalStrategyUsageGuide.md
new file mode 100644
index 0000000..0e56433
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/CoreTechnicalStrategyUsageGuide.md
@@ -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);
+```
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/TechnicalIndicatorStrategy.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/TechnicalIndicatorStrategy.java
new file mode 100644
index 0000000..65b4d95
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/TechnicalIndicatorStrategy.java
@@ -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();
+}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/TradeSignal.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/TradeSignal.java
new file mode 100644
index 0000000..e1c05da
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/TradeSignal.java
@@ -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;
+ }
+}
--
Gitblit v1.9.1