| | |
| | | package com.xcong.excoin.modules.okxApi; |
| | | |
| | | import com.xcong.excoin.modules.okxApi.wsHandler.handler.MarkPriceOkxChannelHandler; |
| | | import com.xcong.excoin.modules.okxApi.wsHandler.handler.OrderAlgoOkxChannelHandler; |
| | | import com.xcong.excoin.modules.okxApi.wsHandler.handler.OrdersOkxChannelHandler; |
| | | import com.xcong.excoin.modules.okxApi.wsHandler.handler.PositionsOkxChannelHandler; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Component; |
| | |
| | | import java.math.BigDecimal; |
| | | |
| | | /** |
| | | * OKX 模块 Spring 容器入口 — 组件组装 + 生命周期管理。 |
| | | * OKX 盈利回收策略 — 唯一入口,所有参数集中在此。 |
| | | * |
| | | * <h3>组装顺序({@code @PostConstruct})</h3> |
| | | * <ol> |
| | | * <li>{@link OkxConfig} — 构建配置(API 密钥、合约、策略参数)</li> |
| | | * <li>{@link OkxGridTradeService} — init():切双向持仓 → 清旧条件单 → 平仓 → 设杠杆</li> |
| | | * <li>{@link OkxKlineWebSocketClient} — 注册 3 个频道处理器 → init():建立 WS 连接并订阅</li> |
| | | * <li>{@code gridTradeService.startGrid()} — 状态重置,等待首根 K 线</li> |
| | | * </ol> |
| | | * <pre> |
| | | * ┌─────────────────────────────────────────────────────────────────┐ |
| | | * │ 🔧 全部参数在此修改 │ |
| | | * ├──────────────────┬──────────────────────────────────────────────┤ |
| | | * │ 参数名 │ 值 │ 说明 │ |
| | | * ├──────────────────┼──────────────────────────────────────┼────────┤ |
| | | * │ apiKey │ ac76252d-... │ API密钥 │ |
| | | * │ apiSecret │ A8168543... │ 签名密钥│ |
| | | * │ passphrase │ Aa12345678@ │ 口令密码│ |
| | | * ├──────────────────┼──────────────────────────────────────┼────────┤ |
| | | * │ contract │ BTC-USDT-SWAP │ 交易合约│ |
| | | * │ leverage │ 50 │ 杠杆倍数│ |
| | | * │ baseQuantity │ 10 │ 底仓张数│ |
| | | * ├──────────────────┼──────────────────────────────────────┼────────┤ |
| | | * │ profitTriggerRatio│ 0.5 │ 触发ROI │ |
| | | * │ reinvestRatio │ 0.5 │ 补仓比例│ |
| | | * ├──────────────────┼──────────────────────────────────────┼────────┤ |
| | | * │ maxPositionMult │ 10 (= base×10 = 100张上限) │ 风控1 │ |
| | | * │ maxLoss │ 15 USDT │ 风控2 │ |
| | | * │ equityRestartRatio│ 0.05 (= 5%) │ 风控3 │ |
| | | * ├──────────────────┼──────────────────────────────────────┼────────┤ |
| | | * │ contractMult │ 0.01 (BTC 每张0.01个币) │ 合约乘数│ |
| | | * │ priceScale │ 2 (价格精度0.01) │ 价格精度│ |
| | | * │ pnlPriceMode │ LAST_PRICE │ 盈亏计价│ |
| | | * │ isProduction │ false (false=模拟盘) │ 交易环境│ |
| | | * └──────────────────┴──────────────────────────────────────────────┘ |
| | | * </pre> |
| | | * |
| | | * <h3>3 个频道处理器</h3> |
| | | * <ol> |
| | | * <li>MarkPriceOkxChannelHandler — 公开频道,标记价格 → onKline() + setMarkPrice()</li> |
| | | * <li>PositionsOkxChannelHandler — 私有频道,仓位 → onPositionUpdate()</li> |
| | | * <li>OrderAlgoOkxChannelHandler — 私有频道,条件单状态 → onAutoOrder()</li> |
| | | * </ol> |
| | | * <h3>策略核心公式</h3> |
| | | * <ul> |
| | | * <li>保证金 = 张数 × 合约乘数 × 开仓均价 / 杠杆</li> |
| | | * <li>ROI = 未实现盈亏 / 该方向保证金</li> |
| | | * <li>触发条件:ROI ≥ profitTriggerRatio</li> |
| | | * <li>平仓:floor(盈利仓位张数 / 2)</li> |
| | | * <li>补仓保证金 B = 已实现利润 × reinvestRatio</li> |
| | | * <li>单张保证金 = 合约乘数 × 开仓均价 / 杠杆</li> |
| | | * <li>补仓张数 = max(baseQuantity, floor(B / 单张保证金))</li> |
| | | * </ul> |
| | | * |
| | | * <h3>销毁顺序({@code @PreDestroy})</h3> |
| | | * <ol> |
| | | * <li>gridTradeService.stopGrid():取消所有条件单 → 关闭交易线程池</li> |
| | | * <li>wsClient.destroy():取消订阅 → 断开 WS → 关闭线程池</li> |
| | | * </ol> |
| | | * <h3>仓位演化示例 (BTC-USDT-SWAP, 50x, base10)</h3> |
| | | * <pre> |
| | | * 初始: LONG=10 SHORT=10 |
| | | * 多盈: LONG=5 SHORT=12 |
| | | * 多盈: LONG=3 SHORT=15 |
| | | * 多盈: LONG=2 SHORT=18 |
| | | * → 权益达+5% → 全平重置 → LONG=10 SHORT=10 |
| | | * </pre> |
| | | * |
| | | * @author Administrator |
| | | */ |
| | |
| | | @Component |
| | | public class OkxWebSocketClientManager { |
| | | |
| | | // ╔══════════════════════════════════════════════════════════════╗ |
| | | // ║ 🔐 OKX API 认证信息 ║ |
| | | // ╚══════════════════════════════════════════════════════════════╝ |
| | | private static final String OKX_API_KEY = "ac76252d-e717-4459-a6f9-80512aed5ea0"; |
| | | private static final String OKX_API_SECRET = "A8168543EF4F08A6DBFE27AB23956898"; |
| | | private static final String OKX_PASSPHRASE = "Aa12345678@"; |
| | | |
| | | // ╔══════════════════════════════════════════════════════════════╗ |
| | | // ║ 📊 交易标的参数 ║ |
| | | // ╚══════════════════════════════════════════════════════════════╝ |
| | | private static final String CONTRACT = "BTC-USDT-SWAP"; // 合约名称 |
| | | private static final String LEVERAGE = "50"; // 杠杆倍数 |
| | | private static final String MARGIN_MODE = "cross"; // 全仓 cross / 逐仓 isolated |
| | | private static final String POSITION_MODE = "long_short_mode"; // 双向持仓 |
| | | |
| | | // ╔══════════════════════════════════════════════════════════════╗ |
| | | // ║ 🎯 策略核心参数 ║ |
| | | // ╚══════════════════════════════════════════════════════════════╝ |
| | | /** 基础仓位张数 — 初始化时多空各开此张数,也是最小开仓单位 */ |
| | | private static final String BASE_QUANTITY = "10"; |
| | | /** 盈利触发比例 — ROI=未实现盈亏/保证金 达到此值时触发平仓 */ |
| | | private static final BigDecimal PROFIT_TRIGGER_RATIO = new BigDecimal("0.5"); // 50% |
| | | /** 再投资比例 — 已实现利润中用于补反向仓的比例 */ |
| | | private static final BigDecimal REINVEST_RATIO = new BigDecimal("0.5"); // 50% |
| | | |
| | | // ╔══════════════════════════════════════════════════════════════╗ |
| | | // ║ 🛡 风控参数 ║ |
| | | // ╚══════════════════════════════════════════════════════════════╝ |
| | | /** 风控1: 反向仓位倍数上限 — 反向最大持仓 = baseQuantity × 此值 (10×10=100张) */ |
| | | private static final int MAX_POSITION_MULTIPLIER = 10; |
| | | /** 风控2: 最大亏损阈值(USDT) — 全局盈亏≤-此值时停止策略 */ |
| | | private static final BigDecimal MAX_LOSS = new BigDecimal("15"); |
| | | /** 风控3: 权益重置比例 — 账户权益 ≥ 初始权益 × (1+此值) 时全平重新双开 */ |
| | | private static final BigDecimal EQUITY_RESTART_RATIO = new BigDecimal("0.05"); // 5% |
| | | |
| | | // ╔══════════════════════════════════════════════════════════════╗ |
| | | // ║ ⚙ 技术与环境参数 ║ |
| | | // ╚══════════════════════════════════════════════════════════════╝ |
| | | /** 合约乘数 — BTC-USDT-SWAP 每张=0.01 BTC */ |
| | | private static final BigDecimal CONTRACT_MULTIPLIER = new BigDecimal("0.01"); |
| | | /** 价格精度 — OKX 价格小数位数 (BTC=2, ETH=2) */ |
| | | private static final int PRICE_SCALE = 2; |
| | | /** 盈亏计价模式 — LAST_PRICE(最新价) / MARK_PRICE(标记价) */ |
| | | private static final OkxConfig.PnLPriceMode PNL_PRICE_MODE = OkxConfig.PnLPriceMode.LAST_PRICE; |
| | | /** false=模拟盘 (wspap.okx.com) / true=实盘 (ws.okx.com) */ |
| | | private static final boolean IS_PRODUCTION = false; |
| | | |
| | | // ==================== 以下为启动代码,通常无需修改 ==================== |
| | | |
| | | private OkxKlineWebSocketClient wsClient; |
| | | private OkxGridTradeService gridTradeService; |
| | | private OkxProfitRecycleStrategy strategy; |
| | | private OkxConfig config; |
| | | |
| | | @PostConstruct |
| | | public void init() { |
| | | log.info("[OKX管理器] 开始初始化..."); |
| | | log.info("[OKX] 正在初始化盈利回收策略..."); |
| | | |
| | | try { |
| | | // TODO: 替换为实际 API 密钥 |
| | | config = OkxConfig.builder() |
| | | .apiKey("ac76252d-e717-4459-a6f9-80512aed5ea0") |
| | | .apiSecret("A8168543EF4F08A6DBFE27AB23956898") |
| | | .passphrase("Aa12345678@") |
| | | .contract("ETH-USDT-SWAP") |
| | | .leverage("100") |
| | | .marginMode("cross") |
| | | .positionMode("long_short_mode") |
| | | .gridRate(new BigDecimal("0.003")) |
| | | .expectedProfit(new BigDecimal("25")) |
| | | .maxLoss(new BigDecimal("15")) |
| | | .baseQuantity("15") |
| | | .quantity("15") |
| | | .restartGridSpan(6) |
| | | .maxPositionSize(2) |
| | | .priceScale(2) |
| | | .contractMultiplier(new BigDecimal("0.01")) |
| | | .unrealizedPnlPriceMode(OkxConfig.PnLPriceMode.LAST_PRICE) |
| | | .isProduction(false) |
| | | .reopenMaxRetries(3) |
| | | .apiKey(OKX_API_KEY) |
| | | .apiSecret(OKX_API_SECRET) |
| | | .passphrase(OKX_PASSPHRASE) |
| | | .contract(CONTRACT) |
| | | .leverage(LEVERAGE) |
| | | .marginMode(MARGIN_MODE) |
| | | .positionMode(POSITION_MODE) |
| | | .baseQuantity(BASE_QUANTITY) |
| | | .maxLoss(MAX_LOSS) |
| | | .priceScale(PRICE_SCALE) |
| | | .contractMultiplier(CONTRACT_MULTIPLIER) |
| | | .unrealizedPnlPriceMode(PNL_PRICE_MODE) |
| | | .isProduction(IS_PRODUCTION) |
| | | .profitTriggerRatio(PROFIT_TRIGGER_RATIO) |
| | | .reinvestRatio(REINVEST_RATIO) |
| | | .maxPositionMultiplier(MAX_POSITION_MULTIPLIER) |
| | | .equityRestartRatio(EQUITY_RESTART_RATIO) |
| | | .build(); |
| | | |
| | | // 1. 初始化交易服务 |
| | | gridTradeService = new OkxGridTradeService(config); |
| | | gridTradeService.init(); |
| | | strategy = new OkxProfitRecycleStrategy(config); |
| | | strategy.init(); |
| | | |
| | | // 2. 创建 WS 客户端并注册频道处理器 |
| | | wsClient = new OkxKlineWebSocketClient(config); |
| | | wsClient.addPublicHandler(new MarkPriceOkxChannelHandler(config.getContract(), strategy)); |
| | | wsClient.addPrivateHandler(new PositionsOkxChannelHandler(config, strategy)); |
| | | wsClient.addPrivateHandler(new OrdersOkxChannelHandler(config, strategy)); |
| | | |
| | | // 公开频道:标记价格(替代 K 线,同时驱动策略 onKline 和 PnL 计算) |
| | | wsClient.addPublicHandler(new MarkPriceOkxChannelHandler( |
| | | config.getContract(), gridTradeService)); |
| | | // 私有频道:仓位 |
| | | wsClient.addPrivateHandler(new PositionsOkxChannelHandler( |
| | | config, gridTradeService)); |
| | | // 私有频道:条件单 |
| | | wsClient.addPrivateHandler(new OrderAlgoOkxChannelHandler( |
| | | config, gridTradeService)); |
| | | |
| | | gridTradeService.setWsClient(wsClient); |
| | | strategy.setWsClient(wsClient); |
| | | wsClient.init(); |
| | | log.info("[OKX管理器] WS已连接, 已注册 3 个频道处理器"); |
| | | log.info("[OKX] WS已连接 | 合约: {} | 杠杆: {}x | 基础张数: {} | " |
| | | + "触发ROI: {}% | 补仓比例: {}% | 仓位上限: {}x | 重置阈值: {}%", |
| | | CONTRACT, LEVERAGE, BASE_QUANTITY, |
| | | PROFIT_TRIGGER_RATIO.multiply(new BigDecimal("100")), |
| | | REINVEST_RATIO.multiply(new BigDecimal("100")), |
| | | MAX_POSITION_MULTIPLIER, |
| | | EQUITY_RESTART_RATIO.multiply(new BigDecimal("100"))); |
| | | |
| | | // 3. 激活策略 |
| | | gridTradeService.startGrid(); |
| | | strategy.startStrategy(); |
| | | } catch (Exception e) { |
| | | log.error("[OKX管理器] 初始化失败", e); |
| | | log.error("[OKX] 初始化失败", e); |
| | | } |
| | | } |
| | | |
| | | @PreDestroy |
| | | public void destroy() { |
| | | log.info("[OKX管理器] 开始销毁..."); |
| | | if (gridTradeService != null) { |
| | | gridTradeService.stopGrid(); |
| | | } |
| | | if (wsClient != null) { |
| | | wsClient.destroy(); |
| | | } |
| | | log.info("[OKX管理器] 销毁完成"); |
| | | log.info("[OKX] 正在销毁..."); |
| | | if (strategy != null) strategy.stopStrategy(); |
| | | if (wsClient != null) wsClient.destroy(); |
| | | log.info("[OKX] 销毁完成"); |
| | | } |
| | | |
| | | public OkxKlineWebSocketClient getKlineWebSocketClient() { return wsClient; } |
| | | public OkxGridTradeService getGridTradeService() { return gridTradeService; } |
| | | public OkxProfitRecycleStrategy getStrategy() { return strategy; } |
| | | public IOkxStrategy getActiveStrategy() { return strategy; } |
| | | } |