package top.lvzhiqiang.service.impl; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.cp.api.WxCpGroupRobotService; import me.chanjar.weixin.cp.api.WxCpService; import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl; import me.chanjar.weixin.cp.bean.article.NewArticle; import me.chanjar.weixin.cp.bean.message.WxCpMessage; import me.chanjar.weixin.cp.bean.message.WxCpMessageSendResult; import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl; import org.jsoup.Connection; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StopWatch; import top.lvzhiqiang.config.InitRunner; import top.lvzhiqiang.config.WorkWeixinProperties; import top.lvzhiqiang.entity.*; import top.lvzhiqiang.exception.BusinessException; import top.lvzhiqiang.mapper.CoinApiConfigMapper; import top.lvzhiqiang.mapper.CoinMapper; import top.lvzhiqiang.service.CoinService; import top.lvzhiqiang.util.*; import javax.annotation.Resource; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; import java.security.InvalidKeyException; import java.text.DecimalFormat; import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; /** * Coin ServiceImpl * * @author lvzhiqiang * 2023/9/5 15:23 */ @Service @Slf4j public class CoinServiceImpl implements CoinService { /** * 任务告警方式-应用文本卡片 */ public static final String JOB_ALARM_MODE_APP_TEXT_CARD = "1"; /** * 任务告警方式-群聊机器人 */ public static final String JOB_ALARM_MODE_CHAT_BOT = "2"; /** * 任务告警方式(1:应用文本卡片,2:群聊机器人文本消息) */ public static String JOB_ALARM_MODE = "1"; // 所有REST请求的header都必须包含以下key: private static final Map basicHeaderMap = new HashMap<>(); private static final Map 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; @Resource private WxCpService wxCpService; @Autowired(required = false) private WorkWeixinProperties properties; private final Map orderMap = new ConcurrentHashMap<>(); private final Map mixMap = new ConcurrentHashMap<>(); private final static ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(10); private final ForkJoinPool forkJoinPool = new ForkJoinPool(16); private final ForkJoinPool forkJoinPool2 = new ForkJoinPool(16); private final ForkJoinPool forkJoinPool3 = new ForkJoinPool(16); private final ForkJoinPool forkJoinPool4 = new ForkJoinPool(16); private final ForkJoinPool forkJoinPool5 = new ForkJoinPool(16); private static final DecimalFormat df1 = new DecimalFormat("#,##0.00"); private static final DecimalFormat df2 = new DecimalFormat("#,##0"); private static final WxCpServiceImpl wxCpService4News; @Resource private CoinApiConfigMapper coinApiConfigMapper; static { // API KEY作为一个字符串。 basicHeaderMap.put("ACCESS-KEY", "bg_433d37306df0e8901c6d107c6d9e9111"); // 使用base64编码签名(请参阅签名消息)。 basicHeaderMap.put("ACCESS-SIGN", ""); // 您请求的时间戳。 basicHeaderMap.put("ACCESS-TIMESTAMP", ""); // 您在创建API KEY时设置的口令。 basicHeaderMap.put("ACCESS-PASSPHRASE", "7f934f62f2701bee932204580d115228"); // 统一设置为application/json。 basicHeaderMap.put("Content-Type", "application/json"); // 支持多语言, 如:中文(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); WxCpDefaultConfigImpl wxCpDefaultConfig = new WxCpDefaultConfigImpl(); wxCpDefaultConfig.setCorpId("ww95a4adba56acb55f"); wxCpDefaultConfig.setAgentId(1000004); wxCpDefaultConfig.setCorpSecret("hG50gbVZ8pXm3tSzY3BpwTzbrMmm6sTf8_bPfJG_6Yc"); wxCpService4News = new WxCpServiceImpl(); wxCpService4News.setWxCpConfigStorage(wxCpDefaultConfig); } @Override @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public void syncData(String startTime, String endTime, String pageSize) { // 获取全部历史委托 Map paramMap = new LinkedHashMap<>(); paramMap.put("productType", "umcbl"); paramMap.put("startTime", startTime); paramMap.put("endTime", endTime); paramMap.put("pageSize", pageSize); String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&")); JSONObject response = requestApi4Common("/api/mix/v1/order/historyProductType", signQueryString, null, JsoupUtil.HTTP_GET, paramMap); JSONArray orderList = response.getJSONObject("data").getJSONArray("orderList"); if (orderList.size() > 0) { coinMapper.insertHistoryOrderList(JSONArray.parseArray(orderList.toJSONString(), CoinHistoryOrder.class)); log.warn("syncData->insertHistoryOrderList,startTime={},endTime={},size={}", startTime, endTime, orderList.size()); } } @Override public void syncData4TraderList() { StopWatch stopWatch = new StopWatch(); stopWatch.start(); // 获取交易员列表 Map paramMap = new LinkedHashMap<>(); paramMap.put("sortRule", "composite"); paramMap.put("sortFlag", "desc"); paramMap.put("languageType", "en-US"); paramMap.put("pageSize", "20"); int i = 0; String url = "/api/mix/v1/trace/traderList"; JSONObject response; int totalNum = 0; for (; ; ) { paramMap.put("pageNo", String.valueOf(++i)); String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&")); response = requestApi4Common(url, signQueryString, null, JsoupUtil.HTTP_GET, paramMap); JSONArray dataList = response.getJSONArray("data"); if (dataList.size() == 0) { break; } try { syncData4TraderListSub(dataList); } catch (Exception e) { log.error("syncData4TraderListSub error,paramMap={}", paramMap, e); } totalNum += dataList.size(); } log.warn("syncData4TraderList 结束:time={},totalNum={}", stopWatch.getTotalTimeSeconds(), totalNum); } @Override public void syncCoinmarketcapCMap() { StopWatch stopWatch = new StopWatch(); stopWatch.start(); String coinmarketcapApikey = InitRunner.dicCodeMap.get("coinmarketcap_apikey").getCodeValue(); String coinmarketcapIdmapUrl = InitRunner.dicCodeMap.get("coinmarketcap_idmap_url").getCodeValue(); String coinmarketcapIdmapParams4listingStatus = InitRunner.dicCodeMap.get("coinmarketcap_idmap_params_listing_status").getCodeValue(); String coinmarketcapIdmapParams4aux = InitRunner.dicCodeMap.get("coinmarketcap_idmap_params_aux").getCodeValue(); Map headerMap = new HashMap<>(); headerMap.put("Accept", "application/json"); headerMap.put("Accept-Encoding", "deflate,gzip"); headerMap.put("X-CMC_PRO_API_KEY", coinmarketcapApikey); String[] listingStatusArr = coinmarketcapIdmapParams4listingStatus.split(","); Map paramMap = new LinkedHashMap<>(); int MAX_NUMBER = 1000; int MAX_NUMBER2 = 5000; Long totalNum = 0L; for (String listingStatus : listingStatusArr) { paramMap.put("listing_status", listingStatus); paramMap.put("aux", coinmarketcapIdmapParams4aux); try { int j = 0; Long totalNum2 = 0L; for (; ; ) { Thread.sleep(3000L); j++; paramMap.put("start", String.valueOf((j - 1) * MAX_NUMBER2 + 1)); paramMap.put("limit", String.valueOf(MAX_NUMBER2)); Connection.Response response = JsoupUtil.requestBody(coinmarketcapIdmapUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, headerMap, paramMap); JSONObject result = JSONObject.parseObject(response.body()); JSONArray dataJA = result.getJSONArray("data"); List cmcMapList = new ArrayList<>(); CoinCmcMap coinCmcMap; for (int i = 0; i < dataJA.size(); i++) { JSONObject dataJO = dataJA.getJSONObject(i); coinCmcMap = new CoinCmcMap(); coinCmcMap.setCmcId(dataJO.getLong("id")); coinCmcMap.setCmcRank(dataJO.getLong("rank")); coinCmcMap.setName(dataJO.getString("name")); coinCmcMap.setSymbol(dataJO.getString("symbol")); coinCmcMap.setSlug(dataJO.getString("slug")); coinCmcMap.setIsActive(dataJO.getInteger("is_active")); coinCmcMap.setStatus(dataJO.getString("status")); coinCmcMap.setFirstHistoricalData(DateUtils.stringutcToLocalDateTime(dataJO.getString("first_historical_data"))); coinCmcMap.setLastHistoricalData(DateUtils.stringutcToLocalDateTime(dataJO.getString("last_historical_data"))); coinCmcMap.setPlatform(dataJO.getString("platform")); cmcMapList.add(coinCmcMap); } // 新增或者更新 Stream.iterate(0, n -> n + 1).limit((cmcMapList.size() + MAX_NUMBER - 1) / MAX_NUMBER) .forEach(i -> { List list = cmcMapList.stream().skip((long) i * MAX_NUMBER).limit(MAX_NUMBER).collect(Collectors.toList()); coinMapper.insertCmcMapList(list); }); totalNum += cmcMapList.size(); totalNum2 += cmcMapList.size(); if (dataJA.size() < MAX_NUMBER2) { break; } } log.warn("syncCoinmarketcapCMap {} success,totalNum={}", listingStatus, totalNum2); } catch (Exception e) { log.error("syncCoinmarketcapCMap {} error", listingStatus, e); } } log.warn("syncCoinmarketcapCMap 结束:time={},totalNum={}", stopWatch.getTotalTimeSeconds(), totalNum); } @Override @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public void syncData4TraderListSub(JSONArray dataList) { coinMapper.insertMixTradeList(parseMixTradeList(dataList)); } @Override public String watchlistDetail(String symbol, String operationType) { CoinWatchlist coinWatchlist = coinMapper.findWatchlistBySymbol(symbol); if (coinWatchlist == null) { throw new BusinessException(1, "symbol不存在!"); } if ("detail".equals(operationType)) { return MarkdownToHtmlUtils.markdownToHtmlExtensions(coinWatchlist.getRemark()); } else if ("update".equals(operationType)) { return coinWatchlist.getRemark(); } else { return "暂不支持该操作!"; } } @Override public Object watchlistUpdate(String symbol, String remark) { CoinWatchlist coinWatchlist = new CoinWatchlist(); coinWatchlist.setSymbol(symbol); coinWatchlist.setRemark(remark); int num = coinMapper.updateCoinWatchlistRemark(coinWatchlist); return num; } private List parseMixTradeList(JSONArray dataList) { List mixTraderList = JSONArray.parseArray(dataList.toJSONString(), CoinTrader.class); mixTraderList.stream().forEach(e -> { Map columnMap = e.getColumnList().stream().filter(Objects::nonNull) .collect(Collectors.toMap( object -> { JSONObject item = (JSONObject) object; return item.getString("describe"); }, object -> { JSONObject item = (JSONObject) object; return item.getString("value"); } )); e.setRoi(columnMap.get("ROI")); e.setTotalProfit(columnMap.get("Total PnL").replace("$", "").replace(",", "")); e.setTotalFollowersProfit(columnMap.get("Total followers PnL").replace("$", "").replace(",", "")); e.setAum(columnMap.get("AUM").replace("$", "").replace(",", "")); e.setMaxCallbackRate(columnMap.get("Max drawdown")); e.setLast3wWinRate(columnMap.get("Last 3W win rate")); e.setAverageWinRate(StringUtils.isNotEmpty(e.getAverageWinRate()) ? new BigDecimal(e.getAverageWinRate()).setScale(2, RoundingMode.HALF_UP).toPlainString() : "0.00"); }); return mixTraderList; } @Override public String orderDetail(String trackingNo) { Map paramMap = new LinkedHashMap<>(); paramMap.put("traderId", "b1b5467f8bb73f53ac97"); paramMap.put("pageSize", "20"); StringBuffer sb = new StringBuffer(); // 交易员当前带单列表筛选 for (int j = 1; j < 5; j++) { try { paramMap.put("pageNo", j + ""); JSONObject response = requestApi4Common("/api/mix/v1/trace/report/order/currentList", null, JSONObject.toJSONString(paramMap), JsoupUtil.HTTP_POST, paramMap); JSONArray orderList = response.getJSONArray("data"); for (int i = 0; i < orderList.size(); i++) { JSONObject order = orderList.getJSONObject(i); String trackingNo1 = order.getString("trackingNo"); if (trackingNo.equals(trackingNo1)) { sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append("
交易对").append(order.getString("symbol")).append("
持仓方向").append(InitRunner.publicParamsMap.get("holdSide").getString(order.getString("holdSide"))).append("
杠杆倍数").append(order.getString("leverage")).append("
开仓均价").append(order.getString("openPrice")).append("
开仓时间").append(DateUtils.longToString(order.getLong("openTime"))).append("
此笔订单跟单人数").append(order.getString("followerNum")).append("
保证金").append(order.getString("marginAmount")).append("
止盈价").append(order.getString("takeProfitPrice")).append("
止损价").append(order.getString("stopLossPrice")).append("
交易员").append("hale").append("
"); break; } } } catch (Exception e) { } } // 交易员历史带单列表筛选 if (sb.length() == 0) { for (int j = 1; j < 5; j++) { try { paramMap.put("pageNo", j + ""); JSONObject response = requestApi4Common("/api/mix/v1/trace/report/order/historyList", null, JSONObject.toJSONString(paramMap), JsoupUtil.HTTP_POST, paramMap); JSONArray orderList = response.getJSONArray("data"); for (int i = 0; i < orderList.size(); i++) { JSONObject order = orderList.getJSONObject(i); String trackingNo1 = order.getString("trackingNo"); if (trackingNo.equals(trackingNo1)) { sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append("
交易对").append(order.getString("symbol")).append("
持仓方向").append(InitRunner.publicParamsMap.get("holdSide").getString(order.getString("holdSide"))).append("
杠杆倍数").append(order.getString("leverage")).append("
开仓均价").append(order.getString("openPrice")).append("
开仓时间").append(DateUtils.longToString(order.getLong("openTime"))).append("
此笔订单跟单人数").append(order.getString("followerNum")).append("
保证金").append(order.getString("marginAmount")).append("
平仓均价").append(order.getString("closePrice")).append("
平仓时间").append(DateUtils.longToString(order.getLong("closeTime"))).append("
平仓数量").append(order.getString("closeAmount")).append("
交易员").append("hale").append("
"); break; } } } catch (Exception e) { } } } return sb.toString(); } @Override public String orderDetail2(String orderId, String symbol) { Map paramMap = new LinkedHashMap<>(); paramMap.put("symbol", symbol); paramMap.put("orderId", orderId); StringBuffer sb = new StringBuffer(""); // 获取订单详情 try { String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&")); JSONObject response = requestApi4Common("/api/mix/v1/order/detail", signQueryString, null, JsoupUtil.HTTP_GET, paramMap); JSONObject order = response.getJSONObject("data"); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); sb.append(""); } catch (Exception e) { log.error("orderDetail2 error,orderId={},symbol={}", orderId, symbol, e); } sb.append("
交易对").append(order.getString("symbol")).append("
交易方向").append(InitRunner.publicParamsMap.get("side").getString(order.getString("side"))).append("
杠杆倍数").append(order.getString("leverage")).append("
成交均价").append(order.getString("priceAvg")).append("
委托价格").append(order.getString("price")).append("
手续费").append(order.getString("fee")).append("
订单状态").append(InitRunner.publicParamsMap.get("state").getString(order.getString("state"))).append("
交易类型").append(InitRunner.publicParamsMap.get("orderType").getString(order.getString("orderType"))).append("
总盈亏").append(order.getString("totalProfits")).append("
预设止盈价格").append(order.getString("presetTakeProfitPrice")).append("
预设止损价格").append(order.getString("presetStopLossPrice")).append("
创建时间").append(DateUtils.longToString(order.getLong("cTime"))).append("
更新时间").append(DateUtils.longToString(order.getLong("uTime"))).append("
"); return sb.toString(); } @Override public String monitorJob() { // BITGET开仓平仓监控报警 scheduler.scheduleWithFixedDelay(() -> { LocalDateTime endTime = LocalDateTime.now(); // 全部历史委托列表 Map 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 = "
交易对:" + order.getString("symbol") + "
" + "
交易方向:" + InitRunner.publicParamsMap.get("side").getString(order.getString("side")) + "
" + "
杠杆倍数:" + order.getString("leverage") + "
" + "
成交均价:" + order.getString("priceAvg") + "
" + "
委托价格:" + order.getString("price") + "
" + "
订单状态:" + InitRunner.publicParamsMap.get("state").getString(order.getString("state")) + "
" + "
订单类型:" + InitRunner.publicParamsMap.get("orderType").getString(order.getString("orderType")) + "
" + "
订单时间:" + DateUtils.longToString(order.getLong("cTime")) + "
"; 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(); // 查看历史持仓信息 Map paramMap = new LinkedHashMap<>(); paramMap.put("instType", "SWAP"); String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&")); try { JSONObject response = requestApi4Common4OKX("/api/v5/account/positions-history", signQueryString, null, JsoupUtil.HTTP_GET, paramMap); JSONArray orderList = response.getJSONArray("data"); for (int i = 0; i < orderList.size(); i++) { JSONObject order = orderList.getJSONObject(i); //LocalDateTime cTime = DateUtils.longToLocalDateTime(order.getLong("cTime")); String orderId = "okx" + order.getString("posId"); String symbol = order.getString("ccy"); if (!orderMap.containsKey(orderId)) { orderMap.put(orderId, "1"); log.warn("okx ={}", order); String content = "
交易对:" + symbol + "
"; JSONObject params = new JSONObject(); params.put("title", "OKX报警"); 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, 10, TimeUnit.SECONDS); scheduler.scheduleAtFixedRate(() -> { // BITGET全部合约仓位信息V2 Map paramMap = new HashMap<>(); paramMap.put("productType", "umcbl"); String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&")); try { JSONObject response = requestApi4Common("/api/mix/v1/position/allPosition-v2", signQueryString, null, JsoupUtil.HTTP_GET, paramMap); JSONArray mixList = response.getJSONArray("data"); for (int i = 0; i < mixList.size(); i++) { JSONObject mixData = mixList.getJSONObject(i); String symbol = mixData.getString("symbol"); String margin = mixData.getString("margin"); String averageOpenPrice = mixData.getString("averageOpenPrice"); String key = symbol + margin + averageOpenPrice; // 回报率=未实现盈亏/保证金 // 持仓方向 long:多头 short:空头 String holdSide = mixData.getString("holdSide"); BigDecimal returnRate = new BigDecimal(mixData.getString("unrealizedPL")).divide(new BigDecimal(margin), 4, RoundingMode.HALF_UP); for (int j = 1; j <= 10; j++) { BigDecimal grid = BigDecimal.valueOf(0.5).multiply(BigDecimal.valueOf(j)); BigDecimal minusGrid = BigDecimal.valueOf(-0.5).multiply(BigDecimal.valueOf(j)); if (returnRate.compareTo(grid) < 0) { if (mixMap.containsKey(key)) { mixMap.get(key).put("returnRate", returnRate); } else { JSONObject jsonObject = new JSONObject(); jsonObject.put("returnRate", returnRate); mixMap.put(key, jsonObject); } break; } if (returnRate.compareTo(grid) > 0) { if (mixMap.containsKey(key)) { mixMap.get(key).put("returnRate", returnRate); if (mixMap.get(key).containsKey(grid.toPlainString())) { continue; } else { mixMap.get(key).put(grid.toPlainString(), true); String requestUrl = mainUrl + "/api/mix/v1/market/ticker?symbol=" + symbol; String last = "--"; try { Connection.Response responseTicker = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null); last = JSONObject.parseObject(responseTicker.body()).getJSONObject("data").getString("last"); } catch (Exception e) { } String content = "币对名称:" + symbol + "\n" + "持仓方向:" + InitRunner.publicParamsMap.get("holdSide").getString(mixData.getString("holdSide")) + "\n" + "杠杆倍数:" + mixData.getString("leverage") + "\n" + "开仓均价:" + mixData.getString("averageOpenPrice") + "\n" + "当前价格:" + last + "\n" + "回报率:" + returnRate.multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP).toPlainString() + ",超过" + grid.multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP).toPlainString(); SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4CHAT_BOT(content, null); } } else { JSONObject jsonObject = new JSONObject(); jsonObject.put("returnRate", returnRate); jsonObject.put(grid.toPlainString(), true); mixMap.put(key, jsonObject); String requestUrl = mainUrl + "/api/mix/v1/market/ticker?symbol=" + symbol; String last = "--"; try { Connection.Response responseTicker = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null); last = JSONObject.parseObject(responseTicker.body()).getJSONObject("data").getString("last"); } catch (Exception e) { } String content = "币对名称:" + symbol + "\n" + "持仓方向:" + InitRunner.publicParamsMap.get("holdSide").getString(mixData.getString("holdSide")) + "\n" + "杠杆倍数:" + mixData.getString("leverage") + "\n" + "开仓均价:" + mixData.getString("averageOpenPrice") + "\n" + "当前价格:" + last + "\n" + "回报率:" + returnRate.multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP).toPlainString() + ",超过" + grid.multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP).toPlainString(); SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4CHAT_BOT(content, null); //break; } } } } } catch (Exception e) { e.printStackTrace(); } }, 0, 5, TimeUnit.SECONDS); // BITGET跟单员监控报警 scheduler.scheduleWithFixedDelay(() -> { List monitorTraderList = coinMapper.findMonitorTraderList(); forkJoinPool5.submit(() -> monitorTraderList.parallelStream().forEach(e -> { LocalDateTime endTime = LocalDateTime.now(); // 交易员当前带单列表 Map paramMap = new LinkedHashMap<>(); String[] split = e.split("\\|"); paramMap.put("traderId", split[0]); paramMap.put("pageNo", "1"); paramMap.put("pageSize", "20"); try { JSONObject response = requestApi4Common("/api/mix/v1/trace/report/order/currentList", null, JSONObject.toJSONString(paramMap), JsoupUtil.HTTP_POST, paramMap); JSONArray orderList = response.getJSONArray("data"); for (int i = 0; i < orderList.size(); i++) { JSONObject order = orderList.getJSONObject(i); LocalDateTime openTime = DateUtils.longToLocalDateTime(order.getLong("openTime")); String trackingNo = order.getString("trackingNo"); if (Duration.between(openTime, endTime).getSeconds() < 50 && !orderMap.containsKey(trackingNo)) { orderMap.put(trackingNo, "1"); String content = "
交易对:" + order.getString("symbol") + "
" + "
持仓方向:" + InitRunner.publicParamsMap.get("holdSide").getString(order.getString("holdSide")) + "
" + "
杠杆倍数:" + order.getString("leverage") + "
" + "
开仓均价:" + order.getString("openPrice") + "
" + "
止盈价:" + order.getString("takeProfitPrice") + "
" + "
止损价:" + order.getString("stopLossPrice") + "
" + "
交易员:" + split[1] + "
" + "
开仓时间:" + DateUtils.longToString(order.getLong("openTime")) + "
"; JSONObject params = new JSONObject(); 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); } } } catch (Exception ex) { } })).join(); }, 0, 3, TimeUnit.SECONDS); // 星球日报新闻快讯监控报警 /*scheduler.scheduleWithFixedDelay(() -> { try { Connection.Response response = JsoupUtil.requestBody("https://www.odaily.news/v1/openapi/feeds", JsoupUtil.HTTP_GET, InitRunner.proxy, null, null); JSONObject result = JSONObject.parseObject(response.body()); JSONArray newsList = result.getJSONObject("data").getJSONArray("arr_news"); LocalDateTime endTime = LocalDateTime.now(); for (int i = 0; i < newsList.size(); i++) { if (i == 5) { break; } JSONObject news = newsList.getJSONObject(i); String id = "Odaily" + news.getString("id"); String publishedAt = news.getString("published_at"); LocalDateTime publishedAtTime = DateUtils.stringToLocalDateTime(publishedAt); if (Duration.between(publishedAtTime, endTime).getSeconds() < 20 && !orderMap.containsKey(id)) { orderMap.put(id, "1"); String type = news.getString("type"); String title = news.getString("title"); String link = news.getString("link"); String content = ""; JSONObject params = new JSONObject(); params.put("title", "Odaily监控报警"); params.put("btnTxt", "新闻详情"); params.put("logUrl", link); params.put("user", "@all"); params.put("agentId", 1000004); if ("newsflashes".equals(type)) { String newsUrl = news.getString("news_url"); content = "
标题:" + title + "
" + "
类型:" + "新闻快讯" + "
" + "
发布时间:" + publishedAt + "
" + "
描述:" + news.getString("description").replace("\n", "    ") + "
"; } else if ("posts".equals(type)) { content = "
标题:" + title + "
" + "
类型:" + "帖子" + "
" + "
发布时间:" + publishedAt + "
" + "
描述:" + news.getString("summary").replace("\n", "    ") + "
"; } SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(content, params, wxCpService4News); } } } catch (Exception e) { } }, 0, 3, TimeUnit.SECONDS);*/ // 律动日报新闻快讯监控报警 scheduler.scheduleWithFixedDelay(() -> { try { Map paramMap = new HashMap<>(); paramMap.put("size", "10"); paramMap.put("page", "1"); Connection.Response response = JsoupUtil.requestBody("https://api.theblockbeats.news/v1/open-api/open-flash", JsoupUtil.HTTP_GET, InitRunner.proxy, null, paramMap); JSONObject result = JSONObject.parseObject(response.body()); JSONArray newsList = result.getJSONObject("data").getJSONArray("data"); LocalDateTime endTime = LocalDateTime.now(); for (int i = 0; i < newsList.size(); i++) { if (i == 5) { break; } JSONObject news = newsList.getJSONObject(i); String id = "BlockBeats" + news.getString("id"); String createTime = news.getString("create_time"); LocalDateTime createTimeTime = DateUtils.longToLocalDateTime_(Long.valueOf(createTime)); createTime = DateUtils.localDateTimeToString(createTimeTime); if (Duration.between(createTimeTime, endTime).getSeconds() < 20 && !orderMap.containsKey(id)) { orderMap.put(id, "1"); String title = news.getString("title"); String content = news.getString("content"); String pic = news.getString("pic"); String link = news.getString("link"); String url = news.getString("url"); JSONObject params = new JSONObject(); params.put("title", "BlockBeats监控报警"); params.put("btnTxt", "新闻详情"); params.put("logUrl", link); params.put("user", "@all"); params.put("agentId", 1000004); if (StringUtils.isEmpty(pic)) { // 文本卡片 String contentStr = "
标题:" + title + "
" + "
类型:" + "新闻快讯" + "
" + "
发布时间:" + createTime + "
" + "
描述:" + content.replace("\n", "    ") + "
"; if (title.contains("Upbit") || title.contains("upbit")) { JSONObject params4Upbit = new JSONObject(); params4Upbit.put("title", "BlockBeats监控报警"); params4Upbit.put("logUrl", link); params4Upbit.put("btnTxt", "新闻详情"); SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(contentStr, params4Upbit, null); } SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(contentStr, params, wxCpService4News); } else { // 图文 NewArticle article1 = new NewArticle(); article1.setUrl(link); article1.setPicUrl(pic); article1.setDescription(content); article1.setTitle(title); if (title.contains("Upbit") || title.contains("upbit")) { JSONObject params4Upbit = new JSONObject(); params4Upbit.put("title", "BlockBeats监控报警"); params4Upbit.put("logUrl", link); params4Upbit.put("btnTxt", "新闻详情"); SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4NEWS(params4Upbit, null, article1); } SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4NEWS(params, wxCpService4News, article1); } } } } catch (Exception e) { } }, 0, 3, TimeUnit.SECONDS); // coingecko scheduler.scheduleAtFixedRate(() -> { Map params = new HashMap<>(); params.put("sortField", "create_time"); params.put("sort", "desc"); List watchlistList = coinMapper.findWatchlistList(params); Map coinWatchlistMap4CoingeckoId = watchlistList.stream().collect(Collectors.toMap(CoinWatchlist::getCoingeckoId, coinWatchlist -> coinWatchlist)); parseWatchlistMap4Coingecko(coinWatchlistMap4CoingeckoId); Map coinWatchlistMap4CmcId = coinWatchlistMap4CoingeckoId.values().stream().collect(Collectors.toMap(CoinWatchlist::getCmcId, coinWatchlist -> coinWatchlist)); parseWatchlistMap4CmC(coinWatchlistMap4CmcId); }, 0, 1, TimeUnit.HOURS); // Upbit交易所监控报警 scheduler.scheduleWithFixedDelay(() -> { try { Map paramMap = new HashMap<>(); paramMap.put("page", "1"); paramMap.put("per_page", "20"); paramMap.put("thread_name", "general"); Connection.Response response = JsoupUtil.requestBody("https://api-manager.upbit.com/api/v1/notices", JsoupUtil.HTTP_GET, InitRunner.proxy, null, paramMap); JSONObject result = JSONObject.parseObject(response.body()); JSONArray noticeList = result.getJSONObject("data").getJSONArray("list"); for (int i = 0; i < noticeList.size(); i++) { if (i == 5) { break; } JSONObject notice = noticeList.getJSONObject(i); String idOri = notice.getString("id"); String id = "Upbit" + idOri; String title = notice.getString("title"); if (title.contains("New digital asset on KRW Market") && !orderMap.containsKey(id)) { orderMap.put(id, "1"); String createTime = notice.getString("created_at"); String updateTime = notice.getString("updated_at"); String viewCount = notice.getString("view_count"); JSONObject params = new JSONObject(); params.put("title", "Upbit监控报警"); params.put("btnTxt", "通知详情"); params.put("logUrl", "https://sg-api-manager.upbit.com/api/v1/notices/" + idOri); // 文本卡片 String contentStr = "
标题:" + title + "
" + "
发布时间:" + createTime + "
" + "
更新时间:" + updateTime + "
" + "
查看次数:" + viewCount + "
"; SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(contentStr, params, null); } } } catch (Exception e) { } }, 0, 3, TimeUnit.SECONDS); return null; } public void parseWatchlistMap4Coingecko(Map watchlistMap4Coingecko) { String coingeckoCoinsMarketsUrl = InitRunner.dicCodeMap.get("coingecko_coins_markets_url").getCodeValue(); Map headerMap = new HashMap<>(); headerMap.put("Accept", "application/json"); headerMap.put("Accept-Encoding", "deflate,gzip"); Map paramMap = new LinkedHashMap<>(); paramMap.put("ids", StringUtils.join(watchlistMap4Coingecko.keySet(), ",")); paramMap.put("vs_currency", "usd"); AtomicInteger i = new AtomicInteger(); try { Connection.Response response = JsoupUtil.requestBody(coingeckoCoinsMarketsUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, headerMap, paramMap); JSONArray result = JSONArray.parseArray(response.body()); for (int j = 0; j < result.size(); j++) { JSONObject marketData = result.getJSONObject(j); String id = marketData.getString("id"); if (watchlistMap4Coingecko.containsKey(id)) { CoinWatchlist coinWatchlist = watchlistMap4Coingecko.get(id); // 总市值排名 if (marketData.containsKey("market_cap_rank") && null != marketData.get("market_cap_rank")) { Integer totalMarketRanking = marketData.getInteger("market_cap_rank"); coinWatchlist.setTotalMarketRanking(totalMarketRanking); } // 总市值 if (marketData.containsKey("market_cap") && null != marketData.get("market_cap")) { BigDecimal totalMarketValue = marketData.getBigDecimal("market_cap").setScale(2, RoundingMode.HALF_UP); coinWatchlist.setTotalMarketValue(totalMarketValue); } // 市场价格 if (marketData.containsKey("current_price") && null != marketData.get("current_price")) { String markPrice = marketData.getBigDecimal("current_price").toPlainString(); coinWatchlist.setMarkPrice(markPrice); } // 历史最高价格 if (marketData.containsKey("ath") && null != marketData.get("ath")) { String highestHistoricalPrice = marketData.getBigDecimal("ath").toPlainString(); coinWatchlist.setHighestHistoricalPrice(highestHistoricalPrice); } // 历史最高日期 if (marketData.containsKey("ath_date") && null != marketData.get("ath_date")) { LocalDate highestHistoricalDate = LocalDate.parse(marketData.getString("ath_date"), DateUtils.utcTimeFormatter); coinWatchlist.setHighestHistoricalDate(highestHistoricalDate); } // 历史最低价格 if (marketData.containsKey("atl") && null != marketData.get("atl")) { String lowestHistoricalPrice = marketData.getBigDecimal("atl").toPlainString(); coinWatchlist.setLowestHistoricalPrice(lowestHistoricalPrice); } // 历史最低日期 if (marketData.containsKey("atl_date") && null != marketData.get("atl_date")) { LocalDate lowestHistoricalDate = LocalDate.parse(marketData.getString("atl_date"), DateUtils.utcTimeFormatter); coinWatchlist.setLowestHistoricalDate(lowestHistoricalDate); } // 涨幅倍数 if (StringUtils.isNotEmpty(coinWatchlist.getHighestHistoricalPrice()) && StringUtils.isNotEmpty(coinWatchlist.getLowestHistoricalPrice())) { BigDecimal increaseMultiple = new BigDecimal(coinWatchlist.getHighestHistoricalPrice()).divide(new BigDecimal(coinWatchlist.getLowestHistoricalPrice()), 0, RoundingMode.HALF_UP); coinWatchlist.setIncreaseMultiple(increaseMultiple.intValue()); } // 发行日期 // 发行天数 if (coinWatchlist.getIssuingDate() != null) { long totalDays = ChronoUnit.DAYS.between(coinWatchlist.getIssuingDate(), LocalDate.now()); coinWatchlist.setIssuingDays((int) totalDays); } coinMapper.updateCoinWatchlist(coinWatchlist); } } } catch (Exception e) { log.error("parseWatchlistMap4Coingecko error,size={},i={}", watchlistMap4Coingecko.size(), i.get(), e); } } public void parseWatchlistMap4CmC(Map watchlistMap4CmC) { String coinmarketcapApikey = InitRunner.dicCodeMap.get("coinmarketcap_apikey").getCodeValue(); String coinmarketcapQuotesLatestUrl = InitRunner.dicCodeMap.get("coinmarketcap_quotes_latest_url").getCodeValue(); Map headerMap = new HashMap<>(); headerMap.put("Accept", "application/json"); headerMap.put("Accept-Encoding", "deflate,gzip"); headerMap.put("X-CMC_PRO_API_KEY", coinmarketcapApikey); Map paramMap = new LinkedHashMap<>(); paramMap.put("id", StringUtils.join(watchlistMap4CmC.keySet(), ",")); AtomicInteger i = new AtomicInteger(); try { Connection.Response response = JsoupUtil.requestBody(coinmarketcapQuotesLatestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, headerMap, paramMap); JSONObject result = JSONObject.parseObject(response.body()); JSONObject dataJO = result.getJSONObject("data"); watchlistMap4CmC.forEach((key, value) -> { i.getAndIncrement(); if (dataJO.containsKey(key)) { JSONObject jsonObject = dataJO.getJSONObject(String.valueOf(key)); value.setTotalMarketRanking(jsonObject.getInteger("cmc_rank")); BigDecimal totalMarketValue = jsonObject.getJSONObject("quote").getJSONObject("USD").getBigDecimal("market_cap").setScale(2, RoundingMode.HALF_UP); if (totalMarketValue.compareTo(BigDecimal.ZERO) == 0) { totalMarketValue = jsonObject.getBigDecimal("self_reported_market_cap").setScale(2, RoundingMode.HALF_UP); } value.setTotalMarketValue(totalMarketValue); coinMapper.updateCoinWatchlist(value); } }); } catch (Exception e) { log.error("parseWatchlistMap4CmC error,size={},i={}", watchlistMap4CmC.size(), i.get(), e); } } @Override @Async("coinTaskExecutor") public void monitorAlarm4APP_TEXT_CARD(String content, JSONObject params, WxCpService wxCpServiceFinal) { // 文本卡片模式发消息 String title = "监控告警明细"; if (params.containsKey("title")) { title = params.getString("title"); } String logUrl = "https://lvzhiqiang.top"; if (params.containsKey("logUrl")) { logUrl = params.getString("logUrl"); } String btnTxt = "日志详情"; if (params.containsKey("btnTxt")) { btnTxt = params.getString("btnTxt"); } String user = "LvZhiQiang"; if (params.containsKey("user")) { user = params.getString("user"); } String party = ""; if (params.containsKey("party")) { party = params.getString("party"); } String tag = ""; if (params.containsKey("tag")) { tag = params.getString("tag"); } Integer agentId = properties.getAgentId(); if (params.containsKey("agentId")) { agentId = params.getInteger("agentId"); } if (wxCpServiceFinal == null) { wxCpServiceFinal = wxCpService; } WxCpMessage wxCpMessage = WxCpMessage.TEXTCARD().agentId(agentId) .toUser(user) .toParty(party) .toTag(tag) .title(title).description(content) .url(logUrl).btnTxt(btnTxt) .build(); try { log.info("企业微信推送消息,send message: {}", wxCpMessage); WxCpMessageSendResult sendResult = wxCpServiceFinal.getMessageService().send(wxCpMessage); log.info("企业微信推送消息成功,send result: {}", sendResult); } catch (WxErrorException e) { log.error("企业微信推送消息失败!Detail: ", e); } } @Override @Async("coinTaskExecutor") public void monitorAlarm4NEWS(JSONObject params, WxCpService wxCpServiceFinal, NewArticle... articles) { // 图文消息 String title = "监控告警明细"; if (params.containsKey("title")) { title = params.getString("title"); } String logUrl = "https://lvzhiqiang.top"; if (params.containsKey("logUrl")) { logUrl = params.getString("logUrl"); } String btnTxt = "日志详情"; if (params.containsKey("btnTxt")) { btnTxt = params.getString("btnTxt"); } String user = "LvZhiQiang"; if (params.containsKey("user")) { user = params.getString("user"); } String party = ""; if (params.containsKey("party")) { party = params.getString("party"); } String tag = ""; if (params.containsKey("tag")) { tag = params.getString("tag"); } Integer agentId = properties.getAgentId(); if (params.containsKey("agentId")) { agentId = params.getInteger("agentId"); } if (wxCpServiceFinal == null) { wxCpServiceFinal = wxCpService; } WxCpMessage wxCpMessage = WxCpMessage.NEWS().agentId(agentId) .toUser(user) .toParty(party) .toTag(tag) .addArticle(articles) .build(); try { log.info("企业微信推送消息,send message: {}", wxCpMessage); WxCpMessageSendResult sendResult = wxCpServiceFinal.getMessageService().send(wxCpMessage); log.info("企业微信推送消息成功,send result: {}", sendResult); } catch (WxErrorException e) { log.error("企业微信推送消息失败!Detail: ", e); } } @Override @Async("coinTaskExecutor") public void monitorAlarm4CHAT_BOT(String content, JSONObject params) { // 调用企业微信群聊机器人发消息 WxCpGroupRobotService groupRobotService = wxCpService.getGroupRobotService(); String webhookUrl = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=082970da-2a33-422a-81f6-15f9bde87940"; List userList = Collections.singletonList("LvZhiQiang"); if (params != null && params.containsKey("user")) { userList = Arrays.asList(params.getString("user").split("[|,]")); } try { log.info("企业微信推送消息,send content: {}, userIdSet: {}", content, userList); groupRobotService.sendText(webhookUrl, content, userList, Collections.emptyList()); log.info("企业微信推送消息成功"); } catch (WxErrorException e) { log.error("企业微信推送消息失败!Detail: ", e); } } @Override @Async("coinTaskExecutor") public void monitorAlarm(String content, String jobAlarmMode) { // 判断告警模式 if (StringUtils.isEmpty(JOB_ALARM_MODE)) { jobAlarmMode = JOB_ALARM_MODE; } // 文本卡片模式发消息 if (JOB_ALARM_MODE_APP_TEXT_CARD.equals(jobAlarmMode)) { String title = "监控告警明细"; String logUrl = "https://lvzhiqiang.top"; String btnTxt = "日志详情"; WxCpMessage wxCpMessage = WxCpMessage.TEXTCARD().agentId(properties.getAgentId()) .toUser("LvZhiQiang") .toParty("") .toTag("") .title(title).description(content) .url(logUrl).btnTxt(btnTxt) .build(); try { log.info("企业微信推送消息,send message: {}", wxCpMessage); WxCpMessageSendResult sendResult = wxCpService.getMessageService().send(wxCpMessage); log.info("企业微信推送消息成功,send result: {}", sendResult); } catch (WxErrorException e) { log.error("企业微信推送消息失败!Detail: ", e); } } // 调用企业微信群聊机器人发消息 if (JOB_ALARM_MODE_CHAT_BOT.equals(jobAlarmMode)) { WxCpGroupRobotService groupRobotService = wxCpService.getGroupRobotService(); String webhookUrl = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=082970da-2a33-422a-81f6-15f9bde87940"; try { log.info("企业微信推送消息,send content: {}, userIdSet: {}", content, "LvZhiQiang"); groupRobotService.sendText(webhookUrl, content, Collections.singletonList("LvZhiQiang"), Collections.emptyList()); log.info("企业微信推送消息成功"); } catch (WxErrorException e) { log.error("企业微信推送消息失败!Detail: ", e); } } } /** * 请求通用API方法 */ private JSONObject requestApi4Common(String requestPath, String signQueryString, String signBody, String httpMethod, Map paramMap) { String timestamp = String.valueOf(System.currentTimeMillis()); Map headerMap = new HashMap<>(); headerMap.putAll(basicHeaderMap); try { String accessSign = CheckSign4Bitget.generate(timestamp, httpMethod, requestPath, signQueryString, signBody, secretKey); 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); } } private JSONObject requestApi4Common4OKX(String requestPath, String signQueryString, String signBody, String httpMethod, Map paramMap) { String timestamp = DateUtils.getUTCTimeStr(); Map headerMap = new HashMap<>(); headerMap.putAll(basicHeaderMap4OKX); try { String accessSign = CheckSign4OKX.generate(timestamp, httpMethod, requestPath, signQueryString, signBody, secretKey4OKX); 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" + 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(); if (params.getString("nameEn").equals("allPositionv2")) { Map paramMap = new HashMap<>(); paramMap.put("productType", "umcbl"); String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&")); JSONObject response = requestApi4Common(params.getString("url"), signQueryString, null, JsoupUtil.HTTP_GET, paramMap); result = response.getJSONArray("data"); renderMainSearch4AllPositionv2(result, params.getInteger("unrealizedPLSort")); } else if (params.getString("nameEn").equals("orderMarginCoinCurrent")) { Map paramMap = new LinkedHashMap<>(); paramMap.put("productType", "umcbl"); paramMap.put("marginCoin", "USDT"); String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&")); JSONObject response = requestApi4Common(params.getString("url"), signQueryString, null, JsoupUtil.HTTP_GET, paramMap); result = response.getJSONArray("data"); renderMainSearch4OrderMarginCoinCurrent(result, params.getInteger("chaRateSort")); } else if (params.getString("nameEn").equals("orderHistoryProductType")) { PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true); List historyOrderList = coinMapper.findHistoryOrderList(params.toJavaObject(Map.class)); PageInfo historyOrderPageInfo = new PageInfo<>(historyOrderList); renderMainSearch4OrderHistoryProductType(historyOrderList); //result = (JSONArray) JSON.toJSON(historyOrderList); return historyOrderPageInfo; } else if (params.getString("nameEn").equals("traderList")) { PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true); List mixTraderList = coinMapper.findMixTraderList(params.toJavaObject(Map.class)); PageInfo coinTraderPageInfo = new PageInfo<>(mixTraderList); renderMainSearch4TraderList(mixTraderList); //result = (JSONArray) JSON.toJSON(mixTraderList); return coinTraderPageInfo; } else if (params.getString("nameEn").equals("watchlist")) { PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true); List watchlistList = coinMapper.findWatchlistList(params.toJavaObject(Map.class)); PageInfo watchlistPageInfo = new PageInfo<>(watchlistList); renderMainSearch4Watchlist(watchlistList); return watchlistPageInfo; } else if (params.getString("nameEn").equals("image")) { PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true); List fileImageList = coinMapper.findImageList(params.toJavaObject(Map.class)); PageInfo imagePageInfo = new PageInfo<>(fileImageList); renderMainSearch4Image(fileImageList); return imagePageInfo; } else if (params.getString("nameEn").equals("cmcmap")) { PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true); List cmcMapList = coinMapper.findCmcMapList(params.toJavaObject(Map.class)); PageInfo cmcMapPageInfo = new PageInfo<>(cmcMapList); renderMainSearch4CmcMap(cmcMapList); return cmcMapPageInfo; }else if (params.getString("nameEn").equals("monitorCurrency")) { List monitorCurrencyList = coinMapper.findMonitorCurrencyList(); Map resultMulti = new ConcurrentHashMap<>(); Arrays.stream(params.getString("url").split(",")).parallel().forEach(e -> { String requestUrl = mainUrl + e; try { Connection.Response response = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null); resultMulti.put(e, JSONObject.parseObject(response.body()).getJSONArray("data")); } catch (Exception ex) { throw new RuntimeException(ex); } }); result = renderMainSearch4MonitorCurrency(resultMulti, monitorCurrencyList, params.getInteger("changeUtcSort")); } else if (params.getString("nameEn").equals("currentPlan")) { Map paramMap = new LinkedHashMap<>(); paramMap.put("productType", "umcbl"); paramMap.put("isPlan", "plan"); String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&")); JSONObject response = requestApi4Common(params.getString("url"), signQueryString, null, JsoupUtil.HTTP_GET, paramMap); result = response.getJSONArray("data"); renderMainSearch4CurrentPlan(result, params.getInteger("chaRateSort")); } return result; } private void renderMainSearch4CmcMap(List cmcMapList) { for (CoinCmcMap coinCmcMap : cmcMapList) { String platform = coinCmcMap.getPlatform(); if (StringUtils.isNotEmpty(platform)) { JSONObject jsonObject = JSONObject.parseObject(platform); jsonObject.remove("token_address"); coinCmcMap.setPlatform(jsonObject.toJSONString()); } } } private void renderMainSearch4Image(List fileImageList) { for (FileImage fileImage : fileImageList) { String newPath = "" + fileImage.getPath() + ""; fileImage.setPath(newPath); } } private void renderMainSearch4Watchlist(List watchlistList) { BigDecimal bigDecimal10000 = new BigDecimal("10000"); List trackCategoryList = coinApiConfigMapper.findTrackCategoryList(); Map trackCategoryMap = new HashMap<>(); List colorList = coinApiConfigMapper.findColorStyleList(); int j = 0; for (int i = 0; i < trackCategoryList.size(); i++) { if (j > colorList.size() - 1) { j = 0; } trackCategoryMap.put(trackCategoryList.get(i), colorList.get(j)); j++; } for (CoinWatchlist coinWatchlist : watchlistList) { if (null != coinWatchlist.getTotalMarketValue()) { BigDecimal divide = coinWatchlist.getTotalMarketValue().divide(bigDecimal10000, 8, RoundingMode.HALF_UP); if (divide.compareTo(bigDecimal10000) <= 0) { coinWatchlist.setTotalMarketValueStr(divide.setScale(4, RoundingMode.HALF_UP) + "万"); } else { divide = divide.divide(bigDecimal10000, 4, RoundingMode.HALF_UP); coinWatchlist.setTotalMarketValueStr(divide + "亿"); } } // 赛道分类 String[] trackCategoryArr = coinWatchlist.getTrackCategory().split(","); StringBuffer sb = new StringBuffer(); for (int i = 0; i < trackCategoryArr.length; i++) { if (i == trackCategoryArr.length - 1) { sb.append("" + trackCategoryArr[i] + " "); } else { sb.append("" + trackCategoryArr[i] + " "); } } coinWatchlist.setTrackCategoryStyle(" style=\"padding:0em 0.3em;\""); coinWatchlist.setTrackCategory(sb.toString()); // 名称 coinWatchlist.setSymbolStyle(" style=\"background-color:rgba(70,169,244,.72);font-weight: bold;\""); } } private void renderMainSearch4TraderList(List mixTraderList) { for (CoinTrader mixTrader : mixTraderList) { mixTrader.setLastTradeTime(DateUtils.longToString(Long.valueOf(mixTrader.getLastTradeTime()))); } } /** * 渲染获取当前计划委托(止盈止损)列表 * * @param result */ private void renderMainSearch4CurrentPlan(JSONArray result, Integer chaRateSort) { forkJoinPool3.submit(() -> result.parallelStream().forEach(e -> { JSONObject jsonObject = (JSONObject) e; // 币对名称 String symbol = jsonObject.getString("symbol"); jsonObject.put("symbol", "" + symbol.replace("USDT_UMCBL", "") + "USDT_UMCBL"); // 订单状态 jsonObject.put("status", InitRunner.publicParamsMap.get("status").getString(jsonObject.getString("status"))); // 交易类型 jsonObject.put("orderType", InitRunner.publicParamsMap.get("orderType").getString(jsonObject.getString("orderType"))); // 订单类型 jsonObject.put("planType", InitRunner.publicParamsMap.get("planType").getString(jsonObject.getString("planType"))); // 开单方向 String side = jsonObject.getString("side"); jsonObject.put("side", InitRunner.publicParamsMap.get("side").getString(side)); if (side.equals("open_long")) { jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\""); } else if (side.equals("open_short")) { jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\""); } else { jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#F0F0F0;\""); } // 触发类型 jsonObject.put("triggerType", InitRunner.publicParamsMap.get("triggerType").getString(jsonObject.getString("triggerType"))); jsonObject.put("cTime", DateUtils.longToString(jsonObject.getLong("cTime"))); jsonObject.put("uTime", StringUtils.isEmpty(jsonObject.getString("uTime")) ? "--" : DateUtils.longToString(jsonObject.getLong("uTime"))); // 获取合约标记价格 String requestUrl = mainUrl + "/api/mix/v1/market/mark-price?symbol=" + symbol; try { Connection.Response response = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null); String markPrice = JSONObject.parseObject(response.body()).getJSONObject("data").getString("markPrice"); BigDecimal chaRate = BigDecimal.ZERO; BigDecimal triggerPriceDecimal = new BigDecimal(jsonObject.getString("triggerPrice")); BigDecimal markPriceDecimal = new BigDecimal(markPrice); if (markPriceDecimal.compareTo(triggerPriceDecimal) < 0) { chaRate = markPriceDecimal.divide(triggerPriceDecimal, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP); } else if (markPriceDecimal.compareTo(triggerPriceDecimal) > 0) { chaRate = triggerPriceDecimal.divide(markPriceDecimal, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP); } jsonObject.put("markPrice", markPrice); jsonObject.put("markPriceStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\""); jsonObject.put("chaRate", chaRate); jsonObject.put("chaRateStyle", " style=\"color:#FFFFFF;background-color:#5EA294;\""); } catch (Exception ex) { throw new RuntimeException(ex); } })).join(); if (chaRateSort != 0) { Collections.sort(result, (o1, o2) -> chaRateSort * (((JSONObject) o1).getBigDecimal("chaRate").compareTo(((JSONObject) o2).getBigDecimal("chaRate")))); } } /** * 渲染获取监控币种列表 * * @param monitorCurrencyList */ private JSONArray renderMainSearch4MonitorCurrency(Map resultMulti, List monitorCurrencyList, Integer changeUtcSort) { Map monitorCurrencyMap4Mix = monitorCurrencyList.stream().filter(e -> e.getType().equals("2")).collect(Collectors.toMap(CoinMonitorCurrency::getSymbol, Function.identity(), (key1, key2) -> key1)); Set symbolSet4Mix = monitorCurrencyMap4Mix.keySet(); Map monitorCurrencyMap4Spot = monitorCurrencyList.stream().filter(e -> e.getType().contains("1")).collect(Collectors.toMap(CoinMonitorCurrency::getSymbol, Function.identity(), (key1, key2) -> key1)); Set symbolSet4Spot = monitorCurrencyMap4Spot.keySet(); JSONArray array4Spot = resultMulti.get("/api/spot/v1/market/tickers").stream() .filter(iter -> symbolSet4Spot.contains(((JSONObject) iter).getString("symbol"))) .collect(Collectors.toCollection(JSONArray::new)); JSONArray array4Mix = resultMulti.get("/api/mix/v1/market/tickers?productType=umcbl").stream() .filter(iter -> symbolSet4Mix.contains(((JSONObject) iter).getString("symbol"))) .collect(Collectors.toCollection(JSONArray::new)); forkJoinPool.submit(() -> array4Spot.parallelStream().forEach(e -> { JSONObject jsonObject = (JSONObject) e; jsonObject.put("changeUtc", jsonObject.getBigDecimal("changeUtc").multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP)); jsonObject.put("change", jsonObject.getBigDecimal("change").multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP)); jsonObject.put("ts", DateUtils.longToString(jsonObject.getLong("ts"))); jsonObject.put("category", monitorCurrencyMap4Spot.get(jsonObject.getString("symbol")).getCategory()); // UTC0时涨跌幅 if (jsonObject.getBigDecimal("changeUtc").compareTo(BigDecimal.ZERO) < 0) { jsonObject.put("changeUtcStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\""); } else { jsonObject.put("changeUtcStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\""); } // 24小时涨跌幅 if (jsonObject.getBigDecimal("change").compareTo(BigDecimal.ZERO) < 0) { jsonObject.put("changeStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\""); } else { jsonObject.put("changeStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\""); } // 币对名称 String symbol = jsonObject.getString("symbol").replace("USDT", ""); if ("BTC".equals(symbol) || "ETH".equals(symbol)) { jsonObject.put("symbol", "" + symbol + "USDT"); } else { jsonObject.put("symbol", "" + symbol + "USDT"); } // 标记价格 jsonObject.put("closeStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\""); // 基础币量 计价币量 usdt币量 jsonObject.put("baseVol", readableFileSize(jsonObject.getDouble("baseVol"))); jsonObject.put("quoteVol", readableFileSize(jsonObject.getDouble("quoteVol"))); jsonObject.put("usdtVol", readableFileSize(jsonObject.getDouble("usdtVol"))); })).join(); forkJoinPool.submit(() -> array4Mix.parallelStream().forEach(e -> { JSONObject jsonObject = (JSONObject) e; jsonObject.put("changeUtc", jsonObject.getBigDecimal("chgUtc").multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP)); jsonObject.put("change", jsonObject.getBigDecimal("priceChangePercent").multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP)); jsonObject.put("ts", DateUtils.longToString(jsonObject.getLong("timestamp"))); jsonObject.put("category", monitorCurrencyMap4Mix.get(jsonObject.getString("symbol")).getCategory()); // UTC0时涨跌幅 if (jsonObject.getBigDecimal("changeUtc").compareTo(BigDecimal.ZERO) < 0) { jsonObject.put("changeUtcStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\""); } else { jsonObject.put("changeUtcStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\""); } // 24小时涨跌幅 if (jsonObject.getBigDecimal("change").compareTo(BigDecimal.ZERO) < 0) { jsonObject.put("changeStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\""); } else { jsonObject.put("changeStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\""); } // 币对名称 String symbol = jsonObject.getString("symbol").replace("USDT_UMCBL", ""); jsonObject.put("symbol", "" + symbol + "USDT_UMCBL"); // 标记价格 jsonObject.put("close",jsonObject.getString("last")); jsonObject.put("closeStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\""); // 基础币量 计价币量 usdt币量 jsonObject.put("baseVol", readableFileSize(jsonObject.getDouble("baseVolume"))); jsonObject.put("quoteVol", readableFileSize(jsonObject.getDouble("quoteVolume"))); jsonObject.put("usdtVol", readableFileSize(jsonObject.getDouble("quoteVolume"))); // 其他字段兼容 jsonObject.put("openUtc0",jsonObject.getString("openUtc")); jsonObject.put("buyOne",jsonObject.getString("bestBid")); jsonObject.put("sellOne",jsonObject.getString("bestAsk")); })).join(); array4Spot.addAll(array4Mix); if (changeUtcSort != 0) { Collections.sort(array4Spot, (o1, o2) -> changeUtcSort * (((JSONObject) o1).getBigDecimal("changeUtc").compareTo(((JSONObject) o2).getBigDecimal("changeUtc")))); } return array4Spot; } /** * 渲染获取全部历史委托 * * @param historyOrderList */ private void renderMainSearch4OrderHistoryProductType(List historyOrderList) { for (CoinHistoryOrder coinHistoryOrder : historyOrderList) { // 币种名称 coinHistoryOrder.setSymbol(coinHistoryOrder.getSymbol().replace("USDT_UMCBL", "")); // 订单状态 coinHistoryOrder.setState(InitRunner.publicParamsMap.get("state").getString(coinHistoryOrder.getState())); // 开单方向 coinHistoryOrder.setSide(InitRunner.publicParamsMap.get("side").getString(coinHistoryOrder.getSide())); // 总盈亏 String TotalProfits = "0E-8"; if (!coinHistoryOrder.getTotalProfits().contains("0E-8")) { TotalProfits = new BigDecimal(coinHistoryOrder.getTotalProfits()).setScale(2, RoundingMode.HALF_UP).toPlainString(); } coinHistoryOrder.setTotalProfits(TotalProfits); // 手续费 String fee = "0E-8"; if (!coinHistoryOrder.getFee().contains("0E-8")) { fee = new BigDecimal(coinHistoryOrder.getFee()).setScale(2, RoundingMode.HALF_UP).toPlainString(); } coinHistoryOrder.setFee(fee); // 持仓方向 coinHistoryOrder.setPosSide(InitRunner.publicParamsMap.get("posSide").getString(coinHistoryOrder.getPosSide())); // 仓位模式 coinHistoryOrder.setMarginMode(InitRunner.publicParamsMap.get("marginMode").getString(coinHistoryOrder.getMarginMode())); // 交易类型 coinHistoryOrder.setOrderType(InitRunner.publicParamsMap.get("orderType").getString(coinHistoryOrder.getOrderType())); // 交易方向 coinHistoryOrder.setTradeSide(InitRunner.publicParamsMap.get("tradeSide").getString(coinHistoryOrder.getTradeSide())); // 持仓模式 coinHistoryOrder.setHoldMode(InitRunner.publicParamsMap.get("holdMode").getString(coinHistoryOrder.getHoldMode())); // orderSource coinHistoryOrder.setOrderSource(InitRunner.publicParamsMap.get("orderSource").getString(coinHistoryOrder.getOrderSource())); coinHistoryOrder.setCTime(DateUtils.longToString(Long.valueOf(coinHistoryOrder.getCTime()))); coinHistoryOrder.setUTime(DateUtils.longToString(Long.valueOf(coinHistoryOrder.getUTime()))); } } /** * 渲染获取全部当前委托 * * @param result */ private void renderMainSearch4OrderMarginCoinCurrent(JSONArray result, Integer chaRateSort) { forkJoinPool2.submit(() -> result.parallelStream().forEach(e -> { JSONObject jsonObject = (JSONObject) e; // 币对名称 String symbol = jsonObject.getString("symbol"); jsonObject.put("symbol", "" + symbol.replace("USDT_UMCBL", "") + "USDT_UMCBL"); // 订单状态 jsonObject.put("state", InitRunner.publicParamsMap.get("state").getString(jsonObject.getString("state"))); // 开单方向 String side = jsonObject.getString("side"); jsonObject.put("side", InitRunner.publicParamsMap.get("side").getString(side)); if (side.equals("open_long")) { jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\""); } else if (side.equals("open_short")) { jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\""); } else { jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#F0F0F0;\""); } // 交易类型 jsonObject.put("orderType", InitRunner.publicParamsMap.get("orderType").getString(jsonObject.getString("orderType"))); // 止盈止损 jsonObject.put("presetTakeProfitPrice", StringUtils.isEmpty(jsonObject.getString("presetTakeProfitPrice")) ? "--" : jsonObject.getString("presetTakeProfitPrice")); jsonObject.put("presetStopLossPrice", StringUtils.isEmpty(jsonObject.getString("presetTakeProfitPrice")) ? "--" : jsonObject.getString("presetTakeProfitPrice")); // 持仓模式 jsonObject.put("holdMode", InitRunner.publicParamsMap.get("holdMode").getString(jsonObject.getString("holdMode"))); // orderSource jsonObject.put("orderSource", InitRunner.publicParamsMap.get("orderSource").getString(jsonObject.getString("orderSource"))); // 仓位模式 jsonObject.put("marginMode", InitRunner.publicParamsMap.get("marginMode").getString(jsonObject.getString("marginMode"))); jsonObject.put("cTime", DateUtils.longToString(jsonObject.getLong("cTime"))); jsonObject.put("uTime", DateUtils.longToString(jsonObject.getLong("uTime"))); // 获取合约标记价格 String requestUrl = mainUrl + "/api/mix/v1/market/mark-price?symbol=" + symbol; try { Connection.Response response = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null); String markPrice = JSONObject.parseObject(response.body()).getJSONObject("data").getString("markPrice"); BigDecimal chaRate = BigDecimal.ZERO; if ("open_short".equals(side)) { chaRate = new BigDecimal(markPrice).divide(new BigDecimal(jsonObject.getString("price")), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP); } else if ("open_long".equals(side)) { chaRate = new BigDecimal(jsonObject.getString("price")).divide(new BigDecimal(markPrice), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP); } jsonObject.put("markPrice", markPrice); jsonObject.put("markPriceStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\""); jsonObject.put("chaRate", chaRate); jsonObject.put("chaRateStyle", " style=\"color:#FFFFFF;background-color:#5EA294;\""); } catch (Exception ex) { throw new RuntimeException(ex); } })).join(); if (chaRateSort != 0) { Collections.sort(result, (o1, o2) -> chaRateSort * (((JSONObject) o1).getBigDecimal("chaRate").compareTo(((JSONObject) o2).getBigDecimal("chaRate")))); } } /** * 渲染获取全部合约仓位信息V2 * * @param result */ private void renderMainSearch4AllPositionv2(JSONArray result, Integer unrealizedPLSort) { forkJoinPool4.submit(() -> result.parallelStream().forEach(e -> { JSONObject jsonObject = (JSONObject) e; // 币对名称 String symbol = jsonObject.getString("symbol"); jsonObject.put("symbol", "" + symbol.replace("USDT_UMCBL", "") + "USDT_UMCBL"); // 持仓方向 String holdSide = jsonObject.getString("holdSide"); jsonObject.put("holdSide", InitRunner.publicParamsMap.get("holdSide").getString(holdSide)); if (holdSide.equals("long")) { jsonObject.put("holdSideStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\""); } else if (holdSide.equals("short")) { jsonObject.put("holdSideStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\""); } else { jsonObject.put("holdSideStyle", " style=\"color:#FFFFFF;background-color:#F0F0F0;\""); } // 保证金模式 jsonObject.put("marginMode", InitRunner.publicParamsMap.get("marginMode").getString(jsonObject.getString("marginMode"))); // 持仓模式 jsonObject.put("holdMode", InitRunner.publicParamsMap.get("holdMode").getString(jsonObject.getString("holdMode"))); // 最近更新时间 保证金数量 (保证金币种) 平均开仓价 未实现盈亏 预估强平价 jsonObject.put("cTime", DateUtils.longToString(jsonObject.getLong("cTime"))); jsonObject.put("margin", new BigDecimal(jsonObject.getString("margin")).setScale(4, RoundingMode.HALF_UP)); jsonObject.put("averageOpenPrice", new BigDecimal(jsonObject.getString("averageOpenPrice")).divide(BigDecimal.ONE, new MathContext(4))); jsonObject.put("unrealizedPL", new BigDecimal(jsonObject.getString("unrealizedPL")).setScale(4, RoundingMode.HALF_UP)); jsonObject.put("liquidationPrice", new BigDecimal(jsonObject.getString("liquidationPrice")).divide(BigDecimal.ONE, new MathContext(4))); // 未实现盈亏 if (jsonObject.getBigDecimal("unrealizedPL").compareTo(BigDecimal.ZERO) < 0) { jsonObject.put("unrealizedPLStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\""); } else { jsonObject.put("unrealizedPLStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\""); } // 回报率=未实现盈亏/保证金 BigDecimal returnRate = jsonObject.getBigDecimal("unrealizedPL").divide(jsonObject.getBigDecimal("margin"), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP); jsonObject.put("returnRate", returnRate); if (returnRate.compareTo(BigDecimal.ZERO) < 0) { jsonObject.put("returnRateStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\""); } else { jsonObject.put("returnRateStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\""); } // 获取当前资金费率 String requestUrl = mainUrl + "/api/mix/v1/market/current-fundRate?symbol=" + symbol; try { Connection.Response response = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null); String fundingRate = JSONObject.parseObject(response.body()).getJSONObject("data").getString("fundingRate"); if (new BigDecimal(fundingRate).compareTo(BigDecimal.ZERO) < 0) { jsonObject.put("fundingRateStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\""); } else { jsonObject.put("fundingRateStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\""); } jsonObject.put("fundingRate", new BigDecimal(fundingRate).multiply(BigDecimal.valueOf(100)).setScale(4, RoundingMode.HALF_UP).toPlainString() + "%"); } catch (Exception ex) { throw new RuntimeException(ex); } // 标记价格 jsonObject.put("marketPriceStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\""); })).join(); if (unrealizedPLSort != 0) { Collections.sort(result, (o1, o2) -> unrealizedPLSort * (((JSONObject) o1).getBigDecimal("unrealizedPL").compareTo(((JSONObject) o2).getBigDecimal("unrealizedPL")))); } } /** * Java实现字节转换,可以自动转换为B、KB、MB、GB、TB * * @param size * @return */ private String readableFileSize(double size) { if (size <= 0) { return "0"; } final String[] units = new String[]{"B", "K", "M", "G", "T"}; int digitGroups = (int) (Math.log10(size) / Math.log10(1000)); return df1.format(size / Math.pow(1000, digitGroups)) + units[digitGroups]; } }