From 635199d881d45a8c92abea57e89b69634ab4b366 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Mon, 22 Dec 2025 16:19:03 +0800
Subject: [PATCH] feat(newPrice): 新增OKX交易所相关功能模块

---
 src/main/java/com/xcong/excoin/modules/newPrice/KlineVo.java                            |   23 
 src/main/java/com/xcong/excoin/modules/newPrice/ExchangeLoginService.java               |   36 +
 src/main/java/com/xcong/excoin/modules/newPrice/utils/JSONParser.java                   |   40 +
 src/main/java/com/xcong/excoin/modules/newPrice/ExchangeInfoEnum.java                   |   34 +
 src/main/java/com/xcong/excoin/modules/newPrice/utils/OkHttpUtils.java                  |  330 ++++++++++++
 src/main/java/com/xcong/excoin/modules/newPrice/utils/ParameterChecker.java             |   36 +
 src/main/java/com/xcong/excoin/utils/CoinTypeConvert.java                               |   38 +
 src/main/java/com/xcong/excoin/modules/newPrice/utils/DateUtils.java                    |   80 +++
 src/main/java/com/xcong/excoin/modules/newPrice/enums/HttpMethod.java                   |    9 
 src/main/java/com/xcong/excoin/modules/newPrice/utils/SignUtils.java                    |   55 ++
 src/main/java/com/xcong/excoin/modules/newPrice/utils/RequestBuilder.java               |  150 +++++
 src/main/java/com/xcong/excoin/modules/newPrice/RequestHandler.java                     |  125 ++++
 src/main/java/com/xcong/excoin/modules/newPrice/ResponseHandler.java                    |   66 ++
 src/main/java/com/xcong/excoin/modules/symbols/controller/SymbolsController.java        |   10 
 src/main/java/com/xcong/excoin/modules/symbols/service/SymbolsService.java              |    2 
 src/main/java/com/xcong/excoin/modules/newPrice/enums/RequestType.java                  |    7 
 src/main/java/com/xcong/excoin/modules/newPrice/impl/ExchangeLoginEventServiceImpl.java |   50 +
 src/main/java/com/xcong/excoin/modules/newPrice/ExchangeLoginEventService.java          |   27 +
 src/main/java/com/xcong/excoin/modules/symbols/service/impl/SymbolsServiceImpl.java     |   30 +
 src/main/java/com/xcong/excoin/common/exception/FebsException.java                      |   15 
 src/main/java/com/xcong/excoin/modules/newPrice/utils/OKXContants.java                  |  185 +++++++
 src/main/java/com/xcong/excoin/modules/newPrice/OKXAccount.java                         |   21 
 src/main/java/com/xcong/excoin/modules/newPrice/utils/UrlBuilder.java                   |  128 ++++
 src/main/java/com/xcong/excoin/modules/newPrice/enums/DefaultUrls.java                  |   38 +
 24 files changed, 1,535 insertions(+), 0 deletions(-)

