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; } }