CoinServiceImpl.java 70 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293
  1. package top.lvzhiqiang.service.impl;
  2. import com.alibaba.fastjson.JSONArray;
  3. import com.alibaba.fastjson.JSONObject;
  4. import com.github.pagehelper.PageHelper;
  5. import com.github.pagehelper.PageInfo;
  6. import lombok.extern.slf4j.Slf4j;
  7. import me.chanjar.weixin.common.error.WxErrorException;
  8. import me.chanjar.weixin.cp.api.WxCpGroupRobotService;
  9. import me.chanjar.weixin.cp.api.WxCpService;
  10. import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
  11. import me.chanjar.weixin.cp.bean.article.NewArticle;
  12. import me.chanjar.weixin.cp.bean.message.WxCpMessage;
  13. import me.chanjar.weixin.cp.bean.message.WxCpMessageSendResult;
  14. import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
  15. import org.jsoup.Connection;
  16. import org.springframework.beans.factory.annotation.Autowired;
  17. import org.springframework.scheduling.annotation.Async;
  18. import org.springframework.stereotype.Service;
  19. import org.springframework.transaction.annotation.Propagation;
  20. import org.springframework.transaction.annotation.Transactional;
  21. import org.springframework.util.StopWatch;
  22. import top.lvzhiqiang.config.InitRunner;
  23. import top.lvzhiqiang.config.WorkWeixinProperties;
  24. import top.lvzhiqiang.entity.CoinHistoryOrder;
  25. import top.lvzhiqiang.entity.CoinMonitorCurrency;
  26. import top.lvzhiqiang.entity.CoinTrader;
  27. import top.lvzhiqiang.mapper.CoinMapper;
  28. import top.lvzhiqiang.service.CoinService;
  29. import top.lvzhiqiang.util.*;
  30. import javax.annotation.Resource;
  31. import java.io.UnsupportedEncodingException;
  32. import java.math.BigDecimal;
  33. import java.math.RoundingMode;
  34. import java.security.InvalidKeyException;
  35. import java.text.DecimalFormat;
  36. import java.time.Duration;
  37. import java.time.LocalDateTime;
  38. import java.util.*;
  39. import java.util.concurrent.*;
  40. import java.util.function.Function;
  41. import java.util.stream.Collectors;
  42. /**
  43. * Coin ServiceImpl
  44. *
  45. * @author lvzhiqiang
  46. * 2023/9/5 15:23
  47. */
  48. @Service
  49. @Slf4j
  50. public class CoinServiceImpl implements CoinService {
  51. /**
  52. * 任务告警方式-应用文本卡片
  53. */
  54. public static final String JOB_ALARM_MODE_APP_TEXT_CARD = "1";
  55. /**
  56. * 任务告警方式-群聊机器人
  57. */
  58. public static final String JOB_ALARM_MODE_CHAT_BOT = "2";
  59. /**
  60. * 任务告警方式(1:应用文本卡片,2:群聊机器人文本消息)
  61. */
  62. public static String JOB_ALARM_MODE = "1";
  63. // 所有REST请求的header都必须包含以下key:
  64. private static final Map<String, String> basicHeaderMap = new HashMap<>();
  65. private static final Map<String, String> basicHeaderMap4OKX = new HashMap<>();
  66. // 主域名 URL
  67. private static final String mainUrl = "https://api.bitget.com";
  68. // 私钥,由系统随机生成,用于签名的生成。
  69. private static final String secretKey = "1fdd0fc2976bea80189ba13710e12825ca3ef6c5e25a0d76fd03f8f6cd4a61d9";
  70. private static final String secretKey4OKX = "32AC470662FBB633374B9A41950995A9";
  71. @Resource
  72. private CoinMapper coinMapper;
  73. @Resource
  74. private WxCpService wxCpService;
  75. @Autowired(required = false)
  76. private WorkWeixinProperties properties;
  77. private final Map<String, String> orderMap = new ConcurrentHashMap<>();
  78. private final Map<String, JSONObject> mixMap = new ConcurrentHashMap<>();
  79. private final static ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(10);
  80. private final ForkJoinPool forkJoinPool = new ForkJoinPool(16);
  81. private final ForkJoinPool forkJoinPool2 = new ForkJoinPool(16);
  82. private final ForkJoinPool forkJoinPool3 = new ForkJoinPool(16);
  83. private final ForkJoinPool forkJoinPool4 = new ForkJoinPool(16);
  84. private final ForkJoinPool forkJoinPool5 = new ForkJoinPool(16);
  85. private static final DecimalFormat df1 = new DecimalFormat("#,##0.00");
  86. private static final WxCpServiceImpl wxCpService4News;
  87. static {
  88. // API KEY作为一个字符串。
  89. basicHeaderMap.put("ACCESS-KEY", "bg_433d37306df0e8901c6d107c6d9e9111");
  90. // 使用base64编码签名(请参阅签名消息)。
  91. basicHeaderMap.put("ACCESS-SIGN", "");
  92. // 您请求的时间戳。
  93. basicHeaderMap.put("ACCESS-TIMESTAMP", "");
  94. // 您在创建API KEY时设置的口令。
  95. basicHeaderMap.put("ACCESS-PASSPHRASE", "7f934f62f2701bee932204580d115228");
  96. // 统一设置为application/json。
  97. basicHeaderMap.put("Content-Type", "application/json");
  98. // 支持多语言, 如:中文(zh-CN),英语(en-US)
  99. basicHeaderMap.put("locale", "zh-CN");
  100. // 字符串类型的APIKey
  101. basicHeaderMap4OKX.put("OK-ACCESS-KEY","25e4f515-5efd-4bb9-a934-3949b21d9f10");
  102. // 使用HMAC SHA256哈希函数获得哈希值,再使用Base-64编码(请参阅签名)
  103. basicHeaderMap4OKX.put("OK-ACCESS-SIGN","");
  104. // 发起请求的时间(UTC),如:2020-12-08T09:08:57.715Z
  105. basicHeaderMap4OKX.put("OK-ACCESS-TIMESTAMP","");
  106. // 您在创建API密钥时指定的Passphrase
  107. basicHeaderMap4OKX.put("OK-ACCESS-PASSPHRASE","tmvxeGY#Q#Y2qm8");
  108. // 统一设置为application/json
  109. basicHeaderMap4OKX.put("Content-Type", "application/json");
  110. df1.setRoundingMode(RoundingMode.HALF_UP);
  111. WxCpDefaultConfigImpl wxCpDefaultConfig = new WxCpDefaultConfigImpl();
  112. wxCpDefaultConfig.setCorpId("ww95a4adba56acb55f");
  113. wxCpDefaultConfig.setAgentId(1000004);
  114. wxCpDefaultConfig.setCorpSecret("hG50gbVZ8pXm3tSzY3BpwTzbrMmm6sTf8_bPfJG_6Yc");
  115. wxCpService4News = new WxCpServiceImpl();
  116. wxCpService4News.setWxCpConfigStorage(wxCpDefaultConfig);
  117. }
  118. @Override
  119. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  120. public void syncData(String startTime, String endTime, String pageSize) {
  121. // 获取全部历史委托
  122. Map<String, String> paramMap = new LinkedHashMap<>();
  123. paramMap.put("productType", "umcbl");
  124. paramMap.put("startTime", startTime);
  125. paramMap.put("endTime", endTime);
  126. paramMap.put("pageSize", pageSize);
  127. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  128. JSONObject response = requestApi4Common("/api/mix/v1/order/historyProductType", signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  129. JSONArray orderList = response.getJSONObject("data").getJSONArray("orderList");
  130. if (orderList.size() > 0) {
  131. coinMapper.insertHistoryOrderList(JSONArray.parseArray(orderList.toJSONString(), CoinHistoryOrder.class));
  132. log.warn("syncData->insertHistoryOrderList,startTime={},endTime={},size={}", startTime, endTime, orderList.size());
  133. }
  134. }
  135. @Override
  136. public void syncData4TraderList() {
  137. StopWatch stopWatch = new StopWatch();
  138. stopWatch.start();
  139. // 获取交易员列表
  140. Map<String, String> paramMap = new LinkedHashMap<>();
  141. paramMap.put("sortRule", "composite");
  142. paramMap.put("sortFlag", "desc");
  143. paramMap.put("languageType", "en-US");
  144. paramMap.put("pageSize", "20");
  145. int i = 0;
  146. String url = "/api/mix/v1/trace/traderList";
  147. JSONObject response;
  148. int totalNum = 0;
  149. for (; ; ) {
  150. paramMap.put("pageNo", String.valueOf(++i));
  151. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  152. response = requestApi4Common(url, signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  153. JSONArray dataList = response.getJSONArray("data");
  154. if (dataList.size() == 0) {
  155. break;
  156. }
  157. try {
  158. syncData4TraderListSub(dataList);
  159. } catch (Exception e) {
  160. log.error("syncData4TraderListSub error,paramMap={}", paramMap, e);
  161. }
  162. totalNum += dataList.size();
  163. }
  164. log.warn("syncData4TraderList 结束:time={},totalNum={}", stopWatch.getTotalTimeSeconds(), totalNum);
  165. }
  166. @Override
  167. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  168. public void syncData4TraderListSub(JSONArray dataList) {
  169. coinMapper.insertMixTradeList(parseMixTradeList(dataList));
  170. }
  171. private List<CoinTrader> parseMixTradeList(JSONArray dataList) {
  172. List<CoinTrader> mixTraderList = JSONArray.parseArray(dataList.toJSONString(), CoinTrader.class);
  173. mixTraderList.stream().forEach(e -> {
  174. Map<String, String> columnMap = e.getColumnList().stream().filter(Objects::nonNull)
  175. .collect(Collectors.toMap(
  176. object -> {
  177. JSONObject item = (JSONObject) object;
  178. return item.getString("describe");
  179. },
  180. object -> {
  181. JSONObject item = (JSONObject) object;
  182. return item.getString("value");
  183. }
  184. ));
  185. e.setRoi(columnMap.get("ROI"));
  186. e.setTotalProfit(columnMap.get("Total PnL").replace("$", "").replace(",", ""));
  187. e.setTotalFollowersProfit(columnMap.get("Total followers PnL").replace("$", "").replace(",", ""));
  188. e.setAum(columnMap.get("AUM").replace("$", "").replace(",", ""));
  189. e.setMaxCallbackRate(columnMap.get("Max drawdown"));
  190. e.setLast3wWinRate(columnMap.get("Last 3W win rate"));
  191. e.setAverageWinRate(StringUtils.isNotEmpty(e.getAverageWinRate()) ? new BigDecimal(e.getAverageWinRate()).setScale(2, RoundingMode.HALF_UP).toPlainString() : "0.00");
  192. });
  193. return mixTraderList;
  194. }
  195. @Override
  196. public String orderDetail(String trackingNo) {
  197. Map<String, String> paramMap = new LinkedHashMap<>();
  198. paramMap.put("traderId", "b1b5467f8bb73f53ac97");
  199. paramMap.put("pageSize", "20");
  200. StringBuffer sb = new StringBuffer();
  201. // 交易员当前带单列表筛选
  202. for (int j = 1; j < 5; j++) {
  203. try {
  204. paramMap.put("pageNo", j + "");
  205. JSONObject response = requestApi4Common("/api/mix/v1/trace/report/order/currentList", null, JSONObject.toJSONString(paramMap), JsoupUtil.HTTP_POST, paramMap);
  206. JSONArray orderList = response.getJSONArray("data");
  207. for (int i = 0; i < orderList.size(); i++) {
  208. JSONObject order = orderList.getJSONObject(i);
  209. String trackingNo1 = order.getString("trackingNo");
  210. if (trackingNo.equals(trackingNo1)) {
  211. sb.append("<table border=\"1\" cellspacing=\"0\"><tr><th>键</th><th>值</th></tr>");
  212. sb.append("<tr><td>交易对</td><td>").append(order.getString("symbol")).append("</td></tr>");
  213. sb.append("<tr><td>持仓方向</td><td>").append(InitRunner.publicParamsMap.get("holdSide").getString(order.getString("holdSide"))).append("</td></tr>");
  214. sb.append("<tr><td>杠杆倍数</td><td>").append(order.getString("leverage")).append("</td></tr>");
  215. sb.append("<tr><td>开仓均价</td><td>").append(order.getString("openPrice")).append("</td></tr>");
  216. sb.append("<tr><td>开仓时间</td><td>").append(DateUtils.longToString(order.getLong("openTime"))).append("</td></tr>");
  217. sb.append("<tr><td>此笔订单跟单人数</td><td>").append(order.getString("followerNum")).append("</td></tr>");
  218. sb.append("<tr><td>保证金</td><td>").append(order.getString("marginAmount")).append("</td></tr>");
  219. sb.append("<tr><td>止盈价</td><td>").append(order.getString("takeProfitPrice")).append("</td></tr>");
  220. sb.append("<tr><td>止损价</td><td>").append(order.getString("stopLossPrice")).append("</td></tr>");
  221. sb.append("<tr><td>交易员</td><td>").append("hale").append("</td></tr>");
  222. sb.append("</table>");
  223. break;
  224. }
  225. }
  226. } catch (Exception e) {
  227. }
  228. }
  229. // 交易员历史带单列表筛选
  230. if (sb.length() == 0) {
  231. for (int j = 1; j < 5; j++) {
  232. try {
  233. paramMap.put("pageNo", j + "");
  234. JSONObject response = requestApi4Common("/api/mix/v1/trace/report/order/historyList", null, JSONObject.toJSONString(paramMap), JsoupUtil.HTTP_POST, paramMap);
  235. JSONArray orderList = response.getJSONArray("data");
  236. for (int i = 0; i < orderList.size(); i++) {
  237. JSONObject order = orderList.getJSONObject(i);
  238. String trackingNo1 = order.getString("trackingNo");
  239. if (trackingNo.equals(trackingNo1)) {
  240. sb.append("<table border=\"1\" cellspacing=\"0\" style=\"font-size: 20px;\"><tr><th>键</th><th>值</th></tr>");
  241. sb.append("<tr><td>交易对</td><td>").append(order.getString("symbol")).append("</td></tr>");
  242. sb.append("<tr><td>持仓方向</td><td>").append(InitRunner.publicParamsMap.get("holdSide").getString(order.getString("holdSide"))).append("</td></tr>");
  243. sb.append("<tr><td>杠杆倍数</td><td>").append(order.getString("leverage")).append("</td></tr>");
  244. sb.append("<tr><td>开仓均价</td><td>").append(order.getString("openPrice")).append("</td></tr>");
  245. sb.append("<tr><td>开仓时间</td><td>").append(DateUtils.longToString(order.getLong("openTime"))).append("</td></tr>");
  246. sb.append("<tr><td>此笔订单跟单人数</td><td>").append(order.getString("followerNum")).append("</td></tr>");
  247. sb.append("<tr><td>保证金</td><td>").append(order.getString("marginAmount")).append("</td></tr>");
  248. sb.append("<tr><td>平仓均价</td><td>").append(order.getString("closePrice")).append("</td></tr>");
  249. sb.append("<tr><td>平仓时间</td><td>").append(DateUtils.longToString(order.getLong("closeTime"))).append("</td></tr>");
  250. sb.append("<tr><td>平仓数量</td><td>").append(order.getString("closeAmount")).append("</td></tr>");
  251. sb.append("<tr><td>交易员</td><td>").append("hale").append("</td></tr>");
  252. sb.append("</table>");
  253. break;
  254. }
  255. }
  256. } catch (Exception e) {
  257. }
  258. }
  259. }
  260. return sb.toString();
  261. }
  262. @Override
  263. public String orderDetail2(String orderId, String symbol) {
  264. Map<String, String> paramMap = new LinkedHashMap<>();
  265. paramMap.put("symbol", symbol);
  266. paramMap.put("orderId", orderId);
  267. StringBuffer sb = new StringBuffer("<table border=\"1\" cellspacing=\"0\" style=\"font-size: 20px;\"><tr><th>键</th><th>值</th></tr>");
  268. // 获取订单详情
  269. try {
  270. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  271. JSONObject response = requestApi4Common("/api/mix/v1/order/detail", signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  272. JSONObject order = response.getJSONObject("data");
  273. sb.append("<tr><td>交易对</td><td>").append(order.getString("symbol")).append("</td></tr>");
  274. sb.append("<tr><td>交易方向</td><td>").append(InitRunner.publicParamsMap.get("side").getString(order.getString("side"))).append("</td></tr>");
  275. sb.append("<tr><td>杠杆倍数</td><td>").append(order.getString("leverage")).append("</td></tr>");
  276. sb.append("<tr><td>成交均价</td><td>").append(order.getString("priceAvg")).append("</td></tr>");
  277. sb.append("<tr><td>委托价格</td><td>").append(order.getString("price")).append("</td></tr>");
  278. sb.append("<tr><td>手续费</td><td>").append(order.getString("fee")).append("</td></tr>");
  279. sb.append("<tr><td>订单状态</td><td>").append(InitRunner.publicParamsMap.get("state").getString(order.getString("state"))).append("</td></tr>");
  280. sb.append("<tr><td>交易类型</td><td>").append(InitRunner.publicParamsMap.get("orderType").getString(order.getString("orderType"))).append("</td></tr>");
  281. sb.append("<tr><td>总盈亏</td><td>").append(order.getString("totalProfits")).append("</td></tr>");
  282. sb.append("<tr><td>预设止盈价格</td><td>").append(order.getString("presetTakeProfitPrice")).append("</td></tr>");
  283. sb.append("<tr><td>预设止损价格</td><td>").append(order.getString("presetStopLossPrice")).append("</td></tr>");
  284. sb.append("<tr><td>创建时间</td><td>").append(DateUtils.longToString(order.getLong("cTime"))).append("</td></tr>");
  285. sb.append("<tr><td>更新时间</td><td>").append(DateUtils.longToString(order.getLong("uTime"))).append("</td></tr>");
  286. } catch (Exception e) {
  287. log.error("orderDetail2 error,orderId={},symbol={}", orderId, symbol, e);
  288. }
  289. sb.append("</table>");
  290. return sb.toString();
  291. }
  292. @Override
  293. public String monitorJob() {
  294. // BITGET开仓平仓监控报警
  295. scheduler.scheduleWithFixedDelay(() -> {
  296. LocalDateTime endTime = LocalDateTime.now();
  297. // 全部历史委托列表
  298. Map<String, String> paramMap = new LinkedHashMap<>();
  299. paramMap.put("productType", "umcbl");
  300. paramMap.put("startTime", String.valueOf(DateUtils.localDateTimeToMilliseconds(endTime.minusMinutes(1))));
  301. paramMap.put("endTime", String.valueOf(DateUtils.localDateTimeToMilliseconds(endTime)));
  302. paramMap.put("pageSize", "100");
  303. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  304. try {
  305. JSONObject response = requestApi4Common("/api/mix/v1/order/historyProductType", signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  306. JSONArray orderList = response.getJSONObject("data").getJSONArray("orderList");
  307. for (int i = 0; i < orderList.size(); i++) {
  308. JSONObject order = orderList.getJSONObject(i);
  309. LocalDateTime cTime = DateUtils.longToLocalDateTime(order.getLong("cTime"));
  310. String orderId = order.getString("orderId");
  311. String symbol = order.getString("symbol");
  312. if (Duration.between(cTime, endTime).getSeconds() < 50 && !orderMap.containsKey(orderId)) {
  313. orderMap.put(orderId, "1");
  314. String content = "<div class=\"highlight\">交易对:" + order.getString("symbol") + "</div>" +
  315. "<div>交易方向:" + InitRunner.publicParamsMap.get("side").getString(order.getString("side")) + "</div>" +
  316. "<div>杠杆倍数:" + order.getString("leverage") + "</div>" +
  317. "<div>成交均价:" + order.getString("priceAvg") + "</div>" +
  318. "<div>委托价格:" + order.getString("price") + "</div>" +
  319. "<div>订单状态:" + InitRunner.publicParamsMap.get("state").getString(order.getString("state")) + "</div>" +
  320. "<div>订单类型:" + InitRunner.publicParamsMap.get("orderType").getString(order.getString("orderType")) + "</div>" +
  321. "<div class=\"gray\">订单时间:" + DateUtils.longToString(order.getLong("cTime")) + "</div>";
  322. JSONObject params = new JSONObject();
  323. params.put("title", (order.getString("side").contains("open") ? "BITGET合约开单" : "BITGET合约平单") + "报警");
  324. params.put("logUrl", "https://jav.lvzhiqiang.top/coin/orderDetail2/" + orderId + "/" + symbol);
  325. params.put("btnTxt", "订单详情");
  326. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(content, params, null);
  327. }
  328. }
  329. } catch (Exception e) {
  330. }
  331. }, 0, 2, TimeUnit.SECONDS);
  332. // OKX开仓平仓监控报警
  333. scheduler.scheduleWithFixedDelay(() -> {
  334. LocalDateTime endTime = LocalDateTime.now();
  335. // 全部历史委托列表
  336. Map<String, String> paramMap = new LinkedHashMap<>();
  337. paramMap.put("productType", "umcbl");
  338. paramMap.put("startTime", String.valueOf(DateUtils.localDateTimeToMilliseconds(endTime.minusMinutes(1))));
  339. paramMap.put("endTime", String.valueOf(DateUtils.localDateTimeToMilliseconds(endTime)));
  340. paramMap.put("pageSize", "100");
  341. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  342. try {
  343. JSONObject response = requestApi4Common("/api/mix/v1/order/historyProductType", signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  344. JSONArray orderList = response.getJSONObject("data").getJSONArray("orderList");
  345. for (int i = 0; i < orderList.size(); i++) {
  346. JSONObject order = orderList.getJSONObject(i);
  347. LocalDateTime cTime = DateUtils.longToLocalDateTime(order.getLong("cTime"));
  348. String orderId = order.getString("orderId");
  349. String symbol = order.getString("symbol");
  350. if (Duration.between(cTime, endTime).getSeconds() < 50 && !orderMap.containsKey(orderId)) {
  351. orderMap.put(orderId, "1");
  352. String content = "<div class=\"highlight\">交易对:" + order.getString("symbol") + "</div>" +
  353. "<div>交易方向:" + InitRunner.publicParamsMap.get("side").getString(order.getString("side")) + "</div>" +
  354. "<div>杠杆倍数:" + order.getString("leverage") + "</div>" +
  355. "<div>成交均价:" + order.getString("priceAvg") + "</div>" +
  356. "<div>委托价格:" + order.getString("price") + "</div>" +
  357. "<div>订单状态:" + InitRunner.publicParamsMap.get("state").getString(order.getString("state")) + "</div>" +
  358. "<div>订单类型:" + InitRunner.publicParamsMap.get("orderType").getString(order.getString("orderType")) + "</div>" +
  359. "<div class=\"gray\">订单时间:" + DateUtils.longToString(order.getLong("cTime")) + "</div>";
  360. JSONObject params = new JSONObject();
  361. params.put("title", (order.getString("side").contains("open") ? "BITGET合约开单" : "BITGET合约平单") + "报警");
  362. params.put("logUrl", "https://jav.lvzhiqiang.top/coin/orderDetail2/" + orderId + "/" + symbol);
  363. params.put("btnTxt", "订单详情");
  364. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(content, params, null);
  365. }
  366. }
  367. } catch (Exception e) {
  368. }
  369. }, 0, 2, TimeUnit.SECONDS);
  370. scheduler.scheduleAtFixedRate(() -> {
  371. // BITGET全部合约仓位信息V2
  372. Map<String, String> paramMap = new HashMap<>();
  373. paramMap.put("productType", "umcbl");
  374. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  375. try {
  376. JSONObject response = requestApi4Common("/api/mix/v1/position/allPosition-v2", signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  377. JSONArray mixList = response.getJSONArray("data");
  378. for (int i = 0; i < mixList.size(); i++) {
  379. JSONObject mixData = mixList.getJSONObject(i);
  380. String symbol = mixData.getString("symbol");
  381. String margin = mixData.getString("margin");
  382. String averageOpenPrice = mixData.getString("averageOpenPrice");
  383. String key = symbol + margin + averageOpenPrice;
  384. // 回报率=未实现盈亏/保证金
  385. // 持仓方向 long:多头 short:空头
  386. String holdSide = mixData.getString("holdSide");
  387. BigDecimal returnRate = new BigDecimal(mixData.getString("unrealizedPL")).divide(new BigDecimal(margin), 4, RoundingMode.HALF_UP);
  388. for (int j = 1; j <= 10; j++) {
  389. BigDecimal grid = BigDecimal.valueOf(0.5).multiply(BigDecimal.valueOf(j));
  390. if (returnRate.compareTo(grid) < 0) {
  391. if (mixMap.containsKey(key)) {
  392. mixMap.get(key).put("returnRate", returnRate);
  393. } else {
  394. JSONObject jsonObject = new JSONObject();
  395. jsonObject.put("returnRate", returnRate);
  396. mixMap.put(key, jsonObject);
  397. }
  398. break;
  399. }
  400. if (returnRate.compareTo(grid) > 0) {
  401. if (mixMap.containsKey(key)) {
  402. mixMap.get(key).put("returnRate", returnRate);
  403. if (mixMap.get(key).containsKey(grid.toPlainString())) {
  404. continue;
  405. } else {
  406. mixMap.get(key).put(grid.toPlainString(), true);
  407. String requestUrl = mainUrl + "/api/mix/v1/market/ticker?symbol=" + symbol;
  408. String last = "--";
  409. try {
  410. Connection.Response responseTicker = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  411. last = JSONObject.parseObject(responseTicker.body()).getJSONObject("data").getString("last");
  412. } catch (Exception e) {
  413. }
  414. String content = "币对名称:" + symbol + "\n" +
  415. "持仓方向:" + InitRunner.publicParamsMap.get("holdSide").getString(mixData.getString("holdSide")) + "\n" +
  416. "杠杆倍数:" + mixData.getString("leverage") + "\n" +
  417. "开仓均价:" + mixData.getString("averageOpenPrice") + "\n" +
  418. "当前价格:" + last + "\n" +
  419. "回报率:" + returnRate.multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP).toPlainString() + ",超过" + grid.multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP).toPlainString();
  420. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4CHAT_BOT(content, null);
  421. }
  422. } else {
  423. JSONObject jsonObject = new JSONObject();
  424. jsonObject.put("returnRate", returnRate);
  425. jsonObject.put(grid.toPlainString(), true);
  426. mixMap.put(key, jsonObject);
  427. String requestUrl = mainUrl + "/api/mix/v1/market/ticker?symbol=" + symbol;
  428. String last = "--";
  429. try {
  430. Connection.Response responseTicker = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  431. last = JSONObject.parseObject(responseTicker.body()).getJSONObject("data").getString("last");
  432. } catch (Exception e) {
  433. }
  434. String content = "币对名称:" + symbol + "\n" +
  435. "持仓方向:" + InitRunner.publicParamsMap.get("holdSide").getString(mixData.getString("holdSide")) + "\n" +
  436. "杠杆倍数:" + mixData.getString("leverage") + "\n" +
  437. "开仓均价:" + mixData.getString("averageOpenPrice") + "\n" +
  438. "当前价格:" + last + "\n" +
  439. "回报率:" + returnRate.multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP).toPlainString() + ",超过" + grid.multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP).toPlainString();
  440. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4CHAT_BOT(content, null);
  441. //break;
  442. }
  443. }
  444. }
  445. }
  446. } catch (Exception e) {
  447. e.printStackTrace();
  448. }
  449. }, 0, 5, TimeUnit.SECONDS);
  450. // BITGET跟单员监控报警
  451. scheduler.scheduleWithFixedDelay(() -> {
  452. List<String> monitorTraderList = coinMapper.findMonitorTraderList();
  453. forkJoinPool5.submit(() -> monitorTraderList.parallelStream().forEach(e -> {
  454. LocalDateTime endTime = LocalDateTime.now();
  455. // 交易员当前带单列表
  456. Map<String, String> paramMap = new LinkedHashMap<>();
  457. String[] split = e.split("\\|");
  458. paramMap.put("traderId", split[0]);
  459. paramMap.put("pageNo", "1");
  460. paramMap.put("pageSize", "20");
  461. try {
  462. JSONObject response = requestApi4Common("/api/mix/v1/trace/report/order/currentList", null, JSONObject.toJSONString(paramMap), JsoupUtil.HTTP_POST, paramMap);
  463. JSONArray orderList = response.getJSONArray("data");
  464. for (int i = 0; i < orderList.size(); i++) {
  465. JSONObject order = orderList.getJSONObject(i);
  466. LocalDateTime openTime = DateUtils.longToLocalDateTime(order.getLong("openTime"));
  467. String trackingNo = order.getString("trackingNo");
  468. if (Duration.between(openTime, endTime).getSeconds() < 50 && !orderMap.containsKey(trackingNo)) {
  469. orderMap.put(trackingNo, "1");
  470. String content = "<div class=\"highlight\">交易对:" + order.getString("symbol") + "</div>" +
  471. "<div>持仓方向:" + InitRunner.publicParamsMap.get("holdSide").getString(order.getString("holdSide")) + "</div>" +
  472. "<div>杠杆倍数:" + order.getString("leverage") + "</div>" +
  473. "<div>开仓均价:" + order.getString("openPrice") + "</div>" +
  474. "<div>止盈价:" + order.getString("takeProfitPrice") + "</div>" +
  475. "<div>止损价:" + order.getString("stopLossPrice") + "</div>" +
  476. "<div >交易员:" + split[1] + "</div>" +
  477. "<div class=\"gray\">开仓时间:" + DateUtils.longToString(order.getLong("openTime")) + "</div>";
  478. JSONObject params = new JSONObject();
  479. params.put("title", "BITGET交易员开单报警");
  480. params.put("logUrl", "https://jav.lvzhiqiang.top/coin/orderDetail/" + order.getString("trackingNo"));
  481. params.put("btnTxt", "跟单详情");
  482. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(content, params, null);
  483. }
  484. }
  485. } catch (Exception ex) {
  486. }
  487. })).join();
  488. }, 0, 3, TimeUnit.SECONDS);
  489. // 星球日报新闻快讯监控报警
  490. scheduler.scheduleWithFixedDelay(() -> {
  491. try {
  492. Connection.Response response = JsoupUtil.requestBody("https://www.odaily.news/v1/openapi/feeds", JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  493. JSONObject result = JSONObject.parseObject(response.body());
  494. JSONArray newsList = result.getJSONObject("data").getJSONArray("arr_news");
  495. LocalDateTime endTime = LocalDateTime.now();
  496. for (int i = 0; i < newsList.size(); i++) {
  497. if (i == 5) {
  498. break;
  499. }
  500. JSONObject news = newsList.getJSONObject(i);
  501. String id = "Odaily" + news.getString("id");
  502. String publishedAt = news.getString("published_at");
  503. LocalDateTime publishedAtTime = DateUtils.stringToLocalDateTime(publishedAt);
  504. if (Duration.between(publishedAtTime, endTime).getSeconds() < 20 && !orderMap.containsKey(id)) {
  505. orderMap.put(id, "1");
  506. String type = news.getString("type");
  507. String title = news.getString("title");
  508. String link = news.getString("link");
  509. String content = "";
  510. JSONObject params = new JSONObject();
  511. params.put("title", "Odaily监控报警");
  512. params.put("btnTxt", "新闻详情");
  513. params.put("logUrl", link);
  514. params.put("user", "@all");
  515. params.put("agentId", 1000004);
  516. if ("newsflashes".equals(type)) {
  517. String newsUrl = news.getString("news_url");
  518. content = "<div class=\"highlight\">标题:" + title + "</div>" +
  519. "<div>类型:" + "新闻快讯" + "</div>" +
  520. "<div>发布时间:" + publishedAt + "</div>" +
  521. "<div class=\"gray\">描述:" + news.getString("description").replace("\n", "&nbsp;&nbsp;&nbsp;&nbsp;") + "</div>";
  522. } else if ("posts".equals(type)) {
  523. content = "<div class=\"highlight\">标题:" + title + "</div>" +
  524. "<div>类型:" + "帖子" + "</div>" +
  525. "<div>发布时间:" + publishedAt + "</div>" +
  526. "<div class=\"gray\">描述:" + news.getString("summary").replace("\n", "&nbsp;&nbsp;&nbsp;&nbsp;") + "</div>";
  527. }
  528. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(content, params, wxCpService4News);
  529. }
  530. }
  531. } catch (Exception e) {
  532. }
  533. }, 0, 3, TimeUnit.SECONDS);
  534. // 律动日报新闻快讯监控报警
  535. scheduler.scheduleWithFixedDelay(() -> {
  536. try {
  537. Map<String, String> paramMap = new HashMap<>();
  538. paramMap.put("size", "10");
  539. paramMap.put("page", "1");
  540. Connection.Response response = JsoupUtil.requestBody("https://api.theblockbeats.news/v1/open-api/open-flash", JsoupUtil.HTTP_GET, InitRunner.proxy, null, paramMap);
  541. JSONObject result = JSONObject.parseObject(response.body());
  542. JSONArray newsList = result.getJSONObject("data").getJSONArray("data");
  543. LocalDateTime endTime = LocalDateTime.now();
  544. for (int i = 0; i < newsList.size(); i++) {
  545. if (i == 5) {
  546. break;
  547. }
  548. JSONObject news = newsList.getJSONObject(i);
  549. String id = "BlockBeats" + news.getString("id");
  550. String createTime = news.getString("create_time");
  551. LocalDateTime createTimeTime = DateUtils.longToLocalDateTime_(Long.valueOf(createTime));
  552. createTime = DateUtils.localDateTimeToString(createTimeTime);
  553. if (Duration.between(createTimeTime, endTime).getSeconds() < 20 && !orderMap.containsKey(id)) {
  554. orderMap.put(id, "1");
  555. String title = news.getString("title");
  556. String content = news.getString("content");
  557. String pic = news.getString("pic");
  558. String link = news.getString("link");
  559. String url = news.getString("url");
  560. JSONObject params = new JSONObject();
  561. params.put("title", "BlockBeats监控报警");
  562. params.put("btnTxt", "新闻详情");
  563. params.put("logUrl", link);
  564. params.put("user", "@all");
  565. params.put("agentId", 1000004);
  566. if (StringUtils.isEmpty(pic)) {
  567. // 文本卡片
  568. String contentStr = "<div class=\"highlight\">标题:" + title + "</div>" +
  569. "<div>类型:" + "新闻快讯" + "</div>" +
  570. "<div>发布时间:" + createTime + "</div>" +
  571. "<div class=\"gray\">描述:" + content.replace("\n", "&nbsp;&nbsp;&nbsp;&nbsp;") + "</div>";
  572. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(contentStr, params, wxCpService4News);
  573. } else {
  574. // 图文
  575. NewArticle article1 = new NewArticle();
  576. article1.setUrl(link);
  577. article1.setPicUrl(pic);
  578. article1.setDescription(content);
  579. article1.setTitle(title);
  580. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4NEWS(params, wxCpService4News, article1);
  581. }
  582. }
  583. }
  584. } catch (Exception e) {
  585. }
  586. }, 0, 3, TimeUnit.SECONDS);
  587. return null;
  588. }
  589. @Override
  590. @Async("coinTaskExecutor")
  591. public void monitorAlarm4APP_TEXT_CARD(String content, JSONObject params, WxCpService wxCpServiceFinal) {
  592. // 文本卡片模式发消息
  593. String title = "监控告警明细";
  594. if (params.containsKey("title")) {
  595. title = params.getString("title");
  596. }
  597. String logUrl = "https://lvzhiqiang.top";
  598. if (params.containsKey("logUrl")) {
  599. logUrl = params.getString("logUrl");
  600. }
  601. String btnTxt = "日志详情";
  602. if (params.containsKey("btnTxt")) {
  603. btnTxt = params.getString("btnTxt");
  604. }
  605. String user = "LvZhiQiang";
  606. if (params.containsKey("user")) {
  607. user = params.getString("user");
  608. }
  609. String party = "";
  610. if (params.containsKey("party")) {
  611. party = params.getString("party");
  612. }
  613. String tag = "";
  614. if (params.containsKey("tag")) {
  615. tag = params.getString("tag");
  616. }
  617. Integer agentId = properties.getAgentId();
  618. if (params.containsKey("agentId")) {
  619. agentId = params.getInteger("agentId");
  620. }
  621. if (wxCpServiceFinal == null) {
  622. wxCpServiceFinal = wxCpService;
  623. }
  624. WxCpMessage wxCpMessage = WxCpMessage.TEXTCARD().agentId(agentId)
  625. .toUser(user)
  626. .toParty(party)
  627. .toTag(tag)
  628. .title(title).description(content)
  629. .url(logUrl).btnTxt(btnTxt)
  630. .build();
  631. try {
  632. log.info("企业微信推送消息,send message: {}", wxCpMessage);
  633. WxCpMessageSendResult sendResult = wxCpServiceFinal.getMessageService().send(wxCpMessage);
  634. log.info("企业微信推送消息成功,send result: {}", sendResult);
  635. } catch (WxErrorException e) {
  636. log.error("企业微信推送消息失败!Detail: ", e);
  637. }
  638. }
  639. @Override
  640. @Async("coinTaskExecutor")
  641. public void monitorAlarm4NEWS(JSONObject params, WxCpService wxCpServiceFinal, NewArticle... articles) {
  642. // 图文消息
  643. String title = "监控告警明细";
  644. if (params.containsKey("title")) {
  645. title = params.getString("title");
  646. }
  647. String logUrl = "https://lvzhiqiang.top";
  648. if (params.containsKey("logUrl")) {
  649. logUrl = params.getString("logUrl");
  650. }
  651. String btnTxt = "日志详情";
  652. if (params.containsKey("btnTxt")) {
  653. btnTxt = params.getString("btnTxt");
  654. }
  655. String user = "LvZhiQiang";
  656. if (params.containsKey("user")) {
  657. user = params.getString("user");
  658. }
  659. String party = "";
  660. if (params.containsKey("party")) {
  661. party = params.getString("party");
  662. }
  663. String tag = "";
  664. if (params.containsKey("tag")) {
  665. tag = params.getString("tag");
  666. }
  667. Integer agentId = properties.getAgentId();
  668. if (params.containsKey("agentId")) {
  669. agentId = params.getInteger("agentId");
  670. }
  671. if (wxCpServiceFinal == null) {
  672. wxCpServiceFinal = wxCpService;
  673. }
  674. WxCpMessage wxCpMessage = WxCpMessage.NEWS().agentId(agentId)
  675. .toUser(user)
  676. .toParty(party)
  677. .toTag(tag)
  678. .addArticle(articles)
  679. .build();
  680. try {
  681. log.info("企业微信推送消息,send message: {}", wxCpMessage);
  682. WxCpMessageSendResult sendResult = wxCpServiceFinal.getMessageService().send(wxCpMessage);
  683. log.info("企业微信推送消息成功,send result: {}", sendResult);
  684. } catch (WxErrorException e) {
  685. log.error("企业微信推送消息失败!Detail: ", e);
  686. }
  687. }
  688. @Override
  689. @Async("coinTaskExecutor")
  690. public void monitorAlarm4CHAT_BOT(String content, JSONObject params) {
  691. // 调用企业微信群聊机器人发消息
  692. WxCpGroupRobotService groupRobotService = wxCpService.getGroupRobotService();
  693. String webhookUrl = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=082970da-2a33-422a-81f6-15f9bde87940";
  694. List<String> userList = Collections.singletonList("LvZhiQiang");
  695. if (params != null && params.containsKey("user")) {
  696. userList = Arrays.asList(params.getString("user").split("[|,]"));
  697. }
  698. try {
  699. log.info("企业微信推送消息,send content: {}, userIdSet: {}", content, userList);
  700. groupRobotService.sendText(webhookUrl, content, userList, Collections.emptyList());
  701. log.info("企业微信推送消息成功");
  702. } catch (WxErrorException e) {
  703. log.error("企业微信推送消息失败!Detail: ", e);
  704. }
  705. }
  706. @Override
  707. @Async("coinTaskExecutor")
  708. public void monitorAlarm(String content, String jobAlarmMode) {
  709. // 判断告警模式
  710. if (StringUtils.isEmpty(JOB_ALARM_MODE)) {
  711. jobAlarmMode = JOB_ALARM_MODE;
  712. }
  713. // 文本卡片模式发消息
  714. if (JOB_ALARM_MODE_APP_TEXT_CARD.equals(jobAlarmMode)) {
  715. String title = "监控告警明细";
  716. String logUrl = "https://lvzhiqiang.top";
  717. String btnTxt = "日志详情";
  718. WxCpMessage wxCpMessage = WxCpMessage.TEXTCARD().agentId(properties.getAgentId())
  719. .toUser("LvZhiQiang")
  720. .toParty("")
  721. .toTag("")
  722. .title(title).description(content)
  723. .url(logUrl).btnTxt(btnTxt)
  724. .build();
  725. try {
  726. log.info("企业微信推送消息,send message: {}", wxCpMessage);
  727. WxCpMessageSendResult sendResult = wxCpService.getMessageService().send(wxCpMessage);
  728. log.info("企业微信推送消息成功,send result: {}", sendResult);
  729. } catch (WxErrorException e) {
  730. log.error("企业微信推送消息失败!Detail: ", e);
  731. }
  732. }
  733. // 调用企业微信群聊机器人发消息
  734. if (JOB_ALARM_MODE_CHAT_BOT.equals(jobAlarmMode)) {
  735. WxCpGroupRobotService groupRobotService = wxCpService.getGroupRobotService();
  736. String webhookUrl = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=082970da-2a33-422a-81f6-15f9bde87940";
  737. try {
  738. log.info("企业微信推送消息,send content: {}, userIdSet: {}", content, "LvZhiQiang");
  739. groupRobotService.sendText(webhookUrl, content, Collections.singletonList("LvZhiQiang"), Collections.emptyList());
  740. log.info("企业微信推送消息成功");
  741. } catch (WxErrorException e) {
  742. log.error("企业微信推送消息失败!Detail: ", e);
  743. }
  744. }
  745. }
  746. /**
  747. * 请求通用API方法
  748. */
  749. private JSONObject requestApi4Common(String requestPath, String signQueryString, String signBody, String httpMethod, Map<String, String> paramMap) {
  750. String timestamp = String.valueOf(System.currentTimeMillis());
  751. Map<String, String> headerMap = new HashMap<>();
  752. headerMap.putAll(basicHeaderMap);
  753. try {
  754. String accessSign = CheckSign4Bitget.generate(timestamp, httpMethod, requestPath, signQueryString, signBody, secretKey);
  755. headerMap.put("ACCESS-TIMESTAMP", timestamp);
  756. headerMap.put("ACCESS-SIGN", accessSign);
  757. } catch (CloneNotSupportedException e) {
  758. throw new RuntimeException(e);
  759. } catch (InvalidKeyException e) {
  760. throw new RuntimeException(e);
  761. } catch (UnsupportedEncodingException e) {
  762. throw new RuntimeException(e);
  763. }
  764. try {
  765. String requestUrl = mainUrl + requestPath;
  766. if (httpMethod.equals(JsoupUtil.HTTP_GET)) {
  767. Connection.Response response = JsoupUtil.requestBody(requestUrl, httpMethod, InitRunner.proxy, headerMap, paramMap);
  768. return JSONObject.parseObject(response.body());
  769. } else {
  770. Connection.Response response = JsoupUtil.requestBodyJSON(requestUrl, httpMethod, InitRunner.proxy, null, headerMap, paramMap);
  771. return JSONObject.parseObject(response.body());
  772. }
  773. } catch (Exception e) {
  774. throw new RuntimeException(e);
  775. }
  776. }
  777. private JSONObject requestApi4Common4OKX(String requestPath, String signQueryString, String signBody, String httpMethod, Map<String, String> paramMap) {
  778. String timestamp = String.valueOf(System.currentTimeMillis());
  779. Map<String, String> headerMap = new HashMap<>();
  780. headerMap.putAll(basicHeaderMap4OKX);
  781. try {
  782. String accessSign = CheckSign4Bitget.generate(timestamp, httpMethod, requestPath, signQueryString, signBody, secretKey4OKX);
  783. headerMap.put("ACCESS-TIMESTAMP", timestamp);
  784. headerMap.put("ACCESS-SIGN", accessSign);
  785. } catch (CloneNotSupportedException e) {
  786. throw new RuntimeException(e);
  787. } catch (InvalidKeyException e) {
  788. throw new RuntimeException(e);
  789. } catch (UnsupportedEncodingException e) {
  790. throw new RuntimeException(e);
  791. }
  792. try {
  793. String requestUrl = mainUrl + requestPath;
  794. if (httpMethod.equals(JsoupUtil.HTTP_GET)) {
  795. Connection.Response response = JsoupUtil.requestBody(requestUrl, httpMethod, InitRunner.proxy, headerMap, paramMap);
  796. return JSONObject.parseObject(response.body());
  797. } else {
  798. Connection.Response response = JsoupUtil.requestBodyJSON(requestUrl, httpMethod, InitRunner.proxy, null, headerMap, paramMap);
  799. return JSONObject.parseObject(response.body());
  800. }
  801. } catch (Exception e) {
  802. throw new RuntimeException(e);
  803. }
  804. }
  805. @Override
  806. public Object mainSearch(JSONObject params) throws Exception {
  807. JSONArray result = new JSONArray();
  808. if (params.getString("nameEn").equals("allPositionv2")) {
  809. Map<String, String> paramMap = new HashMap<>();
  810. paramMap.put("productType", "umcbl");
  811. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  812. JSONObject response = requestApi4Common(params.getString("url"), signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  813. result = response.getJSONArray("data");
  814. renderMainSearch4AllPositionv2(result, params.getInteger("unrealizedPLSort"));
  815. } else if (params.getString("nameEn").equals("orderMarginCoinCurrent")) {
  816. Map<String, String> paramMap = new LinkedHashMap<>();
  817. paramMap.put("productType", "umcbl");
  818. paramMap.put("marginCoin", "USDT");
  819. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  820. JSONObject response = requestApi4Common(params.getString("url"), signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  821. result = response.getJSONArray("data");
  822. renderMainSearch4OrderMarginCoinCurrent(result, params.getInteger("chaRateSort"));
  823. } else if (params.getString("nameEn").equals("orderHistoryProductType")) {
  824. PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true);
  825. List<CoinHistoryOrder> historyOrderList = coinMapper.findHistoryOrderList(params.toJavaObject(Map.class));
  826. PageInfo<CoinHistoryOrder> historyOrderPageInfo = new PageInfo<>(historyOrderList);
  827. renderMainSearch4OrderHistoryProductType(historyOrderList);
  828. //result = (JSONArray) JSON.toJSON(historyOrderList);
  829. return historyOrderPageInfo;
  830. } else if (params.getString("nameEn").equals("traderList")) {
  831. PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true);
  832. List<CoinTrader> mixTraderList = coinMapper.findMixTraderList(params.toJavaObject(Map.class));
  833. PageInfo<CoinTrader> coinTraderPageInfo = new PageInfo<>(mixTraderList);
  834. renderMainSearch4TraderList(mixTraderList);
  835. //result = (JSONArray) JSON.toJSON(mixTraderList);
  836. return coinTraderPageInfo;
  837. } else if (params.getString("nameEn").equals("monitorCurrency")) {
  838. List<CoinMonitorCurrency> monitorCurrencyList = coinMapper.findMonitorCurrencyList();
  839. String requestUrl = mainUrl + params.getString("url");
  840. Connection.Response response = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  841. result = JSONObject.parseObject(response.body()).getJSONArray("data");
  842. result = renderMainSearch4MonitorCurrency(result, monitorCurrencyList, params.getInteger("changeUtcSort"));
  843. } else if (params.getString("nameEn").equals("currentPlan")) {
  844. Map<String, String> paramMap = new LinkedHashMap<>();
  845. paramMap.put("productType", "umcbl");
  846. paramMap.put("isPlan", "plan");
  847. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  848. JSONObject response = requestApi4Common(params.getString("url"), signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  849. result = response.getJSONArray("data");
  850. renderMainSearch4CurrentPlan(result, params.getInteger("chaRateSort"));
  851. }
  852. return result;
  853. }
  854. private void renderMainSearch4TraderList(List<CoinTrader> mixTraderList) {
  855. for (CoinTrader mixTrader : mixTraderList) {
  856. mixTrader.setLastTradeTime(DateUtils.longToString(Long.valueOf(mixTrader.getLastTradeTime())));
  857. }
  858. }
  859. /**
  860. * 渲染获取当前计划委托(止盈止损)列表
  861. *
  862. * @param result
  863. */
  864. private void renderMainSearch4CurrentPlan(JSONArray result, Integer chaRateSort) {
  865. forkJoinPool3.submit(() -> result.parallelStream().forEach(e -> {
  866. JSONObject jsonObject = (JSONObject) e;
  867. // 币对名称
  868. String symbol = jsonObject.getString("symbol");
  869. jsonObject.put("symbol", "<strong style=\"background-color:#F1B90d;\"><font color=\"#242A30\">" + symbol.replace("USDT_UMCBL", "") + "</font></strong>USDT_UMCBL");
  870. // 订单状态
  871. jsonObject.put("status", InitRunner.publicParamsMap.get("status").getString(jsonObject.getString("status")));
  872. // 交易类型
  873. jsonObject.put("orderType", InitRunner.publicParamsMap.get("orderType").getString(jsonObject.getString("orderType")));
  874. // 订单类型
  875. jsonObject.put("planType", InitRunner.publicParamsMap.get("planType").getString(jsonObject.getString("planType")));
  876. // 开单方向
  877. String side = jsonObject.getString("side");
  878. jsonObject.put("side", InitRunner.publicParamsMap.get("side").getString(side));
  879. if (side.equals("open_long")) {
  880. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  881. } else if (side.equals("open_short")) {
  882. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  883. } else {
  884. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#F0F0F0;\"");
  885. }
  886. // 触发类型
  887. jsonObject.put("triggerType", InitRunner.publicParamsMap.get("triggerType").getString(jsonObject.getString("triggerType")));
  888. jsonObject.put("cTime", DateUtils.longToString(jsonObject.getLong("cTime")));
  889. jsonObject.put("uTime", StringUtils.isEmpty(jsonObject.getString("uTime")) ? "--" : DateUtils.longToString(jsonObject.getLong("uTime")));
  890. // 获取合约标记价格
  891. String requestUrl = mainUrl + "/api/mix/v1/market/mark-price?symbol=" + symbol;
  892. try {
  893. Connection.Response response = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  894. String markPrice = JSONObject.parseObject(response.body()).getJSONObject("data").getString("markPrice");
  895. BigDecimal chaRate = BigDecimal.ZERO;
  896. if ("open_long".equals(side)) {
  897. chaRate = new BigDecimal(markPrice).divide(new BigDecimal(jsonObject.getString("triggerPrice")), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
  898. } else if ("open_short".equals(side)) {
  899. chaRate = new BigDecimal(jsonObject.getString("triggerPrice")).divide(new BigDecimal(markPrice), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
  900. }
  901. jsonObject.put("markPrice", markPrice);
  902. jsonObject.put("markPriceStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\"");
  903. jsonObject.put("chaRate", chaRate);
  904. jsonObject.put("chaRateStyle", " style=\"color:#FFFFFF;background-color:#5EA294;\"");
  905. } catch (Exception ex) {
  906. throw new RuntimeException(ex);
  907. }
  908. })).join();
  909. if (chaRateSort != 0) {
  910. Collections.sort(result, (o1, o2) -> chaRateSort * (((JSONObject) o1).getBigDecimal("chaRate").compareTo(((JSONObject) o2).getBigDecimal("chaRate"))));
  911. }
  912. }
  913. /**
  914. * 渲染获取监控币种列表
  915. *
  916. * @param monitorCurrencyList
  917. */
  918. private JSONArray renderMainSearch4MonitorCurrency(JSONArray result, List<CoinMonitorCurrency> monitorCurrencyList, Integer changeUtcSort) {
  919. Map<String, CoinMonitorCurrency> monitorCurrencyMap = monitorCurrencyList.stream().collect(Collectors.toMap(CoinMonitorCurrency::getSymbol, Function.identity(), (key1, key2) -> key1));
  920. Set<String> symbolSet = monitorCurrencyMap.keySet();
  921. JSONArray array = result.stream()
  922. .filter(iter -> symbolSet.contains(((JSONObject) iter).getString("symbol")))
  923. .collect(Collectors.toCollection(JSONArray::new));
  924. forkJoinPool.submit(() -> array.parallelStream().forEach(e -> {
  925. JSONObject jsonObject = (JSONObject) e;
  926. jsonObject.put("changeUtc", jsonObject.getBigDecimal("changeUtc").multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
  927. jsonObject.put("change", jsonObject.getBigDecimal("change").multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
  928. jsonObject.put("ts", DateUtils.longToString(jsonObject.getLong("ts")));
  929. jsonObject.put("category", monitorCurrencyMap.get(jsonObject.getString("symbol")).getCategory());
  930. // UTC0时涨跌幅
  931. if (jsonObject.getBigDecimal("changeUtc").compareTo(BigDecimal.ZERO) < 0) {
  932. jsonObject.put("changeUtcStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  933. } else {
  934. jsonObject.put("changeUtcStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  935. }
  936. // 24小时涨跌幅
  937. if (jsonObject.getBigDecimal("change").compareTo(BigDecimal.ZERO) < 0) {
  938. jsonObject.put("changeStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  939. } else {
  940. jsonObject.put("changeStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  941. }
  942. // 币对名称
  943. String symbol = jsonObject.getString("symbol").replace("USDT", "");
  944. jsonObject.put("symbol", "<strong style=\"background-color:#F1B90d;\"><font color=\"#242A30\">" + symbol + "</font></strong>USDT");
  945. // 标记价格
  946. jsonObject.put("closeStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\"");
  947. // 基础币量 计价币量 usdt币量
  948. jsonObject.put("baseVol", readableFileSize(jsonObject.getDouble("baseVol")));
  949. jsonObject.put("quoteVol", readableFileSize(jsonObject.getDouble("quoteVol")));
  950. jsonObject.put("usdtVol", readableFileSize(jsonObject.getDouble("usdtVol")));
  951. })).join();
  952. if (changeUtcSort != 0) {
  953. Collections.sort(array, (o1, o2) -> changeUtcSort * (((JSONObject) o1).getBigDecimal("changeUtc").compareTo(((JSONObject) o2).getBigDecimal("changeUtc"))));
  954. }
  955. return array;
  956. }
  957. /**
  958. * 渲染获取全部历史委托
  959. *
  960. * @param historyOrderList
  961. */
  962. private void renderMainSearch4OrderHistoryProductType(List<CoinHistoryOrder> historyOrderList) {
  963. for (CoinHistoryOrder coinHistoryOrder : historyOrderList) {
  964. // 币种名称
  965. coinHistoryOrder.setSymbol(coinHistoryOrder.getSymbol().replace("USDT_UMCBL", ""));
  966. // 订单状态
  967. coinHistoryOrder.setState(InitRunner.publicParamsMap.get("state").getString(coinHistoryOrder.getState()));
  968. // 开单方向
  969. coinHistoryOrder.setSide(InitRunner.publicParamsMap.get("side").getString(coinHistoryOrder.getSide()));
  970. // 总盈亏
  971. String TotalProfits = "0E-8";
  972. if (!coinHistoryOrder.getTotalProfits().contains("0E-8")) {
  973. TotalProfits = new BigDecimal(coinHistoryOrder.getTotalProfits()).setScale(2, RoundingMode.HALF_UP).toPlainString();
  974. }
  975. coinHistoryOrder.setTotalProfits(TotalProfits);
  976. // 手续费
  977. String fee = "0E-8";
  978. if (!coinHistoryOrder.getFee().contains("0E-8")) {
  979. fee = new BigDecimal(coinHistoryOrder.getFee()).setScale(2, RoundingMode.HALF_UP).toPlainString();
  980. }
  981. coinHistoryOrder.setFee(fee);
  982. // 持仓方向
  983. coinHistoryOrder.setPosSide(InitRunner.publicParamsMap.get("posSide").getString(coinHistoryOrder.getPosSide()));
  984. // 仓位模式
  985. coinHistoryOrder.setMarginMode(InitRunner.publicParamsMap.get("marginMode").getString(coinHistoryOrder.getMarginMode()));
  986. // 交易类型
  987. coinHistoryOrder.setOrderType(InitRunner.publicParamsMap.get("orderType").getString(coinHistoryOrder.getOrderType()));
  988. // 交易方向
  989. coinHistoryOrder.setTradeSide(InitRunner.publicParamsMap.get("tradeSide").getString(coinHistoryOrder.getTradeSide()));
  990. // 持仓模式
  991. coinHistoryOrder.setHoldMode(InitRunner.publicParamsMap.get("holdMode").getString(coinHistoryOrder.getHoldMode()));
  992. // orderSource
  993. coinHistoryOrder.setOrderSource(InitRunner.publicParamsMap.get("orderSource").getString(coinHistoryOrder.getOrderSource()));
  994. coinHistoryOrder.setCTime(DateUtils.longToString(Long.valueOf(coinHistoryOrder.getCTime())));
  995. coinHistoryOrder.setUTime(DateUtils.longToString(Long.valueOf(coinHistoryOrder.getUTime())));
  996. }
  997. }
  998. /**
  999. * 渲染获取全部当前委托
  1000. *
  1001. * @param result
  1002. */
  1003. private void renderMainSearch4OrderMarginCoinCurrent(JSONArray result, Integer chaRateSort) {
  1004. forkJoinPool2.submit(() -> result.parallelStream().forEach(e -> {
  1005. JSONObject jsonObject = (JSONObject) e;
  1006. // 币对名称
  1007. String symbol = jsonObject.getString("symbol");
  1008. jsonObject.put("symbol", "<strong style=\"background-color:#F1B90d;\"><font color=\"#242A30\">" + symbol.replace("USDT_UMCBL", "") + "</font></strong>USDT_UMCBL");
  1009. // 订单状态
  1010. jsonObject.put("state", InitRunner.publicParamsMap.get("state").getString(jsonObject.getString("state")));
  1011. // 开单方向
  1012. String side = jsonObject.getString("side");
  1013. jsonObject.put("side", InitRunner.publicParamsMap.get("side").getString(side));
  1014. if (side.equals("open_long")) {
  1015. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1016. } else if (side.equals("open_short")) {
  1017. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1018. } else {
  1019. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#F0F0F0;\"");
  1020. }
  1021. // 交易类型
  1022. jsonObject.put("orderType", InitRunner.publicParamsMap.get("orderType").getString(jsonObject.getString("orderType")));
  1023. // 止盈止损
  1024. jsonObject.put("presetTakeProfitPrice", StringUtils.isEmpty(jsonObject.getString("presetTakeProfitPrice")) ? "--" : jsonObject.getString("presetTakeProfitPrice"));
  1025. jsonObject.put("presetStopLossPrice", StringUtils.isEmpty(jsonObject.getString("presetTakeProfitPrice")) ? "--" : jsonObject.getString("presetTakeProfitPrice"));
  1026. // 持仓模式
  1027. jsonObject.put("holdMode", InitRunner.publicParamsMap.get("holdMode").getString(jsonObject.getString("holdMode")));
  1028. // orderSource
  1029. jsonObject.put("orderSource", InitRunner.publicParamsMap.get("orderSource").getString(jsonObject.getString("orderSource")));
  1030. // 仓位模式
  1031. jsonObject.put("marginMode", InitRunner.publicParamsMap.get("marginMode").getString(jsonObject.getString("marginMode")));
  1032. jsonObject.put("cTime", DateUtils.longToString(jsonObject.getLong("cTime")));
  1033. jsonObject.put("uTime", DateUtils.longToString(jsonObject.getLong("uTime")));
  1034. // 获取合约标记价格
  1035. String requestUrl = mainUrl + "/api/mix/v1/market/mark-price?symbol=" + symbol;
  1036. try {
  1037. Connection.Response response = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  1038. String markPrice = JSONObject.parseObject(response.body()).getJSONObject("data").getString("markPrice");
  1039. BigDecimal chaRate = BigDecimal.ZERO;
  1040. if ("open_short".equals(side)) {
  1041. chaRate = new BigDecimal(markPrice).divide(new BigDecimal(jsonObject.getString("price")), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
  1042. } else if ("open_long".equals(side)) {
  1043. chaRate = new BigDecimal(jsonObject.getString("price")).divide(new BigDecimal(markPrice), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
  1044. }
  1045. jsonObject.put("markPrice", markPrice);
  1046. jsonObject.put("markPriceStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\"");
  1047. jsonObject.put("chaRate", chaRate);
  1048. jsonObject.put("chaRateStyle", " style=\"color:#FFFFFF;background-color:#5EA294;\"");
  1049. } catch (Exception ex) {
  1050. throw new RuntimeException(ex);
  1051. }
  1052. })).join();
  1053. if (chaRateSort != 0) {
  1054. Collections.sort(result, (o1, o2) -> chaRateSort * (((JSONObject) o1).getBigDecimal("chaRate").compareTo(((JSONObject) o2).getBigDecimal("chaRate"))));
  1055. }
  1056. }
  1057. /**
  1058. * 渲染获取全部合约仓位信息V2
  1059. *
  1060. * @param result
  1061. */
  1062. private void renderMainSearch4AllPositionv2(JSONArray result, Integer unrealizedPLSort) {
  1063. forkJoinPool4.submit(() -> result.parallelStream().forEach(e -> {
  1064. JSONObject jsonObject = (JSONObject) e;
  1065. // 币对名称
  1066. String symbol = jsonObject.getString("symbol");
  1067. jsonObject.put("symbol", "<strong style=\"background-color:#F1B90d;\"><font color=\"#242A30\">" + symbol.replace("USDT_UMCBL", "") + "</font></strong>USDT_UMCBL");
  1068. // 持仓方向
  1069. String holdSide = jsonObject.getString("holdSide");
  1070. jsonObject.put("holdSide", InitRunner.publicParamsMap.get("holdSide").getString(holdSide));
  1071. if (holdSide.equals("long")) {
  1072. jsonObject.put("holdSideStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1073. } else if (holdSide.equals("short")) {
  1074. jsonObject.put("holdSideStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1075. } else {
  1076. jsonObject.put("holdSideStyle", " style=\"color:#FFFFFF;background-color:#F0F0F0;\"");
  1077. }
  1078. // 保证金模式
  1079. jsonObject.put("marginMode", InitRunner.publicParamsMap.get("marginMode").getString(jsonObject.getString("marginMode")));
  1080. // 持仓模式
  1081. jsonObject.put("holdMode", InitRunner.publicParamsMap.get("holdMode").getString(jsonObject.getString("holdMode")));
  1082. // 最近更新时间 保证金数量 (保证金币种) 平均开仓价 未实现盈亏 预估强平价
  1083. jsonObject.put("cTime", DateUtils.longToString(jsonObject.getLong("cTime")));
  1084. jsonObject.put("margin", new BigDecimal(jsonObject.getString("margin")).setScale(4, RoundingMode.HALF_UP));
  1085. jsonObject.put("averageOpenPrice", new BigDecimal(jsonObject.getString("averageOpenPrice")).setScale(4, RoundingMode.HALF_UP));
  1086. jsonObject.put("unrealizedPL", new BigDecimal(jsonObject.getString("unrealizedPL")).setScale(4, RoundingMode.HALF_UP));
  1087. jsonObject.put("liquidationPrice", new BigDecimal(jsonObject.getString("liquidationPrice")).setScale(4, RoundingMode.HALF_UP));
  1088. // 未实现盈亏
  1089. if (jsonObject.getBigDecimal("unrealizedPL").compareTo(BigDecimal.ZERO) < 0) {
  1090. jsonObject.put("unrealizedPLStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1091. } else {
  1092. jsonObject.put("unrealizedPLStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1093. }
  1094. // 回报率=未实现盈亏/保证金
  1095. BigDecimal returnRate = jsonObject.getBigDecimal("unrealizedPL").divide(jsonObject.getBigDecimal("margin"), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
  1096. jsonObject.put("returnRate", returnRate);
  1097. if (returnRate.compareTo(BigDecimal.ZERO) < 0) {
  1098. jsonObject.put("returnRateStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1099. } else {
  1100. jsonObject.put("returnRateStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1101. }
  1102. // 获取当前资金费率
  1103. String requestUrl = mainUrl + "/api/mix/v1/market/current-fundRate?symbol=" + symbol;
  1104. try {
  1105. Connection.Response response = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  1106. String fundingRate = JSONObject.parseObject(response.body()).getJSONObject("data").getString("fundingRate");
  1107. if (new BigDecimal(fundingRate).compareTo(BigDecimal.ZERO) < 0) {
  1108. jsonObject.put("fundingRateStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1109. } else {
  1110. jsonObject.put("fundingRateStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1111. }
  1112. jsonObject.put("fundingRate", new BigDecimal(fundingRate).multiply(BigDecimal.valueOf(100)).setScale(4, RoundingMode.HALF_UP).toPlainString() + "%");
  1113. } catch (Exception ex) {
  1114. throw new RuntimeException(ex);
  1115. }
  1116. // 标记价格
  1117. jsonObject.put("marketPriceStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\"");
  1118. })).join();
  1119. if (unrealizedPLSort != 0) {
  1120. Collections.sort(result, (o1, o2) -> unrealizedPLSort * (((JSONObject) o1).getBigDecimal("unrealizedPL").compareTo(((JSONObject) o2).getBigDecimal("unrealizedPL"))));
  1121. }
  1122. }
  1123. /**
  1124. * Java实现字节转换,可以自动转换为B、KB、MB、GB、TB
  1125. *
  1126. * @param size
  1127. * @return
  1128. */
  1129. private String readableFileSize(double size) {
  1130. if (size <= 0) {
  1131. return "0";
  1132. }
  1133. final String[] units = new String[]{"B", "K", "M", "G", "T"};
  1134. int digitGroups = (int) (Math.log10(size) / Math.log10(1000));
  1135. return df1.format(size / Math.pow(1000, digitGroups)) + units[digitGroups];
  1136. }
  1137. }