|
@@ -0,0 +1,174 @@
|
|
|
|
|
+package top.lvzhiqiang.service.impl;
|
|
|
|
|
+
|
|
|
|
|
+import com.alibaba.fastjson.JSONArray;
|
|
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.apache.commons.codec.binary.Hex;
|
|
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
|
|
+import org.jsoup.Connection;
|
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
|
+import org.springframework.transaction.annotation.Propagation;
|
|
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
+import top.lvzhiqiang.entity.CoinBinanceOrderHistory;
|
|
|
|
|
+import top.lvzhiqiang.entity.CoinBinanceSymbol;
|
|
|
|
|
+import top.lvzhiqiang.mapper.CoinMapper;
|
|
|
|
|
+import top.lvzhiqiang.util.JsoupUtil;
|
|
|
|
|
+
|
|
|
|
|
+import javax.annotation.Resource;
|
|
|
|
|
+import javax.crypto.Mac;
|
|
|
|
|
+import javax.crypto.spec.SecretKeySpec;
|
|
|
|
|
+import javax.management.RuntimeErrorException;
|
|
|
|
|
+import java.math.BigDecimal;
|
|
|
|
|
+import java.math.RoundingMode;
|
|
|
|
|
+import java.net.Proxy;
|
|
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
|
|
+import java.security.InvalidKeyException;
|
|
|
|
|
+import java.security.NoSuchAlgorithmException;
|
|
|
|
|
+import java.util.*;
|
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Coin ServiceImpl
|
|
|
|
|
+ *
|
|
|
|
|
+ * @author lvzhiqiang
|
|
|
|
|
+ * 2023/9/5 15:23
|
|
|
|
|
+ */
|
|
|
|
|
+@Service
|
|
|
|
|
+@Slf4j
|
|
|
|
|
+public class CoinService2Impl implements CoinService2 {
|
|
|
|
|
+
|
|
|
|
|
+ private static final String secretKey = "Do5YDooH9OFimHXsF6nhi7rTZfxIZWWxaP35zsBEktGvCbogtbzBHpJs5cdVmple";
|
|
|
|
|
+ private static final String apiKey = "1qY3LgxmNcW1dku8NiL8NWX8KZG4SBevXHtqLFgzWZbAuJ207CUuf0FSNzIAONh5";
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private CoinMapper coinMapper;
|
|
|
|
|
+
|
|
|
|
|
+ private static Mac MAC;
|
|
|
|
|
+
|
|
|
|
|
+ static {
|
|
|
|
|
+ try {
|
|
|
|
|
+ CoinService2Impl.MAC = Mac.getInstance("HmacSHA256");
|
|
|
|
|
+ } catch (NoSuchAlgorithmException var1) {
|
|
|
|
|
+ throw new RuntimeErrorException(new Error("Can't get Mac's instance."));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
|
|
|
|
|
+ public void syncData4Binance(String startTime, String endTime, String pageSize) {
|
|
|
|
|
+ String baseUrl = "https://fapi.binance.com";
|
|
|
|
|
+ String businessUrl;
|
|
|
|
|
+ String businessUrl2;
|
|
|
|
|
+ Connection.Response response = null;
|
|
|
|
|
+ Proxy proxy = Proxy.NO_PROXY;
|
|
|
|
|
+ Map<String, String> paramMap = new LinkedHashMap<>();
|
|
|
|
|
+ Map<String, String> headerMap = new HashMap<>();
|
|
|
|
|
+
|
|
|
|
|
+ // 1同步合约交易对
|
|
|
|
|
+ /*try {
|
|
|
|
|
+ businessUrl = "/fapi/v1/exchangeInfo";
|
|
|
|
|
+ response = JsoupUtil.requestBody(baseUrl + businessUrl, "GET", proxy, headerMap, paramMap);
|
|
|
|
|
+ JSONArray symbolList = JSONObject.parseObject(response.body()).getJSONArray("symbols");
|
|
|
|
|
+ if (symbolList.size() > 0) {
|
|
|
|
|
+ coinMapper.insertCoinBinanceSymbolList(JSONArray.parseArray(symbolList.toJSONString(), CoinBinanceSymbol.class));
|
|
|
|
|
+ log.warn("syncData4Binance->exchangeInfo,startTime={},endTime={},size={}", startTime, endTime, symbolList.size());
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("syncData4Binance->exchangeInfo error,response={}", response, e);
|
|
|
|
|
+ }*/
|
|
|
|
|
+ // 2同步订单历史
|
|
|
|
|
+ // 2.1获取合约交易对
|
|
|
|
|
+ Map<String, Object> queryParamMap = new HashMap<>();
|
|
|
|
|
+ queryParamMap.put("status", "TRADING");
|
|
|
|
|
+ queryParamMap.put("marginAsset", "USDT");
|
|
|
|
|
+ queryParamMap.put("contractType", "PERPETUAL");
|
|
|
|
|
+ queryParamMap.put("sortField", "onboardDate");
|
|
|
|
|
+ queryParamMap.put("sort", "desc");
|
|
|
|
|
+ List<CoinBinanceSymbol> coinBinanceSymbolList = coinMapper.findCoinBinanceSymbolListByParams(queryParamMap);
|
|
|
|
|
+ // 2.2获取历史订单号集合
|
|
|
|
|
+ List<String> orderIdList = coinMapper.findAllCoinBinanceOrderIdList();
|
|
|
|
|
+ // 2.3循环调取查询所有订单(包括历史订单)接口
|
|
|
|
|
+ headerMap.put("X-MBX-APIKEY", apiKey);
|
|
|
|
|
+ try {
|
|
|
|
|
+ businessUrl = "/fapi/v1/allOrders";
|
|
|
|
|
+ businessUrl2 = "/fapi/v1/userTrades";
|
|
|
|
|
+ for (CoinBinanceSymbol coinBinanceSymbol : coinBinanceSymbolList) {
|
|
|
|
|
+ List<CoinBinanceOrderHistory> coinBinanceOrderHistoryListAll = new ArrayList<>();
|
|
|
|
|
+ //权重: 5;每12秒调1次
|
|
|
|
|
+ Thread.sleep(12000L);
|
|
|
|
|
+
|
|
|
|
|
+ paramMap.clear();
|
|
|
|
|
+ String timestamp = String.valueOf(System.currentTimeMillis());
|
|
|
|
|
+ paramMap.put("symbol", coinBinanceSymbol.getSymbol());
|
|
|
|
|
+ paramMap.put("timestamp", timestamp);
|
|
|
|
|
+ String queryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
|
|
|
|
|
+ String sign = generate("", "", "", queryString, null, secretKey);
|
|
|
|
|
+ paramMap.put("signature", sign);
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ response = JsoupUtil.requestBody(baseUrl + businessUrl, "GET", proxy, headerMap, paramMap);
|
|
|
|
|
+ JSONArray orderList = JSONArray.parseArray(response.body());
|
|
|
|
|
+ if (orderList != null && orderList.size() > 0) {
|
|
|
|
|
+ List<CoinBinanceOrderHistory> coinBinanceOrderHistoryList = JSONArray.parseArray(orderList.toJSONString(), CoinBinanceOrderHistory.class);
|
|
|
|
|
+ for (CoinBinanceOrderHistory coinBinanceOrderHistory : coinBinanceOrderHistoryList) {
|
|
|
|
|
+ if (orderIdList.contains(coinBinanceOrderHistory.getOrderId())) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ paramMap.clear();
|
|
|
|
|
+ timestamp = String.valueOf(System.currentTimeMillis());
|
|
|
|
|
+ paramMap.put("symbol", coinBinanceSymbol.getSymbol());
|
|
|
|
|
+ paramMap.put("orderId", coinBinanceOrderHistory.getOrderId());
|
|
|
|
|
+ paramMap.put("timestamp", timestamp);
|
|
|
|
|
+ queryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
|
|
|
|
|
+ sign = generate("", "", "", queryString, null, secretKey);
|
|
|
|
|
+ paramMap.put("signature", sign);
|
|
|
|
|
+ response = JsoupUtil.requestBody(baseUrl + businessUrl2, "GET", proxy, headerMap, paramMap);
|
|
|
|
|
+ JSONArray userTradeList = JSONArray.parseArray(response.body());
|
|
|
|
|
+ BigDecimal commission = BigDecimal.ZERO;
|
|
|
|
|
+ BigDecimal realizedPnl = BigDecimal.ZERO;
|
|
|
|
|
+ if (userTradeList != null && userTradeList.size() > 0) {
|
|
|
|
|
+ for (int i = 0; i < userTradeList.size(); i++) {
|
|
|
|
|
+ JSONObject userTradeJO = userTradeList.getJSONObject(i);
|
|
|
|
|
+ commission = commission.add(new BigDecimal(userTradeJO.getString("commission")));
|
|
|
|
|
+ realizedPnl = realizedPnl.add(new BigDecimal(userTradeJO.getString("realizedPnl")));
|
|
|
|
|
+ }
|
|
|
|
|
+ coinBinanceOrderHistory.setCommission(commission.setScale(4, RoundingMode.HALF_UP));
|
|
|
|
|
+ coinBinanceOrderHistory.setRealizedPnl(realizedPnl.setScale(4, RoundingMode.HALF_UP));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ coinBinanceOrderHistoryListAll.add(coinBinanceOrderHistory);
|
|
|
|
|
+ System.out.println(coinBinanceOrderHistory);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (coinBinanceOrderHistoryListAll.size() > 0) {
|
|
|
|
|
+ coinMapper.insertOrUpdateBinanceOrderHistoryList(coinBinanceOrderHistoryListAll);
|
|
|
|
|
+ log.warn("syncData4Binance->allOrders sub,startTime={},endTime={},size={}", startTime, endTime, coinBinanceOrderHistoryListAll.size());
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception ee) {
|
|
|
|
|
+ log.error("syncData4Binance->allOrders sub error,response={}", response, ee);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("syncData4Binance->allOrders error,response={}", response, e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static String generate(String timestamp, String method, String requestPath,
|
|
|
|
|
+ String queryString, String body, String secretKey)
|
|
|
|
|
+ throws CloneNotSupportedException, InvalidKeyException {
|
|
|
|
|
+ method = StringUtils.defaultIfBlank(method.toUpperCase(), StringUtils.EMPTY);
|
|
|
|
|
+ body = StringUtils.defaultIfBlank(body, StringUtils.EMPTY);
|
|
|
|
|
+ queryString = StringUtils.isBlank(queryString) ? StringUtils.EMPTY : (StringUtils.isEmpty(method) ? "" : "?") + 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) CoinService2Impl.MAC.clone();
|
|
|
|
|
+ mac.init(secretKeySpec);
|
|
|
|
|
+
|
|
|
|
|
+ return new String(new Hex().encode(mac.doFinal(preHash.getBytes(StandardCharsets.UTF_8))), StandardCharsets.UTF_8);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|