CoinServiceImpl.java 55 KB

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