Browse Source

ADD:OKX init

lvzhiqiang 2 năm trước cách đây
mục cha
commit
5fd784a077

+ 92 - 5
src/main/java/top/lvzhiqiang/service/impl/CoinServiceImpl.java

@@ -69,10 +69,12 @@ public class CoinServiceImpl implements CoinService {
 
     // 所有REST请求的header都必须包含以下key:
     private static final Map<String, String> basicHeaderMap = new HashMap<>();
+    private static final Map<String, String> basicHeaderMap4OKX = new HashMap<>();
     // 主域名 URL
     private static final String mainUrl = "https://api.bitget.com";
     // 私钥,由系统随机生成,用于签名的生成。
     private static final String secretKey = "1fdd0fc2976bea80189ba13710e12825ca3ef6c5e25a0d76fd03f8f6cd4a61d9";
+    private static final String secretKey4OKX = "32AC470662FBB633374B9A41950995A9";
 
     @Resource
     private CoinMapper coinMapper;
@@ -108,6 +110,17 @@ public class CoinServiceImpl implements CoinService {
         // 支持多语言, 如:中文(zh-CN),英语(en-US)
         basicHeaderMap.put("locale", "zh-CN");
 
+        // 字符串类型的APIKey
+        basicHeaderMap4OKX.put("OK-ACCESS-KEY","25e4f515-5efd-4bb9-a934-3949b21d9f10");
+        // 使用HMAC SHA256哈希函数获得哈希值,再使用Base-64编码(请参阅签名)
+        basicHeaderMap4OKX.put("OK-ACCESS-SIGN","");
+        // 发起请求的时间(UTC),如:2020-12-08T09:08:57.715Z
+        basicHeaderMap4OKX.put("OK-ACCESS-TIMESTAMP","");
+        // 您在创建API密钥时指定的Passphrase
+        basicHeaderMap4OKX.put("OK-ACCESS-PASSPHRASE","tmvxeGY#Q#Y2qm8");
+        // 统一设置为application/json
+        basicHeaderMap4OKX.put("Content-Type", "application/json");
+
         df1.setRoundingMode(RoundingMode.HALF_UP);
 
 
@@ -320,7 +333,49 @@ public class CoinServiceImpl implements CoinService {
 
     @Override
     public String monitorJob() {
-        // 开仓平仓监控报警
+        // BITGET开仓平仓监控报警
+        scheduler.scheduleWithFixedDelay(() -> {
+            LocalDateTime endTime = LocalDateTime.now();
+            // 全部历史委托列表
+            Map<String, String> paramMap = new LinkedHashMap<>();
+            paramMap.put("productType", "umcbl");
+            paramMap.put("startTime", String.valueOf(DateUtils.localDateTimeToMilliseconds(endTime.minusMinutes(1))));
+            paramMap.put("endTime", String.valueOf(DateUtils.localDateTimeToMilliseconds(endTime)));
+            paramMap.put("pageSize", "100");
+
+            String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
+            try {
+                JSONObject response = requestApi4Common("/api/mix/v1/order/historyProductType", signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
+                JSONArray orderList = response.getJSONObject("data").getJSONArray("orderList");
+
+                for (int i = 0; i < orderList.size(); i++) {
+                    JSONObject order = orderList.getJSONObject(i);
+                    LocalDateTime cTime = DateUtils.longToLocalDateTime(order.getLong("cTime"));
+                    String orderId = order.getString("orderId");
+                    String symbol = order.getString("symbol");
+                    if (Duration.between(cTime, endTime).getSeconds() < 50 && !orderMap.containsKey(orderId)) {
+                        orderMap.put(orderId, "1");
+
+                        String content = "<div class=\"highlight\">交易对:" + order.getString("symbol") + "</div>" +
+                                "<div>交易方向:" + InitRunner.publicParamsMap.get("side").getString(order.getString("side")) + "</div>" +
+                                "<div>杠杆倍数:" + order.getString("leverage") + "</div>" +
+                                "<div>成交均价:" + order.getString("priceAvg") + "</div>" +
+                                "<div>委托价格:" + order.getString("price") + "</div>" +
+                                "<div>订单状态:" + InitRunner.publicParamsMap.get("state").getString(order.getString("state")) + "</div>" +
+                                "<div>订单类型:" + InitRunner.publicParamsMap.get("orderType").getString(order.getString("orderType")) + "</div>" +
+                                "<div class=\"gray\">订单时间:" + DateUtils.longToString(order.getLong("cTime")) + "</div>";
+                        JSONObject params = new JSONObject();
+                        params.put("title", (order.getString("side").contains("open") ? "BITGET合约开单" : "BITGET合约平单") + "报警");
+                        params.put("logUrl", "https://jav.lvzhiqiang.top/coin/orderDetail2/" + orderId + "/" + symbol);
+                        params.put("btnTxt", "订单详情");
+                        SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(content, params, null);
+                    }
+                }
+            } catch (Exception e) {
+            }
+        }, 0, 2, TimeUnit.SECONDS);
+
+        // OKX开仓平仓监控报警
         scheduler.scheduleWithFixedDelay(() -> {
             LocalDateTime endTime = LocalDateTime.now();
             // 全部历史委托列表
@@ -352,7 +407,7 @@ public class CoinServiceImpl implements CoinService {
                                 "<div>订单类型:" + InitRunner.publicParamsMap.get("orderType").getString(order.getString("orderType")) + "</div>" +
                                 "<div class=\"gray\">订单时间:" + DateUtils.longToString(order.getLong("cTime")) + "</div>";
                         JSONObject params = new JSONObject();
-                        params.put("title", (order.getString("side").contains("open") ? "合约开单" : "合约平单") + "监控报警");
+                        params.put("title", (order.getString("side").contains("open") ? "BITGET合约开单" : "BITGET合约平单") + "报警");
                         params.put("logUrl", "https://jav.lvzhiqiang.top/coin/orderDetail2/" + orderId + "/" + symbol);
                         params.put("btnTxt", "订单详情");
                         SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(content, params, null);
@@ -363,7 +418,7 @@ public class CoinServiceImpl implements CoinService {
         }, 0, 2, TimeUnit.SECONDS);
 
         scheduler.scheduleAtFixedRate(() -> {
-            // 全部合约仓位信息V2
+            // BITGET全部合约仓位信息V2
             Map<String, String> paramMap = new HashMap<>();
             paramMap.put("productType", "umcbl");
 
@@ -451,7 +506,7 @@ public class CoinServiceImpl implements CoinService {
             }
         }, 0, 5, TimeUnit.SECONDS);
 
-        // 跟单员监控报警
+        // BITGET跟单员监控报警
         scheduler.scheduleWithFixedDelay(() -> {
             List<String> monitorTraderList = coinMapper.findMonitorTraderList();
             forkJoinPool5.submit(() -> monitorTraderList.parallelStream().forEach(e -> {
@@ -485,7 +540,7 @@ public class CoinServiceImpl implements CoinService {
                                     "<div >交易员:" + split[1] + "</div>" +
                                     "<div class=\"gray\">开仓时间:" + DateUtils.longToString(order.getLong("openTime")) + "</div>";
                             JSONObject params = new JSONObject();
-                            params.put("title", "交易员开单监控报警");
+                            params.put("title", "BITGET交易员开单报警");
                             params.put("logUrl", "https://jav.lvzhiqiang.top/coin/orderDetail/" + order.getString("trackingNo"));
                             params.put("btnTxt", "跟单详情");
                             SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(content, params, null);
@@ -815,6 +870,38 @@ public class CoinServiceImpl implements CoinService {
         }
     }
 
+    private JSONObject requestApi4Common4OKX(String requestPath, String signQueryString, String signBody, String httpMethod, Map<String, String> paramMap) {
+        String timestamp = String.valueOf(System.currentTimeMillis());
+
+        Map<String, String> headerMap = new HashMap<>();
+        headerMap.putAll(basicHeaderMap4OKX);
+        try {
+            String accessSign = CheckSign4Bitget.generate(timestamp, httpMethod, requestPath, signQueryString, signBody, secretKey4OKX);
+            headerMap.put("ACCESS-TIMESTAMP", timestamp);
+            headerMap.put("ACCESS-SIGN", accessSign);
+        } catch (CloneNotSupportedException e) {
+            throw new RuntimeException(e);
+        } catch (InvalidKeyException e) {
+            throw new RuntimeException(e);
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        }
+
+        try {
+            String requestUrl = mainUrl + requestPath;
+
+            if (httpMethod.equals(JsoupUtil.HTTP_GET)) {
+                Connection.Response response = JsoupUtil.requestBody(requestUrl, httpMethod, InitRunner.proxy, headerMap, paramMap);
+                return JSONObject.parseObject(response.body());
+            } else {
+                Connection.Response response = JsoupUtil.requestBodyJSON(requestUrl, httpMethod, InitRunner.proxy, null, headerMap, paramMap);
+                return JSONObject.parseObject(response.body());
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     @Override
     public Object mainSearch(JSONObject params) throws Exception {
         JSONArray result = new JSONArray();

+ 111 - 0
src/main/java/top/lvzhiqiang/util/CheckSign4OKX.java

@@ -0,0 +1,111 @@
+package top.lvzhiqiang.util;
+
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.jsoup.Connection;
+import top.lvzhiqiang.config.InitRunner;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import javax.management.RuntimeErrorException;
+import java.io.UnsupportedEncodingException;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Slf4j
+public class CheckSign4OKX {
+    private static final String secretKey = "32AC470662FBB633374B9A41950995A9";
+
+    public static void main(String[] args) throws Exception {
+        //POST sign example
+//        String timestamp = "1684813405151";
+//        String body = "{\"symbol\":\"TRXUSDT_UMCBL\",\"marginCoin\":\"USDT\",\"size\":551,\"side\":\"open_long\",\"orderType\":\"limit\",\"price\":0.0555,\"timeInForceValue\":\"normal\"}";
+//
+//        String sign = generate(timestamp,"POST","/api/mix/v1/order/placeOrder" ,null,body,secretKey);
+//        log.info("sign:{}",sign);
+
+        //GET sign example
+        String timestamp = ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"));
+        System.out.println(timestamp);
+        String queryString = "ccy=USDT";
+        String sign = generate(timestamp, "GET", "/api/v5/account/balance", queryString, null, secretKey);
+        System.out.println("sign:" + sign);
+
+        Map<String, String> headerMap = new HashMap<>();
+        Map<String, String> basicHeaderMap4OKX = new HashMap<>();
+        basicHeaderMap4OKX.put("OK-ACCESS-KEY","25e4f515-5efd-4bb9-a934-3949b21d9f10");
+        // 使用HMAC SHA256哈希函数获得哈希值,再使用Base-64编码(请参阅签名)
+        basicHeaderMap4OKX.put("OK-ACCESS-SIGN","");
+        // 发起请求的时间(UTC),如:2020-12-08T09:08:57.715Z
+        basicHeaderMap4OKX.put("OK-ACCESS-TIMESTAMP","");
+        // 您在创建API密钥时指定的Passphrase
+        basicHeaderMap4OKX.put("OK-ACCESS-PASSPHRASE","tmvxeGY#Q#Y2qm8");
+        // 统一设置为application/json
+        basicHeaderMap4OKX.put("Content-Type", "application/json");
+        headerMap.putAll(basicHeaderMap4OKX);
+
+        Map<String, String> paramMap = new LinkedHashMap<>();
+        //paramMap.put("instType", "SWAP");
+        String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
+        try {
+            String accessSign = generate(timestamp, "GET", "/api/v5/account/positions-history", signQueryString, null, secretKey);
+            headerMap.put("OK-ACCESS-TIMESTAMP", timestamp);
+            headerMap.put("OK-ACCESS-SIGN", accessSign);
+        } catch (CloneNotSupportedException e) {
+            throw new RuntimeException(e);
+        } catch (InvalidKeyException e) {
+            throw new RuntimeException(e);
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        }
+
+        try {
+            String requestUrl = "https://www.okx.com" + "/api/v5/account/balance";
+            Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 1080));
+
+            Connection.Response response = JsoupUtil.requestBody(requestUrl, "GET", proxy, headerMap, paramMap);
+            System.out.println(JSONObject.parseObject(response.body()));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+
+    private static Mac MAC;
+
+    static {
+        try {
+            CheckSign4OKX.MAC = Mac.getInstance("HmacSHA256");
+        } catch (NoSuchAlgorithmException var1) {
+            throw new RuntimeErrorException(new Error("Can't get Mac's instance."));
+        }
+    }
+
+    public static String generate(String timestamp, String method, String requestPath,
+                                  String queryString, String body, String secretKey)
+            throws CloneNotSupportedException, InvalidKeyException, UnsupportedEncodingException {
+        method = method.toUpperCase();
+        body = StringUtils.defaultIfBlank(body, StringUtils.EMPTY);
+        queryString = StringUtils.isBlank(queryString) ? StringUtils.EMPTY : "?" + queryString;
+        String preHash = timestamp + method + requestPath + queryString + body;
+        //log.info("preHash:{}", preHash);
+        byte[] secretKeyBytes = secretKey.getBytes(StandardCharsets.UTF_8);
+        SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyBytes, "HmacSHA256");
+        Mac mac = (Mac) CheckSign4OKX.MAC.clone();
+        mac.init(secretKeySpec);
+        return Base64.getEncoder().encodeToString(mac.doFinal(preHash.getBytes(StandardCharsets.UTF_8)));
+    }
+}

+ 6 - 4
src/main/java/top/lvzhiqiang/util/DateUtils.java

@@ -15,10 +15,7 @@ package top.lvzhiqiang.util;
 import java.sql.Timestamp;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
-import java.time.Instant;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
+import java.time.*;
 import java.time.format.DateTimeFormatter;
 import java.util.Calendar;
 import java.util.Date;
@@ -55,6 +52,7 @@ public class DateUtils {
 
     public static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(PATTERN_TO_SECONDS);
     public static final DateTimeFormatter dateTimeFormatter3 = DateTimeFormatter.ofPattern(PATTERN_TO_MINUTES);
+    public static final DateTimeFormatter utcTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
 
     public static final DateTimeFormatter dateTimeFormatter2 = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
 
@@ -385,6 +383,10 @@ public class DateUtils {
         return LocalDateTime.parse(time, dateTimeFormatter);
     }
 
+    public static String getUTCTimeStr() {
+        return ZonedDateTime.now(ZoneOffset.UTC).format(utcTimeFormatter);
+    }
+
     public static void main(String[] args) {
         System.out.println(getFewDateStrsByMonth(getToday(), 7));
     }