package com.xcong.excoin.modules.okxApi; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.io.IOException; import java.math.BigDecimal; import java.text.SimpleDateFormat; import java.util.Base64; import java.util.Date; import java.util.TimeZone; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; /** * OKX REST API 异步执行器,所有下单/撤单操作经此类提交。 * *
| 方法 | 用途 |
|---|---|
| openLong / openShort | 市价基底开仓 |
| placeConditionalEntryOrder | 挂条件开仓单(价格触发后市价开仓) |
| placeTakeProfit | 挂止盈条件单 |
| cancelConditionalOrder | 取消单个条件单 |
| cancelAllPriceTriggeredOrders | 取消所有条件单(策略停止时) |
服务器监控价格,达到触发价后自动以市价平仓。 * 使用 OKX 的 {@code order-algo} 接口,ordType=conditional。 * *
通过 posSide 指定平仓方向: *
使用 OKX 的 {@code order-algo} 接口,ordType=trigger(计划委托)。 * 服务器监控价格,达到触发价后以市价开仓。 * *
自动添加 OK-ACCESS-KEY、OK-ACCESS-SIGN、OK-ACCESS-TIMESTAMP、OK-ACCESS-PASSPHRASE * 四个认证头。签名算法:base64(HMAC-SHA256(timestamp + method + path + body))。 * * @param path API 路径(如 /api/v5/trade/order) * @param body 请求体 JSON 字符串 * @return 解析后的响应 JSONObject * @throws IOException 网络异常或业务错误 */ JSONObject okPost(String path, String body) throws IOException { String method = "POST"; String timestamp = getIsoTimestamp(); String sign = null; try { sign = sign(timestamp, method, path, body); } catch (Exception e) { e.printStackTrace(); } Request.Builder builder = new Request.Builder() .url(config.getRestBasePath() + path) .header("OK-ACCESS-KEY", config.getApiKey()) .header("OK-ACCESS-SIGN", sign) .header("OK-ACCESS-TIMESTAMP", timestamp) .header("OK-ACCESS-PASSPHRASE", config.getPassphrase()) .header("Content-Type", "application/json; charset=utf-8") .post(RequestBody.create(JSON_MEDIA_TYPE, body)); // 模拟盘需加 x-simulated-trading 头,与生产网共用同一 REST 地址 if (!config.isProduction()) { builder.header("x-simulated-trading", "1"); } Request request = builder.build(); try (Response response = httpClient.newCall(request).execute()) { String responseBody = response.body() != null ? response.body().string() : "{}"; if (!response.isSuccessful()) { log.error("[TradeExec-OKX] HTTP {} POST {}: {}", response.code(), path, responseBody); throw new IOException("HTTP " + response.code() + ": " + responseBody); } return JSON.parseObject(responseBody); } } /** * 发送 OKX 签名 GET 请求并返回解析后的 JSONObject。 * *
GET 请求的签名中 body 为空字符串。 * * @param path API 路径(如 /api/v5/account/positions) * @return 解析后的响应 JSONObject * @throws IOException 网络异常 */ JSONObject okGet(String path) throws IOException { String method = "GET"; String timestamp = getIsoTimestamp(); String sign = null; try { sign = sign(timestamp, method, path, ""); } catch (Exception e) { e.printStackTrace(); } Request.Builder builder = new Request.Builder() .url(config.getRestBasePath() + path) .header("OK-ACCESS-KEY", config.getApiKey()) .header("OK-ACCESS-SIGN", sign) .header("OK-ACCESS-TIMESTAMP", timestamp) .header("OK-ACCESS-PASSPHRASE", config.getPassphrase()) .get(); // 模拟盘需加 x-simulated-trading 头 if (!config.isProduction()) { builder.header("x-simulated-trading", "1"); } Request request = builder.build(); try (Response response = httpClient.newCall(request).execute()) { String responseBody = response.body() != null ? response.body().string() : "{}"; if (!response.isSuccessful()) { log.error("[TradeExec-OKX] HTTP {} GET {}: {}", response.code(), path, responseBody); throw new IOException("HTTP " + response.code() + ": " + responseBody); } return JSON.parseObject(responseBody); } } // ==================== 签名工具方法 ==================== /** * 生成 OKX API 签名。 * *
签名算法: *
格式示例:{@code 2023-01-01T00:00:00.000Z} * * @return ISO 8601 格式的 UTC 时间戳字符串 */ private String getIsoTimestamp() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); sdf.setTimeZone(TimeZone.getTimeZone("UTC")); return sdf.format(new Date()); } }