diff --git a/src/main/java/com/xcong/excoin/common/exception/FebsException.java b/src/main/java/com/xcong/excoin/common/exception/FebsException.java
new file mode 100644
index 0000000..befbffc
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/common/exception/FebsException.java
@@ -0,0 +1,15 @@
+package com.xcong.excoin.common.exception;
+
+/**
+ * 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/newPrice/ExchangeInfoEnum.java b/src/main/java/com/xcong/excoin/modules/newPrice/ExchangeInfoEnum.java
new file mode 100644
index 0000000..248208a
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/newPrice/ExchangeInfoEnum.java
@@ -0,0 +1,34 @@
+package com.xcong.excoin.modules.newPrice;
+
+import lombok.Getter;
+
+@Getter
+public enum ExchangeInfoEnum {
+
+    /**
+     *      模拟盘
+     *		String apiKey = "16c882b0-2853-46c5-8af0-5269e3218d72";
+     *		String passphrase = "Zh12345@@";
+     *		String secretKey = "A13F7BC5721DB6D1F492B0FC333D4D9C";
+     */
+    OKX_UAT("0769b50c-2c36-4310-8bd9-cad6bc6c9d8f",
+            "7AF4A574BC44907CE76BBFF91F53852D",
+            "Aa123456@",
+            false);
+
+    private final String apiKey;
+
+    private final String secretKey;
+
+    private final String passphrase;
+
+//    @Setting(label = "账户类型", order = 40, type = FieldType.SELECT, options = {"实盘账户", "模拟账户"}, optionsVal = {"true", "false"})
+    private final boolean accountType;
+
+    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/newPrice/ExchangeLoginEventService.java b/src/main/java/com/xcong/excoin/modules/newPrice/ExchangeLoginEventService.java
new file mode 100644
index 0000000..69f2932
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/newPrice/ExchangeLoginEventService.java
@@ -0,0 +1,27 @@
+package com.xcong.excoin.modules.newPrice;
+
+import java.util.LinkedHashMap;
+
+public interface ExchangeLoginEventService {
+    /**
+     * 获取交易产品基础信息
+     * 获取所有可交易产品的信息列表。
+     * <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>
+     */
+    String exchangeInfo(LinkedHashMap<String, Object> parameters);
+
+    String lineHistory(LinkedHashMap<String, Object> parameters);
+}
diff --git a/src/main/java/com/xcong/excoin/modules/newPrice/ExchangeLoginService.java b/src/main/java/com/xcong/excoin/modules/newPrice/ExchangeLoginService.java
new file mode 100644
index 0000000..d8f0844
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/newPrice/ExchangeLoginService.java
@@ -0,0 +1,36 @@
+package com.xcong.excoin.modules.newPrice;
+
+
+import com.xcong.excoin.common.exception.FebsException;
+import com.xcong.excoin.modules.newPrice.impl.ExchangeLoginEventServiceImpl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ExchangeLoginService {
+    private final static Map<String, ExchangeLoginEventService> eventMap = new HashMap<>();
+
+    static {
+        for (ExchangeInfoEnum infoEnum : ExchangeInfoEnum.values()) {
+            eventMap.put(infoEnum.name(), new ExchangeLoginEventServiceImpl(
+                    infoEnum.getApiKey(),
+                    infoEnum.getSecretKey(),
+                    infoEnum.getPassphrase(),
+                    infoEnum.isAccountType()));
+        }
+    }
+
+    private ExchangeLoginService() {
+    }
+
+    public final static ExchangeLoginService INSTANCE = new ExchangeLoginService();
+
+    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/newPrice/KlineVo.java b/src/main/java/com/xcong/excoin/modules/newPrice/KlineVo.java
new file mode 100644
index 0000000..3665b0d
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/newPrice/KlineVo.java
@@ -0,0 +1,23 @@
+package com.xcong.excoin.modules.newPrice;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+@Data
+@ApiModel(value = "KlineVo", description = "请求K线类")
+public class KlineVo implements Serializable {
+
+
+    @NotNull
+    @ApiModelProperty(value = "币种", example = "BTC/USDT")
+    private String instId;
+
+    @NotNull
+    @ApiModelProperty(value = "类型 1-币币2-合约", example = "1")
+    private Integer type;
+
+}
diff --git a/src/main/java/com/xcong/excoin/modules/newPrice/OKXAccount.java b/src/main/java/com/xcong/excoin/modules/newPrice/OKXAccount.java
new file mode 100644
index 0000000..6a08aef
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/newPrice/OKXAccount.java
@@ -0,0 +1,21 @@
+package com.xcong.excoin.modules.newPrice;
+
+import lombok.Data;
+
+@Data
+public class OKXAccount {
+
+    String passPhrase;
+    boolean isSimluate;
+    public String baseUrl;
+    public RequestHandler requestHandler;
+    public boolean showLimitUsage;
+
+    public OKXAccount(){}
+
+    public OKXAccount(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/newPrice/RequestHandler.java b/src/main/java/com/xcong/excoin/modules/newPrice/RequestHandler.java
new file mode 100644
index 0000000..72d5dd0
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/newPrice/RequestHandler.java
@@ -0,0 +1,125 @@
+package com.xcong.excoin.modules.newPrice;
+
+import com.alibaba.fastjson.JSON;
+import com.xcong.excoin.common.exception.FebsException;
+import com.xcong.excoin.modules.newPrice.enums.HttpMethod;
+import com.xcong.excoin.modules.newPrice.enums.RequestType;
+import com.xcong.excoin.modules.newPrice.utils.DateUtils;
+import com.xcong.excoin.modules.newPrice.utils.RequestBuilder;
+import com.xcong.excoin.modules.newPrice.utils.SignUtils;
+import com.xcong.excoin.modules.newPrice.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/newPrice/ResponseHandler.java b/src/main/java/com/xcong/excoin/modules/newPrice/ResponseHandler.java
new file mode 100644
index 0000000..53878e5
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/newPrice/ResponseHandler.java
@@ -0,0 +1,66 @@
+package com.xcong.excoin.modules.newPrice;
+
+
+import cn.hutool.json.JSONException;
+import com.xcong.excoin.common.exception.FebsException;
+import com.xcong.excoin.modules.newPrice.utils.JSONParser;
+import com.xcong.excoin.modules.newPrice.utils.OkHttpUtils;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+
+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 {
+            OkHttpUtils.builder();
+            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/newPrice/enums/DefaultUrls.java b/src/main/java/com/xcong/excoin/modules/newPrice/enums/DefaultUrls.java
new file mode 100644
index 0000000..845c706
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/newPrice/enums/DefaultUrls.java
@@ -0,0 +1,38 @@
+package com.xcong.excoin.modules.newPrice.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://aws.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/newPrice/enums/HttpMethod.java b/src/main/java/com/xcong/excoin/modules/newPrice/enums/HttpMethod.java
new file mode 100644
index 0000000..c32ed4d
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/newPrice/enums/HttpMethod.java
@@ -0,0 +1,9 @@
+package com.xcong.excoin.modules.newPrice.enums;
+
+public enum HttpMethod {
+    POST,
+    GET,
+    PUT,
+    DELETE,
+    INVALID
+}
diff --git a/src/main/java/com/xcong/excoin/modules/newPrice/enums/RequestType.java b/src/main/java/com/xcong/excoin/modules/newPrice/enums/RequestType.java
new file mode 100644
index 0000000..faf00e1
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/newPrice/enums/RequestType.java
@@ -0,0 +1,7 @@
+package com.xcong.excoin.modules.newPrice.enums;
+
+public enum RequestType {
+    PUBLIC,
+    WITH_API_KEY,
+    SIGNED
+}
diff --git a/src/main/java/com/xcong/excoin/modules/newPrice/impl/ExchangeLoginEventServiceImpl.java b/src/main/java/com/xcong/excoin/modules/newPrice/impl/ExchangeLoginEventServiceImpl.java
new file mode 100644
index 0000000..b9def41
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/newPrice/impl/ExchangeLoginEventServiceImpl.java
@@ -0,0 +1,50 @@
+package com.xcong.excoin.modules.newPrice.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.xcong.excoin.modules.newPrice.ExchangeLoginEventService;
+import com.xcong.excoin.modules.newPrice.OKXAccount;
+import com.xcong.excoin.modules.newPrice.enums.DefaultUrls;
+import com.xcong.excoin.modules.newPrice.enums.HttpMethod;
+import com.xcong.excoin.modules.newPrice.utils.OKXContants;
+import lombok.extern.slf4j.Slf4j;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.*;
+
+@Slf4j
+public class ExchangeLoginEventServiceImpl implements ExchangeLoginEventService {
+
+
+    private final com.xcong.excoin.modules.newPrice.OKXAccount OKXAccount;
+    private final String apiKey;
+    private final String secretKey;
+    private final String passphrase;
+    private final 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());
+    }
+
+}
diff --git a/src/main/java/com/xcong/excoin/modules/newPrice/utils/DateUtils.java b/src/main/java/com/xcong/excoin/modules/newPrice/utils/DateUtils.java
new file mode 100644
index 0000000..df7ca20
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/newPrice/utils/DateUtils.java
@@ -0,0 +1,80 @@
+package com.xcong.excoin.modules.newPrice.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/newPrice/utils/JSONParser.java b/src/main/java/com/xcong/excoin/modules/newPrice/utils/JSONParser.java
new file mode 100644
index 0000000..92f6c5f
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/newPrice/utils/JSONParser.java
@@ -0,0 +1,40 @@
+package com.xcong.excoin.modules.newPrice.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/newPrice/utils/OKXContants.java b/src/main/java/com/xcong/excoin/modules/newPrice/utils/OKXContants.java
new file mode 100644
index 0000000..6426533
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/newPrice/utils/OKXContants.java
@@ -0,0 +1,185 @@
+package com.xcong.excoin.modules.newPrice.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 = "/api/v5/market/history-candles";
+    public static final String K_LINE_HISTORY = "/api/v5/market/history-mark-price-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";
+    /**
+     * 查看持仓信息
+     * 获取该账户下拥有实际持仓的信息。账户为单向持仓模式会显示净持仓(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";
+
+}
diff --git a/src/main/java/com/xcong/excoin/modules/newPrice/utils/OkHttpUtils.java b/src/main/java/com/xcong/excoin/modules/newPrice/utils/OkHttpUtils.java
new file mode 100644
index 0000000..9e823e0
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/newPrice/utils/OkHttpUtils.java
@@ -0,0 +1,330 @@
+package com.xcong.excoin.modules.newPrice.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/newPrice/utils/ParameterChecker.java b/src/main/java/com/xcong/excoin/modules/newPrice/utils/ParameterChecker.java
new file mode 100644
index 0000000..ede6fdd
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/newPrice/utils/ParameterChecker.java
@@ -0,0 +1,36 @@
+package com.xcong.excoin.modules.newPrice.utils;
+
+import cc.mrbird.febs.common.exception.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/newPrice/utils/RequestBuilder.java b/src/main/java/com/xcong/excoin/modules/newPrice/utils/RequestBuilder.java
new file mode 100644
index 0000000..a36cf50
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/newPrice/utils/RequestBuilder.java
@@ -0,0 +1,150 @@
+package com.xcong.excoin.modules.newPrice.utils;
+
+
+import com.xcong.excoin.common.exception.FebsException;
+import com.xcong.excoin.modules.newPrice.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/newPrice/utils/SignUtils.java b/src/main/java/com/xcong/excoin/modules/newPrice/utils/SignUtils.java
new file mode 100644
index 0000000..9d5da67
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/newPrice/utils/SignUtils.java
@@ -0,0 +1,55 @@
+package com.xcong.excoin.modules.newPrice.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/newPrice/utils/UrlBuilder.java b/src/main/java/com/xcong/excoin/modules/newPrice/utils/UrlBuilder.java
new file mode 100644
index 0000000..5e28e12
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/newPrice/utils/UrlBuilder.java
@@ -0,0 +1,128 @@
+package com.xcong.excoin.modules.newPrice.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/symbols/controller/SymbolsController.java b/src/main/java/com/xcong/excoin/modules/symbols/controller/SymbolsController.java
index 64fe133..20342ef 100644
--- a/src/main/java/com/xcong/excoin/modules/symbols/controller/SymbolsController.java
+++ b/src/main/java/com/xcong/excoin/modules/symbols/controller/SymbolsController.java
@@ -2,6 +2,7 @@
 
 import com.huobi.client.model.Candlestick;
 import com.xcong.excoin.common.response.Result;
+import com.xcong.excoin.modules.newPrice.KlineVo;
 import com.xcong.excoin.modules.symbols.parameter.dto.KlineDetailDto;
 import com.xcong.excoin.modules.symbols.parameter.vo.HomeSymbolsVo;
 import com.xcong.excoin.modules.symbols.parameter.vo.KlineDataVo;
@@ -61,6 +62,15 @@
         return symbolsService.findKlineDetails(klineDetailDto);
     }
 
+    @ApiOperation(value = "查询历史OKXK线数据", notes = "查询历史OKXK线数据")
+    @ApiResponses({
+            @ApiResponse(code = 0, message = "success", response = KlineDataVo.class)
+    })
+    @PostMapping(value = "/klineDetail")
+    public Result klineDetail(@RequestBody @Valid KlineVo klineDetailDto) {
+        return symbolsService.findKlineList(klineDetailDto);
+    }
+
     @ApiOperation(value = "查询当日最高最低价")
     @GetMapping(value = "/getDayHighAndLow")
     public Result getDayHighAndLow(@ApiParam(name = "symbol", value = "币种", required = true, example = "BTC/USDT") @RequestParam(value = "symbol") String symbol) {
diff --git a/src/main/java/com/xcong/excoin/modules/symbols/service/SymbolsService.java b/src/main/java/com/xcong/excoin/modules/symbols/service/SymbolsService.java
index be91c71..2125165 100644
--- a/src/main/java/com/xcong/excoin/modules/symbols/service/SymbolsService.java
+++ b/src/main/java/com/xcong/excoin/modules/symbols/service/SymbolsService.java
@@ -2,6 +2,7 @@
 
 
 import com.xcong.excoin.common.response.Result;
+import com.xcong.excoin.modules.newPrice.KlineVo;
 import com.xcong.excoin.modules.symbols.parameter.dto.KlineDetailDto;
 
 /**
@@ -18,4 +19,5 @@
 
     public Result findKlineDetails(KlineDetailDto klineDetailDto);
 
+    public Result findKlineList(KlineVo klineDetailDto);
 }
diff --git a/src/main/java/com/xcong/excoin/modules/symbols/service/impl/SymbolsServiceImpl.java b/src/main/java/com/xcong/excoin/modules/symbols/service/impl/SymbolsServiceImpl.java
index 98af864..cc882f8 100644
--- a/src/main/java/com/xcong/excoin/modules/symbols/service/impl/SymbolsServiceImpl.java
+++ b/src/main/java/com/xcong/excoin/modules/symbols/service/impl/SymbolsServiceImpl.java
@@ -1,12 +1,18 @@
 package com.xcong.excoin.modules.symbols.service.impl;
 
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.huobi.client.model.Candlestick;
 import com.xcong.excoin.common.contants.AppContants;
 import com.xcong.excoin.common.enumerates.SymbolEnum;
 import com.xcong.excoin.common.response.Result;
 import com.xcong.excoin.common.system.mapper.CandlestickMapper;
+import com.xcong.excoin.modules.newPrice.ExchangeInfoEnum;
+import com.xcong.excoin.modules.newPrice.ExchangeLoginService;
+import com.xcong.excoin.modules.newPrice.KlineVo;
 import com.xcong.excoin.modules.platform.dao.PlatformCnyUsdtExchangeDao;
 import com.xcong.excoin.modules.platform.entity.PlatformCnyUsdtExchangeEntity;
 import com.xcong.excoin.modules.symbols.parameter.dto.KlineDetailDto;
@@ -14,6 +20,7 @@
 import com.xcong.excoin.modules.symbols.parameter.vo.KlineDataVo;
 import com.xcong.excoin.modules.symbols.service.SymbolsService;
 import com.xcong.excoin.utils.CoinTypeConvert;
+import com.xcong.excoin.utils.MessageSourceUtils;
 import com.xcong.excoin.utils.RedisUtils;
 import com.xcong.excoin.utils.api.ApiClient;
 import com.xcong.excoin.utils.api.response.Kline;
@@ -24,6 +31,8 @@
 import javax.annotation.Resource;
 import java.math.BigDecimal;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 
 /**
@@ -179,4 +188,25 @@
         }
         return Result.fail("获取数据失败");
     }
+
+    @Override
+    public Result findKlineList(KlineVo klineDetailDto) {
+        Integer type = klineDetailDto.getType();
+        String instId = null;
+        if (AppContants.HOME_SYMBOLS_COIN == type){
+            instId = CoinTypeConvert.convertToCoinKlineKey(klineDetailDto.getInstId());
+        }else if (AppContants.HOME_SYMBOLS_CONTRACT == type){
+            instId = CoinTypeConvert.convertToKlineKey(klineDetailDto.getInstId());
+        }else{
+            return Result.fail("参数错误");
+        }
+        LinkedHashMap<String, Object> requestParam = new LinkedHashMap<>();
+        requestParam.put("instId",instId);
+        String result = ExchangeLoginService.getInstance(ExchangeInfoEnum.OKX_UAT.name()).lineHistory(requestParam);
+        log.info("加载OKX-KLINE,{}", result);
+        JSONObject json = JSON.parseObject(result);
+        String data = json.getString("data");
+        List<String[]> klinesList = JSON.parseArray(data, String[].class);
+        return Result.ok(klinesList);
+    }
 }
diff --git a/src/main/java/com/xcong/excoin/utils/CoinTypeConvert.java b/src/main/java/com/xcong/excoin/utils/CoinTypeConvert.java
index b60c94e..d5a5771 100644
--- a/src/main/java/com/xcong/excoin/utils/CoinTypeConvert.java
+++ b/src/main/java/com/xcong/excoin/utils/CoinTypeConvert.java
@@ -53,6 +53,44 @@
         }
     }
 
+    public static String convertToCoinKlineKey(String symbol) {
+        switch (symbol) {
+            case "BTC/USDT":
+                return "BTC-USDT";
+            case "ETH/USDT":
+                return "ETH-USDT";
+            case "XRP/USDT":
+                return "XRP-USDT";
+            case "LTC/USDT":
+                return "LTC-USDT";
+            case "BCH/USDT":
+                return "BCH-USDT";
+            case "ETC/USDT":
+                return "ETC-USDT";
+            default:
+                return null;
+        }
+    }
+
+    public static String convertToKlineKey(String symbol) {
+        switch (symbol) {
+            case "BTC/USDT":
+                return "BTC-USDT-SWAP";
+            case "ETH/USDT":
+                return "ETH-USDT-SWAP";
+            case "XRP/USDT":
+                return "XRP-USDT-SWAP";
+            case "LTC/USDT":
+                return "LTC-USDT-SWAP";
+            case "BCH/USDT":
+                return "BCH-USDT-SWAP";
+            case "ETC/USDT":
+                return "ETC-USDT-SWAP";
+            default:
+                return null;
+        }
+    }
+
 
 
     public static String convertToOpenKey(String symbol) {

--
Gitblit v1.9.1