From 6cfe51f29210e1bf5223059402aa15a54278409e Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Wed, 13 May 2026 16:36:50 +0800
Subject: [PATCH] refactor(okxNewPrice): 移除技术指标策略相关类和账户配置类
---
/dev/null | 38 --
src/main/java/com/xcong/excoin/modules/okxApi/okxApi-logic.md | 935 +++++++++++++++---------------------------------------
src/main/java/com/xcong/excoin/modules/okxApi/OkxWebSocketClientManager.java | 6
3 files changed, 265 insertions(+), 714 deletions(-)
diff --git a/src/main/java/com/xcong/excoin/modules/okxApi/OkxWebSocketClientMain.java b/src/main/java/com/xcong/excoin/modules/okxApi/OkxWebSocketClientMain.java
deleted file mode 100644
index ab4732e..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxApi/OkxWebSocketClientMain.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.xcong.excoin.modules.okxApi;
-
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.support.ClassPathXmlApplicationContext;
-
-/**
- * OKX 模块独立测试入口。
- *
- * <p>通过 Spring XML 上下文(applicationContext.xml)初始化管理器,
- * 运行极长时间后手动关闭。用于脱离 Web 容器独立调试策略。
- *
- * @author Administrator
- */
-public class OkxWebSocketClientMain {
- public static void main(String[] args) throws InterruptedException {
- ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- OkxWebSocketClientManager manager = context.getBean(OkxWebSocketClientManager.class);
-
- Thread.sleep(1200000000L);
-
- manager.destroy();
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxApi/OkxWebSocketClientManager.java b/src/main/java/com/xcong/excoin/modules/okxApi/OkxWebSocketClientManager.java
index 94f4fe1..75f199a 100644
--- a/src/main/java/com/xcong/excoin/modules/okxApi/OkxWebSocketClientManager.java
+++ b/src/main/java/com/xcong/excoin/modules/okxApi/OkxWebSocketClientManager.java
@@ -34,10 +34,10 @@
.marginMode("cross")
.posMode("long_short_mode")
.gridRate(new BigDecimal("0.0015"))
- .overallTp(new BigDecimal("5"))
+ .overallTp(new BigDecimal("15"))
.maxLoss(new BigDecimal("20"))
- .quantity("0.1")
- .contractMultiplier(new BigDecimal("1"))
+ .quantity("1")
+ .contractMultiplier(new BigDecimal("0.1"))
.unrealizedPnlPriceMode(OkxConfig.PnLPriceMode.LAST_PRICE)
.isProduction(false)
.build();
diff --git a/src/main/java/com/xcong/excoin/modules/okxApi/okxApi-logic.md b/src/main/java/com/xcong/excoin/modules/okxApi/okxApi-logic.md
index 9e67176..2a0a49c 100644
--- a/src/main/java/com/xcong/excoin/modules/okxApi/okxApi-logic.md
+++ b/src/main/java/com/xcong/excoin/modules/okxApi/okxApi-logic.md
@@ -1,724 +1,313 @@
-# OKX API 网格交易策略 — 逻辑文档
+# OKX 永续合约双边网格策略 — 逻辑文档
+
+> 最后更新: 2026-05-13
---
-## 目录
+## 一、模块架构
-1. [整体架构](#1-整体架构)
-2. [配置层:OkxConfig](#2-配置层okxconfig)
-3. [基础设施层](#3-基础设施层)
- - [3.1 OkxEnums — 常量定义](#31-okxenums--常量定义)
- - [3.2 OkxWsUtil — WebSocket 工具类](#32-okxwsutil--websocket-工具类)
- - [3.3 TradeRequestParam — 下单参数](#33-traderequestparam--下单参数)
-4. [WebSocket 通信层](#4-websocket-通信层)
- - [4.1 OkxWebSocketClientManager — 入口管理器](#41-okxwebsocketclientmanager--入口管理器)
- - [4.2 OkxKlineWebSocketClient — WS 连接客户端](#42-okxklinewebsocketclient--ws-连接客户端)
-5. [频道处理器层](#5-频道处理器层)
- - [5.1 OkxChannelHandler — 处理器接口](#51-okxchannelhandler--处理器接口)
- - [5.2 OkxCandlestickChannelHandler — K线频道](#52-okxcandlestickchannelhandler--k线频道)
- - [5.3 OkxPositionsChannelHandler — 持仓频道](#53-okxpositionschannelhandler--持仓频道)
- - [5.4 OkxAccountChannelHandler — 账户频道](#54-okxaccountchannelhandler--账户频道)
- - [5.5 OkxOrderInfoChannelHandler — 订单成交频道](#55-okxorderinfochannelhandler--订单成交频道)
-6. [策略执行层](#6-策略执行层)
- - [6.1 OkxTradeExecutor — 异步下单执行器](#61-okxtradeexecutor--异步下单执行器)
- - [6.2 OkxGridTradeService — 网格策略核心](#62-okxgridtradeservice--网格策略核心)
-7. [独立启动类:OkxWebSocketClientMain](#7-独立启动类okxwebsocketclientmain)
-8. [完整调用链](#8-完整调用链)
-9. [与 Gate API 的差异对比](#9-与-gate-api-的差异对比)
+```
+OkxWebSocketClientMain ← 入口,持有主运行线程
+ └── OkxWebSocketClientManager ← 管理 WS 连接(公共 + 私有)
+ ├── OkxKlineWebSocketClient (公共) ← K线推送
+ └── OkxKlineWebSocketClient (私有) ← 私有频道(login → 订阅)
+ ├── OkxCandlestickChannelHandler → onKline()
+ ├── OkxOrderInfoChannelHandler → onOrderFilled()
+ ├── OkxPositionsChannelHandler → onPositionUpdate()
+ └── OkxAccountChannelHandler → 仅日志,供后续扩展
+ └── OkxGridTradeService ← 核心策略引擎(状态机)
+ ├── OkxConfig ← 策略配置(Builder 模式)
+ └── OkxTradeExecutor ← REST 下单执行器
+```
+
+### WS 连接层职责
+
+| 类 | 职责 |
+|----|------|
+| `OkxKlineWebSocketClient` | TCP 连接管理、心跳(10s ping)、指数退避重连(最多3次)、消息路由(login/subscribe/pong → handler.handleMessage) |
+| `OkxWebSocketClientManager` | 双重连接(公共/私有)的初始化、销毁、共享线程池 |
+| `OkxWsUtil` | SSL 配置、HMAC-SHA256 签名、订单 ID 生成、ISO8601 时间戳 |
---
-## 1. 整体架构
+## 二、核心配置 `OkxConfig`
-```
-┌──────────────────────────────────────────────────────────────┐
-│ OkxWebSocketClientManager (Spring @Component) │
-│ · 读取配置 · 组装所有组件 · 启动 WS 连接 · 生命周期管理 │
-└──────────────────────┬───────────────────────────────────────┘
- │
- ┌─────────────┼─────────────┐
- ▼ ▼ ▼
- OkxConfig OkxGridTradeService OkxKlineWebSocketClient
- (Builder配置) (策略核心) (WS连接客户端)
- │ │
- │ ┌────────┴────────┐
- │ │ 4 个频道处理器 │
- │ ├─────────────────┤
- │ │ K线 | 持仓 │
- │ │ 账户 | 订单成交 │
- │ └────────┬────────┘
- │ │
- ▼ │
- OkxTradeExecutor │
- (异步下单线程池) ◄────────────────┘
- │
- ▼
- 通过 WS 发送下单 JSON
-```
-
-**核心数据流**:
-
-```
-K线推送 → OkxGridTradeService.onKline() → 匹配网格队列 → OkxTradeExecutor 异步下单
-持仓推送 → OkxGridTradeService.onPositionUpdate() → 识别基底成交 → 设置止盈单 → 队列就绪 → 激活策略
-订单推送 → OkxGridTradeService.onOrderFilled() → 累计盈亏跟踪 → 达标/超限自动停止
-```
-
-**设计原则**:
-- **包自包含**:`okxApi` 包不依赖任何其他业务包(`okxNewPrice`、`gateApi`、`newPrice`、`blackchain` 等)
-- **WS 回调不阻塞**:所有下单操作通过 `OkxTradeExecutor` 单线程池异步执行
-- **状态机驱动**:策略状态(`WAITING_KLINE → OPENING → ACTIVE → STOPPED`)严格控制执行流程
+| 参数 | 默认值 | 说明 |
+|------|--------|------|
+| `contract` | `BTC-USDT-SWAP` | 合约 ID |
+| `gridRate` | `0.003` (0.3%) | 网格步长比率 |
+| `overallTp` | `100 USDT` | 整体止盈线 |
+| `maxLoss` | `100 USDT` | 最大亏损线 |
+| `quantity` | `"1"` | 单次开仓张数(字符串,1 张 = 100 合约) |
+| `gridQueueSize` | `20` | 多/空队列各 20 个元素 |
+| `marginRatioLimit` | `0.05` (5%) | 保证金安全阀 |
+| `contractMultiplier` | `0.01` | 合约乘数(BTC 0.01,ETH 0.1) |
+| `unrealizedPnlPriceMode` | `LAST_PRICE` | 未实现盈亏计算基准价(LAST_PRICE / MARK_PRICE) |
+| `maxPosSize` | `10` 张 | **新** 单方向最大持仓张数 |
+| `step` | 运行时计算 | **新** 网格步长 = `shortBaseEntryPrice × gridRate`,队列生成时写入 |
---
-## 2. 配置层:OkxConfig
+## 三、策略状态机
```
-文件:OkxConfig.java
+INIT ──startGrid()──▶ WAITING_KLINE ──首根K线稳定──▶ OPENING ──双基底成交──▶ ACTIVE ──止盈/止损──▶ STOPPED
+ ▲ │
+ │ K线重连 / 重订阅 │
+ └──────────────────────────────────────────────────────────┘
```
-使用 **Builder 模式**构造配置对象,不可变设计,所有字段 `private final`。
+| 状态 | 说明 | 触发条件 |
+|------|------|----------|
+| `INIT` | 初始态 | `startGrid()` |
+| `WAITING_KLINE` | 等待 K 线推送稳定(标记价在基底持仓价 ± 步长范围内) | 基底开仓成功 |
+| `OPENING` | 等待双基底订单成交 | K线稳定后开仓 |
+| `ACTIVE` | 网格运行中 | 双基底成交,队列生成完成 |
+| `STOPPED` | 策略停止 | `cumulativePnl >= overallTp` 或 `<= -maxLoss` |
-### 配置字段
+---
-| 分组 | 字段 | 说明 |
-|------|------|------|
-| **API 密钥** | `apiKey` | OKX API Key |
-| | `secretKey` | OKX Secret Key |
-| | `passphrase` | OKX Passphrase |
-| **合约参数** | `contract` | 合约品种(如 `BTC-USDT-SWAP`) |
-| | `marginMode` | 保证金模式(`cross` 全仓 / `isolated` 逐仓) |
-| | `tickSz` | 价格精度 |
-| | `contractMultiplier` | 合约乘数(用于盈亏计算) |
-| | `leverage` | 杠杆倍数 |
-| **策略参数** | `quantity` | 每次开仓张数 |
-| | `gridRate` | 网格间距比例(如 0.01 = 1%) |
-| | `gridQueueSize` | 网格队列长度 |
-| | `marginRatioLimit` | 保证金占用比例上限 |
-| | `overallTp` | 全局止盈目标(累计盈亏 ≥ 此值停止) |
-| | `maxLoss` | 最大亏损限制(累计盈亏 ≤ 此值停止) |
-| **环境** | `isProduction` | 是否生产环境(决定 WS URL 域名) |
+## 四、完整策略时序图
-### Builder 方法链
+```mermaid
+sequenceDiagram
+ participant M as Main
+ participant S as OkxGridTradeService
+ participant C as OkxConfig
+ participant K as K线Handler
+ participant O as 订单Handler
+ participant P as 仓位Handler
+ participant E as OkxTradeExecutor
+ participant OKX as OKX交易所
-```java
-OkxConfig config = OkxConfig.builder()
- .apiKey("xxx")
- .secretKey("xxx")
- .passphrase("xxx")
- .contract("BTC-USDT-SWAP")
- .marginMode("cross")
- .leverage(1)
- .quantity("1")
- .gridRate(new BigDecimal("0.01"))
- .gridQueueSize(10)
- .overallTp(new BigDecimal("100"))
- .isProduction(false)
- .build();
+ Note over M,OKX: ═══ 阶段①: 初始化 ═══
+
+ M->>S: startGrid()
+ S->>S: state = INIT
+ S->>E: setPositionMode("long_short_mode")
+ E->>OKX: POST /set-position-mode
+ S->>E: setLeverage(instId, lever, "isolated")
+ E->>OKX: POST /set-leverage
+ S->>E: openShort(quantity, null, null) 基底空单
+ E->>OKX: 市价做空
+ S->>E: openLong(quantity, null, null) 基底多单
+ E->>OKX: 市价做多
+ S->>S: state = WAITING_KLINE
+ S->>S: 重置所有字段(step=null, baseOpened=false, ...)
+
+ Note over M,OKX: ═══ 阶段②: 等待K线稳定 ═══
+
+ K-->>S: onKline(price)
+ Note over S: WAITING_KLINE 状态 <br/> 检查标记价是否在基底价±步长内
+ S->>S: step = shortBaseEntryPrice × gridRate
+ S->>S: state = OPENING
+ S->>E: openLong(quantity, baseStepLongPrice, null)
+ E->>OKX: 限价做多
+ S->>E: openShort(quantity, baseStepShortPrice, null)
+ E->>OKX: 限价做空
+
+ Note over M,OKX: ═══ 阶段③: 订单成交 → 基底识别 + 队列生成 ═══
+
+ O-->>S: onOrderFilled(posSide=long, avgPx, fillSz, pnl)
+ Note over S: !baseLongOpened → 首次多基底
+ S->>S: longBaseEntryPrice = avgPx
+ S->>S: baseLongOpened = true
+ S->>S: tryGenerateQueues()
+
+ O-->>S: onOrderFilled(posSide=short, avgPx, fillSz, pnl)
+ Note over S: !baseShortOpened → 首次空基底
+ S->>S: shortBaseEntryPrice = avgPx
+ S->>S: baseShortOpened = true
+ S->>S: tryGenerateQueues()
+
+ Note over S: 双基底就绪 ✓
+ S->>C: setStep(shortBaseEntryPrice × gridRate)
+ S->>S: generateShortQueue() → [s0-s, s0-2s, ..., s0-Ns]
+ S->>S: generateLongQueue() → [l0+s, l0+2s, ..., l0+Ns]
+ S->>S: state = ACTIVE
+
+ Note over M,OKX: ═══ 阶段④: 网格运行 (ACTIVE) ═══
+
+ loop 每根K线
+ K-->>S: onKline(price)
+ Note over S: 止盈止损检查<br/>队列匹配
+ alt 空仓队列触发 (price <= queue[0])
+ S->>S: processShortGrid()
+ Note over S: 收集所有 price<=queue[i] 的元素<br/>check maxPosSize<br/>开空 + 填充队列 + 转移到多队列
+ else 多仓队列触发 (price >= queue[0])
+ S->>S: processLongGrid()
+ Note over S: 收集所有 price>=queue[i] 的元素<br/>check maxPosSize<br/>开多 + 填充队列 + 转移到空队列
+ end
+ end
+
+ Note over M,OKX: ═══ 阶段⑤: 加仓成交 → 挂止盈 ═══
+
+ O-->>S: onOrderFilled(posSide, avgPx, fillSz, pnl)
+ Note over S: baseOpened && fillSz > 0 → 加仓
+ S->>S: tpPrice = queue.get(0)
+ S->>E: placeTakeProfit(tpPrice, CLOSE_LONG, quantity)
+ E->>OKX: 挂止盈限价单
+
+ Note over M,OKX: ═══ 阶段⑥: 仓位更新 → 累计盈亏 → 停止判断 ═══
+
+ loop 每次仓位变化
+ P-->>S: onPositionUpdate(posSide, size, avgPx, realizedPnl)
+ S->>S: 更新 longRealizedPnl / shortRealizedPnl
+ S->>S: cumulativePnl = longRealizedPnl + shortRealizedPnl
+ alt cumulativePnl >= overallTp
+ S->>S: state = STOPPED
+ Note over S: 🏁 盈利目标达成
+ else cumulativePnl <= -maxLoss
+ S->>S: state = STOPPED
+ Note over S: 🏁 亏损上限触发
+ end
+ end
```
---
-## 3. 基础设施层
+## 五、详细逻辑说明
-### 3.1 OkxEnums — 常量定义
+### 5.1 K线处理 `onKline()` — 策略核心驱动
```
-文件:enums/OkxEnums.java
+WAITING_KLINE:
+ └── 标记价在 [shortBasePrice - step, shortBasePrice + step] 范围 → OPENING
+ └── 开基多限价单(baseStepLongPrice) + 基空限价单(baseStepShortPrice)
+
+ACTIVE:
+ ├── 止盈止损检查(累计已实现盈亏 vs overallTp / maxLoss)
+ ├── 保证金安全阀检查(isMarginSafe)
+ ├── processShortGrid(): 当前价 <= 空队列首 → 触发空仓网格
+ └── processLongGrid(): 当前价 >= 多队列首 → 触发多仓网格
```
-集中管理所有 OKX API 相关字符串常量,替代外部 `CoinEnums` 依赖。
+### 5.2 订单成交处理 `onOrderFilled()` — 基底识别 + 挂止盈
-| 常量 | 值 | 用途 |
-|------|-----|------|
-| `INSTTYPE_SPOT` | `SPOT` | 现货 |
-| `INSTTYPE_SWAP` | `SWAP` | 永续合约 |
-| `POSSIDE_LONG` | `long` | 多仓方向 |
-| `POSSIDE_SHORT` | `short` | 空仓方向 |
-| `SIDE_BUY` | `buy` | 买入 |
-| `SIDE_SELL` | `sell` | 卖出 |
-| `ORDTYPE_MARKET` | `market` | 市价单 |
-| `ORDTYPE_LIMIT` | `limit` | 限价单 |
-| `CHANNEL_POSITIONS` | `positions` | 持仓频道 |
-| `CHANNEL_CANDLE` | `candle` + 周期 | K线频道 |
-| `CHANNEL_ACCOUNT` | `account` | 账户频道 |
-| `CHANNEL_ORDERS` | `orders` | 订单频道 |
-| `CHANNEL_ORDERS_ALGO` | `orders-algo` | 策略委托频道 |
+**数据来源**:orders 频道 `state=filled`,取 `avgPx`(成交量加权均价)。
+
+```
+!baseLongOpened → 基底多成交 → longBaseEntryPrice = avgPx → tryGenerateQueues()
+!baseShortOpened → 基底空成交 → shortBaseEntryPrice = avgPx → tryGenerateQueues()
+baseOpened → 加仓成交 → queue[0] = 止盈价 → placeTakeProfit()
+```
+
+> `tryGenerateQueues()`:双基底都成交后,计算 step,生成多空队列,状态 → ACTIVE。
+
+### 5.3 队列生成逻辑
+
+```
+step = shortBaseEntryPrice × config.gridRate (统一用空基价)
+
+空队列 (shortPriceQueue): [shortBase - s, shortBase - 2s, ...] → 降序(最近的在前)
+多队列 (longPriceQueue): [longBase + s, longBase + 2s, ...] → 升序(最近的在前)
+```
+
+**步长统一来源**:`config.step`,由 `tryGenerateQueues()` 计算并写入。
+
+### 5.4 网格触发 `processShortGrid()` / `processLongGrid()`
+
+```
+processShortGrid():
+ 1. 收集所有 price <= queue[i] 的元素
+ 2. 开空(shortPositionSize < maxPosSize 限制)
+ 3. 填补自身队列(从队尾追加 step 递减)
+ 4. 转移到多仓队列(在 longBaseEntryPrice - step 下方插入)
+ 5. 额外开多条件: 价格在多/空持仓价之间 && 多>空 && 远离多均价 && longPositionSize < maxPosSize
+
+processLongGrid():
+ 1. 收集所有 price >= queue[i] 的元素
+ 2. 开多(longPositionSize < maxPosSize 限制)
+ 3. 填补自身队列(从队尾追加 step 递增)
+ 4. 转移到空仓队列(在 shortBaseEntryPrice + step 上方插入)
+ 5. 额外开空条件: 价格在多/空持仓价之间 && 多>空 && 远离空均价 && shortPositionSize < maxPosSize
+```
+
+### 5.5 止盈止损 `onPositionUpdate()` — 累计已实现盈亏
+
+**数据来源**:positions 频道 `realizedPnl` 字段(含手续费、资金费的综合已实现盈亏)。
+
+```
+cumulativePnl = longRealizedPnl + shortRealizedPnl
+≥ overallTp → STOPPED (盈利)
+≤ -maxLoss → STOPPED (亏损)
+```
+
+> 仓位 size=0 时该方向的 `realizedPnl` 置为 0,避免重复累加。
---
-### 3.2 OkxWsUtil — WebSocket 工具类
+## 六、策略评估
-```
-文件:OkxWsUtil.java
-```
+### ✅ 好的方面
-替代外部 `SSLConfig`、`SignUtils`、`WsParamBuild`、`DateUtil` 等依赖,提供以下静态方法:
+| # | 方面 | 说明 |
+|---|------|------|
+| 1 | **双边网格 + 队列转换** | 多空队列互为补仓,价格在区间内震荡时持续收割网格利润。止盈触发后队列元素转移到对方队列,形成"无限网格"循环 |
+| 2 | **WS 推送驱动,非轮询** | orders + positions + kline 三频道覆盖,低延迟,不浪费 API 额度 |
+| 3 | **用 `avgPx` 而非 `fillPx`** | 均价比单笔成交价更能反映真实持仓成本,避免分笔成交造成的偏差 |
+| 4 | **用 `realizedPnl` 而非手动累加** | positions 频道的 realizedPnl 包含手续费 + 资金费,比 orders 频道的逐笔 pnl 累加更完整 |
+| 5 | **步长统一存储** | `config.step` 全局唯一来源,避免多空各自计算不一致 |
+| 6 | **maxPosSize 持仓上限** | 防止单方向无限加仓,控制风险敞口 |
+| 7 | **保证金安全阀** | `isMarginSafe()` 检查,防止爆仓 |
+| 8 | **状态机清晰** | INIT → WAITING_KLINE → OPENING → ACTIVE → STOPPED,每个状态职责明确 |
+| 9 | **连接层健壮** | 心跳保活、指数退避重连、订阅恢复 |
-| 方法 | 用途 |
-|------|------|
-| `configureSSL(wsClient)` | 为 `WebSocketClient` 配置 SSL(跳过证书验证,仅测试环境) |
-| `generateSignature(timestamp, method, requestPath, body, secretKey)` | OKX 签名算法:HMAC-SHA256 + Base64 |
-| `getOrderNum(side)` | 生成唯一订单 ID(时间戳 + 随机数 + side) |
-| `timestampToDateTime(timestamp)` | 毫秒时间戳 → `yyyy/MM/dd HH:mm:ss` 格式 |
-| `timestampToDateToString(timestamp)` | 毫秒时间戳 → `yyyy/MM/dd` 格式 |
-| `buildJsonObject(connId, channel, args)` | 构建 WS 请求 JSON 对象 |
-| `buildLoginParam(okxConfig)` | 构建登录认证参数(sign + timestamp) |
+### ⚠️ 不好的方面 / 潜在风险
-**签名算法**:
-```
-sign = Base64(HMAC-SHA256(timestamp + "GET" + requestPath + body, secretKey))
-```
+| # | 问题 | 风险等级 | 说明 |
+|---|------|----------|------|
+| 1 | **无订单状态追踪** | 🔴 高 | `onOrderFilled` 触发了止盈挂单,但没有记录止盈单的 `ordId`。如果止盈单一直未成交但策略已标记 STOPPED,可能出现"策略已停止但仍有挂单"的幽灵状态 |
+| 2 | **止盈挂单无超时/撤销机制** | 🔴 高 | `placeTakeProfit` 挂出的限价单没有定时检查和撤销逻辑。市场反向走时止盈单长期挂单,占用保证金且可能过期(OKX 限价单默认 30 天) |
+| 3 | **基底开仓用市价单** | 🟡 中 | `startGrid()` 中 `openShort/openLong` 传 null 即市价,滑点风险 |
+| 4 | **队列队列无限增长风险** | 🟡 中 | `replenishOwnQueue` 从队尾追加新元素,没有队列长度上限检查。如果价格长时间单边运动,队列可能增长到远超 `gridQueueSize` 的规模 |
+| 5 | **基底价更新与队列不同步** | 🟡 中 | `onPositionUpdate` 更新 `longEntryPrice/shortEntryPrice`(加权均价),但队列仍用初始 `longBaseEntryPrice/shortBaseEntryPrice` 计算。加仓后均价变化,队列位置可能不再合理 |
+| 6 | **`realizedPnl` 归零逻辑有漏洞** | 🟡 中 | 仓位 size=0 时 `realizedPnl` 置为 0,但如果仓位先平仓再重新开仓,累计盈亏会丢失。正确的做法是记录到另一个持久累加字段 |
+| 7 | **步长只依赖空基价** | 🟡 中 | 多空各自用 `空基价 × gridRate`,如果多空基底价相差较大,多的队列步长偏大/偏小 |
+| 8 | **无断路器(熔断)** | 🟡 中 | 极端行情下没有暂停交易的机制,可能连续触发网格大量开仓 |
+| 9 | **多线程安全依赖 volatile** | 🟢 低 | 状态字段用 `volatile`,但对于复合操作(判断-修改)没有锁保护。实际风险较低,因为 WS 回调是单线程 |
+| 10 | **无订单幂等保护** | 🟢 低 | 如果 OKX 重复推送同一订单(小概率),会重复挂止盈单 |
+| 11 | **K线处理无防重** | 🟢 低 | 同一根 K 线可能被 onKline 和 processGrid 多次触发(WS 推送更新),不过 markprice 变更才会进入处理,影响有限 |
+| 12 | **账户频道未使用** | 🟢 低 | `OkxAccountChannelHandler` 只输出日志,`isMarginSafe()` 用的是交易量/可用余额估算,不如直接用 account 频道的 `availBal` |
---
-### 3.3 TradeRequestParam — 下单参数
+## 七、优化方向(按优先级排序)
-```
-文件:param/TradeRequestParam.java
-```
+### 🔴 P0 — 紧急
-纯 POJO,替代外部 `TradeRequestParam` 依赖。
+1. **止盈挂单生命周期管理**
+ - 挂单后记录 `tpOrdId`,用 orders 频道监控成交
+ - 止盈单未成交超过 N 根 K 线 → 撤销重挂
+ - 策略 STOPPED 时撤销所有未成交挂单
-| 字段 | 说明 |
-|------|------|
-| `accountName` | 账户标识 |
-| `instId` | 合约 ID |
-| `tdMode` | 保证金模式(cross/isolated) |
-| `posSide` | 持仓方向(long/short) |
-| `ordType` | 订单类型(market/limit) |
-| `side` | 买卖方向(buy/sell) |
-| `clOrdId` | 客户端订单 ID(唯一) |
-| `sz` | 下单数量 |
-| `markPx` | 标记价格(限价单用) |
-| `tradeType` | 交易类型(1=开仓,3=平仓) |
+2. **队列长度上限**
+ - `replenishOwnQueue` 中限制 `queue.size() <= gridQueueSize * 2`
+ - 超过上限时清理队尾(远离当前价的元素)
----
+### 🟡 P1 — 重要
-## 4. WebSocket 通信层
+3. **`realizedPnl` 累加到独立字段**
+ - 当前用 volatile `longRealizedPnl/shortRealizedPnl` 直接赋值会被归零覆盖
+ - 新增 `totalRealizedPnl` 累加字段,`onPositionUpdate` 中 delta 累加而非覆盖
-### 4.1 OkxWebSocketClientManager — 入口管理器
+4. **基底价与队列同步更新**
+ - 当 `longEntryPrice` 偏离 `longBaseEntryPrice` 超过 N 个 step 时,重新生成队列
-```
-文件:OkxWebSocketClientManager.java
-```
+5. **爆仓熔断**
+ - 每分钟开仓次数超过阈值 → 暂停策略 + 告警
+ - 未实现亏损超过保证金 80% → 暂停
-**Spring `@Component`**,管理完整的 WS 生命周期。
+6. **账户频道接入保证金计算**
+ - `isMarginSafe()` 改用 account 频道的 `availBal`,更精确
-#### 职责
+### 🟢 P2 — 优化
-1. **组件组装**:创建 `OkxConfig` → `OkxGridTradeService` → `OkxTradeExecutor`(注入 WS Client)→ 4 个频道处理器 → `OkxKlineWebSocketClient`
-2. **生命周期管理**:
- - `@PostConstruct init()`:初始化和连接(生产环境)或注册 MBean(测试环境)
- - `@PreDestroy close()`:优雅关闭(停止策略 → 取消条件单 → 关闭 WS)
+7. **订单幂等**
+ - 用 `Set<clOrdId>` 缓存已处理订单 ID,防止重复挂单
-#### 初始化流程
+8. **步长优化**
+ - 考虑用 `(longBase + shortBase) / 2 × gridRate` 作为统一基准,而非只用空基价
-```
-init()
- ├── configMap — 从本地缓存读取账户配置
- ├── OkxGridTradeService.startGrid()
- ├── OkxTradeExecutor.setWebSocketClient(wsClient)
- ├── 创建 4 个频道处理器
- ├── OkxKlineWebSocketClient.connect()
- │ ├── 连接 OSKL 公开 WS(K线频道不需要登录)
- │ ├── 登录私有 WS → 订阅 持仓/账户/订单/策略委托频道
- │ └── 启动心跳定时器(30s ping/pong)
- └── isProduction ? 直接启动 : MBean 注册(JMX 手动控制)
-```
-
----
-
-### 4.2 OkxKlineWebSocketClient — WS 连接客户端
-
-```
-文件:OkxKlineWebSocketClient.java
-```
-
-封装 `java-websocket` 客户端,管理物理连接。
-
-#### 连接架构
-
-- **公开频道 WS**(`wss://ws.okx.com:8443/ws/v5/public` 或模拟盘域名):K线推送不需要登录
-- **私有频道 WS**(`wss://ws.okx.com:8443/ws/v5/private` 或模拟盘域名):需要登录认证,订阅持仓/账户/订单/策略委托频道
-
-#### 连接流程
-
-```
-connect()
- ├── 1. 创建公开 WS Client → connect()
- │ └── onOpen → subscribePublicChannels() → 订阅 K线频道
- ├── 2. 创建私有 WS Client → connect()
- │ └── onOpen → login() → onLoginSuccess → subscribePrivateChannels()
- │ ├── 订阅 positions 频道
- │ ├── 订阅 account 频道
- │ ├── 订阅 orders 频道
- │ └── 订阅 orders-algo 频道
- └── 3. 启动心跳定时器(30s 间隔 ping/pong,60s 超时检测)
-```
-
-#### 断线重连机制
-
-- 最多重连 `MAX_RECONNECT_ATTEMPTS` 次(默认 30)
-- 重连延迟 `RECONNECT_DELAY_MS`(默认 5000ms)
-- 重连成功后重新执行登录 + 订阅流程
-- 兜底机制:重连失败后尝试通过 MBean 重启整个 WS 客户端
-
-#### 消息路由
-
-`onMessage` → 遍历所有 `OkxChannelHandler` → 调用 `handleMessage(response)` 分发到具体处理器
-
----
-
-## 5. 频道处理器层
-
-### 5.1 OkxChannelHandler — 处理器接口
-
-```
-文件:wsHandler/OkxChannelHandler.java
-```
-
-统一接口,所有频道处理器实现此接口:
-
-```java
-public interface OkxChannelHandler {
- String getChannelName(); // 频道名称
- void subscribe(WebSocketClient ws); // 订阅
- void unsubscribe(WebSocketClient ws); // 取消订阅
- boolean handleMessage(JSONObject response); // 处理推送消息
-}
-```
-
-### 5.2 OkxCandlestickChannelHandler — K线频道
-
-```
-文件:wsHandler/handler/OkxCandlestickChannelHandler.java
-```
-
-#### 订阅参数
-
-| 参数 | 值 |
-|------|-----|
-| `channel` | `candle{period}`(如 `candle1H`) |
-| `instId` | 合约 ID(如 `BTC-USDT-SWAP`) |
-
-#### 数据处理
-
-- 解析 `data[0]` → `[ts, o, h, l, c, vol, volCcy, ...]`
-- 提取**收盘价** `c` → 调用 `gridTradeService.onKline(closePrice)`
-- K线为 `candle1H` 时打印整点日志
-
-#### 调用链
-
-```
-onKline(closePrice)
- ├── WAITING_KLINE → 进入 OPENING 状态,开基底多+空
- ├── ACTIVE → processShortGrid(closePrice) + processLongGrid(closePrice)
- └── STOPPED → 仅更新未实现盈亏
-```
-
-### 5.3 OkxPositionsChannelHandler — 持仓频道
-
-```
-文件:wsHandler/handler/OkxPositionsChannelHandler.java
-```
-
-#### 订阅参数
-
-| 参数 | 值 |
-|------|-----|
-| `channel` | `positions` |
-| `instType` | `SWAP` |
-| `instId` | 合约 ID |
-
-#### 数据处理
-
-解析 `data[]` 数组 → 提取 `posSide`、`pos`(数量)、`avgPx`(均价)→ 调用 `gridTradeService.onPositionUpdate(posSide, size, avgPx)`
-
-#### 在策略中的作用
-
-```
-onPositionUpdate() → 区分 3 种场景:
- 1. 仓位从无到有(基底开仓成交)→ 标记 baseOpened → 双基底都成后生成网格队列
- 2. 仓位量增加(网格触发开仓成交)→ 取队列首元素做止盈价 → 设止盈条件单
- 3. 仓位归零(止盈平仓完成)→ 标记 active=false
-```
-
-### 5.4 OkxAccountChannelHandler — 账户频道
-
-```
-文件:wsHandler/handler/OkxAccountChannelHandler.java
-```
-
-#### 订阅参数
-
-| 参数 | 值 |
-|------|-----|
-| `channel` | `account` |
-
-#### 数据处理
-
-解析 `data[]` → 提取 `availBal`(可用余额)、`cashBal`(现金余额)、`eq`(权益)、`upl`(未实现盈亏)、`imr`(保证金占用)
-
-**当前版本**:仅做日志输出,不做业务判断。后续可扩展保证金安全阀功能。
-
-### 5.5 OkxOrderInfoChannelHandler — 订单成交频道
-
-```
-文件:wsHandler/handler/OkxOrderInfoChannelHandler.java
-```
-
-#### 订阅参数
-
-| 参数 | 值 |
-|------|-----|
-| `channel` | `orders` |
-| `instType` | `SWAP` |
-| `instId` | 合约 ID |
-
-#### 数据处理
-
-- 过滤 `state=filled` 且 `accFillSz>0` 的订单
-- 提取 `posSide`、`accFillSz`(成交数量)、`fillPnl`(已实现盈亏)
-- 调用 `gridTradeService.onOrderFilled(posSide, accFillSz, fillPnl)`
-
-#### 在策略中的作用
-
-```
-onOrderFilled()
- ├── 累计盈亏 += fillPnl
- ├── 累计盈亏 ≥ overallTp → 策略停止(止盈达标)
- └── 累计盈亏 ≤ -maxLoss → 策略停止(亏损超限)
-```
-
----
-
-## 6. 策略执行层
-
-### 6.1 OkxTradeExecutor — 异步下单执行器
-
-```
-文件:OkxTradeExecutor.java
-```
-
-#### 设计目的
-
-WS 消息在回调线程处理,下单操作提交到**独立线程池异步执行**,避免阻塞 WS 回调线程。
-
-#### 线程模型
-
-| 参数 | 值 |
-|------|-----|
-| 核心线程 | 1 |
-| 最大线程 | 1 |
-| 空闲超时 | 60s(`allowCoreThreadTimeOut`) |
-| 队列类型 | `LinkedBlockingQueue` |
-| 队列容量 | 64 |
-| 拒绝策略 | `CallerRunsPolicy`(队列满时由提交线程直接同步执行,形成自然背压) |
-| 守护线程 | 是 |
-
-**单线程的作用**:保证下单顺序(开多 → 开空 → 止盈单),避免并发竞争。
-
-#### 公开方法
-
-| 方法 | 说明 |
-|------|------|
-| `openLong(quantity, onSuccess, onFailure)` | 异步市价开多 |
-| `openShort(quantity, onSuccess, onFailure)` | 异步市价开空 |
-| `placeTakeProfit(triggerPrice, orderType, size)` | 异步创建止盈条件单(通过 `batch-orders` 发送 algo 委托) |
-| `cancelAllPriceTriggeredOrders()` | 撤销所有条件单(`cancel-algos`) |
-| `setWebSocketClient(wsClient)` | 注入 WS 客户端引用 |
-| `shutdown()` | 优雅关闭(等待 10s,超时强制中断) |
-
-#### 止盈兜底机制
-
-```
-placeTakeProfit()
- ├── 成功 → 发送 batch-orders(algo 条件单)
- └── 失败 → marketClose() 立即市价平仓兜底
-```
-
-#### 下单方式
-
-所有下单通过 **WebSocket** 发送 JSON(非 REST API),`sendOrder` 构建如下消息:
-
-```json
-{
- "id": "order_1712345678901_a1b2c3",
- "op": "order",
- "args": [{
- "instId": "BTC-USDT-SWAP",
- "tdMode": "cross",
- "clOrdId": "buy_1712345678901_d4e5f6",
- "side": "buy",
- "posSide": "long",
- "ordType": "market",
- "sz": "1"
- }]
-}
-```
-
-#### 调用链
-
-```
-OkxGridTradeService.onKline
- └── executor.openLong() / openShort() ← 基底双开 + 网格触发
-
-OkxGridTradeService.onPositionUpdate
- └── executor.placeTakeProfit() ← 仓位成交后设止盈
-
-OkxGridTradeService.stopGrid
- └── executor.cancelAllPriceTriggeredOrders() + shutdown()
-```
-
----
-
-### 6.2 OkxGridTradeService — 网格策略核心
-
-```
-文件:OkxGridTradeService.java
-```
-
-这是整个包的核心,实现了**多空双开网格交易策略**的所有状态机和网格队列逻辑。
-
-#### 策略状态机
-
-```
-WAITING_KLINE ──(首根K线到达)──▶ OPENING ──(双基底成交)──▶ ACTIVE ──(止盈/止损达标)──▶ STOPPED
- ▲ │
- └──────────────────(startGrid() 重新启动)──────────────────┘
-```
-
-| 状态 | 含义 |
-|------|------|
-| `WAITING_KLINE` | 等待首根 K 线到达 |
-| `OPENING` | 已收到 K 线,正在开基底仓位(多+空各一单) |
-| `ACTIVE` | 双基底已成交,网格队列已生成,正常交易中 |
-| `STOPPED` | 已停止(止盈达标 / 亏损超限 / 手动停止) |
-
-#### 数据结构
-
-| 字段 | 类型 | 说明 |
-|------|------|------|
-| `shortPriceQueue` | `List<BigDecimal>` | 空仓价格队列(**降序**) |
-| `longPriceQueue` | `List<BigDecimal>` | 多仓价格队列(**升序**) |
-| `shortBaseEntryPrice` | `BigDecimal` | 空仓基底成交价 |
-| `longBaseEntryPrice` | `BigDecimal` | 多仓基底成交价 |
-| `baseLongOpened` | `boolean` | 多仓基底是否已开 |
-| `baseShortOpened` | `boolean` | 空仓基底是否已开 |
-| `cumulativePnl` | `BigDecimal` | 累计已实现盈亏 |
-
-#### 策略完整生命周期
-
-```
-1. startGrid()
- └── 重置所有状态 → WAITING_KLINE
-
-2. onKline(closePrice) → WAITING_KLINE
- └── 转为 OPENING → 发送基底多单 + 基底空单
-
-3. onPositionUpdate(posSide, size, entryPrice)
- ├── 仓位从无到有
- │ ├── 标记 baseLongOpened / baseShortOpened
- │ ├── 记录 entryPrice
- │ ├── 双基底都成交 → tryGenerateQueues() → ACTIVE
- │ └── 生成网格队列:
- │ · 空仓队列:entryPrice × (1 - gridRate×1), (1 - gridRate×2), ... 降序排列
- │ · 多仓队列:entryPrice × (1 + gridRate×1), (1 + gridRate×2), ... 升序排列
- ├── 仓位量增加(网格触发开仓成交)
- │ └── 检查队列非空 → 取队列首元素做止盈价 → executor.placeTakeProfit()
- └── 仓位归零
- └── 标记 active=false
-
-4. onKline(closePrice) → ACTIVE
- ├── processShortGrid(closePrice) ← 空仓网格处理
- │ ├── 匹配队列中 > closePrice 的元素
- │ ├── 移除已匹配 → 尾部补充新元素(递减)
- │ ├── 多仓队列转移(以对方队列首元素为种子生成递减元素)
- │ ├── 贴近持仓均价过滤(skip)
- │ ├── 保证金安全检查
- │ ├── 开空一次
- │ └── 额外反向开多(价格夹在多/空均价之间且多>空倒挂时)
- └── processLongGrid(closePrice) ← 多仓网格处理
- └── (对称逻辑,方向反转)
-
-5. onOrderFilled(posSide, fillSz, pnl)
- ├── cumulativePnl += pnl
- ├── cumulativePnl ≥ overallTp → STOPPED
- └── cumulativePnl ≤ -maxLoss → STOPPED
-
-6. stopGrid()
- ├── 状态 → STOPPED
- ├── cancelAllPriceTriggeredOrders()
- └── executor.shutdown()
-```
-
-#### 空仓网格处理 `processShortGrid(currentPrice)` 详解
-
-```
-1. 匹配队列元素(空仓队列降序遍历,收集 > currentPrice 的元素)
- └── 为空 → 直接返回
-
-2. 空仓队列更新
- ├── 移除 matched 元素
- └── 尾部补充新元素(尾价 × (1 - gridRate) 循环递减)→ 降序排序
-
-3. 多仓队列转移
- ├── 以多仓队列首元素(最小价)为种子
- ├── 生成 matched.size() 个递减元素加入多仓队列
- ├── 贴近持仓均价过滤:元素与多仓均价差距 < gridRate → skip
- └── 升序排序,超长截断
-
-4. 保证金安全检查
- └── 超限 → warn 跳过开仓
-
-5. 开空一次 → executor.openShort()
-
-6. 额外反向开多条件(同时满足):
- ├── longEntryPrice > shortEntryPrice(多>空倒挂)
- ├── currentPrice > shortEntryPrice(当前价在空仓均价上方)
- └── currentPrice < longEntryPrice × (1 - gridRate)(远离多仓均价)
- └── 满足 → executor.openLong() 额外开多一次
-```
-
-#### 多仓网格处理 `processLongGrid(currentPrice)` 详解
-
-对称逻辑,方向反转。
-
----
-
-## 7. 独立启动类:OkxWebSocketClientMain
-
-```
-文件:OkxWebSocketClientMain.java
-```
-
-**纯 main 方法启动**,不依赖 Spring 容器。
-
-### 用途
-
-用于**本地测试和调试**,无需启动整个 Spring 应用。
-
-### 启动流程
-
-```java
-public static void main(String[] args) {
- OkxConfig config = OkxConfig.builder()
- .apiKey("xxx")
- .secretKey("xxx")
- .passphrase("xxx")
- .contract("BTC-USDT-SWAP")
- .marginMode("cross")
- .leverage(1)
- .quantity("1")
- .gridRate(new BigDecimal("0.01"))
- .gridQueueSize(10)
- .overallTp(new BigDecimal("100"))
- .isProduction(false)
- .build();
-
- OkxGridTradeService service = new OkxGridTradeService(config, "test-account");
- service.startGrid();
-
- OkxKlineWebSocketClient wsClient = new OkxKlineWebSocketClient(config, service, ...);
- wsClient.connect();
-
- // 注册 JVM 关闭钩子
- Runtime.getRuntime().addShutdownHook(new Thread(() -> {
- service.stopGrid();
- wsClient.close();
- }));
-}
-```
-
----
-
-## 8. 完整调用链
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ 1. 启动阶段 │
-├─────────────────────────────────────────────────────────────┤
-│ OkxWebSocketClientManager.init() │
-│ ├── new OkxConfig.Builder()...build() │
-│ ├── new OkxGridTradeService(config, accountName) │
-│ │ └── new OkxTradeExecutor(contract, marginMode, name) │
-│ ├── gridTradeService.startGrid() → WAITING_KLINE │
-│ ├── new OkxCandlestickChannelHandler(instId, candlePeriod, │
-│ │ gridTradeService, config) │
-│ ├── new OkxPositionsChannelHandler(instId, gridTradeService)│
-│ ├── new OkxAccountChannelHandler() │
-│ ├── new OkxOrderInfoChannelHandler(instId, gridTradeService,│
-│ │ config) │
-│ ├── new OkxKlineWebSocketClient(config, handlers, ...) │
-│ ├── wsClient.connect() │
-│ │ ├── 连接公开 WS → 订阅 K线频道 │
-│ │ ├── 连接私有 WS → 登录 → 订阅 持仓/账户/订单/策略委托 │
-│ │ └── 启动心跳定时器 │
-│ └── executor.setWebSocketClient(privateWsClient) │
-└─────────────────────────────────────────────────────────────┘
-
-┌─────────────────────────────────────────────────────────────┐
-│ 2. 运行时数据流 │
-├─────────────────────────────────────────────────────────────┤
-│ [K线推送] │
-│ OkxCandlestickChannelHandler.handleMessage() │
-│ └── gridTradeService.onKline(closePrice) │
-│ ├── WAITING_KLINE → OPENING │
-│ │ ├── executor.openLong(quantity, onSuccess, │
-│ │ │ onFailure) │
-│ │ └── executor.openShort(quantity, onSuccess, │
-│ │ onFailure) │
-│ └── ACTIVE │
-│ ├── processShortGrid(closePrice) │
-│ │ ├── 匹配队列 → 更新队列 → 转移对方队列 │
-│ │ └── executor.openShort() │
-│ └── processLongGrid(closePrice) │
-│ └──(对称逻辑) │
-│ │
-│ [持仓推送] │
-│ OkxPositionsChannelHandler.handleMessage() │
-│ └── gridTradeService.onPositionUpdate(posSide, size, avgPx)│
-│ ├── 基底成交 → 标记 baseOpened → tryGenerateQueues() │
-│ └── 增量成交 → executor.placeTakeProfit(tp, type, sz)│
-│ │
-│ [订单成交推送] │
-│ OkxOrderInfoChannelHandler.handleMessage() │
-│ └── gridTradeService.onOrderFilled(posSide, fillSz, pnl) │
-│ └── cumulativePnl 累加 → 达标则停止 │
-└─────────────────────────────────────────────────────────────┘
-
-┌─────────────────────────────────────────────────────────────┐
-│ 3. 停止阶段 │
-├─────────────────────────────────────────────────────────────┤
-│ OkxWebSocketClientManager.close() │
-│ ├── gridTradeService.stopGrid() │
-│ │ ├── state = STOPPED │
-│ │ ├── executor.cancelAllPriceTriggeredOrders() │
-│ │ │ └── wsClient.send(cancel-algos) │
-│ │ └── executor.shutdown() │
-│ └── wsClient.close() │
-│ └── 关闭公开WS + 私有WS │
-└─────────────────────────────────────────────────────────────┘
-```
-
----
-
-## 9. 与 Gate API 的差异对比
-
-| 方面 | Gate API (gateApi) | OKX API (okxApi) |
-|------|-------------------|-----------------|
-| **下单方式** | REST API (`FuturesApi.createFuturesOrder`) | WebSocket JSON 消息 (`op: "order"`) |
-| **止盈单** | REST API (`createPriceTriggeredOrder`),`plan-close-*-position` | WS 消息 (`op: "batch-orders"`),`ordType: limit` + `px` 触发价 |
-| **仓位方向** | 正数=开多、负数=开空(size 带符号) | `posSide: long/short` 显式区分,`sz` 始终正数 |
-| **保证金模式** | 无(Gate API 隐含) | `tdMode: cross/isolated` 显式指定 |
-| **客户端订单 ID** | 自动生成(Gate API 隐式处理) | `clOrdId` 显式生成和传入 |
-| **取消条件单** | REST API (`cancelPriceTriggeredOrderList`) | WS 消息 (`op: "cancel-algos"`) |
-| **止损失败兜底** | REST `createFuturesOrder` IOC 市价平仓 | WS 消息 `marketClose()`(`tradeType: "3"`) |
-| **成交识别** | 通过 WS `FuturesOrderBookTicker` 或 REST 查询 | WS `orders` 频道推送 `state=filled` |
-| **API 认证** | HMAC-SHA256 请求头签名(Gate SDK 封装) | HMAC-SHA256 + Base64 WS 登录消息 |
-| **WS 连接** | 单一连接,频道订阅混合 | 双连接:公开 WS(K线)+ 私有 WS(持仓/账户/订单) |
-| **`placeTakeProfit` 签名** | `(triggerPrice, rule, orderType, size)` 多一个 `rule` 参数 | `(triggerPrice, orderType, size)` (OKX 无 rule 概念) |
-| **止盈单下单** | 单条 `FuturesPriceTriggeredOrder` | `batch-orders` 包装(List 格式,但实际传 1 条) |
-| **心跳机制** | 应用层 ping/pong JSON 消息 | `java-websocket` 自带 ping/pong + 60s 超时重连 |
-| **包依赖** | 依赖 Gate SDK (`io.gate.gateapi`) | **完全自包含**,仅依赖 `java-websocket` + `fastjson` + `lombok` |
+9. **基底开仓改用限价单**
+ - `startGrid()` 中基底开仓用限价单(取盘口卖一/买一价),减少滑点
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OKX_QUANT_DOCUMENTATION.md b/src/main/java/com/xcong/excoin/modules/okxNewPrice/OKX_QUANT_DOCUMENTATION.md
deleted file mode 100644
index bce4319..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OKX_QUANT_DOCUMENTATION.md
+++ /dev/null
@@ -1,333 +0,0 @@
-# OKX 量化交易系统文档
-
-## 1. 包结构概述
-
-```
-com.xcong.excoin.modules.okxNewPrice/
-├── celue/ # 策略实现模块
-│ ├── CaoZuoService.java # 策略接口
-│ └── CaoZuoServiceImpl.java # 策略实现类
-├── jiaoyi/ # 交易相关模块
-├── okxWs/ # OKX WebSocket 相关类
-│ ├── enums/ # WebSocket 相关枚举
-│ ├── param/ # WebSocket 请求参数
-│ ├── wanggeList/ # 网格列表管理
-│ ├── AccountWs.java # 账户信息处理
-│ ├── BalanceAndPositionWs.java # 余额和持仓处理
-│ ├── InstrumentsWs.java # 合约信息处理
-│ ├── LoginWs.java # 登录处理
-│ ├── OrderInfoWs.java # 订单信息处理
-│ ├── PositionsWs.java # 持仓信息处理
-│ └── TradeOrderWs.java # 交易订单处理
-├── okxpi/ # OKX API 接口封装
-├── utils/ # 工具类
-├── wangge/ # 网格相关模块
-├── zhanghu/ # 账户相关模块
-├── OkxNewPriceWebSocketClient.java # 价格 WebSocket 客户端
-├── OkxQuantWebSocketClient.java # 量化交易 WebSocket 客户端
-├── OkxWebSocketClientManager.java # WebSocket 客户端管理器
-└── OKX_QUANT_DOCUMENTATION.md # 本文档
-```
-
-## 2. 核心组件说明
-
-### 2.1 WebSocket 客户端管理
-
-#### OkxWebSocketClientManager
-
-**功能**:集中管理多个 OKX WebSocket 客户端实例,包括价格客户端和账号客户端。
-
-**核心属性**:
-- `quantClientMap`: 存储所有账号的 `OkxQuantWebSocketClient` 实例
-- `newPriceClient`: 存储价格数据的 `OkxNewPriceWebSocketClient` 实例
-
-**主要方法**:
-- `init()`: 初始化所有 WebSocket 客户端
-- `destroy()`: 销毁所有 WebSocket 客户端资源
-- `getClient()`: 获取指定账号的 WebSocket 客户端
-- `getAllClients()`: 获取所有账号的 WebSocket 客户端
-
-**使用流程**:
-1. Spring 容器启动时自动调用 `init()` 方法
-2. 初始化 `OkxNewPriceWebSocketClient` 用于获取价格数据
-3. 为每个账号创建 `OkxQuantWebSocketClient` 实例
-4. 所有客户端统一由管理器进行生命周期管理
-
-### 2.2 价格 WebSocket 客户端
-
-#### OkxNewPriceWebSocketClient
-
-**功能**:连接 OKX 公共 WebSocket 接口,实时获取标记价格数据,并触发量化交易操作。
-
-**核心属性**:
-- `webSocketClient`: WebSocket 连接客户端
-- `isConnected/isConnecting/isInitialized`: 连接状态标志
-- `lastMessageTime`: 最后收到消息的时间
-
-**主要方法**:
-- `init()`: 初始化 WebSocket 客户端
-- `destroy()`: 销毁 WebSocket 客户端资源
-- `connect()`: 建立 WebSocket 连接
-- `startHeartbeat()`: 启动心跳检测
-- `processPushData()`: 处理价格推送数据
-- `triggerQuantOperations()`: 触发量化交易操作
-
-**价格处理流程**:
-1. 连接 OKX WebSocket 公共接口
-2. 订阅标记价格通道
-3. 收到价格数据后保存到 Redis
-4. 调用 `triggerQuantOperations()` 触发量化交易
-5. 实现心跳检测和自动重连机制
-
-### 2.3 账号 WebSocket 客户端
-
-#### OkxQuantWebSocketClient
-
-**功能**:连接 OKX 私有 WebSocket 接口,处理账号登录、持仓、订单等私有数据。
-
-**核心属性**:
-- `account`: 账号信息枚举
-- `webSocketClient`: WebSocket 连接客户端
-- `isConnected/isConnecting`: 连接状态标志
-
-**主要方法**:
-- `init()`: 初始化 WebSocket 客户端
-- `destroy()`: 销毁 WebSocket 客户端资源
-- `connect()`: 建立 WebSocket 连接
-- `websocketLogin()`: 账号登录
-- `subscribeChannels()`: 订阅私有通道
-- `processPushData()`: 处理数据推送
-
-**登录与订阅流程**:
-1. 连接 OKX WebSocket 私有接口
-2. 发送登录请求
-3. 登录成功后订阅账户、持仓、订单等通道
-4. 接收并处理私有数据推送
-5. 实现心跳检测和自动重连机制
-
-## 3. 网格策略实现
-
-### 3.1 网格配置
-
-#### WangGeListEnum
-
-**功能**:定义不同价格区间的网格参数,包括价格上下限、方向、步距等。
-
-**核心属性**:
-- `name`: 网格名称
-- `jiage_shangxian`: 价格上限
-- `jiage_xiaxian`: 价格下限
-- `jian_ju`: 网格步距
-- `fang_xiang`: 持仓方向 (long/short)
-- `zhi_sun_dian`: 止损点
-
-**主要方法**:
-- `getGridByPrice()`: 根据当前价格获取对应的网格
-
-**网格定义示例**:
-```java
-UP("上层做空", "2", "3100", "3000", "2", "short", "3100"),
-CENTER("中间做多", "2", "3000", "2900", "2", "long", "2900"),
-CENTER_ONE("中间做空", "2", "2900", "2870", "2", "short", "2870"),
-DOWN("下层做多", "2", "2870", "2850", "2", "long", "2850");
-```
-
-### 3.2 策略实现
-
-#### CaoZuoService/CaoZuoServiceImpl
-
-**功能**:实现量化交易策略逻辑,包括加仓、减仓、止损等操作。
-
-**核心方法**:
-- `caoZuoHandler()`: 主要策略逻辑入口
-- `caoZuoZhiSunEvent()`: 止损事件处理
-- `caoZuoInitEvent()`: 初始化订单处理
-- `chooseEvent()`: 事件选择处理
-- `caoZuoLong()`: 多头策略处理
-- `caoZuoShort()`: 空头策略处理
-
-**策略执行流程**:
-1. 检查账户状态和系统开关
-2. 判断当前价格所在网格
-3. 检查是否需要止损
-4. 检查持仓状态:
- - 无持仓:执行初始化订单
- - 有持仓:根据网格策略决定加仓或减仓
-5. 根据多空方向执行相应策略
-
-### 3.3 订单执行
-
-#### TradeOrderWs
-
-**功能**:构建和发送订单请求到 OKX WebSocket 接口。
-
-**核心方法**:
-- `orderEvent()`: 执行订单事件
-
-**订单执行流程**:
-1. 验证下单参数和账户状态
-2. 检查账户和持仓通道是否就绪
-3. 构建订单请求 JSON
-4. 发送订单到 WebSocket 接口
-5. 更新订单状态和就绪标志
-
-## 4. 系统交互流程
-
-### 4.1 启动流程
-
-```
-[Spring 容器启动] → OkxWebSocketClientManager.init() → 初始化 newPriceClient → 初始化所有 quantClient → 建立 WebSocket 连接
-```
-
-### 4.2 价格触发交易流程
-
-```
-OkxNewPriceWebSocketClient.onMessage() → processPushData() → triggerQuantOperations() → WangGeListEnum.getGridByPrice() →
- 对每个账号执行:
- 1. 检查反向持仓并止损 → caoZuoZhiSunEvent()
- 2. 执行当前网格策略 → caoZuoHandler() → chooseEvent() → caoZuoLong()/caoZuoShort()
- 3. 发送订单 → TradeOrderWs.orderEvent()
-```
-
-### 4.3 订单执行流程
-
-```
-TradeOrderWs.orderEvent() → 验证参数 → 检查就绪状态 → 构建订单JSON → 发送订单 → 更新状态标志
-```
-
-## 5. 数据结构
-
-### 5.1 订单请求参数
-
-#### TradeRequestParam
-
-**功能**:封装交易订单请求参数。
-
-**核心属性**:
-- `accountName`: 账号名称
-- `markPx`: 标记价格
-- `instId`: 合约ID
-- `tdMode`: 交易模式
-- `posSide`: 持仓方向
-- `ordType`: 订单类型
-- `side`: 买卖方向
-- `sz`: 数量
-- `clOrdId`: 客户订单ID
-- `tradeType`: 交易类型
-
-### 5.2 数据存储结构
-
-系统使用双层 Map 结构存储不同账号的数据:
-
-```java
-// 第一层 key 为账号名称,第二层 key 为数据项
-Map<String, Map<String, String>> accountDataMap = new ConcurrentHashMap<>();
-```
-
-主要数据存储类:
-- `AccountWs.ACCOUNTWSMAP`: 账户信息
-- `PositionsWs.POSITIONSWSMAP`: 持仓信息
-- `OrderInfoWs.ORDERINFOWSMAP`: 订单信息
-- `TradeOrderWs.TRADEORDERWSMAP`: 交易订单信息
-
-## 6. 网格策略核心算法
-
-### 6.1 网格匹配算法
-
-```java
-public static WangGeListEnum getGridByPrice(BigDecimal price) {
- for (WangGeListEnum grid : WangGeListEnum.values()) {
- BigDecimal upperLimit = new BigDecimal(grid.jiage_shangxian);
- BigDecimal lowerLimit = new BigDecimal(grid.jiage_xiaxian);
-
- if (upperLimit.compareTo(lowerLimit) > 0) {
- if (price.compareTo(lowerLimit) > 0 && price.compareTo(upperLimit) <= 0) {
- return grid;
- }
- }
- }
- return null;
-}
-```
-
-### 6.2 交易决策算法
-
-```java
-public TradeRequestParam caoZuoHandler(String accountName, String markPx, String posSide) {
- // 1. 检查系统开关和账户状态
- // 2. 判断止损条件
- // 3. 检查保证金和仓位情况
- // 4. 根据持仓数量决定操作类型
- // 5. 返回交易请求参数
-}
-```
-
-## 7. 错误处理与容错机制
-
-### 7.1 WebSocket 连接管理
-- 实现心跳检测机制,定期发送 ping 请求
-- 自动重连机制,连接断开后使用指数退避策略重连
-- 连接状态管理,避免重复连接
-
-### 7.2 订单执行保障
-- 订单参数验证,确保参数完整性
-- 账户和持仓通道就绪检查
-- 订单状态跟踪,避免重复下单
-
-### 7.3 风险控制
-- 止损机制,限制单次交易亏损
-- 保证金检查,避免满仓操作
-- 系统开关,支持紧急暂停交易
-
-## 8. 扩展与定制
-
-### 8.1 网格参数配置
-可通过修改 `WangGeListEnum` 枚举值来配置不同的网格参数:
-
-```java
-// 格式:名称, 小数位数, 价格上限, 价格下限, 步距, 方向, 止损点
-NEW_GRID("新网格", "2", "4000", "3500", "5", "long", "3500")
-```
-
-### 8.2 策略扩展
-可通过实现 `CaoZuoService` 接口来扩展新的交易策略:
-
-```java
-public class CustomCaoZuoServiceImpl implements CaoZuoService {
- // 实现自定义策略逻辑
-}
-```
-
-### 8.3 多账号支持
-系统天然支持多账号管理,只需在 `ExchangeInfoEnum` 中添加新的账号配置即可。
-
-## 9. 性能优化
-
-1. **并发处理**:使用 `ConcurrentHashMap` 存储账号数据,支持高并发访问
-2. **线程管理**:使用线程池处理异步任务,避免线程泄漏
-3. **连接复用**:多个账号共享相同的 WebSocket 连接参数,减少连接开销
-4. **数据缓存**:使用 Redis 缓存价格数据,提高数据访问效率
-
-## 10. 监控与日志
-
-系统使用 SLF4J 日志框架,记录关键操作和错误信息:
-
-- WebSocket 连接状态
-- 价格变化和网格匹配
-- 订单执行过程
-- 错误和异常信息
-
-通过日志可以监控系统运行状态和排查问题。
-
-## 11. 总结
-
-OKX 量化交易系统是一个基于 WebSocket 的实时交易系统,实现了多网格策略、自动交易执行和风险控制功能。系统采用模块化设计,各组件职责明确,便于维护和扩展。
-
-核心功能包括:
-- 多网格策略配置和管理
-- 实时价格监控和网格匹配
-- 自动交易决策和执行
-- 多账号管理和统一控制
-- 完善的错误处理和风险控制
-
-系统可以根据市场价格变化自动执行交易策略,实现量化交易的自动化和智能化。
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxKlineWebSocketClient.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxKlineWebSocketClient.java
deleted file mode 100644
index cf5c9fd..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxKlineWebSocketClient.java
+++ /dev/null
@@ -1,577 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.util.ObjectUtil;
-import cn.hutool.json.JSONException;
-import cn.hutool.json.JSONUtil;
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-import com.xcong.excoin.modules.blackchain.service.DateUtil;
-import com.xcong.excoin.modules.okxNewPrice.celue.CaoZuoService;
-import com.xcong.excoin.modules.okxNewPrice.indicator.TradingStrategy;
-import com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy.MacdEmaStrategy;
-import com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy.MacdMaStrategy;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.*;
-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.Kline;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.param.TradeRequestParam;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.wanggeList.WangGeListEnum;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.wanggeList.WangGeListService;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.ExchangeInfoEnum;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.ExchangeLoginService;
-import com.xcong.excoin.modules.okxNewPrice.utils.SSLConfig;
-import com.xcong.excoin.modules.okxNewPrice.utils.WsMapBuild;
-import com.xcong.excoin.modules.okxNewPrice.utils.WsParamBuild;
-import com.xcong.excoin.utils.RedisUtils;
-import lombok.extern.slf4j.Slf4j;
-import org.java_websocket.client.WebSocketClient;
-import org.java_websocket.handshake.ServerHandshake;
-
-import java.math.BigDecimal;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.*;
-import java.util.concurrent.*;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.stream.Collectors;
-
-/**
- * OKX 新价格 WebSocket 客户端类,用于连接 OKX 的 WebSocket 接口,
- * 实时获取并处理标记价格(mark price)数据,并将价格信息存储到 Redis 中。
- * 同时支持心跳检测、自动重连以及异常恢复机制。
- * @author Administrator
- */
-@Slf4j
-public class OkxKlineWebSocketClient {
- private final RedisUtils redisUtils;
- private final CaoZuoService caoZuoService;
- private final OkxWebSocketClientManager clientManager;
- private final WangGeListService wangGeListService;
-
- private WebSocketClient webSocketClient;
- private ScheduledExecutorService heartbeatExecutor;
- private volatile ScheduledFuture<?> pongTimeoutFuture;
- private final AtomicReference<Long> lastMessageTime = new AtomicReference<>(System.currentTimeMillis());
-
- // 连接状态标志
- private final AtomicBoolean isConnected = new AtomicBoolean(false);
- private final AtomicBoolean isConnecting = new AtomicBoolean(false);
- private final AtomicBoolean isInitialized = new AtomicBoolean(false);
-
-// private static final String CHANNEL = "mark-price";
- private static final String CHANNEL = "candle1m";
-// private static final String CHANNEL = "candle15m";
-
- // 心跳超时时间(秒),小于30秒
- private static final int HEARTBEAT_TIMEOUT = 10;
-
- // 共享线程池用于重连等异步任务
- private final ExecutorService sharedExecutor = Executors.newCachedThreadPool(r -> {
- Thread t = new Thread(r, "okx-ws-kline-worker");
- t.setDaemon(true);
- return t;
- });
-
- public OkxKlineWebSocketClient(RedisUtils redisUtils,
- CaoZuoService caoZuoService, OkxWebSocketClientManager clientManager,
- WangGeListService wangGeListService) {
- this.redisUtils = redisUtils;
- this.caoZuoService = caoZuoService;
- this.clientManager = clientManager;
- this.wangGeListService = wangGeListService;
- }
-
- /**
- * 初始化方法,创建并初始化WebSocket客户端实例
- */
- public void init() {
- if (!isInitialized.compareAndSet(false, true)) {
- log.warn("OkxKlineWebSocketClient 已经初始化过,跳过重复初始化");
- return;
- }
- connect();
- startHeartbeat();
- }
-
- /**
- * 销毁方法,关闭WebSocket连接和相关资源
- */
- public void destroy() {
- log.info("开始销毁OkxKlineWebSocketClient");
-
- // 设置关闭标志,避免重连
- if (sharedExecutor != null && !sharedExecutor.isShutdown()) {
- sharedExecutor.shutdown();
- }
-
- if (webSocketClient != null && webSocketClient.isOpen()) {
- try {
- webSocketClient.closeBlocking();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- log.warn("关闭WebSocket连接时被中断");
- }
- }
-
- shutdownExecutorGracefully(heartbeatExecutor);
- if (pongTimeoutFuture != null) {
- pongTimeoutFuture.cancel(true);
- }
- shutdownExecutorGracefully(sharedExecutor);
-
- log.info("OkxKlineWebSocketClient销毁完成");
- }
-
- private static final String WS_URL_MONIPAN = "wss://wspap.okx.com:8443/ws/v5/business";
- private static final String WS_URL_SHIPAN = "wss://ws.okx.com:8443/ws/v5/business";
- private static final boolean isAccountType = false;
-
- /**
- * 建立与 OKX WebSocket 服务器的连接。
- * 设置回调函数以监听连接打开、接收消息、关闭和错误事件。
- */
- private void connect() {
- // 避免重复连接
- if (isConnecting.get()) {
- log.info("连接已在进行中,跳过重复连接请求");
- return;
- }
-
- if (!isConnecting.compareAndSet(false, true)) {
- log.info("连接已在进行中,跳过重复连接请求");
- return;
- }
-
- try {
- SSLConfig.configureSSL();
- System.setProperty("https.protocols", "TLSv1.2,TLSv1.3");
- String WS_URL = WS_URL_MONIPAN;
- if (isAccountType){
- WS_URL = WS_URL_SHIPAN;
- }
- URI uri = new URI(WS_URL);
-
- // 关闭之前的连接(如果存在)
- if (webSocketClient != null) {
- try {
- webSocketClient.closeBlocking();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- log.warn("关闭之前连接时被中断");
- }
- }
-
- webSocketClient = new WebSocketClient(uri) {
- @Override
- public void onOpen(ServerHandshake handshake) {
- log.info("OKX kline WebSocket连接成功");
- isConnected.set(true);
- isConnecting.set(false);
-
- // 检查应用是否正在关闭
- if (sharedExecutor != null && !sharedExecutor.isShutdown()) {
- resetHeartbeatTimer();
- subscribeChannels();
- } else {
- log.warn("应用正在关闭,忽略WebSocket连接成功回调");
- }
- }
-
- @Override
- public void onMessage(String message) {
- lastMessageTime.set(System.currentTimeMillis());
- handleWebSocketMessage(message);
- resetHeartbeatTimer();
- }
-
- @Override
- public void onClose(int code, String reason, boolean remote) {
- log.warn("OKX kline WebSocket连接关闭: code={}, reason={}", code, reason);
- isConnected.set(false);
- isConnecting.set(false);
- cancelPongTimeout();
-
- if (sharedExecutor != null && !sharedExecutor.isShutdown() && !sharedExecutor.isTerminated()) {
- sharedExecutor.execute(() -> {
- try {
- reconnectWithBackoff();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- log.error("重连线程被中断", e);
- } catch (Exception e) {
- log.error("重连失败", e);
- }
- });
- } else {
- log.warn("共享线程池已关闭,无法执行重连任务");
- }
- }
-
- @Override
- public void onError(Exception ex) {
- log.error("OKX New Price WebSocket发生错误", ex);
- isConnected.set(false);
- }
- };
-
- webSocketClient.connect();
- } catch (URISyntaxException e) {
- log.error("WebSocket URI格式错误", e);
- isConnecting.set(false);
- }
- }
-
- /**
- * 订阅指定交易对的价格通道。
- * 构造订阅请求并发送给服务端。
- */
- private void subscribeChannels() {
- JSONObject subscribeMsg = new JSONObject();
- subscribeMsg.put("op", "subscribe");
-
- JSONArray argsArray = new JSONArray();
- JSONObject arg = new JSONObject();
- arg.put("channel", CHANNEL);
- arg.put("instId", CoinEnums.HE_YUE.getCode());
- argsArray.add(arg);
-
- subscribeMsg.put("args", argsArray);
- webSocketClient.send(subscribeMsg.toJSONString());
- log.info("已发送 K线频道订阅请求,订阅通道数: {}", argsArray.size());
- }
-
- /**
- * 处理从 WebSocket 收到的消息。
- * 包括订阅确认、错误响应、心跳响应以及实际的数据推送。
- *
- * @param message 来自 WebSocket 的原始字符串消息
- */
- private void handleWebSocketMessage(String message) {
- try {
- if ("pong".equals(message)) {
- log.debug("{}: 收到心跳响应");
- cancelPongTimeout();
- return;
- }
- JSONObject response = JSON.parseObject(message);
- String event = response.getString("event");
-
- if ("subscribe".equals(event)) {
- log.info(" K线频道订阅成功: {}", response.getJSONObject("arg"));
- } else if ("error".equals(event)) {
- log.error(" K线频道订阅错误: code={}, msg={}",
- response.getString("code"), response.getString("msg"));
- } else if ("pong".equals(event)) {
- log.debug("收到pong响应");
- cancelPongTimeout();
- } else {
-// processPushData(response);
- processPushDataV2(response);
- }
- } catch (Exception e) {
- log.error("处理WebSocket消息失败: {}", message, e);
- }
- }
-
- /**
- * 解析并处理价格推送数据。
- * 将最新的标记价格存入 Redis 并触发后续业务逻辑比较处理。
- * 当价格变化时,调用CaoZuoService的caoZuo方法,触发所有账号的量化操作
- *
- * @param response 包含价格数据的 JSON 对象
- */
- private void processPushDataV2(JSONObject response) {
- try {
- /**
- * {
- * "arg": {
- * "channel": "candle1D",
- * "instId": "BTC-USDT"
- * },
- * "data": [
- * [
- * "1629993600000",
- * "42500",
- * "48199.9",
- * "41006.1",
- * "41006.1",
- * "3587.41204591",
- * "166741046.22583129",
- * "166741046.22583129",
- * "0"
- * ]
- * ]
- * }
- */
- JSONObject arg = response.getJSONObject("arg");
- if (arg == null) {
- log.warn("{}: 无效的推送数据,缺少 'arg' 字段", response);
- return;
- }
-
- String channel = arg.getString("channel");
- if (channel == null) {
- log.warn("{}: 无效的推送数据,缺少 'channel' 字段", response);
- return;
- }
-
- String instId = arg.getString("instId");
- if (instId == null) {
- log.warn("{}: 无效的推送数据,缺少 'instId' 字段", response);
- return;
- }
-
- if (CHANNEL.equals(channel) && CoinEnums.HE_YUE.getCode().equals(instId)) {
- JSONArray dataArray = response.getJSONArray("data");
- if (dataArray == null || dataArray.isEmpty()) {
- log.warn("K线频道数据为空");
- return;
- }
- JSONArray data = dataArray.getJSONArray(0);
- BigDecimal openPx = new BigDecimal(data.getString(1));
- BigDecimal highPx = new BigDecimal(data.getString(2));
- BigDecimal lowPx = new BigDecimal(data.getString(3));
- BigDecimal closePx = new BigDecimal(data.getString(4));
- BigDecimal vol = new BigDecimal(data.getString(5));
- //ts String 开始时间,Unix时间戳的毫秒数格式,如 1597026383085 转日期:2020-08-07 15:13:03.085
- String time = DateUtil.TimeStampToDateTime(Long.parseLong(data.getString(0)));
- /**
- * K线状态
- * 0:K线未完结
- * 1:K线已完结
- */
- String confirm = data.getString(8);
- if ("1".equals(confirm)){
- //调用策略
- // 创建策略实例
- MacdEmaStrategy strategy = new MacdEmaStrategy();
-
- // 生成200个1m价格数据点
- List<Kline> kline1MinuteData = getKlineDataByInstIdAndBar(instId, "1m");
- List<BigDecimal> historicalPrices1M = kline1MinuteData.stream()
- .map(Kline::getC)
- .collect(Collectors.toList());
-
- // 使用策略分析最新价格数据
- MacdEmaStrategy.TradingOrder tradingOrderOpenOpen = strategy.generateTradingOrder(historicalPrices1M, MacdMaStrategy.OperationType.open.name());
- if (tradingOrderOpenOpen == null){
- return;
- }
-
- Collection<OkxQuantWebSocketClient> allClients = clientManager.getAllClients();
- //如果为空,则直接返回
- if (allClients.isEmpty()) {
- return;
- }
- // 获取所有OkxQuantWebSocketClient实例
- for (OkxQuantWebSocketClient client : clientManager.getAllClients()) {
- String accountName = client.getAccountName();
- if (accountName != null) {
- if (ObjectUtil.isNotEmpty(tradingOrderOpenOpen)){
- log.info("{}开仓{}:{}",instId,tradingOrderOpenOpen.getPosSide(),tradingOrderOpenOpen.getSide());
- doOpen(client.getWebSocketClient(),accountName, tradingOrderOpenOpen, closePx);
- }
- }
- }
- }
- }
- } catch (Exception e) {
- log.error("处理 K线频道推送数据失败", e);
- }
- }
-
- private void doOpen(WebSocketClient webSocketClient, String accountName, MacdEmaStrategy.TradingOrder tradingOrderOpenOpen, BigDecimal closePx) {
- // 根据信号执行交易操作
- TradeRequestParam tradeRequestParam = new TradeRequestParam();
-
- String posSide = tradingOrderOpenOpen.getPosSide();
- tradeRequestParam.setPosSide(posSide);
- String currentPrice = String.valueOf(closePx);
- tradeRequestParam = caoZuoService.caoZuoStrategy(accountName, currentPrice, posSide);
-
- String side = tradingOrderOpenOpen.getSide();
- tradeRequestParam.setSide(side);
-
- String clOrdId = WsParamBuild.getOrderNum(side);
- tradeRequestParam.setClOrdId(clOrdId);
-
- String sz = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.BUY_CNT_INIT.name());
- tradeRequestParam.setSz(sz);
- TradeOrderWs.orderEvent(webSocketClient, tradeRequestParam);
- }
-
- private List<Kline> getKlineDataByInstIdAndBar(String instId, String bar) {
- List<Kline> klineList = new ArrayList<>();
- try {
- LinkedHashMap<String, Object> requestParam = new LinkedHashMap<>();
- requestParam.put("instId", instId);
- requestParam.put("bar", bar);
- requestParam.put("limit", "200");
- String result = ExchangeLoginService.getInstance(ExchangeInfoEnum.OKX_UAT.name()).lineHistory(requestParam);
- JSONObject json = JSON.parseObject(result);
- String data = json.getString("data");
-
- if (data != null) {
- List<String[]> klinesList = JSON.parseArray(data, String[].class);
- if (!CollUtil.isEmpty(klinesList)) {
- for (String[] s : klinesList) {
- // 确保数组有足够的元素
- if (s != null && s.length >= 9) {
- String s1 = s[8];
- try {
- if ("1".equals(s1)){
- Kline kline = new Kline();
- kline.setTs(s[0]);
- kline.setO(new BigDecimal(s[1]));
- kline.setH(new BigDecimal(s[2]));
- kline.setL(new BigDecimal(s[3]));
- kline.setC(new BigDecimal(s[4]));
- kline.setVol(new BigDecimal(s[5]));
- kline.setConfirm(s[8]);
- klineList.add(kline);
- }
- } catch (NumberFormatException e) {
- log.error("K线数据转换为BigDecimal失败: {}", Arrays.toString(s), e);
- }
- } else {
- log.warn("K线数据数组长度不足: {}", Arrays.toString(s));
- }
- }
- }
- } else {
- log.warn("K线数据为空");
- }
- } catch (JSONException e) {
- log.error("K线数据解析失败", e);
- } catch (Exception e) {
- log.error("获取K线数据异常", e);
- }
- return klineList;
- }
-
- /**
- * 构建 Redis Key
- */
- private String buildRedisKey(String instId) {
- return "PRICE_" + instId.replace("-", "");
- }
-
- /**
- * 启动心跳检测任务。
- * 使用 ScheduledExecutorService 定期检查是否需要发送 ping 请求来维持连接。
- */
- private void startHeartbeat() {
- if (heartbeatExecutor != null && !heartbeatExecutor.isTerminated()) {
- heartbeatExecutor.shutdownNow();
- }
-
- heartbeatExecutor = Executors.newSingleThreadScheduledExecutor(r -> {
- Thread t = new Thread(r, "okx-kline-heartbeat");
- t.setDaemon(true);
- return t;
- });
-
- heartbeatExecutor.scheduleWithFixedDelay(this::checkHeartbeatTimeout, 25, 25, TimeUnit.SECONDS);
- }
-
- /**
- * 重置心跳计时器。
- * 当收到新消息或发送 ping 后取消当前超时任务并重新安排下一次超时检查。
- */
- private synchronized void resetHeartbeatTimer() {
- cancelPongTimeout();
-
- if (heartbeatExecutor != null && !heartbeatExecutor.isShutdown()) {
- pongTimeoutFuture = heartbeatExecutor.schedule(this::checkHeartbeatTimeout,
- HEARTBEAT_TIMEOUT, TimeUnit.SECONDS);
- }
- }
-
- /**
- * 检查心跳超时情况。
- * 若长时间未收到任何消息则主动发送 ping 请求保持连接活跃。
- */
- private void checkHeartbeatTimeout() {
- // 只有在连接状态下才检查心跳
- if (!isConnected.get()) {
- return;
- }
-
- long currentTime = System.currentTimeMillis();
- long lastTime = lastMessageTime.get();
-
- if (currentTime - lastTime >= HEARTBEAT_TIMEOUT * 1000L) {
- sendPing();
- }
- }
-
- /**
- * 发送 ping 请求至 WebSocket 服务端。
- * 用于维持长连接有效性。
- */
- private void sendPing() {
- try {
- if (webSocketClient != null && webSocketClient.isOpen()) {
- webSocketClient.send("ping");
- log.debug("发送ping请求");
- }
- } catch (Exception e) {
- log.warn("发送ping失败", e);
- }
- }
-
- /**
- * 取消当前的心跳超时任务。
- * 在收到 pong 或其他有效消息时调用此方法避免不必要的断开重连。
- */
- private synchronized void cancelPongTimeout() {
- if (pongTimeoutFuture != null && !pongTimeoutFuture.isDone()) {
- pongTimeoutFuture.cancel(true);
- }
- }
-
- /**
- * 执行 WebSocket 重连操作。
- * 在连接意外中断后尝试重新建立连接。
- */
- private void reconnectWithBackoff() throws InterruptedException {
- int attempt = 0;
- int maxAttempts = 3;
- long delayMs = 5000;
-
- while (attempt < maxAttempts) {
- try {
- Thread.sleep(delayMs);
- connect();
- return;
- } catch (Exception e) {
- log.warn("第{}次重连失败", attempt + 1, e);
- delayMs *= 2;
- attempt++;
- }
- }
-
- log.error("超过最大重试次数({})仍未连接成功", maxAttempts);
- }
-
- /**
- * 优雅关闭线程池
- */
- private void shutdownExecutorGracefully(ExecutorService executor) {
- if (executor == null || executor.isTerminated()) {
- return;
- }
- try {
- executor.shutdown();
- if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
- executor.shutdownNow();
- }
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- executor.shutdownNow();
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxNewPriceWebSocketClient.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxNewPriceWebSocketClient.java
deleted file mode 100644
index 782467e..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxNewPriceWebSocketClient.java
+++ /dev/null
@@ -1,468 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice;
-
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-import com.xcong.excoin.modules.okxNewPrice.celue.CaoZuoService;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.TradeOrderWs;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.CoinEnums;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.param.TradeRequestParam;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.wanggeList.WangGeListEnum;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.wanggeList.WangGeListService;
-import com.xcong.excoin.modules.okxNewPrice.utils.SSLConfig;
-import com.xcong.excoin.utils.RedisUtils;
-import java.math.BigDecimal;
-import lombok.extern.slf4j.Slf4j;
-import org.java_websocket.client.WebSocketClient;
-import org.java_websocket.handshake.ServerHandshake;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Collection;
-import java.util.concurrent.*;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * OKX 新价格 WebSocket 客户端类,用于连接 OKX 的 WebSocket 接口,
- * 实时获取并处理标记价格(mark price)数据,并将价格信息存储到 Redis 中。
- * 同时支持心跳检测、自动重连以及异常恢复机制。
- * @author Administrator
- */
-@Slf4j
-public class OkxNewPriceWebSocketClient {
- private final RedisUtils redisUtils;
- private final CaoZuoService caoZuoService;
- private final OkxWebSocketClientManager clientManager;
- private final WangGeListService wangGeListService;
-
- private WebSocketClient webSocketClient;
- private ScheduledExecutorService heartbeatExecutor;
- private volatile ScheduledFuture<?> pongTimeoutFuture;
- private final AtomicReference<Long> lastMessageTime = new AtomicReference<>(System.currentTimeMillis());
-
- // 连接状态标志
- private final AtomicBoolean isConnected = new AtomicBoolean(false);
- private final AtomicBoolean isConnecting = new AtomicBoolean(false);
- private final AtomicBoolean isInitialized = new AtomicBoolean(false);
-
- private static final String CHANNEL = "mark-price";
-
- // 心跳超时时间(秒),小于30秒
- private static final int HEARTBEAT_TIMEOUT = 10;
-
- // 共享线程池用于重连等异步任务
- private final ExecutorService sharedExecutor = Executors.newCachedThreadPool(r -> {
- Thread t = new Thread(r, "okx-ws-shared-worker");
- t.setDaemon(true);
- return t;
- });
-
- public OkxNewPriceWebSocketClient(RedisUtils redisUtils,
- CaoZuoService caoZuoService, OkxWebSocketClientManager clientManager,
- WangGeListService wangGeListService) {
- this.redisUtils = redisUtils;
- this.caoZuoService = caoZuoService;
- this.clientManager = clientManager;
- this.wangGeListService = wangGeListService;
- }
-
- /**
- * 初始化方法,创建并初始化WebSocket客户端实例
- */
- public void init() {
- if (!isInitialized.compareAndSet(false, true)) {
- log.warn("OkxNewPriceWebSocketClient 已经初始化过,跳过重复初始化");
- return;
- }
- connect();
- startHeartbeat();
- }
-
- /**
- * 销毁方法,关闭WebSocket连接和相关资源
- */
- public void destroy() {
- log.info("开始销毁OkxNewPriceWebSocketClient");
-
- // 设置关闭标志,避免重连
- if (sharedExecutor != null && !sharedExecutor.isShutdown()) {
- sharedExecutor.shutdown();
- }
-
- if (webSocketClient != null && webSocketClient.isOpen()) {
- try {
- webSocketClient.closeBlocking();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- log.warn("关闭WebSocket连接时被中断");
- }
- }
-
- shutdownExecutorGracefully(heartbeatExecutor);
- if (pongTimeoutFuture != null) {
- pongTimeoutFuture.cancel(true);
- }
- shutdownExecutorGracefully(sharedExecutor);
-
- log.info("OkxNewPriceWebSocketClient销毁完成");
- }
-
- private static final String WS_URL_MONIPAN = "wss://wspap.okx.com:8443/ws/v5/public";
- private static final String WS_URL_SHIPAN = "wss://ws.okx.com:8443/ws/v5/public";
- private static final boolean isAccountType = true;
-
- /**
- * 建立与 OKX WebSocket 服务器的连接。
- * 设置回调函数以监听连接打开、接收消息、关闭和错误事件。
- */
- private void connect() {
- // 避免重复连接
- if (isConnecting.get()) {
- log.info("连接已在进行中,跳过重复连接请求");
- return;
- }
-
- if (!isConnecting.compareAndSet(false, true)) {
- log.info("连接已在进行中,跳过重复连接请求");
- return;
- }
-
- try {
- SSLConfig.configureSSL();
- System.setProperty("https.protocols", "TLSv1.2,TLSv1.3");
- String WS_URL = WS_URL_MONIPAN;
- if (isAccountType){
- WS_URL = WS_URL_SHIPAN;
- }
- URI uri = new URI(WS_URL);
-
- // 关闭之前的连接(如果存在)
- if (webSocketClient != null) {
- try {
- webSocketClient.closeBlocking();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- log.warn("关闭之前连接时被中断");
- }
- }
-
- webSocketClient = new WebSocketClient(uri) {
- @Override
- public void onOpen(ServerHandshake handshake) {
- log.info("OKX New Price WebSocket连接成功");
- isConnected.set(true);
- isConnecting.set(false);
-
- // 检查应用是否正在关闭
- if (sharedExecutor != null && !sharedExecutor.isShutdown()) {
- resetHeartbeatTimer();
- subscribeChannels();
- } else {
- log.warn("应用正在关闭,忽略WebSocket连接成功回调");
- }
- }
-
- @Override
- public void onMessage(String message) {
- lastMessageTime.set(System.currentTimeMillis());
- handleWebSocketMessage(message);
- resetHeartbeatTimer();
- }
-
- @Override
- public void onClose(int code, String reason, boolean remote) {
- log.warn("OKX New Price WebSocket连接关闭: code={}, reason={}", code, reason);
- isConnected.set(false);
- isConnecting.set(false);
- cancelPongTimeout();
-
- if (sharedExecutor != null && !sharedExecutor.isShutdown() && !sharedExecutor.isTerminated()) {
- sharedExecutor.execute(() -> {
- try {
- reconnectWithBackoff();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- log.error("重连线程被中断", e);
- } catch (Exception e) {
- log.error("重连失败", e);
- }
- });
- } else {
- log.warn("共享线程池已关闭,无法执行重连任务");
- }
- }
-
- @Override
- public void onError(Exception ex) {
- log.error("OKX New Price WebSocket发生错误", ex);
- isConnected.set(false);
- }
- };
-
- webSocketClient.connect();
- } catch (URISyntaxException e) {
- log.error("WebSocket URI格式错误", e);
- isConnecting.set(false);
- }
- }
-
- /**
- * 订阅指定交易对的价格通道。
- * 构造订阅请求并发送给服务端。
- */
- private void subscribeChannels() {
- JSONObject subscribeMsg = new JSONObject();
- subscribeMsg.put("op", "subscribe");
-
- JSONArray argsArray = new JSONArray();
- JSONObject arg = new JSONObject();
- arg.put("channel", CHANNEL);
- arg.put("instId", CoinEnums.HE_YUE.getCode());
- argsArray.add(arg);
-
- subscribeMsg.put("args", argsArray);
- webSocketClient.send(subscribeMsg.toJSONString());
- log.info("已发送价格订阅请求,订阅通道数: {}", argsArray.size());
- }
-
- /**
- * 处理从 WebSocket 收到的消息。
- * 包括订阅确认、错误响应、心跳响应以及实际的数据推送。
- *
- * @param message 来自 WebSocket 的原始字符串消息
- */
- private void handleWebSocketMessage(String message) {
- try {
- JSONObject response = JSON.parseObject(message);
- String event = response.getString("event");
-
- if ("subscribe".equals(event)) {
- log.info("价格订阅成功: {}", response.getJSONObject("arg"));
- } else if ("error".equals(event)) {
- log.error("价格订阅错误: code={}, msg={}",
- response.getString("code"), response.getString("msg"));
- } else if ("pong".equals(event)) {
- log.debug("收到pong响应");
- cancelPongTimeout();
- } else {
- processPushData(response);
- }
- } catch (Exception e) {
- log.error("处理WebSocket消息失败: {}", message, e);
- }
- }
-
- /**
- * 解析并处理价格推送数据。
- * 将最新的标记价格存入 Redis 并触发后续业务逻辑比较处理。
- * 当价格变化时,调用CaoZuoService的caoZuo方法,触发所有账号的量化操作
- *
- * @param response 包含价格数据的 JSON 对象
- */
- private void processPushData(JSONObject response) {
- try {
- JSONArray dataArray = response.getJSONArray("data");
- if (dataArray != null && !dataArray.isEmpty()) {
- for (int i = 0; i < dataArray.size(); i++) {
- try {
- JSONObject priceData = dataArray.getJSONObject(i);
- String instId = priceData.getString("instId");
- String markPx = priceData.getString("markPx");
- // 保存价格到Redis
- redisUtils.set(CoinEnums.HE_YUE.getCode(), markPx);
-
- log.debug("更新最新价格: {} = {}, 币种: {}", CoinEnums.HE_YUE.getCode(), markPx, instId);
-
- // 价格变化时,触发所有账号的量化操作
- triggerQuantOperations(markPx);
- } catch (Exception innerEx) {
- log.warn("处理单条价格数据失败", innerEx);
- }
- }
- }
- } catch (Exception e) {
- log.error("处理价格推送数据失败", e);
- }
- }
-
- /**
- * 触发所有账号的量化操作
- * @param markPx 当前标记价格
- */
- private void triggerQuantOperations(String markPx) {
- try {
- // 1. 判断当前价格属于哪个网格
- WangGeListEnum gridByPriceNew = WangGeListEnum.getGridByPrice(new BigDecimal(markPx));
- if (gridByPriceNew == null) {
- log.error("当前价格{}不在任何网格范围内,无法触发量化操作", markPx);
- return;
- }
- /**
- * 获取当前网格信息
- * 根据当前网格的持仓方向获取反方向是否存在持仓
- * 如果持有,直接止损
- */
- Collection<OkxQuantWebSocketClient> allClients = clientManager.getAllClients();
- //如果为空,则直接返回
- if (allClients.isEmpty()) {
- return;
- }
- // 获取所有OkxQuantWebSocketClient实例
- for (OkxQuantWebSocketClient client : clientManager.getAllClients()) {
- String accountName = client.getAccountName();
- if (accountName != null) {
- /**
- * 处理历史网格的订单
- * 根据历史网格的开单方向,是否需要止损处理
- * 如果方向一致就不需要处理
- * 如果不一致则需要处理
- */
- String fangXiang = gridByPriceNew.getFang_xiang();
- String fangXiangOld = CoinEnums.POSSIDE_LONG.getCode().equals(fangXiang) ? CoinEnums.POSSIDE_SHORT.getCode() : CoinEnums.POSSIDE_LONG.getCode();
- log.info("历史网格方向为:{}", fangXiangOld);
- if (!fangXiang.equals(fangXiangOld)){
- TradeRequestParam tradeRequestParamOld = caoZuoService.caoZuoZhiSunEvent(accountName, markPx, fangXiangOld);
- TradeOrderWs.orderEvent(client.getWebSocketClient(), tradeRequestParamOld);
- }
-
- /**
- * 处理当前网格的订单,触发量化操作
- */
- log.info("当前价格{}属于网格: {}-{}({}-{})", markPx, gridByPriceNew.getName(),gridByPriceNew.getFang_xiang(), gridByPriceNew.getJiage_xiaxian(), gridByPriceNew.getJiage_shangxian());
- wangGeListService.initWangGe(markPx);
- TradeRequestParam tradeRequestParam = caoZuoService.caoZuoHandler(accountName, markPx, gridByPriceNew.getFang_xiang());
- TradeOrderWs.orderEvent(client.getWebSocketClient(), tradeRequestParam);
- log.info("价格变化触发量化操作: 账号={}, 价格={}", accountName, markPx);
- }
- }
- } catch (Exception e) {
- log.error("触发量化操作失败", e);
- }
- }
-
- /**
- * 构建 Redis Key
- */
- private String buildRedisKey(String instId) {
- return "PRICE_" + instId.replace("-", "");
- }
-
- /**
- * 启动心跳检测任务。
- * 使用 ScheduledExecutorService 定期检查是否需要发送 ping 请求来维持连接。
- */
- private void startHeartbeat() {
- if (heartbeatExecutor != null && !heartbeatExecutor.isTerminated()) {
- heartbeatExecutor.shutdownNow();
- }
-
- heartbeatExecutor = Executors.newSingleThreadScheduledExecutor(r -> {
- Thread t = new Thread(r, "okx-newprice-heartbeat");
- t.setDaemon(true);
- return t;
- });
-
- heartbeatExecutor.scheduleWithFixedDelay(this::checkHeartbeatTimeout, 25, 25, TimeUnit.SECONDS);
- }
-
- /**
- * 重置心跳计时器。
- * 当收到新消息或发送 ping 后取消当前超时任务并重新安排下一次超时检查。
- */
- private synchronized void resetHeartbeatTimer() {
- cancelPongTimeout();
-
- if (heartbeatExecutor != null && !heartbeatExecutor.isShutdown()) {
- pongTimeoutFuture = heartbeatExecutor.schedule(this::checkHeartbeatTimeout,
- HEARTBEAT_TIMEOUT, TimeUnit.SECONDS);
- }
- }
-
- /**
- * 检查心跳超时情况。
- * 若长时间未收到任何消息则主动发送 ping 请求保持连接活跃。
- */
- private void checkHeartbeatTimeout() {
- // 只有在连接状态下才检查心跳
- if (!isConnected.get()) {
- return;
- }
-
- long currentTime = System.currentTimeMillis();
- long lastTime = lastMessageTime.get();
-
- if (currentTime - lastTime >= HEARTBEAT_TIMEOUT * 1000L) {
- sendPing();
- }
- }
-
- /**
- * 发送 ping 请求至 WebSocket 服务端。
- * 用于维持长连接有效性。
- */
- private void sendPing() {
- try {
- if (webSocketClient != null && webSocketClient.isOpen()) {
- JSONObject ping = new JSONObject();
- ping.put("op", "ping");
- webSocketClient.send(ping.toJSONString());
- log.debug("发送ping请求");
- }
- } catch (Exception e) {
- log.warn("发送ping失败", e);
- }
- }
-
- /**
- * 取消当前的心跳超时任务。
- * 在收到 pong 或其他有效消息时调用此方法避免不必要的断开重连。
- */
- private synchronized void cancelPongTimeout() {
- if (pongTimeoutFuture != null && !pongTimeoutFuture.isDone()) {
- pongTimeoutFuture.cancel(true);
- }
- }
-
- /**
- * 执行 WebSocket 重连操作。
- * 在连接意外中断后尝试重新建立连接。
- */
- private void reconnectWithBackoff() throws InterruptedException {
- int attempt = 0;
- int maxAttempts = 3;
- long delayMs = 5000;
-
- while (attempt < maxAttempts) {
- try {
- Thread.sleep(delayMs);
- connect();
- return;
- } catch (Exception e) {
- log.warn("第{}次重连失败", attempt + 1, e);
- delayMs *= 2;
- attempt++;
- }
- }
-
- log.error("超过最大重试次数({})仍未连接成功", maxAttempts);
- }
-
- /**
- * 优雅关闭线程池
- */
- private void shutdownExecutorGracefully(ExecutorService executor) {
- if (executor == null || executor.isTerminated()) {
- return;
- }
- try {
- executor.shutdown();
- if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
- executor.shutdownNow();
- }
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- executor.shutdownNow();
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxQuantWebSocketClient.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxQuantWebSocketClient.java
deleted file mode 100644
index fd30e06..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxQuantWebSocketClient.java
+++ /dev/null
@@ -1,517 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice;
-
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONObject;
-import com.xcong.excoin.modules.okxNewPrice.celue.CaoZuoService;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.*;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.CoinEnums;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.ExchangeInfoEnum;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.param.TradeRequestParam;
-import com.xcong.excoin.modules.okxNewPrice.utils.SSLConfig;
-import com.xcong.excoin.utils.RedisUtils;
-import lombok.extern.slf4j.Slf4j;
-import org.java_websocket.client.WebSocketClient;
-import org.java_websocket.handshake.ServerHandshake;
-
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
-import java.math.BigDecimal;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.List;
-import java.util.concurrent.*;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * OKX 新价格 WebSocket 客户端类,用于连接 OKX 的 WebSocket 接口,
- * 实时获取并处理标记价格(mark price)数据,并将价格信息存储到 Redis 中。
- * 同时支持心跳检测、自动重连以及异常恢复机制。
- * @author Administrator
- */
-@Slf4j
-public class OkxQuantWebSocketClient {
- private final RedisUtils redisUtils;
- private final ExchangeInfoEnum account;
- private final CaoZuoService caoZuoService;
-
- private WebSocketClient webSocketClient;
- private ScheduledExecutorService heartbeatExecutor;
- private volatile ScheduledFuture<?> pongTimeoutFuture;
- private final AtomicReference<Long> lastMessageTime = new AtomicReference<>(System.currentTimeMillis());
-
- // 连接状态标志
- private final AtomicBoolean isConnected = new AtomicBoolean(false);
- private final AtomicBoolean isConnecting = new AtomicBoolean(false);
-
- /**
- * 获取WebSocketClient实例
- * @return WebSocketClient实例
- */
- public WebSocketClient getWebSocketClient() {
- return webSocketClient;
- }
-
- /**
- * 获取账号名称
- * @return 账号名称
- */
- public String getAccountName() {
- return account.name();
- }
-
- public OkxQuantWebSocketClient(ExchangeInfoEnum account,
- CaoZuoService caoZuoService,
- RedisUtils redisUtils) {
- this.account = account;
- this.caoZuoService = caoZuoService;
- this.redisUtils = redisUtils;
- }
-
- private static final String WS_URL_MONIPAN = "wss://wspap.okx.com:8443/ws/v5/private";
- private static final String WS_URL_SHIPAN = "wss://ws.okx.com:8443/ws/v5/private";
-
- /**
- * 订阅频道指令
- */
- private static final String SUBSCRIBE = "subscribe";
- private static final String UNSUBSCRIBE = "unsubscribe";
-
- // 心跳超时时间(秒),小于30秒
- private static final int HEARTBEAT_TIMEOUT = 10;
-
- // 共享线程池用于重连等异步任务
- private final ExecutorService sharedExecutor = Executors.newCachedThreadPool(r -> {
- Thread t = new Thread(r, "okx-ws-account-order-worker");
- t.setDaemon(true);
- return t;
- });
-
- // 在 OkxQuantWebSocketClient 中添加初始化标记
- private final AtomicBoolean isInitialized = new AtomicBoolean(false);
-
- /**
- * 初始化方法,在 Spring Bean 构造完成后执行。
- * 负责建立 WebSocket 连接并启动心跳检测任务。
- */
- @PostConstruct
- public void init() {
- // 防止重复初始化
- if (!isInitialized.compareAndSet(false, true)) {
- log.warn("OkxQuantWebSocketClient 已经初始化过,跳过重复初始化");
- return;
- }
-
- connect();
- startHeartbeat();
- }
-
- /**
- * 销毁方法,在 Spring Bean 销毁前执行。
- * 关闭 WebSocket 连接、停止心跳定时器及相关的线程资源。
- */
-// @PreDestroy
-// public void destroy() {
-// if (webSocketClient != null && webSocketClient.isOpen()) {
-// subscribeAccountChannel(UNSUBSCRIBE);
-// subscribePositionChannel(UNSUBSCRIBE);
-// subscribeOrderInfoChannel(UNSUBSCRIBE);
-// webSocketClient.close();
-// }
-// shutdownExecutorGracefully(heartbeatExecutor);
-// if (pongTimeoutFuture != null) {
-// pongTimeoutFuture.cancel(true);
-// }
-// shutdownExecutorGracefully(sharedExecutor);
-//
-// // 移除了 reconnectScheduler 的关闭操作
-// }
- @PreDestroy
- public void destroy() {
- log.info("开始销毁OkxQuantWebSocketClient");
-
- // 设置关闭标志,避免重连
- if (sharedExecutor != null && !sharedExecutor.isShutdown()) {
- sharedExecutor.shutdown();
- }
-
- if (webSocketClient != null && webSocketClient.isOpen()) {
- try {
- subscribeAccountChannel(UNSUBSCRIBE);
- subscribePositionChannel(UNSUBSCRIBE);
- subscribeOrderInfoChannel(UNSUBSCRIBE);
- webSocketClient.closeBlocking();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- log.warn("关闭WebSocket连接时被中断");
- }
- }
-
- shutdownExecutorGracefully(heartbeatExecutor);
- if (pongTimeoutFuture != null) {
- pongTimeoutFuture.cancel(true);
- }
- shutdownExecutorGracefully(sharedExecutor);
-
- log.info("OkxQuantWebSocketClient销毁完成");
- }
-
- private void shutdownExecutorGracefully(ExecutorService executor) {
- if (executor == null || executor.isTerminated()) {
- return;
- }
- try {
- executor.shutdown();
- if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
- executor.shutdownNow();
- }
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- executor.shutdownNow();
- }
- }
-
- /**
- * 建立与 OKX WebSocket 服务器的连接。
- * 设置回调函数以监听连接打开、接收消息、关闭和错误事件。
- */
- private void connect() {
- // 避免重复连接
- if (isConnecting.get()) {
- log.info("连接已在进行中,跳过重复连接请求");
- return;
- }
-
- if (!isConnecting.compareAndSet(false, true)) {
- log.info("连接已在进行中,跳过重复连接请求");
- return;
- }
-
- try {
- InstrumentsWs.handleEvent(account.name());
- SSLConfig.configureSSL();
- System.setProperty("https.protocols", "TLSv1.2,TLSv1.3");
- String WS_URL = WS_URL_MONIPAN;
- if (account.isAccountType()){
- WS_URL = WS_URL_SHIPAN;
- }
- URI uri = new URI(WS_URL);
-
- // 关闭之前的连接(如果存在)
- if (webSocketClient != null) {
- try {
- webSocketClient.closeBlocking();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- log.warn("关闭之前连接时被中断");
- }
- }
-
- webSocketClient = new WebSocketClient(uri) {
- @Override
- public void onOpen(ServerHandshake handshake) {
- log.info("OKX account-order WebSocket连接成功");
- isConnected.set(true);
- isConnecting.set(false);
-
- // 棜查应用是否正在关闭
- if (!sharedExecutor.isShutdown()) {
- resetHeartbeatTimer();
- websocketLogin(account);
- } else {
- log.warn("应用正在关闭,忽略WebSocket连接成功回调");
- }
- }
-
- @Override
- public void onMessage(String message) {
- lastMessageTime.set(System.currentTimeMillis());
- handleWebSocketMessage(message);
- resetHeartbeatTimer();
- }
-
- @Override
- public void onClose(int code, String reason, boolean remote) {
- log.warn("OKX account-order WebSocket连接关闭: code={}, reason={}", code, reason);
- isConnected.set(false);
- isConnecting.set(false);
- cancelPongTimeout();
-
- if (sharedExecutor != null && !sharedExecutor.isShutdown() && !sharedExecutor.isTerminated()) {
- sharedExecutor.execute(() -> {
- try {
- reconnectWithBackoff();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- log.error("重连线程被中断", e);
- } catch (Exception e) {
- log.error("重连失败", e);
- }
- });
- } else {
- log.warn("共享线程池已关闭,无法执行重连任务");
- }
- }
-
- @Override
- public void onError(Exception ex) {
- log.error("OKX account-order WebSocket发生错误", ex);
- isConnected.set(false);
- }
- };
-
- webSocketClient.connect();
- } catch (URISyntaxException e) {
- log.error("WebSocket URI格式错误", e);
- isConnecting.set(false);
- }
- }
-
- private void websocketLogin(ExchangeInfoEnum account) {
- LoginWs.websocketLogin(webSocketClient, account);
- }
-
- private void subscribeBalanceAndPositionChannel(String option) {
- BalanceAndPositionWs.subscribeBalanceAndPositionChannel(webSocketClient, option);
- }
-
- private void subscribeOrderInfoChannel(String option) {
- OrderInfoWs.subscribeOrderInfoChannel(webSocketClient, option);
- }
-
- private void subscribeAccountChannel(String option) {
- AccountWs.subscribeAccountChannel(webSocketClient, option);
- }
-
- private void subscribePositionChannel(String option) {
- PositionsWs.subscribePositionChannel(webSocketClient, option);
- }
-
- /**
- * 处理从 WebSocket 收到的消息。
- * 包括订阅确认、错误响应、心跳响应以及实际的数据推送。
- *
- * @param message 来自 WebSocket 的原始字符串消息
- */
- private void handleWebSocketMessage(String message) {
- try {
- if ("pong".equals(message)) {
- log.debug("{}: 收到心跳响应", account.name());
- cancelPongTimeout();
- return;
- }
- JSONObject response = JSON.parseObject(message);
- String event = response.getString("event");
-
- if ("login".equals(event)) {
- String code = response.getString("code");
- if ("0".equals(code)) {
- String connId = response.getString("connId");
- log.info("{}: WebSocket登录成功, connId: {}", account.name(), connId);
- subscribeAccountChannel(SUBSCRIBE);
- subscribeOrderInfoChannel(SUBSCRIBE);
- subscribePositionChannel(SUBSCRIBE);
- } else {
- log.error("{}: WebSocket登录失败, code: {}, msg: {}", account.name(), code, response.getString("msg"));
- }
- } else if ("subscribe".equals(event)) {
- subscribeEvent(response);
- } else if ("error".equals(event)) {
- log.error("{}: 订阅错误: code={}, msg={}",
- account.name(), response.getString("code"), response.getString("msg"));
- } else if ("channel-conn-count".equals(event)) {
- log.info("{}: 连接限制更新: channel={}, connCount={}",
- account.name(), response.getString("channel"), response.getString("connCount"));
- } else {
- processPushData(response);
- }
- } catch (Exception e) {
- log.error("{}: 处理WebSocket消息失败: {}", account.name(), message, e);
- }
- }
-
- private void subscribeEvent(JSONObject response) {
- JSONObject arg = response.getJSONObject("arg");
- if (arg == null) {
- log.warn("无效的推送数据,缺少 'arg' 字段 :{}",response);
- return;
- }
-
- String channel = arg.getString("channel");
- if (channel == null) {
- log.warn("无效的推送数据,缺少 'channel' 字段{}",response);
- return;
- }
- if (OrderInfoWs.ORDERINFOWS_CHANNEL.equals(channel)) {
- OrderInfoWs.initEvent(response, account.name());
- }
- if (AccountWs.ACCOUNTWS_CHANNEL.equals(channel)) {
- AccountWs.initEvent(response, account.name());
- }
- if (PositionsWs.POSITIONSWS_CHANNEL.equals(channel)) {
- PositionsWs.initEvent(response, account.name());
- }
- }
-
- /**
- * 解析并处理价格推送数据。
- * 将最新的标记价格存入 Redis 并触发后续业务逻辑比较处理。
- *
- * @param response 包含价格数据的 JSON 对象
- */
- private void processPushData(JSONObject response) {
- String op = response.getString("op");
- if (op != null){
- if (TradeOrderWs.ORDERWS_CHANNEL.equals(op)) {
- // 直接使用Object类型接收,避免强制类型转换
- Object data = response.get("data");
- log.info("{}: 收到下单推送结果: {}", account.name(), JSON.toJSONString(data));
- return;
- }
- }
- JSONObject arg = response.getJSONObject("arg");
- if (arg == null) {
- log.warn("{}: 无效的推送数据,缺少 'arg' 字段 :{}", account.name(), response);
- return;
- }
-
- String channel = arg.getString("channel");
- if (channel == null) {
- log.warn("{}: 无效的推送数据,缺少 'channel' 字段{}", account.name(), response);
- return;
- }
-
- // 注意:当前实现中,OrderInfoWs等类使用静态Map存储数据
- // 这会导致多账号之间的数据冲突。需要进一步修改这些类的设计,让数据存储与特定账号关联
- if (OrderInfoWs.ORDERINFOWS_CHANNEL.equals(channel)) {
-// OrderInfoWs.handleEvent(response, redisUtils, account.name());
- List<TradeRequestParam> tradeRequestParams = OrderInfoWs.handleEvent(response, redisUtils, account.name());
- TradeOrderWs.orderZhiYingZhiSunEventNoState(webSocketClient, tradeRequestParams);
- }else if (AccountWs.ACCOUNTWS_CHANNEL.equals(channel)) {
- AccountWs.handleEvent(response, account.name());
- } else if (PositionsWs.POSITIONSWS_CHANNEL.equals(channel)) {
- PositionsWs.handleEvent(response, account.name());
- } else if (BalanceAndPositionWs.CHANNEL_NAME.equals(channel)) {
- BalanceAndPositionWs.handleEvent(response);
- }
- }
-
- /**
- * 启动心跳检测任务。
- * 使用 ScheduledExecutorService 定期检查是否需要发送 ping 请求来维持连接。
- */
- private void startHeartbeat() {
- if (heartbeatExecutor != null && !heartbeatExecutor.isTerminated()) {
- heartbeatExecutor.shutdownNow();
- }
-
- heartbeatExecutor = Executors.newSingleThreadScheduledExecutor(r -> {
- Thread t = new Thread(r, "okx-account-order-heartbeat");
- t.setDaemon(true);
- return t;
- });
-
- heartbeatExecutor.scheduleWithFixedDelay(this::checkHeartbeatTimeout,
- HEARTBEAT_TIMEOUT, HEARTBEAT_TIMEOUT, TimeUnit.SECONDS);
- }
-
- // 移除了 schedulePeriodicReconnect 方法
-
- /**
- * 重置心跳计时器。
- * 当收到新消息或发送 ping 后取消当前超时任务并重新安排下一次超时检查。
- */
- private void resetHeartbeatTimer() {
- cancelPongTimeout();
-
- // 检查线程池状态,避免在关闭过程中提交任务
- if (heartbeatExecutor != null && !heartbeatExecutor.isShutdown()) {
- pongTimeoutFuture = heartbeatExecutor.schedule(this::checkHeartbeatTimeout,
- HEARTBEAT_TIMEOUT, TimeUnit.SECONDS);
- }
- }
-
- // 移除了 performScheduledReconnect 方法
-
- /**
- * 检查心跳超时情况。
- * 若长时间未收到任何消息则主动发送 ping 请求保持连接活跃。
- */
- private void checkHeartbeatTimeout() {
- // 只有在连接状态下才检查心跳
- if (!isConnected.get()) {
- return;
- }
-
- long currentTime = System.currentTimeMillis();
- long lastTime = lastMessageTime.get();
-
- if (currentTime - lastTime >= HEARTBEAT_TIMEOUT * 1000L) {
- sendPing();
- }
- }
-
- /**
- * 发送 ping 请求至 WebSocket 服务端。
- * 用于维持长连接有效性。
- */
- private void sendPing() {
- try {
- if (webSocketClient != null && webSocketClient.isOpen()) {
- webSocketClient.send("ping");
- log.debug("发送ping请求");
- }
- } catch (Exception e) {
- log.warn("发送ping失败", e);
- }
- }
-
- /**
- * 取消当前的心跳超时任务。
- * 在收到 pong 或其他有效消息时调用此方法避免不必要的断开重连。
- */
- private void cancelPongTimeout() {
- if (pongTimeoutFuture != null && !pongTimeoutFuture.isDone()) {
- pongTimeoutFuture.cancel(true);
- }
- }
-
- /**
- * 执行 WebSocket 重连操作。
- * 在连接意外中断后尝试重新建立连接。
- */
- private void reconnectWithBackoff() throws InterruptedException {
- // 如果正在连接,则不重复发起重连
- if (isConnecting.get()) {
- log.info("连接已在进行中,跳过重连请求");
- return;
- }
-
- int attempt = 0;
- int maxAttempts = 3;
- long delayMs = 1000;
-
- while (attempt < maxAttempts && !isConnected.get()) {
- try {
- Thread.sleep(delayMs);
- connect();
-
- // 等待连接建立
- for (int i = 0; i < 10 && isConnecting.get(); i++) {
- Thread.sleep(500);
- }
-
- if (isConnected.get()) {
- log.info("重连成功");
- return;
- }
- } catch (Exception e) {
- log.warn("第{}次重连失败", attempt + 1, e);
- delayMs *= 2;
- attempt++;
- }
- }
-
- log.error("超过最大重试次数({})仍未连接成功", maxAttempts);
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxWebSocketClientMain.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxWebSocketClientMain.java
deleted file mode 100644
index 9eebd2d..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxWebSocketClientMain.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice;
-
-import com.xcong.excoin.modules.okxNewPrice.celue.CaoZuoServiceImpl;
-import com.xcong.excoin.modules.okxNewPrice.wangge.WangGeServiceImpl;
-import com.xcong.excoin.utils.RedisUtils;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.support.ClassPathXmlApplicationContext;
-
-public class OkxWebSocketClientMain {
- public static void main(String[] args) throws InterruptedException {
- // 使用Spring上下文初始化管理器
- ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- OkxWebSocketClientManager manager = context.getBean(OkxWebSocketClientManager.class);
-
- // 运行一段时间以观察结果
- Thread.sleep(1200000000L); // 运行一小时
-
- // 关闭连接
- manager.destroy();
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxWebSocketClientManager.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxWebSocketClientManager.java
deleted file mode 100644
index c00f893..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxWebSocketClientManager.java
+++ /dev/null
@@ -1,132 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice;
-
-import com.xcong.excoin.modules.okxNewPrice.celue.CaoZuoService;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.ExchangeInfoEnum;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.wanggeList.WangGeListService;
-import com.xcong.excoin.utils.RedisUtils;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.stereotype.Component;
-
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * 管理多个OKX WebSocket客户端实例,每个实例对应一个账号
- */
-@Slf4j
-@Component
-@ConditionalOnProperty(prefix = "app", name = "quant", havingValue = "true")
-public class OkxWebSocketClientManager {
- @Autowired
- private CaoZuoService caoZuoService;
- @Autowired
- private RedisUtils redisUtils;
- @Autowired
- private WangGeListService wangGeListService;
-
- // 存储所有OkxQuantWebSocketClient实例,key为账号类型名称
- private final Map<String, OkxQuantWebSocketClient> quantClientMap = new ConcurrentHashMap<>();
-
- // 存储OkxNewPriceWebSocketClient实例
- private OkxKlineWebSocketClient klinePriceClient;
-
-
- /**
- * 初始化方法,在Spring Bean构造完成后执行
- * 创建并初始化所有账号的WebSocket客户端实例
- */
- @PostConstruct
- public void init() {
- log.info("开始初始化OkxWebSocketClientManager");
-
- // 初始化价格WebSocket客户端
- try {
- klinePriceClient = new OkxKlineWebSocketClient(redisUtils, caoZuoService, this, wangGeListService);
- klinePriceClient.init();
- log.info("已初始化OkxNewPriceWebSocketClient");
- } catch (Exception e) {
- log.error("初始化OkxNewPriceWebSocketClient失败", e);
- }
-
- // 获取所有ExchangeInfoEnum枚举值
- ExchangeInfoEnum[] accounts = ExchangeInfoEnum.values();
-
- // 为每个账号创建一个WebSocket客户端实例
- for (ExchangeInfoEnum account : accounts) {
- try {
- OkxQuantWebSocketClient client = new OkxQuantWebSocketClient(account, caoZuoService, redisUtils);
- quantClientMap.put(account.name(), client);
- client.init();
- log.info("已初始化账号 {} 的WebSocket客户端", account.name());
- } catch (Exception e) {
- log.error("初始化账号 {} 的WebSocket客户端失败", account.name(), e);
- }
- }
-
- log.info("OkxWebSocketClientManager初始化完成");
- }
-
- /**
- * 销毁方法,在Spring Bean销毁前执行
- * 关闭所有WebSocket客户端连接和相关资源
- */
- @PreDestroy
- public void destroy() {
- log.info("开始销毁OkxWebSocketClientManager");
-
- // 关闭价格WebSocket客户端
- if (klinePriceClient != null) {
- try {
- klinePriceClient.destroy();
- log.info("已销毁OkxNewPriceWebSocketClient");
- } catch (Exception e) {
- log.error("销毁OkxNewPriceWebSocketClient失败", e);
- }
- }
-
- // 关闭所有量化交易WebSocket客户端实例
- for (Map.Entry<String, OkxQuantWebSocketClient> entry : quantClientMap.entrySet()) {
- try {
- OkxQuantWebSocketClient client = entry.getValue();
- client.destroy();
- log.info("已销毁账号 {} 的WebSocket客户端", entry.getKey());
- } catch (Exception e) {
- log.error("销毁账号 {} 的WebSocket客户端失败", entry.getKey(), e);
- }
- }
-
- // 清空客户端映射
- quantClientMap.clear();
-
- log.info("OkxWebSocketClientManager销毁完成");
- }
-
- /**
- * 获取指定账号的OkxQuantWebSocketClient实例
- * @param accountName 账号类型名称
- * @return WebSocket客户端实例
- */
- public OkxQuantWebSocketClient getClient(String accountName) {
- return quantClientMap.get(accountName);
- }
-
- /**
- * 获取所有OkxQuantWebSocketClient实例
- * @return 所有客户端实例的集合
- */
- public Collection<OkxQuantWebSocketClient> getAllClients() {
- return quantClientMap.values();
- }
-
- /**
- * 获取OkxNewPriceWebSocketClient实例
- * @return 价格WebSocket客户端实例
- */
- public OkxKlineWebSocketClient getKlineWebSocketClient() {
- return klinePriceClient;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/README.md b/src/main/java/com/xcong/excoin/modules/okxNewPrice/README.md
deleted file mode 100644
index cb6e715..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/README.md
+++ /dev/null
@@ -1,285 +0,0 @@
-# OKX 新价格量化交易模块文档
-
-## 1. 包结构
-
-```
-okxNewPrice/
-├── celue/ # 策略层
-│ ├── CaoZuoService.java # 操作服务接口
-│ └── CaoZuoServiceImpl.java # 操作服务实现
-├── jiaoyi/ # 交易层
-│ ├── IMQService.java # 消息队列服务接口
-│ └── IMQServiceImpl.java # 消息队列服务实现
-├── okxWs/ # OKX WebSocket 处理层
-│ ├── enums/ # WebSocket 相关枚举
-│ │ ├── CoinEnums.java # 币相关枚举
-│ │ ├── ExchangeInfoEnum.java # 交易所信息枚举
-│ │ └── OrderParamEnums.java # 订单参数枚举
-│ ├── wanggeList/ # 网格列表相关
-│ │ ├── WangGeListEnum.java # 网格枚举
-│ │ ├── WangGeListQueue.java # 网格队列
-│ │ ├── WangGeListService.java # 网格服务接口
-│ │ └── WangGeListServiceImpl.java # 网格服务实现
-│ ├── AccountWs.java # 账户信息处理
-│ ├── BalanceAndPositionWs.java # 余额和持仓处理
-│ ├── InstrumentsWs.java # 合约信息处理
-│ ├── LoginWs.java # 登录处理
-│ ├── OrderInfoWs.java # 订单信息处理
-│ ├── PositionsWs.java # 持仓信息处理
-│ └── TradeOrderWs.java # 交易订单处理
-├── okxpi/ # OKX API 相关
-│ ├── config/ # 配置相关
-│ ├── enumerates/ # 枚举
-│ ├── order/ # 订单相关
-│ ├── query/ # 查询相关
-│ ├── trade/ # 交易相关
-│ ├── verify/ # 验证相关
-│ └── ... # 其他API工具类
-├── utils/ # 工具类
-│ ├── FebsException.java # 异常类
-│ ├── FebsResponse.java # 响应类
-│ ├── SSLConfig.java # SSL配置
-│ ├── SignUtils.java # 签名工具
-│ ├── WsMapBuild.java # WebSocket Map构建工具
-│ └── WsParamBuild.java # WebSocket 参数构建工具
-├── wangge/ # 网格相关
-│ ├── WangGeEnum.java # 网格枚举
-│ ├── WangGeQueue.java # 网格队列
-│ ├── WangGeService.java # 网格服务接口
-│ └── WangGeServiceImpl.java # 网格服务实现
-├── zhanghu/ # 账户相关
-│ ├── ApiMessageServiceImpl.java # API消息服务实现
-│ ├── IApiMessageService.java # API消息服务接口
-│ └── ZhangHuEnum.java # 账户枚举
-├── OkxNewPriceWebSocketClient.java # 价格WebSocket客户端
-├── OkxQuantWebSocketClient.java # 量化WebSocket客户端
-├── OkxWebSocketClientMain.java # WebSocket客户端主类
-└── OkxWebSocketClientManager.java # WebSocket客户端管理器
-```
-
-## 2. 核心组件说明
-
-### 2.1 WebSocket 客户端管理
-
-#### OkxWebSocketClientManager
-- **作用**:统一管理所有 OKX WebSocket 客户端实例
-- **核心功能**:
- - 初始化价格 WebSocket 客户端和多账号量化客户端
- - 提供客户端的获取和销毁功能
- - 管理客户端生命周期
-- **关键方法**:
- - `init()`:初始化所有客户端
- - `destroy()`:销毁所有客户端
- - `getAllClients()`:获取所有量化客户端实例
-
-#### OkxNewPriceWebSocketClient
-- **作用**:价格 WebSocket 客户端,负责获取实时价格数据
-- **核心功能**:
- - 连接 OKX 公共 WebSocket 接口获取标记价格
- - 将价格数据保存到 Redis
- - 价格变化时触发量化操作
- - 支持心跳检测和自动重连
-- **关键方法**:
- - `init()`:初始化客户端
- - `destroy()`:销毁客户端
- - `processPushData()`:处理价格推送数据
- - `triggerQuantOperations()`:触发所有账号的量化操作
-
-#### OkxQuantWebSocketClient
-- **作用**:量化交易 WebSocket 客户端,每个账号对应一个实例
-- **核心功能**:
- - 连接 OKX 私有 WebSocket 接口
- - 处理账户、持仓、订单等私有数据
- - 支持多账号独立操作
- - 支持心跳检测和自动重连
-- **关键方法**:
- - `init()`:初始化客户端
- - `destroy()`:销毁客户端
- - `websocketLogin()`:登录 WebSocket
- - `subscribeChannels()`:订阅相关频道
-
-### 2.2 策略层
-
-#### CaoZuoService
-- **作用**:交易策略服务接口
-- **核心功能**:
- - 决定是否进行交易操作
- - 根据价格和网格信息决定交易方向
- - 处理多头和空头策略
-
-#### CaoZuoServiceImpl
-- **作用**:交易策略服务实现类
-- **核心功能**:
- - 检查账户和持仓状态
- - 根据当前价格获取对应的网格
- - 实现多头和空头的具体交易逻辑
- - 管理网格队列和交易决策
-- **关键方法**:
- - `caoZuo()`:主交易逻辑
- - `caoZuoLong()`:多头交易逻辑
- - `caoZuoShort()`:空头交易逻辑
-
-### 2.3 网格策略
-
-#### WangGeListEnum
-- **作用**:网格数据枚举,定义不同价格区间的网格参数
-- **核心参数**:
- - `name`:网格名称
- - `jiage_shangxian`:价格上限
- - `jiage_xiaxian`:价格下限
- - `jian_ju`:网格间距
- - `fang_xiang`:交易方向(long/short)
-- **关键方法**:
- - `getGridByPrice()`:根据价格获取对应的网格
-
-#### WangGeListService
-- **作用**:网格服务接口,提供网格相关操作
-- **核心功能**:
- - 初始化网格队列
- - 管理网格的开仓和平仓队列
-
-### 2.4 持仓管理
-
-#### PositionsWs
-- **作用**:持仓信息处理类
-- **核心功能**:
- - 管理持仓数据(双层 Map 结构:账号_方向 -> 数据)
- - 提供持仓数据的获取和更新方法
- - 支持多账号多方向持仓管理
-- **关键方法**:
- - `initAccountName()`:初始化带方向的账号名
- - `handleEvent()`:处理持仓数据推送
- - `getAccountMap()`:获取指定账号的持仓数据
-
-## 3. 工作流程
-
-### 3.1 系统初始化流程
-
-1. **客户端初始化**:
- - Spring 容器启动时,`OkxWebSocketClientManager` 自动初始化
- - 创建并初始化 `OkxNewPriceWebSocketClient` 实例
- - 为每个账号创建并初始化 `OkxQuantWebSocketClient` 实例
-
-2. **WebSocket 连接**:
- - `OkxNewPriceWebSocketClient` 连接公共价格 WebSocket
- - 每个 `OkxQuantWebSocketClient` 连接私有 WebSocket 并登录
- - 订阅相关频道(价格、账户、持仓、订单等)
-
-### 3.2 价格触发交易流程
-
-```
-┌─────────────────────────┐ ┌─────────────────────────┐
-│ OkxNewPriceWebSocketClient │ │ WangGeListEnum │
-│ └─ processPushData() │────▶│ └─ getGridByPrice() │
-└─────────────────────────┘ └─────────────────────────┘
- ▲ ▼
- │ ┌─────────────────────────┐
- │ │ CaoZuoServiceImpl │
- │ │ └─ caoZuo() │
- │ └─────────────────────────┘
- │ ▼
- │ ┌─────────────────────────┐
- │ │ TradeOrderWs │
- │ │ └─ orderEvent() │
- │ └─────────────────────────┘
- │ ▼
-┌────────┴─────────────────────────────────────────────┐
-│ OkxQuantWebSocketClient │
-│ └─ handleWebSocketMessage() │
-└──────────────────────────────────────────────────────┘
-```
-
-1. **价格接收**:
- - `OkxNewPriceWebSocketClient` 接收实时价格推送
- - 调用 `processPushData()` 处理价格数据
-
-2. **策略决策**:
- - 根据当前价格获取对应的网格参数(`WangGeListEnum.getGridByPrice()`)
- - 调用 `CaoZuoServiceImpl.caoZuo()` 进行策略决策
- - 根据网格方向调用对应的多头或空头策略
-
-3. **订单执行**:
- - 调用 `TradeOrderWs.orderEvent()` 执行交易订单
- - 通过 `OkxQuantWebSocketClient` 发送订单指令
-
-### 3.3 持仓数据管理流程
-
-1. **数据接收**:
- - `OkxQuantWebSocketClient` 接收持仓数据推送
- - 调用 `PositionsWs.handleEvent()` 处理持仓数据
-
-2. **数据存储**:
- - 使用双层 Map 存储持仓数据:`accountName_posSide -> data`
- - 支持多头和空头方向的独立存储
-
-3. **数据使用**:
- - 策略层通过 `PositionsWs.getAccountMap()` 获取持仓数据
- - 根据持仓数据和当前价格决定交易操作
-
-## 4. 关键特性
-
-### 4.1 多网格策略
-
-- **实现方式**:通过 `WangGeListEnum` 定义多个价格区间的网格
-- **核心功能**:
- - 每个网格可设置独立的交易方向(多头/空头)
- - 根据当前价格自动匹配对应的网格
- - 支持跨网格的仓位迁移和止损
-
-### 4.2 多账号管理
-
-- **实现方式**:每个账号对应一个 `OkxQuantWebSocketClient` 实例
-- **核心功能**:
- - 支持多个交易所账号独立操作
- - 每个账号可设置独立的交易参数
- - 账号间数据隔离,互不影响
-
-### 4.3 长/空头策略支持
-
-- **实现方式**:通过 `PositionsWs` 的双层 Map 结构
-- **核心功能**:
- - 支持多头和空头方向的独立持仓管理
- - 每个方向有独立的交易逻辑
- - 支持方向切换时的仓位调整
-
-### 4.4 自动重连和心跳机制
-
-- **实现方式**:在 WebSocket 客户端中实现
-- **核心功能**:
- - 定时发送心跳包维持连接
- - 连接断开时自动重连(指数退避策略)
- - 异常处理和资源清理
-
-## 5. 配置与扩展
-
-### 5.1 网格参数配置
-
-- **当前实现**:通过 `WangGeListEnum` 硬编码配置
-- **扩展建议**:
- - 将网格参数改为可配置项(数据库或配置文件)
- - 支持动态调整网格参数
- - 提供网格参数管理界面
-
-### 5.2 交易参数配置
-
-- **核心参数**:
- - 网格间距
- - 交易方向(多头/空头)
- - 止损点
- - 交易数量
-- **扩展建议**:
- - 支持每个账号独立配置交易参数
- - 提供参数优化建议
- - 支持回测功能
-
-## 6. 总结
-
-`okxNewPrice` 包是一个完整的 OKX 量化交易系统,具有以下特点:
-
-1. **模块化设计**:清晰的分层结构,便于维护和扩展
-2. **多账号支持**:每个账号独立运行,互不影响
-3. **多网格策略**:根据价格自动切换网格,支持多头和空头策略
-4. **实时响应**:基于 WebSocket 的实时数据推送和交易执行
-5. **高可靠性**:支持心跳检测、自动重连和异常处理
-
-该系统实现了从价格获取、策略决策到订单执行的完整流程,为量化交易提供了稳定可靠的基础架构。
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoService.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoService.java
deleted file mode 100644
index 645c35d..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoService.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.celue;
-
-import com.xcong.excoin.modules.okxNewPrice.okxWs.param.TradeRequestParam;
-
-/**
- * @author Administrator
- */
-public interface CaoZuoService {
-
- TradeRequestParam caoZuoStrategy(String accountName, String markPx, String posSide);
-
- TradeRequestParam caoZuoHandler(String accountName, String markPx, String posSide);
-
- /**
- * 止损 事件
- * @param accountName
- * @param markPx
- * @param posSide
- * @return
- */
- TradeRequestParam caoZuoZhiSunEvent(String accountName, String markPx, String posSide);
-
- /**
- * 初始化 事件
- * @param accountName
- * @param markPx
- * @param posSide
- * @return
- */
- TradeRequestParam caoZuoInitEvent(String accountName, String markPx, String posSide);
-
- TradeRequestParam chooseEvent(TradeRequestParam tradeRequestParam);
-
- TradeRequestParam caoZuoLong(TradeRequestParam tradeRequestParam);
-
- TradeRequestParam caoZuoShort(TradeRequestParam tradeRequestParam);
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoServiceImpl.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoServiceImpl.java
deleted file mode 100644
index 9d6a2e2..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoServiceImpl.java
+++ /dev/null
@@ -1,613 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.celue;
-
-import cn.hutool.core.util.ObjectUtil;
-import cn.hutool.core.util.StrUtil;
-import com.xcong.excoin.modules.okxNewPrice.indicator.strategy.CoreTechnicalStrategy;
-import com.xcong.excoin.modules.okxNewPrice.indicator.strategy.TechnicalIndicatorStrategy;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.*;
-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.okxWs.wanggeList.WangGeListEnum;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.wanggeList.WangGeListQueue;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.wanggeList.WangGeListService;
-import com.xcong.excoin.modules.okxNewPrice.utils.WsMapBuild;
-import com.xcong.excoin.modules.okxNewPrice.utils.WsParamBuild;
-import com.xcong.excoin.rabbit.pricequeue.AscBigDecimal;
-import com.xcong.excoin.rabbit.pricequeue.DescBigDecimal;
-import com.xcong.excoin.utils.RedisUtils;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.Map;
-import java.util.concurrent.PriorityBlockingQueue;
-
-/**
- * 操作服务实现类,用于处理与交易相关的逻辑操作。
- * 包括根据市场行情判断是否进行加仓或减仓,并维护相关价格队列。
- *
- * @author Administrator
- */
-@Slf4j
-@Service
-@RequiredArgsConstructor
-public class CaoZuoServiceImpl implements CaoZuoService {
-
- private final WangGeListService wangGeListService;
- private final RedisUtils redisUtils;
-
- @Override
- public TradeRequestParam caoZuoStrategy(String accountName, String markPx, String posSide) {
- TradeRequestParam tradeRequestParam = new TradeRequestParam();
- tradeRequestParam.setAccountName(accountName);
- tradeRequestParam.setMarkPx(markPx);
- tradeRequestParam.setInstId(CoinEnums.HE_YUE.getCode());
- tradeRequestParam.setTdMode(CoinEnums.CROSS.getCode());
- tradeRequestParam.setPosSide(posSide);
- tradeRequestParam.setOrdType(CoinEnums.ORDTYPE_MARKET.getCode());
-
- log.info("操作账户:{},当前价格: {},仓位方向: {}", accountName,markPx,posSide);
- /**
- * 准备工作
- * 1、准备好下单的基本信息
- */
- // 系统设置的开关,等于冷静中,则代表不开仓
- String outStr = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.OUT.name());
- if (OrderParamEnums.OUT_YES.getValue().equals(outStr)){
- log.error("冷静中,不允许下单......");
- tradeRequestParam.setTradeType(OrderParamEnums.TRADE_NO.getValue());
- return tradeRequestParam;
- }
- BigDecimal cashBal = WsMapBuild.parseBigDecimalSafe(AccountWs.getAccountMap(accountName).get("cashBal"));
- /**
- * 判断止损抗压
- */
- BigDecimal realKuiSunAmount = WsMapBuild.parseBigDecimalSafe(AccountWs.getAccountMap(accountName).get("upl"));
- log.info("实际盈亏金额: {}", realKuiSunAmount);
- String zhiSunPercent = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.ZHI_SUN.name());
- BigDecimal zhiSunAmount = cashBal.multiply(new BigDecimal(zhiSunPercent));
- log.info("预期亏损金额: {}", zhiSunAmount);
- String kangYaPercent = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.KANG_CANG.name());
- BigDecimal kangYaAmount = cashBal.multiply(new BigDecimal(kangYaPercent));
- log.info("预期抗仓金额: {}", kangYaAmount);
-
- if (realKuiSunAmount.compareTo(BigDecimal.ZERO) < 0){
- realKuiSunAmount = realKuiSunAmount.multiply(new BigDecimal("-1"));
- // 账户预期亏损金额比这个还小时,立即止损
- if (realKuiSunAmount.compareTo(zhiSunAmount) > 0){
- log.error("账户冷静止损......");
- WsMapBuild.saveStringToMap(InstrumentsWs.getAccountMap(accountName), CoinEnums.OUT.name(), OrderParamEnums.OUT_YES.getValue());
- tradeRequestParam.setTradeType(OrderParamEnums.TRADE_YES.getValue());
- return caoZuoZhiSunEvent(accountName, markPx, posSide);
- }
- // 判断抗压
- if (realKuiSunAmount.compareTo(kangYaAmount) > 0 && realKuiSunAmount.compareTo(zhiSunAmount) <= 0){
- log.error("账户紧张扛仓......");
- tradeRequestParam.setTradeType(OrderParamEnums.TRADE_NO.getValue());
- return tradeRequestParam;
- }
- }
-
- String positionAccountName = PositionsWs.initAccountName(accountName, posSide);
- // 判断是否保证金超标
- if (PositionsWs.getAccountMap(positionAccountName).get("imr") == null){
- log.error("没有获取到持仓信息,等待初始化......");
- tradeRequestParam.setTradeType(OrderParamEnums.TRADE_NO.getValue());
- return tradeRequestParam;
- }
- BigDecimal ordFrozImr = PositionsWs.getAccountMap(positionAccountName).get("imr");
- BigDecimal totalOrderUsdt = WsMapBuild.parseBigDecimalSafe(AccountWs.getAccountMap(accountName).get(CoinEnums.TOTAL_ORDER_USDT.name()))
- .divide(new BigDecimal("2"), RoundingMode.DOWN);
- if (ordFrozImr.compareTo(totalOrderUsdt) >= 0){
- log.error("已满仓......");
- tradeRequestParam.setTradeType(OrderParamEnums.TRADE_NO.getValue());
- return tradeRequestParam;
- }
-
- if (PositionsWs.getAccountMap(positionAccountName).get("pos") == null){
- log.error("没有获取到持仓信息,等待初始化......");
- tradeRequestParam.setTradeType(OrderParamEnums.TRADE_NO.getValue());
- return tradeRequestParam;
- }
- tradeRequestParam.setTradeType(OrderParamEnums.TRADE_YES.getValue());
- return tradeRequestParam;
- }
-
- /**
- * 执行主要的操作逻辑,包括读取合约状态、获取市场价格信息,
- * 并根据当前持仓均价和标记价格决定是否执行买卖操作。
- *
- * @return 返回操作类型字符串(如买入BUY、卖出SELL等),如果无有效操作则返回null
- */
- @Override
- public TradeRequestParam caoZuoHandler(String accountName, String markPx, String posSide) {
- TradeRequestParam tradeRequestParam = new TradeRequestParam();
- tradeRequestParam.setAccountName(accountName);
- tradeRequestParam.setMarkPx(markPx);
- tradeRequestParam.setInstId(CoinEnums.HE_YUE.getCode());
- tradeRequestParam.setTdMode(CoinEnums.CROSS.getCode());
- tradeRequestParam.setPosSide(posSide);
- tradeRequestParam.setOrdType(CoinEnums.ORDTYPE_MARKET.getCode());
-
- log.info("操作账户:{},当前价格: {},仓位方向: {}", accountName,markPx,posSide);
- /**
- * 准备工作
- * 1、准备好下单的基本信息
- */
- // 系统设置的开关,等于冷静中,则代表不开仓
- String outStr = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.OUT.name());
- if (OrderParamEnums.OUT_YES.getValue().equals(outStr)){
- log.error("冷静中,不允许下单......");
- tradeRequestParam.setTradeType(OrderParamEnums.TRADE_NO.getValue());
- return chooseEvent(tradeRequestParam);
- }
- BigDecimal cashBal = WsMapBuild.parseBigDecimalSafe(AccountWs.getAccountMap(accountName).get("cashBal"));
- /**
- * 判断止损抗压
- */
- BigDecimal realKuiSunAmount = WsMapBuild.parseBigDecimalSafe(AccountWs.getAccountMap(accountName).get("upl"));
- log.info("实际盈亏金额: {}", realKuiSunAmount);
- String zhiSunPercent = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.ZHI_SUN.name());
- BigDecimal zhiSunAmount = cashBal.multiply(new BigDecimal(zhiSunPercent));
- log.info("预期亏损金额: {}", zhiSunAmount);
- String kangYaPercent = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.KANG_CANG.name());
- BigDecimal kangYaAmount = cashBal.multiply(new BigDecimal(kangYaPercent));
- log.info("预期抗仓金额: {}", kangYaAmount);
-
- if (realKuiSunAmount.compareTo(BigDecimal.ZERO) < 0){
- realKuiSunAmount = realKuiSunAmount.multiply(new BigDecimal("-1"));
- // 账户预期亏损金额比这个还小时,立即止损
- if (realKuiSunAmount.compareTo(zhiSunAmount) > 0){
- log.error("账户冷静止损......");
- WsMapBuild.saveStringToMap(InstrumentsWs.getAccountMap(accountName), CoinEnums.OUT.name(), OrderParamEnums.OUT_YES.getValue());
- tradeRequestParam.setTradeType(OrderParamEnums.TRADE_YES.getValue());
- return caoZuoZhiSunEvent(accountName, markPx, posSide);
- }
- // 判断抗压
- if (realKuiSunAmount.compareTo(kangYaAmount) > 0 && realKuiSunAmount.compareTo(zhiSunAmount) <= 0){
- log.error("账户紧张扛仓......");
- tradeRequestParam.setTradeType(OrderParamEnums.TRADE_NO.getValue());
- return chooseEvent(tradeRequestParam);
- }
- }
-
- String positionAccountName = PositionsWs.initAccountName(accountName, posSide);
- // 判断是否保证金超标
- if (PositionsWs.getAccountMap(positionAccountName).get("imr") == null){
- log.error("没有获取到持仓信息,等待初始化......");
- tradeRequestParam.setTradeType(OrderParamEnums.TRADE_NO.getValue());
- return chooseEvent(tradeRequestParam);
- }
- BigDecimal ordFrozImr = PositionsWs.getAccountMap(positionAccountName).get("imr");
- BigDecimal totalOrderUsdt = WsMapBuild.parseBigDecimalSafe(AccountWs.getAccountMap(accountName).get(CoinEnums.TOTAL_ORDER_USDT.name()))
- .divide(new BigDecimal("2"), RoundingMode.DOWN);
- if (ordFrozImr.compareTo(totalOrderUsdt) >= 0){
- log.error("已满仓......");
- tradeRequestParam.setTradeType(OrderParamEnums.TRADE_NO.getValue());
- return chooseEvent(tradeRequestParam);
- }
-
- if (PositionsWs.getAccountMap(positionAccountName).get("pos") == null){
- log.error("没有获取到持仓信息,等待初始化......");
- tradeRequestParam.setTradeType(OrderParamEnums.TRADE_NO.getValue());
- return chooseEvent(tradeRequestParam);
- }
- BigDecimal pos = PositionsWs.getAccountMap(positionAccountName).get("pos");
- if (BigDecimal.ZERO.compareTo( pos) >= 0) {
- log.error("持仓数量为零,进行初始化订单");
- return caoZuoInitEvent(accountName, markPx, posSide);
- }
-
- tradeRequestParam.setTradeType(OrderParamEnums.TRADE_YES.getValue());
- return chooseEvent(tradeRequestParam);
- }
-
- @Override
- public TradeRequestParam caoZuoZhiSunEvent(String accountName, String markPx, String posSide) {
-
- log.info("历史网格:操作账户:{},当前价格: {},仓位方向: {}", accountName,markPx,posSide);
- /**
- * 初始化订单请求参数
- * 获取仓位数量
- * 获取仓位方向
- */
- TradeRequestParam tradeRequestParam = new TradeRequestParam();
- tradeRequestParam.setAccountName(accountName);
- tradeRequestParam.setMarkPx(markPx);
- tradeRequestParam.setInstId(CoinEnums.HE_YUE.getCode());
- tradeRequestParam.setTdMode(CoinEnums.CROSS.getCode());
- tradeRequestParam.setPosSide(posSide);
- tradeRequestParam.setOrdType(CoinEnums.ORDTYPE_MARKET.getCode());
-
- tradeRequestParam.setTradeType(OrderParamEnums.TRADE_YES.getValue());
- String side = null;
- if (CoinEnums.POSSIDE_LONG.getCode().equals(posSide)){
- side = CoinEnums.SIDE_SELL.getCode();
- }
- if (CoinEnums.POSSIDE_SHORT.getCode().equals(posSide)){
- side = CoinEnums.SIDE_BUY.getCode();
- }
- tradeRequestParam.setSide(side);
-
- String clOrdId = WsParamBuild.getOrderNum(side);
- tradeRequestParam.setClOrdId(clOrdId);
-
- String positionAccountName = PositionsWs.initAccountName(accountName, posSide);
- BigDecimal pos = PositionsWs.getAccountMap(positionAccountName).get("pos");
- if (BigDecimal.ZERO.compareTo( pos) >= 0) {
- log.error("历史网格止损方向没有持仓");
- tradeRequestParam.setTradeType(OrderParamEnums.TRADE_NO.getValue());
- }
- tradeRequestParam.setSz(String.valueOf( pos));
- return tradeRequestParam;
-
- }
-
- @Override
- public TradeRequestParam caoZuoInitEvent(String accountName, String markPx, String posSide) {
-
- log.info("当前网格初始化:操作账户:{},当前价格: {},仓位方向: {}", accountName,markPx,posSide);
- /**
- * 初始化订单请求参数
- * 获取仓位数量
- * 获取仓位方向
- */
- TradeRequestParam tradeRequestParam = new TradeRequestParam();
- tradeRequestParam.setAccountName(accountName);
- tradeRequestParam.setMarkPx(markPx);
- tradeRequestParam.setInstId(CoinEnums.HE_YUE.getCode());
- tradeRequestParam.setTdMode(CoinEnums.CROSS.getCode());
- tradeRequestParam.setPosSide(posSide);
- tradeRequestParam.setOrdType(CoinEnums.ORDTYPE_MARKET.getCode());
-
- tradeRequestParam.setTradeType(OrderParamEnums.TRADE_YES.getValue());
- String side = null;
- if (CoinEnums.POSSIDE_LONG.getCode().equals(posSide)){
- side = CoinEnums.SIDE_BUY.getCode();
- }
- if (CoinEnums.POSSIDE_SHORT.getCode().equals(posSide)){
- side = CoinEnums.SIDE_SELL.getCode();
- }
- tradeRequestParam.setSide(side);
-
- String clOrdId = WsParamBuild.getOrderNum(side);
- tradeRequestParam.setClOrdId(clOrdId);
- String sz = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.BUY_CNT_INIT.name());
- tradeRequestParam.setSz(sz);
-
- WsMapBuild.saveStringToMap(OrderInfoWs.getAccountMap(accountName), "orderPrice", String.valueOf(markPx));
- return tradeRequestParam;
- }
-
- @Override
- public TradeRequestParam chooseEvent(TradeRequestParam tradeRequestParam) {
- log.info("开始执行chooseEvent......");
- if (OrderParamEnums.TRADE_NO.getValue().equals(tradeRequestParam.getTradeType())){
- return tradeRequestParam;
- }
- if (OrderParamEnums.TRADE_YES.getValue().equals(tradeRequestParam.getTradeType())){
- String posSide = tradeRequestParam.getPosSide();
- if (CoinEnums.POSSIDE_LONG.getCode().equals(posSide)){
- tradeRequestParam = caoZuoLong(tradeRequestParam);
- }else if (CoinEnums.POSSIDE_SHORT.getCode().equals(posSide)){
- tradeRequestParam = caoZuoShort(tradeRequestParam);
- }
- }
- return tradeRequestParam;
- }
-
- @Override
- public TradeRequestParam caoZuoLong(TradeRequestParam tradeRequestParam) {
- log.info("开始做{}执行操作CaoZuoServiceImpl......",tradeRequestParam.getPosSide());
-
- String accountName = tradeRequestParam.getAccountName();
- String markPxStr = tradeRequestParam.getMarkPx();
- String posSide = tradeRequestParam.getPosSide();
-
- try {
-
- String positionAccountName = PositionsWs.initAccountName(accountName, posSide);
- // 获取标记价格和平均持仓价格
- BigDecimal markPx = new BigDecimal(markPxStr);
- BigDecimal avgPx = PositionsWs.getAccountMap(positionAccountName).get("avgPx");
- log.info("持仓价格: {}, 当前价格:{},匹配队列中......", avgPx, markPx);
- // 初始化网格队列
- PriorityBlockingQueue<AscBigDecimal> queueAsc = WangGeListQueue.getQueueAsc();
- PriorityBlockingQueue<DescBigDecimal> queueKaiCang = wangGeListService.initKaiCang(avgPx, queueAsc);
- PriorityBlockingQueue<AscBigDecimal> queuePingCang = wangGeListService.initPingCang(avgPx, queueAsc);
-
- // 处理订单价格在队列中的情况
- String orderPrice = OrderInfoWs.getAccountMap(accountName).get("orderPrice");
- log.info("上一次网格触发价格: {}", orderPrice);
- handleOrderPriceInQueues(orderPrice, queueKaiCang, queuePingCang);
- // 判断是加仓还是减仓
- if (avgPx.compareTo(markPx) > 0) {
- log.info("开始买入开多...");
- if (!queueKaiCang.isEmpty()) {
- DescBigDecimal kaiCang = queueKaiCang.peek();
- log.info("买入开多队列价格{}", kaiCang.getValue());
- if (kaiCang != null && markPx.compareTo(kaiCang.getValue()) <= 0 && avgPx.compareTo(kaiCang.getValue()) >= 0) {
- log.info("开始买入开多...买入开多队列价格价格大于当前价格{}>{}", kaiCang.getValue(), markPx);
- WsMapBuild.saveStringToMap(OrderInfoWs.getAccountMap(accountName), "orderPrice", String.valueOf(markPx));
- String side = CoinEnums.SIDE_BUY.getCode();
- tradeRequestParam.setSide(side);
- String clOrdId = WsParamBuild.getOrderNum(side);
- tradeRequestParam.setClOrdId(clOrdId);
- String sz = buyCntTimeLongEvent(accountName, avgPx, markPx);
- tradeRequestParam.setSz(sz);
- log.info("买入开多参数准备成功......");
- } else {
- log.info("未触发加仓......,等待");
- }
- }else{
- // 队列为空
- log.info("超出了网格设置...");
- }
- } else if (avgPx.compareTo(markPx) < 0) {
- log.info("开始卖出平多...");
- if (!queuePingCang.isEmpty()) {
- AscBigDecimal pingCang = queuePingCang.peek();
- log.info("卖出平多队列价格:{}", pingCang.getValue());
- if (pingCang != null && avgPx.compareTo(pingCang.getValue()) < 0) {
- log.info("开始卖出平多...卖出平多队列价格大于开仓价格{}>{}", pingCang.getValue(), avgPx);
- // 手续费
- BigDecimal feeValue = PositionsWs.getAccountMap(positionAccountName).get("fee").multiply(new BigDecimal(2));
- //未实现收益
- BigDecimal uplValue = PositionsWs.getAccountMap(positionAccountName).get("upl");
- //已实现收益
- BigDecimal realizedPnlValue = PositionsWs.getAccountMap(positionAccountName).get("realizedPnl");
- realizedPnlValue = realizedPnlValue.add(feeValue);
-
- //持仓保证金
- BigDecimal imr = PositionsWs.getAccountMap(positionAccountName).get("imr");
- String pingCangImr = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.PING_CANG_SHOUYI.name());
- BigDecimal imrValue = imr.multiply(new BigDecimal(pingCangImr));
-
- if (realizedPnlValue.compareTo(BigDecimal.ZERO) <= 0) {
- BigDecimal realizedPnlValueZheng = realizedPnlValue.multiply(new BigDecimal("-1"));
- if (uplValue.compareTo(realizedPnlValue) > 0 && uplValue.compareTo(imrValue.add(realizedPnlValueZheng)) >= 0) {
- log.info("当前未实现盈亏:{}大于预计收益>{},赚钱咯", uplValue, imrValue.add(realizedPnlValueZheng));
- WsMapBuild.saveStringToMap(OrderInfoWs.getAccountMap(accountName), "orderPrice", String.valueOf(markPx));
- String side = CoinEnums.SIDE_SELL.getCode();
- tradeRequestParam.setSide(side);
- String clOrdId = WsParamBuild.getOrderNum(side);
- tradeRequestParam.setClOrdId(clOrdId);
- BigDecimal sz = PositionsWs.getAccountMap(positionAccountName).get("pos");
- tradeRequestParam.setSz(String.valueOf( sz));
- log.info("卖出平多参数准备成功......");
- }else{
- log.info("当前未实现盈亏:{}没有大于预计收益>{},钱在路上了", uplValue, imrValue.add(realizedPnlValueZheng));
- }
- }else {
- if (uplValue.compareTo(imrValue.add(feeValue)) >= 0) {
- log.info("当前未实现盈亏:{}大于预计收益>{},赚钱咯", uplValue, imrValue.add(feeValue));
- WsMapBuild.saveStringToMap(OrderInfoWs.getAccountMap(accountName), "orderPrice", String.valueOf(markPx));
- String side = CoinEnums.SIDE_SELL.getCode();
- tradeRequestParam.setSide(side);
- String clOrdId = WsParamBuild.getOrderNum(side);
- tradeRequestParam.setClOrdId(clOrdId);
- BigDecimal sz = PositionsWs.getAccountMap(positionAccountName).get("pos");
- tradeRequestParam.setSz(String.valueOf( sz));
- log.info("卖出平多参数准备成功......");
- }else{
- log.info("当前未实现盈亏:{}没有大于预计收益>{},钱在路上了", uplValue, imrValue.add(feeValue));
- }
- }
- } else {
- log.info("未触发减仓......,等待");
- }
- }else{
- // 队列为空
- log.info("超出了网格设置...");
- }
- } else {
- log.info("价格波动较小......,等待");
- }
- return tradeRequestParam;
- } catch (NumberFormatException e) {
- log.error("开多方向异常", e);
- return tradeRequestParam;
- }
- }
-
- @Override
- public TradeRequestParam caoZuoShort(TradeRequestParam tradeRequestParam) {
- log.info("开始做{}执行操作CaoZuoServiceImpl......",tradeRequestParam.getPosSide());
-
- String accountName = tradeRequestParam.getAccountName();
- String markPxStr = tradeRequestParam.getMarkPx();
- String posSide = tradeRequestParam.getPosSide();
-
- try {
- String positionAccountName = PositionsWs.initAccountName(accountName, posSide);
- // 获取标记价格和平均持仓价格
- BigDecimal markPx = new BigDecimal(markPxStr);
- BigDecimal avgPx = PositionsWs.getAccountMap(positionAccountName).get("avgPx");
- log.info("持仓价格: {}, 当前价格:{},匹配队列中......", avgPx, markPx);
-
- // 初始化网格队列
- PriorityBlockingQueue<AscBigDecimal> queueAsc = WangGeListQueue.getQueueAsc();
- PriorityBlockingQueue<DescBigDecimal> queueKaiCang = wangGeListService.initKaiCang(avgPx, queueAsc);
- PriorityBlockingQueue<AscBigDecimal> queuePingCang = wangGeListService.initPingCang(avgPx, queueAsc);
-
- // 处理订单价格在队列中的情况
- String orderPrice = OrderInfoWs.getAccountMap(accountName).get("orderPrice");
- log.info("上一次网格触发价格:{}", orderPrice);
- handleOrderPriceInQueues(orderPrice, queueKaiCang, queuePingCang);
- // 判断是加仓还是减仓
- if (avgPx.compareTo(markPx) > 0) {
- log.info("开始买入平空...");
- if (!queueKaiCang.isEmpty()) {
- DescBigDecimal kaiCang = queueKaiCang.peek();
- log.info("买入平空队列价格{}", kaiCang.getValue());
- if (kaiCang != null && avgPx.compareTo(kaiCang.getValue()) >= 0) {
- log.info("开始买入平空...买入平空队列价格小于开仓价格{}<{}", kaiCang.getValue(), avgPx);
-
- // 手续费
- BigDecimal feeValue = PositionsWs.getAccountMap(positionAccountName).get("fee").multiply(new BigDecimal("2"));
- //未实现收益
- BigDecimal uplValue = PositionsWs.getAccountMap(positionAccountName).get("upl");
- //已实现收益
- BigDecimal realizedPnlValue = PositionsWs.getAccountMap(positionAccountName).get("realizedPnl");
- realizedPnlValue = realizedPnlValue.add(feeValue);
-
- //持仓保证金
- BigDecimal imr = PositionsWs.getAccountMap(positionAccountName).get("imr");
- String pingCangImr = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.PING_CANG_SHOUYI.name());
- BigDecimal imrValue = imr.multiply(new BigDecimal(pingCangImr));
-
- if (realizedPnlValue.compareTo(BigDecimal.ZERO) <= 0) {
- BigDecimal realizedPnlValueZheng = realizedPnlValue.multiply(new BigDecimal("-1"));
- if (uplValue.compareTo(realizedPnlValue) > 0 && uplValue.compareTo(imrValue.add(realizedPnlValueZheng)) >= 0) {
- log.info("当前未实现盈亏:{}大于预计收益>{},赚钱咯", uplValue, imrValue.add(realizedPnlValueZheng));
- WsMapBuild.saveStringToMap(OrderInfoWs.getAccountMap(accountName), "orderPrice", String.valueOf(markPx));
- String side = CoinEnums.SIDE_BUY.getCode();
- tradeRequestParam.setSide(side);
- String clOrdId = WsParamBuild.getOrderNum(side);
- tradeRequestParam.setClOrdId(clOrdId);
- BigDecimal sz = PositionsWs.getAccountMap(positionAccountName).get("pos");
- tradeRequestParam.setSz(String.valueOf( sz));
- log.info("买入平空参数准备成功......");
- }else{
- log.info("当前未实现盈亏:{}没有大于预计收益>{},钱在路上了", uplValue, imrValue.add(realizedPnlValueZheng));
- }
- }else {
- if (uplValue.compareTo(imrValue.add(feeValue)) >= 0) {
- WsMapBuild.saveStringToMap(OrderInfoWs.getAccountMap(accountName), "orderPrice", String.valueOf(markPx));
- log.info("当前未实现盈亏:{}大于预计收益>{},赚钱咯", uplValue, imrValue.add(feeValue));
- String side = CoinEnums.SIDE_BUY.getCode();
- tradeRequestParam.setSide(side);
- String clOrdId = WsParamBuild.getOrderNum(side);
- tradeRequestParam.setClOrdId(clOrdId);
- BigDecimal sz = PositionsWs.getAccountMap(positionAccountName).get("pos");
- tradeRequestParam.setSz(String.valueOf( sz));
- log.info("买入平空参数准备成功......");
- }else{
- log.info("当前未实现盈亏:{}没有大于预计收益>{},钱在路上了", uplValue, imrValue.add(feeValue));
- }
- }
- } else {
- log.info("未触发减仓......,等待");
- }
- }else{
- log.info("开始减仓,但是超出了网格设置...");
- }
- } else if (avgPx.compareTo(markPx) < 0) {
- log.info("开始卖出开空...");
- if (!queuePingCang.isEmpty()) {
- AscBigDecimal pingCang = queuePingCang.peek();
- log.info("上限队列价格: {}", pingCang.getValue());
- if (pingCang != null && markPx.compareTo(pingCang.getValue()) >= 0 && avgPx.compareTo(pingCang.getValue()) < 0) {
- log.info("开始加仓...上限队列价格小于当前价格{}<={}", pingCang.getValue(), markPx);
- WsMapBuild.saveStringToMap(OrderInfoWs.getAccountMap(accountName), "orderPrice", String.valueOf(markPx));
- String side = CoinEnums.SIDE_SELL.getCode();
- tradeRequestParam.setSide(side);
- String clOrdId = WsParamBuild.getOrderNum(side);
- tradeRequestParam.setClOrdId(clOrdId);
- String sz = buyCntTimeShortEvent(accountName, avgPx, markPx);
- tradeRequestParam.setSz(sz);
- log.info("卖出开空参数准备成功......");
- } else {
- log.info("未触发加仓......,等待");
- }
- }else{
- // 队列为空
- log.info("超出了网格设置...");
- }
- } else {
- log.info("价格波动较小......,等待");
- }
- return tradeRequestParam;
- } catch (NumberFormatException e) {
- log.error("开空方向异常", e);
- return tradeRequestParam;
- }
- }
-
- private String buyCntTimeLongEvent(String accountName, BigDecimal avgPx, BigDecimal markPx){
- //判断当前价格和开仓价格直接间隔除以间距,取整,获取的数量是否大于等于0,如果大于0,则下单基础张数*倍数
- String buyCntTime = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.BUY_CNT_TIME.name());
- log.info("倍数次数间隔{}", buyCntTime);
- BigDecimal subtract = avgPx.subtract(markPx);
- log.info("倍数价格差距{}", subtract);
- BigDecimal divide = subtract.divide(new BigDecimal(buyCntTime), 0, RoundingMode.DOWN).add(BigDecimal.ONE);
- log.info("倍数次数{}", divide);
- String buyCntInit = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.BUY_CNT_INIT.name());
- return String.valueOf(divide.multiply(new BigDecimal(buyCntInit)));
- }
-
- private String buyCntTimeShortEvent(String accountName, BigDecimal avgPx, BigDecimal markPx){
- //判断当前价格和开仓价格直接间隔除以间距,取整,获取的数量是否大于等于0,如果大于0,则下单基础张数*倍数
-
- String buyCntTime = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.BUY_CNT_TIME.name());
- log.info("倍数次数间隔{}", buyCntTime);
- BigDecimal subtract = markPx.subtract(avgPx);
- log.info("倍数价格差距{}", subtract);
- BigDecimal divide = subtract.divide(new BigDecimal(buyCntTime), 0, RoundingMode.DOWN).add(BigDecimal.ONE);
- log.info("倍数次数{}", divide);
- String buyCntInit = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.BUY_CNT_INIT.name());
- return String.valueOf(divide.multiply(new BigDecimal(buyCntInit)));
- }
-
- /**
- * 根据订单价格更新开仓和平仓队列的内容。
- * 若该价格不在对应队列中则加入;若已存在,则从队列中移除。
- *
- * @param orderPrice 订单价格
- * @param queueKaiCang 开仓价格优先队列(降序)
- * @param queuePingCang 平仓价格优先队列(升序)
- */
- private void handleOrderPriceInQueues(String orderPrice,
- PriorityBlockingQueue<DescBigDecimal> queueKaiCang,
- PriorityBlockingQueue<AscBigDecimal> queuePingCang) {
- if (orderPrice == null) {
- return;
- }
- log.info("需要移除的价格: {}", orderPrice);
-
- BigDecimal priceDecimal;
- try {
- priceDecimal = new BigDecimal(orderPrice);
- } catch (NumberFormatException ex) {
- log.warn("无效的价格格式: {}", orderPrice);
- return;
- }
-
- // 删除比该价格大的数据
- queueKaiCang.removeIf(item -> item.getValue().compareTo(priceDecimal) >= 0);
-
- // 打印开仓队列
- StringBuilder kaiCangStr = new StringBuilder();
- kaiCangStr.append("下限队列: [");
- boolean first = true;
- for (DescBigDecimal item : queueKaiCang) {
- if (!first) {
- kaiCangStr.append(", ");
- }
- kaiCangStr.append(item.getValue());
- first = false;
- }
- kaiCangStr.append("]");
- log.info(kaiCangStr.toString());
-
- // 删除比该价格小的数据
- queuePingCang.removeIf(item -> item.getValue().compareTo(priceDecimal) <= 0);
-
- // 打印平仓队列
- StringBuilder pingCangStr = new StringBuilder();
- pingCangStr.append("上限队列: [");
- first = true;
- for (AscBigDecimal item : queuePingCang) {
- if (!first) {
- pingCangStr.append(", ");
- }
- pingCangStr.append(item.getValue());
- first = false;
- }
- pingCangStr.append("]");
- log.info(pingCangStr.toString());
- }
-}
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
deleted file mode 100644
index 4308fcb..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/AdvancedMA.java
+++ /dev/null
@@ -1,129 +0,0 @@
-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交叉系统
- *
- * 作用:
- * 1. 基于三重EMA交叉系统识别趋势方向和强度
- * 2. 当9EMA > 21EMA > 55EMA时形成多头排列,提示上涨趋势
- * 3. 当9EMA < 21EMA < 55EMA时形成空头排列,提示下跌趋势
- * 4. 计算三线粘合度,自动过滤震荡行情
- *
- * 价格参数类型:
- * - 参数名称:prices
- * - 参数类型:List<BigDecimal>
- * - 参数说明:需要至少1个价格数据点用于计算,根据不同周期需求更多数据点
- *
- * 推荐时间粒度及优缺点:
- * 1. 5分钟(5m):
- * - 优点:适合短线三重EMA交叉策略
- * - 缺点:需要频繁监控,容易受短期波动影响
- * 2. 15分钟(15m):
- * - 优点:平衡了信号可靠性和反应速度
- * - 缺点:仍有一定噪音
- * 3. 1小时(1h):
- * - 优点:信号较为可靠,适合中期趋势跟踪
- * - 缺点:反应较慢
- * 4. 4小时(4h)及以上:
- * - 优点:趋势信号明确,适合长期持仓
- * - 缺点:反应滞后,入场点较晚
- */
-@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.info("三重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
deleted file mode 100644
index 7075f88..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/BOLL.java
+++ /dev/null
@@ -1,169 +0,0 @@
-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倍标准差
- *
- * 作用:
- * 1. 测量价格波动范围和市场宽度
- * 2. 价格突破上轨,提示超买或趋势加速
- * 3. 价格跌破下轨,提示超卖或趋势加速
- * 4. 轨道收窄,提示即将发生剧烈波动
- * 5. 价格回归轨道内,提示趋势可能反转
- *
- * 价格参数类型:
- * - 参数名称:prices
- * - 参数类型:List<BigDecimal>
- * - 参数说明:需要至少20个(默认周期)价格数据点用于计算
- *
- * 推荐时间粒度及优缺点:
- * 1. 1分钟(1m):
- * - 优点:反应迅速,适合超短线突破策略
- * - 缺点:布林带宽度窄,假突破多
- * 2. 5分钟(5m):
- * - 优点:布林带宽度适中,突破信号相对可靠
- * - 缺点:仍有一定假突破
- * 3. 15分钟(15m):
- * - 优点:适合日内交易,突破信号较为可靠
- * - 缺点:反应速度较慢
- * 4. 1小时(1h)及以上:
- * - 优点:布林带宽度稳定,突破信号可靠
- * - 缺点:反应滞后,不适合短线交易
- */
-@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.info("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/FifteenMinuteStrategyExample.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/FifteenMinuteStrategyExample.java
deleted file mode 100644
index ed1b226..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/FifteenMinuteStrategyExample.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.indicator;
-
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 15分钟交易策略使用示例
- * 展示如何使用FifteenMinuteTradingStrategy处理100个15分钟价格数据点
- */
-public class FifteenMinuteStrategyExample {
-
- public static void main(String[] args) {
- // 1. 创建策略实例
- FifteenMinuteTradingStrategy strategy = new FifteenMinuteTradingStrategy();
-
- // 2. 准备100个15分钟价格数据(这里使用模拟数据,用户可以替换为真实数据)
- List<BigDecimal> prices = generateSampleFifteenMinuteData();
- System.out.println("已加载 " + prices.size() + " 个15分钟价格数据点");
-
- // 3. 获取当前价格
- BigDecimal currentPrice = prices.get(prices.size() - 1);
- System.out.println("当前价格: " + currentPrice);
-
- // 4. 示例1:获取多空方向
- System.out.println("\n=== 多空方向分析 ===");
- FifteenMinuteTradingStrategy.Direction direction = strategy.getDirection(prices);
- System.out.println("当前市场方向: " + direction);
-
- // 5. 示例2:获取开仓平仓信号(假设当前没有持仓)
- System.out.println("\n=== 开仓平仓信号分析(无持仓)===");
- FifteenMinuteTradingStrategy.PositionSignal signal1 =
- strategy.getPositionSignal(prices, false, false);
- System.out.println("无持仓时的信号: " + signal1);
-
- // 6. 示例3:获取开仓平仓信号(假设当前持有多仓)
- System.out.println("\n=== 开仓平仓信号分析(持有多仓)===");
- FifteenMinuteTradingStrategy.PositionSignal signal2 =
- strategy.getPositionSignal(prices, true, false);
- System.out.println("持有多仓时的信号: " + signal2);
-
- // 7. 示例4:获取开仓平仓信号(假设当前持有空仓)
- System.out.println("\n=== 开仓平仓信号分析(持有空仓)===");
- FifteenMinuteTradingStrategy.PositionSignal signal3 =
- strategy.getPositionSignal(prices, false, true);
- System.out.println("持有空仓时的信号: " + signal3);
-
- // 8. 示例5:获取完整交易结果
- System.out.println("\n=== 完整交易结果分析 ===");
- FifteenMinuteTradingStrategy.TradingResult result =
- strategy.getTradingResult(prices, false, false);
- System.out.println("市场方向: " + result.getDirection());
- System.out.println("交易信号: " + result.getSignal());
- System.out.println("\n指标状态详情:");
- System.out.println(result.getIndicatorStatus());
- }
-
- /**
- * 生成模拟的15分钟价格数据(100个数据点)
- * 用户可以替换为真实的价格数据
- * @return 15分钟价格数据列表
- */
- private static List<BigDecimal> generateSampleFifteenMinuteData() {
- List<BigDecimal> prices = new ArrayList<>();
-
- // 模拟ETH价格数据(从2400开始,有一定波动)
- BigDecimal basePrice = new BigDecimal(2400);
-
- for (int i = 0; i < 100; i++) {
- // 添加一些随机波动,但保持整体上升趋势
- double random = (Math.random() - 0.48) * 10; // -5 到 5 的随机波动,略微偏向上行
- BigDecimal price = basePrice.add(new BigDecimal(random));
- prices.add(price.setScale(2, BigDecimal.ROUND_HALF_UP));
-
- // 整体缓慢上升
- basePrice = basePrice.add(new BigDecimal(0.2));
- }
-
- return prices;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/FifteenMinuteTradingExample.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/FifteenMinuteTradingExample.java
deleted file mode 100644
index d98eccf..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/FifteenMinuteTradingExample.java
+++ /dev/null
@@ -1,214 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.indicator;
-
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-
-/**
- * 15分钟交易策略示例
- * 演示如何使用交易策略与15分钟时间框架数据
- * 展示如何获取方向信号和交易信号
- */
-public class FifteenMinuteTradingExample {
-
- public static void main(String[] args) {
- // 创建交易策略
- TradingStrategy tradingStrategy = new TradingStrategy();
-
- // 生成100个15分钟价格数据点
- List<BigDecimal> prices = generateSampleFifteenMinuteData(100);
-
- // 生成对应的高、低、收盘价数据
- List<BigDecimal> high = generateHighPrices(prices);
- List<BigDecimal> low = generateLowPrices(prices);
- List<BigDecimal> close = new ArrayList<>(prices); // 使用价格作为收盘价
-
- // 生成成交量数据
- List<BigDecimal> volume = generateVolumeData(prices.size());
-
- // 获取最新价格
- BigDecimal currentPrice = prices.get(prices.size() - 1);
-
- // 生成多周期价格数据(5分钟、1小时、4小时)
- List<BigDecimal> fiveMinPrices = generateSampleFifteenMinuteData(100);
- List<BigDecimal> oneHourPrices = generateSampleFifteenMinuteData(100);
- List<BigDecimal> fourHourPrices = generateSampleFifteenMinuteData(100);
-
- // 其他参数
- BigDecimal fundingRate = new BigDecimal("0.001"); // 正常资金费率
- boolean hasLargeTransfer = false; // 无大额转账
- boolean hasUpcomingEvent = false; // 无即将到来的重大事件
-
- // 确定市场方向
- TradingStrategy.Direction direction = tradingStrategy.getDirection(prices, high, low, close, currentPrice);
- System.out.println("市场方向(15分钟): " + direction);
-
- // 检查当前持仓状态
- boolean hasLongPosition = false; // 示例:无当前做多持仓
- boolean hasShortPosition = false; // 示例:无当前做空持仓
-
- // 生成交易信号(开仓/平仓)
- TradingStrategy.SignalType signal = tradingStrategy.generateSignal(prices, high, low, close, volume, currentPrice,
- hasLongPosition, hasShortPosition,
- fiveMinPrices, oneHourPrices, fourHourPrices,
- fundingRate, hasLargeTransfer, hasUpcomingEvent);
- System.out.println("交易信号(15分钟): " + signal);
-
- // 显示指标状态用于分析
- System.out.println("\n指标状态:");
- System.out.println(tradingStrategy.getIndicatorStatus());
-
- // 计算动态杠杆
- BigDecimal dynamicLeverage = tradingStrategy.calculateDynamicLeverage(high, low, close);
- System.out.println("\n动态杠杆倍数: " + dynamicLeverage);
-
- // 基于信号模拟持仓变化
- if (signal == TradingStrategy.SignalType.BUY) {
- System.out.println("\n=== 执行开多操作 ===");
- hasLongPosition = true;
-
- // 演示三段式止盈策略
- BigDecimal entryPrice = currentPrice;
- BigDecimal positionSize = new BigDecimal(100);
- TradingStrategy.ProfitTakingResult profitTakingResult =
- tradingStrategy.calculateThreeStepProfitTaking(entryPrice, currentPrice, direction, positionSize);
- System.out.println("三段式止盈信号: " + profitTakingResult.getSignal());
- System.out.println("应平仓仓位大小: " + profitTakingResult.getClosePositionSize());
- } else if (signal == TradingStrategy.SignalType.SELL) {
- System.out.println("\n=== 执行开空操作 ===");
- hasShortPosition = true;
-
- // 演示三段式止盈策略
- BigDecimal entryPrice = currentPrice;
- BigDecimal positionSize = new BigDecimal(100);
- TradingStrategy.ProfitTakingResult profitTakingResult =
- tradingStrategy.calculateThreeStepProfitTaking(entryPrice, currentPrice, direction, positionSize);
- System.out.println("三段式止盈信号: " + profitTakingResult.getSignal());
- System.out.println("应平仓仓位大小: " + profitTakingResult.getClosePositionSize());
- } else if (signal == TradingStrategy.SignalType.CLOSE_BUY) {
- System.out.println("\n=== 执行平多操作 ===");
- hasLongPosition = false;
- } else if (signal == TradingStrategy.SignalType.CLOSE_SELL) {
- System.out.println("\n=== 执行平空操作 ===");
- hasShortPosition = false;
- } else {
- System.out.println("\n无需交易操作。");
- }
-
- // 现有做多持仓的模拟示例
- System.out.println("\n=== 现有做多持仓的模拟 ===");
- hasLongPosition = true;
- hasShortPosition = false;
- signal = tradingStrategy.generateSignal(prices, high, low, close, volume, currentPrice,
- hasLongPosition, hasShortPosition,
- fiveMinPrices, oneHourPrices, fourHourPrices,
- fundingRate, hasLargeTransfer, hasUpcomingEvent);
- System.out.println("有做多持仓时的交易信号: " + signal);
-
- // 现有做空持仓的模拟示例
- System.out.println("\n=== 现有做空持仓的模拟 ===");
- hasLongPosition = false;
- hasShortPosition = true;
- signal = tradingStrategy.generateSignal(prices, high, low, close, volume, currentPrice,
- hasLongPosition, hasShortPosition,
- fiveMinPrices, oneHourPrices, fourHourPrices,
- fundingRate, hasLargeTransfer, hasUpcomingEvent);
- System.out.println("有做空持仓时的交易信号: " + signal);
-
- // 模拟盈利场景演示三段式止盈
- System.out.println("\n=== 三段式止盈盈利场景演示 ===");
- BigDecimal entryPrice = new BigDecimal(2500.0);
- BigDecimal currentPriceProfit = new BigDecimal(2700.0); // 模拟盈利价格
- BigDecimal positionSize = new BigDecimal(100);
- TradingStrategy.ProfitTakingResult profitTakingResult =
- tradingStrategy.calculateThreeStepProfitTaking(entryPrice, currentPriceProfit, TradingStrategy.Direction.LONG, positionSize);
- System.out.println("入场价格: " + entryPrice);
- System.out.println("当前价格: " + currentPriceProfit);
- System.out.println("三段式止盈信号: " + profitTakingResult.getSignal());
- System.out.println("应平仓仓位大小: " + profitTakingResult.getClosePositionSize());
- }
-
- /**
- * 生成具有真实波动的15分钟价格数据
- * @param size 要生成的数据点数量
- * @return 价格数据列表
- */
- private static List<BigDecimal> generateSampleFifteenMinuteData(int size) {
- List<BigDecimal> prices = new ArrayList<>();
- Random random = new Random();
-
- // 以基础价格开始(ETH示例价格)
- BigDecimal basePrice = new BigDecimal(2500.0);
- prices.add(basePrice);
-
- // 生成具有真实波动的后续价格
- for (int i = 1; i < size; i++) {
- // 创建价格趋势(轻微上升偏向)
- BigDecimal trend = new BigDecimal(0.1).multiply(new BigDecimal(i));
- // 添加随机波动(每个周期±2%)
- BigDecimal volatility = new BigDecimal(random.nextDouble() * 0.04 - 0.02);
- // 计算新价格
- BigDecimal newPrice = basePrice.add(trend).multiply(BigDecimal.ONE.add(volatility));
- // 四舍五入到2位小数
- newPrice = newPrice.setScale(2, BigDecimal.ROUND_HALF_UP);
- prices.add(newPrice);
- }
-
- return prices;
- }
-
- /**
- * 生成最高价数据
- * @param prices 价格数据
- * @return 最高价数据列表
- */
- private static List<BigDecimal> generateHighPrices(List<BigDecimal> prices) {
- List<BigDecimal> high = new ArrayList<>();
- Random random = new Random();
-
- for (BigDecimal price : prices) {
- // 最高价比当前价格高0-2%
- BigDecimal highPrice = price.multiply(BigDecimal.ONE.add(new BigDecimal(random.nextDouble() * 0.02)));
- high.add(highPrice.setScale(2, BigDecimal.ROUND_HALF_UP));
- }
-
- return high;
- }
-
- /**
- * 生成最低价数据
- * @param prices 价格数据
- * @return 最低价数据列表
- */
- private static List<BigDecimal> generateLowPrices(List<BigDecimal> prices) {
- List<BigDecimal> low = new ArrayList<>();
- Random random = new Random();
-
- for (BigDecimal price : prices) {
- // 最低价比当前价格低0-2%
- BigDecimal lowPrice = price.multiply(BigDecimal.ONE.subtract(new BigDecimal(random.nextDouble() * 0.02)));
- low.add(lowPrice.setScale(2, BigDecimal.ROUND_HALF_UP));
- }
-
- return low;
- }
-
- /**
- * 生成成交量数据
- * @param size 数据点数量
- * @return 成交量数据列表
- */
- private static List<BigDecimal> generateVolumeData(int size) {
- List<BigDecimal> volume = new ArrayList<>();
- Random random = new Random();
-
- for (int i = 0; i < size; i++) {
- // 生成1000-10000之间的随机成交量
- BigDecimal vol = new BigDecimal(random.nextInt(9001) + 1000);
- volume.add(vol);
- }
-
- return volume;
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/FifteenMinuteTradingStrategy.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/FifteenMinuteTradingStrategy.java
deleted file mode 100644
index 6510022..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/FifteenMinuteTradingStrategy.java
+++ /dev/null
@@ -1,237 +0,0 @@
-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;
-
-/**
- * 15分钟时间粒度的交易策略实现
- * 专门针对100个15分钟数据点设计的策略,包含明确的多空方向选择和开仓平仓方法
- */
-@Slf4j
-public class FifteenMinuteTradingStrategy {
-
- @Getter
- @Setter
- public static class TradingResult {
- private Direction direction; // 多空方向
- private PositionSignal signal; // 开仓平仓信号
- private String indicatorStatus; // 指标状态
-
- public TradingResult(Direction direction, PositionSignal signal, String indicatorStatus) {
- this.direction = direction;
- this.signal = signal;
- this.indicatorStatus = indicatorStatus;
- }
- }
-
- public enum Direction {
- LONG, // 多头方向
- SHORT, // 空头方向
- RANGING // 震荡行情
- }
-
- public enum PositionSignal {
- OPEN_LONG, // 开多仓
- OPEN_SHORT, // 开空仓
- CLOSE_LONG, // 平多仓
- CLOSE_SHORT, // 平空仓
- HOLD, // 持有
- STAY_OUT // 观望
- }
-
- private final MA ma;
- private final AdvancedMA advancedMA;
- private final BOLL boll;
- private final KDJ kdj;
- private final MACD macd;
- private final RSI rsi;
-
- public FifteenMinuteTradingStrategy() {
- // 15分钟数据优化的参数配置
- this.ma = new MA();
- this.advancedMA = new AdvancedMA();
- this.boll = new BOLL(20, 2.0); // BOLL使用默认20周期
- this.kdj = new KDJ(9); // KDJ使用默认9周期
- this.macd = new MACD(); // MACD使用默认12/26/9周期
- this.rsi = new RSI(14); // RSI使用默认14周期
- }
-
- /**
- * 计算所有指标
- * @param prices 15分钟价格数据(至少100个数据点)
- */
- private void calculateIndicators(List<BigDecimal> prices) {
- ma.calculate(prices);
- advancedMA.calculateTripleEMA(prices);
- boll.calculate(prices);
- kdj.calculate(prices);
- macd.calculate(prices);
- rsi.calculate(prices);
- }
-
- /**
- * 判断市场是否处于震荡行情
- * @return 是否为震荡行情
- */
- private boolean isRangeMarket() {
- // AdvancedMA三线粘合 + RSI(40-60) + BOLL带宽收窄
- boolean isMaConverged = advancedMA.calculatePercent().compareTo(new BigDecimal(2)) < 0;
- boolean isRsiNeutral = rsi.getRsi().compareTo(new BigDecimal(40)) > 0 &&
- rsi.getRsi().compareTo(new BigDecimal(60)) < 0;
- boolean isBollNarrow = boll.calculateBandWidth().compareTo(new BigDecimal(0.05)) < 0;
-
- return isMaConverged && isRsiNeutral && isBollNarrow;
- }
-
- /**
- * 获取多空方向选择
- * @param prices 15分钟价格数据(100个数据点)
- * @return 多空方向
- */
- public Direction getDirection(List<BigDecimal> prices) {
- if (prices == null || prices.size() < 100) {
- throw new IllegalArgumentException("需要至少100个15分钟价格数据点");
- }
-
- calculateIndicators(prices);
-
- // 震荡过滤
- if (isRangeMarket()) {
- return Direction.RANGING;
- }
-
- BigDecimal currentPrice = prices.get(prices.size() - 1);
-
- // 多头信号判断:MA多头排列 + MACD金叉 + RSI(30-70) + BOLL价格在上轨与中轨之间
- boolean isLongSignal =
- ma.getEma5().compareTo(ma.getEma10()) > 0 && // MA5 > MA10
- ma.getEma10().compareTo(ma.getEma20()) > 0 && // MA10 > MA20
- macd.getDif().compareTo(macd.getDea()) > 0 && // MACD金叉
- rsi.getRsi().compareTo(new BigDecimal(30)) > 0 && rsi.getRsi().compareTo(new BigDecimal(70)) < 0 && // RSI(30-70)
- currentPrice.compareTo(boll.getMid()) > 0 && currentPrice.compareTo(boll.getUpper()) < 0; // BOLL价格在上轨与中轨之间
-
- // 空头信号判断:MA空头排列 + MACD死叉 + RSI(30-70) + BOLL价格在下轨与中轨之间
- boolean isShortSignal =
- ma.getEma5().compareTo(ma.getEma10()) < 0 && // MA5 < MA10
- ma.getEma10().compareTo(ma.getEma20()) < 0 && // MA10 < MA20
- macd.getDif().compareTo(macd.getDea()) < 0 && // MACD死叉
- rsi.getRsi().compareTo(new BigDecimal(30)) > 0 && rsi.getRsi().compareTo(new BigDecimal(70)) < 0 && // RSI(30-70)
- currentPrice.compareTo(boll.getMid()) < 0 && currentPrice.compareTo(boll.getLower()) > 0; // BOLL价格在下轨与中轨之间
-
- if (isLongSignal) {
- return Direction.LONG;
- } else if (isShortSignal) {
- return Direction.SHORT;
- } else {
- return Direction.RANGING;
- }
- }
-
- /**
- * 获取开仓平仓策略信号
- * @param prices 15分钟价格数据(100个数据点)
- * @param hasLongPosition 当前是否持有多仓
- * @param hasShortPosition 当前是否持有空仓
- * @return 开仓平仓信号
- */
- public PositionSignal getPositionSignal(List<BigDecimal> prices, boolean hasLongPosition, boolean hasShortPosition) {
- if (prices == null || prices.size() < 100) {
- throw new IllegalArgumentException("需要至少100个15分钟价格数据点");
- }
-
- calculateIndicators(prices);
-
- // 震荡过滤
- if (isRangeMarket()) {
- return PositionSignal.STAY_OUT;
- }
-
- BigDecimal currentPrice = prices.get(prices.size() - 1);
-
- // 开多信号:MA金叉 + MACD金叉 + KDJ金叉 + RSI中性 + 价格在BOLL中轨上方
- boolean shouldOpenLong =
- ma.getEma5().compareTo(ma.getEma20()) > 0 && // MA金叉(5日EMA上穿20日EMA)
- macd.getDif().compareTo(macd.getDea()) > 0 && macd.getMacdBar().compareTo(BigDecimal.ZERO) > 0 && // MACD金叉且柱状图为正
- kdj.isGoldenCross() && // KDJ金叉
- rsi.getRsi().compareTo(new BigDecimal(30)) > 0 && rsi.getRsi().compareTo(new BigDecimal(70)) < 0 && // RSI中性
- currentPrice.compareTo(boll.getMid()) > 0; // 价格在BOLL中轨上方
-
- // 开空信号:MA死叉 + MACD死叉 + KDJ死叉 + RSI中性 + 价格在BOLL中轨下方
- boolean shouldOpenShort =
- ma.getEma5().compareTo(ma.getEma20()) < 0 && // MA死叉(5日EMA下穿20日EMA)
- macd.getDif().compareTo(macd.getDea()) < 0 && macd.getMacdBar().compareTo(BigDecimal.ZERO) < 0 && // MACD死叉且柱状图为负
- kdj.isDeathCross() && // KDJ死叉
- rsi.getRsi().compareTo(new BigDecimal(30)) > 0 && rsi.getRsi().compareTo(new BigDecimal(70)) < 0 && // RSI中性
- currentPrice.compareTo(boll.getMid()) < 0; // 价格在BOLL中轨下方
-
- // 平多信号:MA死叉 + MACD死叉 + RSI超买 + 价格跌破BOLL中轨
- boolean shouldCloseLong =
- (ma.getEma5().compareTo(ma.getEma20()) < 0 && // MA死叉
- macd.getDif().compareTo(macd.getDea()) < 0 && // MACD死叉
- (rsi.isOverbought() || rsi.isExtremelyOverbought())) || // RSI超买
- currentPrice.compareTo(boll.getMid()) < 0; // 价格跌破BOLL中轨
-
- // 平空信号:MA金叉 + MACD金叉 + RSI超卖 + 价格突破BOLL中轨
- boolean shouldCloseShort =
- (ma.getEma5().compareTo(ma.getEma20()) > 0 && // MA金叉
- macd.getDif().compareTo(macd.getDea()) > 0 && // MACD金叉
- (rsi.isOversold() || rsi.isExtremelyOversold())) || // RSI超卖
- currentPrice.compareTo(boll.getMid()) > 0; // 价格突破BOLL中轨
-
- // 确定开仓信号
- if (shouldOpenLong && !hasLongPosition && !hasShortPosition) {
- return PositionSignal.OPEN_LONG;
- } else if (shouldOpenShort && !hasLongPosition && !hasShortPosition) {
- return PositionSignal.OPEN_SHORT;
- }
-
- // 确定平仓信号
- if (shouldCloseLong && hasLongPosition) {
- return PositionSignal.CLOSE_LONG;
- } else if (shouldCloseShort && hasShortPosition) {
- return PositionSignal.CLOSE_SHORT;
- }
-
- // 无信号
- return hasLongPosition || hasShortPosition ? PositionSignal.HOLD : PositionSignal.STAY_OUT;
- }
-
- /**
- * 综合获取交易结果
- * @param prices 15分钟价格数据(100个数据点)
- * @param hasLongPosition 当前是否持有多仓
- * @param hasShortPosition 当前是否持有空仓
- * @return 包含多空方向和开仓平仓信号的完整交易结果
- */
- public TradingResult getTradingResult(List<BigDecimal> prices, boolean hasLongPosition, boolean hasShortPosition) {
- Direction direction = getDirection(prices);
- PositionSignal signal = getPositionSignal(prices, hasLongPosition, hasShortPosition);
- String indicatorStatus = getIndicatorStatus();
-
- return new TradingResult(direction, signal, indicatorStatus);
- }
-
- /**
- * 获取当前指标状态
- * @return 指标状态字符串
- */
- private String getIndicatorStatus() {
- return String.format("MA5: %s, MA20: %s, " +
- "MACD-DIF: %s, MACD-DEA: %s, MACD-BAR: %s, " +
- "KDJ-K: %s, KDJ-D: %s, KDJ-J: %s, " +
- "RSI: %s, " +
- "BOLL-UP: %s, BOLL-MID: %s, BOLL-DN: %s, " +
- "AdvancedMA-Bullish: %s, Bearish: %s, Percent: %s",
- ma.getEma5(), ma.getEma20(),
- macd.getDif(), macd.getDea(), macd.getMacdBar(),
- kdj.getK(), kdj.getD(), kdj.getJ(),
- rsi.getRsi(),
- boll.getUpper(), boll.getMid(), boll.getLower(),
- advancedMA.isBullish(), advancedMA.isBearish(), advancedMA.calculatePercent());
- }
-
-}
\ No newline at end of file
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
deleted file mode 100644
index a3b80b7..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/IndicatorBase.java
+++ /dev/null
@@ -1,161 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.indicator;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Technical indicators base class, provides common calculation methods
- *
- * Indicator combination strategy:
- * 1. Trend judgment (MA/AdvancedMA/MACD): Determine the overall trend direction of prices
- * 2. Momentum confirmation (RSI/KDJ): Confirm the strength and sustainability of the current trend
- * 3. Volatility reference (BOLL): Determine reasonable price volatility range and breakthrough timing
- *
- * Long/Short direction selection logic:
- * - Long signal: MA bullish arrangement + MACD golden cross + RSI(30-70) + BOLL price between upper and middle band
- * - Short signal: MA bearish arrangement + MACD death cross + RSI(30-70) + BOLL price between lower and middle band
- * - Consolidation signal: AdvancedMA three-line convergence + RSI(40-60) + BOLL bandwidth narrowing
- *
- * Open and close position strategies:
- * - Open long: MA golden cross + MACD golden cross + KDJ golden cross + RSI(30-70) + price breaks through BOLL middle band
- * - Open short: MA death cross + MACD death cross + KDJ death cross + RSI(30-70) + price breaks below BOLL middle band
- * - Close long: MA death cross + MACD death cross + RSI overbought(>70) + price breaks below BOLL middle band
- * - Close short: MA golden cross + MACD golden cross + RSI oversold(<30) + price breaks through BOLL middle band
- */
-public abstract class IndicatorBase {
-
- /**
- * Calculate moving average
- * @param prices Price list
- * @param period Period
- * @return Moving average value
- */
- 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);
- }
-
- /**
- * Calculate exponential moving average
- * @param prices Price list
- * @param period Period
- * @param prevEMA Previous EMA value
- * @return Exponential moving average value
- */
- 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)));
- }
-
- /**
- * Calculate standard deviation
- * @param prices Price list
- * @param period Period
- * @return Standard deviation
- */
- 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);
- }
-
- /**
- * Calculate square root (simplified implementation)
- * @param value Input value
- * @return Square root
- */
- 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);
- }
-
- /**
- * Get recent price data
- * @param prices All price data
- * @param period Period
- * @return Recent period price data
- */
- 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());
- }
-
- /**
- * Calculate ATR (Average True Range)
- * @param high High price list
- * @param low Low price list
- * @param close Close price list
- * @param period Period
- * @return ATR value
- */
- protected BigDecimal calculateATR(List<BigDecimal> high, List<BigDecimal> low, List<BigDecimal> close, int period) {
- if (high == null || low == null || close == null ||
- high.size() < period || low.size() < period || close.size() < period) {
- return BigDecimal.ZERO;
- }
-
- List<BigDecimal> trList = new ArrayList<>();
- for (int i = 1; i < high.size(); i++) {
- BigDecimal trueRange = calculateTrueRange(high.get(i), low.get(i), close.get(i - 1));
- trList.add(trueRange);
- }
-
- // Use simple moving average to calculate ATR
- return calculateMA(trList, Math.min(period, trList.size()));
- }
-
- /**
- * Calculate True Range
- * @param high Current high price
- * @param low Current low price
- * @param prevClose Previous close price
- * @return True range
- */
- protected BigDecimal calculateTrueRange(BigDecimal high, BigDecimal low, BigDecimal prevClose) {
- BigDecimal h1 = high.subtract(low);
- BigDecimal h2 = high.subtract(prevClose).abs();
- BigDecimal h3 = low.subtract(prevClose).abs();
- return h1.max(h2).max(h3);
- }
-
- /**
- * Calculate normalized volatility (based on ATR)
- * @param close Close price list
- * @param atr ATR value
- * @return Normalized volatility (percentage)
- */
- protected BigDecimal normalizeVolatility(List<BigDecimal> close, BigDecimal atr) {
- if (close == null || close.size() == 0 || atr.compareTo(BigDecimal.ZERO) == 0) {
- return BigDecimal.ZERO;
- }
- return atr.divide(close.get(close.size() - 1), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100));
- }
-}
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
deleted file mode 100644
index 7148f4e..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/KDJ.java
+++ /dev/null
@@ -1,145 +0,0 @@
-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
- *
- * 作用:
- * 1. 衡量价格的超买超卖状态(K值>80超买,K值<20超卖)
- * 2. K线上穿D线形成金叉,提示买入信号
- * 3. K线下穿D线形成死叉,提示卖出信号
- * 4. J值反映市场的极端状态,J值>100或J值<0为极端行情
- *
- * 价格参数类型:
- * - 参数名称:prices
- * - 参数类型:List<BigDecimal>
- * - 参数说明:需要至少9个(默认周期)价格数据点用于计算
- *
- * 推荐时间粒度及优缺点:
- * 1. 1分钟(1m):
- * - 优点:反应迅速,适合超短线交易
- * - 缺点:K值波动剧烈,信号频繁且可靠性低
- * 2. 5分钟(5m):
- * - 优点:K值波动相对稳定,适合短线交易
- * - 缺点:仍有一定虚假信号
- * 3. 15分钟(15m):
- * - 优点:信号较为可靠,适合日内交易
- * - 缺点:反应速度较慢
- * 4. 1小时(1h)及以上:
- * - 优点:超买超卖信号明确,适合中期交易
- * - 缺点:反应滞后,不适合短线交易
- */
-@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.info("KDJ计算结果 - K: {}, D: {}, J: {}", k, d, j);
- }
-
- /**
- * 判断超买(J > 85)
- * @return 是否超买
- */
- public boolean isOverbought() {
- return j.compareTo(new BigDecimal(85)) > 0;
- }
-
- /**
- * 判断超卖(J < 15)
- * @return 是否超卖
- */
- public boolean isOversold() {
- return j.compareTo(new BigDecimal(15)) < 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
deleted file mode 100644
index 0da46e6..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MA.java
+++ /dev/null
@@ -1,232 +0,0 @@
-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)
- *
- * 作用:
- * 1. 平滑价格波动,识别趋势方向
- * 2. 短周期MA上穿长周期MA形成金叉,提示买入信号
- * 3. 短周期MA下穿长周期MA形成死叉,提示卖出信号
- * 4. 价格上穿/下穿MA线,也可作为买卖参考
- *
- * 价格参数类型:
- * - 参数名称:prices
- * - 参数类型:List<BigDecimal>
- * - 参数说明:需要至少1个价格数据点用于计算,根据不同周期需求更多数据点
- *
- * 推荐时间粒度及优缺点:
- * 1. 1分钟(1m):
- * - 优点:反应迅速,适合超短线交易
- * - 缺点:噪音多,容易产生虚假信号
- * 2. 5分钟(5m):
- * - 优点:平衡了反应速度和噪音过滤
- * - 缺点:仍有一定噪音
- * 3. 15分钟(15m):
- * - 优点:适合日内交易,信号相对可靠
- * - 缺点:反应速度较慢
- * 4. 1小时(1h)及以上:
- * - 优点:趋势信号明确,虚假信号少
- * - 缺点:反应滞后,不适合短线交易
- */
-@Slf4j
-@Getter
-@Setter
-public class MA extends IndicatorBase {
-
- // 默认周期
- public static final int DEFAULT_MA5 = 5;
- public static final int DEFAULT_MA10 = 10;
- public static final int DEFAULT_MA20 = 20;
- public static final int DEFAULT_MA30 = 30;
- public static final int DEFAULT_MA60 = 60;
-
- // 动态周期参数
- private int ma5Period;
- private int ma10Period;
- private int ma20Period;
- private int ma30Period;
- private int ma60Period;
-
- 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;
-
- // 构造函数使用默认周期
- public MA() {
- this.ma5Period = DEFAULT_MA5;
- this.ma10Period = DEFAULT_MA10;
- this.ma20Period = DEFAULT_MA20;
- this.ma30Period = DEFAULT_MA30;
- this.ma60Period = DEFAULT_MA60;
- }
-
- /**
- * 计算所有周期的MA指标(使用当前周期设置)
- * @param prices 价格列表
- */
- public void calculate(List<BigDecimal> prices) {
- calculate(prices, null);
- }
-
- /**
- * 计算所有周期的MA指标,并支持动态周期调整
- * @param prices 价格列表
- * @param volatility 标准化波动率(ATR百分比),用于动态调整周期
- */
- public void calculate(List<BigDecimal> prices, BigDecimal volatility) {
- if (prices == null || prices.size() < 1) {
- return;
- }
-
- // 如果提供了波动率,则动态调整周期
- if (volatility != null) {
- adjustPeriodsByVolatility(volatility);
- }
-
- // 计算SMA
- if (prices.size() >= ma5Period) {
- ma5 = calculateMA(prices, ma5Period);
- }
- if (prices.size() >= ma10Period) {
- ma10 = calculateMA(prices, ma10Period);
- }
- if (prices.size() >= ma20Period) {
- ma20 = calculateMA(prices, ma20Period);
- }
- if (prices.size() >= ma30Period) {
- ma30 = calculateMA(prices, ma30Period);
- }
- if (prices.size() >= ma60Period) {
- ma60 = calculateMA(prices, ma60Period);
- }
-
- // 计算EMA
- prevEma5 = calculateEMA(prices, ma5Period, prevEma5);
- ema5 = prevEma5;
-
- prevEma10 = calculateEMA(prices, ma10Period, prevEma10);
- ema10 = prevEma10;
-
- prevEma20 = calculateEMA(prices, ma20Period, prevEma20);
- ema20 = prevEma20;
-
- prevEma30 = calculateEMA(prices, ma30Period, prevEma30);
- ema30 = prevEma30;
-
- prevEma60 = calculateEMA(prices, ma60Period, prevEma60);
- ema60 = prevEma60;
-
- log.info("MA计算结果 - MA5({}): {}, MA10({}): {}, MA20({}): {}, MA30({}): {}, MA60({}): {}",
- ma5Period, ma5, ma10Period, ma10, ma20Period, ma20, ma30Period, ma30, ma60Period, ma60);
- log.info("EMA计算结果 - EMA5({}): {}, EMA10({}): {}, EMA20({}): {}, EMA30({}): {}, EMA60({}): {}",
- ma5Period, ema5, ma10Period, ema10, ma20Period, ema20, ma30Period, ema30, ma60Period, ema60);
- }
-
- /**
- * 根据波动率调整MA周期
- * @param volatility 标准化波动率(ATR百分比)
- */
- private void adjustPeriodsByVolatility(BigDecimal volatility) {
- // 根据波动率缩放均线周期
- // 3%、5%、8%作为ATR阈值
- BigDecimal lowVolatility = new BigDecimal(3);
- BigDecimal midVolatility = new BigDecimal(5);
- BigDecimal highVolatility = new BigDecimal(8);
-
- // 快速MA周期 (ma5)
- ma5Period = volatility.compareTo(lowVolatility) < 0 ? 10 : 6;
-
- // 中期MA周期 (ma10, ma20)
- ma10Period = volatility.compareTo(midVolatility) < 0 ? 10 : 8;
- ma20Period = volatility.compareTo(midVolatility) < 0 ? 21 : 13;
-
- // 长期MA周期 (ma30, ma60)
- ma30Period = volatility.compareTo(highVolatility) < 0 ? 30 : 24;
- ma60Period = volatility.compareTo(highVolatility) < 0 ? 50 : 34;
-
- log.info("根据波动率{}调整MA周期: ma5={}, ma10={}, ma20={}, ma30={}, ma60={}",
- volatility, ma5Period, ma10Period, ma20Period, ma30Period, ma60Period);
- }
-
- /**
- * 判断短期均线是否上穿长期均线(金叉)
- * @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
deleted file mode 100644
index 9b63ee2..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MACD.java
+++ /dev/null
@@ -1,241 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.indicator;
-
-import lombok.Getter;
-import lombok.Setter;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * MACD (Moving Average Convergence Divergence) 指标实现
- * 计算逻辑:
- * 1. 快线DIFF = EMA(Close, 12) - EMA(Close, 26)
- * 2. 慢线DEA = EMA(DIFF, 9)
- * 3. MACD柱状图 = (DIFF - DEA) * macdBarsMultiplier
- *
- * 核心概念:
- * 1. DIFF是EMA12与EMA26之间的距离,反映短期与长期趋势的差异
- * 2. 当DIFF > 0表示EMA12在EMA26上方,代表多头趋势
- * 3. 当DIFF < 0表示EMA12在EMA26下方,代表空头趋势
- * 4. DEA是DIFF的EMA平滑线,用于过滤DIFF的波动
- * 5. MACD柱状图通过放大倍数展示DIFF与DEA之间的关系,便于观察趋势变化
- *
- * 多空判断:
- * - 多头机会:DIFF在0轴上且MACD柱状图向上,股价同步上涨
- * - 空头机会:DIFF在0轴下且MACD柱状图向下,股价同步下跌
- *
- * 信号过滤:
- * - 可通过设置macdBarsSmoothingPeriod启用MACD柱状图平滑处理
- * - 平滑公式:MACD柱状图 = MA((DIFF - DEA) * macdBarsMultiplier, macdBarsSmoothingPeriod)
- * - 作用:消除柱状图杂讯,使信号更加清晰
- */
-@Getter
-@Setter
-public class MACD extends IndicatorBase {
-
- // 默认周期参数
- public static final int DEFAULT_FAST_PERIOD = 12;
- public static final int DEFAULT_SLOW_PERIOD = 26;
- public static final int DEFAULT_SIGNAL_PERIOD = 9;
-
- // 默认MACD柱状图放大倍数
- public static final int DEFAULT_MACDBARS_MULTIPLIER = 2;
- // 默认MACD柱状图平滑周期(0表示不平滑)
- public static final int DEFAULT_MACDBARS_SMOOTHING_PERIOD = 0;
-
- // 周期参数
- private int fastPeriod;
- private int slowPeriod;
- private int signalPeriod;
-
- // MACD柱状图参数
- private int macdBarsMultiplier; // MACD柱状图放大倍数
- private int macdBarsSmoothingPeriod; // MACD柱状图平滑周期(0表示不平滑)
-
- // MACD计算结果
- 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;
- private List<BigDecimal> difHistory = new ArrayList<>(); // 保存历史DIF值,用于计算DEA
- private List<BigDecimal> rawMacdBarHistory = new ArrayList<>(); // 保存原始MACD柱状图值,用于平滑处理
-
- // 最大保存的历史值数量
- private static final int MAX_HISTORY_SIZE = 50;
-
- // 构造函数使用默认参数
- public MACD() {
- this.fastPeriod = DEFAULT_FAST_PERIOD;
- this.slowPeriod = DEFAULT_SLOW_PERIOD;
- this.signalPeriod = DEFAULT_SIGNAL_PERIOD;
- this.macdBarsMultiplier = DEFAULT_MACDBARS_MULTIPLIER;
- this.macdBarsSmoothingPeriod = DEFAULT_MACDBARS_SMOOTHING_PERIOD;
- }
-
- /**
- * 计算MACD指标(使用当前周期设置)
- * @param prices 价格列表
- */
- public void calculate(List<BigDecimal> prices) {
- calculate(prices, null);
- }
-
- /**
- * 计算MACD指标,并支持动态周期调整
- * @param prices 价格列表
- * @param volatility 标准化波动率(百分比),用于动态调整周期
- */
- public void calculate(List<BigDecimal> prices, BigDecimal volatility) {
- if (prices == null || prices.isEmpty()) {
- return;
- }
-
- // 如果提供了波动率,则动态调整周期
- if (volatility != null) {
- adjustPeriodsByVolatility(volatility);
- }
-
-
-
- // 计算快速EMA (12日)
- prevFastEMA = calculateEMA(prices, fastPeriod, prevFastEMA);
-
- // 计算慢速EMA (26日)
- prevSlowEMA = calculateEMA(prices, slowPeriod, prevSlowEMA);
-
- // 计算DIF = EMA(12) - EMA(26)
- dif = prevFastEMA.subtract(prevSlowEMA).setScale(8, RoundingMode.HALF_UP);
-
- // 将新的DIF值添加到历史记录
- difHistory.add(dif);
- // 保持历史记录在合理范围内
- if (difHistory.size() > MAX_HISTORY_SIZE) {
- difHistory.remove(0);
- }
-
- // 计算DEA = EMA(DIFF, 9)
- calculateDEA();
-
- // 计算原始MACD柱状图值 = (DIF - DEA) * 放大倍数
- BigDecimal rawMacdBar = dif.subtract(dea).multiply(new BigDecimal(macdBarsMultiplier)).setScale(8, RoundingMode.HALF_UP);
-
- // 将原始MACD柱状图值添加到历史记录
- rawMacdBarHistory.add(rawMacdBar);
- // 保持历史记录在合理范围内
- if (rawMacdBarHistory.size() > MAX_HISTORY_SIZE) {
- rawMacdBarHistory.remove(0);
- }
-
- // 如果启用了平滑处理,则计算平滑后的MACD柱状图
- if (macdBarsSmoothingPeriod > 0) {
- macdBar = smoothMacdBars().setScale(8, RoundingMode.HALF_UP);
- } else {
- macdBar = rawMacdBar;
- }
-
-
- }
-
- /**
- * 计算DEA指标
- */
- private void calculateDEA() {
- int difCount = difHistory.size();
-
- // 如果没有足够的DIF历史值,无法计算有效的DEA
- if (difCount == 0) {
- dea = BigDecimal.ZERO;
- prevDea = null;
- return;
- }
-
- // 计算DEA = EMA(DIFF, signalPeriod)
- // 使用所有DIF历史值来计算初始EMA,然后使用最新值更新
- prevDea = calculateEMA(difHistory, signalPeriod, prevDea);
- dea = prevDea.setScale(8, RoundingMode.HALF_UP);
- }
-
- /**
- * 平滑MACD柱状图
- * @return 平滑后的MACD柱状图值
- */
- private BigDecimal smoothMacdBars() {
- int historyCount = rawMacdBarHistory.size();
-
- // 如果没有足够的历史数据,返回最新的原始值
- if (historyCount < macdBarsSmoothingPeriod) {
- return rawMacdBarHistory.get(historyCount - 1);
- }
-
- // 使用简单移动平均平滑MACD柱状图
- List<BigDecimal> recentMacdBars = rawMacdBarHistory.subList(historyCount - macdBarsSmoothingPeriod, historyCount);
- return calculateMA(recentMacdBars, macdBarsSmoothingPeriod);
- }
-
- /**
- * 根据波动率调整MACD周期参数
- * @param volatility 标准化波动率(百分比)
- */
- private void adjustPeriodsByVolatility(BigDecimal volatility) {
- // 波动率阈值
- BigDecimal volatilityThreshold = new BigDecimal(15);
-
- // 根据波动率调整MACD参数
- if (volatility.compareTo(volatilityThreshold) < 0) {
- // 低波动率环境,使用默认参数
- fastPeriod = DEFAULT_FAST_PERIOD;
- slowPeriod = DEFAULT_SLOW_PERIOD;
- signalPeriod = DEFAULT_SIGNAL_PERIOD;
- } else {
- // 高波动率环境,使用更灵敏的参数
- fastPeriod = 8;
- slowPeriod = 17;
- signalPeriod = 5;
- }
-
-
- }
-
- /**
- * 判断金叉信号(DIF上穿DEA)
- * @param previousDIF 上一个DIF值
- * @param previousDEA 上一个DEA值
- * @return 是否形成金叉
- */
- public boolean isGoldenCross(BigDecimal previousDIF, BigDecimal previousDEA) {
- return previousDIF != null && previousDEA != null &&
- previousDIF.compareTo(previousDEA) < 0 && dif.compareTo(dea) > 0;
- }
-
- /**
- * 判断死叉信号(DIF下穿DEA)
- * @param previousDIF 上一个DIF值
- * @param previousDEA 上一个DEA值
- * @return 是否形成死叉
- */
- public boolean isDeathCross(BigDecimal previousDIF, BigDecimal previousDEA) {
- return previousDIF != null && previousDEA != null &&
- previousDIF.compareTo(previousDEA) > 0 && dif.compareTo(dea) < 0;
- }
-
- /**
- * 重置MACD指标状态
- */
- public void reset() {
- dif = BigDecimal.ZERO;
- dea = BigDecimal.ZERO;
- macdBar = BigDecimal.ZERO;
- prevFastEMA = null;
- prevSlowEMA = null;
- prevDea = null;
- difHistory.clear();
- rawMacdBarHistory.clear();
-
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MACDTest.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MACDTest.java
deleted file mode 100644
index ec93614..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/MACDTest.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.indicator;
-
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * MACD Indicator Test Class
- * Used to verify the correctness of MACD calculation logic
- */
-public class MACDTest {
-
- public static void main(String[] args) {
- // Create MACD instance
- MACD macd = new MACD();
-
- // Set MACD bars parameters (optional, using default values here)
- macd.setMacdBarsMultiplier(2); // Default multiplier
- macd.setMacdBarsSmoothingPeriod(0); // No smoothing
-
- // Generate test price data (simple upward trend)
- List<BigDecimal> prices = generateTestPrices(30);
-
- System.out.println("=== MACD Indicator Test Start ===");
- System.out.println("Price count: " + prices.size());
- System.out.println("MACD parameters: fast=" + macd.getFastPeriod() + ", slow=" + macd.getSlowPeriod() + ", signal=" + macd.getSignalPeriod());
- System.out.println("MACD bars parameters: multiplier=" + macd.getMacdBarsMultiplier() + ", smoothing=" + macd.getMacdBarsSmoothingPeriod());
- System.out.println();
-
- // Calculate MACD
- macd.calculate(prices);
-
- // Output results
- System.out.println("=== MACD Calculation Results ===");
- System.out.println("DIF: " + macd.getDif());
- System.out.println("DEA: " + macd.getDea());
- System.out.println("MACD Bars: " + macd.getMacdBar());
- System.out.println();
-
- // Trend judgment
- System.out.println("=== Trend Judgment ===");
- if (macd.getDif().compareTo(BigDecimal.ZERO) > 0) {
- System.out.println("DIFF > 0: Bullish trend");
- } else if (macd.getDif().compareTo(BigDecimal.ZERO) < 0) {
- System.out.println("DIFF < 0: Bearish trend");
- } else {
- System.out.println("DIFF = 0: No trend");
- }
-
- // Test smoothing function
- testSmoothingFunction();
-
- System.out.println("=== MACD Indicator Test End ===");
- }
-
- /**
- * Generate test price data
- * @param count Number of data points
- * @return Price list
- */
- private static List<BigDecimal> generateTestPrices(int count) {
- List<BigDecimal> prices = new ArrayList<>();
- // Start from 100, simple upward trend with some random fluctuations
- BigDecimal basePrice = new BigDecimal(100);
- for (int i = 0; i < count; i++) {
- // Add random fluctuation between 0.1 and 0.5
- BigDecimal price = basePrice.add(new BigDecimal(Math.random() * 0.4 + 0.1));
- prices.add(price.setScale(2, BigDecimal.ROUND_HALF_UP));
- basePrice = price;
- }
- return prices;
- }
-
- /**
- * Test MACD bars smoothing function
- */
- private static void testSmoothingFunction() {
- System.out.println("=== MACD Bars Smoothing Function Test ===");
-
- MACD macd = new MACD();
- macd.setMacdBarsMultiplier(2);
- macd.setMacdBarsSmoothingPeriod(3); // 3-day smoothing
-
- // Generate test price data with more fluctuations
- List<BigDecimal> prices = generateVolatileTestPrices(30);
-
- macd.calculate(prices);
-
- System.out.println("Price count: " + prices.size());
- System.out.println("MACD parameters: fast=" + macd.getFastPeriod() + ", slow=" + macd.getSlowPeriod() + ", signal=" + macd.getSignalPeriod());
- System.out.println("MACD bars parameters: multiplier=" + macd.getMacdBarsMultiplier() + ", smoothing=" + macd.getMacdBarsSmoothingPeriod());
- System.out.println();
-
- System.out.println("Smoothed MACD Results:");
- System.out.println("DIF: " + macd.getDif());
- System.out.println("DEA: " + macd.getDea());
- System.out.println("MACD Bars: " + macd.getMacdBar());
- System.out.println();
- }
-
- /**
- * Generate test price data with more fluctuations
- * @param count Number of data points
- * @return Price list
- */
- private static List<BigDecimal> generateVolatileTestPrices(int count) {
- List<BigDecimal> prices = new ArrayList<>();
- // Start from 100 with more random fluctuations
- BigDecimal basePrice = new BigDecimal(100);
- for (int i = 0; i < count; i++) {
- // Add random fluctuation between -1.0 and 1.0
- BigDecimal price = basePrice.add(new BigDecimal(Math.random() * 2 - 1));
- prices.add(price.setScale(2, BigDecimal.ROUND_HALF_UP));
- basePrice = price;
- }
- return prices;
- }
-}
\ No newline at end of file
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
deleted file mode 100644
index 3e05082..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/RSI.java
+++ /dev/null
@@ -1,145 +0,0 @@
-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 + (平均上涨幅度 / 平均下跌幅度)))
- *
- * 作用:
- * 1. 衡量市场的相对强弱程度(0-100)
- * 2. 超买信号:RSI>70表示市场超买,可能回调
- * 3. 超卖信号:RSI<30表示市场超卖,可能反弹
- * 4. 极端超买:RSI>80表示市场极度超买
- * 5. 极端超卖:RSI<20表示市场极度超卖
- *
- * 价格参数类型:
- * - 参数名称:prices
- * - 参数类型:List<BigDecimal>
- * - 参数说明:需要至少15个(默认周期+1)价格数据点用于计算
- *
- * 推荐时间粒度及优缺点:
- * 1. 1分钟(1m):
- * - 优点:反应迅速,适合超短线交易
- * - 缺点:RSI波动剧烈,频繁进入超买超卖区域
- * 2. 5分钟(5m):
- * - 优点:RSI波动相对稳定,适合短线交易
- * - 缺点:仍有一定虚假超买超卖信号
- * 3. 15分钟(15m):
- * - 优点:超买超卖信号较为可靠,适合日内交易
- * - 缺点:反应速度较慢
- * 4. 1小时(1h)及以上:
- * - 优点:超买超卖信号明确,适合中期交易
- * - 缺点:反应滞后,不适合短线交易
- */
-@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.info("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/TradingStrategy.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/TradingStrategy.java
deleted file mode 100644
index 558ad79..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/TradingStrategy.java
+++ /dev/null
@@ -1,1029 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.indicator;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
-import lombok.extern.slf4j.Slf4j;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 交易策略实现
- * 展示如何为ETH合约交易(开仓/平仓)组合所有指标
- */
-@Slf4j
-public class TradingStrategy extends IndicatorBase {
-
- @Getter
- @Setter
- @AllArgsConstructor
- @NoArgsConstructor
- public static class StrategyConfig {
- private int maShortPeriod = 5; // 短期移动平均周期
- private int maLongPeriod = 20; // 长期移动平均周期
- private int rsiPeriod = 14; // RSI指标周期
- private int kdjPeriod = 9; // KDJ指标周期
- private int bollPeriod = 20; // 布林带周期
- private double bollK = 2.0; // 布林带标准差倍数
- private int atrPeriod = 14; // ATR计算周期
- private boolean enableDynamicParams = true; // 是否启用动态参数优化
- private boolean enableMultiTimeframeConfirm = true; // 是否启用多周期确认
- private int volumeMaPeriod = 20; // 成交量移动平均周期
- private boolean enableVolumeConfirm = true; // 是否启用成交量验证
-
- // 风险控制参数
- private BigDecimal baseLeverage = new BigDecimal(3); // 基础杠杆倍数
- private int volatilityThresholdPeriod = 30; // 波动率阈值计算周期(用于动态杠杆)
- private boolean enableDynamicLeverage = true; // 是否启用动态杠杆
- private boolean enableThreeStepProfitTaking = true; // 是否启用三段式止盈
- private boolean enableBlackSwanFilter = true; // 是否启用黑天鹅事件过滤
- }
-
- public enum Direction {
- LONG, // 做多方向信号
- SHORT, // 做空方向信号
- RANGING // 震荡市场
- }
-
- public enum SignalType {
- NONE, // 无信号
- BUY, // 开多信号
- SELL, // 开空信号
- CLOSE_BUY, // 平多信号
- CLOSE_SELL // 平空信号
- }
-
- private final StrategyConfig config;
- private final MA ma;
- private final AdvancedMA advancedMA;
- private final BOLL boll;
- private final KDJ kdj;
- private final MACD macd;
- private final RSI rsi;
-
- public TradingStrategy() {
- this(new StrategyConfig());
- }
-
- public TradingStrategy(StrategyConfig config) {
- this.config = config;
- this.ma = new MA();
- this.advancedMA = new AdvancedMA();
- this.boll = new BOLL(config.getBollPeriod(), config.getBollK());
- this.kdj = new KDJ(config.getKdjPeriod());
- this.macd = new MACD();
- this.rsi = new RSI(config.getRsiPeriod());
- }
-
- /**
- * 计算所有指标并生成交易信号
- * @param prices 价格数据
- * @param high 最高价列表
- * @param low 最低价列表
- * @param close 收盘价列表
- * @param volume 成交量列表
- * @param currentPrice 当前价格
- * @param hasLongPosition 是否当前持有做多仓位
- * @param hasShortPosition 是否当前持有做空仓位
- * @param fiveMinPrices 5分钟价格数据(多周期确认)
- * @param oneHourPrices 1小时价格数据(多周期确认)
- * @param fourHourPrices 4小时价格数据(多周期确认)
- * @param fundingRate 当前资金费率(用于黑天鹅过滤)
- * @param hasLargeTransfer 是否有大额转账(用于黑天鹅过滤)
- * @param hasUpcomingEvent 是否有即将到来的重大事件(用于黑天鹅过滤)
- * @return 交易信号
- */
- public SignalType generateSignal(List<BigDecimal> prices, List<BigDecimal> high,
- List<BigDecimal> low, List<BigDecimal> close,
- List<BigDecimal> volume, BigDecimal currentPrice,
- boolean hasLongPosition, boolean hasShortPosition,
- List<BigDecimal> fiveMinPrices,
- List<BigDecimal> oneHourPrices,
- List<BigDecimal> fourHourPrices,
- BigDecimal fundingRate,
- boolean hasLargeTransfer,
- boolean hasUpcomingEvent) {
- // 计算所有指标
- calculateIndicators(prices, high, low, close);
-
- // 检查是否为震荡市场,如果是,则执行区间交易策略
- if (isRangeMarket(prices)) {
- log.info("当前市场为震荡行情,执行区间交易策略");
- return generateRangeTradingSignal(currentPrice, volume, hasLongPosition, hasShortPosition);
- }
-
- // 黑天鹅事件过滤
- if (blackSwanFilter(fundingRate, hasLargeTransfer, hasUpcomingEvent)) {
- log.info("黑天鹅事件过滤触发,不产生信号");
- return SignalType.NONE;
- }
-
- // 开多信号
- if (shouldOpenLong(currentPrice, prices, volume, fiveMinPrices, oneHourPrices, fourHourPrices) && !hasLongPosition && !hasShortPosition) {
- log.info("生成买入信号");
- return SignalType.BUY;
- }
-
- // 开空信号
- if (shouldOpenShort(currentPrice, prices, volume, fiveMinPrices, oneHourPrices, fourHourPrices) && !hasLongPosition && !hasShortPosition) {
- log.info("生成卖出信号");
- return SignalType.SELL;
- }
-
- // 平多信号
- if (shouldCloseLong(currentPrice, volume, fiveMinPrices, oneHourPrices, fourHourPrices) && hasLongPosition) {
- log.info("生成平多信号");
- return SignalType.CLOSE_BUY;
- }
-
- // 平空信号
- if (shouldCloseShort(currentPrice, volume, fiveMinPrices, oneHourPrices, fourHourPrices) && hasShortPosition) {
- log.info("生成平空信号");
- return SignalType.CLOSE_SELL;
- }
-
- log.info("未生成信号");
- return SignalType.NONE;
- }
-
- /**
- * 多周期确认辅助方法(看涨)
- * @param fiveMinPrices 5分钟价格数据
- * @param oneHourPrices 1小时价格数据
- * @param fourHourPrices 4小时价格数据
- * @return 是否有足够的多周期确认
- */
- private boolean multiTimeframeConfirm(List<BigDecimal> fiveMinPrices,
- List<BigDecimal> oneHourPrices,
- List<BigDecimal> fourHourPrices) {
- if (!config.isEnableMultiTimeframeConfirm()) {
- return true; // 如果未启用多周期确认,则默认返回true
- }
-
- int confirmCount = 0;
-
- // 检查5分钟周期
- if (hasBullishTrend(fiveMinPrices)) {
- confirmCount++;
- }
-
- // 检查1小时周期
- if (hasBullishTrend(oneHourPrices)) {
- confirmCount++;
- }
-
- // 检查4小时周期
- if (hasBullishTrend(fourHourPrices)) {
- confirmCount++;
- }
-
- // 至少需要2个周期确认
- return confirmCount >= 2;
- }
-
- /**
- * 多周期确认辅助方法(看跌)
- * @param fiveMinPrices 5分钟价格数据
- * @param oneHourPrices 1小时价格数据
- * @param fourHourPrices 4小时价格数据
- * @return 是否有足够的多周期确认
- */
- private boolean multiTimeframeBearishConfirm(List<BigDecimal> fiveMinPrices,
- List<BigDecimal> oneHourPrices,
- List<BigDecimal> fourHourPrices) {
- if (!config.isEnableMultiTimeframeConfirm()) {
- return true; // 如果未启用多周期确认,则默认返回true
- }
-
- int confirmCount = 0;
-
- // 检查5分钟周期
- if (hasBearishTrend(fiveMinPrices)) {
- confirmCount++;
- }
-
- // 检查1小时周期
- if (hasBearishTrend(oneHourPrices)) {
- confirmCount++;
- }
-
- // 检查4小时周期
- if (hasBearishTrend(fourHourPrices)) {
- confirmCount++;
- }
-
- // 至少需要2个周期确认
- return confirmCount >= 2;
- }
-
- /**
- * 检查指定周期是否有看涨趋势
- * @param prices 价格数据
- * @return 是否有看涨趋势
- */
- private boolean hasBullishTrend(List<BigDecimal> prices) {
- if (prices == null || prices.size() < 20) {
- return false; // 数据不足
- }
-
- // 创建临时MA指标用于判断趋势
- MA tempMA = new MA();
- MACD tempMACD = new MACD();
-
- // 计算指标
- tempMA.calculate(prices);
- tempMACD.calculate(prices);
-
- // 优化后的多头趋势判断:
- // 1. MA多头排列:短期MA > 中期MA > 长期MA
- // 2. 所有均线向上发散
- // 3. MACD趋势:DIFF线在DEA线之上
- boolean isMaBullish = tempMA.getEma5().compareTo(tempMA.getEma10()) > 0 &&
- tempMA.getEma10().compareTo(tempMA.getEma20()) > 0;
-
- // 判断均线向上发散(简单方法:近期均线斜率为正)
- boolean isMaDivergingUp = calculateMaSlope(prices, 5) > 0 &&
- calculateMaSlope(prices, 10) > 0 &&
- calculateMaSlope(prices, 20) > 0;
-
- // MACD趋势判断
- boolean isMacdBullish = tempMACD.getDif().compareTo(tempMACD.getDea()) > 0;
-
- return isMaBullish && isMaDivergingUp && isMacdBullish;
- }
-
- /**
- * 检查指定周期是否有看跌趋势
- * @param prices 价格数据
- * @return 是否有看跌趋势
- */
- private boolean hasBearishTrend(List<BigDecimal> prices) {
- if (prices == null || prices.size() < 20) {
- return false; // 数据不足
- }
-
- // 创建临时MA指标用于判断趋势
- MA tempMA = new MA();
- MACD tempMACD = new MACD();
-
- // 计算指标
- tempMA.calculate(prices);
- tempMACD.calculate(prices);
-
- // 优化后的空头趋势判断:
- // 1. MA空头排列:短期MA < 中期MA < 长期MA
- // 2. 所有均线向下发散
- // 3. MACD趋势:DIFF线在DEA线之下
- boolean isMaBearish = tempMA.getEma5().compareTo(tempMA.getEma10()) < 0 &&
- tempMA.getEma10().compareTo(tempMA.getEma20()) < 0;
-
- // 判断均线向下发散(简单方法:近期均线斜率为负)
- boolean isMaDivergingDown = calculateMaSlope(prices, 5) < 0 &&
- calculateMaSlope(prices, 10) < 0 &&
- calculateMaSlope(prices, 20) < 0;
-
- // MACD趋势判断
- boolean isMacdBearish = tempMACD.getDif().compareTo(tempMACD.getDea()) < 0;
-
- return isMaBearish && isMaDivergingDown && isMacdBearish;
- }
-
- /**
- * 计算均线斜率
- * @param prices 价格数据
- * @param period 均线周期
- * @return 均线斜率(正数表示向上,负数表示向下)
- */
- private double calculateMaSlope(List<BigDecimal> prices, int period) {
- if (prices == null || prices.size() < period * 2) {
- return 0; // 数据不足
- }
-
- // 获取最近两个周期的均线值
- int endIndex = prices.size() - 1;
- int startIndex = endIndex - period + 1;
-
- // 计算当前周期的均线
- BigDecimal currentMa = calculateMA(prices.subList(startIndex, endIndex + 1), period);
-
- // 计算前一个周期的均线
- int prevStartIndex = startIndex - period;
- BigDecimal prevMa = calculateMA(prices.subList(prevStartIndex, startIndex), period);
-
- // 计算斜率(简单差值法)
- return currentMa.subtract(prevMa).doubleValue();
- }
-
- /**
- * 成交量验证辅助方法
- * 增强版:当前成交量需大于20周期均量的1.2倍,以过滤无量反弹/回调的假信号
- * @param volume 成交量列表
- * @return 是否通过成交量验证
- */
- private boolean volumeConfirm(List<BigDecimal> volume) {
- if (!config.isEnableVolumeConfirm() || volume == null || volume.size() < config.getVolumeMaPeriod()) {
- return true; // 如果未启用成交量验证或数据不足,则默认返回true
- }
-
- // 计算成交量移动平均
- BigDecimal volumeMA = calculateMA(volume, config.getVolumeMaPeriod());
- BigDecimal currentVolume = volume.get(volume.size() - 1);
-
- // 增强验证:成交量需要大于1.2倍均线
- return currentVolume.compareTo(volumeMA.multiply(new BigDecimal("1.2"))) > 0;
- }
-
- /**
- * 量价背离检测
- * 检测价格上涨/下跌但成交量萎缩的情况,或价格和成交量趋势不一致
- * @param prices 价格列表
- * @param volume 成交量列表
- * @return 是否存在量价背离
- */
- private boolean hasPriceVolumeDivergence(List<BigDecimal> prices, List<BigDecimal> volume) {
- if (!config.isEnableVolumeConfirm() || prices == null || volume == null || prices.size() < 3 || volume.size() < 3) {
- return false; // 如果未启用成交量验证或数据不足,则默认返回false
- }
-
- // 获取最近3个周期的价格和成交量
- BigDecimal currentPrice = prices.get(prices.size() - 1);
- BigDecimal prevPrice1 = prices.get(prices.size() - 2);
- BigDecimal prevPrice2 = prices.get(prices.size() - 3);
-
- BigDecimal currentVolume = volume.get(volume.size() - 1);
- BigDecimal prevVolume1 = volume.get(volume.size() - 2);
- BigDecimal prevVolume2 = volume.get(volume.size() - 3);
-
- // 计算价格趋势
- boolean priceTrendUp = currentPrice.compareTo(prevPrice1) > 0 && prevPrice1.compareTo(prevPrice2) > 0;
- boolean priceTrendDown = currentPrice.compareTo(prevPrice1) < 0 && prevPrice1.compareTo(prevPrice2) < 0;
-
- // 计算成交量趋势
- boolean volumeTrendUp = currentVolume.compareTo(prevVolume1) > 0 && prevVolume1.compareTo(prevVolume2) > 0;
- boolean volumeTrendDown = currentVolume.compareTo(prevVolume1) < 0 && prevVolume1.compareTo(prevVolume2) < 0;
-
- // 检测量价背离
- // 价格上涨但成交量萎缩
- boolean bullishDivergence = priceTrendUp && volumeTrendDown;
- // 价格下跌但成交量萎缩(通常是强势信号,不视为背离)
- // 价格下跌但成交量放大(可能是恐慌性抛售,视为背离)
- boolean bearishDivergence = priceTrendDown && volumeTrendUp;
-
- return bullishDivergence || bearishDivergence;
- }
-
- /**
- * 计算所有指标
- * @param prices 价格数据
- * @param high 最高价列表
- * @param low 最低价列表
- * @param close 收盘价列表
- */
- private void calculateIndicators(List<BigDecimal> prices, List<BigDecimal> high,
- List<BigDecimal> low, List<BigDecimal> close) {
- // 计算ATR和波动率
- BigDecimal atr = calculateATR(high, low, close, config.getAtrPeriod());
- BigDecimal volatility = normalizeVolatility(close, atr);
-
- // 使用动态参数计算指标
- if (config.isEnableDynamicParams()) {
- log.info("使用动态参数计算指标,波动率: {}", volatility);
- ma.calculate(prices, volatility);
- macd.calculate(prices, volatility);
- } else {
- ma.calculate(prices);
- macd.calculate(prices);
- }
-
- // 其他指标计算
- advancedMA.calculateTripleEMA(prices);
- boll.calculate(prices);
- kdj.calculate(prices);
- rsi.calculate(prices);
- }
-
- /**
- * 检查是否为震荡市场
- * @param prices 价格数据列表
- * @return 是否为震荡市场
- */
- private boolean isRangeMarket(List<BigDecimal> prices) {
- // 优化的震荡市场判断条件,放宽标准以减少RANGING信号频率
-
- // 1. 高级MA三线收敛(小于3%,放宽条件)
- boolean isMaConverged = advancedMA.calculatePercent().compareTo(new BigDecimal(3)) < 0;
-
- // 2. RSI中性区间(35-65,放宽条件)
- boolean isRsiNeutral = rsi.getRsi().compareTo(new BigDecimal(35)) > 0 &&
- rsi.getRsi().compareTo(new BigDecimal(65)) < 0;
-
- // 3. BOLL带宽收窄(小于0.06,放宽条件)
- boolean isBollNarrow = boll.calculateBandWidth().compareTo(new BigDecimal(0.06)) < 0;
-
- // 4. MACD柱状图趋近于0(多空力量平衡,放宽条件)
- boolean isMacdBalanced = macd.getMacdBar().abs().compareTo(new BigDecimal(0.02)) < 0;
-
- // 5. KDJ在中间区域波动(25-75,放宽条件)
- boolean isKdjNeutral = kdj.getK().compareTo(new BigDecimal(25)) > 0 &&
- kdj.getK().compareTo(new BigDecimal(75)) < 0 &&
- kdj.getD().compareTo(new BigDecimal(25)) > 0 &&
- kdj.getD().compareTo(new BigDecimal(75)) < 0 &&
- kdj.getJ().compareTo(new BigDecimal(20)) > 0 &&
- kdj.getJ().compareTo(new BigDecimal(80)) < 0;
-
- // 6. 价格波动范围较小(最近20根K线最高价与最低价的比值小于1.06,放宽条件)
- boolean isPriceVolatilityLow = calculatePriceVolatility(prices).compareTo(new BigDecimal(1.06)) < 0;
-
- // 综合判断:只需要满足部分条件即可,增加趋势信号的机会
- int rangeConditionsMet = 0;
- if (isMaConverged) {
- rangeConditionsMet++;
- }
- if (isRsiNeutral) {
- rangeConditionsMet++;
- }
- if (isBollNarrow) {
- rangeConditionsMet++;
- }
- if (isMacdBalanced) {
- rangeConditionsMet++;
- }
- if (isKdjNeutral) {
- rangeConditionsMet++;
- }
- if (isPriceVolatilityLow) {
- rangeConditionsMet++;
- }
-
- // 只有满足4个或以上条件才判定为震荡市场
- return rangeConditionsMet >= 4;
- }
-
- /**
- * 计算价格波动率(最近20根K线最高价与最低价的比值)
- * @param prices 价格数据列表
- * @return 价格波动率
- */
- private BigDecimal calculatePriceVolatility(List<BigDecimal> prices) {
- if (prices == null || prices.isEmpty()) {
- return BigDecimal.ONE;
- }
-
- List<BigDecimal> recentPrices = prices.subList(Math.max(0, prices.size() - 20), prices.size());
- if (recentPrices.isEmpty()) {
- return BigDecimal.ONE;
- }
-
- BigDecimal highest = recentPrices.stream().max(BigDecimal::compareTo).orElse(BigDecimal.ONE);
- BigDecimal lowest = recentPrices.stream().min(BigDecimal::compareTo).orElse(BigDecimal.ONE);
-
- return highest.divide(lowest, 10, RoundingMode.HALF_UP);
- }
-
-
-
- /**
- * 根据15分钟时间框架指标确定市场方向(做多/做空/震荡)
- * @param prices 价格数据列表
- * @param high 最高价列表
- * @param low 最低价列表
- * @param close 收盘价列表
- * @param currentPrice 当前价格
- * @return 市场方向
- */
- public Direction getDirection(List<BigDecimal> prices, List<BigDecimal> high,
- List<BigDecimal> low, List<BigDecimal> close,
- BigDecimal currentPrice) {
- // 计算所有指标
- calculateIndicators(prices, high, low, close);
-
- // 检查是否为震荡市场
- if (isRangeMarket(prices)) {
- return Direction.RANGING;
- }
-
- // 优化后的多头趋势条件:
- // 1. MA多头排列(短期MA > 中期MA > 长期MA)
- // 2. MACD趋势(DIFF在DEA之上,代表多头趋势)
- // 3. 价格在BOLL中轨上方
- // 4. RSI(50-65) 为健康多头区间
- boolean isMaBullish = ma.getEma5().compareTo(ma.getEma10()) > 0 &&
- ma.getEma10().compareTo(ma.getEma20()) > 0;
- boolean isMacdBullish = macd.getDif().compareTo(macd.getDea()) > 0;
- boolean isPriceAboveBollMid = currentPrice.compareTo(boll.getMid()) > 0;
- boolean isRsiBullish = rsi.getRsi().compareTo(new BigDecimal(50)) > 0 &&
- rsi.getRsi().compareTo(new BigDecimal(65)) < 0;
-
- // 检查多头信号(MA多头 + MACD金叉 + 价格在BOLL中轨上方 + RSI(50-65))
- if (isMaBullish && isMacdBullish && isPriceAboveBollMid && isRsiBullish) {
- return Direction.LONG;
- }
-
- // 优化后的空头趋势条件:
- // 1. MA空头排列(短期MA < 中期MA < 长期MA)
- // 2. MACD趋势(DIFF在DEA之下,代表空头趋势)
- // 3. 价格在BOLL中轨下方
- // 4. RSI(35-50) 为健康空头区间
- boolean isMaBearish = ma.getEma5().compareTo(ma.getEma10()) < 0 &&
- ma.getEma10().compareTo(ma.getEma20()) < 0;
- boolean isMacdBearish = macd.getDif().compareTo(macd.getDea()) < 0;
- boolean isPriceBelowBollMid = currentPrice.compareTo(boll.getMid()) < 0;
- boolean isRsiBearish = rsi.getRsi().compareTo(new BigDecimal(35)) > 0 &&
- rsi.getRsi().compareTo(new BigDecimal(50)) < 0;
-
- // 检查空头信号(MA空头 + MACD死叉 + 价格在BOLL中轨下方 + RSI(35-50))
- if (isMaBearish && isMacdBearish && isPriceBelowBollMid && isRsiBearish) {
- return Direction.SHORT;
- }
-
- // 如果没有明确方向,默认为震荡
- return Direction.RANGING;
- }
-
- /**
- * 根据优化建议检查是否应该开多仓位
- * 使用主次结合法:
- * - 主信号(趋势):MA多头排列 + MACD金叉
- * - 辅信号(入场点):价格回调至BOLL中轨附近获得支撑,且出现KDJ金叉或RSI从低位回升至50以上
- * @param currentPrice 当前价格
- * @param prices 价格数据
- * @param volume 成交量列表
- * @param fiveMinPrices 5分钟价格数据
- * @param oneHourPrices 1小时价格数据
- * @param fourHourPrices 4小时价格数据
- * @return 是否应该开多
- */
- private boolean shouldOpenLong(BigDecimal currentPrice, List<BigDecimal> prices, List<BigDecimal> volume,
- List<BigDecimal> fiveMinPrices,
- List<BigDecimal> oneHourPrices,
- List<BigDecimal> fourHourPrices) {
- // 主信号:趋势判断
- // MA多头排列(短期MA > 中期MA > 长期MA)
- boolean isMaBullish = ma.getEma5().compareTo(ma.getEma10()) > 0 &&
- ma.getEma10().compareTo(ma.getEma20()) > 0;
- // MACD趋势(DIFF在DEA之上)
- boolean isMacdBullish = macd.getDif().compareTo(macd.getDea()) > 0;
-
- // 如果主信号不满足,直接返回false
- if (!(isMaBullish && isMacdBullish)) {
- return false;
- }
-
- // 辅信号:入场点判断
- // 价格在BOLL中轨上方,且未触及上轨(避免追高)
- boolean isPriceInSafeZone = currentPrice.compareTo(boll.getMid()) > 0 &&
- currentPrice.compareTo(boll.getUpper()) < 0;
- // 价格靠近BOLL中轨
- boolean isPriceNearBollMid = currentPrice.compareTo(boll.getMid().multiply(new BigDecimal(0.98))) > 0 &&
- currentPrice.compareTo(boll.getMid().multiply(new BigDecimal(1.02))) < 0;
-
- // RSI:健康区间30-70,刚从50中线向上
- boolean isRsiHealthy = rsi.getRsi().compareTo(new BigDecimal(30)) > 0 &&
- rsi.getRsi().compareTo(new BigDecimal(70)) < 0;
- boolean isRsiAboveMid = rsi.getRsi().compareTo(new BigDecimal(50)) > 0;
-
- // KDJ:金叉或超卖区域
- boolean isKdjGoldenCross = kdj.isGoldenCross();
- boolean isKdjOversold = kdj.getJ().compareTo(new BigDecimal(15)) < 0;
-
- // 入场点条件:价格在安全区域或靠近中轨,且动量健康
- boolean isEntryPointValid = (isPriceInSafeZone || isPriceNearBollMid) &&
- (isRsiHealthy && isRsiAboveMid) &&
- (isKdjGoldenCross || isKdjOversold);
-
- // 成交量验证
- boolean isVolumeConfirmed = volumeConfirm(volume);
- // 量价背离检测
- boolean isPriceVolumeDivergence = hasPriceVolumeDivergence(prices, volume);
- // 多周期确认
- boolean isMultiTimeframeConfirmed = multiTimeframeConfirm(fiveMinPrices, oneHourPrices, fourHourPrices);
-
- return isEntryPointValid && isVolumeConfirmed && !isPriceVolumeDivergence && isMultiTimeframeConfirmed;
- }
-
- /**
- * 根据优化建议检查是否应该开空仓位
- * 使用主次结合法:
- * - 主信号(趋势):MA空头排列 + MACD死叉
- * - 辅信号(入场点):价格反弹至BOLL中轨附近遇阻,且出现KDJ死叉或RSI从高位回落至50以下
- * @param currentPrice 当前价格
- * @param prices 价格数据
- * @param volume 成交量列表
- * @param fiveMinPrices 5分钟价格数据
- * @param oneHourPrices 1小时价格数据
- * @param fourHourPrices 4小时价格数据
- * @return 是否应该开空
- */
- private boolean shouldOpenShort(BigDecimal currentPrice, List<BigDecimal> prices, List<BigDecimal> volume,
- List<BigDecimal> fiveMinPrices,
- List<BigDecimal> oneHourPrices,
- List<BigDecimal> fourHourPrices) {
- // 主信号:趋势判断
- // MA空头排列(短期MA < 中期MA < 长期MA)
- boolean isMaBearish = ma.getEma5().compareTo(ma.getEma10()) < 0 &&
- ma.getEma10().compareTo(ma.getEma20()) < 0;
- // MACD趋势(DIFF在DEA之下)
- boolean isMacdBearish = macd.getDif().compareTo(macd.getDea()) < 0;
-
- // 如果主信号不满足,直接返回false
- if (!(isMaBearish && isMacdBearish)) {
- return false;
- }
-
- // 辅信号:入场点判断
- // 价格在BOLL中轨下方,且未触及下轨(避免追空)
- boolean isPriceInSafeZone = currentPrice.compareTo(boll.getMid()) < 0 &&
- currentPrice.compareTo(boll.getLower()) > 0;
- // 价格靠近BOLL中轨
- boolean isPriceNearBollMid = currentPrice.compareTo(boll.getMid().multiply(new BigDecimal(0.98))) > 0 &&
- currentPrice.compareTo(boll.getMid().multiply(new BigDecimal(1.02))) < 0;
-
- // RSI:健康区间30-70,刚从50中线向下
- boolean isRsiHealthy = rsi.getRsi().compareTo(new BigDecimal(30)) > 0 &&
- rsi.getRsi().compareTo(new BigDecimal(70)) < 0;
- boolean isRsiBelowMid = rsi.getRsi().compareTo(new BigDecimal(50)) < 0;
-
- // KDJ:死叉或超买区域
- boolean isKdjDeathCross = kdj.isDeathCross();
- boolean isKdjOverbought = kdj.getJ().compareTo(new BigDecimal(85)) > 0;
-
- // 入场点条件:价格在安全区域或靠近中轨,且动量健康
- boolean isEntryPointValid = (isPriceInSafeZone || isPriceNearBollMid) &&
- (isRsiHealthy && isRsiBelowMid) &&
- (isKdjDeathCross || isKdjOverbought);
-
- // 成交量验证
- boolean isVolumeConfirmed = volumeConfirm(volume);
- // 量价背离检测
- boolean isPriceVolumeDivergence = hasPriceVolumeDivergence(prices, volume);
- // 多周期确认(看跌)
- boolean isMultiTimeframeConfirmed = multiTimeframeBearishConfirm(fiveMinPrices, oneHourPrices, fourHourPrices);
-
- return isEntryPointValid && isVolumeConfirmed && !isPriceVolumeDivergence && isMultiTimeframeConfirmed;
- }
-
- /**
- * 根据优化建议检查是否应该平多仓位
- * 采用分层止盈止损策略:
- * - 保护止损:价格跌破BOLL中轨
- * - 跟踪止损:价格有效跌破移动平均线
- * - 最终平仓:趋势反转信号(MA空头排列 + MACD死叉)
- * @param currentPrice 当前价格
- * @param volume 成交量列表
- * @param fiveMinPrices 5分钟价格数据
- * @param oneHourPrices 1小时价格数据
- * @param fourHourPrices 4小时价格数据
- * @return 是否应该平多
- */
- private boolean shouldCloseLong(BigDecimal currentPrice, List<BigDecimal> volume,
- List<BigDecimal> fiveMinPrices,
- List<BigDecimal> oneHourPrices,
- List<BigDecimal> fourHourPrices) {
- // 保护止损:价格跌破BOLL中轨(关键支撑位)
- boolean isStopLossTriggered = currentPrice.compareTo(boll.getMid()) < 0;
-
- // 跟踪止损:价格有效跌破短期均线(5EMA)
- boolean isTrailingStopTriggered = currentPrice.compareTo(ma.getEma5()) < 0;
-
- // 趋势反转信号:MA空头排列 + MACD死叉
- boolean isMaBearish = ma.getEma5().compareTo(ma.getEma10()) < 0 &&
- ma.getEma10().compareTo(ma.getEma20()) < 0;
- boolean isMacdBearish = macd.getDif().compareTo(macd.getDea()) < 0;
- boolean isTrendReversed = isMaBearish && isMacdBearish;
-
- // 多周期确认(看跌)
- boolean isMultiTimeframeConfirmed = multiTimeframeBearishConfirm(fiveMinPrices, oneHourPrices, fourHourPrices);
-
- // 平多条件:保护止损触发 或 跟踪止损触发 或 (趋势反转且多周期确认)
- return isStopLossTriggered || isTrailingStopTriggered || (isTrendReversed && isMultiTimeframeConfirmed);
- }
-
- /**
- * 根据优化建议检查是否应该平空仓位
- * 采用分层止盈止损策略:
- * - 保护止损:价格突破BOLL中轨
- * - 跟踪止损:价格有效突破移动平均线
- * - 最终平仓:趋势反转信号(MA多头排列 + MACD金叉)
- * @param currentPrice 当前价格
- * @param volume 成交量列表
- * @param fiveMinPrices 5分钟价格数据
- * @param oneHourPrices 1小时价格数据
- * @param fourHourPrices 4小时价格数据
- * @return 是否应该平空
- */
- private boolean shouldCloseShort(BigDecimal currentPrice, List<BigDecimal> volume,
- List<BigDecimal> fiveMinPrices,
- List<BigDecimal> oneHourPrices,
- List<BigDecimal> fourHourPrices) {
- // 保护止损:价格突破BOLL中轨(关键阻力位)
- boolean isStopLossTriggered = currentPrice.compareTo(boll.getMid()) > 0;
-
- // 跟踪止损:价格有效突破短期均线(5EMA)
- boolean isTrailingStopTriggered = currentPrice.compareTo(ma.getEma5()) > 0;
-
- // 趋势反转信号:MA多头排列 + MACD金叉
- boolean isMaBullish = ma.getEma5().compareTo(ma.getEma10()) > 0 &&
- ma.getEma10().compareTo(ma.getEma20()) > 0;
- boolean isMacdBullish = macd.getDif().compareTo(macd.getDea()) > 0;
- boolean isTrendReversed = isMaBullish && isMacdBullish;
-
- // 多周期确认(看涨)
- boolean isMultiTimeframeConfirmed = multiTimeframeConfirm(fiveMinPrices, oneHourPrices, fourHourPrices);
-
- // 平空条件:保护止损触发 或 跟踪止损触发 或 (趋势反转且多周期确认)
- return isStopLossTriggered || isTrailingStopTriggered || (isTrendReversed && isMultiTimeframeConfirmed);
- }
-
- /**
- * 获取所有指标的当前状态
- * @return 指标状态字符串
- */
- public String getIndicatorStatus() {
- return String.format("MA5: %s, MA20: %s, ", ma.getEma5(), ma.getEma20()) +
- String.format("MACD-DIF: %s, MACD-DEA: %s, MACD-BAR: %s, ", macd.getDif(), macd.getDea(), macd.getMacdBar()) +
- String.format("KDJ-K: %s, KDJ-D: %s, KDJ-J: %s, ", kdj.getK(), kdj.getD(), kdj.getJ()) +
- String.format("RSI: %s, ", rsi.getRsi()) +
- String.format("BOLL-MID: %s, BOLL-UP: %s, BOLL-DN: %s, ", boll.getMid(), boll.getUpper(), boll.getLower()) +
- String.format("AdvancedMA-Bullish: %s, Bearish: %s, Percent: %s",
- advancedMA.isBullish(), advancedMA.isBearish(), advancedMA.calculatePercent());
- }
-
- /**
- * 生成区间交易信号
- * 在震荡行情下,使用BOLL通道作为区间边界,结合KDJ指标生成交易信号
- * @param currentPrice 当前价格
- * @param volume 成交量列表
- * @param hasLongPosition 是否当前持有做多仓位
- * @param hasShortPosition 是否当前持有做空仓位
- * @return 交易信号
- */
- private SignalType generateRangeTradingSignal(BigDecimal currentPrice, List<BigDecimal> volume,
- boolean hasLongPosition, boolean hasShortPosition) {
- // 区间交易策略逻辑:
- // 1. 价格触及BOLL下轨且KDJ超卖 → 买入信号
- // 2. 价格触及BOLL上轨且KDJ超买 → 卖出信号
- // 3. 价格回归BOLL中轨 → 平仓信号
-
- // 检查KDJ极端超买超卖情况
- boolean isKdjJExtremeOverbought = kdj.getJ().compareTo(new BigDecimal("100")) > 0;
- boolean isKdjJExtremeOversold = kdj.getJ().compareTo(new BigDecimal("10")) < 0;
-
- // 价格触及BOLL下轨(基础条件)
- boolean isPriceNearBollLower = currentPrice.compareTo(boll.getLower()) >= 0 &&
- currentPrice.compareTo(boll.getLower().multiply(new BigDecimal("1.01"))) <= 0;
-
- // 价格触及BOLL上轨(基础条件)
- boolean isPriceNearBollUpper = currentPrice.compareTo(boll.getUpper()) <= 0 &&
- currentPrice.compareTo(boll.getUpper().multiply(new BigDecimal("0.99"))) >= 0;
-
- // 当KDJ-J极度超买/超卖时,放宽BOLL边界要求
- if (isKdjJExtremeOverbought) {
- isPriceNearBollUpper = currentPrice.compareTo(boll.getUpper().multiply(new BigDecimal("0.985"))) >= 0;
- }
- if (isKdjJExtremeOversold) {
- isPriceNearBollLower = currentPrice.compareTo(boll.getLower().multiply(new BigDecimal("1.015"))) <= 0;
- }
-
- // 价格回归BOLL中轨附近
- boolean isPriceNearBollMid = currentPrice.compareTo(boll.getMid().multiply(new BigDecimal("0.998"))) >= 0 &&
- currentPrice.compareTo(boll.getMid().multiply(new BigDecimal("1.002"))) <= 0;
-
- // KDJ超卖(使用调整后的阈值)
- boolean isKdjOversold = kdj.getJ().compareTo(new BigDecimal(15)) < 0;
-
- // KDJ超买(使用调整后的阈值)
- boolean isKdjOverbought = kdj.getJ().compareTo(new BigDecimal(85)) > 0;
-
- // RSI超卖(<30)
- boolean isRsiOversold = rsi.getRsi().compareTo(new BigDecimal(30)) < 0;
-
- // RSI超买(>70)
- boolean isRsiOverbought = rsi.getRsi().compareTo(new BigDecimal(70)) > 0;
-
- // 成交量验证(当前成交量大于20周期均值的1.1倍)
- boolean isVolumeValid = volumeConfirm(volume) &&
- volume.get(volume.size() - 1).compareTo(calculateMA(volume, config.getVolumeMaPeriod()).multiply(new BigDecimal("1.1"))) > 0;
-
- // 开多逻辑:价格触及BOLL下轨且KDJ超卖且RSI超卖且有成交量支持
- if (isPriceNearBollLower && isKdjOversold && isRsiOversold && isVolumeValid && !hasLongPosition && !hasShortPosition) {
- log.info("区间交易:价格触及BOLL下轨({}), KDJ-J值({})超卖,RSI({})超卖,生成买入信号", boll.getLower(), kdj.getJ(), rsi.getRsi());
- return SignalType.BUY;
- }
-
- // 开空逻辑:价格触及BOLL上轨且KDJ超买且RSI超买且有成交量支持
- if (isPriceNearBollUpper && isKdjOverbought && isRsiOverbought && isVolumeValid && !hasLongPosition && !hasShortPosition) {
- log.info("区间交易:价格触及BOLL上轨({}), KDJ-J值({})超买,RSI({})超买,生成卖出信号", boll.getUpper(), kdj.getJ(), rsi.getRsi());
- return SignalType.SELL;
- }
-
- // 平多逻辑:价格回归BOLL中轨
- if (isPriceNearBollMid && hasLongPosition) {
- log.info("区间交易:价格回归BOLL中轨({}),生成平多信号", boll.getMid());
- return SignalType.CLOSE_BUY;
- }
-
- // 平空逻辑:价格回归BOLL中轨
- if (isPriceNearBollMid && hasShortPosition) {
- log.info("区间交易:价格回归BOLL中轨({}),生成平空信号", boll.getMid());
- return SignalType.CLOSE_SELL;
- }
-
- return SignalType.NONE;
- }
-
- /**
- * 计算动态杠杆倍数
- * 杠杆倍数 = 基础杠杆 * (波动率阈值/当前波动率)
- * @param high 最高价列表
- * @param low 最低价列表
- * @param close 收盘价列表
- * @return 动态杠杆倍数
- */
- public BigDecimal calculateDynamicLeverage(List<BigDecimal> high, List<BigDecimal> low, List<BigDecimal> close) {
- if (!config.isEnableDynamicLeverage()) {
- return config.getBaseLeverage();
- }
-
- // 计算当前ATR和波动率
- BigDecimal currentAtr = calculateATR(high, low, close, config.getAtrPeriod());
- BigDecimal currentVolatility = normalizeVolatility(close, currentAtr);
-
- // 计算30日ATR移动中位数作为波动率阈值
- BigDecimal volatilityThreshold = calculateVolatilityThreshold(high, low, close);
-
- // 动态计算杠杆倍数
- BigDecimal leverage = config.getBaseLeverage().multiply(volatilityThreshold).divide(currentVolatility, 2, BigDecimal.ROUND_HALF_UP);
-
- // 限制杠杆范围在1x-10x之间
- leverage = leverage.min(new BigDecimal(10)).max(BigDecimal.ONE);
-
- log.info("动态杠杆计算 - 基础杠杆: {}, 波动率阈值: {}, 当前波动率: {}, 计算杠杆: {}",
- config.getBaseLeverage(), volatilityThreshold, currentVolatility, leverage);
-
- return leverage;
- }
-
- /**
- * 计算波动率阈值(30日ATR移动中位数)
- * @param high 最高价列表
- * @param low 最低价列表
- * @param close 收盘价列表
- * @return 波动率阈值
- */
- private BigDecimal calculateVolatilityThreshold(List<BigDecimal> high, List<BigDecimal> low, List<BigDecimal> close) {
- if (high == null || low == null || close == null || close.size() < config.getVolatilityThresholdPeriod()) {
- return new BigDecimal(5); // 默认阈值
- }
-
- List<BigDecimal> volatilityList = new ArrayList<>();
- for (int i = close.size() - config.getVolatilityThresholdPeriod(); i < close.size(); i++) {
- List<BigDecimal> recentHigh = high.subList(Math.max(0, i - config.getAtrPeriod()), i + 1);
- List<BigDecimal> recentLow = low.subList(Math.max(0, i - config.getAtrPeriod()), i + 1);
- List<BigDecimal> recentClose = close.subList(Math.max(0, i - config.getAtrPeriod()), i + 1);
-
- BigDecimal atr = calculateATR(recentHigh, recentLow, recentClose, config.getAtrPeriod());
- BigDecimal volatility = normalizeVolatility(recentClose, atr);
- volatilityList.add(volatility);
- }
-
- // 计算中位数
- volatilityList.sort(BigDecimal::compareTo);
- int midIndex = volatilityList.size() / 2;
- return volatilityList.get(midIndex);
- }
-
- /**
- * 三段式止盈策略
- * 第一目标:BOLL上轨(30%仓位)
- * 第二目标:斐波那契161.8%(50%仓位)
- * 第三目标:趋势线破位(20%仓位)
- * @param entryPrice 入场价格
- * @param currentPrice 当前价格
- * @param direction 交易方向
- * @param positionSize 当前仓位大小
- * @return 止盈信号和应该平仓的仓位比例
- */
- public ProfitTakingResult calculateThreeStepProfitTaking(BigDecimal entryPrice, BigDecimal currentPrice,
- Direction direction, BigDecimal positionSize) {
- if (!config.isEnableThreeStepProfitTaking()) {
- return new ProfitTakingResult(SignalType.NONE, BigDecimal.ZERO);
- }
-
- // 计算三个止盈目标
- BigDecimal firstTarget = calculateFirstProfitTarget(entryPrice, currentPrice, direction);
- BigDecimal secondTarget = calculateSecondProfitTarget(entryPrice, direction);
- BigDecimal thirdTarget = calculateThirdProfitTarget(currentPrice, direction);
-
- // 判断当前价格是否达到目标
- if (direction == Direction.LONG) {
- if (currentPrice.compareTo(thirdTarget) >= 0) {
- // 达到第三目标,平全部仓位
- return new ProfitTakingResult(SignalType.CLOSE_BUY, positionSize);
- } else if (currentPrice.compareTo(secondTarget) >= 0) {
- // 达到第二目标,平50%仓位
- return new ProfitTakingResult(SignalType.CLOSE_BUY, positionSize.multiply(new BigDecimal("0.5")));
- } else if (currentPrice.compareTo(firstTarget) >= 0) {
- // 达到第一目标,平30%仓位
- return new ProfitTakingResult(SignalType.CLOSE_BUY, positionSize.multiply(new BigDecimal("0.3")));
- }
- } else if (direction == Direction.SHORT) {
- if (currentPrice.compareTo(thirdTarget) <= 0) {
- // 达到第三目标,平全部仓位
- return new ProfitTakingResult(SignalType.CLOSE_SELL, positionSize);
- } else if (currentPrice.compareTo(secondTarget) <= 0) {
- // 达到第二目标,平50%仓位
- return new ProfitTakingResult(SignalType.CLOSE_SELL, positionSize.multiply(new BigDecimal("0.5")));
- } else if (currentPrice.compareTo(firstTarget) <= 0) {
- // 达到第一目标,平30%仓位
- return new ProfitTakingResult(SignalType.CLOSE_SELL, positionSize.multiply(new BigDecimal("0.3")));
- }
- }
-
- return new ProfitTakingResult(SignalType.NONE, BigDecimal.ZERO);
- }
-
- /**
- * 计算第一止盈目标:BOLL上轨
- */
- private BigDecimal calculateFirstProfitTarget(BigDecimal entryPrice, BigDecimal currentPrice, Direction direction) {
- return direction == Direction.LONG ? boll.getUpper() : boll.getLower();
- }
-
- /**
- * 计算第二止盈目标:斐波那契161.8%
- */
- private BigDecimal calculateSecondProfitTarget(BigDecimal entryPrice, Direction direction) {
- BigDecimal fibonacciRatio = new BigDecimal("1.618");
- if (direction == Direction.LONG) {
- return entryPrice.multiply(BigDecimal.ONE.add(fibonacciRatio.divide(new BigDecimal(100))));
- } else {
- return entryPrice.multiply(BigDecimal.ONE.subtract(fibonacciRatio.divide(new BigDecimal(100))));
- }
- }
-
- /**
- * 计算第三止盈目标:简单趋势线破位(这里简化为MA5下穿MA20)
- */
- private BigDecimal calculateThirdProfitTarget(BigDecimal currentPrice, Direction direction) {
- // 这里使用简化的趋势线破位判断
- // 实际应用中可以使用更复杂的趋势线计算
- return direction == Direction.LONG ? ma.getEma20() : ma.getEma20();
- }
-
-
-
- /**
- * 黑天鹅事件过滤
- * 规避重大事件前后30分钟、链上大额转账、异常资金费率
- * @param fundingRate 当前资金费率
- * @param hasLargeTransfer 是否有大额转账
- * @param hasUpcomingEvent 是否有即将到来的重大事件
- * @return 是否应该规避交易
- */
- public boolean blackSwanFilter(BigDecimal fundingRate, boolean hasLargeTransfer, boolean hasUpcomingEvent) {
- if (!config.isEnableBlackSwanFilter()) {
- return false;
- }
-
- // 资金费率绝对值大于0.2%
- boolean isAbnormalFundingRate = fundingRate != null &&
- fundingRate.abs().compareTo(new BigDecimal("0.002")) > 0;
-
- // 大额转账监控(这里简化为外部传入)
- // 重大事件监控(这里简化为外部传入)
-
- boolean shouldAvoid = isAbnormalFundingRate || hasLargeTransfer || hasUpcomingEvent;
-
- if (shouldAvoid) {
- log.info("黑天鹅事件过滤触发 - 资金费率异常: {}, 大额转账: {}, 即将发生重大事件: {}",
- isAbnormalFundingRate, hasLargeTransfer, hasUpcomingEvent);
- }
-
- return shouldAvoid;
- }
-
- /**
- * 止盈结果类
- */
- public static class ProfitTakingResult {
- private SignalType signal;
- private BigDecimal closePositionSize;
-
- public ProfitTakingResult(SignalType signal, BigDecimal closePositionSize) {
- this.signal = signal;
- this.closePositionSize = closePositionSize;
- }
-
- public SignalType getSignal() {
- return signal;
- }
-
- public BigDecimal getClosePositionSize() {
- return closePositionSize;
- }
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/EMACalculator.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/EMACalculator.java
deleted file mode 100644
index 019b1a1..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/EMACalculator.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/**
- * 指数移动平均线(EMA)计算器
- * <p>
- * EMA(Exponential Moving Average)是一种加权移动平均线,对近期价格赋予更高权重,
- * 对远期价格赋予较低权重,能够更敏感地反映价格变化趋势。
- * 本计算器提供了EMA的多种计算方式,支持使用SMA作为初始值或使用第一个价格作为初始值。
- */
-package com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 指数移动平均线(EMA)计算器
- *
- * <p>计算公式:EMA(today) = Price(today) * k + EMA(yesterday) * (1 - k)</p>
- * <p>其中:k = 2 / (period + 1),period为EMA的周期</p>
- */
-public class EMACalculator {
- /**
- * 计算价格序列的指数移动平均线(EMA)
- *
- * @param prices 价格序列,使用BigDecimal确保计算精度
- * @param period EMA计算周期
- * @param initialSMA 是否使用SMA(简单移动平均线)作为初始值
- * @return 计算得到的EMA序列,与输入价格序列一一对应
- * @throws IllegalArgumentException 当输入参数无效时抛出异常
- */
- public static List<BigDecimal> calculateEMA(List<BigDecimal> prices, int period, boolean initialSMA) {
- if (prices == null || prices.isEmpty() || period <= 0) {
- throw new IllegalArgumentException("Invalid input parameters.");
- }
- if (initialSMA && prices.size() < period) {
- throw new IllegalArgumentException("Prices list too short for initial SMA.");
- }
-
- // 计算权重因子k = 2 / (period + 1)
- BigDecimal alpha = BigDecimal.valueOf(2.0).divide(BigDecimal.valueOf(period + 1), 10, RoundingMode.HALF_UP);
- List<BigDecimal> ema = new ArrayList<>();
-
- if (initialSMA) {
- // 使用SMA作为初始EMA值(前period个价格的平均值)
- BigDecimal sum = BigDecimal.ZERO;
- for (int i = 0; i < period; i++) {
- sum = sum.add(prices.get(i));
- }
- BigDecimal sma = sum.divide(BigDecimal.valueOf(period), 10, RoundingMode.HALF_UP);
- ema.add(sma);
-
- // 从第period+1个数据点开始计算后续EMA值
- for (int i = period; i < prices.size(); i++) {
- BigDecimal price = prices.get(i);
- BigDecimal prevEMA = ema.get(ema.size() - 1);
- // EMA计算公式:Price(today) * alpha + EMA(yesterday) * (1 - alpha)
- BigDecimal emaToday = price.multiply(alpha)
- .add(prevEMA.multiply(BigDecimal.ONE.subtract(alpha)))
- .setScale(10, RoundingMode.HALF_UP);
- ema.add(emaToday);
- }
- } else {
- // 使用第一个价格作为初始EMA值,并从第二个数据点开始计算
- ema.add(prices.get(0));
- for (int i = 1; i < prices.size(); i++) {
- BigDecimal price = prices.get(i);
- BigDecimal prevEMA = ema.get(ema.size() - 1);
- // EMA计算公式:Price(today) * alpha + EMA(yesterday) * (1 - alpha)
- BigDecimal emaToday = price.multiply(alpha)
- .add(prevEMA.multiply(BigDecimal.ONE.subtract(alpha)))
- .setScale(10, RoundingMode.HALF_UP);
- ema.add(emaToday);
- }
- }
-
- return ema;
- }
-
- /**
- * 计算价格序列的指数移动平均线(EMA),默认使用SMA作为初始值
- *
- * @param prices 价格序列
- * @param period EMA计算周期
- * @return 计算得到的EMA序列
- */
- public static List<BigDecimal> calculateEMA(List<BigDecimal> prices, int period) {
- return calculateEMA(prices, period, true);
- }
-
- /**
- * 计算单个EMA值(递归计算方式)
- *
- * @param currentPrice 当前价格
- * @param prevEMA 前一个EMA值
- * @param period EMA周期
- * @return 当前EMA值
- */
- public static BigDecimal calculateSingleEMA(BigDecimal currentPrice, BigDecimal prevEMA, int period) {
- // 计算权重因子alpha = 2 / (period + 1)
- BigDecimal alpha = BigDecimal.valueOf(2.0).divide(BigDecimal.valueOf(period + 1), 10, RoundingMode.HALF_UP);
-
- // EMA(today) = Price(today) * alpha + EMA(yesterday) * (1 - alpha)
- return currentPrice.multiply(alpha)
- .add(prevEMA.multiply(BigDecimal.ONE.subtract(alpha)))
- .setScale(10, RoundingMode.HALF_UP);
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/IndicatorUtils.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/IndicatorUtils.java
deleted file mode 100644
index a3cbec7..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/IndicatorUtils.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/**
- * 指标计算工具类
- * 封装MACD策略中常用的通用功能,如高低点查找等
- */
-package com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy;
-
-import java.math.BigDecimal;
-import java.util.List;
-
-/**
- * 指标计算工具类,提供MACD策略中常用的通用功能
- */
-public class IndicatorUtils {
-
- /**
- * 找到最近的价格高点索引
- *
- * @param prices 价格列表
- * @param startIdx 起始索引
- * @return 最近的价格高点索引
- */
- public static int findRecentHighIndex(List<BigDecimal> prices, int startIdx) {
- if (prices == null || startIdx < 0 || startIdx >= prices.size()) {
- return -1;
- }
-
- int highIdx = startIdx;
- BigDecimal highPrice = prices.get(startIdx);
-
- for (int i = startIdx + 1; i < prices.size(); i++) {
- BigDecimal currentPrice = prices.get(i);
- if (currentPrice.compareTo(highPrice) > 0) {
- highPrice = currentPrice;
- highIdx = i;
- }
- }
-
- return highIdx;
- }
-
- /**
- * 找到最近的价格低点索引
- *
- * @param prices 价格列表
- * @param startIdx 起始索引
- * @return 最近的价格低点索引
- */
- public static int findRecentLowIndex(List<BigDecimal> prices, int startIdx) {
- if (prices == null || startIdx < 0 || startIdx >= prices.size()) {
- return -1;
- }
-
- int lowIdx = startIdx;
- BigDecimal lowPrice = prices.get(startIdx);
-
- for (int i = startIdx + 1; i < prices.size(); i++) {
- BigDecimal currentPrice = prices.get(i);
- if (currentPrice.compareTo(lowPrice) < 0) {
- lowPrice = currentPrice;
- lowIdx = i;
- }
- }
-
- return lowIdx;
- }
-
- /**
- * 找到最近价格高点之前的价格高点索引
- *
- * @param prices 价格列表
- * @param startIdx 起始索引
- * @param recentHighIdx 最近的价格高点索引
- * @return 之前的价格高点索引
- */
- public static int findPreviousHighIndex(List<BigDecimal> prices, int startIdx, int recentHighIdx) {
- if (prices == null || startIdx < 0 || recentHighIdx <= startIdx || recentHighIdx >= prices.size()) {
- return -1;
- }
-
- int highIdx = startIdx;
- BigDecimal highPrice = prices.get(startIdx);
-
- for (int i = startIdx + 1; i < recentHighIdx; i++) {
- BigDecimal currentPrice = prices.get(i);
- if (currentPrice.compareTo(highPrice) > 0) {
- highPrice = currentPrice;
- highIdx = i;
- }
- }
-
- return highIdx;
- }
-
- /**
- * 找到最近价格低点之前的价格低点索引
- *
- * @param prices 价格列表
- * @param startIdx 起始索引
- * @param recentLowIdx 最近的价格低点索引
- * @return 之前的价格低点索引
- */
- public static int findPreviousLowIndex(List<BigDecimal> prices, int startIdx, int recentLowIdx) {
- if (prices == null || startIdx < 0 || recentLowIdx <= startIdx || recentLowIdx >= prices.size()) {
- return -1;
- }
-
- int lowIdx = startIdx;
- BigDecimal lowPrice = prices.get(startIdx);
-
- for (int i = startIdx + 1; i < recentLowIdx; i++) {
- BigDecimal currentPrice = prices.get(i);
- if (currentPrice.compareTo(lowPrice) < 0) {
- lowPrice = currentPrice;
- lowIdx = i;
- }
- }
-
- return lowIdx;
- }
-
- /**
- * 寻找最近的价格高点(带有回调确认)
- *
- * @param prices 价格列表
- * @param startIndex 起始索引
- * @param endIndex 结束索引
- * @return 符合条件的价格高点索引,未找到则返回-1
- */
- public static int findRecentHighWithRetrace(List<BigDecimal> prices, int startIndex, int endIndex) {
- if (prices == null || startIndex < 0 || endIndex >= prices.size() || startIndex >= endIndex) {
- return -1;
- }
-
- int highIndex = -1;
- BigDecimal highPrice = BigDecimal.ZERO;
-
- // 从右向左搜索,找到第一个有效高点
- for (int i = endIndex; i >= startIndex; i--) {
- if (prices.get(i).compareTo(highPrice) > 0) {
- highPrice = prices.get(i);
- highIndex = i;
- }
-
- // 检查高点后是否有回调
- if (highIndex != -1 && i < endIndex) {
- if (prices.get(i + 1).compareTo(highPrice) < 0) {
- return highIndex; // 找到确认回调的高点
- }
- }
- }
-
- return highIndex;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MACDCalculator.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MACDCalculator.java
deleted file mode 100644
index 5538216..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MACDCalculator.java
+++ /dev/null
@@ -1,241 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy;
-
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * MACD(Moving Average Convergence Divergence)指标计算器
- * <p>
- * MACD指标由三部分组成:
- * 1. DIF(Difference):短期EMA与长期EMA的差值
- * 2. DEA(Signal Line):DIF的指数移动平均线,作为MACD的信号线
- * 3. MACD柱状图(Histogram):DIF与DEA的差值,反映市场动量
- * <p>
- * 默认参数:短期周期=12,长期周期=26,信号周期=9
- */
-public class MACDCalculator {
-
- /**
- * 计算MACD指标
- *
- * @param closePrices 收盘价列表(使用BigDecimal确保计算精度)
- * @param fastlen 短期EMA周期(通常为12)
- * @param slowlen 长期EMA周期(通常为26)
- * @param siglen DEA的周期(通常为9)
- * @return 包含MACD各部分数据的PriceData列表
- * @throws IllegalArgumentException 如果数据点不足或参数无效
- */
- public static MACDResult calculateMACD(List<BigDecimal> closePrices, int fastlen, int slowlen, int siglen) {
- // 参数校验:确保数据点足够
- if (closePrices == null || closePrices.isEmpty()) {
- throw new IllegalArgumentException("Close prices list cannot be null or empty.");
- }
- if (fastlen <= 0 || slowlen <= 0 || siglen <= 0) {
- throw new IllegalArgumentException("All periods must be positive integers.");
- }
- if (fastlen >= slowlen) {
- throw new IllegalArgumentException("Fast period must be less than slow period.");
- }
- if (closePrices.size() < Math.max(fastlen, slowlen)) {
- throw new IllegalArgumentException("Insufficient data points for the specified periods.");
- }
-
- // 反转数据,确保从旧到新处理(因为用户提供的数据是从新到旧)
- List<BigDecimal> prices = new ArrayList<>(closePrices);
- Collections.reverse(prices);
-
- // 1. 计算快速EMA和慢速EMA,使用SMA作为初始值
- // 当initialSMA=true时,EMA列表长度为prices.size() - period + 1
- List<BigDecimal> fastEma = EMACalculator.calculateEMA(prices, fastlen, true);
- List<BigDecimal> slowEma = EMACalculator.calculateEMA(prices, slowlen, true);
-
- // 2. 计算MACD线(快速EMA减去慢速EMA)
- List<BigDecimal> macdLine = new ArrayList<>();
- // EMA列表的起始索引与价格列表的对应关系
- int slowEmaStartIdx = slowlen - 1; // slowEma中第一个有效值对应的价格索引
- int fastEmaStartIdx = fastlen - 1; // fastEma中第一个有效值对应的价格索引
-
- for (int i = 0; i < prices.size(); i++) {
- if (i < slowEmaStartIdx) {
- // 在慢速EMA开始有效之前,MACD线值为0
- macdLine.add(BigDecimal.ZERO);
- } else {
- // MACD线 = 快速EMA - 慢速EMA
- // 需要将价格索引转换为EMA列表索引
- int slowEmaIdx = i - slowEmaStartIdx;
- int fastEmaIdx = i - fastEmaStartIdx;
- BigDecimal macdValue = fastEma.get(fastEmaIdx).subtract(slowEma.get(slowEmaIdx));
- macdLine.add(macdValue);
- }
- }
-
- // 3. 计算信号线(MACD线的siglen周期EMA),使用SMA作为初始值
- List<BigDecimal> signalLine = EMACalculator.calculateEMA(macdLine, siglen, true);
-
- // 4. 计算柱状图(MACD线与信号线的差值)
- List<BigDecimal> histogram = new ArrayList<>();
- int signalLineStartIdx = siglen - 1; // signalLine中第一个有效值对应的macdLine索引
-
- for (int i = 0; i < macdLine.size(); i++) {
- if (i < slowEmaStartIdx + signalLineStartIdx) {
- // 在信号线开始有效之前,柱状图值为0
- histogram.add(BigDecimal.ZERO);
- } else {
- // 将macdLine索引转换为signalLine索引
- int signalLineIdx = i - signalLineStartIdx;
- // 柱状图 = (MACD线 - 信号线) * 2(放大信号)
- BigDecimal histValue = macdLine.get(i).subtract(signalLine.get(signalLineIdx)).multiply(new BigDecimal("2"));
- histogram.add(histValue);
- }
- }
-
- // 5. 构建结果数据
- List<PriceData> result = new ArrayList<>();
- int startIndex = slowEmaStartIdx + signalLineStartIdx; // 从信号线开始有效的位置开始
-
- for (int i = startIndex; i < prices.size(); i++) {
- PriceData data = new PriceData(prices.get(i));
-
- // 设置EMA值(需要转换索引)
- int fastEmaIdx = i - fastEmaStartIdx;
- int slowEmaIdx = i - slowEmaStartIdx;
- data.setEmaShort(fastEma.get(fastEmaIdx));
- data.setEmaLong(slowEma.get(slowEmaIdx));
-
- // 设置MACD指标值
- data.setDif(macdLine.get(i));
- data.setDea(signalLine.get(i - signalLineStartIdx));
- data.setMacdHist(histogram.get(i));
-
- result.add(data);
- }
-
- // 反转结果列表,恢复为从新到旧的顺序
- Collections.reverse(result);
-
- return new MACDResult(result, startIndex);
- }
-
- /**
- * 使用默认参数计算MACD指标
- * <p>
- * 默认参数:短期周期=12,长期周期=26,信号周期=9
- *
- * @param closePrices 收盘价列表
- * @return 包含MACD各部分数据的PriceData列表
- */
- public static MACDResult calculateMACD(List<BigDecimal> closePrices) {
- // 默认参数:快速周期12,慢速周期26,信号周期9
- return calculateMACD(closePrices, 12, 26, 9);
- }
-
- /**
- * 判断是否出现顶背离
- * <p>
- * 顶背离:价格创新高,但DIF未创新高,且与价格走势背离
- * 增强空头信号可靠性
- *
- * @param closePrices 原始收盘价列表
- * @param macdResult MACD计算结果
- * @return 是否出现顶背离
- */
- public static boolean isTopDivergence(List<BigDecimal> closePrices, MACDResult macdResult) {
- List<PriceData> macdData = macdResult.getMacdData();
- int startIdx = macdResult.getStartIndex();
-
- // 确保有足够的数据点进行判断(至少需要2个高点)
- if (macdData.size() < 10) {
- return false;
- }
-
- // 反转原始价格列表,确保从旧到新处理
- List<BigDecimal> prices = new ArrayList<>(closePrices);
- Collections.reverse(prices);
-
- // 找到最近的价格高点和对应的DIF值
- int recentPriceHighIdx = IndicatorUtils.findRecentHighIndex(prices, startIdx);
- if (recentPriceHighIdx < startIdx + 2 || recentPriceHighIdx == -1) {
- return false;
- }
-
- // 找到之前的价格高点和对应的DIF值
- int previousPriceHighIdx = IndicatorUtils.findPreviousHighIndex(prices, startIdx, recentPriceHighIdx);
- if (previousPriceHighIdx < startIdx || previousPriceHighIdx == -1) {
- return false;
- }
-
- // 获取对应位置的DIF值
- int recentDifIdx = recentPriceHighIdx - startIdx;
- int previousDifIdx = previousPriceHighIdx - startIdx;
-
- // 边界检查
- if (recentDifIdx >= macdData.size() || previousDifIdx >= macdData.size()) {
- return false;
- }
-
- BigDecimal recentPrice = prices.get(recentPriceHighIdx);
- BigDecimal previousPrice = prices.get(previousPriceHighIdx);
- BigDecimal recentDif = macdData.get(recentDifIdx).getDif();
- BigDecimal previousDif = macdData.get(previousDifIdx).getDif();
-
- // 顶背离条件:价格创新高,但DIF未创新高
- return recentPrice.compareTo(previousPrice) > 0 &&
- recentDif.compareTo(previousDif) < 0;
- }
-
- /**
- * 判断是否出现底背离
- * <p>
- * 底背离:价格创新低,但DIF未创新低,且与价格走势背离
- * 增强多头信号可靠性
- *
- * @param closePrices 原始收盘价列表
- * @param macdResult MACD计算结果
- * @return 是否出现底背离
- */
- public static boolean isBottomDivergence(List<BigDecimal> closePrices, MACDResult macdResult) {
- List<PriceData> macdData = macdResult.getMacdData();
- int startIdx = macdResult.getStartIndex();
-
- // 确保有足够的数据点进行判断(至少需要2个低点)
- if (macdData.size() < 10) {
- return false;
- }
-
- // 反转原始价格列表,确保从旧到新处理
- List<BigDecimal> prices = new ArrayList<>(closePrices);
- Collections.reverse(prices);
-
- // 找到最近的价格低点和对应的DIF值
- int recentPriceLowIdx = IndicatorUtils.findRecentLowIndex(prices, startIdx);
- if (recentPriceLowIdx < startIdx + 2 || recentPriceLowIdx == -1) {
- return false;
- }
-
- // 找到之前的价格低点和对应的DIF值
- int previousPriceLowIdx = IndicatorUtils.findPreviousLowIndex(prices, startIdx, recentPriceLowIdx);
- if (previousPriceLowIdx < startIdx || previousPriceLowIdx == -1) {
- return false;
- }
-
- // 获取对应位置的DIF值
- int recentDifIdx = recentPriceLowIdx - startIdx;
- int previousDifIdx = previousPriceLowIdx - startIdx;
-
- // 边界检查
- if (recentDifIdx >= macdData.size() || previousDifIdx >= macdData.size()) {
- return false;
- }
-
- BigDecimal recentPrice = prices.get(recentPriceLowIdx);
- BigDecimal previousPrice = prices.get(previousPriceLowIdx);
- BigDecimal recentDif = macdData.get(recentDifIdx).getDif();
- BigDecimal previousDif = macdData.get(previousDifIdx).getDif();
-
- // 底背离条件:价格创新低,但DIF未创新低
- return recentPrice.compareTo(previousPrice) < 0 &&
- recentDif.compareTo(previousDif) > 0;
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MACDResult.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MACDResult.java
deleted file mode 100644
index 0ab00ae..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MACDResult.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * MACD计算结果类
- * <p>
- * 用于封装MACD指标计算的结果数据,包括完整的MACD数据序列和数据的起始索引信息,
- * 方便策略模块获取和使用MACD计算结果。
- */
-package com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy;
-
-import java.util.List;
-
-/**
- * MACD计算结果封装类
- */
-public class MACDResult {
- /** MACD完整数据序列,包含每个价格点对应的DIF、DEA和MACD柱状图值 */
- private List<PriceData> macdData;
-
- /** 在原始价格序列中的起始索引,表示MACD数据的计算起点 */
- private int startIndex;
-
- /**
- * 构造函数,创建MACD计算结果对象
- *
- * @param result 计算得到的MACD数据序列
- * @param startIdx 数据在原始价格序列中的起始索引
- */
- public MACDResult(List<PriceData> result, int startIdx) {
- this.macdData = result;
- this.startIndex = startIdx;
- }
-
- /**
- * 获取MACD数据序列
- * @return MACD数据序列
- */
- public List<PriceData> getMacdData() {
- return macdData;
- }
-
- /**
- * 获取起始索引
- * @return 起始索引值
- */
- public int getStartIndex() {
- return startIndex;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MacdEmaStrategy.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MacdEmaStrategy.java
deleted file mode 100644
index 8dab3cb..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MacdEmaStrategy.java
+++ /dev/null
@@ -1,463 +0,0 @@
-/**
- * MACD和MA组合交易策略实现类
- * 基于多时间粒度K线数据生成交易信号并确定持仓方向
- *
- * 该策略综合考虑了EMA指标、MACD指标、价格突破信号和波动率因素,
- * 形成了一套完整的开仓、平仓和持仓管理机制。
- * 支持1分钟(K线)和1分钟(K线)级别的数据输入,数据顺序要求从新到旧排列。
- */
-package com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy;
-
-import lombok.extern.slf4j.Slf4j;
-
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * MACD和MA组合交易策略实现
- * <p>
- * 该策略利用EMA交叉、MACD指标、价格突破信号和波动率过滤,
- * 为多时间粒度K线级别交易提供综合决策支持。
- * <p>
- * 数据输入要求:
- * - historicalPrices1M:1分钟K线收盘价列表,顺序从新到旧
- * - historicalPrices1D:日线K线收盘价列表,顺序从新到旧
- */
-@Slf4j
-public class MacdEmaStrategy {
-
- /** 操作类型枚举 */
- public enum OperationType {
- /** 开仓 */
- open,
- /** 平仓 */
- close
- }
-
- /** 持仓状态枚举 */
- public enum PositionType {
- /** 多头开仓 */
- LONG_BUY,
- /** 多头平仓 */
- LONG_SELL,
- /** 空头开仓 */
- SHORT_SELL,
- /** 空头平仓 */
- SHORT_BUY,
- /** 空仓 */
- NONE
- }
-
- /** 交易指令类,封装side和posSide的组合 */
- public static class TradingOrder {
- private String side; // buy或sell
- private String posSide; // long或short
-
- public TradingOrder(String side, String posSide) {
- this.side = side;
- this.posSide = posSide;
- }
-
- public String getSide() {
- return side;
- }
-
- public String getPosSide() {
- return posSide;
- }
-
- @Override
- public String toString() {
- return String.format("TradingOrder{side='%s', posSide='%s'}", side, posSide);
- }
- }
-
- // 策略参数
- private int shortPeriod; // 短期EMA周期
- private int longPeriod; // 长期EMA周期
- private int signalPeriod; // MACD信号线周期
- private int volatilityPeriod; // 波动率计算周期
- private int trendPeriod = 200; // 趋势过滤EMA周期(200日)
- private BigDecimal stopLossRatio; // 止损比例
- private BigDecimal takeProfitRatio; // 止盈比例
-
- /**
- * 默认构造函数,使用标准MACD参数
- * 短期周期=12, 长期周期=26, 信号线周期=9, 波动率周期=20
- * 止损比例=1%, 止盈比例=2%
- */
- public MacdEmaStrategy() {
- this(12, 26, 9, 20, new BigDecimal("0.01"), new BigDecimal("0.02"));
- }
-
- /**
- * 自定义参数构造函数,使用默认趋势周期200
- *
- * @param shortPeriod 短期EMA周期
- * @param longPeriod 长期EMA周期
- * @param signalPeriod MACD信号线周期
- * @param volatilityPeriod 波动率计算周期
- * @param stopLossRatio 止损比例
- * @param takeProfitRatio 止盈比例
- */
- public MacdEmaStrategy(int shortPeriod, int longPeriod, int signalPeriod, int volatilityPeriod,
- BigDecimal stopLossRatio, BigDecimal takeProfitRatio) {
- this(shortPeriod, longPeriod, signalPeriod, volatilityPeriod, 200, stopLossRatio, takeProfitRatio);
- }
-
- /**
- * 自定义参数构造函数
- *
- * @param shortPeriod 短期EMA周期
- * @param longPeriod 长期EMA周期
- * @param signalPeriod MACD信号线周期
- * @param volatilityPeriod 波动率计算周期
- * @param trendPeriod 趋势过滤EMA周期(200日)
- * @param stopLossRatio 止损比例
- * @param takeProfitRatio 止盈比例
- */
- public MacdEmaStrategy(int shortPeriod, int longPeriod, int signalPeriod, int volatilityPeriod, int trendPeriod,
- BigDecimal stopLossRatio, BigDecimal takeProfitRatio) {
- this.shortPeriod = shortPeriod;
- this.longPeriod = longPeriod;
- this.signalPeriod = signalPeriod;
- this.volatilityPeriod = volatilityPeriod;
- this.trendPeriod = trendPeriod;
- this.stopLossRatio = stopLossRatio;
- this.takeProfitRatio = takeProfitRatio;
- }
-
- // 主流程方法
-
- /**
- * 分析历史价格数据并生成交易指令
- *
- * @param historicalPrices 历史价格序列(1分钟K线收盘价),顺序从新到旧
- * @param operation 操作类型(open/close)
- * @return 交易指令(包含side和posSide),如果没有交易信号则返回null
- */
- public TradingOrder generateTradingOrder(List<BigDecimal> historicalPrices, String operation) {
- PositionType signal = null;
-
- if (OperationType.open.name().equals(operation)) {
- signal = analyzeOpen(historicalPrices);
- } else if (OperationType.close.name().equals(operation)) {
- signal = analyzeClose(historicalPrices);
- }
-
- // 根据信号生成交易指令
- return convertSignalToTradingOrder(signal);
- }
-
- /**
- * 分析最新价格数据并生成开仓信号
- *
- * @param closePrices 1分钟K线收盘价序列,顺序从新到旧
- * @return 生成的交易信号(LONG_BUY、SHORT_SELL或NONE)
- */
- public PositionType analyzeOpen(List<BigDecimal> closePrices) {
- // 数据检查:确保有足够的数据点进行计算(需要足够数据计算200日EMA)
- if (closePrices == null || closePrices.size() < Math.max(34, trendPeriod)) {
- return PositionType.NONE; // 数据不足,无法生成信号
- }
-
- // 计算MACD指标
- MACDResult macdResult = MACDCalculator.calculateMACD(
- closePrices, shortPeriod, longPeriod, signalPeriod);
- log.info("MACD计算结果:{}", macdResult.getMacdData().get(0));
-
- // 多头开仓条件检查
- if (isLongEntryCondition(macdResult, closePrices)) {
- log.info("多头开仓信号,价格:{}", closePrices.get(0));
- return PositionType.LONG_BUY;
- }
-
- // 空头开仓条件检查
- if (isShortEntryCondition(macdResult, closePrices)) {
- log.info("空头开仓信号,价格:{}", closePrices.get(0));
- return PositionType.SHORT_SELL;
- }
-
- // 无信号
- return PositionType.NONE;
- }
-
- /**
- * 分析最新价格数据并生成平仓信号
- *
- * @param closePrices 1分钟K线收盘价序列,顺序从新到旧
- * @return 生成的交易信号(LONG_SELL、SHORT_BUY或NONE)
- */
- public PositionType analyzeClose(List<BigDecimal> closePrices) {
- // 数据检查:确保有足够的数据点进行计算
- if (closePrices == null || closePrices.size() < Math.max(34, trendPeriod)) {
- return PositionType.NONE; // 数据不足,无法生成信号
- }
-
- // 计算MACD指标
- MACDResult macdResult = MACDCalculator.calculateMACD(
- closePrices, shortPeriod, longPeriod, signalPeriod);
-
- // 最新收盘价
- BigDecimal latestPrice = closePrices.get(0);
-
- if (isLongExitCondition(macdResult, latestPrice)) {
- log.info("多头平仓信号,价格:{}", latestPrice);
- return PositionType.LONG_SELL;
- }
-
- if (isShortExitCondition(macdResult, latestPrice)) {
- log.info("空头平仓信号,价格:{}", latestPrice);
- return PositionType.SHORT_BUY;
- }
-
- // 无信号
- return PositionType.NONE;
- }
-
- // 信号转换方法
-
- /**
- * 将持仓信号转换为交易指令
- *
- * @param signal 持仓信号
- * @return 交易指令,无信号则返回null
- */
- private TradingOrder convertSignalToTradingOrder(PositionType signal) {
- if (signal == null) {
- return null;
- }
-
- switch (signal) {
- case LONG_BUY:
- // 开多:买入开多(side 填写 buy; posSide 填写 long )
- return new TradingOrder("buy", "long");
- case LONG_SELL:
- // 平多:卖出平多(side 填写 sell; posSide 填写 long )
- return new TradingOrder("sell", "long");
- case SHORT_SELL:
- // 开空:卖出开空(side 填写 sell; posSide 填写 short )
- return new TradingOrder("sell", "short");
- case SHORT_BUY:
- // 平空:买入平空(side 填写 buy; posSide 填写 short )
- return new TradingOrder("buy", "short");
- default:
- // 无信号
- return null;
- }
- }
-
- // 开仓条件检查方法
-
- /**
- * 多头开仓条件检查
- *
- * @param macdResult MACD计算结果
- * @param closePrices 1分钟K线收盘价序列,顺序从新到旧
- * @return 是否满足多头开仓条件
- */
- private boolean isLongEntryCondition(MACDResult macdResult, List<BigDecimal> closePrices) {
- // 1. 计算200日EMA(趋势过滤)
- // 复制并反转日线数据,确保从旧到新计算EMA
- List<BigDecimal> reversed1DPrices = new ArrayList<>(closePrices);
- Collections.reverse(reversed1DPrices);
- List<BigDecimal> trendEma = EMACalculator.calculateEMA(reversed1DPrices, trendPeriod, true);
- BigDecimal latestTrendEma = trendEma.get(trendEma.size() - 1);
- BigDecimal latestPrice = closePrices.get(0);
-
- // 2. 价格必须位于200日EMA上方(多头趋势确认)
- boolean isAboveTrend = latestPrice.compareTo(latestTrendEma) > 0;
-
- // 3. MACD金叉检查
- boolean isGoldenCross = isGoldenCross(macdResult);
-
- // 4. MACD柱状线由负转正(动量转变)
- boolean isMacdHistTurningPositive = isMacdHistTurningPositive(macdResult);
-
- // 5. 底背离检查(增强多头信号可靠性)
- boolean isBottomDivergence = MACDCalculator.isBottomDivergence(closePrices, macdResult);
-
- log.info("多头信号检查, 200日EMA价格{}位于上方: {}, 金叉: {}, MACD柱状线由负转正: {}, 底背离: {}",
- latestTrendEma,isAboveTrend, isGoldenCross, isMacdHistTurningPositive, isBottomDivergence);
-
- // 多头开仓条件:柱状线转强 + 金叉 + (趋势向上或底背离)
- return isMacdHistTurningPositive && isGoldenCross && ( isAboveTrend|| isBottomDivergence);
- }
-
- /**
- * 空头开仓条件检查
- *
- * @param macdResult MACD计算结果
- * @param closePrices 1分钟K线收盘价序列,顺序从新到旧
- * @return 是否满足空头开仓条件
- */
- private boolean isShortEntryCondition(MACDResult macdResult, List<BigDecimal> closePrices) {
- // 1. 计算200日EMA(趋势过滤)
- // 复制并反转日线数据,确保从旧到新计算EMA
- List<BigDecimal> reversed1DPrices = new ArrayList<>(closePrices);
- Collections.reverse(reversed1DPrices);
- List<BigDecimal> trendEma = EMACalculator.calculateEMA(reversed1DPrices, trendPeriod, true);
- BigDecimal latestTrendEma = trendEma.get(trendEma.size() - 1);
- BigDecimal latestPrice = closePrices.get(0);
-
- // 2. 价格必须位于200日EMA下方(空头趋势确认)
- boolean isBelowTrend = latestPrice.compareTo(latestTrendEma) < 0;
-
- // 3. MACD死叉检查
- boolean isDeathCross = isDeathCross(macdResult);
-
- // 4. MACD柱状线由正转负(动量转变)
- boolean isMacdHistTurningNegative = isMacdHistTurningNegative(macdResult);
-
- // 5. 顶背离检查(增强空头信号可靠性)
- boolean isTopDivergence = MACDCalculator.isTopDivergence(closePrices, macdResult);
-
- log.info("空头信号检查, 200日EMA价格{}位于下方: {}, 死叉: {}, MACD柱状线由正转负: {}, 顶背离: {}",
- latestTrendEma,isBelowTrend, isDeathCross, isMacdHistTurningNegative, isTopDivergence);
-
- // 空头开仓条件:柱状线转弱 + 死叉 + (趋势向下或顶背离)
- return isMacdHistTurningNegative && isDeathCross && ( isBelowTrend || isTopDivergence);
- }
-
- // 平仓条件检查方法
-
- /**
- * 多头平仓条件检查
- *
- * @param macdResult MACD计算结果
- * @param currentPrice 当前价格
- * @return 是否满足多头平仓条件
- */
- private boolean isLongExitCondition(MACDResult macdResult, BigDecimal currentPrice) {
- // 多头平仓条件:MACD柱状线动量减弱(由正转弱)
- List<PriceData> macdData = macdResult.getMacdData();
- if (macdData.size() >= 2) {
- PriceData latest = macdData.get(0); // 最新数据
- PriceData previous = macdData.get(1); // 前一个数据
-
- // 柱状线由正转弱:前一根为正,当前绝对值减小
- boolean momentumWeakening = previous.getMacdHist().compareTo(BigDecimal.ZERO) >= 0 &&
- latest.getMacdHist().abs().compareTo(previous.getMacdHist().abs()) < 0;
-
- return momentumWeakening;
- }
- return false;
- }
-
- /**
- * 空头平仓条件检查
- *
- * @param macdResult MACD计算结果
- * @param currentPrice 当前价格
- * @return 是否满足空头平仓条件
- */
- private boolean isShortExitCondition(MACDResult macdResult, BigDecimal currentPrice) {
- // 空头平仓条件:MACD柱状线动量减弱(由负转弱)
- List<PriceData> macdData = macdResult.getMacdData();
- if (macdData.size() >= 2) {
- PriceData latest = macdData.get(0); // 最新数据
- PriceData previous = macdData.get(1); // 前一个数据
-
- // 柱状线由负转弱:前一根为负,当前绝对值减小
- boolean momentumWeakening = previous.getMacdHist().compareTo(BigDecimal.ZERO) <= 0 &&
- latest.getMacdHist().abs().compareTo(previous.getMacdHist().abs()) < 0;
-
- return momentumWeakening;
- }
-
- return false;
- }
-
- // MACD信号辅助方法
-
- /**
- * 简单金叉判断
- * <p>
- * 条件:DIF线从下往上穿过DEA线
- *
- * @param macdResult MACD计算结果
- * @return 是否形成金叉
- */
- private boolean isGoldenCross(MACDResult macdResult) {
- List<PriceData> macdData = macdResult.getMacdData();
- if (macdData.size() < 2) {
- return false;
- }
-
- PriceData latest = macdData.get(0);
- PriceData previous = macdData.get(1);
-
- // 金叉判断:DIF从下往上穿过DEA
- return previous.getDif().compareTo(previous.getDea()) < 0 &&
- latest.getDif().compareTo(latest.getDea()) > 0;
- }
-
- /**
- * 简单死叉判断
- * <p>
- * 条件:DIF线从上往下穿过DEA线
- *
- * @param macdResult MACD计算结果
- * @return 是否形成死叉
- */
- private boolean isDeathCross(MACDResult macdResult) {
- List<PriceData> macdData = macdResult.getMacdData();
- if (macdData.size() < 2) {
- return false;
- }
-
- PriceData latest = macdData.get(0);
- PriceData previous = macdData.get(1);
-
- // 死叉判断:DIF从上往下穿过DEA
- return previous.getDif().compareTo(previous.getDea()) > 0 &&
- latest.getDif().compareTo(latest.getDea()) < 0;
- }
-
- /**
- * MACD柱状线由负转正判断
- * <p>
- * 条件:前一根柱状线为负,当前柱状线为正
- *
- * @param macdResult MACD计算结果
- * @return 是否由负转正
- */
- private boolean isMacdHistTurningPositive(MACDResult macdResult) {
- List<PriceData> macdData = macdResult.getMacdData();
- if (macdData.size() < 2) {
- return false;
- }
-
- PriceData latest = macdData.get(0); // 最新数据
- PriceData previous = macdData.get(1); // 前一个数据
-
- // 柱状线由负转正:前一根为负,当前为正
- return previous.getMacdHist().compareTo(BigDecimal.ZERO) <= 0 &&
- latest.getMacdHist().compareTo(BigDecimal.ZERO) > 0;
- }
-
- /**
- * MACD柱状线由正转负判断
- * <p>
- * 条件:前一根柱状线为正,当前柱状线为负
- *
- * @param macdResult MACD计算结果
- * @return 是否由正转负
- */
- private boolean isMacdHistTurningNegative(MACDResult macdResult) {
- List<PriceData> macdData = macdResult.getMacdData();
- if (macdData.size() < 2) {
- return false;
- }
-
- PriceData latest = macdData.get(0); // 最新数据
- PriceData previous = macdData.get(1); // 前一个数据
-
- // 柱状线由正转负:前一根为正,当前为负
- return previous.getMacdHist().compareTo(BigDecimal.ZERO) >= 0 &&
- latest.getMacdHist().compareTo(BigDecimal.ZERO) < 0;
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MacdMaStrategy.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MacdMaStrategy.java
deleted file mode 100644
index f5ebaf3..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/MacdMaStrategy.java
+++ /dev/null
@@ -1,473 +0,0 @@
-/**
- * MACD和MA组合交易策略实现类
- * 基于多时间粒度K线数据生成交易信号并确定持仓方向
- *
- * 该策略综合考虑了EMA指标、MACD指标、价格突破信号和波动率因素,
- * 形成了一套完整的开仓、平仓和持仓管理机制。
- * 支持1分钟(K线)和日线(K线)级别的数据输入,数据顺序要求从新到旧排列。
- */
-package com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy;
-
-import lombok.extern.slf4j.Slf4j;
-
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * MACD和MA组合交易策略实现
- * <p>
- * 该策略利用EMA交叉、MACD指标、价格突破信号和波动率过滤,
- * 为多时间粒度K线级别交易提供综合决策支持。
- * <p>
- * 数据输入要求:
- * - historicalPrices1M:1分钟K线收盘价列表,顺序从新到旧
- * - historicalPrices1D:日线K线收盘价列表,顺序从新到旧
- */
-@Slf4j
-public class MacdMaStrategy {
-
- /** 操作类型枚举 */
- public enum OperationType {
- /** 开仓 */
- open,
- /** 平仓 */
- close
- }
-
- /** 持仓状态枚举 */
- public enum PositionType {
- /** 多头开仓 */
- LONG_BUY,
- /** 多头平仓 */
- LONG_SELL,
- /** 空头开仓 */
- SHORT_SELL,
- /** 空头平仓 */
- SHORT_BUY,
- /** 空仓 */
- NONE
- }
-
- /** 交易指令类,封装side和posSide的组合 */
- public static class TradingOrder {
- private String side; // buy或sell
- private String posSide; // long或short
-
- public TradingOrder(String side, String posSide) {
- this.side = side;
- this.posSide = posSide;
- }
-
- public String getSide() {
- return side;
- }
-
- public String getPosSide() {
- return posSide;
- }
-
- @Override
- public String toString() {
- return String.format("TradingOrder{side='%s', posSide='%s'}", side, posSide);
- }
- }
-
- // 策略参数
- private int shortPeriod; // 短期EMA周期
- private int longPeriod; // 长期EMA周期
- private int signalPeriod; // MACD信号线周期
- private int volatilityPeriod; // 波动率计算周期
- private int trendPeriod = 200; // 趋势过滤EMA周期(200日)
- private BigDecimal stopLossRatio; // 止损比例
- private BigDecimal takeProfitRatio; // 止盈比例
-
- /**
- * 默认构造函数,使用标准MACD参数
- * 短期周期=12, 长期周期=26, 信号线周期=9, 波动率周期=20
- * 止损比例=1%, 止盈比例=2%
- */
- public MacdMaStrategy() {
- this(12, 26, 9, 20, new BigDecimal("0.01"), new BigDecimal("0.02"));
- }
-
- /**
- * 自定义参数构造函数,使用默认趋势周期200
- *
- * @param shortPeriod 短期EMA周期
- * @param longPeriod 长期EMA周期
- * @param signalPeriod MACD信号线周期
- * @param volatilityPeriod 波动率计算周期
- * @param stopLossRatio 止损比例
- * @param takeProfitRatio 止盈比例
- */
- public MacdMaStrategy(int shortPeriod, int longPeriod, int signalPeriod, int volatilityPeriod,
- BigDecimal stopLossRatio, BigDecimal takeProfitRatio) {
- this(shortPeriod, longPeriod, signalPeriod, volatilityPeriod, 200, stopLossRatio, takeProfitRatio);
- }
-
- /**
- * 自定义参数构造函数
- *
- * @param shortPeriod 短期EMA周期
- * @param longPeriod 长期EMA周期
- * @param signalPeriod MACD信号线周期
- * @param volatilityPeriod 波动率计算周期
- * @param trendPeriod 趋势过滤EMA周期(200日)
- * @param stopLossRatio 止损比例
- * @param takeProfitRatio 止盈比例
- */
- public MacdMaStrategy(int shortPeriod, int longPeriod, int signalPeriod, int volatilityPeriod, int trendPeriod,
- BigDecimal stopLossRatio, BigDecimal takeProfitRatio) {
- this.shortPeriod = shortPeriod;
- this.longPeriod = longPeriod;
- this.signalPeriod = signalPeriod;
- this.volatilityPeriod = volatilityPeriod;
- this.trendPeriod = trendPeriod;
- this.stopLossRatio = stopLossRatio;
- this.takeProfitRatio = takeProfitRatio;
- }
-
- // 主流程方法
-
- /**
- * 分析历史价格数据并生成交易指令
- *
- * @param historicalPrices 历史价格序列(1分钟K线收盘价),顺序从新到旧
- * @param historical1DayPrices 日线历史价格序列,顺序从新到旧
- * @param operation 操作类型(open/close)
- * @return 交易指令(包含side和posSide),如果没有交易信号则返回null
- */
- public TradingOrder generateTradingOrder(List<BigDecimal> historicalPrices, List<BigDecimal> historical1DayPrices, String operation) {
- PositionType signal = null;
-
- if (OperationType.open.name().equals(operation)) {
- signal = analyzeOpen(historicalPrices, historical1DayPrices);
- } else if (OperationType.close.name().equals(operation)) {
- signal = analyzeClose(historicalPrices, historical1DayPrices);
- }
-
- // 根据信号生成交易指令
- return convertSignalToTradingOrder(signal);
- }
-
- /**
- * 分析最新价格数据并生成开仓信号
- *
- * @param closePrices 1分钟K线收盘价序列,顺序从新到旧
- * @param close1DPrices 日线K线收盘价序列,顺序从新到旧
- * @return 生成的交易信号(LONG_BUY、SHORT_SELL或NONE)
- */
- public PositionType analyzeOpen(List<BigDecimal> closePrices, List<BigDecimal> close1DPrices) {
- // 数据检查:确保有足够的数据点进行计算(需要足够数据计算200日EMA)
- if (closePrices == null || closePrices.size() < Math.max(34, trendPeriod) ||
- close1DPrices == null || close1DPrices.size() < Math.max(34, trendPeriod)) {
- return PositionType.NONE; // 数据不足,无法生成信号
- }
-
- // 计算MACD指标
- MACDResult macdResult = MACDCalculator.calculateMACD(
- closePrices, shortPeriod, longPeriod, signalPeriod);
- log.info("MACD计算结果:{}", macdResult.getMacdData().get(0));
-
- // 多头开仓条件检查
- if (isLongEntryCondition(macdResult, closePrices, close1DPrices)) {
- log.info("多头开仓信号,价格:{}", closePrices.get(0));
- return PositionType.LONG_BUY;
- }
-
- // 空头开仓条件检查
- if (isShortEntryCondition(macdResult, closePrices, close1DPrices)) {
- log.info("空头开仓信号,价格:{}", closePrices.get(0));
- return PositionType.SHORT_SELL;
- }
-
- // 无信号
- return PositionType.NONE;
- }
-
- /**
- * 分析最新价格数据并生成平仓信号
- *
- * @param closePrices 1分钟K线收盘价序列,顺序从新到旧
- * @param close1DPrices 日线K线收盘价序列,顺序从新到旧
- * @return 生成的交易信号(LONG_SELL、SHORT_BUY或NONE)
- */
- public PositionType analyzeClose(List<BigDecimal> closePrices, List<BigDecimal> close1DPrices) {
- // 数据检查:确保有足够的数据点进行计算
- if (closePrices == null || closePrices.size() < Math.max(34, trendPeriod) ||
- close1DPrices == null || close1DPrices.size() < Math.max(34, trendPeriod)) {
- return PositionType.NONE; // 数据不足,无法生成信号
- }
-
- // 计算MACD指标
- MACDResult macdResult = MACDCalculator.calculateMACD(
- closePrices, shortPeriod, longPeriod, signalPeriod);
-
- // 最新收盘价
- BigDecimal latestPrice = closePrices.get(0);
-
- if (isLongExitCondition(macdResult, latestPrice)) {
- log.info("多头平仓信号,价格:{}", latestPrice);
- return PositionType.LONG_SELL;
- }
-
- if (isShortExitCondition(macdResult, latestPrice)) {
- log.info("空头平仓信号,价格:{}", latestPrice);
- return PositionType.SHORT_BUY;
- }
-
- // 无信号
- return PositionType.NONE;
- }
-
- // 信号转换方法
-
- /**
- * 将持仓信号转换为交易指令
- *
- * @param signal 持仓信号
- * @return 交易指令,无信号则返回null
- */
- private TradingOrder convertSignalToTradingOrder(PositionType signal) {
- if (signal == null) {
- return null;
- }
-
- switch (signal) {
- case LONG_BUY:
- // 开多:买入开多(side 填写 buy; posSide 填写 long )
- return new TradingOrder("buy", "long");
- case LONG_SELL:
- // 平多:卖出平多(side 填写 sell; posSide 填写 long )
- return new TradingOrder("sell", "long");
- case SHORT_SELL:
- // 开空:卖出开空(side 填写 sell; posSide 填写 short )
- return new TradingOrder("sell", "short");
- case SHORT_BUY:
- // 平空:买入平空(side 填写 buy; posSide 填写 short )
- return new TradingOrder("buy", "short");
- default:
- // 无信号
- return null;
- }
- }
-
- // 开仓条件检查方法
-
- /**
- * 多头开仓条件检查
- *
- * @param macdResult MACD计算结果
- * @param closePrices 1分钟K线收盘价序列,顺序从新到旧
- * @param close1DPrices 日线K线收盘价序列,顺序从新到旧
- * @return 是否满足多头开仓条件
- */
- private boolean isLongEntryCondition(MACDResult macdResult, List<BigDecimal> closePrices, List<BigDecimal> close1DPrices) {
- // 1. 计算200日EMA(趋势过滤)
- // 复制并反转日线数据,确保从旧到新计算EMA
- List<BigDecimal> reversed1DPrices = new ArrayList<>(close1DPrices);
- Collections.reverse(reversed1DPrices);
- List<BigDecimal> trendEma = EMACalculator.calculateEMA(reversed1DPrices, trendPeriod, true);
- BigDecimal latestTrendEma = trendEma.get(trendEma.size() - 1);
- BigDecimal latestPrice = closePrices.get(0);
- log.info( "200日EMA:{}, 最新价格:{}", latestTrendEma, latestPrice);
-
- // 2. 价格必须位于200日EMA上方(多头趋势确认)
- boolean isAboveTrend = latestPrice.compareTo(latestTrendEma) > 0;
-
- // 3. MACD金叉检查
- boolean isGoldenCross = isGoldenCross(macdResult);
-
- // 4. MACD柱状线由负转正(动量转变)
- boolean isMacdHistTurningPositive = isMacdHistTurningPositive(macdResult);
-
- // 5. 底背离检查(增强多头信号可靠性)
- boolean isBottomDivergence = MACDCalculator.isBottomDivergence(closePrices, macdResult);
-
- log.info("多头信号检查, 价格位于200日EMA上方: {}, 金叉: {}, MACD柱状线由负转正: {}, 底背离: {}",
- isAboveTrend, isGoldenCross, isMacdHistTurningPositive, isBottomDivergence);
-
- // 多头开仓条件:趋势向上 + 金叉 + (柱状线转强或底背离)
- return isAboveTrend && isGoldenCross && (isMacdHistTurningPositive || isBottomDivergence);
- }
-
- /**
- * 空头开仓条件检查
- *
- * @param macdResult MACD计算结果
- * @param closePrices 1分钟K线收盘价序列,顺序从新到旧
- * @param close1DPrices 日线K线收盘价序列,顺序从新到旧
- * @return 是否满足空头开仓条件
- */
- private boolean isShortEntryCondition(MACDResult macdResult, List<BigDecimal> closePrices, List<BigDecimal> close1DPrices) {
- // 1. 计算200日EMA(趋势过滤)
- // 复制并反转日线数据,确保从旧到新计算EMA
- List<BigDecimal> reversed1DPrices = new ArrayList<>(close1DPrices);
- Collections.reverse(reversed1DPrices);
- List<BigDecimal> trendEma = EMACalculator.calculateEMA(reversed1DPrices, trendPeriod, true);
- BigDecimal latestTrendEma = trendEma.get(trendEma.size() - 1);
- BigDecimal latestPrice = closePrices.get(0);
-
- log.info( "200日EMA:{}, 最新价格:{}", latestTrendEma, latestPrice);
-
- // 2. 价格必须位于200日EMA下方(空头趋势确认)
- boolean isBelowTrend = latestPrice.compareTo(latestTrendEma) < 0;
-
- // 3. MACD死叉检查
- boolean isDeathCross = isDeathCross(macdResult);
-
- // 4. MACD柱状线由正转负(动量转变)
- boolean isMacdHistTurningNegative = isMacdHistTurningNegative(macdResult);
-
- // 5. 顶背离检查(增强空头信号可靠性)
- boolean isTopDivergence = MACDCalculator.isTopDivergence(closePrices, macdResult);
-
- log.info("空头信号检查, 价格位于200日EMA下方: {}, 死叉: {}, MACD柱状线由正转负: {}, 顶背离: {}",
- isBelowTrend, isDeathCross, isMacdHistTurningNegative, isTopDivergence);
-
- // 空头开仓条件:趋势向下 + 死叉 + (柱状线转弱或顶背离)
- return isBelowTrend && isDeathCross && (isMacdHistTurningNegative || isTopDivergence);
- }
-
- // 平仓条件检查方法
-
- /**
- * 多头平仓条件检查
- *
- * @param macdResult MACD计算结果
- * @param currentPrice 当前价格
- * @return 是否满足多头平仓条件
- */
- private boolean isLongExitCondition(MACDResult macdResult, BigDecimal currentPrice) {
- // 多头平仓条件:MACD柱状线动量减弱(由正转弱)
- List<PriceData> macdData = macdResult.getMacdData();
- if (macdData.size() >= 2) {
- PriceData latest = macdData.get(0); // 最新数据
- PriceData previous = macdData.get(1); // 前一个数据
-
- // 柱状线由正转弱:前一根为正,当前绝对值减小
- boolean momentumWeakening = previous.getMacdHist().compareTo(BigDecimal.ZERO) >= 0 &&
- latest.getMacdHist().abs().compareTo(previous.getMacdHist().abs()) < 0;
-
- return momentumWeakening;
- }
- return false;
- }
-
- /**
- * 空头平仓条件检查
- *
- * @param macdResult MACD计算结果
- * @param currentPrice 当前价格
- * @return 是否满足空头平仓条件
- */
- private boolean isShortExitCondition(MACDResult macdResult, BigDecimal currentPrice) {
- // 空头平仓条件:MACD柱状线动量减弱(由负转弱)
- List<PriceData> macdData = macdResult.getMacdData();
- if (macdData.size() >= 2) {
- PriceData latest = macdData.get(0); // 最新数据
- PriceData previous = macdData.get(1); // 前一个数据
-
- // 柱状线由负转弱:前一根为负,当前绝对值减小
- boolean momentumWeakening = previous.getMacdHist().compareTo(BigDecimal.ZERO) <= 0 &&
- latest.getMacdHist().abs().compareTo(previous.getMacdHist().abs()) < 0;
-
- return momentumWeakening;
- }
-
- return false;
- }
-
- // MACD信号辅助方法
-
- /**
- * 简单金叉判断
- * <p>
- * 条件:DIF线从下往上穿过DEA线
- *
- * @param macdResult MACD计算结果
- * @return 是否形成金叉
- */
- private boolean isGoldenCross(MACDResult macdResult) {
- List<PriceData> macdData = macdResult.getMacdData();
- if (macdData.size() < 2) {
- return false;
- }
-
- PriceData latest = macdData.get(0);
- PriceData previous = macdData.get(1);
-
- // 金叉判断:DIF从下往上穿过DEA
- return previous.getDif().compareTo(previous.getDea()) < 0 &&
- latest.getDif().compareTo(latest.getDea()) > 0;
- }
-
- /**
- * 简单死叉判断
- * <p>
- * 条件:DIF线从上往下穿过DEA线
- *
- * @param macdResult MACD计算结果
- * @return 是否形成死叉
- */
- private boolean isDeathCross(MACDResult macdResult) {
- List<PriceData> macdData = macdResult.getMacdData();
- if (macdData.size() < 2) {
- return false;
- }
-
- PriceData latest = macdData.get(0);
- PriceData previous = macdData.get(1);
-
- // 死叉判断:DIF从上往下穿过DEA
- return previous.getDif().compareTo(previous.getDea()) > 0 &&
- latest.getDif().compareTo(latest.getDea()) < 0;
- }
-
- /**
- * MACD柱状线由负转正判断
- * <p>
- * 条件:前一根柱状线为负,当前柱状线为正
- *
- * @param macdResult MACD计算结果
- * @return 是否由负转正
- */
- private boolean isMacdHistTurningPositive(MACDResult macdResult) {
- List<PriceData> macdData = macdResult.getMacdData();
- if (macdData.size() < 2) {
- return false;
- }
-
- PriceData latest = macdData.get(0); // 最新数据
- PriceData previous = macdData.get(1); // 前一个数据
-
- // 柱状线由负转正:前一根为负,当前为正
- return previous.getMacdHist().compareTo(BigDecimal.ZERO) <= 0 &&
- latest.getMacdHist().compareTo(BigDecimal.ZERO) > 0;
- }
-
- /**
- * MACD柱状线由正转负判断
- * <p>
- * 条件:前一根柱状线为正,当前柱状线为负
- *
- * @param macdResult MACD计算结果
- * @return 是否由正转负
- */
- private boolean isMacdHistTurningNegative(MACDResult macdResult) {
- List<PriceData> macdData = macdResult.getMacdData();
- if (macdData.size() < 2) {
- return false;
- }
-
- PriceData latest = macdData.get(0); // 最新数据
- PriceData previous = macdData.get(1); // 前一个数据
-
- // 柱状线由正转负:前一根为正,当前为负
- return previous.getMacdHist().compareTo(BigDecimal.ZERO) >= 0 &&
- latest.getMacdHist().compareTo(BigDecimal.ZERO) < 0;
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/PriceData.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/PriceData.java
deleted file mode 100644
index 38f61e8..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/PriceData.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/**
- * 价格数据实体类
- * <p>
- * 用于存储K线的价格数据及其衍生指标值。作为MACD和MA策略计算过程中的数据载体,
- * 包含收盘价、指数移动平均线、MACD指标等关键价格和指标信息。
- */
-package com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy;
-
-import java.math.BigDecimal;
-
-/**
- * 价格数据类,封装单条K线的价格信息及相关技术指标数据
- * 使用BigDecimal确保金融计算的精确性
- */
-public class PriceData {
- /** 收盘价 */
- private BigDecimal close;
-
- /** 短期指数移动平均线(通常为12周期) */
- private BigDecimal emaShort;
-
- /** 长期指数移动平均线(通常为26周期) */
- private BigDecimal emaLong;
-
- /** DIF值(短期EMA与长期EMA的差值) */
- private BigDecimal dif;
-
- /** DEA值(DIF的移动平均线,通常为9周期EMA) */
- private BigDecimal dea;
-
- /** MACD柱状图值(DIF与DEA的差值) */
- private BigDecimal macdHist;
-
- /**
- * 构造函数,创建价格数据对象
- *
- * @param close 收盘价
- */
- public PriceData(BigDecimal close) {
- this.close = close;
- }
-
- /**
- * 获取收盘价
- *
- * @return 收盘价
- */
- public BigDecimal getClose() {
- return close;
- }
-
- /**
- * 设置收盘价
- *
- * @param close 收盘价
- */
- public void setClose(BigDecimal close) {
- this.close = close;
- }
-
- /**
- * 获取短期EMA值
- *
- * @return 短期EMA值
- */
- public BigDecimal getEmaShort() {
- return emaShort;
- }
-
- /**
- * 设置短期EMA值
- *
- * @param emaShort 短期EMA值
- */
- public void setEmaShort(BigDecimal emaShort) {
- this.emaShort = emaShort;
- }
-
- /**
- * 获取长期EMA值
- *
- * @return 长期EMA值
- */
- public BigDecimal getEmaLong() {
- return emaLong;
- }
-
- /**
- * 设置长期EMA值
- *
- * @param emaLong 长期EMA值
- */
- public void setEmaLong(BigDecimal emaLong) {
- this.emaLong = emaLong;
- }
-
- /**
- * 获取DIF值
- *
- * @return DIF值
- */
- public BigDecimal getDif() {
- return dif;
- }
-
- /**
- * 设置DIF值
- *
- * @param dif DIF值
- */
- public void setDif(BigDecimal dif) {
- this.dif = dif;
- }
-
- /**
- * 获取DEA值
- *
- * @return DEA值
- */
- public BigDecimal getDea() {
- return dea;
- }
-
- /**
- * 设置DEA值
- *
- * @param dea DEA值
- */
- public void setDea(BigDecimal dea) {
- this.dea = dea;
- }
-
- /**
- * 获取MACD柱状图值
- *
- * @return MACD柱状图值
- */
- public BigDecimal getMacdHist() {
- return macdHist;
- }
-
- /**
- * 设置MACD柱状图值
- *
- * @param macdHist MACD柱状图值
- */
- public void setMacdHist(BigDecimal macdHist) {
- this.macdHist = macdHist;
- }
-
- /**
- * 转换为字符串表示
- *
- * @return 格式化的字符串表示
- */
- @Override
- public String toString() {
- return String.format("PriceData{close=%.2f, EMA_short=%.2f, EMA_long=%.2f, DIF=%.2f, DEA=%.2f, MACD_hist=%.2f}",
- close.doubleValue(), emaShort.doubleValue(), emaLong.doubleValue(),
- dif.doubleValue(), dea.doubleValue(), macdHist.doubleValue());
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/Volatility.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/Volatility.java
deleted file mode 100644
index f4d1543..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/Volatility.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/**
- * 波动率指标计算类
- * <p>
- * 波动率是衡量金融市场价格波动程度的指标,通常用价格的标准差与平均值的比率表示。
- * 本类实现了基于滚动窗口的波动率计算,通过标准差与平均值的比值计算出百分比形式的波动率。
- */
-package com.xcong.excoin.modules.okxNewPrice.indicator.macdAndMatrategy;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * 波动率指标实现类
- * <p>波动率计算原理:使用标准差与平均值的比值,以百分比形式表示价格的波动程度。</p>
- * <p>计算公式:波动率 = (标准差 / 平均值) * 100%</p>
- *
- * <p>使用示例:</p>
- * <pre>
- * // 初始化20日波动率计算器
- * Volatility vol = new Volatility(20);
- *
- * // 动态添加每日价格
- * priceFeed.subscribe(price -> {
- * vol.addPrice(price);
- * vol.calculate();
- * });
- *
- * // 判断是否满足低波动条件(<1%)
- * if (vol.getValue().compareTo(new BigDecimal("1.00")) < 0) {
- * System.out.println("低波动市场,暂停交易");
- * }
- * </pre>
- */
-public class Volatility {
- /** 波动率计算的周期(如20日波动率) */
- private final int period;
-
- /** 当前计算出的波动率值(百分比形式) */
- private BigDecimal volatility = BigDecimal.ZERO;
-
- /** 使用LinkedList存储滚动窗口内的价格数据,便于添加和删除操作 */
- private LinkedList<BigDecimal> priceWindow = new LinkedList<>();
-
- /** 窗口内价格的总和,用于快速计算平均值 */
- private BigDecimal sum = BigDecimal.ZERO;
-
- /** 窗口内价格平方的总和,用于快速计算方差 */
- private BigDecimal sumSquares = BigDecimal.ZERO;
-
- /**
- * 构造函数,创建指定周期的波动率计算器
- *
- * @param period 波动率计算周期,如20表示计算20日波动率
- */
- public Volatility(int period) {
- this.period = period;
- }
-
- /**
- * 添加新价格到计算窗口,并维护窗口内的价格数据
- * 采用滑动窗口方式,当价格数量超过周期时,自动移除最早的价格
- *
- * @param price 新的价格数据,使用BigDecimal确保计算精度
- * @throws IllegalArgumentException 当价格为null时抛出异常
- */
- public void addPrice(BigDecimal price) {
- if (price == null) {
- throw new IllegalArgumentException("Price cannot be null");
- }
-
- // 当窗口大小达到周期时,移除最早的价格,并从总和中减去
- if (priceWindow.size() == period) {
- BigDecimal removed = priceWindow.removeFirst();
- sum = sum.subtract(removed);
- sumSquares = sumSquares.subtract(removed.pow(2));
- }
-
- // 添加新价格到窗口,并更新总和
- priceWindow.add(price);
- sum = sum.add(price);
- sumSquares = sumSquares.add(price.pow(2));
- }
-
- /**
- * 计算当前窗口内价格的波动率
- * 使用标准差与平均值的比值计算波动率百分比
- */
- public void calculate() {
- // 数据点不足,无法计算波动率
- if (priceWindow.size() < period) {
- return;
- }
-
- // 计算平均值:sum / period
- BigDecimal avg = sum.divide(new BigDecimal(period), 8, RoundingMode.HALF_UP);
-
- // 防止除以零的情况
- if (avg.compareTo(BigDecimal.ZERO) == 0) {
- volatility = BigDecimal.ZERO;
- return;
- }
-
- // 计算方差:(sumSquares / period) - avg^2
- BigDecimal variance = sumSquares.divide(new BigDecimal(period), 8, RoundingMode.HALF_UP)
- .subtract(avg.pow(2));
-
- // 确保方差非负(防止浮点数计算误差导致负数方差)
- variance = variance.max(BigDecimal.ZERO);
-
- // 计算标准差:sqrt(variance)
- BigDecimal stdDev = sqrt(variance, 8);
-
- // 计算波动率:(标准差 / 平均值) * 100%
- volatility = stdDev.divide(avg, 8, RoundingMode.HALF_UP)
- .multiply(new BigDecimal(100))
- .setScale(2, RoundingMode.HALF_UP);
- }
-
- /**
- * 计算BigDecimal的平方根(使用牛顿迭代法)
- *
- * @param value 要计算平方根的数值
- * @param scale 结果的精度(小数位数)
- * @return 平方根结果
- */
- private BigDecimal sqrt(BigDecimal value, int scale) {
- // 负数没有实数平方根,返回0
- if (value.compareTo(BigDecimal.ZERO) < 0) {
- return BigDecimal.ZERO;
- }
-
- // 使用牛顿迭代法计算平方根
- BigDecimal x = value.divide(new BigDecimal(2), scale, RoundingMode.HALF_UP); // 初始猜测值
- BigDecimal prev;
-
- do {
- prev = x;
- // 牛顿迭代公式:x(n+1) = (x(n) + value/x(n))/2
- // 添加零检查,防止除以零异常
- if (x.compareTo(BigDecimal.ZERO) == 0) {
- x = new BigDecimal(1); // 设置一个合理的初始值
- }
- x = x.add(value.divide(x, scale, RoundingMode.HALF_UP)).divide(new BigDecimal(2), scale, RoundingMode.HALF_UP);
- } while (x.subtract(prev).abs().compareTo(BigDecimal.ONE.movePointLeft(scale)) > 0); // 直到满足精度要求
-
- return x;
- }
-
- /**
- * 获取最新的波动率计算结果
- *
- * @return 波动率值,以百分比形式表示(例如:2.5表示2.5%)
- */
- public BigDecimal getValue() {
- return volatility;
- }
-}
\ No newline at end of file
diff --git "a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/\345\206\205\345\256\271" "b/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/\345\206\205\345\256\271"
deleted file mode 100644
index 16e57d4..0000000
--- "a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/macdAndMatrategy/\345\206\205\345\256\271"
+++ /dev/null
@@ -1,107 +0,0 @@
-MACD 是什麼?三個核心組成介紹
-MACD 全名為 Moving Average Convergence Divergence,中文為平滑異同移動平均線,它主要是透過快線 DIF、慢線 DEA、能量柱 Histogram三個成分組成,用以幫助投資者捕捉股價或資產價格的趨勢變化、動能強弱以及潛在的買賣訊號。
-
-MACD
-
-MACD 指標主要由三個核心構成,每個核心皆有各自的涵義,讓投資者對於市場動態清楚明瞭:
-
-快線 DIF(DIFferential Line)-差離值
-
-DIF 是 MACD 的核心快線,是 MACD 中主要用來判斷市場長短期趨勢差異的工具,DIF 波動較快的特性也使得投資者可以透過其捕捉更敏銳的短期變化,當 DIF 上漲時表示短期動能增強,反之則減弱。
-
-快線 DIF(DIFferential Line)
-
-慢線 DEA(DIFference Exponential Average)-訊號線
-
-慢線 DEA 又稱為 Signal 訊號線,曲線通常較為平滑,能夠用以輔佐投資者過濾 DIF 的雜訊,提供更穩定的趨勢確認訊號。當 DIF 穿越 DEA 向上時,常被視作買入訊號(黃金交叉),反之則為賣出訊號(死亡交叉)。
-
-慢線 DEA(DIFference Exponential Average)-訊號線
-
-能量柱(Histogram)
-
-Histogram 是 MACD 的柱狀圖部分,由快線 DIF 減去慢線 DEA 得來,柱狀圖的高度反映快線與慢線的差距,當正柱狀圖(零軸之上)愈來愈高時表示多頭動能增強,負柱狀圖(零軸之下)愈來愈低時表示空頭動能增強,而柱狀圖逐漸收斂時則可能表示趨勢即將反轉。
-
-能量柱(Histogram)
-
-MACD公式計算詳細解析
-要深入理解 MACD 指標首先必須先掌握其數值的算法並得知運算結果,MACD 的計算主要基於指數移動平均線(EMA),並通過一系列簡單的數學公式計算得出 DIF、DEA 和柱狀圖。
-
-EMA(Exponential Moving Average)介紹
-
-EMA 中文為指數移動平均線,與簡單移動平均線(SMA)不同的是,EMA 對近期價格數據給予更高的權重,使得它對價格變化的反應更為靈敏。
-
-EMA 的運算公式為:【今日收盤價 × α】 + 【昨日 EMA × (1 − α)】
-
-其中,α(平滑因子)= 2/(N + 1),N 為選定的周期數。例如,12 期 EMA 的 α = 2/(12 + 1) ≈ 0.1538。初次計算 EMA 時,若無前一日的 EMA,可使用該周期的簡單移動平均(SMA)作為起點。EMA 的靈敏性使 MACD 更能快速反映市場動態。
-
-EMA
-
-快線 DIF 計算方式
-
-DIF 作為 MACD 核心之一,計算方式為:EMA(12)-EMA(26)。
-
-EMA 12 代表短期價格趨勢(較快反應),而 EMA 26 代表長期趨勢(較平滑)。當短期 EMA 高於長期 EMA 時,DIF 為正值,表示短期動能強於長期動能,暗示看漲;反之,DIF 為負值則暗示看跌。這之間的差值能幫助投資者快速判斷趨勢的強弱與方向。
-
-慢線 DEA 計算方式
-
-DEA 計算方式為:EMA(DIF,9)
-
-這樣的計算方式將 DIF 的曲線進一步平滑,減少短期價格噪音的干擾,提供更穩定的趨勢指標。DEA 線的作用在於與 DIF 形成交叉訊號,例如 DIF 上穿 DEA 線(黃金交叉)通常被視為買入訊號,而下穿(死亡交叉)則為賣出訊號。
-
-柱狀圖 Histogram 計算方式
-
-Histogram 作為 MACD 指標唯一的圖形,計算方式為:DIF − DEA
-
-柱狀圖直觀顯示快線與慢線的差距,當 DIF 大於 DEA 時,柱狀圖為正時,表示多頭動能增強,反之柱狀圖為負時,表示空頭力量占優。
-
-MACD 公式實盤範例解析
-為了更直觀的理解 MACD 在實盤上的計算過程,我們以幣安的 ETHUSDT 圖表進行 MACD 試算。
-
-第一步驟:新增 EMA 指標
-
-由於 MACD 中的快線、慢線計算過程中皆會使用到 EMA 指標,因此我們可以直接新增 EMA 指標並進行參數調整即可計算 MACD 相關參數。
-
-第二步驟:計算 MACD 中的快線 DIF
-
-快線 DIF 的計算方式為 EMA(12)-EMA(26),因此我們只要將兩條 EMA 指標分別設定為 12、26 並進行相減即可,例如下圖中 EMA 12 為 4271.55,EMA 26 為 3941.88,相減即得 329.67,這一數字與 MACD 中顯示的快線 DIF 相同。
-
-第三步驟:計算 MACD 中的慢線 DEA
-
-慢線的公式為:EMA(DIF,9),其中 9 代表周期數,EMA 的計算使用平滑因子 α = 2 ÷ (9 + 1) = 0.2,因此 MACD 中的慢線 DEA 計算公式為:【今日 DIF*0.2】+【昨日 DEA*0.8】。
-
-今日 DIF 為 329.67,而昨日 DEA 為 250.86,套入計算公式 329.67*0.2+250.86*0.8=266.62,這一數字同樣與 MACD 中顯示的慢線 DEA 相同。
-
-第四步驟:計算 MACD 中的柱狀圖 Histogram
-
-只要解出快線及慢線,要求出 MACD 中的柱狀圖就十分容易,僅需將快線減去慢線即可。上述計算出快線 DIF 數值為 329.67,而慢線 DEA 數值為 266.62,相減即可得出柱狀圖為 63.05,如此一來就得出 MACD 指標所需的所有數據。
-
-MACD公式
-
-MACD黃金/死亡交叉案例
-黃金交叉與死亡交叉是 MACD 指標中最為人所知的交易訊號,許多交易者會藉由黃金交叉與死亡交叉判斷行情趨勢。
-
-首先我們可以觀察到 ETHUSDT 在 4 月 13 號時快線 DIF 上穿慢線 DEA 形成有效金叉後,接下來不斷上漲,甚至直接開啟以太坊牛市。
-
-MACD金叉案例
-
-而再將時間往回推,可以看到在 2024 年 12 月 9 號時 MACD 快線下穿慢線形成死叉,並在接下來回撤超過 60%,當時若觀察到 MACD 為死叉的投資者即可避免該次下跌甚至進行空單布局。
-
-MACD死叉範例
-
-常見問題
-為什麼有的版本能量柱( Histogram) 會*2?
-
-有些版本的 MACD 會將柱狀圖放大一倍,目的是為了讓視覺效果更明顯,投資者即可更直接的觀察快線與慢線之間的差距變化。
-
-金叉、死叉一定會上漲、下跌嗎?
-
-不一定,金融市場中並沒有哪一個指標可以保證上漲、下跌,MACD 也不例外。且包括 MACD 在內的所有指標皆具有一定的延遲性,其中可能包含雜訊,因此建議搭配技術分析或其他指標一同使用。
-
-MACD 的參數可以修改嗎?
-
-MACD 的參數是可以隨個人喜好修改的,例如(5, 13, 5)適用於短期交易,(50, 200, 20) 則適合長期趨勢分析,不過修改 MACD 參數會影響指標的靈敏度,建議回測歷史數據以找到最適合的設定。
-
-小結
-本篇文章解析了 MACD 的核心,從 MACD 的計算基礎 EMA 平均線到快線、慢線和柱狀圖三大核心,以及結合 ETHUSDT 圖表展示了 EMA 推導 MACD 數值的計算過程,但指標真正的價值在於如何靈活應用並提升勝率。因此看完這篇文章的你如果對於 MACD 有濃厚興趣的話,趕快打開 MACD 指標並進行深入的覆盤研究吧!
-
-本報告僅供資訊分享之用,內容不構成任何形式的投資建議或決策依據。文中所引用的數據、分析與觀點均基於作者的研究與公開來源,可能存在不確定性或隨時變動的情況。讀者應根據自身情況及風險承受能力,審慎進行投資判斷。如需進一步指導,建議尋求專業顧問意見。
\ No newline at end of file
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
deleted file mode 100644
index a853473..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/AbstractTechnicalIndicatorStrategy.java
+++ /dev/null
@@ -1,89 +0,0 @@
-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
deleted file mode 100644
index a6c320c..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/ComprehensiveTechnicalStrategy.java
+++ /dev/null
@@ -1,351 +0,0 @@
-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
deleted file mode 100644
index 61469c5..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/CoreTechnicalStrategy.java
+++ /dev/null
@@ -1,589 +0,0 @@
-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
deleted file mode 100644
index 0e56433..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/CoreTechnicalStrategyUsageGuide.md
+++ /dev/null
@@ -1,198 +0,0 @@
-# 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
deleted file mode 100644
index 65b4d95..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/TechnicalIndicatorStrategy.java
+++ /dev/null
@@ -1,44 +0,0 @@
-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
deleted file mode 100644
index e1c05da..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/indicator/strategy/TradeSignal.java
+++ /dev/null
@@ -1,54 +0,0 @@
-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;
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/jiaoyi/IMQService.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/jiaoyi/IMQService.java
deleted file mode 100644
index 411481c..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/jiaoyi/IMQService.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.jiaoyi;
-
-import com.xcong.excoin.modules.okxNewPrice.okxpi.trade.TradeRequestBuy;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.trade.TradeRequestSell;
-
-public interface IMQService {
-
-
-
- /**
- * 消费买入消息
- * @param returnVo
- */
- void operationBuyMsg(TradeRequestBuy returnVo);
-
-
- /**
- * 消费卖出消息
- * @param returnVo
- */
- void operationSellMsg(TradeRequestSell returnVo);
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/jiaoyi/IMQServiceImpl.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/jiaoyi/IMQServiceImpl.java
deleted file mode 100644
index 4ac6153..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/jiaoyi/IMQServiceImpl.java
+++ /dev/null
@@ -1,198 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.jiaoyi;
-
-import cn.hutool.core.util.ObjectUtil;
-import cn.hutool.json.JSONUtil;
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.MallUtils;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.Dto.QuantApiMessage;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.OKXAccount;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.enumerates.TradeTypeEnum;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.trade.TradeEventEnum;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.trade.TradeEventRunner;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.trade.TradeRequestBuy;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.trade.TradeRequestSell;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.verify.VerifyAccountFactory;
-import com.xcong.excoin.modules.okxNewPrice.zhanghu.IApiMessageService;
-import com.xcong.excoin.modules.okxNewPrice.zhanghu.ZhangHuEnum;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-import java.math.BigDecimal;
-import java.util.LinkedHashMap;
-
-/**
- * @author Administrator
- */
-@Slf4j
-@Service
-@RequiredArgsConstructor
-public class IMQServiceImpl implements IMQService{
-
- private final IApiMessageService apiMessageService;
-
- private final VerifyAccountFactory verifyAccountFactory;
- @Override
- public void operationBuyMsg(TradeRequestBuy returnVo) {
-
- log.info("买入入参:{}", JSON.toJSONString(returnVo));
- /**
- * 1、获取用户的策略信息
- * 2、quant_operate_recode 记录表新增一条记录,finish_status 为1.未完成
- * 3、发起OKX接口调用
- */
- QuantApiMessage quantApiMessage = apiMessageService.getApiMessage(ZhangHuEnum.JIAOYISUO.getValue());
- if(ObjectUtil.isEmpty(quantApiMessage)){
- return;
- }
-
- OKXAccount okxAccount = (OKXAccount) verifyAccountFactory.getAccountMap()
- .get(quantApiMessage.getExchange())
- .initAccount(quantApiMessage);
- if(ObjectUtil.isEmpty(okxAccount)){
- return;
- }
-
- LinkedHashMap<String, Object> requestParam = new LinkedHashMap<>();
- requestParam.put("instId", returnVo.getInstId()+"-SWAP");
- /**
- * 订单方向
- * buy:买, sell:卖
- */
- requestParam.put("side", TradeTypeEnum.BUY.getValue());
- /**
- * 订单类型
- * market:市价单
- * limit:限价单
- * post_only:只做maker单
- * fok:全部成交或立即取消
- * ioc:立即成交并取消剩余
- * optimal_limit_ioc:市价委托立即成交并取消剩余(仅适用交割、永续)
- * mmp:做市商保护(仅适用于组合保证金账户模式下的期权订单)
- * mmp_and_post_only:做市商保护且只做maker单(仅适用于组合保证金账户模式下的期权订单)
- */
- String type = (BigDecimal.ZERO.compareTo(new BigDecimal(returnVo.getLimitPrice())) == 0) ? TradeTypeEnum.MARKET.getValue() : TradeTypeEnum.LIMIT.getValue();
- requestParam.put("ordType", type);
- if (TradeTypeEnum.LIMIT.getValue().equals(type)) {
- requestParam.put("px", returnVo.getLimitPrice());
- requestParam.put("ordType", TradeTypeEnum.LIMIT.getValue());
- }
- /**
- * 持仓方向
- * 在开平仓模式下必填,且仅可选择 long 或 short。 仅适用交割、永续。
- */
- String positionSide = (TradeTypeEnum.LONG.getCode() == returnVo.getPositionSide()) ? TradeTypeEnum.LONG.getValue() : TradeTypeEnum.SHORT.getValue();
- requestParam.put("posSide", positionSide);
- /**
- * 交易模式
- * 保证金模式:isolated:逐仓 ;cross:全仓
- * 非保证金模式:cash:非保证金
- * spot_isolated:现货逐仓(仅适用于现货带单) ,现货带单时,tdMode 的值需要指定为spot_isolated
- */
- String tdMode = (TradeTypeEnum.ISOLATED.getCode() == returnVo.getTdMode()) ? TradeTypeEnum.ISOLATED.getValue() : TradeTypeEnum.CROSS.getValue();
- requestParam.put("tdMode", tdMode);
- /**
- * 委托数量
- */
- requestParam.put("sz", returnVo.getTradeCnt());
- /**
- * 客户自定义订单ID
- * 字母(区分大小写)与数字的组合,可以是纯字母、纯数字且长度要在1-32位之间。
- */
- String clOrdId = MallUtils.getOrderNum();
- requestParam.put("clOrdId", clOrdId);
-
- String placeOrderRspOkxRestResponse = TradeEventRunner.runTradeEvent(TradeEventEnum.TRADE_BUY.getEventPoint(),requestParam,okxAccount);
- log.info("下买单结果成功:{}", placeOrderRspOkxRestResponse);
- log.info("消费成功:{}", JSONUtil.parseObj(returnVo));
- }
-
- @Override
- public void operationSellMsg(TradeRequestSell returnVo) {
- /**
- * 1、获取用户的策略信息
- * 2、quant_operate_recode 记录表新增一条记录,finish_status 为1.未完成
- * 3、发起OKX接口调用
- */
- QuantApiMessage quantApiMessage = apiMessageService.getApiMessage(ZhangHuEnum.JIAOYISUO.getValue());
- if(ObjectUtil.isEmpty(quantApiMessage)){
- return;
- }
-
- OKXAccount okxAccount = (OKXAccount) verifyAccountFactory.getAccountMap()
- .get(quantApiMessage.getExchange())
- .initAccount(quantApiMessage);
- if(ObjectUtil.isEmpty(okxAccount)){
- return;
- }
- LinkedHashMap<String, Object> requestParam = new LinkedHashMap<>();
-
- requestParam.put("instId", returnVo.getInstId()+"-SWAP");
- /**
- * 订单方向
- * buy:买, sell:卖
- */
- requestParam.put("side", TradeTypeEnum.SELL.getValue());
- /**
- * 订单类型
- * market:市价单
- * limit:限价单
- * post_only:只做maker单
- * fok:全部成交或立即取消
- * ioc:立即成交并取消剩余
- * optimal_limit_ioc:市价委托立即成交并取消剩余(仅适用交割、永续)
- * mmp:做市商保护(仅适用于组合保证金账户模式下的期权订单)
- * mmp_and_post_only:做市商保护且只做maker单(仅适用于组合保证金账户模式下的期权订单)
- */
- String type = (BigDecimal.ZERO.compareTo(new BigDecimal(returnVo.getLimitPrice())) == 0) ? TradeTypeEnum.MARKET.getValue() : TradeTypeEnum.LIMIT.getValue();
- requestParam.put("ordType", type);
- if (TradeTypeEnum.LIMIT.getValue().equals(type)) {
- requestParam.put("px", returnVo.getLimitPrice());
- requestParam.put("ordType", TradeTypeEnum.LIMIT.getValue());
- }
- /**
- * 持仓方向
- * 在开平仓模式下必填,且仅可选择 long 或 short。 仅适用交割、永续。
- */
- String positionSide = (TradeTypeEnum.LONG.getCode() == returnVo.getPositionSide()) ? TradeTypeEnum.LONG.getValue() : TradeTypeEnum.SHORT.getValue();
- requestParam.put("posSide", positionSide);
- /**
- * 交易模式
- * 保证金模式:isolated:逐仓 ;cross:全仓
- * 非保证金模式:cash:非保证金
- * spot_isolated:现货逐仓(仅适用于现货带单) ,现货带单时,tdMode 的值需要指定为spot_isolated
- */
- String tdMode = (TradeTypeEnum.ISOLATED.getCode() == returnVo.getTdMode()) ? TradeTypeEnum.ISOLATED.getValue() : TradeTypeEnum.CROSS.getValue();
- requestParam.put("tdMode", tdMode);
- /**
- * 委托数量
- */
- requestParam.put("sz", returnVo.getTradeCnt());
- /**
- * 客户自定义订单ID
- * 字母(区分大小写)与数字的组合,可以是纯字母、纯数字且长度要在1-32位之间。
- */
- String clOrdId = MallUtils.getOrderNum();
- requestParam.put("clOrdId", clOrdId);
-
-
- String placeOrderRspOkxRestResponse = TradeEventRunner.runTradeEvent(TradeEventEnum.TRADE_SELL.getEventPoint(), requestParam, okxAccount);
-
- String code = JSON.parseObject(placeOrderRspOkxRestResponse).get("code").toString();
- if("1".equals(code)){
- String data = JSON.parseObject(placeOrderRspOkxRestResponse).get("data").toString();
-
- JSONArray jsonArray = JSON.parseArray(data);
- JSONObject jsonObject = jsonArray.getJSONObject(0);
-
- String sCode = jsonObject.getString("sCode");
- if("51169".equals(sCode)){
- log.warn("平仓下单返回结果-失败:{}", data);
- }
- }
- log.info("平仓下单返回结果:{}", placeOrderRspOkxRestResponse);
- log.info("消费成功:{}", JSONUtil.parseObj(returnVo));
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/AccountWs.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/AccountWs.java
deleted file mode 100644
index 81188a8..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/AccountWs.java
+++ /dev/null
@@ -1,160 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxWs;
-
-import cn.hutool.json.JSONUtil;
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.CoinEnums;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.OrderParamEnums;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.MallUtils;
-import com.xcong.excoin.modules.okxNewPrice.utils.WsMapBuild;
-import com.xcong.excoin.modules.okxNewPrice.utils.WsParamBuild;
-import io.micrometer.core.instrument.util.JsonUtils;
-import lombok.extern.slf4j.Slf4j;
-import org.java_websocket.client.WebSocketClient;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * 账户 WebSocket 处理类,用于订阅 OKX 的账户频道并处理账户信息推送。
- * 包含账户资金状态判断、计算下单保证金及保存到 Redis 的逻辑。
- *
- * @author Administrator
- */
-@Slf4j
-public class AccountWs {
-
- // 使用双层Map,第一层key为账号名称,第二层key为数据key
- private static final Map<String, Map<String, String>> ACCOUNTWSMAP = new ConcurrentHashMap<>();
-
- // 获取指定账号的Map,如果不存在则创建
- public static Map<String, String> getAccountMap(String accountName) {
- return ACCOUNTWSMAP.computeIfAbsent(accountName, k -> new ConcurrentHashMap<>());
- }
- /**
- * 账户频道名称常量
- */
- public static final String ACCOUNTWS_CHANNEL = "account";
-
- /**
- * 订阅账户频道
- *
- * @param webSocketClient WebSocket 客户端实例
- * @param option 请求选项(如 unsubscribe 或 subscribe)
- */
- public static void subscribeAccountChannel(WebSocketClient webSocketClient, String option) {
- try {
- JSONArray argsArray = new JSONArray();
- JSONObject args = new JSONObject();
- args.put("channel", ACCOUNTWS_CHANNEL);
- args.put("ccy", CoinEnums.USDT.getCode());
- argsArray.add(args);
-
- String connId = MallUtils.getOrderNum(ACCOUNTWS_CHANNEL);
- JSONObject jsonObject = WsParamBuild.buildJsonObject(connId, option, argsArray);
- webSocketClient.send(jsonObject.toJSONString());
-// log.info("发送账户频道:{}", option);
- } catch (Exception e) {
- log.error("订阅账户频道构建失败", e);
- }
- }
-
- public static void initEvent(JSONObject response, String accountName) {
-// log.info("订阅成功: {}", response.getJSONObject("arg"));
- JSONObject arg = response.getJSONObject("arg");
- initParam(arg, accountName);
- }
-
- /**
- * 处理账户频道推送的数据
- *
- * @param response 推送的 JSON 数据对象
- * @param accountName 账号名称
- */
- public static void handleEvent(JSONObject response, String accountName) {
-
-
-// log.info("开始执行AccountWs......{}",ACCOUNTWS_CHANNEL);
- try {
- JSONArray dataArray = response.getJSONArray("data");
- if (dataArray == null || dataArray.isEmpty()) {
- log.warn("账户频道数据为空");
- return;
- }
-
- for (int i = 0; i < dataArray.size(); i++) {
- try {
- JSONObject accountData = dataArray.getJSONObject(i);
- JSONArray detailsArray = accountData.getJSONArray("details");
- if (detailsArray == null || detailsArray.isEmpty()) {
- log.warn("账户频道{}数据为空",CoinEnums.USDT.getCode());
- continue;
- }
-
- for (int j = 0; j < detailsArray.size(); j++) {
- JSONObject detail = detailsArray.getJSONObject(j);
- initParam(detail, accountName);
-
- Map<String, String> accountMap = getAccountMap(accountName);
- WsMapBuild.saveStringToMap(accountMap, CoinEnums.READY_STATE.name(), CoinEnums.READY_STATE_YES.getCode());
- }
- } catch (Exception innerEx) {
- log.warn("处理账户频道数据失败", innerEx);
- }
- }
- } catch (Exception e) {
- log.error("处理账户频道推送数据失败", e);
- }
- }
-
-
- public static final String ccyKey = "ccy";
- public static final String availBalKey = "availBal";
- public static final String cashBalKey = "cashBal";
- public static final String eqKey = "eq";
- public static final String uplKey = "upl";
- public static final String imrKey = "imr";
- private static void initParam(JSONObject detail, String accountName) {
- Map<String, String> accountMap = getAccountMap(accountName);
-
- String ccy = WsMapBuild.parseStringSafe( detail.getString(ccyKey));
- WsMapBuild.saveStringToMap(accountMap, ccyKey, ccy);
-
- String availBal = WsMapBuild.parseStringSafe(detail.getString(availBalKey));
- WsMapBuild.saveStringToMap(accountMap, availBalKey, availBal);
-
- String cashBal = WsMapBuild.parseStringSafe(detail.getString(cashBalKey));
- WsMapBuild.saveStringToMap(accountMap, cashBalKey, cashBal);
-
- String eq = WsMapBuild.parseStringSafe(detail.getString(eqKey));
- WsMapBuild.saveStringToMap(accountMap, eqKey, eq);
-
- String upl = WsMapBuild.parseStringSafe(detail.getString(uplKey));
- WsMapBuild.saveStringToMap(accountMap, uplKey, upl);
-
- String imr = WsMapBuild.parseStringSafe(detail.getString(imrKey));
- WsMapBuild.saveStringToMap(accountMap, imrKey, imr);
- BigDecimal cashBalDecimal = WsMapBuild.parseBigDecimalSafe(cashBal);
- // 根据可用余额计算下单总保证金
- String total_order_usdtpecent = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.TOTAL_ORDER_USDTPECENT.name());
- BigDecimal total_order_usdt_factor = WsMapBuild.parseBigDecimalSafe(total_order_usdtpecent);
- BigDecimal totalOrderUsdt = cashBalDecimal.multiply(total_order_usdt_factor).setScale(2, RoundingMode.DOWN);
- WsMapBuild.saveStringToMap(accountMap, CoinEnums.TOTAL_ORDER_USDT.name(), String.valueOf(totalOrderUsdt));
-
- /**
- * 当前账户未满仓,并且账户余额不为0,才更新为已就绪
- */
- BigDecimal imrDecimal = WsMapBuild.parseBigDecimalSafe(imr);
- if (BigDecimal.ZERO.compareTo(cashBalDecimal) < 0 && imrDecimal.compareTo(totalOrderUsdt) < 0){
- WsMapBuild.saveStringToMap(accountMap, CoinEnums.READY_STATE.name(), CoinEnums.READY_STATE_YES.getCode());
- }
-
-// log.info(
-// "{}: 账户详情-币种: {}, 可用余额: {}, 现金余额: {}, 余额: {}, 全仓未实现盈亏: {}, 下单总保证金: {},已使用保证金:{}",
-// accountName, ccy, availBal, cashBal, eq, upl, totalOrderUsdt,imr
-// );
- }
-}
-
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/BalanceAndPositionWs.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/BalanceAndPositionWs.java
deleted file mode 100644
index 9667283..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/BalanceAndPositionWs.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxWs;
-
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.MallUtils;
-import com.xcong.excoin.modules.okxNewPrice.utils.WsParamBuild;
-import lombok.extern.slf4j.Slf4j;
-import org.java_websocket.client.WebSocketClient;
-
-/**
- * @author Administrator
- */
-@Slf4j
-public class BalanceAndPositionWs {
-
- public static final String CHANNEL_NAME = "balance_and_position";
- private static final String LOG_PREFIX = "账户余额和持仓频道";
-
- public static void subscribeBalanceAndPositionChannel(WebSocketClient webSocketClient, String option) {
- try {
- JSONArray argsArray = new JSONArray();
- JSONObject args = new JSONObject();
- args.put("channel", CHANNEL_NAME);
- argsArray.add(args);
-
- String connId = MallUtils.getOrderNum("bap");
- JSONObject jsonObject = WsParamBuild.buildJsonObject(connId, option, argsArray);
- webSocketClient.send(jsonObject.toJSONString());
- log.info("账户余额和持仓频道订阅成功: {}", LOG_PREFIX, option);
- } catch (Exception e) {
- log.error("账户余额和持仓频道订阅构建失败", LOG_PREFIX, e);
- }
- }
-
- public static void handleEvent(JSONObject response) {
-
- log.info("开始执行BalanceAndPositionWs......");
- try {
- JSONArray dataArray = response.getJSONArray("data");
- if (dataArray == null || dataArray.isEmpty()) {
- log.warn("{}数据为空", LOG_PREFIX);
- return;
- }
-
- JSONObject firstData = dataArray.getJSONObject(0);
- processBalData(firstData);
- processPosData(firstData);
- } catch (Exception e) {
- log.error("{}推送数据处理失败", LOG_PREFIX, e);
- }
- }
-
- private static void processBalData(JSONObject dataObject) {
- JSONArray balDataArray = dataObject.getJSONArray("balData");
- if (balDataArray == null || balDataArray.isEmpty()) {
- return;
- }
-
- for (int i = 0; i < balDataArray.size(); i++) {
- JSONObject balData = balDataArray.getJSONObject(i);
- if (!balData.containsKey("ccy") || !balData.containsKey("cashBal") || !balData.containsKey("uTime")) {
- continue;
- }
-
- String ccy = balData.getString("ccy");
- String cashBal = balData.getString("cashBal");
- String uTime = balData.getString("uTime");
-
- log.info("币种: {}, 余额: {}, 更新时间: {}", ccy, cashBal, uTime);
- }
- }
-
- private static void processPosData(JSONObject dataObject) {
- JSONArray posDataArray = dataObject.getJSONArray("posData");
- if (posDataArray == null || posDataArray.isEmpty()) {
- return;
- }
-
- for (int i = 0; i < posDataArray.size(); i++) {
- JSONObject posData = posDataArray.getJSONObject(i);
- if (!posData.containsKey("posId") || !posData.containsKey("instId")
- || !posData.containsKey("instType") || !posData.containsKey("posSide")
- || !posData.containsKey("pos") || !posData.containsKey("avgPx")
- || !posData.containsKey("ccy")) {
- continue;
- }
-
- String posId = posData.getString("posId");
- String instId = posData.getString("instId");
- String instType = posData.getString("instType");
- String posSide = posData.getString("posSide");
- String pos = posData.getString("pos");
- String avgPx = posData.getString("avgPx");
- String ccy = posData.getString("ccy");
-
- log.info("持仓ID: {}, 产品ID: {}, 类型: {}, 方向: {}, 数量: {}, 平均价: {}, 币种: {}",
- posId, instId, instType, posSide, pos, avgPx, ccy);
- }
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/InstrumentsWs.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/InstrumentsWs.java
deleted file mode 100644
index 334443e..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/InstrumentsWs.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxWs;
-
-import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.CoinEnums;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.OrderParamEnums;
-import com.xcong.excoin.modules.okxNewPrice.utils.WsMapBuild;
-import lombok.extern.slf4j.Slf4j;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * @author Administrator
- */
-@Slf4j
-public class InstrumentsWs {
-
- public static final Map<String, Map<String, String>> INSTRUMENTSWSMAP = new ConcurrentHashMap<>();
-
- public static final String INSTRUMENTSWS_CHANNEL = "instruments";
-
- public static Map<String, String> getAccountMap(String accountName) {
- return INSTRUMENTSWSMAP.computeIfAbsent(accountName, k -> new ConcurrentHashMap<>());
- }
-
- public static void handleEvent(String accountName) {
-// log.info("开始执行InstrumentsWs......");
-
- Map<String, String> accountMap = getAccountMap(accountName);
- WsMapBuild.saveStringToMap(accountMap, CoinEnums.HE_YUE.name(), CoinEnums.HE_YUE.getCode());
- WsMapBuild.saveStringToMap(accountMap, CoinEnums.CTVAL.name(), CoinEnums.CTVAL.getCode());
- WsMapBuild.saveStringToMap(accountMap, CoinEnums.TICKSZ.name(), CoinEnums.TICKSZ.getCode());
- WsMapBuild.saveStringToMap(accountMap, CoinEnums.MINSZ.name(), CoinEnums.MINSZ.getCode());
- WsMapBuild.saveStringToMap(accountMap, CoinEnums.OUT.name(), OrderParamEnums.OUT_NO.getValue());
- WsMapBuild.saveStringToMap(accountMap, CoinEnums.LEVERAGE.name(), CoinEnums.LEVERAGE.getCode());
- WsMapBuild.saveStringToMap(accountMap, CoinEnums.BUY_CNT.name(), CoinEnums.BUY_CNT.getCode());
- WsMapBuild.saveStringToMap(accountMap, CoinEnums.BUY_CNT_INIT.name(), CoinEnums.BUY_CNT_INIT.getCode());
- WsMapBuild.saveStringToMap(accountMap, CoinEnums.BUY_CNT_TIME.name(), CoinEnums.BUY_CNT_TIME.getCode());
- WsMapBuild.saveStringToMap(accountMap, CoinEnums.ZHI_SUN.name(), CoinEnums.ZHI_SUN.getCode());
- WsMapBuild.saveStringToMap(accountMap, CoinEnums.KANG_CANG.name(), CoinEnums.KANG_CANG.getCode());
- WsMapBuild.saveStringToMap(accountMap, CoinEnums.PING_CANG_SHOUYI.name(), CoinEnums.PING_CANG_SHOUYI.getCode());
- WsMapBuild.saveStringToMap(accountMap, CoinEnums.TOTAL_ORDER_USDTPECENT.name(), CoinEnums.TOTAL_ORDER_USDTPECENT.getCode());
- WsMapBuild.saveStringToMap(accountMap, CoinEnums.CONTRACTMULTIPLIER.name(), CoinEnums.CONTRACTMULTIPLIER.getCode());
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/LoginWs.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/LoginWs.java
deleted file mode 100644
index eb3e416..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/LoginWs.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxWs;
-
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.ExchangeInfoEnum;
-import com.xcong.excoin.modules.okxNewPrice.utils.SignUtils;
-import com.xcong.excoin.modules.okxNewPrice.utils.WsParamBuild;
-import lombok.extern.slf4j.Slf4j;
-import org.java_websocket.client.WebSocketClient;
-
-/**
- * @author Administrator
- */
-@Slf4j
-public class LoginWs {
- public static void websocketLogin(WebSocketClient webSocketClient, ExchangeInfoEnum account) {
-
-// log.info("开始执行LoginWs......");
- try {
-
- JSONArray argsArray = new JSONArray();
- JSONObject loginArgs = new JSONObject();
- // 获取登录凭证信息(需要从配置或Redis中获取)
- String apiKey = account.getApiKey();
- String passphrase = account.getPassphrase();
- String timestamp = String.valueOf(System.currentTimeMillis() /1000);
- String sign = SignUtils.signWebsocket(timestamp, account.getSecretKey());
-
- loginArgs.put("apiKey", apiKey);
- loginArgs.put("passphrase", passphrase);
- loginArgs.put("timestamp", timestamp);
- loginArgs.put("sign", sign);
- argsArray.add(loginArgs);
-
- String option = "login";
- JSONObject login = WsParamBuild.buildJsonObject(null,option, argsArray);
- webSocketClient.send(login.toJSONString());
- log.info("发送登录:{}",option);
- } catch (Exception e) {
- log.error("WebSocket登录请求构建失败", e);
- }
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/OrderInfoWs.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/OrderInfoWs.java
deleted file mode 100644
index 883a2ca..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/OrderInfoWs.java
+++ /dev/null
@@ -1,254 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxWs;
-
-import cn.hutool.core.util.StrUtil;
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-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.okxWs.wanggeList.WangGeListEnum;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.MallUtils;
-import com.xcong.excoin.modules.okxNewPrice.utils.WsMapBuild;
-import com.xcong.excoin.modules.okxNewPrice.utils.WsParamBuild;
-import com.xcong.excoin.utils.RedisUtils;
-import lombok.extern.slf4j.Slf4j;
-import org.java_websocket.client.WebSocketClient;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * @author Administrator
- */
-@Slf4j
-public class OrderInfoWs {
-
- // 使用双层Map,第一层key为账号名称,第二层key为数据key
- public static final Map<String, Map<String, String>> ORDERINFOWSMAP = new ConcurrentHashMap<>();
-
- // 获取指定账号的Map,如果不存在则创建
- public static Map<String, String> getAccountMap(String accountName) {
- return ORDERINFOWSMAP.computeIfAbsent(accountName, k -> new ConcurrentHashMap<>());
- }
-
- public static final String ORDERINFOWS_CHANNEL = "orders";
-
- public static void subscribeOrderInfoChannel(WebSocketClient webSocketClient, String option) {
- try {
- JSONArray argsArray = new JSONArray();
- JSONObject args = new JSONObject();
- args.put("channel", ORDERINFOWS_CHANNEL);
- args.put("instType", CoinEnums.INSTTYPE_SWAP.getCode());
- args.put("instId", CoinEnums.HE_YUE.getCode());
- argsArray.add(args);
-
- String connId = MallUtils.getOrderNum(ORDERINFOWS_CHANNEL);
- JSONObject jsonObject = WsParamBuild.buildJsonObject(connId, option, argsArray);
- webSocketClient.send(jsonObject.toJSONString());
-// log.info("发送订单频道频道:{}", option);
- } catch (Exception e) {
- log.error("订阅订单频道构建失败", e);
- }
- }
-
- public static void initEvent(JSONObject response, String accountName) {
-// log.info("订阅成功: {}", response.getJSONObject("arg"));
- }
-
-
- private static final String DATA_KEY = "data";
- private static final String INSTID_KEY = "instId";
- private static final String ORDID_KEY = "ordId";
- private static final String CLORDID_KEY = "clOrdId";
- private static final String SIDE_KEY = "side";
- private static final String TDMODE_KEY = "tdMode";
- private static final String ACCFILLSZ_KEY = "accFillSz";
- private static final String AVGPX_KEY = "avgPx";
- private static final String STATE_KEY = "state";
- private static final String FILLFEE_KEY = "fillFee";
- private static final String POSSIDE_KEY = "posSide";
- public static List<TradeRequestParam> handleEvent(JSONObject response, RedisUtils redisUtils, String accountName) {
-
- log.info("开始执行OrderInfoWs......");
- try {
- JSONArray dataArray = response.getJSONArray(DATA_KEY);
- if (dataArray == null || dataArray.isEmpty()) {
- log.warn("订单频道数据为空");
- return null;
- }
-
- for (int i = 0; i < dataArray.size(); i++) {
- JSONObject detail = dataArray.getJSONObject(i);
-
- String instId = detail.getString(INSTID_KEY);
- if (!CoinEnums.HE_YUE.getCode().equals(instId)){
- log.info( "订单详情-币种: {} 没有成交订单", CoinEnums.HE_YUE.getCode() );
- continue;
- }
- String ordId = detail.getString(ORDID_KEY);
- String clOrdId = detail.getString(CLORDID_KEY);
- String side = detail.getString(SIDE_KEY);
- String tdMode = detail.getString(TDMODE_KEY);
- String accFillSz = detail.getString(ACCFILLSZ_KEY);
- String avgPx = detail.getString(AVGPX_KEY);
- String state = detail.getString(STATE_KEY);
- String fillFee = detail.getString(FILLFEE_KEY);
- String posSide = detail.getString(POSSIDE_KEY);
-
- log.info(
- "{}: 订单详情-币种: {}, 系统编号: {}, 自定义编号: {}, 订单方向: {}, 交易模式: {}," +
- " 累计成交数量: {}, 成交均价: {}, 订单状态: {}, 手续费: {}, 持仓方向: {}",
- accountName, instId, ordId, clOrdId, side, tdMode,
- accFillSz, avgPx,state, fillFee,posSide
- );
-
- String stateStr = TradeOrderWs.getAccountMap(accountName).get("state");
- if (StrUtil.isNotBlank(stateStr) && state.equals(stateStr)){
- // 使用账号特定的Map
- String positionAccountName = PositionsWs.initAccountName(accountName, posSide);
- Map<String, BigDecimal> positionsMap = PositionsWs.getAccountMap(positionAccountName);
- WsMapBuild.saveBigDecimalToMap(positionsMap, CoinEnums.READY_STATE.name(), WsMapBuild.parseBigDecimalSafe(CoinEnums.READY_STATE_NO.getCode()));
-
- Map<String, String> accountWsMap = AccountWs.getAccountMap(accountName);
- WsMapBuild.saveStringToMap(accountWsMap, CoinEnums.READY_STATE.name(), CoinEnums.READY_STATE_NO.getCode());
-
- log.info("{}: 订单详情已完成: {}, 自定义编号: {}", accountName, CoinEnums.HE_YUE.getCode(), clOrdId);
-
- ArrayList<TradeRequestParam> tradeRequestParams = new ArrayList<>();
- TradeRequestParam tradeRequestParam = new TradeRequestParam();
- tradeRequestParam.setAccountName(accountName);
- BigDecimal zhiYingPx = getZhiYingPx(
- accountName,
- posSide,
- fillFee,
- WsMapBuild.parseBigDecimalSafe(InstrumentsWs.getAccountMap(accountName).get(CoinEnums.CTVAL.name())),
- WsMapBuild.parseBigDecimalSafe(accFillSz),
- WsMapBuild.parseBigDecimalSafe(InstrumentsWs.getAccountMap(accountName).get(CoinEnums.CONTRACTMULTIPLIER.name())),
- WsMapBuild.parseBigDecimalSafe(avgPx),
- WsMapBuild.parseBigDecimalSafe(CoinEnums.LEVERAGE.getCode())
- );
- tradeRequestParam.setMarkPx(String.valueOf(zhiYingPx));
- tradeRequestParam.setInstId(CoinEnums.HE_YUE.getCode());
- tradeRequestParam.setTdMode(CoinEnums.CROSS.getCode());
- tradeRequestParam.setPosSide(posSide);
- tradeRequestParam.setOrdType(CoinEnums.ORDTYPE_LIMIT.getCode());
- tradeRequestParam.setTradeType(OrderParamEnums.TRADE_YES.getValue());
- tradeRequestParam.setSide(CoinEnums.POSSIDE_LONG.getCode().equals(posSide) ? CoinEnums.SIDE_SELL.getCode() : CoinEnums.SIDE_BUY.getCode());
- tradeRequestParam.setClOrdId(WsParamBuild.getOrderNum(tradeRequestParam.getSide()));
- tradeRequestParam.setSz(accFillSz);
- tradeRequestParams.add(tradeRequestParam);
-
- TradeRequestParam tradeRequestParamZhiSun = new TradeRequestParam();
- tradeRequestParamZhiSun.setAccountName(accountName);
- BigDecimal zhiSunPx = getZhiSunPx(
- accountName,
- posSide,
- fillFee,
- WsMapBuild.parseBigDecimalSafe(InstrumentsWs.getAccountMap(accountName).get(CoinEnums.CTVAL.name())),
- WsMapBuild.parseBigDecimalSafe(accFillSz),
- WsMapBuild.parseBigDecimalSafe(InstrumentsWs.getAccountMap(accountName).get(CoinEnums.CONTRACTMULTIPLIER.name())),
- WsMapBuild.parseBigDecimalSafe(avgPx),
- WsMapBuild.parseBigDecimalSafe(CoinEnums.LEVERAGE.getCode())
- );
- tradeRequestParamZhiSun.setMarkPx(String.valueOf(zhiSunPx));
- tradeRequestParamZhiSun.setInstId(CoinEnums.HE_YUE.getCode());
- tradeRequestParamZhiSun.setTdMode(CoinEnums.CROSS.getCode());
- tradeRequestParamZhiSun.setPosSide(posSide);
- tradeRequestParamZhiSun.setOrdType(CoinEnums.ORDTYPE_LIMIT.getCode());
- tradeRequestParamZhiSun.setTradeType(OrderParamEnums.TRADE_YES.getValue());
- tradeRequestParamZhiSun.setSide(CoinEnums.POSSIDE_LONG.getCode().equals(posSide) ? CoinEnums.SIDE_SELL.getCode() : CoinEnums.SIDE_BUY.getCode());
- tradeRequestParamZhiSun.setClOrdId(WsParamBuild.getOrderNum(tradeRequestParamZhiSun.getSide()));
- tradeRequestParamZhiSun.setSz(accFillSz);
- tradeRequestParams.add(tradeRequestParamZhiSun);
- return tradeRequestParams;
-
- }
- return null;
- }
- } catch (Exception e) {
- log.error("处理订单频道推送数据失败", e);
- }
- return null;
- }
-
- public static void main(String[] args) {
- System.out.println(
- getZhiYingPx(
- "eth",
- CoinEnums.POSSIDE_LONG.getCode(),
- "0.0001",
- new BigDecimal("0.1"),
- new BigDecimal("0.05"),
- new BigDecimal("1"),
- new BigDecimal("2950"),
- new BigDecimal("100"))
- );
- }
- /**
- * 计算预期收益
- */
- public static BigDecimal getZhiSunPx(
- String accountName, String posSide, String fillFee, BigDecimal coinValue, BigDecimal coinNum,
- BigDecimal contractMultiplier, BigDecimal avgPx, BigDecimal leverage
- ) {
- BigDecimal initMargin = getInitMargin(coinValue, coinNum, contractMultiplier, avgPx, leverage);
- String pingCangImr = StrUtil.isEmpty(InstrumentsWs.getAccountMap(accountName).get(CoinEnums.PING_CANG_SHOUYI.name())) ? "0.2" : InstrumentsWs.getAccountMap(accountName).get(CoinEnums.PING_CANG_SHOUYI.name());
- BigDecimal expectProfit = (initMargin)
- .multiply(new BigDecimal(pingCangImr))
- .add(new BigDecimal(fillFee).abs())
- .multiply(new BigDecimal("-1"))
- .setScale(4, RoundingMode.DOWN);
- log.info("{}: 订单详情-预期收益: {}", accountName, expectProfit);
- return getMarkPrice(expectProfit,posSide, coinValue, coinNum, contractMultiplier, avgPx, leverage);
- }
- /**
- * 计算预期收益
- */
- public static BigDecimal getZhiYingPx(
- String accountName, String posSide, String fillFee, BigDecimal coinValue, BigDecimal coinNum,
- BigDecimal contractMultiplier, BigDecimal avgPx, BigDecimal leverage
- ) {
- BigDecimal initMargin = getInitMargin(coinValue, coinNum, contractMultiplier, avgPx, leverage);
- String pingCangImr = StrUtil.isEmpty(InstrumentsWs.getAccountMap(accountName).get(CoinEnums.PING_CANG_SHOUYI.name())) ? "0.2" : InstrumentsWs.getAccountMap(accountName).get(CoinEnums.PING_CANG_SHOUYI.name());
- BigDecimal expectProfit = (initMargin).multiply(new BigDecimal(pingCangImr)).add(new BigDecimal(fillFee).abs()).setScale(4, RoundingMode.DOWN);
- log.info("{}: 订单详情-预期收益: {}", accountName, expectProfit);
- return getMarkPrice(expectProfit,posSide, coinValue, coinNum, contractMultiplier, avgPx, leverage);
- }
-
- /**
- * 计算初始保证金
- * 面值 * 张数 * 合约乘数 * 标记价格 / 杠杆倍数
- */
- public static BigDecimal getInitMargin(BigDecimal coinValue, BigDecimal coinNum, BigDecimal contractMultiplier, BigDecimal avgPx, BigDecimal leverage) {
- BigDecimal initMargin = coinValue.multiply(coinNum).multiply(contractMultiplier).multiply(avgPx).divide(leverage, 4, RoundingMode.DOWN);
- log.info("订单详情-初始保证金: {}", initMargin);
- return initMargin;
- }
- /**
- * USDT保证金合约
- * 多仓收益 = 面值 * |张数| * 合约乘数 *(标记价格 - 开仓均价)
- * (标记价格 - 开仓均价) = 多仓收益 /(面值 * |张数| * 合约乘数)
- * 标记价格 = 多仓收益 /(面值 * |张数| * 合约乘数) + 开仓均价
- * 空仓收益 = 面值 * |张数| * 合约乘数 *(开仓均价 - 标记价格)
- * (开仓均价 - 标记价格) = 空仓收益 /(面值 * |张数| * 合约乘数)
- * 标记价格 = 开仓均价 - 空仓收益 /(面值 * |张数| * 合约乘数)
- */
- public static BigDecimal getMarkPrice(BigDecimal expectProfit,String posSide,BigDecimal coinValue, BigDecimal sz, BigDecimal contractMultiplier, BigDecimal openPrice, BigDecimal leverage) {
- BigDecimal markPrice = BigDecimal.ZERO;
- BigDecimal multiply = coinValue.multiply(sz.abs()).multiply(contractMultiplier);
- BigDecimal bigDecimal = expectProfit.divide(multiply, 2, RoundingMode.DOWN);
- if (CoinEnums.POSSIDE_LONG.getCode().equals(posSide)){
- markPrice = openPrice.add(bigDecimal).setScale(2, RoundingMode.DOWN);
- }
- if (CoinEnums.POSSIDE_SHORT.getCode().equals(posSide)){
- markPrice = openPrice.subtract(bigDecimal).setScale(2, RoundingMode.DOWN);
- }
- log.info("订单详情-止盈标记价格: {}", markPrice);
- return markPrice;
- }
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/PositionsWs.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/PositionsWs.java
deleted file mode 100644
index 4bb3ca3..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/PositionsWs.java
+++ /dev/null
@@ -1,141 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxWs;
-
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.CoinEnums;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.OrderParamEnums;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.MallUtils;
-import com.xcong.excoin.modules.okxNewPrice.utils.WsMapBuild;
-import com.xcong.excoin.modules.okxNewPrice.utils.WsParamBuild;
-import lombok.extern.slf4j.Slf4j;
-import org.java_websocket.client.WebSocketClient;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * @author Administrator
- */
-@Slf4j
-public class PositionsWs {
-
- // 使用双层Map,第一层key为账号名称,第二层key为数据key
- public static final Map<String, Map<String, BigDecimal>> POSITIONSWSMAP = new ConcurrentHashMap<>();
-
- // 获取指定账号的Map,如果不存在则创建
- public static Map<String, BigDecimal> getAccountMap(String accountName) {
- return POSITIONSWSMAP.computeIfAbsent(accountName, k -> new ConcurrentHashMap<>());
- }
-
- public static final String POSITIONSWS_CHANNEL = "positions";
-
- public static void subscribePositionChannel(WebSocketClient webSocketClient, String option) {
- try {
- JSONArray argsArray = new JSONArray();
- JSONObject args = new JSONObject();
- args.put("channel", POSITIONSWS_CHANNEL);
- args.put("instType", CoinEnums.INSTTYPE_SWAP.getCode());
- args.put("instId", CoinEnums.HE_YUE.getCode());
- argsArray.add(args);
-
- String connId = MallUtils.getOrderNum(POSITIONSWS_CHANNEL);
- JSONObject jsonObject = WsParamBuild.buildJsonObject(connId, option, argsArray);
- webSocketClient.send(jsonObject.toJSONString());
- log.info("发送持仓频道频道:{}", option);
- } catch (Exception e) {
- log.error("订阅持仓频道频道构建失败", e);
- }
- }
-
- public static String initAccountName(String accountName, String posSide) {
- return accountName+"_"+ posSide;
- }
-
- public static void initEvent(JSONObject response, String accountName) {
- log.info("订阅成功,数据初始化: {}", response.getJSONObject("arg"));
- JSONObject arg = response.getJSONObject("arg");
- initParam(arg, accountName,CoinEnums.POSSIDE_LONG.getCode());
- initParam(arg, accountName,CoinEnums.POSSIDE_SHORT.getCode());
- }
-
- public static void handleEvent(JSONObject response, String accountName) {
-
-
-// log.info("开始执行PositionsWs......");
- try {
- JSONArray dataArray = response.getJSONArray("data");
- if (dataArray == null || dataArray.isEmpty()) {
- log.info("账户持仓频道数据为空,等待更新");
- return;
- }
-
- for (int i = 0; i < dataArray.size(); i++) {
- JSONObject posData = dataArray.getJSONObject(i);
- String instId = posData.getString("instId");
- if (CoinEnums.HE_YUE.getCode().equals(instId)) {
-// log.info("查询到账户{}持仓数据",CoinEnums.HE_YUE.getCode());
- String mgnMode = posData.getString("mgnMode");
- String posSide = posData.getString("posSide");
- String pos = posData.getString("pos");
- String avgPx = posData.getString("avgPx");
- String upl = posData.getString("upl");
- String uplRatio = posData.getString("uplRatio");
- String lever = posData.getString("lever");
- String liqPx = posData.getString("liqPx");
- String markPx = posData.getString("markPx");
- String imr = posData.getString("imr");
- String mgnRatio = posData.getString("mgnRatio");
- String mmr = posData.getString("mmr");
- String notionalUsd = posData.getString("notionalUsd");
- String ccy = posData.getString("ccy");
- String last = posData.getString("last");
- String idxPx = posData.getString("idxPx");
- String bePx = posData.getString("bePx");
- String realizedPnl = posData.getString("realizedPnl");
- String settledPnl = posData.getString("settledPnl");
- String fee = posData.getString("fee");
- String fundingFee = posData.getString("fundingFee");
-// log.info(
-// "{}: 账户持仓频道-产品类型: {}, 保证金模式: {}, 持仓方向: {}, 持仓数量: {}, 开仓平均价: {}, "
-// + "未实现收益: {}, 未实现收益率: {}, 杠杆倍数: {}, 预估强平价: {}, 初始保证金: {}, "
-// + "维持保证金率: {}, 维持保证金: {}, 以美金价值为单位的持仓数量: {}, 占用保证金的币种: {}, "
-// + "最新成交价: {}, 最新指数价格: {}, 盈亏平衡价: {}, 已实现收益: {}, 累计已结算收益: {}"
-// + "最新标记价格: {},累计手续费: {},累计持仓费: {},",
-// initAccountName(accountName, posSide), instId, mgnMode, posSide, pos, avgPx,
-// upl, uplRatio, lever, liqPx, imr,
-// mgnRatio, mmr, notionalUsd, ccy,
-// last, idxPx, bePx, realizedPnl, settledPnl,
-// markPx,fee,fundingFee
-// );
- initParam(posData, accountName,posSide);
- }
- }
- } catch (Exception e) {
- log.error("处理持仓频道推送数据失败", e);
- }
- }
-
- private static void initParam(JSONObject posData, String accountName,String posSide) {
- String accountNamePositons = initAccountName(accountName, posSide);
- Map<String, BigDecimal> accountMap = getAccountMap(accountNamePositons);
- WsMapBuild.saveBigDecimalToMap(accountMap, "avgPx", WsMapBuild.parseBigDecimalSafe(posData.getString("avgPx")));
- WsMapBuild.saveBigDecimalToMap(accountMap, "pos", WsMapBuild.parseBigDecimalSafe(posData.getString("pos")));
- WsMapBuild.saveBigDecimalToMap(accountMap, "upl", WsMapBuild.parseBigDecimalSafe(posData.getString("upl")));
- WsMapBuild.saveBigDecimalToMap(accountMap, "imr", WsMapBuild.parseBigDecimalSafe(posData.getString("imr")));
- WsMapBuild.saveBigDecimalToMap(accountMap, "mgnRatio", WsMapBuild.parseBigDecimalSafe(posData.getString("mgnRatio")));
- WsMapBuild.saveBigDecimalToMap(accountMap, "markPx", WsMapBuild.parseBigDecimalSafe(posData.getString("markPx")));
- WsMapBuild.saveBigDecimalToMap(accountMap, "bePx", WsMapBuild.parseBigDecimalSafe(posData.getString("bePx")));
- WsMapBuild.saveBigDecimalToMap(accountMap, "realizedPnl", WsMapBuild.parseBigDecimalSafe(posData.getString("realizedPnl")));
- WsMapBuild.saveBigDecimalToMap(accountMap, "fee", WsMapBuild.parseBigDecimalSafe(posData.getString("fee")));
- WsMapBuild.saveBigDecimalToMap(accountMap, "fundingFee", WsMapBuild.parseBigDecimalSafe(posData.getString("fundingFee")));
-
- BigDecimal ordFrozImr = PositionsWs.getAccountMap(accountNamePositons).get("imr");
- BigDecimal totalOrderUsdt = WsMapBuild.parseBigDecimalSafe(AccountWs.getAccountMap(accountName).get(CoinEnums.TOTAL_ORDER_USDT.name()))
- .divide(new BigDecimal("2"), RoundingMode.DOWN);
- if (ordFrozImr.compareTo(totalOrderUsdt) <= 0){
- WsMapBuild.saveBigDecimalToMap(accountMap, CoinEnums.READY_STATE.name(), WsMapBuild.parseBigDecimalSafe(CoinEnums.READY_STATE_YES.getCode()));
- }
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/TradeOrderWs.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/TradeOrderWs.java
deleted file mode 100644
index bea34fd..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/TradeOrderWs.java
+++ /dev/null
@@ -1,247 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxWs;
-
-import cn.hutool.core.util.StrUtil;
-import cn.hutool.json.JSONUtil;
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-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 com.xcong.excoin.modules.okxNewPrice.utils.WsParamBuild;
-import lombok.extern.slf4j.Slf4j;
-import org.java_websocket.client.WebSocketClient;
-
-import java.math.BigDecimal;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * 交易订单处理类,负责构建和发送订单请求到OKX WebSocket
- *
- * @author Administrator
- */
-@Slf4j
-public class TradeOrderWs {
-
- // 使用双层Map,第一层key为账号名称,第二层key为数据key
- public static final Map<String, Map<String,String>> TRADEORDERWSMAP = new ConcurrentHashMap<>();
-
- // 获取指定账号的Map,如果不存在则创建
- public static Map<String, String> getAccountMap(String accountName) {
- return TRADEORDERWSMAP.computeIfAbsent(accountName, k -> new ConcurrentHashMap<>());
- }
-
- public static final String ORDERWS_CHANNEL = "order";
- public static final String BATCH_ORDERSWS_CHANNEL = "batch-orders";
-
- public static void orderEvent(WebSocketClient webSocketClient, TradeRequestParam tradeRequestParam) {
-
-
- log.info("开始执行TradeOrderWs{}......", JSONUtil.parse(tradeRequestParam));
- String accountName = tradeRequestParam.getAccountName();
- String markPx = tradeRequestParam.getMarkPx();
- String instId = tradeRequestParam.getInstId();
- String tdMode = tradeRequestParam.getTdMode();
- String posSide = tradeRequestParam.getPosSide();
- String ordType = tradeRequestParam.getOrdType();
-
- String tradeType = tradeRequestParam.getTradeType();
-
- String clOrdId = tradeRequestParam.getClOrdId();
- String side = tradeRequestParam.getSide();
- String sz = tradeRequestParam.getSz();
- log.info("账户:{},类型:{},触发价格:{},币种:{},方向:{},买卖:{},数量:{},是否允许下单:{},编号:{},",
- accountName,ordType, markPx, instId, posSide,side, sz, tradeType, clOrdId);
- //验证是否允许下单
- if (StrUtil.isNotEmpty(tradeType) && OrderParamEnums.TRADE_NO.getValue().equals(tradeType)) {
- log.warn("账户{}不允许下单,取消发送", accountName);
- return;
- }
- /**
- * 校验必要参数
- * 验证下单参数是否存在空值
- */
- if (
- StrUtil.isBlank(accountName)
- || StrUtil.isBlank(instId)
- || StrUtil.isBlank(tdMode)
- || StrUtil.isBlank(posSide)
- || StrUtil.isBlank(ordType)
- || StrUtil.isBlank(clOrdId)
- || StrUtil.isBlank(side)
- || StrUtil.isBlank(sz)
-
- ){
- log.warn("下单参数缺失,取消发送");
- return;
- }
- if (BigDecimal.ZERO.compareTo(new BigDecimal(sz)) >= 0) {
- log.warn("下单数量{}不允许下单,取消发送", sz);
- return;
- }
-
- /**
- * 检验账户和仓位是否准备就绪
- * 开多:买入开多(side 填写 buy; posSide 填写 long )
- * 开空:卖出开空(side 填写 sell; posSide 填写 short ) 需要检验账户通道是否准备就绪
- * 平多:卖出平多(side 填写 sell;posSide 填写 long )
- * 平空:买入平空(side 填写 buy; posSide 填写 short ) 需要检验仓位通道是否准备就绪
- */
- //买入开多、卖出开空则验证仓位通道是否准备就绪
-
- String positionAccountName = PositionsWs.initAccountName(accountName, posSide);
- boolean b = posSide.equals(CoinEnums.POSSIDE_LONG.getCode()) && side.equals(CoinEnums.SIDE_BUY.getCode());
- boolean c = posSide.equals(CoinEnums.POSSIDE_SHORT.getCode()) && side.equals(CoinEnums.SIDE_SELL.getCode());
- if ( b || c ){
- BigDecimal positionsReadyState = PositionsWs.getAccountMap(positionAccountName).get(CoinEnums.READY_STATE.name()) == null
- ? BigDecimal.ZERO : PositionsWs.getAccountMap(positionAccountName).get(CoinEnums.READY_STATE.name());
- if (WsMapBuild.parseBigDecimalSafe(CoinEnums.READY_STATE_YES.getCode()).compareTo(positionsReadyState) != 0) {
- log.info("仓位{}通道未就绪,取消发送",positionAccountName);
- return;
- }
- String accountReadyState = AccountWs.getAccountMap(accountName).get(CoinEnums.READY_STATE.name());
- if (!CoinEnums.READY_STATE_YES.getCode().equals(accountReadyState)) {
- log.info("账户通道未就绪,取消发送");
- return;
- }
- }
-
- try {
- JSONArray argsArray = new JSONArray();
- JSONObject args = new JSONObject();
- args.put("instId", instId);
- args.put("tdMode", tdMode);
- args.put("clOrdId", clOrdId);
- args.put("side", side);
-
- args.put("posSide", posSide);
- args.put("ordType", ordType);
- args.put("sz", sz);
- argsArray.add(args);
-
- String connId = WsParamBuild.getOrderNum(ORDERWS_CHANNEL);
- JSONObject jsonObject = WsParamBuild.buildJsonObject(connId, ORDERWS_CHANNEL, argsArray);
- webSocketClient.send(jsonObject.toJSONString());
- log.info("发送下单频道:{},数量:{}", side, sz);
-
- WsMapBuild.saveStringToMap(getAccountMap(accountName), "state", CoinEnums.ORDER_FILLED.getCode());
- /**
- * 将状态更新为未准备就绪
- */
- WsMapBuild.saveBigDecimalToMap(PositionsWs.getAccountMap(positionAccountName), CoinEnums.READY_STATE.name(), WsMapBuild.parseBigDecimalSafe(CoinEnums.READY_STATE_NO.getCode()));
- WsMapBuild.saveStringToMap(AccountWs.getAccountMap(accountName), CoinEnums.READY_STATE.name(), CoinEnums.READY_STATE_NO.getCode());
-
- } catch (Exception e) {
- log.error("下单构建失败", e);
- }
- }
-
- public static void orderZhiYingZhiSunEventNoState(WebSocketClient webSocketClient, List<TradeRequestParam> tradeRequestParams) {
-
-
- log.info("开始执行TradeOrderWs......");
- if (tradeRequestParams == null){
-
- log.warn("下单{}参数缺失,取消发送",tradeRequestParams);
- return;
- }
-
- JSONArray argsArray = new JSONArray();
- for (TradeRequestParam tradeRequestParam : tradeRequestParams){
- String accountName = tradeRequestParam.getAccountName();
- String markPx = tradeRequestParam.getMarkPx();
- String instId = tradeRequestParam.getInstId();
- String tdMode = tradeRequestParam.getTdMode();
- String posSide = tradeRequestParam.getPosSide();
- String ordType = tradeRequestParam.getOrdType();
-
- String tradeType = tradeRequestParam.getTradeType();
-
- String clOrdId = tradeRequestParam.getClOrdId();
- String side = tradeRequestParam.getSide();
- String sz = tradeRequestParam.getSz();
- /**
- * 校验必要参数
- * 验证下单参数是否存在空值
- */
- if (
- StrUtil.isBlank(accountName)
- || StrUtil.isBlank(instId)
- || StrUtil.isBlank(tdMode)
- || StrUtil.isBlank(posSide)
- || StrUtil.isBlank(ordType)
- || StrUtil.isBlank(clOrdId)
- || StrUtil.isBlank(side)
- || StrUtil.isBlank(sz)
- || StrUtil.isBlank(markPx)
-
- ){
- log.warn("下单参数缺失,取消发送");
- continue;
- }
- log.info("账户:{},类型:{},触发价格:{},币种:{},方向:{},买卖:{},数量:{},是否允许下单:{},编号:{},",
- accountName,ordType, markPx, instId, posSide,side, sz, tradeType, clOrdId);
- //验证是否允许下单
- if (StrUtil.isNotEmpty(tradeType) && OrderParamEnums.TRADE_NO.getValue().equals(tradeType)) {
- log.warn("账户{}不允许下单,取消发送", accountName);
- continue;
- }
-
- /**
- * 检验账户和仓位是否准备就绪
- * 开多:买入开多(side 填写 buy; posSide 填写 long )
- * 开空:卖出开空(side 填写 sell; posSide 填写 short ) 需要检验账户通道是否准备就绪
- * 平多:卖出平多(side 填写 sell;posSide 填写 long )
- * 平空:买入平空(side 填写 buy; posSide 填写 short ) 需要检验仓位通道是否准备就绪
- */
-
- JSONObject args = new JSONObject();
- args.put("instId", instId);
- args.put("tdMode", tdMode);
- args.put("clOrdId", clOrdId);
- args.put("side", side);
-
- args.put("posSide", posSide);
- args.put("ordType", ordType);
- args.put("sz", sz);
- args.put("px", markPx);
- argsArray.add(args);
- }
-
- String connId = WsParamBuild.getOrderNum(null);
- JSONObject jsonObject = WsParamBuild.buildJsonObject(connId, BATCH_ORDERSWS_CHANNEL, argsArray);
- webSocketClient.send(jsonObject.toJSONString());
- log.info("发送止盈止损批量下单频道:{}",argsArray);
- }
-
-
-
- /**
- * 计算盈亏金额。
- *
- * @param faceValue 面值
- * @param position 持仓数量
- * @param markPrice 标记价格
- * @param openPrice 开仓价格
- * @param isLong 是否为多头仓位
- * @param minTickSz 最小变动单位精度
- * @return 盈亏金额,保留指定精度的小数位
- */
- public BigDecimal profit(BigDecimal faceValue, BigDecimal position,
- BigDecimal markPrice, BigDecimal openPrice, boolean isLong, int minTickSz) {
- BigDecimal profit = BigDecimal.ZERO;
- if (isLong) {
- profit = markPrice.subtract(openPrice)
- .multiply(faceValue)
- .multiply(position);
- } else {
- profit = openPrice.subtract(markPrice)
- .multiply(faceValue)
- .multiply(position);
- }
- return profit.setScale(minTickSz, BigDecimal.ROUND_DOWN);
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/enums/CoinEnums.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/enums/CoinEnums.java
deleted file mode 100644
index 8429c7d..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/enums/CoinEnums.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxWs.enums;
-
-import lombok.Getter;
-
-/**
- * @author Administrator
- */
-@Getter
-public enum CoinEnums {
-
- /**
- * 0: 仅根据持仓事件推送数据
- * 2000, 3000, 4000: 根据持仓事件推送,且根据设置的时间间隔定时推送(ms)
- *
- * 若不添加该字段或将其设置为上述合法值以外的其他值,数据将根据事件推送并大约每 5 秒定期推送一次
- */
- UPDATEINTERVAL("2000","2000"),
-
- ORDER_FILLED("filled","filled"),
- ORDER_LIVE("live","live"),
-
- INSTTYPE_SWAP("SWAP","SWAP"),
-
- ORDTYPE_MARKET("market","market"),
- ORDTYPE_LIMIT("limit","limit"),
-
- POSSIDE_SHORT("short","short"),
-
- POSSIDE_LONG("long","long"),
-
- SIDE_SELL("sell","sell"),
-
- SIDE_BUY("buy","buy"),
-
- CROSS("cross","cross"),
-
- USDT("USDT","USDT"),
-
-
- WANG_GE_OLD("上一个网格wang_ge_old", "0"),
-
- READY_STATE("是否准备就绪ready_state", "1"),
- READY_STATE_YES("准备就绪ready_state", "1"),
- READY_STATE_NO("未准备就绪ready_state", "0"),
-
- PING_CANG_SHOUYI("平仓收益比例", "1"),
- //下单的总保障金为账户总金额cashBal * TOTAL_ORDER_USDT用来做保证金
- TOTAL_ORDER_USDTPECENT("总保证金比例total_order_usdtpecent","0.06"),
- TOTAL_ORDER_USDT("总保证金totalOrderUsdt","0"),
- KANG_CANG("抗压比例KANG_CANG","0.9"),
- ZHI_SUN("止损比例ZHI_SUN","0.8"),
- //每次下单的张数
- BUY_CNT("每次开仓的张数buyCnt","0.5"),
- BUY_CNT_INIT("每次初始化开仓张数的基础值buyCntInit","0.5"),
- BUY_CNT_TIME("每次开仓张数的倍数基础值buyCntTime","20"),
- OUT("是否允许下单out","操作中"),
- CTVAL("合约面值ctVal","0.1"),
- CONTRACTMULTIPLIER("合约乘积ctVal","1"),
- TICKSZ("下单价格精度tickSz","2"),
- MINSZ("最小下单数小数位minSz","2"),
- LEVERAGE("合约杠杆leverage","100"),
-// HE_YUE("合约instId","ETH-USDT-SWAP"),
- HE_YUE("合约instId","BTC-USDT-SWAP"),
- POSSIDE("持仓方向posSide","short");
-
- private String name;
-
- private String code;
-
- /**
- * 构造方法
- *
- * @param name
- * @param code
- */
- CoinEnums(String name, String code) {
- this.name = name;
- this.code = code;
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/enums/ExchangeInfoEnum.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/enums/ExchangeInfoEnum.java
deleted file mode 100644
index 42a1d83..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/enums/ExchangeInfoEnum.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxWs.enums;
-
-import lombok.Getter;
-
-/**
- * 交易信息枚举类
- * 用于存储不同交易账户的密钥信息,包括实盘账户和模拟账户
- */
-@Getter
-public enum ExchangeInfoEnum {
-
- /**
- * 模拟盘账户1信息
- * 存储了模拟盘交易所需的API密钥、秘钥和通过码
- */
-// OKX_PRD_xiao("f512673b-2685-4fcb-9bb1-2ae8db745d62",
-// "B0C1CC8F39625B41140D93DC25039E33",
-// "Aa12345678@",
-// true);
- OKX_UAT_ceshi("ffb4e79f-fcf5-4afb-82c5-2fbb64123f61",
- "AA06C5ED1D7C7F5AFE6484052E231C55",
- "Aa12345678@",
- false);
-//
-// /**
-// * 模拟盘账户2信息
-// * 存储了模拟盘交易所需的API密钥、秘钥和通过码
-// */
-// OKX_PRD_wang("72e380a6-4133-451b-8b10-8b1905b30717",
-// "2A5BD55BF0771F1ADF08AE0A2FB4D561",
-// "Aa12345678@",
-// true);
-// OKX_UAT2("7a023eb2-06c0-4255-9969-b86ea1cef0d7",
-// "D0106A4D63BD22BEAB9CBA8F41219661",
-// "Aa12345678@",
-// false);
-
- /**
- * 模拟盘账户3信息
- * 存储了模拟盘交易所需的API密钥、秘钥和通过码
- */
-// OKX_UAT3("0769b50c-2c36-4310-8bd9-cad6bc6c9d8f",
-// "7AF4A574BC44907CE76BBFF91F53852D",
-// "Aa123456@",
-// false);
-
- // API公钥,用于识别用户身份
- private String apiKey;
-
- // API秘钥,用于签名和验证请求
- private String secretKey;
-
- // API通过码,用于额外的身份验证
- private String passphrase;
-
- // 账户类型,true表示实盘账户,false表示模拟账户
- private boolean accountType;
-
- /**
- * 构造方法
- *
- * @param apiKey API公钥,用于识别用户身份
- * @param secretKey API秘钥,用于签名和验证请求
- * @param passphrase API通过码,用于额外的身份验证
- * @param accountType 账户类型,true表示实盘账户,false表示模拟账户
- */
- ExchangeInfoEnum(String apiKey, String secretKey, String passphrase, boolean accountType) {
- this.apiKey = apiKey;
- this.secretKey = secretKey;
- this.passphrase = passphrase;
- this.accountType = accountType;
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/enums/OrderParamEnums.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/enums/OrderParamEnums.java
deleted file mode 100644
index 981203e..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/enums/OrderParamEnums.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxWs.enums;
-
-import com.xcong.excoin.common.enumerates.SymbolEnum;
-import lombok.Getter;
-
-/**
- * @author Administrator
- */
-
-@Getter
-public enum OrderParamEnums {
-
- TRADE_YES("允许下单", "TRADE_YES"),
- TRADE_NO("拒绝下单", "TRADE_NO"),
-
- OUT_NO("操作中", "操作中"),
- OUT_YES("冷静中", "冷静中"),
-
- ORDERING("操作下单中", "ORDERING"),
- LIMIT("限价止损", "limit"),
- OUT("止损", "out"),
- INIT("初始化", "init"),
- HOLDING("持仓", "holding"),
- BUY("买", "buy"),
- SELL("卖", "sell")
- ;
-
- private String name;
-
- private String value;
-
- private OrderParamEnums(String name, String value) {
- this.name = name;
- this.value = value;
- }
-
- public static String getNameByValue(String value) {
- String name = "";
- for (OrderParamEnums orderParamEnum : values()) {
- if (value.equals(orderParamEnum.getValue())){
- name = orderParamEnum.getName();
- break;
- }
- }
- return name;
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/param/Kline.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/param/Kline.java
deleted file mode 100644
index 5adafea..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/param/Kline.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxWs.param;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import lombok.Data;
-
-import java.io.Serializable;
-import java.math.BigDecimal;
-
-@Data
-public class Kline{
- private String ts;
- private BigDecimal o;
- private BigDecimal h;
- private BigDecimal l;
- private BigDecimal c;
- private BigDecimal vol;
- private String confirm;
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/param/StrategyParam.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/param/StrategyParam.java
deleted file mode 100644
index 5dcd018..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/param/StrategyParam.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxWs.param;
-
-import lombok.Data;
-
-/**
- * @author Administrator
- */
-@Data
-public class StrategyParam {
- private String accountName;
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/param/TradeRequestParam.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/param/TradeRequestParam.java
deleted file mode 100644
index 14260d7..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/param/TradeRequestParam.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxWs.param;
-
-import lombok.Data;
-
-/**
- * @author Administrator
- */
-@Data
-public class TradeRequestParam {
- /**
- * 这些参数由 caoZuoHandler方法提供
- */
- private String accountName;
- private String markPx;
-
- private String instId;
- private String tdMode;
- private String posSide;
- private String ordType;
-
- /**
- * 决定是否进行下单操作
- */
- private String tradeType;
- /**
- * 这些参数由 caoZuoLong 或者 caoZuoShort 提供
- */
- private String side;
- private String clOrdId;
- private String sz;
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/wanggeList/WangGeListEnum.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/wanggeList/WangGeListEnum.java
deleted file mode 100644
index 7ad9442..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/wanggeList/WangGeListEnum.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxWs.wanggeList;
-
-import lombok.Getter;
-
-import java.math.BigDecimal;
-
-/**
- * @author Administrator
- * 网格数据枚举 数据
- * todo 后期考虑优化为可配置项
- */
-@Getter
-public enum WangGeListEnum {
- UP("上层做空", "2", "3200", "3000", "4", "short", "3100"),
- CENTER("中间做空", "2", "3000", "2700", "4", "short", "2700"),
- DOWN("下层做多", "2", "2700", "2500", "4", "long", "2500"),
- DOWN_ONE("下层做空", "2", "2500", "2200", "4", "short", "2500");
-
- private String name;
- private String xiaoshu_weishu;
- private String jiage_shangxian;
- private String jiage_xiaxian;
- private String jian_ju;
- private String fang_xiang;
- private String zhi_sun_dian;
-
- WangGeListEnum(String name, String xiaoshu_weishu, String jiage_shangxian, String jiage_xiaxian, String jian_ju, String fang_xiang, String zhi_sun_dian) {
- this.name = name;
- this.xiaoshu_weishu = xiaoshu_weishu;
- this.jiage_shangxian = jiage_shangxian;
- this.jiage_xiaxian = jiage_xiaxian;
- this.jian_ju = jian_ju;
- this.fang_xiang = fang_xiang;
- this.zhi_sun_dian = zhi_sun_dian;
- }
-
- /**
- * 根据价格获取匹配的网格信息
- * @param price 待比较的价格
- * @return 匹配的网格枚举信息,如果没有匹配项则返回null
- */
- public static WangGeListEnum getGridByPrice(BigDecimal price) {
- for (WangGeListEnum grid : WangGeListEnum.values()) {
- BigDecimal upperLimit = new BigDecimal(grid.jiage_shangxian);
- BigDecimal lowerLimit = new BigDecimal(grid.jiage_xiaxian);
-
- // 确保上限大于下限
- if (upperLimit.compareTo(lowerLimit) > 0) {
- // 检查价格是否在区间内
- if (price.compareTo(lowerLimit) > 0 && price.compareTo(upperLimit) <= 0) {
- return grid;
- }
- }
- }
- return null;
- }
-
- /**
- * 根据枚举名称获取枚举
- */
- public static WangGeListEnum getByName(String name) {
- for (WangGeListEnum grid : WangGeListEnum.values()) {
- if (grid.name.equals(name)) {
- return grid;
- }
- }
- return null;
- }
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/wanggeList/WangGeListQueue.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/wanggeList/WangGeListQueue.java
deleted file mode 100644
index 70ae5a0..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/wanggeList/WangGeListQueue.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxWs.wanggeList;
-
-import com.xcong.excoin.rabbit.pricequeue.AscBigDecimal;
-import com.xcong.excoin.rabbit.pricequeue.DescBigDecimal;
-
-import java.util.concurrent.PriorityBlockingQueue;
-
-/**
- * 网格交易队列管理类
- *
- * 用于管理系统中各种网格交易相关的优先级阻塞队列,
- * 包括完整的网格队列、平仓队列和开仓队列。
- *
- * @author Administrator
- */
-public class WangGeListQueue {
-
- //------------------------------------------------------------------------------------------------------------------
- //------------------------------------------------------------------------------------------------------------------
- // todo 系统启动后,初始化网格队列
- /**
- * 完整的网格 头元素最小
- */
- public static PriorityBlockingQueue<AscBigDecimal> QUEUE_ASC = null;
-
-
- //------------------------------------------------------------------------------------------------------------------
- //------------------------------------------------------------------------------------------------------------------
- // todo 当用户下了第一单后,根据开仓价格初始化网格平仓队列和开仓队列
- /**
- * 网格平仓队列 头元素最小
- */
- public static PriorityBlockingQueue<AscBigDecimal> QUEUE_PINGCANG_ASC = null;
-
- /**
- * 网格开仓队列 头元素最大
- */
- public static PriorityBlockingQueue<DescBigDecimal> QUEUE_KAICANG_DESC = null;
-
- /**
- * 获取完整的网格队列(升序)
- * 如果队列未初始化则创建新的优先级阻塞队列
- *
- * @return 返回升序排列的PriorityBlockingQueue队列,队列头部元素最小
- */
- public static PriorityBlockingQueue<AscBigDecimal> getQueueAsc() {
- if (QUEUE_ASC == null) {
- QUEUE_ASC = new PriorityBlockingQueue<AscBigDecimal>();
- }
- return QUEUE_ASC;
- }
-
- /**
- * 获取网格平仓队列(升序)
- * 如果队列未初始化则创建新的优先级阻塞队列
- *
- * @return 返回升序排列的PriorityBlockingQueue队列,队列头部元素最小
- */
- public static PriorityBlockingQueue<AscBigDecimal> getPingCang() {
- if (QUEUE_PINGCANG_ASC == null) {
- QUEUE_PINGCANG_ASC = new PriorityBlockingQueue<AscBigDecimal>();
- }
- return QUEUE_PINGCANG_ASC;
- }
-
- /**
- * 获取网格开仓队列(降序)
- * 如果队列未初始化则创建新的优先级阻塞队列
- *
- * @return 返回降序排列的PriorityBlockingQueue队列,队列头部元素最大
- */
- public static PriorityBlockingQueue<DescBigDecimal> getKaiCang() {
- if (QUEUE_KAICANG_DESC == null) {
- QUEUE_KAICANG_DESC = new PriorityBlockingQueue<DescBigDecimal>();
- }
- return QUEUE_KAICANG_DESC;
- }
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/wanggeList/WangGeListService.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/wanggeList/WangGeListService.java
deleted file mode 100644
index b8b304a..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/wanggeList/WangGeListService.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxWs.wanggeList;
-
-import com.xcong.excoin.rabbit.pricequeue.AscBigDecimal;
-import com.xcong.excoin.rabbit.pricequeue.DescBigDecimal;
-
-import java.math.BigDecimal;
-import java.util.concurrent.PriorityBlockingQueue;
-
-/**
- * 网格交易服务接口
- * 定义了网格交易的核心操作方法,包括初始化网格、开仓和平仓等操作
- * @author Administrator
- */
-public interface WangGeListService {
-
- /**
- * 初始化网格交易
- * 创建并初始化用于网格交易的价格队列,按照价格升序排列
- * @return 初始化结果信息,返回按价格升序排列的阻塞队列
- */
- PriorityBlockingQueue<AscBigDecimal> initWangGe(String markPx);
-
- /**
- * 初始化开仓操作
- * 根据指定价格初始化开仓队列,将开仓价格点加入到价格队列中
- * @param jiaGe 开仓价格
- * @param queueAsc 价格队列,用于存储按升序排列的价格点
- */
- PriorityBlockingQueue<DescBigDecimal> initKaiCang(BigDecimal jiaGe, PriorityBlockingQueue<AscBigDecimal> queueAsc);
-
- /**
- * 初始化平仓操作
- * 根据指定价格初始化平仓队列,将平仓价格点加入到价格队列中
- * @param jiaGe 开仓价格
- * @param queueAsc 价格队列,用于存储按升序排列的价格点
- */
- PriorityBlockingQueue<AscBigDecimal> initPingCang(BigDecimal jiaGe, PriorityBlockingQueue<AscBigDecimal> queueAsc);
-
-
-}
-
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/wanggeList/WangGeListServiceImpl.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/wanggeList/WangGeListServiceImpl.java
deleted file mode 100644
index 6b3b1d6..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/wanggeList/WangGeListServiceImpl.java
+++ /dev/null
@@ -1,175 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxWs.wanggeList;
-
-import com.xcong.excoin.rabbit.pricequeue.AscBigDecimal;
-import com.xcong.excoin.rabbit.pricequeue.DescBigDecimal;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.concurrent.PriorityBlockingQueue;
-
-/**
- * 网格交易服务实现类,用于初始化价格网格、开仓和平仓操作。
- *
- * @author Administrator
- */
-@Slf4j
-@Service
-@RequiredArgsConstructor
-public class WangGeListServiceImpl implements WangGeListService {
-
- /**
- * 初始化价格网格队列。根据配置的价格上限、下限和间隔生成一系列价格点,
- * 并将这些价格点存入升序优先阻塞队列中。
- *
- * @return 返回初始化完成的升序价格队列;若初始化失败则返回null
- */
- @Override
- public PriorityBlockingQueue<AscBigDecimal> initWangGe(String markPx) {
- log.info("网格初始化中");
- PriorityBlockingQueue<AscBigDecimal> queueAsc = WangGeListQueue.getQueueAsc();
- queueAsc.clear();
-
- //获取WangGeListEnum全部网格参数
- WangGeListEnum gridByPrice = WangGeListEnum.getGridByPrice(new BigDecimal(markPx));
- log.info("获取的网格参数: {}", gridByPrice);
- if (gridByPrice == null){
- log.error("没有获取到网格参数......");
- return null;
- }
-
- String shangxianValue = gridByPrice.getJiage_shangxian();
- String xiaxianValue = gridByPrice.getJiage_xiaxian();
- String jianjuValue = gridByPrice.getJian_ju();
- String weishuValueStr = gridByPrice.getXiaoshu_weishu();
-
- try {
- BigDecimal shangxian = new BigDecimal(shangxianValue);
- BigDecimal xiaxian = new BigDecimal(xiaxianValue);
- BigDecimal jianju = new BigDecimal(jianjuValue);
-
- if (jianju.compareTo(BigDecimal.ZERO) == 0) {
- log.error("价格间隔不能为0");
- return null;
- }
-
- int weishu = Integer.parseInt(weishuValueStr);
- BigDecimal diff = shangxian.subtract(xiaxian);
- int count = diff.divide(jianju, 0, RoundingMode.DOWN).intValue();
-
- BigDecimal currentStep = BigDecimal.ZERO;
- for (int i = 0; i <= count; i++) {
- BigDecimal stepMultiplier = currentStep.multiply(jianju);
- BigDecimal wangGeJiaGe = xiaxian.add(stepMultiplier).setScale(weishu, RoundingMode.DOWN);
- AscBigDecimal ascBigDecimal = new AscBigDecimal(wangGeJiaGe.toString());
- queueAsc.add(ascBigDecimal);
- currentStep = currentStep.add(BigDecimal.ONE);
- }
-
- if (queueAsc.isEmpty()) {
- log.info("网格初始化失败");
- return null;
- }
-
- return queueAsc;
- } catch (NumberFormatException e) {
- log.error("解析价格参数失败", e);
- return null;
- } catch (Exception e) {
- log.error("初始化网格发生未知异常", e);
- return null;
- }
- }
-
- /**
- * 根据当前价格初始化开仓队列。遍历已有的升序价格队列,
- * 将小于当前价格的所有价格点加入降序的开仓队列中。
- *
- * @param jiaGe 当前价格
- * @param queueAsc 已初始化的价格升序队列
- */
- @Override
- public PriorityBlockingQueue<DescBigDecimal> initKaiCang(BigDecimal jiaGe, PriorityBlockingQueue<AscBigDecimal> queueAsc) {
- PriorityBlockingQueue<DescBigDecimal> queueKaiCang = WangGeListQueue.getKaiCang();
- queueKaiCang.clear();
-
- AscBigDecimal now = new AscBigDecimal(jiaGe.toString());
-
- for (AscBigDecimal ascBigDecimal : queueAsc) {
- if (ascBigDecimal.compareTo(now) < 0) {
- DescBigDecimal kaiCangJia = new DescBigDecimal(ascBigDecimal.getValue().toString());
- queueKaiCang.add(kaiCangJia);
- }
- }
- StringBuilder kaiCangStr = new StringBuilder();
- kaiCangStr.append("初始化下限队列: [");
- boolean first = true;
- for (DescBigDecimal item : queueKaiCang) {
- if (!first) {
- kaiCangStr.append(", ");
- }
- kaiCangStr.append(item.getValue());
- first = false;
- }
- kaiCangStr.append("]");
- log.info(kaiCangStr.toString());
-
- return queueKaiCang;
- }
-
- /**
- * 根据当前价格初始化平仓队列。遍历已有的升序价格队列,
- * 将大于当前价格的所有价格点加入升序的平仓队列中。
- *
- * @param jiaGe 当前价格
- * @param queueAsc 已初始化的价格升序队列
- */
- @Override
- public PriorityBlockingQueue<AscBigDecimal> initPingCang(BigDecimal jiaGe, PriorityBlockingQueue<AscBigDecimal> queueAsc) {
- PriorityBlockingQueue<AscBigDecimal> queuePingCang = WangGeListQueue.getPingCang();
- queuePingCang.clear();
-
- AscBigDecimal now = new AscBigDecimal(jiaGe.toString());
-
- for (AscBigDecimal ascBigDecimal : queueAsc) {
- if (ascBigDecimal.compareTo(now) > 0) {
- queuePingCang.add(ascBigDecimal);
- }
- }
-
- StringBuilder pingCangStr = new StringBuilder();
- pingCangStr.append("初始化上限队列: [");
- boolean first = true;
- for (AscBigDecimal item : queuePingCang) {
- if (!first) {
- pingCangStr.append(", ");
- }
- pingCangStr.append(item.getValue());
- first = false;
- }
- pingCangStr.append("]");
- log.info(pingCangStr.toString());
-
- return queuePingCang;
- }
-
- /**
- * 主方法,用于测试网格初始化及开仓/平仓逻辑。
- * 示例使用固定价格"0.355"进行模拟调用。
- *
- * @param args 启动参数(未使用)
- */
- public static void main(String[] args) {
- WangGeListServiceImpl wangGeService = new WangGeListServiceImpl();
- String openPx = "2875";
- String markPx = "2905";
- String orderPx = "2895";
- PriorityBlockingQueue<AscBigDecimal> queueAsc = wangGeService.initWangGe(openPx);
- if (queueAsc != null) {
- wangGeService.initKaiCang(new BigDecimal(orderPx), queueAsc);
- wangGeService.initPingCang(new BigDecimal(orderPx), queueAsc);
- }
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/DataUtil.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/DataUtil.java
deleted file mode 100644
index 4992a3b..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/DataUtil.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi;
-
-import java.math.BigDecimal;
-
-/**
- * 时间工具类
- *
- * @author MrBird
- */
-public class DataUtil {
-
- //输入两个整数a、b,a/b取模,输出模,如果有余数,输出模+1
- public static int mod(int a, int b) {
- int mod = a / b;
- if (mod != 0) {
- return mod + 1;
- }
- return mod;
- }
-
- public static void main(String[] args) {
- System.out.println(getDecimalDigits8("3422.66666666666666"));
- }
-
- //输入一个字符串类的小数,输出小数位数
- public static int getDecimalDigits(String num) {
- if( num.indexOf(".") == -1){
- return Integer.valueOf(num);
- } else {
- return String.valueOf(num).split("\\.")[1].length(); //split() 方法用于把一个字符串分割成字符串数组。
- }
- }
-
- //输入一个BigDecimal类的小数,输出小数位数,
- public static int getDecimalDigitsNew(BigDecimal num) {
- //除去小数点后多余的0
- num = num.stripTrailingZeros();
- if (num.scale() == 0) {
- return 0;
- } else {
- return num.scale(); //scale() 方法返回小数点后的位数。
- }
- }
-
- //输入一个包含有小数的字符串,输出原字符串,如果小数位数超过8位,则保留8位小数
- public static String getDecimalDigits8(String num) {
- if (getDecimalDigits(num) > 8) {
- return String.format("%.8f", Double.valueOf(num));
- } else {
- return num;
- }
- }
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/MallUtils.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/MallUtils.java
deleted file mode 100644
index 9f378bf..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/MallUtils.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi;
-
-import cn.hutool.core.util.StrUtil;
-
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Random;
-
-/**
- * @author wzy
- * @date 2021-09-22
- **/
-public class MallUtils {
-
- public static String getRandomNum(int length) {
- String str = "0123456789";
- Random random = new Random();
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < length; ++i) {
- int number = random.nextInt(str.length());
- sb.append(str.charAt(number));
- }
-
- return sb.toString();
- }
-
- public static String getOrderNum(String prefix) {
- SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
- String dd=df.format(new Date());
- if (StrUtil.isNotBlank(prefix)) {
- return prefix+dd+getRandomNum(5);
- }
- return dd+getRandomNum(5);
- }
-
- public static String getOrderNum() {
- return getOrderNum(null);
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/Account.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/Account.java
deleted file mode 100644
index 3bf3669..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/Account.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config;
-
-import lombok.Data;
-
-@Data
-public abstract class Account {
- // 交易所的基URL
- public String baseUrl;
- // 处理HTTP请求的处理器
- public RequestHandler requestHandler;
- // 表示是否显示限制使用情况
- public boolean isSimluate;
-
- public Account(){}
-
- public Account(String baseUrl, String apiKey, String secretKey, String passPhrase,boolean isSimluate) {
- this.baseUrl = baseUrl;
- this.requestHandler = new RequestHandler(apiKey, secretKey,passPhrase);
- this.isSimluate = isSimluate;
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/Dto/QuantApiMessage.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/Dto/QuantApiMessage.java
deleted file mode 100644
index 48be097..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/Dto/QuantApiMessage.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config.Dto;
-
-import lombok.Data;
-
-/**
- * 交易所信息绑定
- */
-@Data
-public class QuantApiMessage{
- private Long memberId;//用户ID
- private String exchange;//交易所名称
- private String aSecretkey;//A秘钥(access_key)
- private String bSecretkey;//s秘钥(secret_key)
- private String passPhrass;//passphrass
- private String accountType;//账户类型 true:正式 false:测试
- private int state;//是否成功连通账户 0-失败 1-成功
- private int isTrade;//是否可交易 1.是 2-否
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/Dto/SubmitOrderReqDto.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/Dto/SubmitOrderReqDto.java
deleted file mode 100644
index 9a1cd32..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/Dto/SubmitOrderReqDto.java
+++ /dev/null
@@ -1,165 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config.Dto;
-
-import lombok.Data;
-
-import java.util.List;
-
-@Data
-public class SubmitOrderReqDto {
- /**
- * 是 当前价
- */
- private String price;
- /**
- * 是 产品ID,如 BTC-USDT
- */
- private String instId;
- /**
- * 是 交易模式
- * 保证金模式:isolated:逐仓 ;cross:全仓
- * 非保证金模式:cash:非保证金
- * spot_isolated:现货逐仓(仅适用于现货带单) ,现货带单时,tdMode 的值需要指定为spot_isolated
- */
- private String tdMode;
- /**
- * 否 保证金币种,仅适用于现货和合约模式下的全仓杠杆订单
- */
- private String ccy;
- /**
- * 否 客户自定义订单ID
- * 字母(区分大小写)与数字的组合,可以是纯字母、纯数字且长度要在1-32位之间。
- */
- private String clOrdId;
- /**
- * 否 订单标签
- * 字母(区分大小写)与数字的组合,可以是纯字母、纯数字,且长度在1-16位之间。
- */
- private String tag;
- /**
- * 是 订单方向
- * buy:买, sell:卖
- */
- private String side;
- /**
- * 可选 持仓方向
- * 在开平仓模式下必填,且仅可选择 long 或 short。 仅适用交割、永续。
- */
- private String posSide;
- /**
- * 是 订单类型
- * market:市价单
- * limit:限价单
- * post_only:只做maker单
- * fok:全部成交或立即取消
- * ioc:立即成交并取消剩余
- * optimal_limit_ioc:市价委托立即成交并取消剩余(仅适用交割、永续)
- * mmp:做市商保护(仅适用于组合保证金账户模式下的期权订单)
- * mmp_and_post_only:做市商保护且只做maker单(仅适用于组合保证金账户模式下的期权订单)
- */
- private String ordType;
- /**
- * 是 委托数量
- */
- private String sz;
- /**
- * 可选 委托价格,仅适用于limit、post_only、fok、ioc、mmp、mmp_and_post_only类型的订单
- * 期权下单时,px/pxUsd/pxVol 只能填一个
- */
- private String px;
- /**
- * 可选 以USD价格进行期权下单
- * 仅适用于期权
- * 期权下单时 px/pxUsd/pxVol 必填一个,且只能填一个
- */
- private String pxUsd;
- /**
- * 可选 以隐含波动率进行期权下单,例如 1 代表 100%
- * 仅适用于期权
- * 期权下单时 px/pxUsd/pxVol 必填一个,且只能填一个
- */
- private String pxVol;
- /**
- * 否 是否只减仓,true 或 false,默认false
- * 仅适用于币币杠杆,以及买卖模式下的交割/永续
- * 仅适用于现货和合约模式和跨币种保证金模式
- */
- private Boolean reduceOnly;
- /**
- * 否 市价单委托数量sz的单位,仅适用于币币市价订单
- * base_ccy: 交易货币 ;quote_ccy:计价货币
- * 买单默认quote_ccy, 卖单默认base_ccy
- */
- private String tgtCcy;
- /**
- * 否 是否禁止币币市价改单,true 或 false,默认false
- * 为true时,余额不足时,系统不会改单,下单会失败,仅适用于币币市价单
- */
- private Boolean banAmend;
- /**
- * 否 自成交保护模式
- * 默认为 cancel maker
- * cancel_maker,cancel_taker, cancel_both
- * Cancel both不支持FOK
- */
- private String stpMode;
- /**
- * 否 下单附带止盈止损信息
- */
- private List<Object> attachAlgoOrds;
- /**
- * 否 下单附带止盈止损时,客户自定义的策略订单ID
- * 字母(区分大小写)与数字的组合,可以是纯字母、纯数字且长度要在1-32位之间。
- * 订单完全成交,下止盈止损委托单时,该值会传给algoClOrdId
- */
- private String attachAlgoClOrdId;
- /**
- * 可选 止盈触发价
- * 对于条件止盈单,如果填写此参数,必须填写 止盈委托价
- */
- private String tpTriggerPx;
- /**
- * 可选 止盈委托价
- * 对于条件止盈单,如果填写此参数,必须填写 止盈触发价
- * 对于限价止盈单,需填写此参数,不需要填写止盈触发价
- * 委托价格为-1时,执行市价止盈
- */
- private String tpOrdPx;
- /**
- * 否 止盈订单类型
- * condition: 条件单
- * limit: 限价单
- * 默认为condition
- */
- private String tpOrdKind;
- /**
- * 可选 止损触发价,如果填写此参数,必须填写 止损委托价
- */
- private String slTriggerPx;
- /**
- * String 可选 止损委托价,如果填写此参数,必须填写 止损触发价
- * 委托价格为-1时,执行市价止损
- */
- private String slOrdPx;
- /**
- * 否 止盈触发价类型
- * last:最新价格
- * index:指数价格
- * mark:标记价格
- * 默认为last
- */
- private String tpTriggerPxType;
- /**
- * 否 止损触发价类型
- * last:最新价格
- * index:指数价格
- * mark:标记价格
- * 默认为last
- */
- private String slTriggerPxType;
- /**
- * 否 是否启用开仓价止损,仅适用于分批止盈的止损订单,第一笔止盈触发时,止损触发价格是否移动到开仓均价止损
- * 0:不开启,默认值
- * 1:开启,且止损触发价不能为空
- */
- private String amendPxOnTriggerType;
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/Dto/TradeOrderDto.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/Dto/TradeOrderDto.java
deleted file mode 100644
index b11b8ba..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/Dto/TradeOrderDto.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config.Dto;
-
-import io.swagger.annotations.ApiModel;
-import lombok.Data;
-
-/**
- * @author wzy
- * @date 2021-09-16
- **/
-@Data
-@ApiModel(value = "TradeOrderDto", description = "交易订单参数接收类")
-public class TradeOrderDto {
- private String instrumentId;
- private String side;
- private String type;
- private String size;
- private String price;
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/ExchangeInfoEnum.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/ExchangeInfoEnum.java
deleted file mode 100644
index 8aab481..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/ExchangeInfoEnum.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config;
-
-import lombok.Getter;
-
-/**
- * 交易信息枚举类
- * 用于存储不同交易账户的密钥信息,包括实盘账户和模拟账户
- */
-@Getter
-public enum ExchangeInfoEnum {
-
- /**
- * 模拟盘账户信息
- * 存储了模拟盘交易所需的API密钥、秘钥和通过码
- */
-// OKX_UAT("f512673b-2685-4fcb-9bb1-2ae8db745d62",
-// "B0C1CC8F39625B41140D93DC25039E33",
-// "Aa12345678@",
-// true);
- OKX_UAT("ffb4e79f-fcf5-4afb-82c5-2fbb64123f61",
- "AA06C5ED1D7C7F5AFE6484052E231C55",
- "Aa12345678@",
- false);
-
-// /**
-// * 模拟盘账户信息
-// * 存储了模拟盘交易所需的API密钥、秘钥和通过码
-// */
-// OKX_UAT("0769b50c-2c36-4310-8bd9-cad6bc6c9d8f",
-// "7AF4A574BC44907CE76BBFF91F53852D",
-// "Aa123456@",
-// false);
-
- // API公钥,用于识别用户身份
- private String apiKey;
-
- // API秘钥,用于签名和验证请求
- private String secretKey;
-
- // API通过码,用于额外的身份验证
- private String passphrase;
-
- // 账户类型,true表示实盘账户,false表示模拟账户
- private boolean accountType;
-
- /**
- * 构造方法
- *
- * @param apiKey API公钥,用于识别用户身份
- * @param secretKey API秘钥,用于签名和验证请求
- * @param passphrase API通过码,用于额外的身份验证
- * @param accountType 账户类型,true表示实盘账户,false表示模拟账户
- */
- ExchangeInfoEnum(String apiKey, String secretKey, String passphrase, boolean accountType) {
- this.apiKey = apiKey;
- this.secretKey = secretKey;
- this.passphrase = passphrase;
- this.accountType = accountType;
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/ExchangeLoginEventService.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/ExchangeLoginEventService.java
deleted file mode 100644
index b4d7b33..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/ExchangeLoginEventService.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config;
-
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.Dto.SubmitOrderReqDto;
-
-import java.util.LinkedHashMap;
-
-public interface ExchangeLoginEventService {
- /**
- * 获取交易产品基础信息
- * 获取所有可交易产品的信息列表。
- * <br><br>
- * GET /api/v5/public/instruments /api/v5/account/instruments
- * <br>
- *
- * @param parameters LinkedHashedMap of String,Object pair
- * where String is the name of the parameter and Object is the value of the parameter
- * <br><br>
- * instType -- String 是 产品类型 SPOT:币币 MARGIN:币币杠杆 SWAP:永续合约 FUTURES:交割合约 OPTION:期权 <br>
- * uly -- String 可选 标的指数,仅适用于交割/永续/期权,期权必填 <br>
- * instFamily -- String 否 交易品种,仅适用于交割/永续/期权 <br>
- * instId -- String 否 产品ID <br>
- * @return String
- * @see <a href="https://www.okx.com/docs-v5/zh/#rest-api-public-data-get-instruments">
- * https://www.okx.com/docs-v5/zh/#rest-api-public-data-get-instruments</a>
- */
- String exchangeInfo(LinkedHashMap<String, Object> parameters);
-
- String lineHistory(LinkedHashMap<String, Object> parameters);
- /**
- * 查看账户余额
- * 获取交易账户中资金余额信息。
- * <br><br>
- * GET /api/v5/account/balance
- * <br>
- * @param
- * parameters LinkedHashedMap of String,Object pair
- * where String is the name of the parameter and Object is the value of the parameter
- * <br><br>
- * ccy -- String 否 币种,如 BTC 支持多币种查询(不超过20个),币种之间半角逗号分隔 <br>
- * @return String
- * @see <a href="https://www.okx.com/docs-v5/zh/#rest-api-account-get-balance">
- * https://www.okx.com/docs-v5/zh/#rest-api-account-get-balance</a>
- */
- String balance(LinkedHashMap<String, Object> parameters);
- /**
- * 查看持仓信息
- * 获取该账户下拥有实际持仓的信息。账户为单向持仓模式会显示净持仓(net),账户为双向持仓模式下会分别返回多头(long)或空头(short)的仓位。按照仓位创建时间倒序排列。
- * <br><br>
- * GET /api/v5/account/positions
- * <br>
- * @param
- * parameters LinkedHashedMap of String,Object pair
- * where String is the name of the parameter and Object is the value of the parameter
- * <br><br>
- * instType -- String 否 产品类型
- * MARGIN:币币杠杆
- * SWAP:永续合约
- * FUTURES:交割合约
- * OPTION:期权
- * instType和instId同时传入的时候会校验instId与instType是否一致。<br>
- * instId -- String 否 交易产品ID,如:BTC-USD-190927-5000-C
- * 支持多个instId查询(不超过10个),半角逗号分隔<br>
- * posId -- String 否 持仓ID
- * 支持多个posId查询(不超过20个),半角逗号分割<br>
- * @return String <br>
- * note: 如果该 instId 拥有过仓位且当前持仓量为0,传 instId 时,会返回仓位信息;不传 instId 时,仓位信息不返回。
- * 逐仓交易设置中,如果设置为自主划转模式,逐仓转入保证金后,会生成一个持仓量为0的仓位 <br>
- * @see <a href="https://www.okx.com/docs-v5/zh/#rest-api-account-get-positions">
- * https://www.okx.com/docs-v5/zh/#rest-api-account-get-positions</a>
- */
- String positions(LinkedHashMap<String, Object> parameters);
- /**
- * 查看历史持仓信息
- * 获取最近3个月有更新的仓位信息,按照仓位更新时间倒序排列。
- * <br><br>
- * GET /api/v5/account/positions-history
- * <br>
- * @param
- * parameters LinkedHashedMap of String,Object pair
- * where String is the name of the parameter and Object is the value of the parameter
- * <br><br>
- * instType -- String 否 产品类型
- * MARGIN:币币杠杆
- * SWAP:永续合约
- * FUTURES:交割合约
- * OPTION:期权 <br>
- * instId -- String 否 交易产品ID,如:BTC-USD-SWAP <br>
- * mgnMode -- String 否 保证金模式
- * cross:全仓,isolated:逐仓
- * type -- String 否 平仓类型
- * 1:部分平仓;2:完全平仓;3:强平;4:强减; 5:ADL自动减仓;
- * 状态叠加时,以最新的平仓类型为准状态为准。 <br>
- * posId -- String 否 持仓ID <br>
- * after -- String 否 查询仓位更新 (uTime) 之前的内容,值为时间戳,Unix 时间戳为毫秒数格式,如 1597026383085 <br>
- * before -- String 否 查询仓位更新 (uTime) 之后的内容,值为时间戳,Unix 时间戳为毫秒数格式,如 1597026383085 <br>
- * limit -- String 否 分页返回结果的数量,最大为100,默认100条 <br>
- * @return String
- * @see <a href="https://www.okx.com/docs-v5/zh/#rest-api-account-get-positions-history">
- * https://www.okx.com/docs-v5/zh/#rest-api-account-get-positions-history</a>
- */
- String positionsHistory(LinkedHashMap<String, Object> parameters);
- /**
- * 撤单
- * 撤销之前下的未完成订单。
- *
- * <br><br>
- * GET /api/v5/trade/cancel-order
- * <br>
- *
- * @param originOrderId 用户自定义ID
- * LinkedHashedMap of String,Object pair
- * where String is the name of the parameter and Object is the value of the parameter
- * <br><br>
- * instId -- String 是 产品ID,如 BTC-USD-190927 <br>
- * ordId -- String 可选 订单ID, ordId和clOrdId必须传一个,若传两个,以ordId为主 <br>
- * clOrdId -- String 可选 用户自定义ID <br>
- * @return String
- * @see <a href="https://www.okx.com/docs-v5/zh/#rest-api-trade-cancel-order">
- * https://www.okx.com/docs-v5/zh/#rest-api-trade-cancel-order</a>
- */
- boolean cancelOrder(String originOrderId,String instId);
- /**
- * 下单
- * 只有当您的账户有足够的资金才能下单。
- *
- * <br><br>
- * GET /api/v5/trade/order
- * <br>
- *
- * @param submitOrderReq
- * LinkedHashedMap of String,Object pair
- * where String is the name of the parameter and Object is the value of the parameter
- * <br><br>
- * instId -- String 是 产品ID,如 BTC-USD-190927-5000-C <br>
- * tdMode -- String 是 交易模式
- * 保证金模式:isolated:逐仓 ;cross:全仓
- * 非保证金模式:cash:非保证金 <br>
- * ccy -- String 否 保证金币种,仅适用于单币种保证金模式下的全仓杠杆订单 <br>
- * clOrdId -- String 否 客户自定义订单ID
- * 字母(区分大小写)与数字的组合,可以是纯字母、纯数字且长度要在1-32位之间。<br>
- * tag -- String 否 订单标签
- * 字母(区分大小写)与数字的组合,可以是纯字母、纯数字,且长度在1-16位之间。 <br>
- * side -- String 是 订单方向
- * buy:买, sell:卖 <br>
- * posSide -- String 可选 持仓方向
- * 在双向持仓模式下必填,且仅可选择 long 或 short。 仅适用交割、永续。 <br>
- * ordType -- String 是 订单类型
- * market:市价单
- * limit:限价单
- * post_only:只做maker单
- * fok:全部成交或立即取消
- * ioc:立即成交并取消剩余
- * optimal_limit_ioc:市价委托立即成交并取消剩余(仅适用交割、永续) <br>
- * sz -- String 是 委托数量 <br>
- * px -- String 可选 委托价格,仅适用于limit、post_only、fok、ioc类型的订单 <br>
- * reduceOnly -- Boolean 否 是否只减仓,true 或 false,默认false
- * 仅适用于币币杠杆,以及买卖模式下的交割/永续
- * 仅适用于单币种保证金模式和跨币种保证金模式 <br>
- * tgtCcy -- String 否 市价单委托数量sz的单位,仅适用于币币市价订单
- * base_ccy: 交易货币 ;quote_ccy:计价货币
- * 买单默认quote_ccy, 卖单默认base_ccy <br>
- * banAmend -- Boolean 否 是否禁止币币市价改单,true 或 false,默认false
- * 为true时,余额不足时,系统不会改单,下单会失败,仅适用于币币市价单 <br>
- * tpTriggerPx -- String 否 止盈触发价,如果填写此参数,必须填写 止盈委托价 <br>
- * tpOrdPx -- String 否 止盈委托价,如果填写此参数,必须填写 止盈触发价
- * 委托价格为-1时,执行市价止盈 <br>
- * slTriggerPx -- String 否 止损触发价,如果填写此参数,必须填写 止损委托价 <br>
- * slOrdPx -- String 否 止损委托价,如果填写此参数,必须填写 止损触发价
- * 委托价格为-1时,执行市价止损 <br>
- * tpTriggerPxType -- String 否 止盈触发价类型
- * last:最新价格
- * index:指数价格
- * mark:标记价格
- * 默认为last <br>
- * slTriggerPxType -- String 否 止损触发价类型
- * last:最新价格
- * index:指数价格
- * mark:标记价格
- * 默认为last <br>
- * @return String
- * @see <a href="https://www.okx.com/docs-v5/zh/#rest-api-trade-place-order">
- * https://www.okx.com/docs-v5/zh/#rest-api-trade-place-order</a>
- */
- String submitOrder(SubmitOrderReqDto submitOrderReq);
-
- /**
- * 获取单个币种价格信息
- * @param parameters
- * @return
- */
- String tickerMess(LinkedHashMap<String, Object> parameters);
-
- /**
- * 获取单个订单信息
- * @param parameters
- * @return
- */
- public String getOrderMessage(LinkedHashMap<String, Object> parameters);
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/ExchangeLoginService.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/ExchangeLoginService.java
deleted file mode 100644
index 20ff07a..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/ExchangeLoginService.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config;
-
-
-import com.xcong.excoin.modules.okxNewPrice.utils.FebsException;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.impl.ExchangeLoginEventServiceImpl;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * 交易所登录服务 singleton模式
- * 负责根据交易所类型提供对应的交易所登录事件服务
- */
-public class ExchangeLoginService {
- // 存储交易所类型与登录服务实例的映射
- private final static Map<String, ExchangeLoginEventService> eventMap = new HashMap<>();
-
- // 静态代码块,用于初始化eventMap
- static {
- for (ExchangeInfoEnum infoEnum : ExchangeInfoEnum.values()) {
- eventMap.put(infoEnum.name(), new ExchangeLoginEventServiceImpl(
- infoEnum.getApiKey(),
- infoEnum.getSecretKey(),
- infoEnum.getPassphrase(),
- infoEnum.isAccountType()));
- }
- }
-
- // 私有构造方法,防止外部实例化
- private ExchangeLoginService() {
- }
-
- // Singleton实例
- public final static ExchangeLoginService INSTANCE = new ExchangeLoginService();
-
- /**
- * 根据交易所类型获取对应的交易所登录事件服务
- * @param exchangeType 交易所类型
- * @return 对应的交易所登录事件服务实例
- * @throws FebsException 如果提供的交易所类型无效,则抛出异常
- */
- public static ExchangeLoginEventService getInstance(String exchangeType) {
- ExchangeLoginEventService exchange = eventMap.get(exchangeType);
- if (exchange == null) {
- throw new FebsException("参数错误");
- }
-
- return exchange;
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/OKXAccount.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/OKXAccount.java
deleted file mode 100644
index 0277c5c..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/OKXAccount.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config;
-
-import lombok.Data;
-
-/**
- * OKX交易所账户配置类
- * 用于存储和管理OKX交易所的账户信息和请求处理
- */
-@Data
-public class OKXAccount extends Account{
-
- // 用于在OKX交易所进行身份验证的密码短语
- String passPhrase;
- // 表示是否显示限制使用情况
- public boolean showLimitUsage;
-
- /**
- * OKXAccount的构造方法
- *
- * @param baseUrl OKX交易所的基URL
- * @param apiKey API的密钥
- * @param secretKey 私钥
- * @param passPhrase 用于身份验证的密码短语
- * @param isSimluate 表示是否为模拟交易
- */
- public OKXAccount(String baseUrl, String apiKey, String secretKey, String passPhrase,boolean isSimluate) {
- super(baseUrl, apiKey, secretKey,passPhrase, isSimluate);
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/RequestHandler.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/RequestHandler.java
deleted file mode 100644
index fbf5f94..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/RequestHandler.java
+++ /dev/null
@@ -1,125 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config;
-
-import com.alibaba.fastjson.JSON;
-import com.xcong.excoin.modules.okxNewPrice.utils.FebsException;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.enums.HttpMethod;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.enums.RequestType;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils.DateUtils;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils.RequestBuilder;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils.SignUtils;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils.UrlBuilder;
-import lombok.extern.slf4j.Slf4j;
-import okhttp3.Request;
-
-import java.util.Date;
-import java.util.LinkedHashMap;
-
-@Slf4j
-public class RequestHandler {
- private final String apiKey;
- private final String secretKey;
- private final String passphrase;
-
- public RequestHandler(String apiKey) {
- this.apiKey = apiKey;
- this.secretKey = null;
- this.passphrase = null;
- }
-
- public RequestHandler(String apiKey, String secretKey, String passphrase) {
- this.apiKey = apiKey;
- this.secretKey = secretKey;
- this.passphrase = passphrase;
- }
-
- public static void main(String[] args) {
- LinkedHashMap<String, Object> balanceParameters = new LinkedHashMap<>();
- String queryString = UrlBuilder.joinQueryParameters(new StringBuilder("/api/v5/account/balance"), balanceParameters).toString();
- String balanceParameters1 = UrlBuilder.buildFullUrl("/api/v5/account/balance","" , balanceParameters, null);
- System.out.println(queryString);
- System.out.println(balanceParameters1);
- }
-
- /**
- * Build request based on request type and send the requests to server.
- *
- * @param baseUrl
- * @param urlPath
- * @param parameters
- * @param httpMethod
- * @param requestType
- * @return String - response from server
- */
- private String sendApiRequest(String baseUrl, String urlPath, LinkedHashMap<String, Object> parameters,
- HttpMethod httpMethod, RequestType requestType, boolean isSimluate) {
- String fullUrl = UrlBuilder.buildFullUrl(baseUrl, urlPath, parameters, null);
- log.debug("{} {}", httpMethod, fullUrl);
- //System.out.println("sendApiRequest:fullUrl"+fullUrl);
- Request request;
- switch (requestType) {
- case PUBLIC:
- request = RequestBuilder.buildPublicRequest(fullUrl, httpMethod, isSimluate).build();
- break;
- case WITH_API_KEY:
- case SIGNED:
- // 获取签名
- String timestamp = DateUtils.format(DateUtils.FORMAT_UTC_ISO8601, new Date(), 0);
- String queryString = UrlBuilder.joinQueryParameters(new StringBuilder(urlPath), parameters).toString();
- // String timestamp = System.currentTimeMillis()+"";
-// System.out.println("timestamp:"+timestamp);
-// System.out.println("timestamp:"+timestamp);
-// System.out.println("secretKey:"+secretKey);
-// System.out.println("httpMethod.toString():"+httpMethod.toString());
-// System.out.println("queryString:"+queryString);
-// System.out.println("passphrase:"+passphrase);
- // 组装body
- String body = "";
- if (HttpMethod.POST.equals(httpMethod)) {
- body = JSON.toJSONString(parameters);
- queryString = UrlBuilder.joinQueryParameters(new StringBuilder(urlPath), null).toString();
- fullUrl = UrlBuilder.buildFullUrl(baseUrl, urlPath, null, null);
- }
- if (HttpMethod.GET.equals(httpMethod)) {
- queryString = UrlBuilder.buildFullUrl(urlPath,"" , parameters, null);
-// queryString = UrlBuilder.buildFullUrl(null, urlPath, parameters, null);
- }
- String sign = SignUtils.signRest(secretKey,
- timestamp,
- httpMethod.toString(),
- queryString, body);
-
-
- request = RequestBuilder.buildApiKeyRequest(fullUrl, body, passphrase, sign, timestamp, httpMethod, apiKey,isSimluate);
-
-
- break;
- default:
- throw new FebsException("[RequestHandler] Invalid request type: " + requestType);
- }
- return ResponseHandler.handleResponse(request, isSimluate);
- }
-
- public String sendPublicRequest(String baseUrl, String urlPath, LinkedHashMap<String, Object> parameters,
- HttpMethod httpMethod, boolean isSimluate) {
- return sendApiRequest(baseUrl, urlPath, parameters, httpMethod, RequestType.PUBLIC, isSimluate);
- }
-
- public String sendWithApiKeyRequest(String baseUrl, String urlPath, LinkedHashMap<String, Object> parameters,
- HttpMethod httpMethod, boolean isSimluate) {
- if (null == apiKey || apiKey.isEmpty()) {
- throw new FebsException("[RequestHandler] API key cannot be null or empty!");
- }
- return sendApiRequest(baseUrl, urlPath, parameters, httpMethod, RequestType.WITH_API_KEY, isSimluate);
- }
-
- public String sendSignedRequest(String baseUrl, String urlPath, LinkedHashMap<String, Object> parameters,
- HttpMethod httpMethod, boolean isSimluate) {
- if (null == secretKey || secretKey.isEmpty() || null == apiKey || apiKey.isEmpty()) {
- throw new FebsException("[RequestHandler] Secret key/API key cannot be null or empty!");
- }
- //parameters.put("timestamp", UrlBuilder.buildTimestamp());
- //String queryString = UrlBuilder.joinQueryParameters(parameters);
- //String signature = SignatureGenerator.getSignature(queryString, secretKey);
- return sendApiRequest(baseUrl, urlPath, parameters, httpMethod, RequestType.SIGNED, isSimluate);
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/ResponseHandler.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/ResponseHandler.java
deleted file mode 100644
index 3ef2c50..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/ResponseHandler.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config;
-
-
-import com.xcong.excoin.modules.okxNewPrice.utils.FebsException;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils.JSONParser;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils.OkHttpUtils;
-import okhttp3.Request;
-import okhttp3.Response;
-import okhttp3.ResponseBody;
-import org.json.JSONException;
-
-import java.io.IOException;
-
-public final class ResponseHandler {
-
- private static final int HTTP_STATUS_CODE_400 = 400;
- private static final int HTTP_STATUS_CODE_499 = 499;
- private static final int HTTP_STATUS_CODE_500 = 500;
-
- private ResponseHandler() {
- }
-
- public static String handleResponse(Request request, boolean showLimitUsage) {
- try (Response response = OkHttpUtils.okHttpClient.newCall(request).execute()) {//OkHttpUtils.builder().okHttpClient
- String responseAsString = getResponseBodyAsString(response.body());
-
- if (response.code() >= HTTP_STATUS_CODE_400 && response.code() <= HTTP_STATUS_CODE_499) {
- throw handleErrorResponse(responseAsString, response.code());
- } else if (response.code() >= HTTP_STATUS_CODE_500) {
- System.out.println("handleResponse:"+response.code());
- throw new FebsException("responseAsString-"+responseAsString+";handleResponse-"+response.code());
- }
- return responseAsString;
-// if (showLimitUsage) {
-// return getlimitUsage(response, responseAsString);
-// } else {
-// return responseAsString;
-// }
- } catch (IOException | IllegalStateException e) {
- e.printStackTrace();
- throw new FebsException("[ResponseHandler] OKHTTP Error: " + e.getMessage());
- }
- }
-
-
- private static FebsException handleErrorResponse(String responseBody, int responseCode) {
- try {
- String errorMsg = JSONParser.getJSONStringValue(responseBody, "msg");
- int errorCode = JSONParser.getJSONIntValue(responseBody, "code");
- return new FebsException("responseBody-"+responseBody+";errorMsg-"+errorMsg+";responseCode-"+responseCode+";errorCode-"+errorCode);
- } catch (JSONException e) {
- throw new FebsException("responseBody-"+responseBody+";responseCode-"+responseCode);
- }
- }
-
- private static String getResponseBodyAsString(ResponseBody body) throws IOException {
- if (null != body) {
- return body.string();
- } else {
- return "";
- }
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/enums/DefaultUrls.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/enums/DefaultUrls.java
deleted file mode 100644
index a666b21..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/enums/DefaultUrls.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config.enums;
-
-public final class DefaultUrls {
- public static final String USDM_UAT_URL = "https://www.okx.com";
- public static final String USDM_UAT_WSS_URL = "wss://wspap.okx.com:8443";
- //public static final String USDM_UAT_WSS_URL = "wss://ws.okx.com:8443";
- //USD-M Futures
- public static final String USDM_PROD_URL = "https://www.okx.com";
- public static final String USDM_PROD_WS_URL = "wss://ws.okx.com:8443";
- //比特币买入数量
- public static final String BTC_BUYNUMBER = "0.001";
- //以太坊买入数量
- public static final String ETH_BUYNUMBER = "0.01";
- //狗狗币买入数量
- public static final String DOGE_BUYNUMBER = "100";
- /**
- * 全部卖出
- */
- public static final String OPERATION_ALLSOLD = "allsold";
-
- /**
- * 卖出
- */
- public static final String OPERATION_SOLD = "sell";
-
- /**
- * 买入
- */
- public static final String OPERATION_BUY = "buy";
-
- /**
- * 不买入
- */
- public static final String OPERATION_NOBUY = "nobuy";
-
- private DefaultUrls() {
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/enums/HttpMethod.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/enums/HttpMethod.java
deleted file mode 100644
index 44f0a21..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/enums/HttpMethod.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config.enums;
-
-public enum HttpMethod {
- POST,
- GET,
- PUT,
- DELETE,
- INVALID
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/enums/RequestType.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/enums/RequestType.java
deleted file mode 100644
index e50bc1b..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/enums/RequestType.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config.enums;
-
-public enum RequestType {
- PUBLIC,
- WITH_API_KEY,
- SIGNED
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/impl/ExchangeLoginEventServiceImpl.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/impl/ExchangeLoginEventServiceImpl.java
deleted file mode 100644
index 3d781ed..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/impl/ExchangeLoginEventServiceImpl.java
+++ /dev/null
@@ -1,169 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config.impl;
-
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.Dto.SubmitOrderReqDto;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.ExchangeInfoEnum;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.ExchangeLoginEventService;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.ExchangeLoginService;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.OKXAccount;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.enums.DefaultUrls;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.enums.HttpMethod;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils.OKXContants;
-import lombok.extern.slf4j.Slf4j;
-
-import java.math.BigDecimal;
-import java.util.*;
-
-@Slf4j
-public class ExchangeLoginEventServiceImpl implements ExchangeLoginEventService {
-
-
- private OKXAccount OKXAccount;
- private String apiKey;
- private String secretKey;
- private String passphrase;
- private boolean accountType;
-
- public ExchangeLoginEventServiceImpl(String apiKey, String secretKey, String passphrase, boolean accountType) {
- this.apiKey = apiKey;
- this.secretKey = secretKey;
- this.passphrase = passphrase;
- this.accountType = accountType;
- OKXAccount = new OKXAccount(
- accountType ? DefaultUrls.USDM_PROD_URL : DefaultUrls.USDM_UAT_URL,
- apiKey,
- secretKey,
- passphrase,
- !accountType);
- }
-
- @Override
- public String exchangeInfo(LinkedHashMap<String, Object> parameters) {
- return OKXAccount.requestHandler.sendPublicRequest(OKXAccount.baseUrl, OKXContants.INSTRUMENTS,parameters, HttpMethod.GET, OKXAccount.isSimluate());
- }
-
- @Override
- public String lineHistory(LinkedHashMap<String, Object> parameters) {
- return OKXAccount.requestHandler.sendPublicRequest(OKXAccount.baseUrl, OKXContants.K_LINE_HISTORY,parameters, HttpMethod.GET, OKXAccount.isSimluate());
- }
-
- @Override
- public String balance(LinkedHashMap<String, Object> parameters) {
- return OKXAccount.requestHandler.sendSignedRequest(OKXAccount.baseUrl, OKXContants.BALANCE, parameters, HttpMethod.GET, OKXAccount.isSimluate());
- }
-
- @Override
- public String positions(LinkedHashMap<String, Object> parameters) {
- return OKXAccount.requestHandler.sendSignedRequest(OKXAccount.baseUrl, OKXContants.POSITIONS, parameters, HttpMethod.GET, OKXAccount.isSimluate());
- }
-
- @Override
- public String positionsHistory(LinkedHashMap<String, Object> parameters) {
- return OKXAccount.requestHandler.sendSignedRequest(OKXAccount.baseUrl, OKXContants.POSITIONS_HISTORY, parameters, HttpMethod.GET, OKXAccount.isSimluate());
- }
-
- @Override
- public boolean cancelOrder(String originOrderId,String instId) {
- LinkedHashMap<String, Object> parameters = new LinkedHashMap<>();
- parameters.put("instId", instId);
- parameters.put("clOrdId", originOrderId);
-
- String s = OKXAccount.requestHandler.sendSignedRequest(OKXAccount.baseUrl, OKXContants.CANCEL_ORDER, parameters, HttpMethod.POST, OKXAccount.isSimluate());
- log.info("[{}] 收到撤单请求,返回", s);
- return true;
- }
-
- @Override
- public String submitOrder(SubmitOrderReqDto submitOrderReq) {
- log.info("收到下单请求,参数:[{}]", submitOrderReq);
- LinkedHashMap<String, Object> parameters = new LinkedHashMap<>();
-
- String side = submitOrderReq.getSide();
- String type = submitOrderReq.getOrdType();
- String positionSides = submitOrderReq.getPosSide();
- // 开仓
- //开多:买多BUY、LONG
- //开空:卖空SELL、SHORT
- if ("buy".equals(side)) {
- if ("limit".equals(type)) {
- parameters.put("px", submitOrderReq.getPx());
- parameters.put("ordType", "limit");
- }
- // 持仓方向
- parameters.put("posSide", positionSides);
- //placeOrderReq.setPosSide(positionSide);
- // slTriggerPx 止损触发价,如果填写此参数,必须填写 止损委托价
- // slOrdPx 止损委托价,如果填写此参数,必须填写 止损触发价
-
- if (new BigDecimal(submitOrderReq.getSlTriggerPx()).compareTo(BigDecimal.ZERO) > 0) {
- Map<String, Object> attachAlgoOrder = new HashMap<>();
- // 如果是开多 止损价小于传来的价格
-
- List<Map<String, Object>> attachAlgoOrds = new ArrayList<>();
- // 如果是开空 止损价小于传来的价格
- attachAlgoOrder.put("slTriggerPx", submitOrderReq.getSlTriggerPx());
- attachAlgoOrder.put("slOrdPx", submitOrderReq.getSlOrdPx());
- attachAlgoOrds.add(attachAlgoOrder);
- parameters.put("attachAlgoOrds", attachAlgoOrds);
- } else {
- BigDecimal price = new BigDecimal(submitOrderReq.getPrice());
- BigDecimal stopPrice = BigDecimal.ZERO;
- if ("buy".equalsIgnoreCase(side)) {
- stopPrice = price.multiply(new BigDecimal("0.99")).setScale(2,BigDecimal.ROUND_DOWN);
- } else {
- stopPrice = price.multiply(new BigDecimal("1.01")).setScale(2,BigDecimal.ROUND_DOWN);
- }
- Map<String, Object> attachAlgoOrder = new HashMap<>();
- // 如果是开多 止损价小于传来的价格
- List<Map<String, Object>> attachAlgoOrds = new ArrayList<>();
- // 如果是开空 止损价小于传来的价格
- attachAlgoOrder.put("slTriggerPx", stopPrice);
- attachAlgoOrder.put("slOrdPx", stopPrice);
- attachAlgoOrds.add(attachAlgoOrder);
- parameters.put("attachAlgoOrds", attachAlgoOrds);
- }
- } else {
- // 平仓
- //平空:卖空BUY、SHORT
- //平多:卖多SELL、LONG
- //side = (CoreEnum.DirectionEnum.D_Buy.getNumber() == direction.getNumber()) ? "BUY" : "SELL";
- // 平仓方向
- //positionSide = (CoreEnum.DirectionEnum.D_Buy.getNumber() == direction.getNumber()) ? "SHORT" : "LONG";
- if ("limit".equals(type)) {
- //placeOrderReq.setPx(new BigDecimal(submitOrderReq.price()));
- parameters.put("px", submitOrderReq.getPrice());
- //parameters.put("timeInForce", timeInForce);
- }
- //placeOrderReq.setPosSide(positionSide);
- parameters.put("posSide", positionSides);
- }
-
- //订单种类,市价单不传价格
- parameters.put("instId", submitOrderReq.getInstId());
- parameters.put("side", side);
- //placeOrderReq.setSide(side);
- parameters.put("ordType", type);
- //placeOrderReq.setSz(new BigDecimal(quantity));
- parameters.put("sz", submitOrderReq.getSz());
- //placeOrderReq.setClOrdId(submitOrderReq.originOrderId());
- parameters.put("clOrdId", submitOrderReq.getClOrdId());
- parameters.put("tdMode", submitOrderReq.getTdMode());
- log.info("下单参数:[{}]",JSON.toJSONString(parameters));
- String placeOrderRspOkxRestResponse = OKXAccount.requestHandler.sendSignedRequest(OKXAccount.baseUrl, OKXContants.ORDER, parameters, HttpMethod.POST, OKXAccount.isSimluate());
- log.info("收到下单返回,响应:[{}]", JSON.parseObject(placeOrderRspOkxRestResponse).get("data"));
- return submitOrderReq.getClOrdId();
- }
-
- @Override
- public String tickerMess(LinkedHashMap<String, Object> parameters) {
- return OKXAccount.requestHandler.sendSignedRequest(OKXAccount.baseUrl, OKXContants.TICKER, parameters, HttpMethod.GET, OKXAccount.isSimluate());
- }
-
- @Override
- public String getOrderMessage(LinkedHashMap<String, Object> parameters) {
- return OKXAccount.requestHandler.sendSignedRequest(OKXAccount.baseUrl, OKXContants.TICKER, parameters, HttpMethod.GET, OKXAccount.isSimluate());
- }
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/DateUtils.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/DateUtils.java
deleted file mode 100644
index 4d5199e..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/DateUtils.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils;
-
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.time.ZoneId;
-import java.util.Date;
-import java.util.TimeZone;
-
-public class DateUtils {
-
- public static final String FORMAT_Y = "yyyy";
- public static final String FORMAT_D_1 = "yyyy/MM/dd";
- public static final String FORMAT_D_2 = "yyyy-MM-dd";
- public static final String FORMAT_D_3 = "yyyyMMdd";
- public static final String FORMAT_D_4 = "yyyy.MM.dd";
- public static final String FORMAT_D = "dd";
- public static final String FORMAT_DT_1 = "yyyy/MM/dd HH:mm:ss";
- public static final String FORMAT_DT_2 = "yyyy-MM-dd HH:mm:ss";
- public static final String FORMAT_DT_3 = "yyyyMMdd HH:mm:ss";
- public static final String FORMAT_DT_4 = "yyyy-MM-dd HH:mm";
- public static final String FORMAT_DT_5 = "yyyy.MM.dd HH:mm:ss";
- public static final String FORMAT_DT_6 = "yyyyMMddHHmmss";
- public static final String FORMAT_DT_7 = "yyyyMMddHH";
- public static final String FORMAT_M_1 = "yyyy/MM";
- public static final String FORMAT_M_2 = "yyyy-MM";
- public static final String FORMAT_M_3 = "yyyyMM";
- public static final String FORMAT_M = "MM";
- public static final String FORMAT_MD_1 = "MM/dd";
- public static final String FORMAT_MD_2 = "MM-dd";
- public static final String FORMAT_MD_3 = "MMdd";
- public static final String FORMAT_T_1 = "HH:mm:ss";
- public static final String FORMAT_T_2 = "HH:mm";
- public static final String FORMAT_TH = "HH";
- public static final String FORMAT_TM = "mm";
- public static final String FORMAT_TS = "ss";
- public static final String FORMAT_UTC_ISO8601 = "yyyy-MM-dd'T'HH:mm:ss'Z'";
-
- public static String format(String format, Date date) {
- SimpleDateFormat sdf = new SimpleDateFormat(format);
- return sdf.format(date);
- }
-
- /**
- * @param format format
- * @param date date
- * @param timeZone 时区数字 -8, 0, 8 等
- * @return date string
- */
- public static String format(String format, Date date, int timeZone) {
- timeZone = timeZone % 13;
- SimpleDateFormat sdf = new SimpleDateFormat(format);
- ZoneId zoneId = ZoneId.of("GMT" + (timeZone >= 0 ? "+" : "") + timeZone);
- TimeZone tz = TimeZone.getTimeZone(zoneId);
- sdf.setTimeZone(tz);
- return sdf.format(date);
- }
-
- public static Date parse(String dateString, String format) {
- SimpleDateFormat sdf = new SimpleDateFormat(format);
-
- try {
- return sdf.parse(dateString);
- } catch (ParseException var4) {
- return null;
- }
- }
-
- public static Date parse(String dateString, String format, int timeZone) {
- SimpleDateFormat sdf = new SimpleDateFormat(format);
- ZoneId zoneId = ZoneId.of("GMT" + (timeZone >= 0 ? "+" : "") + timeZone);
- TimeZone tz = TimeZone.getTimeZone(zoneId);
- sdf.setTimeZone(tz);
- try {
- return sdf.parse(dateString);
- } catch (ParseException var4) {
- return null;
- }
- }
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/JSONParser.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/JSONParser.java
deleted file mode 100644
index cf41cbc..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/JSONParser.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.ArrayList;
-
-public final class JSONParser {
-
- private JSONParser() {
- }
-
- public static String getJSONStringValue(String json, String key) {
- try {
- JSONObject obj = new JSONObject(json);
- return obj.getString(key);
- } catch (JSONException e) {
- throw new JSONException(String.format("[JSONParser] Failed to get \"%s\" from JSON object", key));
- }
- }
-
- public static int getJSONIntValue(String json, String key) {
- try {
- JSONObject obj = new JSONObject(json);
- return obj.getInt(key);
- } catch (JSONException e) {
- throw new JSONException(String.format("[JSONParser] Failed to get \"%s\" from JSON object", key));
- }
- }
-
- public static String getJSONArray(ArrayList<?> symbols, String key) {
- try {
- JSONArray arr = new JSONArray(symbols);
- return arr.toString();
- } catch (JSONException e) {
- throw new JSONException(String.format("[JSONParser] Failed to convert \"%s\" to JSON array", key));
- }
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/OKXContants.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/OKXContants.java
deleted file mode 100644
index ac69758..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/OKXContants.java
+++ /dev/null
@@ -1,224 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils;
-
-public class OKXContants {
- /**
- * 获取交易产品基础信息
- * 获取所有可交易产品的信息列表。
- * <br><br>
- * GET /api/v5/public/instruments
- * <br>
- *
- * @param parameters LinkedHashedMap of String,Object pair
- * where String is the name of the parameter and Object is the value of the parameter
- * <br><br>
- * instType -- String 是 产品类型 SPOT:币币 MARGIN:币币杠杆 SWAP:永续合约 FUTURES:交割合约 OPTION:期权 <br>
- * uly -- String 可选 标的指数,仅适用于交割/永续/期权,期权必填 <br>
- * instFamily -- String 否 交易品种,仅适用于交割/永续/期权 <br>
- * instId -- String 否 产品ID <br>
- * @return String
- * @see <a href="https://www.okx.com/docs-v5/zh/#rest-api-public-data-get-instruments">
- * https://www.okx.com/docs-v5/zh/#rest-api-public-data-get-instruments</a>
- */
- public static final String INSTRUMENTS = "/api/v5/public/instruments";
- public static final String K_LINE_HISTORY_MARK_PRICE = "/api/v5/market/history-mark-price-candles";
- public static final String K_LINE_HISTORY = "/api/v5/market/history-candles";
- /**
- * 查看账户余额
- * 获取交易账户中资金余额信息。
- * <br><br>
- * GET /api/v5/account/balance
- * <br>
- * @param
- * parameters LinkedHashedMap of String,Object pair
- * where String is the name of the parameter and Object is the value of the parameter
- * <br><br>
- * ccy -- String 否 币种,如 BTC 支持多币种查询(不超过20个),币种之间半角逗号分隔 <br>
- * @return String
- * @see <a href="https://www.okx.com/docs-v5/zh/#rest-api-account-get-balance">
- * https://www.okx.com/docs-v5/zh/#rest-api-account-get-balance</a>
- */
- public static final String BALANCE = "/api/v5/account/balance";
- /**
- * /api/v5/asset/balances
- * 获取资金账户余额
- * 获取资金账户所有资产列表,查询各币种的余额、冻结和可用等信息。
- * 币种,如 BTC
- * 支持多币种查询(不超过20个),币种之间半角逗号分隔
- */
- public static final String ACCOUNT_BALANCE = "/api/v5/asset/balances";
- /**
- * 查看持仓信息
- * 获取该账户下拥有实际持仓的信息。账户为单向持仓模式会显示净持仓(net),账户为双向持仓模式下会分别返回多头(long)或空头(short)的仓位。按照仓位创建时间倒序排列。
- * <br><br>
- * GET /api/v5/account/positions
- * <br>
- * @param
- * parameters LinkedHashedMap of String,Object pair
- * where String is the name of the parameter and Object is the value of the parameter
- * <br><br>
- * instType -- String 否 产品类型
- * MARGIN:币币杠杆
- * SWAP:永续合约
- * FUTURES:交割合约
- * OPTION:期权
- * instType和instId同时传入的时候会校验instId与instType是否一致。<br>
- * instId -- String 否 交易产品ID,如:BTC-USD-190927-5000-C
- * 支持多个instId查询(不超过10个),半角逗号分隔<br>
- * posId -- String 否 持仓ID
- * 支持多个posId查询(不超过20个),半角逗号分割<br>
- * @return String <br>
- * note: 如果该 instId 拥有过仓位且当前持仓量为0,传 instId 时,会返回仓位信息;不传 instId 时,仓位信息不返回。
- * 逐仓交易设置中,如果设置为自主划转模式,逐仓转入保证金后,会生成一个持仓量为0的仓位 <br>
- * @see <a href="https://www.okx.com/docs-v5/zh/#rest-api-account-get-positions">
- * https://www.okx.com/docs-v5/zh/#rest-api-account-get-positions</a>
- */
- public static final String POSITIONS = "/api/v5/account/positions";
- /**
- * 查看历史持仓信息
- * 获取最近3个月有更新的仓位信息,按照仓位更新时间倒序排列。
- * <br><br>
- * GET /api/v5/account/positions-history
- * <br>
- * @param
- * parameters LinkedHashedMap of String,Object pair
- * where String is the name of the parameter and Object is the value of the parameter
- * <br><br>
- * instType -- String 否 产品类型
- * MARGIN:币币杠杆
- * SWAP:永续合约
- * FUTURES:交割合约
- * OPTION:期权 <br>
- * instId -- String 否 交易产品ID,如:BTC-USD-SWAP <br>
- * mgnMode -- String 否 保证金模式
- * cross:全仓,isolated:逐仓
- * type -- String 否 平仓类型
- * 1:部分平仓;2:完全平仓;3:强平;4:强减; 5:ADL自动减仓;
- * 状态叠加时,以最新的平仓类型为准状态为准。 <br>
- * posId -- String 否 持仓ID <br>
- * after -- String 否 查询仓位更新 (uTime) 之前的内容,值为时间戳,Unix 时间戳为毫秒数格式,如 1597026383085 <br>
- * before -- String 否 查询仓位更新 (uTime) 之后的内容,值为时间戳,Unix 时间戳为毫秒数格式,如 1597026383085 <br>
- * limit -- String 否 分页返回结果的数量,最大为100,默认100条 <br>
- * @return String
- * @see <a href="https://www.okx.com/docs-v5/zh/#rest-api-account-get-positions-history">
- * https://www.okx.com/docs-v5/zh/#rest-api-account-get-positions-history</a>
- */
- public static final String POSITIONS_HISTORY = "/api/v5/account/positions-history";
- /**
- * 撤单
- * 撤销之前下的未完成订单。
- *
- * <br><br>
- * GET /api/v5/trade/cancel-order
- * <br>
- *
- * @param parameters LinkedHashedMap of String,Object pair
- * where String is the name of the parameter and Object is the value of the parameter
- * <br><br>
- * instId -- String 是 产品ID,如 BTC-USD-190927 <br>
- * ordId -- String 可选 订单ID, ordId和clOrdId必须传一个,若传两个,以ordId为主 <br>
- * clOrdId -- String 可选 用户自定义ID <br>
- * @return String
- * @see <a href="https://www.okx.com/docs-v5/zh/#rest-api-trade-cancel-order">
- * https://www.okx.com/docs-v5/zh/#rest-api-trade-cancel-order</a>
- */
- public static final String CANCEL_ORDER = "/api/v5/trade/cancel-order";
- /**
- * 下单
- * 只有当您的账户有足够的资金才能下单。
- *
- * <br><br>
- * GET /api/v5/trade/order
- * <br>
- *
- * @param parameters LinkedHashedMap of String,Object pair
- * where String is the name of the parameter and Object is the value of the parameter
- * <br><br>
- * instId -- String 是 产品ID,如 BTC-USD-190927-5000-C <br>
- * tdMode -- String 是 交易模式
- * 保证金模式:isolated:逐仓 ;cross:全仓
- * 非保证金模式:cash:非保证金 <br>
- * ccy -- String 否 保证金币种,仅适用于单币种保证金模式下的全仓杠杆订单 <br>
- * clOrdId -- String 否 客户自定义订单ID
- * 字母(区分大小写)与数字的组合,可以是纯字母、纯数字且长度要在1-32位之间。<br>
- * tag -- String 否 订单标签
- * 字母(区分大小写)与数字的组合,可以是纯字母、纯数字,且长度在1-16位之间。 <br>
- * side -- String 是 订单方向
- * buy:买, sell:卖 <br>
- * posSide -- String 可选 持仓方向
- * 在双向持仓模式下必填,且仅可选择 long 或 short。 仅适用交割、永续。 <br>
- * ordType -- String 是 订单类型
- * market:市价单
- * limit:限价单
- * post_only:只做maker单
- * fok:全部成交或立即取消
- * ioc:立即成交并取消剩余
- * optimal_limit_ioc:市价委托立即成交并取消剩余(仅适用交割、永续) <br>
- * sz -- String 是 委托数量 <br>
- * px -- String 可选 委托价格,仅适用于limit、post_only、fok、ioc类型的订单 <br>
- * reduceOnly -- Boolean 否 是否只减仓,true 或 false,默认false
- * 仅适用于币币杠杆,以及买卖模式下的交割/永续
- * 仅适用于单币种保证金模式和跨币种保证金模式 <br>
- * tgtCcy -- String 否 市价单委托数量sz的单位,仅适用于币币市价订单
- * base_ccy: 交易货币 ;quote_ccy:计价货币
- * 买单默认quote_ccy, 卖单默认base_ccy <br>
- * banAmend -- Boolean 否 是否禁止币币市价改单,true 或 false,默认false
- * 为true时,余额不足时,系统不会改单,下单会失败,仅适用于币币市价单 <br>
- * tpTriggerPx -- String 否 止盈触发价,如果填写此参数,必须填写 止盈委托价 <br>
- * tpOrdPx -- String 否 止盈委托价,如果填写此参数,必须填写 止盈触发价
- * 委托价格为-1时,执行市价止盈 <br>
- * slTriggerPx -- String 否 止损触发价,如果填写此参数,必须填写 止损委托价 <br>
- * slOrdPx -- String 否 止损委托价,如果填写此参数,必须填写 止损触发价
- * 委托价格为-1时,执行市价止损 <br>
- * tpTriggerPxType -- String 否 止盈触发价类型
- * last:最新价格
- * index:指数价格
- * mark:标记价格
- * 默认为last <br>
- * slTriggerPxType -- String 否 止损触发价类型
- * last:最新价格
- * index:指数价格
- * mark:标记价格
- * 默认为last <br>
- * @return String
- * @see <a href="https://www.okx.com/docs-v5/zh/#rest-api-trade-place-order">
- * https://www.okx.com/docs-v5/zh/#rest-api-trade-place-order</a>
- */
- public static final String ORDER = "/api/v5/trade/order";
-
- /**
- * 获取币种价格信息
- */
- public static final String TICKER = "/api/v5/market/ticker";
-
- /**
- * 获取杠杆倍数
- */
- public static final String LEVERAGE = "/api/v5/account/leverage-info";
-
- /**
- * 设置杠杆倍数
- */
- public static final String SETLEVERAGE = "/api/v5/account/set-leverage";
-
-
- /**
- * 获取支持大数据的币种列表
- */
- public static final String TRADEDATA = "/api/v5/rubik/stat/trading-data/support-coin";
-
- /**
- * 获取合约主动买入/卖出情况
- */
- public static final String BUYSELLSITUATION = "/api/v5/rubik/stat/taker-volume-contract";
-
- /**
- * 获取合约多空持仓人数比
- */
- public static final String POSITIONRATIO = "/api/v5/rubik/stat/contracts/long-short-account-ratio-contract";
-
- /**
- * 获取合约持仓量及交易量
- */
- public static final String POSITIONVOLUME = "/api/v5/rubik/stat/contracts/open-interest-volume";
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/OkHttpUtils.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/OkHttpUtils.java
deleted file mode 100644
index 1c349bb..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/OkHttpUtils.java
+++ /dev/null
@@ -1,330 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils;
-
-import lombok.extern.slf4j.Slf4j;
-import okhttp3.*;
-import org.json.JSONObject;
-
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocketFactory;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.X509TrustManager;
-import java.io.IOException;
-import java.net.URLEncoder;
-import java.security.SecureRandom;
-import java.security.cert.X509Certificate;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-/**
- * OkHttp请求工具封装
- * 参考:https://blog.csdn.net/DwZ735660836/article/details/119976068
- */
-@Slf4j
-public class OkHttpUtils {
- public static volatile OkHttpClient okHttpClient = null;
- private static volatile Semaphore semaphore = null;
- private Map<String, String> headerMap;
- private Map<String, String> paramMap;
- private String url;
- private Request.Builder request;
- // 开发环境用的 ShadowsocksR-dotnet4.0 免费版本 正式环境得使用外网服务器
- // 安易代理 http://127.0.0.1:10809/ http://127.0.0.1:10808/
-
- /**
- * 初始化okHttpClient,并且允许https访问
- */
- private OkHttpUtils() {
- if (okHttpClient == null) {
- synchronized (OkHttpUtils.class) {
- if (okHttpClient == null) {
- TrustManager[] trustManagers = buildTrustManagers();
- okHttpClient = new OkHttpClient.Builder()
- .connectTimeout(30, TimeUnit.SECONDS)
- .writeTimeout(20, TimeUnit.SECONDS)
- .readTimeout(60, TimeUnit.SECONDS)
- .sslSocketFactory(createSSLSocketFactory(trustManagers), (X509TrustManager) trustManagers[0])
- //.hostnameVerifier((hostName, session) -> true)
- //配置自定义连接池参数
- .connectionPool(new ConnectionPool(5, 60, TimeUnit.SECONDS))
- .retryOnConnectionFailure(true)
- .build();
- addHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36");
- addHeader("Connection", "close");
- addHeader("Accept-Encoding", "identity");
- }
- }
- }
- }
-
- /**
- * 用于异步请求时,控制访问线程数,返回结果
- *
- * @return
- */
- private static Semaphore getSemaphoreInstance() {
- //只能1个线程同时访问
- synchronized (OkHttpUtils.class) {
- if (semaphore == null) {
- semaphore = new Semaphore(0);
- }
- }
- return semaphore;
- }
-
- /**
- * 创建OkHttpUtils
- *
- * @return
- */
- public static OkHttpUtils builder() {
- return new OkHttpUtils();
- }
-
- /**
- * 添加url
- *
- * @param url
- * @return
- */
- public OkHttpUtils url(String url) {
- this.url = url;
- return this;
- }
-
- /**
- * 添加参数
- *
- * @param key 参数名
- * @param value 参数值
- * @return
- */
- public OkHttpUtils addParam(String key, String value) {
- if (paramMap == null) {
- paramMap = new LinkedHashMap<>(16);
- }
- paramMap.put(key, value);
- return this;
- }
-
- /**
- * 添加请求头
- *
- * @param key 参数名
- * @param value 参数值
- * @return
- */
- public OkHttpUtils addHeader(String key, String value) {
- if (headerMap == null) {
- headerMap = new LinkedHashMap<>(16);
- }
- headerMap.put(key, value);
- return this;
- }
-
- /**
- * 初始化get方法
- *
- * @return
- */
- public OkHttpUtils get() {
- request = new Request.Builder().get();
- StringBuilder urlBuilder = new StringBuilder(url);
- if (paramMap != null) {
- urlBuilder.append("?");
- try {
- for (Map.Entry<String, String> entry : paramMap.entrySet()) {
- urlBuilder.append(URLEncoder.encode(entry.getKey(), "utf-8")).
- append("=").
- append(URLEncoder.encode(entry.getValue(), "utf-8")).
- append("&");
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- urlBuilder.deleteCharAt(urlBuilder.length() - 1);
- }
- request.url(urlBuilder.toString());
- return this;
- }
-
- /**
- * 初始化post方法
- *
- * @param isJsonPost true等于json的方式提交数据,类似postman里post方法的raw
- * false等于普通的表单提交
- * @return
- */
- public OkHttpUtils post(boolean isJsonPost) {
- RequestBody requestBody;
- if (isJsonPost) {
- String json = "";
- if (paramMap != null) {
- json = JSONObject.valueToString(paramMap);
- }
- requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json);
- } else {
- FormBody.Builder formBody = new FormBody.Builder();
- if (paramMap != null) {
- paramMap.forEach(formBody::add);
- }
- requestBody = formBody.build();
- }
- request = new Request.Builder().post(requestBody).url(url);
- return this;
- }
-
- /**
- * 同步请求
- *
- * @return
- */
- public Request.Builder sync() {
- return setHeader(request);
- }
-
- /**
- * 同步请求
- *
- * @return
- */
- public String syncStr() {
- setHeader(request);
- try {
- Response response = okHttpClient.newCall(request.build()).execute();
- assert response.body() != null;
- return response.body().string();
- } catch (IOException e) {
- e.printStackTrace();
- return "请求失败:" + e.getMessage();
- }
- }
-
-
- /**
- * 异步请求,有返回值
- */
- public String async() {
- StringBuilder buffer = new StringBuilder("");
- setHeader(request);
- okHttpClient.newCall(request.build()).enqueue(new Callback() {
- @Override
- public void onFailure(Call call, IOException e) {
- buffer.append("请求出错:").append(e.getMessage());
- }
-
- @Override
- public void onResponse(Call call, Response response) throws IOException {
- assert response.body() != null;
- buffer.append(response.body().string());
- getSemaphoreInstance().release();
- }
- });
- try {
- getSemaphoreInstance().acquire();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return buffer.toString();
- }
-
- /**
- * 异步请求,带有接口回调
- *
- * @param callBack
- */
- public void async(ICallBack callBack) {
- setHeader(request);
- okHttpClient.newCall(request.build()).enqueue(new Callback() {
- @Override
- public void onFailure(Call call, IOException e) {
- callBack.onFailure(call, e.getMessage());
- }
-
- @Override
- public void onResponse(Call call, Response response) throws IOException {
- assert response.body() != null;
- callBack.onSuccessful(call, response.body().string());
- }
- });
- }
-
- /**
- * 为request添加请求头
- *
- * @param request
- */
- private Request.Builder setHeader(Request.Builder request) {
- if (headerMap != null) {
- try {
- for (Map.Entry<String, String> entry : headerMap.entrySet()) {
- request.addHeader(entry.getKey(), entry.getValue());
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- return request;
- }
-
-
- /**
- * 生成安全套接字工厂,用于https请求的证书跳过
- *
- * @return
- */
- private static SSLSocketFactory createSSLSocketFactory(TrustManager[] trustAllCerts) {
- SSLSocketFactory ssfFactory = null;
- try {
- SSLContext sc = SSLContext.getInstance("SSL");
- sc.init(null, trustAllCerts, new SecureRandom());
- ssfFactory = sc.getSocketFactory();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return ssfFactory;
- }
-
- private static TrustManager[] buildTrustManagers() {
- return new TrustManager[]{
- new X509TrustManager() {
- @Override
- public void checkClientTrusted(X509Certificate[] chain, String authType) {
- }
-
- @Override
- public void checkServerTrusted(X509Certificate[] chain, String authType) {
- }
-
- @Override
- public X509Certificate[] getAcceptedIssuers() {
- return new X509Certificate[]{};
- }
- }
- };
- }
-
- /**
- * 自定义一个接口回调
- */
- public interface ICallBack {
-
- void onSuccessful(Call call, String data);
-
- void onFailure(Call call, String errorMsg);
-
- }
-
- public static void main(String[] args) {
- String url = "https://api2.binance.com/api/v3/ticker/24hr?symbol=BNBUSDT&type=MINI";
- String result = OkHttpUtils.builder()
- .url(url)
- .addHeader("Content-Type", "application/x-www-form-urlencoded")
- .get()
- .syncStr();
- System.out.println(result);
- }
-}
-
-
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/ParameterChecker.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/ParameterChecker.java
deleted file mode 100644
index 1eec770..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/ParameterChecker.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils;
-
-import com.xcong.excoin.modules.okxNewPrice.utils.FebsException;
-
-import java.util.LinkedHashMap;
-
-public final class ParameterChecker {
-
- private ParameterChecker() {
- }
-
- public static void checkParameter(LinkedHashMap<String, Object> parameters, String parameter, Class t) {
- checkRequiredParameter(parameters, parameter);
- checkParameterType(parameters.get(parameter), t, parameter);
- }
-
- public static void checkOrParameters(LinkedHashMap<String, Object> parameters, String parameter, String parameter2) {
- if (!parameters.containsKey(parameter) && (!parameters.containsKey(parameter2))) {
- throw new FebsException(String.format("Either \"%s\" or \"%s\" is required!", parameter, parameter2));
- }
- }
-
- public static void checkRequiredParameter(LinkedHashMap<String, Object> parameters, String parameter) {
- if (!parameters.containsKey(parameter)) {
- throw new FebsException(String.format("\"%s\" is a mandatory parameter!", parameter));
- }
- }
-
- public static void checkParameterType(Object parameter, Class t, String name) {
- if (!t.isInstance(parameter)) {
- throw new FebsException(String.format("\"%s\" must be of %s type.", name, t));
- } else if (t == String.class && parameter.toString().trim().equals("")) {
- throw new FebsException(String.format("\"%s\" must not be empty.", name));
- }
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/RequestBuilder.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/RequestBuilder.java
deleted file mode 100644
index e887de5..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/RequestBuilder.java
+++ /dev/null
@@ -1,150 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils;
-
-
-import com.xcong.excoin.modules.okxNewPrice.utils.FebsException;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.enums.HttpMethod;
-import okhttp3.MediaType;
-import okhttp3.Request;
-import okhttp3.RequestBody;
-
-public final class RequestBuilder {
- private static final MediaType JSON_TYPE = MediaType.parse("application/json; charset=utf-8");
-
- private RequestBuilder() {
- }
-
- public static Request.Builder buildPublicRequest(String fullUrl, HttpMethod httpMethod, boolean issimulated) {
- try {
- final Request.Builder result;
- switch (httpMethod) {
- case POST:
- OkHttpUtils builder = OkHttpUtils.builder();
- if(issimulated){
- builder.addHeader("x-simulated-trading","1");
- }
- result = builder
- .url(fullUrl)
- .post(true)
- .sync();
- break;
- case GET:
- OkHttpUtils builder1 = OkHttpUtils.builder();
- if(issimulated){
- builder1.addHeader("x-simulated-trading","1");
- }
- result = builder1
- .url(fullUrl)
- .addHeader("Content-Type", "application/x-www-form-urlencoded")
- .get()
- .sync();
- break;
- case PUT:
- OkHttpUtils builder2 = OkHttpUtils.builder();
- if(issimulated){
- builder2.addHeader("x-simulated-trading","1");
- }
- result = builder2
- .url(fullUrl)
- .addHeader("Content-Type", "application/x-www-form-urlencoded")
- .post(false)
- .sync();
- break;
- case DELETE:
- OkHttpUtils builder3 = OkHttpUtils.builder();
- if(issimulated){
- builder3.addHeader("x-simulated-trading","1");
- }
- result = builder3
- .url(fullUrl)
- .post(false)
- .sync();
- break;
- default:
- throw new FebsException("Invalid HTTP method: " + httpMethod);
- }
- return result;
- } catch (IllegalArgumentException e) {
- throw new FebsException("Invalid URL: " + e.getMessage());
- }
- }
-
- public static Request buildApiKeyRequest(String fullUrl,String body,String passphrase,String sign,String timeStamp, HttpMethod httpMethod, String apiKey,boolean issimulate) {
- try {
- final Request request;
- switch (httpMethod) {
- case POST:
- Request.Builder builder = new Request.Builder();
- if(issimulate){
- builder.addHeader("x-simulated-trading","1");
- }
- request = builder
- .url(fullUrl)
- .post(RequestBody.create(JSON_TYPE, body))
- .addHeader("OK-ACCESS-KEY", apiKey)
- .addHeader("OK-ACCESS-SIGN", sign)
- .addHeader("OK-ACCESS-TIMESTAMP", timeStamp)
- .addHeader("OK-ACCESS-PASSPHRASE", passphrase)
- .build();
- break;
- case GET:
- Request.Builder builder1 = new Request.Builder();
- if(issimulate){
- builder1.addHeader("x-simulated-trading","1");
- }
- request = builder1
- .url(fullUrl)
- .get()
- .addHeader("Content-Type", "application/x-www-form-urlencoded")
- .addHeader("OK-ACCESS-KEY", apiKey)
- .addHeader("OK-ACCESS-KEY", apiKey)
- .addHeader("OK-ACCESS-SIGN", sign)
- .addHeader("OK-ACCESS-TIMESTAMP", timeStamp)
- .addHeader("OK-ACCESS-PASSPHRASE", passphrase)
- .build();
- break;
- case PUT:
- Request.Builder builder2 = new Request.Builder();
- if(issimulate){
- builder2.addHeader("x-simulated-trading","1");
- }
- request = builder2
- .url(fullUrl)
- .put(RequestBody.create(JSON_TYPE, ""))
- .addHeader("Content-Type", "application/x-www-form-urlencoded")
- .addHeader("OK-ACCESS-KEY", apiKey)
- .addHeader("OK-ACCESS-KEY", apiKey)
- .addHeader("OK-ACCESS-SIGN", sign)
- .addHeader("OK-ACCESS-TIMESTAMP", timeStamp)
- .addHeader("OK-ACCESS-PASSPHRASE", passphrase)
- .build();
- break;
- case DELETE:
- Request.Builder builder3 = new Request.Builder();
- if(issimulate){
- builder3.addHeader("x-simulated-trading","1");
- }
- request = builder3
- .url(fullUrl)
- .delete()
- .addHeader("Content-Type", "application/x-www-form-urlencoded")
- .addHeader("OK-ACCESS-KEY", apiKey)
- .addHeader("OK-ACCESS-KEY", apiKey)
- .addHeader("OK-ACCESS-SIGN", sign)
- .addHeader("OK-ACCESS-TIMESTAMP", timeStamp)
- .addHeader("OK-ACCESS-PASSPHRASE", passphrase)
- .build();
- break;
- default:
- throw new FebsException("Invalid HTTP method: " + httpMethod);
- }
- return request;
- } catch (IllegalArgumentException e) {
- throw new FebsException("Invalid URL: " + e.getMessage());
- }
- }
-
- public static Request buildWebsocketRequest(String fullUrl) {
- return new Request.Builder().url(fullUrl).build();
- }
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/SignUtils.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/SignUtils.java
deleted file mode 100644
index fdd5a45..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/SignUtils.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils;
-
-import lombok.extern.slf4j.Slf4j;
-
-import javax.crypto.Mac;
-import javax.crypto.spec.SecretKeySpec;
-import java.util.Base64;
-
-
-@Slf4j
-public class SignUtils {
-
- public static String signRest(String secretKey, String timestamp, String method, String path, String body) {
- String str = String.format("%s%s%s%s",
- timestamp, // timestamp
- method, // method GET/POST
- path, // requestPath
- body // body
- );
- try {
- return Base64.getEncoder().encodeToString(hmacSHA256(secretKey.getBytes(), str.getBytes()));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
-
-
- /**
- * HmacSHA256算法,返回的结果始终是32位
- *
- * @param key 加密的键,可以是任何数据
- * @param content 待加密的内容
- * @return 加密后的内容
- * @throws Exception ex
- */
- public static byte[] hmacSHA256(byte[] key, byte[] content) throws Exception {
- Mac hmacSha256 = Mac.getInstance("HmacSHA256");
- hmacSha256.init(new SecretKeySpec(key, 0, key.length, "HmacSHA256"));
- return hmacSha256.doFinal(content);
- }
-
- public static String signWebsocket(String timestamp, String secretKey) {
- String str = String.format("%s%s%s",
- timestamp,
- "GET",
- "/users/self/verify");
- try {
- return Base64.getEncoder().encodeToString(hmacSHA256(secretKey.getBytes(), str.getBytes()));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/UrlBuilder.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/UrlBuilder.java
deleted file mode 100644
index c26cd8d..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/utils/UrlBuilder.java
+++ /dev/null
@@ -1,128 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils;
-
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
-import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-
-public final class UrlBuilder {
- private static final int MAX_DECIMAL_DIGITS = 30;
- private static DecimalFormat df;
-
-
- private UrlBuilder() {
- }
-
- public static String buildFullUrl(String baseUrl, String urlPath, LinkedHashMap<String, Object> parameters, String signature) {
- if (parameters != null && !parameters.isEmpty()) {
- StringBuilder sb = new StringBuilder(baseUrl);
- sb.append(urlPath).append('?');
- joinQueryParameters(sb, parameters);
- if (null != signature) {
- sb.append("&signature=").append(signature);
- }
- return sb.toString();
- } else {
- return baseUrl + urlPath;
- }
- }
-
- public static String buildStreamUrl(String baseUrl, ArrayList<String> streams) {
- StringBuilder sb = new StringBuilder(baseUrl);
- sb.append("?streams=");
- return joinStreamUrls(sb, streams);
- }
-
- //concatenate query parameters
- public static String joinQueryParameters(LinkedHashMap<String, Object> parameters) {
- return joinQueryParameters(new StringBuilder(), parameters).toString();
- }
-
- public static StringBuilder joinQueryParameters(StringBuilder urlPath, LinkedHashMap<String, Object> parameters) {
- if (parameters == null || parameters.isEmpty()) {
- return urlPath;
- }
-
- boolean isFirst = true;
- for (Map.Entry<String, Object> mapElement : parameters.entrySet()) {
-
- if (mapElement.getValue() instanceof Double) {
- parameters.replace(mapElement.getKey(), getFormatter().format(mapElement.getValue()));
- } else if (mapElement.getValue() instanceof ArrayList) {
- if (((ArrayList<?>) mapElement.getValue()).isEmpty()) {
- continue;
- }
- String key = mapElement.getKey();
- joinArrayListParameters(key, urlPath, (ArrayList<?>) mapElement.getValue(), isFirst);
- isFirst = false;
- continue;
- }
-
- if (isFirst) {
- isFirst = false;
- } else {
- urlPath.append('&');
- }
-
- urlPath.append(mapElement.getKey())
- .append('=')
- .append(urlEncode(mapElement.getValue().toString()));
- }
- return urlPath;
- }
-
- private static void joinArrayListParameters(String key, StringBuilder urlPath, ArrayList<?> values, boolean isFirst) {
- for (Object value: values) {
- if (isFirst) {
- isFirst = false;
- } else {
- urlPath.append('&');
- }
-
- urlPath.append(key)
- .append('=')
- .append(urlEncode(value.toString()));
- }
- }
-
- private static String joinStreamUrls(StringBuilder urlPath, ArrayList<String> streams) {
- boolean isFirst = true;
- for (String stream: streams) {
- if (isFirst) {
- isFirst = false;
- } else {
- urlPath.append('/');
- }
- urlPath.append(stream);
- }
- return urlPath.toString();
- }
-
-
- public static String urlEncode(String s) {
- try {
- return URLEncoder.encode(s, StandardCharsets.UTF_8.name());
- } catch (UnsupportedEncodingException e) {
- // UTF-8 being unsuppored is unlikely
- // Replace with a unchecked exception to tidy up exception handling
- throw new RuntimeException(StandardCharsets.UTF_8.name() + " is unsupported", e);
- }
- }
-
- private static DecimalFormat getFormatter() {
- if (null == df) {
- df = new DecimalFormat();
- df.setMaximumFractionDigits(MAX_DECIMAL_DIGITS);
- df.setGroupingUsed(false);
- }
- return df;
- }
-
- public static String buildTimestamp() {
- return String.valueOf(System.currentTimeMillis());
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/vo/BalanceVo.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/vo/BalanceVo.java
deleted file mode 100644
index b81bebb..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/vo/BalanceVo.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config.vo;
-
-import lombok.Data;
-
-@Data
-public class BalanceVo {
- //币种 如BTC
- private String ccy;
- // 可用余额
- private String availBal;
- // 币种余额
- private String cashBal;
- // 可用保证金
- private String availEq;
- // 未实现盈亏总额
- private String unrealizedProfit;
- // 维持保证金
- private String maintMargin;
- //合约率
- private String mgnRatio;
- //占用保证金
- private String frozenBal;
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/vo/InstrumentsVo.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/vo/InstrumentsVo.java
deleted file mode 100644
index 9cd7358..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/vo/InstrumentsVo.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config.vo;
-
-import lombok.Data;
-
-@Data
-public class InstrumentsVo {
- /**
- * 产品id, 如 BTC-USDT
- */
- private String instId;
- /**
- * 产品状态
- * live:交易中
- * suspend:暂停中
- * preopen:预上线,如:交割和期权的新合约在 live 之前,会有 preopen 状态
- * test:测试中(测试产品,不可交易)
- */
- private String state;
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/vo/PositionsVo.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/vo/PositionsVo.java
deleted file mode 100644
index f331032..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/config/vo/PositionsVo.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.config.vo;
-
-import com.alibaba.fastjson.JSONObject;
-import lombok.Data;
-
-import java.util.List;
-import java.util.Map;
-
-@Data
-public class PositionsVo {
- private List<JSONObject> positionList;
- private Map<String, JSONObject> newPositionMap;
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/enumerates/TradeTypeEnum.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/enumerates/TradeTypeEnum.java
deleted file mode 100644
index 07fddf7..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/enumerates/TradeTypeEnum.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.enumerates;
-
-import lombok.Getter;
-
-@Getter
-public enum TradeTypeEnum {
- /**
- * 1 - 开仓 2 - 平仓
- */
- OPEN_ORDER(1,"open","开仓"),
- CLOSE_ORDER(2,"close","平仓"),
- /**
- * 交易模式
- * 保证金模式:isolated:逐仓 ;cross:全仓
- * 非保证金模式:cash:非保证金
- * spot_isolated:现货逐仓(仅适用于现货带单) ,现货带单时,tdMode 的值需要指定为spot_isolated
- */
- ISOLATED(1,"isolated","逐仓"),
- CROSS(2,"cross","全仓"),
- /**
- * 持仓方向
- * long:开平仓模式开多,pos为正
- * short:开平仓模式开空,pos为正
- * net:买卖模式(交割/永续/期权:pos为正代表开多,pos为负代表开空。币币杠杆时,pos均为正,posCcy为交易货币时,代表开多;posCcy为计价货币时,代表开空。)
- */
- LONG(1,"long","持仓方向-long"),
- SHORT(2,"short","持仓方向-short"),
- /**
- * 订单类型
- * market:市价单
- * limit:限价单
- * post_only:只做maker单
- * fok:全部成交或立即取消
- * ioc:立即成交并取消剩余
- * optimal_limit_ioc:市价委托立即成交并取消剩余(仅适用交割、永续)
- * mmp:做市商保护(仅适用于组合保证金账户模式下的期权订单)
- * mmp_and_post_only:做市商保护且只做maker单(仅适用于组合保证金账户模式下的期权订单)
- */
- MARKET(1,"market","市价单"),
- LIMIT(2,"limit","限价单"),
- /**
- * 订单方向
- * buy:买, sell:卖
- */
- BUY(1,"buy","买"),
- SELL(2,"sell","卖")
- ;
-
- private int code;
- private String value;
- private String description;
-
- TradeTypeEnum(int code, String value, String description) {
- this.code = code;
- this.value = value;
- this.description = description;
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/order/ITradeOrderService.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/order/ITradeOrderService.java
deleted file mode 100644
index f18ddb8..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/order/ITradeOrderService.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.order;
-
-import com.xcong.excoin.modules.okxNewPrice.utils.FebsResponse;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.Dto.QuantApiMessage;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.Dto.TradeOrderDto;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.trade.TradeRequestBuy;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.trade.TradeRequestSell;
-
-//订单交易接口 欧易调用的接口是 撮合交易/交易/ POST 下单
-public interface ITradeOrderService {
- FebsResponse QuantExchangeReturnVo(TradeOrderDto tradeOrderDto, QuantApiMessage quantApiMessage);
-
- /**
- * 消费买入消息
- * @param returnVo
- */
- void operationBuyMsg(TradeRequestBuy returnVo);
-
- /**
- * 消费卖出消息
- * @param returnVo
- */
- void operationSellMsg(TradeRequestSell returnVo);
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/order/TradeOrderFactory.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/order/TradeOrderFactory.java
deleted file mode 100644
index 9736802..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/order/TradeOrderFactory.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.order;
-
-import lombok.Data;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.stereotype.Component;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * 交易订单工厂类
- * 该类用于根据不同的交易平台(如OKX、BINANCE)获取相应的交易订单服务实例
- */
-@Component
-@Data
-public class TradeOrderFactory {
- /**
- * OKX交易平台的交易订单服务实例
- */
- @Qualifier("oKXTradeOrderServiceImpl")
- private final ITradeOrderService oKXTradeOrderServiceImpl;
-
- /**
- * 存储不同交易平台对应的交易订单服务实例的映射
- */
- private Map<String, ITradeOrderService> accountMap = new HashMap<>();
-
- /**
- * 构造方法,初始化交易平台与交易订单服务实例的映射
- *
- * @param oKXTradeOrderServiceImpl OKX交易平台的交易订单服务实例
- */
- public TradeOrderFactory(ITradeOrderService oKXTradeOrderServiceImpl) {
- this.oKXTradeOrderServiceImpl = oKXTradeOrderServiceImpl;
- accountMap.put("OKX", oKXTradeOrderServiceImpl);
- }
-
- /**
- * 根据平台关键字获取交易订单服务实例
- *
- * @param key 平台关键字,如"OKX"、"BINANCE"
- * @return 对应平台的交易订单服务实例
- */
- public ITradeOrderService get(String key) {
- return accountMap.get(key);
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/order/impl/OKXTradeOrderServiceImpl.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/order/impl/OKXTradeOrderServiceImpl.java
deleted file mode 100644
index 7832be2..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/order/impl/OKXTradeOrderServiceImpl.java
+++ /dev/null
@@ -1,130 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.order.impl;
-
-import com.xcong.excoin.modules.okxNewPrice.utils.FebsResponse;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.Dto.QuantApiMessage;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.Dto.TradeOrderDto;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.order.ITradeOrderService;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.order.vo.QuantExchangeReturnVo;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.trade.TradeRequestBuy;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.trade.TradeRequestSell;
-import com.xcong.excoin.modules.okxNewPrice.jiaoyi.IMQService;
-import lombok.SneakyThrows;
-import org.json.JSONObject;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-
-import javax.annotation.Resource;
-import javax.crypto.Mac;
-import javax.crypto.spec.SecretKeySpec;
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.Base64;
-
-@Service("oKXTradeOrderServiceImpl")
-public class OKXTradeOrderServiceImpl implements ITradeOrderService {
- @Value("${spring.OKEX.baseurl}")
- private String baseurl;
-
- @Resource
- private IMQService imqService;
-
- @SneakyThrows
- @Override
- public FebsResponse QuantExchangeReturnVo(TradeOrderDto tradeOrderDto, QuantApiMessage quantApiMessage) {
- try {
- // 构建订单的JSON对象
- JSONObject jsonBody = new JSONObject();
- jsonBody.put("instId", "BTC-USDT");
- jsonBody.put("tdMode", "cash");
- jsonBody.put("side", "buy");
- jsonBody.put("ordType", "limit");
- jsonBody.put("sz", "0.01");
- jsonBody.put("px", "30000");
-
- // 发起下单请求
- String result = postRequest("/api/v5/trade/order", jsonBody.toString(),quantApiMessage);
- System.out.println("Result: " + result);
-
- //解析返回数据
- JSONObject jsonResponse = new JSONObject(result);
- if (jsonResponse.has("code") && "0".equals(jsonResponse.getString("code"))) {
- // 订单提交成功
- JSONObject jSONObject = jsonResponse.getJSONObject("data");
- String clOrdId = jSONObject.getString("clOrdId");
- String ordId = jSONObject.getString("ordId");
- QuantExchangeReturnVo quantExchangeReturnVo = new QuantExchangeReturnVo();
- quantExchangeReturnVo.setCode("0");
- quantExchangeReturnVo.setOrdId(ordId);
- quantExchangeReturnVo.setClOrdId(clOrdId);
- return new FebsResponse().success().data(quantExchangeReturnVo);
- } else {
- String code = jsonResponse.getString("code");
- String msg = jsonResponse.getString("msg");
- QuantExchangeReturnVo quantExchangeReturnVo = new QuantExchangeReturnVo();
- quantExchangeReturnVo.setCode(code);
- quantExchangeReturnVo.setMessage(msg);
- return new FebsResponse().fail().data(quantExchangeReturnVo);
- }
-
- } catch (Exception e) {
- return new FebsResponse().fail().message("下单失败");
- }
- }
-
- @Override
- public void operationBuyMsg(TradeRequestBuy returnVo) {
-
- imqService.operationBuyMsg(returnVo);
- }
-
- @Override
- public void operationSellMsg(TradeRequestSell returnVo) {
-
- imqService.operationSellMsg(returnVo);
- }
-
- private String postRequest(String endpoint, String body, QuantApiMessage quantApiMessage) throws Exception {
- URL url = new URL(baseurl + endpoint);
- HttpURLConnection connection = (HttpURLConnection) url.openConnection();
- connection.setDoOutput(true);
- connection.setRequestMethod("POST");
- connection.setRequestProperty("Content-Type", "application/json");
- connection.setRequestProperty("OK-ACCESS-KEY", quantApiMessage.getASecretkey());
- connection.setRequestProperty("OK-ACCESS-SIGN", generateSignature(body,quantApiMessage));
- connection.setRequestProperty("OK-ACCESS-TIMESTAMP", getTimestamp());
- connection.setRequestProperty("OK-ACCESS-PASSPHRASE", quantApiMessage.getPassPhrass());
-
- try (OutputStream os = connection.getOutputStream()) {
- os.write(body.getBytes());
- os.flush();
- }
-
- if (connection.getResponseCode() != 200) {
- throw new RuntimeException("Failed : HTTP Error code : " + connection.getResponseCode());
- }
-
- BufferedReader br = new BufferedReader(new InputStreamReader((connection.getInputStream())));
- StringBuilder output = new StringBuilder();
- String line;
- while ((line = br.readLine()) != null) {
- output.append(line);
- }
- connection.disconnect();
- return output.toString();
- }
-
- private static String generateSignature(String body, QuantApiMessage quantApiMessage) throws Exception {
- String preHash = getTimestamp() + "POST" + "/api/v5/order" + body;
- SecretKeySpec secretKey = new SecretKeySpec(quantApiMessage.getBSecretkey().getBytes(), "HmacSHA256");
- Mac mac = Mac.getInstance("HmacSHA256");
- mac.init(secretKey);
- return Base64.getEncoder().encodeToString(mac.doFinal(preHash.getBytes()));
- }
-
- private static String getTimestamp() {
- return String.valueOf(System.currentTimeMillis() / 1000);
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/order/vo/QuantExchangeReturnVo.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/order/vo/QuantExchangeReturnVo.java
deleted file mode 100644
index 280fe02..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/order/vo/QuantExchangeReturnVo.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.order.vo;
-
-import lombok.Data;
-
-/**
- * 订单返回信息
- */
-@Data
-public class QuantExchangeReturnVo{
-
- private String code;//交易状态 0代表成功
- private String message;//错误信息
- private String ordId;//交易所订单号
- private String clOrdId;//客户订单id
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/query/IQueryOrderService.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/query/IQueryOrderService.java
deleted file mode 100644
index 225d64f..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/query/IQueryOrderService.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.query;
-
-
-import com.xcong.excoin.modules.okxNewPrice.utils.FebsResponse;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.Dto.QuantApiMessage;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.query.dto.QuantOperateRecode;
-
-//订单查询接口 欧易调用的接口是 撮合交易/交易/ GET 获取订单信息
-public interface IQueryOrderService {
- FebsResponse QueryOrder(QuantApiMessage quantApiMessage, String instId, String clOrdId);
-
- //检查订单在交易所交易情况 exChangeState返回状态说明 1.filled代表交易成功 2.canceled代表交易失败或者取消 3.live代表交易中
- FebsResponse checkOrder(QuantOperateRecode operateRecode);
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/query/QueryOrderFactory.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/query/QueryOrderFactory.java
deleted file mode 100644
index 0bd1ac6..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/query/QueryOrderFactory.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.query;
-
-import lombok.Data;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.stereotype.Component;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * 查询委托工厂类
- * 该类用于根据不同的交易平台(如OKX、BINANCE)获取相应的查询委托服务实例
- */
-@Component
-@Data
-public class QueryOrderFactory {
- // OKX的查询委托服务实现
- @Qualifier("oKXQueryOrderServiceImpl")
- private final IQueryOrderService oKXQueryOrderServiceImpl;
-
- // 存储不同交易平台查询委托服务的映射
- private Map<String, IQueryOrderService> accountMap = new HashMap<>();
-
- /**
- * 构造方法,初始化查询委托服务映射
- * @param oKXQueryOrderServiceImpl OKX的查询委托服务实现
- */
- public QueryOrderFactory(IQueryOrderService oKXQueryOrderServiceImpl) {
- this.oKXQueryOrderServiceImpl = oKXQueryOrderServiceImpl;
- accountMap.put("OKX", oKXQueryOrderServiceImpl);
- }
-
- /**
- * 根据平台名称获取查询委托服务实例
- * @param key 平台名称,如"OKX"、"BINANCE"
- * @return 对应平台的查询委托服务实例
- */
- public IQueryOrderService get(String key) {
- return accountMap.get(key);
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/query/dto/QuantOperateRecode.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/query/dto/QuantOperateRecode.java
deleted file mode 100644
index 27d525e..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/query/dto/QuantOperateRecode.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.query.dto;
-
-import com.baomidou.mybatisplus.annotation.TableField;
-import lombok.Data;
-
-import java.math.BigDecimal;
-
-@Data
-public class QuantOperateRecode{
-
- private String tradeNumber;//交易编号
- private String operationCode;//操作编号
- private String sellTradeNumber;//卖出的订单号
- private Long currencyId;//操作币种ID
- private String exchange;//交易所名称
- private Long apiMessageId;//交易所ID
- private Long memberId;//用户ID
- private String coinPair;//货币对
- private Integer coinType;//计价货币 1-USDTU本位 2-USDT币本位
- private Integer coinDirect;//方向 1-买多 2-买空
- private BigDecimal coinLevel;//杠杆倍数
- private BigDecimal pageNum;//张数
- private BigDecimal price;//均价
- private BigDecimal amount;//总价
- private BigDecimal profit;//盈亏
- private BigDecimal fee;//费用与返佣
- private BigDecimal singleOrder;//单序
- private BigDecimal quantity;//数量
- private Integer directStatus;//策略 1-顺势 2-逆势
- private Integer type;//方式 1.网络 2.其他
- private Integer status;//所属状态 1-开仓 2-平仓
- private Integer finishStatus;//所属状态 1-未完成 2-已完成 3-撤单 4-已卖出(针对买单由此状态)5.-交易失败
- private String ordId;//交易所订单ID
- private BigDecimal sellPrice;//计划平仓价格
- private BigDecimal incomePrice;//平仓传入价格
- private BigDecimal rangeRatio;//比率区域
- private Integer lockStatus;//卖出状态 0-未锁定 1-已锁定
- private String buyRediskey;//买单的rediskey
- private Integer tickSz;//下单精度
-
- @TableField(exist = false)
- private String startTime;//开始时间
- @TableField(exist = false)
- private String endTime;//结束时间
- @TableField(exist = false)
- private Integer excludeFinishStatus;//查询时排除状态
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/query/impl/OKXQueryOrderServiceImpl.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/query/impl/OKXQueryOrderServiceImpl.java
deleted file mode 100644
index 8913a48..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/query/impl/OKXQueryOrderServiceImpl.java
+++ /dev/null
@@ -1,156 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.query.impl;
-
-import cn.hutool.core.util.ObjectUtil;
-import com.alibaba.fastjson.JSON;
-import com.xcong.excoin.modules.okxNewPrice.utils.FebsException;
-import com.xcong.excoin.modules.okxNewPrice.utils.FebsResponse;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.Dto.QuantApiMessage;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.OKXAccount;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.enums.HttpMethod;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils.OKXContants;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.vo.BalanceVo;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.query.IQueryOrderService;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.query.dto.QuantOperateRecode;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.query.vo.QuantCheckOrderVo;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.query.vo.QuantOperateRecodeVo;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.verify.VerifyAccountFactory;
-import com.xcong.excoin.modules.okxNewPrice.zhanghu.IApiMessageService;
-import com.xcong.excoin.modules.okxNewPrice.zhanghu.ZhangHuEnum;
-import lombok.RequiredArgsConstructor;
-import lombok.SneakyThrows;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
-import org.json.JSONObject;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-
-import javax.crypto.Mac;
-import javax.crypto.spec.SecretKeySpec;
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-import java.math.BigDecimal;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Base64;
-import java.util.LinkedHashMap;
-
-@Service("oKXQueryOrderServiceImpl")
-@RequiredArgsConstructor
-@Slf4j
-public class OKXQueryOrderServiceImpl implements IQueryOrderService {
- @Value("spring.OKEX.baseurl")
- private String baseurl;
-
- private final IApiMessageService apiMessageService;
-
- private final VerifyAccountFactory verifyAccountFactory;
-
- private static String hmacSha256(String data, String key) throws Exception {
- Mac mac = Mac.getInstance("HmacSHA256");
- SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "HmacSHA256");
- mac.init(secretKeySpec);
- return Base64.getEncoder().encodeToString(mac.doFinal(data.getBytes()));
- }
-
- @SneakyThrows
- @Override
- public FebsResponse QueryOrder(QuantApiMessage quantApiMessage, String instId, String clOrdId) {
- String url = baseurl + "/api/v5/trade/order?instId=" + instId + "&clOrdId=" + clOrdId; // 构建请求URL
- try {
- HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
- connection.setRequestMethod("GET");
- connection.setRequestProperty("Content-Type", "application/json");
- connection.setRequestProperty("OK-ACCESS-KEY", quantApiMessage.getASecretkey());
- connection.setRequestProperty("OK-ACCESS-SIGN", hmacSha256(quantApiMessage.getBSecretkey(), clOrdId)); // 根据需要生成签名
- connection.setRequestProperty("OK-ACCESS-TIMESTAMP", String.valueOf(System.currentTimeMillis() / 1000));
- connection.setRequestProperty("OK-ACCESS-PASSPHRASE", "your_passphrase"); // 替换为您的Passphrase
-
- int responseCode = connection.getResponseCode();
-
- if (responseCode == HttpURLConnection.HTTP_OK) {
- BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
- String inputLine;
- StringBuilder response = new StringBuilder();
-
- while ((inputLine = in.readLine()) != null) {
- response.append(inputLine);
- }
- in.close();
-
- JSONObject jsonResponse = new JSONObject(response.toString());
- QuantCheckOrderVo quantCheckOrderVo = new QuantCheckOrderVo();
- if (jsonResponse.has("clOrdId") && StringUtils.isNotBlank(jsonResponse.getString("clOrdId"))) {
- quantCheckOrderVo.setTradeNumber(jsonResponse.getString("clOrdId"));
- }
- if (jsonResponse.has("ordId") && StringUtils.isNotBlank(jsonResponse.getString("ordId"))) {
- quantCheckOrderVo.setOrdId(jsonResponse.getString("ordId"));
- }
- if (jsonResponse.has("pnl") && StringUtils.isNotBlank(jsonResponse.getString("pnl"))) {
- quantCheckOrderVo.setProfit(new BigDecimal(jsonResponse.getString("pnl")));
- }
- if (jsonResponse.has("sz") && StringUtils.isNotBlank(jsonResponse.getString("sz"))) {
- quantCheckOrderVo.setPageNum(new Integer(jsonResponse.getString("sz")));
- }
- if (jsonResponse.has("fillPx") && StringUtils.isNotBlank(jsonResponse.getString("fillPx"))) {
- quantCheckOrderVo.setPrice(new BigDecimal(jsonResponse.getString("fillPx")));
- BigDecimal total = new BigDecimal(new Integer(jsonResponse.getString("sz"))).multiply(new BigDecimal(jsonResponse.getString("fillPx")));
- quantCheckOrderVo.setAmount(total);
- }
- return new FebsResponse().success().data(quantCheckOrderVo);
- } else {
- return new FebsResponse().fail().message("交易所订单查询异常");
- }
- } catch (Exception e) {
- return new FebsResponse().fail().message("订单查询异常");
- }
- }
-
- @Override
- public FebsResponse checkOrder(QuantOperateRecode operateRecode) {
- log.info("查询OKX订单信息:{}",operateRecode);
-
- QuantApiMessage quantApiMessage = apiMessageService.getApiMessage(ZhangHuEnum.JIAOYISUO.getValue());
- if(ObjectUtil.isEmpty(quantApiMessage)){
- throw new FebsException("参数错误");
- }
-
- OKXAccount okxAccount = (OKXAccount) verifyAccountFactory.getAccountMap().get(quantApiMessage.getExchange())
- .initAccount(quantApiMessage);
- ArrayList<BalanceVo> balanceVos = new ArrayList<>();
-
- LinkedHashMap<String, Object> orderParameters = new LinkedHashMap<>();
- orderParameters.put("clOrdId", operateRecode.getTradeNumber());
- //将字符串BTC-USDTU-空 截取为 BTC-USDT
- String coinPair = operateRecode.getCoinPair().substring(0, operateRecode.getCoinPair().indexOf("-USDT")+5);
- orderParameters.put("instId", coinPair+"-SWAP");
- String orderStr = okxAccount.requestHandler.sendSignedRequest(okxAccount.baseUrl, OKXContants.ORDER, orderParameters, HttpMethod.GET, okxAccount.isSimluate());
- log.info("查询OKX订单返回信息:{}",orderStr);
- QuantOperateRecodeVo quantOperateRecodeVo = new QuantOperateRecodeVo();
- com.alibaba.fastjson.JSONObject orderStrJson = JSON.parseObject(orderStr);
- String code = orderStrJson.getString("code");
- if("0".equals(code)){
- com.alibaba.fastjson.JSONObject data = orderStrJson.getJSONArray("data").getJSONObject(0);
- quantOperateRecodeVo.setQuantity(ObjectUtil.isEmpty(data.getString("accFillSz"))?new BigDecimal("0"): new BigDecimal(data.getString("accFillSz")));
- quantOperateRecodeVo.setPrice(ObjectUtil.isEmpty(data.getString("avgPx"))?new BigDecimal("0"): new BigDecimal(data.getString("avgPx")));
- quantOperateRecodeVo.setProfit(ObjectUtil.isEmpty(data.getString("pnl"))?new BigDecimal("0"): new BigDecimal(data.getString("pnl")));
- quantOperateRecodeVo.setFee(ObjectUtil.isEmpty(data.getString("fee"))?new BigDecimal("0"): new BigDecimal(data.getString("fee")));
- quantOperateRecodeVo.setExchangeNumber(data.getString("ordId"));
- quantOperateRecodeVo.setCoinLevel(ObjectUtil.isEmpty(data.getString("lever"))?new BigDecimal("0"): new BigDecimal(data.getString("lever")));
- quantOperateRecodeVo.setOrdType(data.getString("ordType"));
- quantOperateRecodeVo.setExChangeState(data.getString("state"));
- } else if("51603".equals(code)){
- quantOperateRecodeVo.setQuantity(new BigDecimal("0"));
- quantOperateRecodeVo.setPrice(new BigDecimal("0"));
- quantOperateRecodeVo.setProfit(new BigDecimal("0"));
- quantOperateRecodeVo.setCoinLevel(new BigDecimal("0"));
- quantOperateRecodeVo.setExchangeNumber("");
- quantOperateRecodeVo.setOrdType("");
- quantOperateRecodeVo.setExChangeState("canceled");
- } else {
- return null;
- }
-
- return new FebsResponse().success().data(quantOperateRecodeVo);
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/query/vo/QuantCheckOrderVo.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/query/vo/QuantCheckOrderVo.java
deleted file mode 100644
index a462272..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/query/vo/QuantCheckOrderVo.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.query.vo;
-
-import lombok.Data;
-
-import java.math.BigDecimal;
-
-/**
- * 订单返回信息
- */
-@Data
-public class QuantCheckOrderVo{
-
- private String tradeNumber;//交易编号
- private Integer pageNum;//张数
- private BigDecimal price;//均价
- private BigDecimal amount;//总价
- private BigDecimal profit;//盈亏
- private String ordId;//交易所订单ID
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/query/vo/QuantOperateRecodeVo.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/query/vo/QuantOperateRecodeVo.java
deleted file mode 100644
index df9d931..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/query/vo/QuantOperateRecodeVo.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.query.vo;
-
-
-import lombok.Data;
-
-import java.math.BigDecimal;
-
-/**
- * 合约操作记录
- */
-@Data
-public class QuantOperateRecodeVo{
-
- private String tradeNumber;//交易编号
- private Long apiMessageId;//交易所ID
- private String exchange;//交易所名称
- private String exchangeNumber;//交易所交易编号
- private String operationCode;//操作编号
- private Long currencyId;//操作币种ID
- private Long memberId;//用户ID
- private String coinPair;//货币对
- private Integer coinDirect;//方向 1-买多 2-买空
- private BigDecimal coinLevel;//杠杆倍数
- private BigDecimal pageNum;//张数
- private BigDecimal quantity;//数量
- private BigDecimal price;//均价
- private BigDecimal amount;//成交金额
- private BigDecimal profit;//盈亏
- private String sellTradeNumber;//卖出的订单号
- private BigDecimal fee;//费用与返佣
- private BigDecimal singleOrder;//单序
- private BigDecimal sellPrice;//计划平仓价格
- private Integer coinType;//计价货币 1-USDTU本位 2-USDT币本位
- private Integer directStatus;//策略 1-顺势 2-逆势
- private Integer type;//方式 1.网络 2.其他
- private Integer status;//所属状态 1-开仓 2-平仓
- private Integer tradeStatus;//交易状态 1-未完成 2-成功 3-失败
- private Integer finishStatus;//所属状态 1-未完成 2-已完成 3-撤单 4-已卖出(针对买单由此状态)5.-交易失败
- private String exChangeState;//交易所状态
- private Integer lockStatus;//卖出状态 0-未锁定 1-已锁定
- private Integer tickSz;//下单精度 1-0.1 2-0.01 3-0.001 4-0.0001 5-0.00001
- private String ordType;//订单类型 订单类型 market:市价单 limit:限价单 post_only:只做maker单 fok:全部成交或立即取消 ioc:立即成交并取消剩余 optimal_limit_ioc:市价委托立即成交并取消剩余(仅适用交割、永续)
- //mmp:做市商保护(仅适用于组合保证金账户模式下的期权订单) mmp_and_post_only:做市商保护且只做maker单(仅适用于组合保证金账户模式下的期权订单) op_fok:期权简选(全部成交或立即取消)
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/TradeEventEnum.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/TradeEventEnum.java
deleted file mode 100644
index 17e05bf..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/TradeEventEnum.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.trade;
-
-import lombok.Getter;
-
-@Getter
-public enum TradeEventEnum {
-
-
- TRADE_CLOSE_POSITION("CLOSE_POSITION",
- "编码:{},用户:{}, 触发价:{},实际成交价格:{},执行全平操作"),
-
- TRADE_SELL("SELL",
- "编码:{},用户:{}, 触发价:{},实际成交价格:{},执行平仓操作"),
-
- TRADE_BUY("BUY",
- "编码:{},用户:{}, 触发价:{},实际成交价格:{},执行开仓操作");
-
- private String eventPoint;
-
- private String eventDec;
-
-
- TradeEventEnum(String eventPoint, String eventDec) {
- this.eventPoint = eventPoint;
- this.eventDec = eventDec;
- }
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/TradeEventRunner.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/TradeEventRunner.java
deleted file mode 100644
index c14b24e..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/TradeEventRunner.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.trade;
-
-import com.xcong.excoin.modules.okxNewPrice.utils.FebsException;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.OKXAccount;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.trade.impl.TradeServiceBuy;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.trade.impl.TradeServiceClosePosition;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.trade.impl.TradeServiceSell;
-
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * 交易事件运行类,用于根据不同的事件点选择相应的交易策略并执行
- */
-public class TradeEventRunner {
-
- // 使用线程安全的ConcurrentHashMap存储交易事件和对应的策略服务
- private static final Map<String, TradeService> TRADE_EVENT_MAP = new ConcurrentHashMap<>();
-
- static {
- // 初始化策略
- List<TradeService> tradeServices = Arrays.asList(
- new TradeServiceBuy(), // 买入策略
- new TradeServiceSell(), // 卖出策略
- new TradeServiceClosePosition() // 平仓策略
- );
-
- // 将策略放入ConcurrentHashMap中,确保线程安全
- for (TradeService service : tradeServices) {
- TRADE_EVENT_MAP.put(service.tradeEvent(), service);
- }
- }
-
- /**
- * 根据事件点选择并执行相应的交易策略
- *
- * @param eventPoint 事件点,用于匹配相应的交易策略
- * @param tradeRequest 包含交易请求信息的LinkedHashMap
- * @param okxAccount OKX账户信息,用于执行交易
- * @return 交易执行的结果
- * @throws FebsException 如果找不到对应的交易策略,则抛出异常
- */
- public static String runTradeEvent(String eventPoint, LinkedHashMap<String, Object> tradeRequest, OKXAccount okxAccount) {
- TradeService tradeService = TRADE_EVENT_MAP.get(eventPoint);
- if (tradeService == null) {
- throw new FebsException("交易EVENT异常");
- }
-
- return tradeService.doTrade(tradeRequest,okxAccount);
- }
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/TradeRequest.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/TradeRequest.java
deleted file mode 100644
index 3aec237..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/TradeRequest.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.trade;
-
-import lombok.Data;
-
-@Data
-public class TradeRequest {
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/TradeRequestBuy.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/TradeRequestBuy.java
deleted file mode 100644
index 5189e06..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/TradeRequestBuy.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.trade;
-
-import lombok.Data;
-
-import java.io.Serializable;
-import java.math.BigDecimal;
-
-@Data
-public class TradeRequestBuy implements Serializable {
- /**
- * 实现了 Serializable 接口,rabbitmq可以直接发送它,但需要确保 SimpleMessageConverter 能够处理它(对象简单,不是复杂的对象)
- */
- private static final long serialVersionUID = 1L;
-
- private Long strategiesId;//策略ID ReturnBuyVo.strategyId
-
- private String tradeCode;//自定义订单编号
-
- private Double tradeCnt;//交易数量 ReturnBuyVo.quantity
- /**
- * 限定价格
- * 限定价格为0的时候,表示市价操作当前订单
- */
- private String limitPrice = "0";
- /**
- * 产品ID,如 BTC-USDT
- */
- private String instId; //ReturnBuyVo.instId
- /**
- * 持仓方向
- * 在开平仓模式下必填,且仅可选择 long 或 short。 仅适用交割、永续。
- * 1-long 2-short
- */
- private Integer positionSide = 1;
- /**
- * 1 - isolated:逐仓 ;2 - cross:全仓
- * 交易模式
- * 保证金模式:isolated:逐仓 ;cross:全仓
- * 非保证金模式:cash:非保证金
- * spot_isolated:现货逐仓(仅适用于现货带单) ,现货带单时,tdMode 的值需要指定为spot_isolated
- */
- private Integer tdMode = 2;
-
- private BigDecimal priceRange; //买入的价格段位
-
- private Long operationCurrencyId; //操作货币id
-
- private String redisKey; //redisKey 用于标识是否已经处理过
-
- private String exchange; //交易所
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/TradeRequestSell.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/TradeRequestSell.java
deleted file mode 100644
index d167070..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/TradeRequestSell.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.trade;
-
-import com.xcong.excoin.modules.okxNewPrice.okxpi.trade.vo.SellOrderVo;
-import lombok.Data;
-
-import java.io.Serializable;
-import java.math.BigDecimal;
-import java.util.List;
-
-@Data
-public class TradeRequestSell implements Serializable {
- /**
- * 实现了 Serializable 接口,rabbitmq可以直接发送它,但需要确保 SimpleMessageConverter 能够处理它(对象简单,不是复杂的对象)
- */
- private static final long serialVersionUID = 1L;
-
- private Long strategiesId;//策略ID ReturnVo.strategyId
-
- private String tradeCode;//自定义订单编号
-
- private BigDecimal rangeRatio;//区域比率
-
- private BigDecimal incomePrice;//平仓传入价格
-
- private Double tradeCnt;//交易数量 ReturnVo.quantity
- /**
- * 限定价格
- * 限定价格为0的时候,表示市价操作当前订单
- */
- private String limitPrice = "0";
- /**
- * 产品ID,如 BTC-USDT
- */
- private String instId; //ReturnVo.instId
- /**
- * 持仓方向
- * 在开平仓模式下必填,且仅可选择 long 或 short。 仅适用交割、永续。
- * 1-long 2-short
- */
- private Integer positionSide = 1;
- /**
- * 1 - isolated:逐仓 ;2 - cross:全仓
- * 交易模式
- * 保证金模式:isolated:逐仓 ;cross:全仓
- * 非保证金模式:cash:非保证金
- * spot_isolated:现货逐仓(仅适用于现货带单) ,现货带单时,tdMode 的值需要指定为spot_isolated
- */
- private Integer tdMode = 2;
-
- private List<SellOrderVo> sellOrderList; //卖出订单列表,当operations为buy时,该字段为空
-
- private Long operationCurrencyId; //操作货币id
-
- private String redisKey; //redisKey 用于标识是否已经处理过
-
- private String exchange; //交易所
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/TradeService.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/TradeService.java
deleted file mode 100644
index f19809a..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/TradeService.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.trade;
-
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.OKXAccount;
-
-import java.util.LinkedHashMap;
-
-public interface TradeService {
- /**
- * 采用策略
- * @return
- */
- String tradeEvent();
-
- /**
- * 执行
- * @param tradeDto
- * @param okxAccount
- */
- String doTrade(LinkedHashMap<String, Object> tradeDto, OKXAccount okxAccount);
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/impl/TradeServiceBuy.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/impl/TradeServiceBuy.java
deleted file mode 100644
index b303fda..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/impl/TradeServiceBuy.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.trade.impl;
-
-import com.alibaba.fastjson.JSON;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.OKXAccount;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.enums.HttpMethod;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils.OKXContants;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils.ParameterChecker;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.trade.TradeEventEnum;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.trade.TradeService;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-
-import java.util.LinkedHashMap;
-
-@Slf4j
-@RequiredArgsConstructor
-public class TradeServiceBuy implements TradeService {
-
- @Override
- public String tradeEvent() {
- return TradeEventEnum.TRADE_BUY.getEventPoint();
- }
-
- @Override
- public String doTrade(LinkedHashMap<String, Object> tradeDto, OKXAccount okxAccount) {
- ParameterChecker.checkParameter(tradeDto, "instId", String.class);
- ParameterChecker.checkParameter(tradeDto, "tdMode", String.class);
- ParameterChecker.checkParameter(tradeDto, "side", String.class);
- ParameterChecker.checkParameter(tradeDto, "ordType", String.class);
- ParameterChecker.checkParameter(tradeDto, "sz", Double.class);
- String placeOrderRspOkxRestResponse = okxAccount.requestHandler.sendSignedRequest(okxAccount.baseUrl, OKXContants.ORDER, tradeDto, HttpMethod.POST, okxAccount.isSimluate());
- log.info("开仓响应:{}",JSON.parseObject(placeOrderRspOkxRestResponse).get("data"));
- /**
- * todo 下单之后的日志处理
- */
- return placeOrderRspOkxRestResponse;
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/impl/TradeServiceClosePosition.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/impl/TradeServiceClosePosition.java
deleted file mode 100644
index c06bcdb..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/impl/TradeServiceClosePosition.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.trade.impl;
-
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.OKXAccount;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.trade.TradeEventEnum;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.trade.TradeService;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-
-import java.util.LinkedHashMap;
-
-@Slf4j
-@RequiredArgsConstructor
-public class TradeServiceClosePosition implements TradeService {
- @Override
- public String tradeEvent() {
- return TradeEventEnum.TRADE_CLOSE_POSITION.getEventPoint();
- }
-
- @Override
- public String doTrade(LinkedHashMap<String, Object> tradeDto, OKXAccount okxAccount) {
- System.out.println(this.tradeEvent());
- return null;
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/impl/TradeServiceSell.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/impl/TradeServiceSell.java
deleted file mode 100644
index 6a36a4c..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/impl/TradeServiceSell.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.trade.impl;
-
-import com.alibaba.fastjson.JSON;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.OKXAccount;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.enums.HttpMethod;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils.OKXContants;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils.ParameterChecker;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.trade.TradeEventEnum;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.trade.TradeService;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-
-import java.util.LinkedHashMap;
-
-@Slf4j
-@RequiredArgsConstructor
-public class TradeServiceSell implements TradeService {
- @Override
- public String tradeEvent() {
- return TradeEventEnum.TRADE_SELL.getEventPoint();
- }
-
- @Override
- public String doTrade(LinkedHashMap<String, Object> tradeDto, OKXAccount okxAccount) {
- ParameterChecker.checkParameter(tradeDto, "instId", String.class);
- ParameterChecker.checkParameter(tradeDto, "tdMode", String.class);
- ParameterChecker.checkParameter(tradeDto, "side", String.class);
- ParameterChecker.checkParameter(tradeDto, "ordType", String.class);
- ParameterChecker.checkParameter(tradeDto, "sz", Double.class);
- String placeOrderRspOkxRestResponse = okxAccount.requestHandler.sendSignedRequest(okxAccount.baseUrl, OKXContants.ORDER, tradeDto, HttpMethod.POST, okxAccount.isSimluate());
- log.info("平仓响应:{}", JSON.parseObject(placeOrderRspOkxRestResponse).get("data"));
- /**
- * todo 下单之后的日志处理
- */
-
- return placeOrderRspOkxRestResponse;
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/vo/SellOrderVo.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/vo/SellOrderVo.java
deleted file mode 100644
index 2d14a67..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/trade/vo/SellOrderVo.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.trade.vo;
-
-import lombok.Data;
-
-import java.io.Serializable;
-import java.math.BigDecimal;
-
-/**
- * @author wzy
- * @date 2021-09-16
- **/
-@Data
-public class SellOrderVo implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- private String ordId; //交易所订单号
-
- private String tradeNumber; //本地订单号
-
- private BigDecimal quantity;//数量
-
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/IVerifyAccountService.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/IVerifyAccountService.java
deleted file mode 100644
index fdb0269..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/IVerifyAccountService.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.verify;
-
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.OKXAccount;
-import com.xcong.excoin.modules.okxNewPrice.utils.FebsResponse;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.Account;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.Dto.QuantApiMessage;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.verify.dto.*;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.verify.vo.ApiAccountHoldVo;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.verify.vo.ApiPositionsInfoVo;
-
-//检查账户信息是否合格 欧易调用的接口是交易账户/REST API/查看账户余额
-public interface IVerifyAccountService {
-
- /**
- * 初始化api信息
- * @return
- */
- OKXAccount initAccount(QuantApiMessage quantApiMessage);
- /**
- * 初始化api信息
- * @param apiKey
- * @param secretKey
- * @param accountType
- * @return
- */
- Account initAccountV2(String apiKey, String secretKey, String passPhrass, boolean accountType);
-
- FebsResponse verifyAccount(ApiMessageDto apiMessageDto);
-
- /**
- * 获取产品信息
- * @param apiValidApiMessageDto
- */
- FebsResponse getProductMess(ApiValidApiMessageDto apiValidApiMessageDto);
-
- /**
- * 获取当前价格
- * @param operateCurrencyDto
- */
- FebsResponse getCurrenPrice(OperateCurrencyDto operateCurrencyDto);
-
- /**
- * 获取杠杆列表
- * @param operateCurrencyLeverDto
- */
- FebsResponse getlever(OperateCurrencyLeverDto operateCurrencyLeverDto);
-
- /**
- * 设置币种杠杆
- * @param operateCurrencyLeverDto
- */
- FebsResponse setLever(OperateCurrencyLeverDto operateCurrencyLeverDto);
-
- /**
- * 获取持仓信息
- * @param quantApiMessage
- */
- FebsResponse testApiLink(QuantApiMessage quantApiMessage);
-
- /**
- * 获取账户总权益
- * @param apiAccountBalanceDto
- */
- void getAccountBalance(ApiAccountBalanceDto apiAccountBalanceDto);
-
- /**
- * 获取持仓概况
- * @param apiAccountBalanceInfoDto
- */
- ApiAccountHoldVo getAccountBalanceInfo(ApiAccountBalanceInfoDto apiAccountBalanceInfoDto);
-
- ApiPositionsInfoVo getAccountPositionsInfo(ApiPositionsInfoDto apiPositionsInfoDto);
-
- /**
- * 获取交易大数据
- * @return
- */
- FebsResponse getTradeData(ApiValidApiMessageDto apiValidApiMessageDto);
-
- /**
- * 获取买入卖出情况
- * @return
- */
- FebsResponse getBuySellSituation(ApiValidApiMessageDto apiValidApiMessageDto);
-
- /**
- * 获取持仓人数比
- * @return
- */
- FebsResponse getPositionRatio(ApiValidApiMessageDto apiValidApiMessageDto);
-
- /**
- * 获取合约持仓量及交易量
- * @return
- */
- FebsResponse getPositionTradingvolume(ApiValidApiMessageDto apiValidApiMessageDto);
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/VerifyAccountFactory.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/VerifyAccountFactory.java
deleted file mode 100644
index 1ae8b06..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/VerifyAccountFactory.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.verify;
-
-import lombok.Data;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.stereotype.Component;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * 账号验证工厂类
- * 该类用于管理不同交易所的账号验证服务通过传入的键获取相应的验证服务
- */
-@Component
-@Data
-public class VerifyAccountFactory {
-
- /**
- * OKX交易所的账号验证服务
- */
- @Qualifier("oKXVerifyAccount")
- private final IVerifyAccountService oKXVerifyAccount;
-
- /**
- * 存储不同交易所的账号验证服务的映射
- */
- private Map<String, IVerifyAccountService> accountMap = new HashMap<>();
-
- /**
- * 构造方法,初始化账号验证工厂
- * @param oKXVerifyAccount OKX交易所的账号验证服务
- */
- public VerifyAccountFactory(IVerifyAccountService oKXVerifyAccount) {
- this.oKXVerifyAccount = oKXVerifyAccount;
- accountMap.put("OKX", oKXVerifyAccount);
- }
-
- /**
- * 根据传入的键获取相应的账号验证服务
- * @param key 交易所的键,如"OKX"或"BINANCE"
- * @return 对应的账号验证服务
- */
- public IVerifyAccountService get(String key) {
- return accountMap.get(key);
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/ApiAccountBalDto.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/ApiAccountBalDto.java
deleted file mode 100644
index 00c9cc3..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/ApiAccountBalDto.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.verify.dto;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import javax.validation.constraints.NotBlank;
-
-@Data
-@ApiModel(value = "ApiAccountBalDto", description = "参数接收类")
-public class ApiAccountBalDto {
-
- @NotBlank(message = "请选择时间范围")
- @ApiModelProperty(value = "开始时间", example = "")
- private String startTime;//开始时间
-
- @NotBlank(message = "请选择时间范围")
- @ApiModelProperty(value = "结束时间", example = "")
- private String endTime;//结束时间
-
- @ApiModelProperty(value = "交易所类型", example = "1")
- private String exchange;
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/ApiAccountBalanceDto.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/ApiAccountBalanceDto.java
deleted file mode 100644
index 9c5dc63..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/ApiAccountBalanceDto.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.verify.dto;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import javax.validation.constraints.NotNull;
-
-@Data
-@ApiModel(value = "ApiAccountBalanceDto", description = "参数接收类")
-public class ApiAccountBalanceDto {
-
- @ApiModelProperty(value = "apiMessageId", example = "11")
- private Long id;
-
- @NotNull(message = "账户类型不能为空")
- @ApiModelProperty(value = "账户类型:实盘账户-true, 模拟账户-false", example = "false")
- private boolean accountType;
-
- @ApiModelProperty(value = "币种名称", example = "BTC")
- private String coinName;
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/ApiAccountBalanceInfoDto.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/ApiAccountBalanceInfoDto.java
deleted file mode 100644
index 7774264..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/ApiAccountBalanceInfoDto.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.verify.dto;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-@Data
-@ApiModel(value = "ApiAccountBalanceInfoDto", description = "参数接收类")
-public class ApiAccountBalanceInfoDto {
-
- @ApiModelProperty(value = "apiMessageId", example = "11")
- private Long id;
-
- @ApiModelProperty(value = "币种名称", example = "BTC")
- private String coinName;
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/ApiAccountProfitDailyDto.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/ApiAccountProfitDailyDto.java
deleted file mode 100644
index 229f292..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/ApiAccountProfitDailyDto.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.verify.dto;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-@Data
-@ApiModel(value = "ApiAccountBalanceInfoDto", description = "参数接收类")
-public class ApiAccountProfitDailyDto {
-
- @ApiModelProperty(value = "交易所类型", example = "1")
- private String exchange;
-
- @ApiModelProperty(value = "开始时间", example = "")
- private String startTime;//开始时间
-
- @ApiModelProperty(hidden = true)
- private String endTime;//结束时间
-
- @ApiModelProperty(hidden = true)
- private Long memberId;
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/ApiMessageDto.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/ApiMessageDto.java
deleted file mode 100644
index 2b9f2f2..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/ApiMessageDto.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.verify.dto;
-
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotNull;
-
-/**
- * @author lyq
- * @date 2024-09-16
- **/
-@Data
-public class ApiMessageDto {
-
- @ApiModelProperty(value = "id")
- private Long id;
-
- @ApiModelProperty(value = "会员id", example = "11")
- private Long memberId;
-
- @NotBlank(message = "交易所不能为空")
- @ApiModelProperty(value = "交易所", example = "jys")
- private String exchange;
-
- @NotBlank(message = "a秘钥不能为空")
- @ApiModelProperty(value = "a秘钥", example = "123456")
- private String aSecretkey;
-
- @NotBlank(message = "b秘钥不能为空")
- @ApiModelProperty(value = "b秘钥", example = "123456")
- private String bSecretkey;
-
- @NotBlank(message = "passPhrass不能为空")
- @ApiModelProperty(value = "passPhrass", example = "123456")
- private String passPhrass;
-
- @NotNull(message = "账户类型不能为空")
- @ApiModelProperty(value = "账户类型:实盘账户-true, 模拟账户-false", example = "false")
- private String accountType;
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/ApiPositionsInfoDto.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/ApiPositionsInfoDto.java
deleted file mode 100644
index 3222f37..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/ApiPositionsInfoDto.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.verify.dto;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-@Data
-@ApiModel(value = "ApiAccountBalanceInfoDto", description = "参数接收类")
-public class ApiPositionsInfoDto {
-
- @ApiModelProperty(value = "apiMessageId", example = "11")
- private Long id;
-
- @ApiModelProperty(value = "币种名称", example = "BTC")
- private String coinName;
-
- @ApiModelProperty(value = "交易所名称", example = "OKX")
- private String exchange;
-
- @ApiModelProperty(value = "持仓ID", example = "")
- private String posId;
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/ApiValidApiMessageDto.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/ApiValidApiMessageDto.java
deleted file mode 100644
index 3a57022..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/ApiValidApiMessageDto.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.verify.dto;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import javax.validation.constraints.NotNull;
-
-@Data
-@ApiModel(value = "ApiValidApiMessageDto", description = "API信息参数接收类")
-public class ApiValidApiMessageDto {
-
- @NotNull(message = "apiMessageId")
- @ApiModelProperty(value = "apiMessageId", example = "11")
- private Long id;
-
- @NotNull(message = "账户类型不能为空")
- @ApiModelProperty(value = "账户类型:实盘账户-true, 模拟账户-false", example = "false")
- private boolean accountType;
-
- @ApiModelProperty(value = "币种名称", example = "BTC")
- private String coinName;
-
- @ApiModelProperty(value = "交易所", example = "OKX")
- private String exchange;
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/OperateCurrencyDto.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/OperateCurrencyDto.java
deleted file mode 100644
index 4aac95a..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/OperateCurrencyDto.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.verify.dto;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import javax.validation.constraints.NotBlank;
-
-/**
- * @author wzy
- * @date 2024-08-10
- **/
-@Data
-@ApiModel(value = "OperateCurrencyDto", description = "登录接口参数接收类")
-public class OperateCurrencyDto {
-
- @ApiModelProperty(value = "id")
- private Long id;
-
- @ApiModelProperty(value = "交易所ID", example = "")
- private Long apiMessageId;//交易所ID
-
- @ApiModelProperty(value = "交易所名称", example = "")
- private String exchange;//交易所名称
-
-// @NotBlank(message = "币种标识不能为空")
-// @ApiModelProperty(value = "币种标识", example = "DOGE")
-// private String coinSymbol;//货币对(交易货币(币种标识))
-
- @NotBlank(message = "币种标识不能为空")
- @ApiModelProperty(value = "币种标识", example = "DOGE")
- private String coinName;//货币货币名称(交易货币(币种标识))
-
- @NotBlank(message = "计价货币不能为空")
- @ApiModelProperty(value = "计价货币", example = "USDTU")
- private Integer coinType;//计价货币 1-USDTU本位 2-USDT币本位
-
- @NotBlank(message = "方向不能为空")
- @ApiModelProperty(value = "方向", example = "1")
- private Integer coinDirect;//方向 1-多 2-空
-
- @ApiModelProperty(value = "策略方向", example = "1")
- private Integer directStatus;//策略方向 1-顺势 2-逆势
-
- @ApiModelProperty(value = "货币对", example = "-SWAP")
- private String coinSymbol;//货币对(交易货币(币种标识))
-
- @ApiModelProperty(value = "用户id", example = "1")
- private Long MemberId;//用户ID
-
- @ApiModelProperty(value = "账户类型BTC-USD:实盘账户-true, 模拟账户-false", example = "false")
- private boolean accountType;
-
- @ApiModelProperty(value = "是否开启监控 0-否 1-是", example = "0")
- private Integer monitorStatus;//是否开启监控 0-未开启 1-已开启
-
- @ApiModelProperty(value = "杠杆倍率", example = "20")
- private String lever;//杠杆倍率
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/OperateCurrencyLeverDto.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/OperateCurrencyLeverDto.java
deleted file mode 100644
index 3c42ab9..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/OperateCurrencyLeverDto.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.verify.dto;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-/**
- * @author wzy
- * @date 2024-08-10
- **/
-@Data
-@ApiModel(value = "OperateCurrencyLeverDto", description = "倍率查询接收类")
-public class OperateCurrencyLeverDto {
-
- @ApiModelProperty(value = "apiMessageId", example = "1")
- private Long apiMessageId;//交易所ID
-
- @ApiModelProperty(value = "产品ID", example = "BTC-USDT")
- private String instId;//交易所名称
-
- @ApiModelProperty(value = "杠杆倍率", example = "20")
- private String lever;//杠杆倍率
-
- @ApiModelProperty(value = "交易所名称", example = "OKX")
- private String exchange;//交易所名称
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/TradeBigdataDto.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/TradeBigdataDto.java
deleted file mode 100644
index bb16168..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/dto/TradeBigdataDto.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.verify.dto;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import java.io.Serializable;
-
-@Data
-@ApiModel(value = "TradeBigdataDto", description = "交易大数据数据接收类")
-public class TradeBigdataDto implements Serializable {
- private static final long serialVersionUID = 1L;
- @ApiModelProperty(value = "数据产生时间", example = "2024-09-18 14:38:01")
- private String ts;//数据产生时间
-
- @ApiModelProperty(value = "卖出量", example = "1000")
- private String sellVol;//卖出量
-
- @ApiModelProperty(value = "买入量", example = "1000")
- private String buyVol;//买入量
-
- @ApiModelProperty(value = "多空人数比", example = "0.8")
- private String longShortAcctRatio;//多空人数比
-
- @ApiModelProperty(value = "持仓总量", example = "1000")
- private String oi;//持仓总量
-
- @ApiModelProperty(value = "交易总量", example = "1000")
- private String vol;//交易总量
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/enums/PublicStatusEnum.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/enums/PublicStatusEnum.java
deleted file mode 100644
index a267e11..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/enums/PublicStatusEnum.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.verify.enums;
-
-import java.math.BigDecimal;
-
-public class PublicStatusEnum {
-
- /**
- * API验证返回状态
- */
- public static final int API_MESSAGE_STATUS_SUCCESS = 1;
- public static final int API_MESSAGE_STATUS_FAIL = 0;
-
- /**
- * 货币对监控状态
- */
- //货币对监控状态开启
- public static final int CURRENCY_MONITOR_OPEN = 1;
-
- //货币对监控状态开启
- public static final int CURRENCY_ONLYSELL_OPEN = 1;
- //货币对监控状态关闭
-
- public static final int CURRENCY_MONITOR_CLOSE = 0;
- //是否开启过监听
- public static final int CURRENCY_MONITORED_OPEN = 1;
- //订单未完成
- public static final int ORDER_FINISH_STATUS_NO = 1;
- //订单已完成
- public static final int ORDER_FINISH_STATUS_YES = 2;
- //订单取消
- public static final int ORDER_FINISH_STATUS_CANEL = 3;
- //订单已卖出(针对买单)
- public static final int ORDER_FINISH_STATUS_SELL = 4;
- //订单下单失败
- public static final int ORDER_FINISH_STATUS_FAIL = 5;
-
- //其他方式卖出
- public static final int ORDER_FINISH_STATUS_OTHERSELL = 6;
-
- //买单
- public static final int ORDER_STATUS_BUY = 1;
-
- public static final BigDecimal ORDER_DOWN_RATIO= new BigDecimal("0.01");
- //买单
-
- public static final int ORDER_STATUS_SELL = 2;
-
-// public static final boolean AccountType = false;
-
- public static final int ORDER_FINISH_STATUS = 2;
-
- //订单卖出未锁定
- public static final int ORDER_SELL_LOCK_STATUS_NO = 0;
- //订单卖出已锁定
- public static final int ORDER_SELL_LOCK_STATUS_YES = 1;
-
- //定义超过开仓均价时需向下调整卖价的比率
- public static final BigDecimal HIGH_SELL_RATIO= new BigDecimal("1.1");
-
- //定义需要调整后的比率
- public static final BigDecimal HIGH_ADJUST_RATIO= new BigDecimal("1.03");
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/impl/OKXVerifyAccountServiceImpl.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/impl/OKXVerifyAccountServiceImpl.java
deleted file mode 100644
index fe5de75..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/impl/OKXVerifyAccountServiceImpl.java
+++ /dev/null
@@ -1,680 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.verify.impl;
-
-import cn.hutool.core.util.ObjectUtil;
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-import com.xcong.excoin.modules.okxNewPrice.utils.FebsException;
-import com.xcong.excoin.modules.okxNewPrice.utils.FebsResponse;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.DataUtil;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.Dto.QuantApiMessage;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.OKXAccount;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.enums.DefaultUrls;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.enums.HttpMethod;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils.OKXContants;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.verify.IVerifyAccountService;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.verify.dto.*;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.verify.enums.PublicStatusEnum;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.verify.vo.ApiAccountHoldVo;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.verify.vo.ApiPositionsInfoVo;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.verify.vo.ProductMessVo;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.verify.vo.SinglemarketVo;
-import com.xcong.excoin.modules.okxNewPrice.zhanghu.IApiMessageService;
-import com.xcong.excoin.modules.okxNewPrice.zhanghu.ZhangHuEnum;
-import lombok.RequiredArgsConstructor;
-import lombok.SneakyThrows;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-import java.math.BigDecimal;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.*;
-
-@Slf4j
-@Service("oKXVerifyAccount")
-@RequiredArgsConstructor
-public class OKXVerifyAccountServiceImpl implements IVerifyAccountService {
-
- private final IApiMessageService apiMessageService;
- @Value("${spring.OKEX.baseurl}")
- private String baseurl;
-
- @Override
- public OKXAccount initAccount(QuantApiMessage quantApiMessage) {
- return new OKXAccount(
- quantApiMessage.getAccountType().equalsIgnoreCase("true") ? DefaultUrls.USDM_PROD_URL : DefaultUrls.USDM_UAT_URL,
- quantApiMessage.getASecretkey(),
- quantApiMessage.getBSecretkey(),
- quantApiMessage.getPassPhrass(),
- !quantApiMessage.getAccountType().equalsIgnoreCase("true"));
-
- }
-
- @Override
- public OKXAccount initAccountV2(String apiKey, String secretKey, String passPhrass, boolean accountType) {
- return new OKXAccount(
- accountType ? DefaultUrls.USDM_PROD_URL : DefaultUrls.USDM_UAT_URL,
- apiKey,
- secretKey,
- passPhrass,
- !accountType);
- }
-
- @SneakyThrows
- @Override
- public FebsResponse verifyAccount(ApiMessageDto apiMessageDto) {
- if(validateAccount(apiMessageDto)){
- return new FebsResponse().success();
- }
- return new FebsResponse().fail();
- }
-
- @Override
- public FebsResponse getProductMess(ApiValidApiMessageDto apiValidApiMessageDto) {
- Long id = apiValidApiMessageDto.getId();
-
- QuantApiMessage quantApiMessage = apiMessageService.getApiMessage(ZhangHuEnum.JIAOYISUO.getValue());
- if(ObjectUtil.isEmpty(quantApiMessage)){
- throw new FebsException("参数错误");
- }
-
- OKXAccount okxAccount = this.initAccount(quantApiMessage);
- LinkedHashMap<String, Object> balanceParameters = new LinkedHashMap<>();
- balanceParameters.put("instType", "SWAP");
- if(StringUtils.isNotBlank(apiValidApiMessageDto.getCoinName())){
- balanceParameters.put("instId", apiValidApiMessageDto.getCoinName()+"-USD-SWAP");
- balanceParameters.put("instFamily", apiValidApiMessageDto.getCoinName()+"-USD");
- }
-
- String productStr = okxAccount.requestHandler.sendSignedRequest(okxAccount.baseUrl, OKXContants.INSTRUMENTS, balanceParameters, HttpMethod.GET, okxAccount.isSimluate());
- log.info("productStr:{}", productStr);
- JSONObject balanceJson = JSON.parseObject(productStr);
- JSONObject data = balanceJson.getJSONArray("data").getJSONObject(0);
-
- ProductMessVo productMessVo = new ProductMessVo();
- productMessVo.setLotSz(data.getString("lotSz"));
- productMessVo.setMinSz(data.getString("minSz"));
- productMessVo.setTickSz(data.getString("tickSz"));
- productMessVo.setCtVal(data.getString("ctVal"));
-
- return new FebsResponse().success().data(productMessVo);
- }
-
- @Override
- public FebsResponse getCurrenPrice(OperateCurrencyDto operateCurrencyDto) {
-
- QuantApiMessage quantApiMessage = apiMessageService.getApiMessage(ZhangHuEnum.JIAOYISUO.getValue());
- if(null == quantApiMessage){
- return new FebsResponse().fail().message("请先配置交易所");
- }
- OKXAccount okxAccount = this.initAccount(quantApiMessage);
-
- LinkedHashMap<String, Object> balanceParameters = new LinkedHashMap<>();
- if(StringUtils.isNotBlank(operateCurrencyDto.getCoinSymbol())){
- balanceParameters.put("instId", operateCurrencyDto.getCoinSymbol()+"-SWAP");
- }
- String ticker = okxAccount.requestHandler.sendSignedRequest(okxAccount.baseUrl, OKXContants.TICKER, balanceParameters, HttpMethod.GET, okxAccount.isSimluate());
-
- log.info("ticker================:{}", ticker);
-
- JSONObject jsonObject = JSON.parseObject(ticker);
- JSONObject data = jsonObject.getJSONArray("data").getJSONObject(0);
-
- SinglemarketVo singlemarketVo = new SinglemarketVo();
- singlemarketVo.setInstId(data.getString("instId"));
- singlemarketVo.setLast(data.getString("last"));
- singlemarketVo.setInstType(data.getString("instType"));
-
- return new FebsResponse().success().data(singlemarketVo);
- }
-
- @Override
- public FebsResponse getlever(OperateCurrencyLeverDto operateCurrencyLeverDto) {
-
- QuantApiMessage quantApiMessage = apiMessageService.getApiMessage(ZhangHuEnum.JIAOYISUO.getValue());
- if(ObjectUtil.isEmpty(quantApiMessage)){
- throw new FebsException("参数错误");
- }
-
- OKXAccount okxAccount = this.initAccount(quantApiMessage);
- LinkedHashMap<String, Object> balanceParameters = new LinkedHashMap<>();
- balanceParameters.put("instId", operateCurrencyLeverDto.getInstId());
- balanceParameters.put("mgnMode", "cross");
- String leverStr = okxAccount.requestHandler.sendSignedRequest(okxAccount.baseUrl, OKXContants.LEVERAGE, balanceParameters, HttpMethod.GET, okxAccount.isSimluate());
- log.info("leverStr:{}", leverStr);
- JSONObject leverStrJson = JSON.parseObject(leverStr);
-
- Set<String> leverage = new HashSet<>();
-
- JSONArray details = leverStrJson.getJSONArray("data");
- int size = details.size();
- for (int i = 0; i < size; i++) {
- leverage.add(details.getJSONObject(i).getString("lever"));
- }
-
-
- log.info("leverage:{}", leverage);
-
- return new FebsResponse().success().data(leverage);
- }
-
- @Override
- public FebsResponse setLever(OperateCurrencyLeverDto operateCurrencyLeverDto) {
-
- log.info("OKX_SET_LEVEL");
- log.info("operateCurrencyLeverDto:{}", operateCurrencyLeverDto);
-
- QuantApiMessage quantApiMessage = apiMessageService.getApiMessage(ZhangHuEnum.JIAOYISUO.getValue());
- if(ObjectUtil.isEmpty(quantApiMessage)){
- throw new FebsException("参数错误");
- }
-
- OKXAccount okxAccount = this.initAccount(quantApiMessage);
- LinkedHashMap<String, Object> balanceParameters = new LinkedHashMap<>();
- balanceParameters.put("instId", operateCurrencyLeverDto.getInstId()+"-SWAP");
- balanceParameters.put("mgnMode", "cross");
- balanceParameters.put("lever", operateCurrencyLeverDto.getLever());
- String setleverStr = okxAccount.requestHandler.sendSignedRequest(okxAccount.baseUrl, OKXContants.SETLEVERAGE, balanceParameters, HttpMethod.POST, okxAccount.isSimluate());
- log.info("setleverStr:{}", setleverStr);
- JSONObject leverStrJson = JSON.parseObject(setleverStr);
-
- String code = leverStrJson.getString("code");
- if("0".equals(code)||"59000".equals(code)){
- if("59000".equals(code)){
- return new FebsResponse().success().message("59000");
- }
- return new FebsResponse().success().message("设置杠杆成功");
- } else {
- return new FebsResponse().fail().message("设置杠杆失败");
- }
-
- }
-
- /**
- * 测试API链接
- *
- * @param quantApiMessage 包含API链接信息的DTO对象
- * @return 返回一个FebsResponse对象,包含验证结果和消息
- */
- @Override
- public FebsResponse testApiLink(QuantApiMessage quantApiMessage) {
- log.info("apiMessageDto:{}",quantApiMessage);
- try{
- // 初始化账户对象
- OKXAccount okxAccount = this.initAccount(quantApiMessage);
-
- // 创建用于存储余额查询参数的容器
- LinkedHashMap<String, Object> balanceParameters = new LinkedHashMap<>();
- log.info("okxAccount:{}",okxAccount);
-
- // 发送签名请求以获取账户余额
- String balance = okxAccount.requestHandler.sendSignedRequest(okxAccount.baseUrl, OKXContants.BALANCE, balanceParameters, HttpMethod.GET, okxAccount.isSimluate());
- log.info("balance:{}",balance);
-
- // 解析余额响应JSON
- JSONObject balanceJson = JSON.parseObject(balance);
-
- // 检查余额响应代码,如果为0则表示成功
- if("0".equals(balanceJson.getString("code").toString())){
- // 更新QuantApiMessage状态为成功
- HashMap<String, Integer> objectObjectHashMap = new HashMap<>();
- objectObjectHashMap.put("state", PublicStatusEnum.API_MESSAGE_STATUS_SUCCESS);
- return new FebsResponse().success().data(objectObjectHashMap).message("账号验证成功");
- }
-
- // 返回验证失败的消息
- return new FebsResponse().fail().message("账号验证失败");
- } catch (Exception e){
- // 异常情况下返回验证失败的消息
- return new FebsResponse().fail().message("账号验证失败");
- }
- }
-
- /**
- * 获取账户余额的方法
- *
- * @param apiAccountBalanceDto 包含账户余额查询信息的数据传输对象
- * @throws FebsException 当API消息信息未设置时抛出异常
- */
- @Override
- public void getAccountBalance(ApiAccountBalanceDto apiAccountBalanceDto) {
- // 提取API消息ID、账户类型和币种名称
- String coinName = apiAccountBalanceDto.getCoinName();
-
- // 根据ID查询API消息信息
- QuantApiMessage quantApiMessage = apiMessageService.getApiMessage(ZhangHuEnum.JIAOYISUO.getValue());
- // 检查API消息信息是否存在
- if(ObjectUtil.isEmpty(quantApiMessage)){
- throw new FebsException("API_MESSAGE信息未设置");
- }
-
- // 初始化账户对象
- OKXAccount okxAccount = this.initAccount(quantApiMessage);
-
- // 构建查询余额的参数
- LinkedHashMap<String, Object> balanceParameters = new LinkedHashMap<>();
- balanceParameters.put("ccy", coinName);
- // 发送签名请求获取余额信息
- String balance = okxAccount.requestHandler.sendSignedRequest(
- okxAccount.baseUrl,
- OKXContants.BALANCE,
- balanceParameters,
- HttpMethod.GET,
- okxAccount.isSimluate());
- // 记录余额信息日志
- log.info("ACCOUNT_BALANCE:{}", balance);
- JSONObject balanceJson = JSON.parseObject(balance);
- JSONObject data = balanceJson.getJSONArray("data").getJSONObject(0);
- JSONArray details = data.getJSONArray("details");
- for (int i = 0; i < details.size(); i++) {
- JSONObject jsonObject = details.getJSONObject(i);
- // 总权益
- String bal = jsonObject.getString("eq");
-// String upl = jsonObject.getString("upl");
- }
- }
-
- @Override
- public ApiAccountHoldVo getAccountBalanceInfo(ApiAccountBalanceInfoDto apiAccountBalanceInfoDto) {
- ApiAccountHoldVo apiAccountHoldVo = new ApiAccountHoldVo();
-
- // 提取API消息ID、账户类型和币种名称
- Long apiMessageId = apiAccountBalanceInfoDto.getId();
- String coinName = apiAccountBalanceInfoDto.getCoinName();
-
- // 根据ID查询API消息信息
- QuantApiMessage quantApiMessage = apiMessageService.getApiMessage(
- ZhangHuEnum.JIAOYISUO.getValue()
- );
- // 检查API消息信息是否存在
- if(ObjectUtil.isEmpty(quantApiMessage)){
- throw new FebsException("API_MESSAGE信息未设置");
- }
-
- // 初始化账户对象
- OKXAccount okxAccount = this.initAccount(quantApiMessage);
-
- // 构建查询余额的参数
- LinkedHashMap<String, Object> balanceParameters = new LinkedHashMap<>();
- balanceParameters.put("ccy", coinName);
- // 发送签名请求获取余额信息
- String balance = okxAccount.requestHandler.sendSignedRequest(
- okxAccount.baseUrl,
- OKXContants.BALANCE,
- balanceParameters,
- HttpMethod.GET,
- okxAccount.isSimluate());
- // 记录余额信息日志
- log.info("ACCOUNT_BALANCE:{}", balance);
- JSONObject balanceJson = JSON.parseObject(balance);
- JSONObject data = balanceJson.getJSONArray("data").getJSONObject(0);
- JSONArray details = data.getJSONArray("details");
- for (int i = 0; i < details.size(); i++) {
- JSONObject jsonObject = details.getJSONObject(i);
- log.info("OKX-HOLD:",jsonObject);
- // 总权益
- String bal = jsonObject.getString("eq");//币种总权益
- String cashBal = jsonObject.getString("cashBal");//币种余额
- String availEq = jsonObject.getString("availEq");//可用保证金
- String availBal = jsonObject.getString("availBal");//可用余额
- String frozenBal = jsonObject.getString("frozenBal");//币种占用金额
- String imr = jsonObject.getString("imr");//币种维度全仓占用保证金,适用于现货和合约模式且有全仓仓位时
- String mgnRatio = jsonObject.getString("mgnRatio");//币种全仓保证金率,衡量账户内某项资产风险的指标
- String mmr = jsonObject.getString("mmr");//币种维度全仓维持保证金
- String upl = jsonObject.getString("upl");//未实现盈亏
- apiAccountHoldVo.setTotalBalance(new BigDecimal(bal));
- apiAccountHoldVo.setTotalPercent(new BigDecimal(upl));
- apiAccountHoldVo.setAvailableBalance(new BigDecimal(availBal));
-// apiAccountHoldVo.setHoldBalance(new BigDecimal(mmr));
- apiAccountHoldVo.setHoldBalance(new BigDecimal(mmr));
- apiAccountHoldVo.setUseBalance(new BigDecimal(mmr));
- apiAccountHoldVo.setCashBal(new BigDecimal(cashBal));
- apiAccountHoldVo.setFrozenBal(new BigDecimal(frozenBal));
- apiAccountHoldVo.setAvailEq(new BigDecimal(availEq));
- apiAccountHoldVo.setMgnRatio(mgnRatio);
- }
- return apiAccountHoldVo;
- }
-
- @Override
- public ApiPositionsInfoVo getAccountPositionsInfo(ApiPositionsInfoDto apiPositionsInfoDto) {
-
- log.info("getAccountPositionsInfo:{}",apiPositionsInfoDto);
- ApiPositionsInfoVo apiPositionsInfoVo = new ApiPositionsInfoVo();
- // 提取API消息ID、账户类型和币种名称
- Long apiMessageId = apiPositionsInfoDto.getId();
- String coinName = apiPositionsInfoDto.getCoinName();
-
- // 根据ID查询API消息信息
- QuantApiMessage quantApiMessage = apiMessageService.getApiMessage(
- ZhangHuEnum.JIAOYISUO.getValue()
- );
- // 检查API消息信息是否存在
- if(ObjectUtil.isEmpty(quantApiMessage)){
- throw new FebsException("API_MESSAGE信息未设置");
- }
-
- // 初始化账户对象
- OKXAccount okxAccount = this.initAccount(quantApiMessage);
-
- // 构建查询余额的参数
- LinkedHashMap<String, Object> positionsParameters = new LinkedHashMap<>();
- positionsParameters.put("instType", "SWAP");
- positionsParameters.put("instId", coinName+"-USDT-SWAP");
- try{
- // 发送签名请求获取余额信息
- String positions = okxAccount.requestHandler.sendSignedRequest(
- okxAccount.baseUrl,
- OKXContants.POSITIONS,
- positionsParameters,
- HttpMethod.GET,
- okxAccount.isSimluate());
- // 记录余额信息日志
- log.info("POSITIONS:{}", positions);
- JSONObject balanceJson = JSON.parseObject(positions);
- String code = balanceJson.getString("code");
- if(!"0".equals(code)){
- return null;
- }
- JSONObject data = balanceJson.getJSONArray("data").getJSONObject(0);
- String avgPx = data.getString("avgPx");
- String upl = data.getString("upl");
- String markPx = data.getString("markPx");
- String bePx = data.getString("bePx");
- String pos = data.getString("pos");
- apiPositionsInfoVo.setUpl(new BigDecimal(ObjectUtil.isNotEmpty(upl)? DataUtil.getDecimalDigits8(upl):"0"));
- apiPositionsInfoVo.setAvgPx(new BigDecimal(ObjectUtil.isNotEmpty(avgPx)?DataUtil.getDecimalDigits8(avgPx):"0"));
- apiPositionsInfoVo.setMarkPx(new BigDecimal(ObjectUtil.isNotEmpty(markPx)?DataUtil.getDecimalDigits8(markPx):"0"));
- apiPositionsInfoVo.setBePx(new BigDecimal(ObjectUtil.isNotEmpty(bePx)?DataUtil.getDecimalDigits8(bePx):"0"));
- apiPositionsInfoVo.setPos(new BigDecimal(ObjectUtil.isNotEmpty(pos)?DataUtil.getDecimalDigits8(pos):"0"));
- apiPositionsInfoVo.setCoinName(coinName);
- return apiPositionsInfoVo;
- }catch (Exception e){
- return null;
- }
-
- }
-
- @Override
- public FebsResponse getTradeData(ApiValidApiMessageDto apiValidApiMessageDto) {
-
- log.info("getTradeData:{}", apiValidApiMessageDto);
- QuantApiMessage quantApiMessage = apiMessageService.getApiMessage(
- ZhangHuEnum.JIAOYISUO.getValue()
- );
- // 检查API消息信息是否存在
- if(ObjectUtil.isEmpty(quantApiMessage)){
- throw new FebsException("API_MESSAGE信息未设置");
- }
-
- // 初始化账户对象
- OKXAccount okxAccount = this.initAccount(quantApiMessage);
-
- // 构建查询余额的参数
- LinkedHashMap<String, Object> positionsParameters = new LinkedHashMap<>();
- try{
- // 发送签名请求获取余额信息
- String tradedata = okxAccount.requestHandler.sendSignedRequest(
- okxAccount.baseUrl,
- OKXContants.TRADEDATA,
- positionsParameters,
- HttpMethod.GET,
- okxAccount.isSimluate());
- // 记录余额信息日志
- log.info("tradedata:{}", tradedata);
- JSONObject balanceJson = JSON.parseObject(tradedata);
- String code = balanceJson.getString("code");
- if(!"0".equals(code)){
- return null;
- }
- JSONObject dataObject = balanceJson.getJSONObject("data");
- JSONArray contractArray = dataObject.getJSONArray("contract");
- List<String> contractList = new ArrayList<>();
- if (contractArray != null) {
- for (int i = 0; i < contractArray.size(); i++) {
- contractList.add(contractArray.getString(i));
- }
- }
- return new FebsResponse().data(contractList);
- }catch (Exception e){
- return null;
- }
- }
-
- @Override
- public FebsResponse getBuySellSituation(ApiValidApiMessageDto apiValidApiMessageDto) {
- log.info("getBuySellSituation:{}", apiValidApiMessageDto);
-
- String coinName = apiValidApiMessageDto.getCoinName();// String coinName = "BTC";
- QuantApiMessage quantApiMessage = apiMessageService.getApiMessage(
- ZhangHuEnum.JIAOYISUO.getValue()
- );
- // 检查API消息信息是否存在
- if(ObjectUtil.isEmpty(quantApiMessage)){
- throw new FebsException("API_MESSAGE信息未设置");
- }
-
- // 初始化账户对象
- OKXAccount okxAccount = this.initAccount(quantApiMessage);
-
- // 构建查询余额的参数
- LinkedHashMap<String, Object> positionsParameters = new LinkedHashMap<>();
- positionsParameters.put("period", "4H");
- positionsParameters.put("instId", coinName+"-USDT-SWAP");
- positionsParameters.put("limit", "1");
- try{
- // 发送签名请求获取余额信息
- String buySellSituation = okxAccount.requestHandler.sendSignedRequest(
- okxAccount.baseUrl,
- OKXContants.BUYSELLSITUATION,
- positionsParameters,
- HttpMethod.GET,
- okxAccount.isSimluate());
- // 记录余额信息日志
- log.info("buySellSituation:{}", buySellSituation);
- JSONObject balanceJson = JSON.parseObject(buySellSituation);
- String code = balanceJson.getString("code");
- if(!"0".equals(code)){
- return null;
- }
- JSONArray dataArray = balanceJson.getJSONArray("data");
- TradeBigdataDto tradeBigdataDto = new TradeBigdataDto();
- if (dataArray != null) {
- JSONArray innerArray = dataArray.getJSONArray(0);
- String sellVol = innerArray.getString(1);
- String buyVol = innerArray.getString(2);
- String ts = innerArray.getString(0);
- tradeBigdataDto.setBuyVol(buyVol);
- tradeBigdataDto.setSellVol(sellVol);
- tradeBigdataDto.setTs(ts);
- }
- return new FebsResponse().data(tradeBigdataDto);
- }catch (Exception e){
- return null;
- }
- }
-
- @Override
- public FebsResponse getPositionRatio(ApiValidApiMessageDto apiValidApiMessageDto) {
- log.info("getPositionRatio:{}", apiValidApiMessageDto);
-
- String coinName = apiValidApiMessageDto.getCoinName();// String coinName = "BTC";
- QuantApiMessage quantApiMessage = apiMessageService.getApiMessage(
- ZhangHuEnum.JIAOYISUO.getValue()
- );
- // 检查API消息信息是否存在
- if(ObjectUtil.isEmpty(quantApiMessage)){
- throw new FebsException("API_MESSAGE信息未设置");
- }
-
- // 初始化账户对象
- OKXAccount okxAccount = this.initAccount(quantApiMessage);
-
- // 构建查询余额的参数
- LinkedHashMap<String, Object> positionsParameters = new LinkedHashMap<>();
- positionsParameters.put("period", "4H");
- positionsParameters.put("instId", coinName+"-USDT-SWAP");
- positionsParameters.put("limit", "1");
- try{
- // 发送签名请求获取余额信息
- String positionRatio = okxAccount.requestHandler.sendSignedRequest(
- okxAccount.baseUrl,
- OKXContants.POSITIONRATIO,
- positionsParameters,
- HttpMethod.GET,
- okxAccount.isSimluate());
- // 记录余额信息日志
- log.info("positionRatio:{}", positionRatio);
- JSONObject balanceJson = JSON.parseObject(positionRatio);
- String code = balanceJson.getString("code");
- if(!"0".equals(code)){
- return null;
- }
- JSONArray dataArray = balanceJson.getJSONArray("data");
- TradeBigdataDto tradeBigdataDto = new TradeBigdataDto();
- if (dataArray != null) {
- JSONArray innerArray = dataArray.getJSONArray(0);
- String longShortAcctRatio = innerArray.getString(1);
- String ts = innerArray.getString(0);
- tradeBigdataDto.setLongShortAcctRatio(longShortAcctRatio);
- tradeBigdataDto.setTs(ts);
- }
- return new FebsResponse().data(tradeBigdataDto);
- }catch (Exception e){
- return null;
- }
- }
-
- @Override
- public FebsResponse getPositionTradingvolume(ApiValidApiMessageDto apiValidApiMessageDto) {
- log.info("getPositionTradingvolume:{}", apiValidApiMessageDto);
-
- String coinName = apiValidApiMessageDto.getCoinName();// String coinName = "BTC";
- QuantApiMessage quantApiMessage = apiMessageService.getApiMessage(
- ZhangHuEnum.JIAOYISUO.getValue()
- );
- // 检查API消息信息是否存在
- if(ObjectUtil.isEmpty(quantApiMessage)){
- throw new FebsException("API_MESSAGE信息未设置");
- }
-
- // 初始化账户对象
- OKXAccount okxAccount = this.initAccount(quantApiMessage);
-
- // 构建查询余额的参数
- LinkedHashMap<String, Object> positionsParameters = new LinkedHashMap<>();
- positionsParameters.put("period", "1D");
- positionsParameters.put("ccy", coinName);
- positionsParameters.put("limit", "1");
- try{
- // 发送签名请求获取余额信息
- String positionTradingvolume = okxAccount.requestHandler.sendSignedRequest(
- okxAccount.baseUrl,
- OKXContants.POSITIONVOLUME,
- positionsParameters,
- HttpMethod.GET,
- okxAccount.isSimluate());
- // 记录余额信息日志
- log.info("positionTradingvolume:{}", positionTradingvolume);
- JSONObject balanceJson = JSON.parseObject(positionTradingvolume);
- String code = balanceJson.getString("code");
- if(!"0".equals(code)){
- return null;
- }
- JSONArray dataArray = balanceJson.getJSONArray("data");
- TradeBigdataDto tradeBigdataDto = new TradeBigdataDto();
- if (dataArray != null) {
- JSONArray innerArray = dataArray.getJSONArray(0);
- String oi = innerArray.getString(1);
- String vol = innerArray.getString(2);
- String ts = innerArray.getString(0);
- tradeBigdataDto.setOi(oi);
- tradeBigdataDto.setVol(vol);
- tradeBigdataDto.setTs(ts);
- }
- return new FebsResponse().data(tradeBigdataDto);
- }catch (Exception e){
- return null;
- }
- }
-
- //此方法用于验证账号
- private boolean validateAccount(ApiMessageDto apiMessageDto) throws Exception {
- boolean flag = false;
-// try {
- //设置请求
- URL url = new URL(baseurl+"/api/v5/account/balance");
- HttpURLConnection connection = (HttpURLConnection) url.openConnection();
-
- //设置请求方法
- connection.setRequestMethod("GET");
-
- //设置必需的头信息
- long now = System.currentTimeMillis() / 1000;
- String timestamp = String.valueOf(now);
- String preHash = timestamp + "GET" + "/api/v5/account/balance"; //URL路径
- String signature = generateSignature(preHash, apiMessageDto.getBSecretkey());
-
- connection.setRequestProperty("OK-ACCESS-KEY", apiMessageDto.getASecretkey());
- connection.setRequestProperty("OK-ACCESS-SIGN", signature);
- connection.setRequestProperty("OK-ACCESS-TIMESTAMP", timestamp);
- connection.setRequestProperty("OK-ACCESS-PASSPHRASE", apiMessageDto.getPassPhrass());
-
- connection.setRequestProperty("Content-Type", "application/json");
-
- //执行请求
- int responseCode = connection.getResponseCode();
- BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
- String inputLine;
- StringBuffer response = new StringBuffer();
-
- while ((inputLine = in.readLine()) != null) {
- response.append(inputLine);
- }
- in.close();
-
- //打印响应
- if(responseCode == 200){
- flag = true;
- } else {
- flag = false;
- }
-// } catch (Exception e) {
-// //在此打印异常信息
-// log.error("Exception: " + e.getMessage());
-// flag = false;
-// }
- return flag;
- }
-
- //此方法用于生成签名
- private static String generateSignature(String preHash, String secretKey) throws Exception{
- javax.crypto.Mac sha256_HMAC = javax.crypto.Mac.getInstance("HmacSHA256");
- javax.crypto.spec.SecretKeySpec secret_key = new javax.crypto.spec.SecretKeySpec(secretKey.getBytes(), "HmacSHA256");
- sha256_HMAC.init(secret_key);
- return Base64.getEncoder().encodeToString(sha256_HMAC.doFinal(preHash.getBytes()));
- }
-
- public static void main(String[] args) throws Exception {
- String buySellSituation = "{\"code\":\"0\",\"data\":[[\"1718164800000\",\"52000.2999999999975\",\"60671.0000000000019\"]],\"msg\":\"\"}";
- log.info("buySellSituation:{}", buySellSituation);
- JSONObject balanceJson = JSON.parseObject(buySellSituation);
- JSONArray dataArray = balanceJson.getJSONArray("data");
- TradeBigdataDto tradeBigdataDto = new TradeBigdataDto();
- if (dataArray != null) {
- JSONArray innerArray = dataArray.getJSONArray(0);
- String sellVol = innerArray.getString(1);
- String buyVol = innerArray.getString(2);
- String ts = innerArray.getString(0);
- tradeBigdataDto.setBuyVol(buyVol);
- tradeBigdataDto.setSellVol(sellVol);
- tradeBigdataDto.setTs(ts);
- }
- System.out.println(tradeBigdataDto.toString());
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/vo/ApiAccountHoldVo.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/vo/ApiAccountHoldVo.java
deleted file mode 100644
index 88c5367..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/vo/ApiAccountHoldVo.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.verify.vo;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import java.math.BigDecimal;
-
-@Data
-@ApiModel(value = "ApiAccountHoldVo", description = "列表")
-public class ApiAccountHoldVo {
-
- @ApiModelProperty(value = "总金额")
- private BigDecimal totalBalance = BigDecimal.ZERO;
- @ApiModelProperty(value = "盈亏比")
- private BigDecimal totalPercent = BigDecimal.ZERO;
- @ApiModelProperty(value = "可用金额")
- private BigDecimal availableBalance = BigDecimal.ZERO;
- @ApiModelProperty(value = "持仓金额")
- private BigDecimal holdBalance = BigDecimal.ZERO;
- @ApiModelProperty(value = "占用保证金")
- private BigDecimal useBalance = BigDecimal.ZERO;
-
- @ApiModelProperty(value = "币种余额")
- private BigDecimal cashBal = BigDecimal.ZERO;
-
- @ApiModelProperty(value = "可用保证金")
- private BigDecimal availEq = BigDecimal.ZERO;
-
- @ApiModelProperty(value = "币种占用金额")
- private BigDecimal frozenBal = BigDecimal.ZERO;
-
- @ApiModelProperty(value = "币种全仓保证金率")
- private String mgnRatio;
-
- @ApiModelProperty(value = "当前所需起始保证金")
- private BigDecimal initialMargin;
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/vo/ApiPositionsInfoVo.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/vo/ApiPositionsInfoVo.java
deleted file mode 100644
index 671c1c8..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/vo/ApiPositionsInfoVo.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.verify.vo;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import java.io.Serializable;
-import java.math.BigDecimal;
-
-@Data
-@ApiModel(value = "ApiPositionsInfoVo", description = "持仓信息")
-public class ApiPositionsInfoVo implements Serializable {
-
- @ApiModelProperty(value = "开仓均价")
- private BigDecimal avgPx = BigDecimal.ZERO;
- @ApiModelProperty(value = "未实现盈亏")
- private BigDecimal upl = BigDecimal.ZERO;
- @ApiModelProperty(value = "盈亏平衡价")
- private BigDecimal bePx = BigDecimal.ZERO;
- @ApiModelProperty(value = "标记价格")
- private BigDecimal markPx = BigDecimal.ZERO;
- @ApiModelProperty(value = "持仓数量")
- private BigDecimal pos = BigDecimal.ZERO;
- @ApiModelProperty(value = "币种")
- private String coinName;
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/vo/ProductMessVo.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/vo/ProductMessVo.java
deleted file mode 100644
index 6c42884..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/vo/ProductMessVo.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.verify.vo;
-
-import lombok.Data;
-
-@Data
-public class ProductMessVo {
- // 币种
- private String symbol;
-
- // 下单价格精度
- private String tickSz;
-
- // 下单数量精度
- private String lotSz;
-
- // 最小下单数量
- private String minSz;
-
- // 合约面值
- private String ctVal;
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/vo/SinglemarketVo.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/vo/SinglemarketVo.java
deleted file mode 100644
index b169a1c..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxpi/verify/vo/SinglemarketVo.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.okxpi.verify.vo;
-
-import lombok.Data;
-
-@Data
-public class SinglemarketVo {
- // 产品类型
- private String instType;
-
- // 产品ID
- private String instId;
-
- // 最新成交价
- private String last;
-
- // 最新成交的数量,0 代表没有成交量
- private String lastSz;
-
- // 卖一价
- private String askPx;
-
- // 卖一价对应的数量
- private String askSz;
-
- // 买一价
- private String bidPx;
-
- // 买一价对应的数量
- private String bidSz;
-
- // 24小时开盘价
- private String open24h;
-
- // 24小时最高价
- private String high24h;
-
- // 24小时最低价
- private String low24h;
-
- // 24小时成交量,以币为单位
- // 如果是衍生品合约,数值为交易货币的数量。
- // 如果是币币/币币杠杆,数值为计价货币的数量。
- private String volCcy24h;
-
- // 24小时成交量,以张为单位
- // 如果是衍生品合约,数值为合约的张数。
- // 如果是币币/币币杠杆,数值为交易货币的数量。
- private String vol24h;
-
- // UTC+0 时开盘价
- private String sodUtc0;
-
- // UTC+8 时开盘价
- private String sodUtc8;
-
- // ticker数据产生时间,Unix时间戳的毫秒数格式,如 1597026383085
- private String ts;
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/utils/FebsException.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/utils/FebsException.java
deleted file mode 100644
index 98219f7..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/utils/FebsException.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.utils;
-
-/**
- * FEBS系统内部异常
- *
- * @author MrBird
- */
-public class FebsException extends RuntimeException {
-
- private static final long serialVersionUID = -994962710559017255L;
-
- public FebsException(String message) {
- super(message);
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/utils/FebsResponse.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/utils/FebsResponse.java
deleted file mode 100644
index c0e8d12..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/utils/FebsResponse.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.utils;
-
-import org.springframework.http.HttpStatus;
-
-import java.util.HashMap;
-
-/**
- * @author MrBird
- */
-public class FebsResponse extends HashMap<String, Object> {
-
- private static final long serialVersionUID = -8713837118340960775L;
-
- public FebsResponse code(HttpStatus status) {
- this.put("code", status.value());
- return this;
- }
-
- public FebsResponse message(String message) {
- this.put("message", message);
- return this;
- }
-
- public FebsResponse data(Object data) {
- this.put("data", data);
- return this;
- }
-
- public FebsResponse success() {
- this.code(HttpStatus.OK);
- return this;
- }
-
- public FebsResponse fail() {
- this.code(HttpStatus.INTERNAL_SERVER_ERROR);
- return this;
- }
-
- @Override
- public FebsResponse put(String key, Object value) {
- super.put(key, value);
- return this;
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/utils/SSLConfig.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/utils/SSLConfig.java
deleted file mode 100644
index 2187807..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/utils/SSLConfig.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.utils;
-
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLContext;
-import java.security.SecureRandom;
-
-/**
- * @author Administrator
- */
-public class SSLConfig {
- public static void configureSSL() {
- try {
- // 配置SSL上下文
- SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
- sslContext.init(null, null, new SecureRandom());
-
- // 设置默认SSL套接字工厂
- HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/utils/SignUtils.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/utils/SignUtils.java
deleted file mode 100644
index 87dfb2c..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/utils/SignUtils.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.utils;
-
-import lombok.extern.slf4j.Slf4j;
-
-import javax.crypto.Mac;
-import javax.crypto.spec.SecretKeySpec;
-import java.util.Base64;
-
-
-@Slf4j
-public class SignUtils {
-
- public static String signRest(String secretKey, String timestamp, String method, String path, String body) {
- String str = String.format("%s%s%s%s",
- timestamp, // timestamp
- method, // method GET/POST
- path, // requestPath
- body // body
- );
- try {
- return Base64.getEncoder().encodeToString(hmacSHA256(secretKey.getBytes(), str.getBytes()));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
-
-
- /**
- * HmacSHA256算法,返回的结果始终是32位
- *
- * @param key 加密的键,可以是任何数据
- * @param content 待加密的内容
- * @return 加密后的内容
- * @throws Exception ex
- */
- public static byte[] hmacSHA256(byte[] key, byte[] content) throws Exception {
- Mac hmacSha256 = Mac.getInstance("HmacSHA256");
- hmacSha256.init(new SecretKeySpec(key, 0, key.length, "HmacSHA256"));
- return hmacSha256.doFinal(content);
- }
-
- public static String signWebsocket(String timestamp, String secretKey) {
- String str = String.format("%s%s%s",
- timestamp,
- "GET",
- "/users/self/verify");
- try {
- return Base64.getEncoder().encodeToString(hmacSHA256(secretKey.getBytes(), str.getBytes()));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/utils/WsMapBuild.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/utils/WsMapBuild.java
deleted file mode 100644
index bb31a95..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/utils/WsMapBuild.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.utils;
-
-import cn.hutool.core.util.StrUtil;
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.CoinEnums;
-import lombok.extern.slf4j.Slf4j;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.Map;
-
-/**
- * @author Administrator
- */
-@Slf4j
-public class WsMapBuild {
-
- public static boolean saveBigDecimalToMap(Map<String,BigDecimal> accountMap, String key, BigDecimal value) {
- try {
- accountMap.put(key, value);
- return true;
- } catch (Exception e) {
- log.error("保存账户数据到MAP 失败", e);
- return false;
- }
- }
-
- public static boolean saveStringToMap(Map<String,String> accountMap, String key, String value) {
- try {
- accountMap.put(key, value);
- return true;
- } catch (Exception e) {
- log.error("保存账户数据到MAP 失败", e);
- return false;
- }
- }
-
-
- /**
- * 安全地将字符串解析为 BigDecimal 类型
- *
- * @param value 字符串数值
- * @return 解析后的 BigDecimal 对象,若解析失败则返回 null
- */
- public static BigDecimal parseBigDecimalSafe(String value) {
- if (value == null || value.isEmpty()) {
- return new BigDecimal(0);
- }
- return new BigDecimal(value).setScale(Integer.parseInt(CoinEnums.TICKSZ.getCode()), RoundingMode.DOWN);
- }
-
- /**
- * 安全地将字符串解析为 BigDecimal 类型
- *
- * @param value 字符串数值
- * @return 解析后的 BigDecimal 对象,若解析失败则返回 null
- */
- public static String parseStringSafe(String value) {
- if (value == null || value.isEmpty()) {
- return "0";
- }
- return value;
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/utils/WsParamBuild.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/utils/WsParamBuild.java
deleted file mode 100644
index 082e530..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/utils/WsParamBuild.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.utils;
-
-import cn.hutool.core.util.StrUtil;
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-
-import java.math.BigDecimal;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Random;
-
-/**
- * @author Administrator
- */
-public class WsParamBuild {
-
- public static String getOrderNum(String prefix) {
- SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
- String dd=df.format(new Date());
- if (StrUtil.isNotBlank(prefix)) {
- return prefix+dd+getRandomNum(5);
- }
- return dd+getRandomNum(5);
- }
-
- public static String getRandomNum(int length) {
- String str = "0123456789";
- Random random = new Random();
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < length; ++i) {
- int number = random.nextInt(str.length());
- sb.append(str.charAt(number));
- }
-
- return sb.toString();
- }
-
- public static JSONObject buildJsonObject(String connId, String option, JSONArray argsArray) {
- JSONObject jsonObject = new JSONObject();
- if (StrUtil.isNotEmpty(connId)){
- jsonObject.put("id", connId);
- }
- jsonObject.put("op", option);
- jsonObject.put("args", argsArray);
- return jsonObject;
- }
-
-
- /**
- * 计算购买合约的数量
- *
- * USDT 币本位合约
- * 公式:张数 = 保证金 / (面值 * 标记价格 / 杠杆倍数)
- *
- * @param margin 用户的保证金金额
- * @param leverage 杠杆倍数
- * @param faceValue 合约面值
- * @param markPrice 标记价格
- * @param minLotSz 最小下单精度
- * @return 返回用户可以购买的合约数量
- */
- public static BigDecimal buyCnt(BigDecimal margin, BigDecimal leverage, BigDecimal faceValue, BigDecimal markPrice, int minLotSz) {
- if (margin.compareTo(BigDecimal.ZERO) <= 0 ||
- leverage.compareTo(BigDecimal.ZERO) <= 0 ||
- faceValue.compareTo(BigDecimal.ZERO) <= 0 ||
- markPrice.compareTo(BigDecimal.ZERO) <= 0) {
- return BigDecimal.ZERO;
- }
-
- BigDecimal divisor = markPrice.divide(leverage, 10, BigDecimal.ROUND_DOWN);
- BigDecimal denominator = faceValue.multiply(divisor);
- return margin.divide(denominator, minLotSz, BigDecimal.ROUND_DOWN);
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/wangge/WangGeEnum.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/wangge/WangGeEnum.java
deleted file mode 100644
index 24f6f87..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/wangge/WangGeEnum.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.wangge;
-
-/**
- * @author Administrator
- * 网格数据枚举 数据
- * todo 后期考虑优化为可配置项
- */
-
-public enum WangGeEnum {
-
- XIAOSHU_WEISHU("网格价格小数位数", "2"),
- JIAGE_SHANGXIAN("网格上限", "3500"),
- JIAGE_XIAXIAN("网格下限", "2500"),
- JIAN_JU("网格间距", "5")
- ;
-
- private String name;
-
- private String value;
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getValue() {
- return value;
- }
-
- public void setValue(String value) {
- this.value = value;
- }
-
- private WangGeEnum(String name, String value) {
- this.name = name;
- this.value = value;
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/wangge/WangGeQueue.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/wangge/WangGeQueue.java
deleted file mode 100644
index 85dddea..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/wangge/WangGeQueue.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.wangge;
-
-import com.xcong.excoin.rabbit.pricequeue.AscBigDecimal;
-import com.xcong.excoin.rabbit.pricequeue.DescBigDecimal;
-
-import java.util.concurrent.PriorityBlockingQueue;
-
-/**
- * 网格交易队列管理类
- *
- * 用于管理系统中各种网格交易相关的优先级阻塞队列,
- * 包括完整的网格队列、平仓队列和开仓队列。
- *
- * @author Administrator
- */
-public class WangGeQueue {
-
- //------------------------------------------------------------------------------------------------------------------
- //------------------------------------------------------------------------------------------------------------------
- // todo 系统启动后,初始化网格队列
- /**
- * 完整的网格 头元素最小
- */
- public static PriorityBlockingQueue<AscBigDecimal> QUEUE_ASC = null;
-
-
- //------------------------------------------------------------------------------------------------------------------
- //------------------------------------------------------------------------------------------------------------------
- // todo 当用户下了第一单后,根据开仓价格初始化网格平仓队列和开仓队列
- /**
- * 网格平仓队列 头元素最小
- */
- public static PriorityBlockingQueue<AscBigDecimal> QUEUE_PINGCANG_ASC = null;
-
- /**
- * 网格开仓队列 头元素最大
- */
- public static PriorityBlockingQueue<DescBigDecimal> QUEUE_KAICANG_DESC = null;
-
- /**
- * 获取完整的网格队列(升序)
- * 如果队列未初始化则创建新的优先级阻塞队列
- *
- * @return 返回升序排列的PriorityBlockingQueue队列,队列头部元素最小
- */
- public static PriorityBlockingQueue<AscBigDecimal> getQueueAsc() {
- if (QUEUE_ASC == null) {
- QUEUE_ASC = new PriorityBlockingQueue<AscBigDecimal>();
- }
- return QUEUE_ASC;
- }
-
- /**
- * 获取网格平仓队列(升序)
- * 如果队列未初始化则创建新的优先级阻塞队列
- *
- * @return 返回升序排列的PriorityBlockingQueue队列,队列头部元素最小
- */
- public static PriorityBlockingQueue<AscBigDecimal> getPingCang() {
- if (QUEUE_PINGCANG_ASC == null) {
- QUEUE_PINGCANG_ASC = new PriorityBlockingQueue<AscBigDecimal>();
- }
- return QUEUE_PINGCANG_ASC;
- }
-
- /**
- * 获取网格开仓队列(降序)
- * 如果队列未初始化则创建新的优先级阻塞队列
- *
- * @return 返回降序排列的PriorityBlockingQueue队列,队列头部元素最大
- */
- public static PriorityBlockingQueue<DescBigDecimal> getKaiCang() {
- if (QUEUE_KAICANG_DESC == null) {
- QUEUE_KAICANG_DESC = new PriorityBlockingQueue<DescBigDecimal>();
- }
- return QUEUE_KAICANG_DESC;
- }
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/wangge/WangGeService.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/wangge/WangGeService.java
deleted file mode 100644
index 9b240bb..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/wangge/WangGeService.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.wangge;
-
-import com.xcong.excoin.rabbit.pricequeue.AscBigDecimal;
-import com.xcong.excoin.rabbit.pricequeue.DescBigDecimal;
-
-import java.math.BigDecimal;
-import java.util.concurrent.PriorityBlockingQueue;
-
-/**
- * 网格交易服务接口
- * 定义了网格交易的核心操作方法,包括初始化网格、开仓和平仓等操作
- * @author Administrator
- */
-public interface WangGeService {
-
- /**
- * 初始化网格交易
- * 创建并初始化用于网格交易的价格队列,按照价格升序排列
- * @return 初始化结果信息,返回按价格升序排列的阻塞队列
- */
- PriorityBlockingQueue<AscBigDecimal> initWangGe();
-
- /**
- * 初始化开仓操作
- * 根据指定价格初始化开仓队列,将开仓价格点加入到价格队列中
- * @param jiaGe 开仓价格
- * @param queueAsc 价格队列,用于存储按升序排列的价格点
- */
- PriorityBlockingQueue<DescBigDecimal> initKaiCang(BigDecimal jiaGe, PriorityBlockingQueue<AscBigDecimal> queueAsc);
-
- /**
- * 初始化平仓操作
- * 根据指定价格初始化平仓队列,将平仓价格点加入到价格队列中
- * @param jiaGe 开仓价格
- * @param queueAsc 价格队列,用于存储按升序排列的价格点
- */
- PriorityBlockingQueue<AscBigDecimal> initPingCang(BigDecimal jiaGe, PriorityBlockingQueue<AscBigDecimal> queueAsc);
-
-
-}
-
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/wangge/WangGeServiceImpl.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/wangge/WangGeServiceImpl.java
deleted file mode 100644
index 02371a7..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/wangge/WangGeServiceImpl.java
+++ /dev/null
@@ -1,142 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.wangge;
-
-import com.xcong.excoin.rabbit.pricequeue.AscBigDecimal;
-import com.xcong.excoin.rabbit.pricequeue.DescBigDecimal;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.concurrent.PriorityBlockingQueue;
-
-/**
- * 网格交易服务实现类,用于初始化价格网格、开仓和平仓操作。
- *
- * @author Administrator
- */
-@Slf4j
-@Service
-@RequiredArgsConstructor
-public class WangGeServiceImpl implements WangGeService {
-
- /**
- * 初始化价格网格队列。根据配置的价格上限、下限和间隔生成一系列价格点,
- * 并将这些价格点存入升序优先阻塞队列中。
- *
- * @return 返回初始化完成的升序价格队列;若初始化失败则返回null
- */
- @Override
- public PriorityBlockingQueue<AscBigDecimal> initWangGe() {
- log.info("网格初始化中");
- PriorityBlockingQueue<AscBigDecimal> queueAsc = WangGeQueue.getQueueAsc();
- queueAsc.clear();
-
- String shangxianValue = WangGeEnum.JIAGE_SHANGXIAN.getValue();
- String xiaxianValue = WangGeEnum.JIAGE_XIAXIAN.getValue();
- String jianjuValue = WangGeEnum.JIAN_JU.getValue();
- String weishuValueStr = WangGeEnum.XIAOSHU_WEISHU.getValue();
-
- try {
- BigDecimal shangxian = new BigDecimal(shangxianValue);
- BigDecimal xiaxian = new BigDecimal(xiaxianValue);
- BigDecimal jianju = new BigDecimal(jianjuValue);
-
- if (jianju.compareTo(BigDecimal.ZERO) == 0) {
- log.error("价格间隔不能为0");
- return null;
- }
-
- int weishu = Integer.parseInt(weishuValueStr);
- BigDecimal diff = shangxian.subtract(xiaxian);
- int count = diff.divide(jianju, 0, RoundingMode.DOWN).intValue();
-
- BigDecimal currentStep = BigDecimal.ZERO;
- for (int i = 0; i <= count; i++) {
- BigDecimal stepMultiplier = currentStep.multiply(jianju);
- BigDecimal wangGeJiaGe = xiaxian.add(stepMultiplier).setScale(weishu, RoundingMode.DOWN);
- AscBigDecimal ascBigDecimal = new AscBigDecimal(wangGeJiaGe.toString());
- queueAsc.add(ascBigDecimal);
- currentStep = currentStep.add(BigDecimal.ONE);
- }
-
- if (queueAsc.isEmpty()) {
- log.info("网格初始化失败");
- return null;
- }
-
- log.info("网格初始化成功");
- return queueAsc;
- } catch (NumberFormatException e) {
- log.error("解析价格参数失败", e);
- return null;
- } catch (Exception e) {
- log.error("初始化网格发生未知异常", e);
- return null;
- }
- }
-
- /**
- * 根据当前价格初始化开仓队列。遍历已有的升序价格队列,
- * 将小于当前价格的所有价格点加入降序的开仓队列中。
- *
- * @param jiaGe 当前价格
- * @param queueAsc 已初始化的价格升序队列
- */
- @Override
- public PriorityBlockingQueue<DescBigDecimal> initKaiCang(BigDecimal jiaGe, PriorityBlockingQueue<AscBigDecimal> queueAsc) {
- PriorityBlockingQueue<DescBigDecimal> queueKaiCang = WangGeQueue.getKaiCang();
- queueKaiCang.clear();
-
- AscBigDecimal now = new AscBigDecimal(jiaGe.toString());
-
- for (AscBigDecimal ascBigDecimal : queueAsc) {
- if (ascBigDecimal.compareTo(now) < 0) {
- DescBigDecimal kaiCangJia = new DescBigDecimal(ascBigDecimal.getValue().toString());
- queueKaiCang.add(kaiCangJia);
- }
- }
-
- return queueKaiCang;
- }
-
- /**
- * 根据当前价格初始化平仓队列。遍历已有的升序价格队列,
- * 将大于当前价格的所有价格点加入升序的平仓队列中。
- *
- * @param jiaGe 当前价格
- * @param queueAsc 已初始化的价格升序队列
- */
- @Override
- public PriorityBlockingQueue<AscBigDecimal> initPingCang(BigDecimal jiaGe, PriorityBlockingQueue<AscBigDecimal> queueAsc) {
- PriorityBlockingQueue<AscBigDecimal> queuePingCang = WangGeQueue.getPingCang();
- queuePingCang.clear();
-
- AscBigDecimal now = new AscBigDecimal(jiaGe.toString());
-
- for (AscBigDecimal ascBigDecimal : queueAsc) {
- if (ascBigDecimal.compareTo(now) > 0) {
- queuePingCang.add(ascBigDecimal);
- }
- }
-
- return queuePingCang;
- }
-
- /**
- * 主方法,用于测试网格初始化及开仓/平仓逻辑。
- * 示例使用固定价格"0.355"进行模拟调用。
- *
- * @param args 启动参数(未使用)
- */
- public static void main(String[] args) {
- for (int i = 0; i < 10; i++) {
- WangGeServiceImpl wangGeService = new WangGeServiceImpl();
- PriorityBlockingQueue<AscBigDecimal> queueAsc = wangGeService.initWangGe();
- if (queueAsc != null) {
- wangGeService.initKaiCang(new BigDecimal("91000"), queueAsc);
- wangGeService.initPingCang(new BigDecimal("91000"), queueAsc);
- }
- }
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/zhanghu/ApiMessageServiceImpl.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/zhanghu/ApiMessageServiceImpl.java
deleted file mode 100644
index c8b8f0e..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/zhanghu/ApiMessageServiceImpl.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.zhanghu;
-
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.Dto.QuantApiMessage;
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.ExchangeInfoEnum;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-
-@Slf4j
-@Service
-@RequiredArgsConstructor
-public class ApiMessageServiceImpl implements IApiMessageService {
-
- @Override
- public QuantApiMessage getApiMessage(String okx) {
-
- QuantApiMessage quantApiMessage = new QuantApiMessage();
- quantApiMessage.setExchange(okx);
- quantApiMessage.setMemberId(1L);
-
- // 根据传入的账号名称获取对应的账号信息
- ExchangeInfoEnum account = ExchangeInfoEnum.valueOf(okx);
- quantApiMessage.setAccountType(account.isAccountType()? "true":"false");
- quantApiMessage.setState(1);
- quantApiMessage.setIsTrade(1);
- quantApiMessage.setASecretkey(account.getApiKey());
- quantApiMessage.setBSecretkey(account.getSecretKey());
- quantApiMessage.setPassPhrass(account.getPassphrase());
- return quantApiMessage;
- }
-
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/zhanghu/IApiMessageService.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/zhanghu/IApiMessageService.java
deleted file mode 100644
index 73ec98d..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/zhanghu/IApiMessageService.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.zhanghu;
-
-import com.xcong.excoin.modules.okxNewPrice.okxpi.config.Dto.QuantApiMessage;
-
-
-public interface IApiMessageService{
-
- QuantApiMessage getApiMessage(String okx);
-
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/zhanghu/ZhangHuEnum.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/zhanghu/ZhangHuEnum.java
deleted file mode 100644
index 3647e8b..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/zhanghu/ZhangHuEnum.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.xcong.excoin.modules.okxNewPrice.zhanghu;
-
-/**
- * @author Administrator
- */
-
-public enum ZhangHuEnum {
- JIAOYISUO("交易所名字", "OKX"),
- zhanghu_zongjine("账户总资金", "1000"),
- baozhengjine("账户下单总保证金", "300"),
- meicixiadanjine("每次下单金额", "30"),
- ;
-
- private String name;
-
- private String value;
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getValue() {
- return value;
- }
-
- public void setValue(String value) {
- this.value = value;
- }
-
- private ZhangHuEnum(String name, String value) {
- this.name = name;
- this.value = value;
- }
-}
--
Gitblit v1.9.1