CoinServiceImpl.java 111 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061
  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.redisson.api.RLock;
  17. import org.redisson.api.RedissonClient;
  18. import org.springframework.beans.factory.annotation.Autowired;
  19. import org.springframework.scheduling.annotation.Async;
  20. import org.springframework.stereotype.Service;
  21. import org.springframework.transaction.annotation.Propagation;
  22. import org.springframework.transaction.annotation.Transactional;
  23. import org.springframework.util.StopWatch;
  24. import top.lvzhiqiang.config.InitRunner;
  25. import top.lvzhiqiang.config.WorkWeixinProperties;
  26. import top.lvzhiqiang.entity.*;
  27. import top.lvzhiqiang.exception.BusinessException;
  28. import top.lvzhiqiang.mapper.CoinApiConfigMapper;
  29. import top.lvzhiqiang.mapper.CoinMapper;
  30. import top.lvzhiqiang.service.CoinService;
  31. import top.lvzhiqiang.util.*;
  32. import javax.annotation.Resource;
  33. import java.io.UnsupportedEncodingException;
  34. import java.math.BigDecimal;
  35. import java.math.MathContext;
  36. import java.math.RoundingMode;
  37. import java.security.InvalidKeyException;
  38. import java.text.DecimalFormat;
  39. import java.time.Duration;
  40. import java.time.LocalDate;
  41. import java.time.LocalDateTime;
  42. import java.time.temporal.ChronoUnit;
  43. import java.util.*;
  44. import java.util.concurrent.*;
  45. import java.util.concurrent.atomic.AtomicInteger;
  46. import java.util.function.Function;
  47. import java.util.stream.Collectors;
  48. import java.util.stream.Stream;
  49. /**
  50. * Coin ServiceImpl
  51. *
  52. * @author lvzhiqiang
  53. * 2023/9/5 15:23
  54. */
  55. @Service
  56. @Slf4j
  57. public class CoinServiceImpl implements CoinService {
  58. /**
  59. * 任务告警方式-应用文本卡片
  60. */
  61. public static final String JOB_ALARM_MODE_APP_TEXT_CARD = "1";
  62. /**
  63. * 任务告警方式-群聊机器人
  64. */
  65. public static final String JOB_ALARM_MODE_CHAT_BOT = "2";
  66. /**
  67. * 任务告警方式(1:应用文本卡片,2:群聊机器人文本消息)
  68. */
  69. public static String JOB_ALARM_MODE = "1";
  70. // 所有REST请求的header都必须包含以下key:
  71. private static final Map<String, String> basicHeaderMap = new HashMap<>();
  72. private static final Map<String, String> basicHeaderMap4OKX = new HashMap<>();
  73. // 主域名 URL
  74. private static final String mainUrl = "https://api.bitget.com";
  75. // 私钥,由系统随机生成,用于签名的生成。
  76. private static final String secretKey = "1fdd0fc2976bea80189ba13710e12825ca3ef6c5e25a0d76fd03f8f6cd4a61d9";
  77. private static final String secretKey4OKX = "32AC470662FBB633374B9A41950995A9";
  78. @Resource
  79. private CoinMapper coinMapper;
  80. @Resource
  81. private WxCpService wxCpService;
  82. @Autowired(required = false)
  83. private WorkWeixinProperties properties;
  84. private final Map<String, String> orderMap = new ConcurrentHashMap<>();
  85. private final Map<String, JSONObject> mixMap = new ConcurrentHashMap<>();
  86. private final static ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(10);
  87. private final ForkJoinPool forkJoinPool = new ForkJoinPool(16);
  88. private final ForkJoinPool forkJoinPool2 = new ForkJoinPool(16);
  89. private final ForkJoinPool forkJoinPool3 = new ForkJoinPool(16);
  90. private final ForkJoinPool forkJoinPool4 = new ForkJoinPool(16);
  91. private final ForkJoinPool forkJoinPool5 = new ForkJoinPool(16);
  92. private static final DecimalFormat df1 = new DecimalFormat("#,##0.00");
  93. private static final DecimalFormat df2 = new DecimalFormat("#,##0");
  94. private static final WxCpServiceImpl wxCpService4News;
  95. @Resource
  96. private CoinApiConfigMapper coinApiConfigMapper;
  97. @Resource
  98. private RedissonClient redissonClient;
  99. @Resource
  100. private RedisUtils redisUtils;
  101. static {
  102. // API KEY作为一个字符串。
  103. basicHeaderMap.put("ACCESS-KEY", "bg_433d37306df0e8901c6d107c6d9e9111");
  104. // 使用base64编码签名(请参阅签名消息)。
  105. basicHeaderMap.put("ACCESS-SIGN", "");
  106. // 您请求的时间戳。
  107. basicHeaderMap.put("ACCESS-TIMESTAMP", "");
  108. // 您在创建API KEY时设置的口令。
  109. basicHeaderMap.put("ACCESS-PASSPHRASE", "7f934f62f2701bee932204580d115228");
  110. // 统一设置为application/json。
  111. basicHeaderMap.put("Content-Type", "application/json");
  112. // 支持多语言, 如:中文(zh-CN),英语(en-US)
  113. basicHeaderMap.put("locale", "zh-CN");
  114. // 字符串类型的APIKey
  115. basicHeaderMap4OKX.put("OK-ACCESS-KEY", "25e4f515-5efd-4bb9-a934-3949b21d9f10");
  116. // 使用HMAC SHA256哈希函数获得哈希值,再使用Base-64编码(请参阅签名)
  117. basicHeaderMap4OKX.put("OK-ACCESS-SIGN", "");
  118. // 发起请求的时间(UTC),如:2020-12-08T09:08:57.715Z
  119. basicHeaderMap4OKX.put("OK-ACCESS-TIMESTAMP", "");
  120. // 您在创建API密钥时指定的Passphrase
  121. basicHeaderMap4OKX.put("OK-ACCESS-PASSPHRASE", "tmvxeGY#Q#Y2qm8");
  122. // 统一设置为application/json
  123. basicHeaderMap4OKX.put("Content-Type", "application/json");
  124. df1.setRoundingMode(RoundingMode.HALF_UP);
  125. WxCpDefaultConfigImpl wxCpDefaultConfig = new WxCpDefaultConfigImpl();
  126. wxCpDefaultConfig.setCorpId("ww95a4adba56acb55f");
  127. wxCpDefaultConfig.setAgentId(1000004);
  128. wxCpDefaultConfig.setCorpSecret("hG50gbVZ8pXm3tSzY3BpwTzbrMmm6sTf8_bPfJG_6Yc");
  129. wxCpService4News = new WxCpServiceImpl();
  130. wxCpService4News.setWxCpConfigStorage(wxCpDefaultConfig);
  131. }
  132. @Override
  133. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  134. public void syncData(String startTime, String endTime, String pageSize) {
  135. // 获取全部历史委托
  136. Map<String, String> paramMap = new LinkedHashMap<>();
  137. paramMap.put("productType", "umcbl");
  138. paramMap.put("startTime", startTime);
  139. paramMap.put("endTime", endTime);
  140. paramMap.put("pageSize", pageSize);
  141. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  142. JSONObject response = null;
  143. try {
  144. response = requestApi4Common("/api/mix/v1/order/historyProductType", signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  145. JSONArray orderList = response.getJSONObject("data").getJSONArray("orderList");
  146. if (orderList != null && orderList.size() > 0) {
  147. coinMapper.insertHistoryOrderList(JSONArray.parseArray(orderList.toJSONString(), CoinHistoryOrder.class));
  148. log.warn("syncData->insertHistoryOrderList,startTime={},endTime={},size={}", startTime, endTime, orderList.size());
  149. }
  150. } catch (Exception e) {
  151. log.error("syncData->insertHistoryOrderList error,response={}", response, e);
  152. }
  153. }
  154. @Override
  155. public void syncData4TraderList() {
  156. StopWatch stopWatch = new StopWatch();
  157. stopWatch.start();
  158. // 获取交易员列表
  159. Map<String, String> paramMap = new LinkedHashMap<>();
  160. paramMap.put("sortRule", "composite");
  161. paramMap.put("sortFlag", "desc");
  162. paramMap.put("languageType", "en-US");
  163. paramMap.put("pageSize", "20");
  164. int i = 0;
  165. String url = "/api/mix/v1/trace/traderList";
  166. JSONObject response;
  167. int totalNum = 0;
  168. for (; ; ) {
  169. paramMap.put("pageNo", String.valueOf(++i));
  170. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  171. response = requestApi4Common(url, signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  172. JSONArray dataList = response.getJSONArray("data");
  173. if (dataList.size() == 0) {
  174. break;
  175. }
  176. try {
  177. syncData4TraderListSub(dataList);
  178. } catch (Exception e) {
  179. log.error("syncData4TraderListSub error,paramMap={}", paramMap, e);
  180. }
  181. totalNum += dataList.size();
  182. }
  183. log.warn("syncData4TraderList 结束:time={},totalNum={}", stopWatch.getTotalTimeSeconds(), totalNum);
  184. }
  185. @Override
  186. public void syncCoinmarketcapCMap() {
  187. StopWatch stopWatch = new StopWatch();
  188. stopWatch.start();
  189. String coinmarketcapApikey = InitRunner.dicCodeMap.get("coinmarketcap_apikey").getCodeValue();
  190. String coinmarketcapIdmapUrl = InitRunner.dicCodeMap.get("coinmarketcap_idmap_url").getCodeValue();
  191. String coinmarketcapIdmapParams4listingStatus = InitRunner.dicCodeMap.get("coinmarketcap_idmap_params_listing_status").getCodeValue();
  192. String coinmarketcapIdmapParams4aux = InitRunner.dicCodeMap.get("coinmarketcap_idmap_params_aux").getCodeValue();
  193. Map<String, String> headerMap = new HashMap<>();
  194. headerMap.put("Accept", "application/json");
  195. headerMap.put("Accept-Encoding", "deflate,gzip");
  196. headerMap.put("X-CMC_PRO_API_KEY", coinmarketcapApikey);
  197. String[] listingStatusArr = coinmarketcapIdmapParams4listingStatus.split(",");
  198. Map<String, String> paramMap = new LinkedHashMap<>();
  199. int MAX_NUMBER = 1000;
  200. int MAX_NUMBER2 = 5000;
  201. Long totalNum = 0L;
  202. for (String listingStatus : listingStatusArr) {
  203. paramMap.put("listing_status", listingStatus);
  204. paramMap.put("aux", coinmarketcapIdmapParams4aux);
  205. try {
  206. int j = 0;
  207. Long totalNum2 = 0L;
  208. for (; ; ) {
  209. Thread.sleep(3000L);
  210. j++;
  211. paramMap.put("start", String.valueOf((j - 1) * MAX_NUMBER2 + 1));
  212. paramMap.put("limit", String.valueOf(MAX_NUMBER2));
  213. Connection.Response response = JsoupUtil.requestBody(coinmarketcapIdmapUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, headerMap, paramMap);
  214. JSONObject result = JSONObject.parseObject(response.body());
  215. JSONArray dataJA = result.getJSONArray("data");
  216. List<CoinCmcMap> cmcMapList = new ArrayList<>();
  217. CoinCmcMap coinCmcMap;
  218. for (int i = 0; i < dataJA.size(); i++) {
  219. JSONObject dataJO = dataJA.getJSONObject(i);
  220. coinCmcMap = new CoinCmcMap();
  221. coinCmcMap.setCmcId(dataJO.getLong("id"));
  222. coinCmcMap.setCmcRank(dataJO.getLong("rank"));
  223. coinCmcMap.setName(dataJO.getString("name"));
  224. coinCmcMap.setSymbol(dataJO.getString("symbol"));
  225. coinCmcMap.setSlug(dataJO.getString("slug"));
  226. coinCmcMap.setIsActive(dataJO.getInteger("is_active"));
  227. coinCmcMap.setStatus(dataJO.getString("status"));
  228. coinCmcMap.setFirstHistoricalData(DateUtils.stringutcToLocalDateTime(dataJO.getString("first_historical_data")));
  229. coinCmcMap.setLastHistoricalData(DateUtils.stringutcToLocalDateTime(dataJO.getString("last_historical_data")));
  230. coinCmcMap.setPlatform(dataJO.getString("platform"));
  231. cmcMapList.add(coinCmcMap);
  232. }
  233. // 新增或者更新
  234. Stream.iterate(0, n -> n + 1).limit((cmcMapList.size() + MAX_NUMBER - 1) / MAX_NUMBER)
  235. .forEach(i -> {
  236. List<CoinCmcMap> list = cmcMapList.stream().skip((long) i * MAX_NUMBER).limit(MAX_NUMBER).collect(Collectors.toList());
  237. coinMapper.insertCmcMapList(list);
  238. });
  239. totalNum += cmcMapList.size();
  240. totalNum2 += cmcMapList.size();
  241. if (dataJA.size() < MAX_NUMBER2) {
  242. break;
  243. }
  244. }
  245. log.warn("syncCoinmarketcapCMap {} success,totalNum={}", listingStatus, totalNum2);
  246. } catch (Exception e) {
  247. log.error("syncCoinmarketcapCMap {} error", listingStatus, e);
  248. }
  249. }
  250. log.warn("syncCoinmarketcapCMap 结束:time={},totalNum={}", stopWatch.getTotalTimeSeconds(), totalNum);
  251. }
  252. @Override
  253. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  254. public void syncData4TraderListSub(JSONArray dataList) {
  255. coinMapper.insertMixTradeList(parseMixTradeList(dataList));
  256. }
  257. @Override
  258. public String watchlistDetail(String symbol, String operationType) {
  259. CoinWatchlist coinWatchlist = coinMapper.findWatchlistBySymbol(symbol);
  260. if (coinWatchlist == null) {
  261. throw new BusinessException(1, "symbol不存在!");
  262. }
  263. if ("detail".equals(operationType)) {
  264. return MarkdownToHtmlUtils.markdownToHtmlExtensions(coinWatchlist.getRemark());
  265. } else if ("update".equals(operationType)) {
  266. return coinWatchlist.getRemark();
  267. } else {
  268. return "暂不支持该操作!";
  269. }
  270. }
  271. @Override
  272. public Object watchlistUpdate(String symbol, String remark, String score) {
  273. CoinWatchlist coinWatchlist = new CoinWatchlist();
  274. coinWatchlist.setSymbol(symbol);
  275. coinWatchlist.setRemark(remark);
  276. coinWatchlist.setScore(score);
  277. int num = coinMapper.updateCoinWatchlistRemark(coinWatchlist);
  278. return num;
  279. }
  280. private List<CoinTrader> parseMixTradeList(JSONArray dataList) {
  281. List<CoinTrader> mixTraderList = JSONArray.parseArray(dataList.toJSONString(), CoinTrader.class);
  282. mixTraderList.stream().forEach(e -> {
  283. Map<String, String> columnMap = e.getColumnList().stream().filter(Objects::nonNull)
  284. .collect(Collectors.toMap(
  285. object -> {
  286. JSONObject item = (JSONObject) object;
  287. return item.getString("describe");
  288. },
  289. object -> {
  290. JSONObject item = (JSONObject) object;
  291. return item.getString("value");
  292. }
  293. ));
  294. e.setRoi(columnMap.get("ROI"));
  295. e.setTotalProfit(columnMap.get("Total PnL").replace("$", "").replace(",", ""));
  296. e.setTotalFollowersProfit(columnMap.get("Total followers PnL").replace("$", "").replace(",", ""));
  297. e.setAum(columnMap.get("AUM").replace("$", "").replace(",", ""));
  298. e.setMaxCallbackRate(columnMap.get("Max drawdown"));
  299. e.setLast3wWinRate(columnMap.get("Last 3W win rate"));
  300. e.setAverageWinRate(StringUtils.isNotEmpty(e.getAverageWinRate()) ? new BigDecimal(e.getAverageWinRate()).setScale(2, RoundingMode.HALF_UP).toPlainString() : "0.00");
  301. e.setTraderNickName(StringUtils.isNotEmpty(e.getTraderNickName()) ? e.getTraderNickName() : "--");
  302. });
  303. return mixTraderList;
  304. }
  305. @Override
  306. public String orderDetail(String trackingNo) {
  307. Map<String, String> paramMap = new LinkedHashMap<>();
  308. paramMap.put("traderId", "b1b5467f8bb73f53ac97");
  309. paramMap.put("pageSize", "20");
  310. StringBuffer sb = new StringBuffer();
  311. // 交易员当前带单列表筛选
  312. for (int j = 1; j < 5; j++) {
  313. try {
  314. paramMap.put("pageNo", j + "");
  315. JSONObject response = requestApi4Common("/api/mix/v1/trace/report/order/currentList", null, JSONObject.toJSONString(paramMap), JsoupUtil.HTTP_POST, paramMap);
  316. JSONArray orderList = response.getJSONArray("data");
  317. for (int i = 0; i < orderList.size(); i++) {
  318. JSONObject order = orderList.getJSONObject(i);
  319. String trackingNo1 = order.getString("trackingNo");
  320. if (trackingNo.equals(trackingNo1)) {
  321. sb.append("<table border=\"1\" cellspacing=\"0\"><tr><th>键</th><th>值</th></tr>");
  322. sb.append("<tr><td>交易对</td><td>").append(order.getString("symbol")).append("</td></tr>");
  323. sb.append("<tr><td>持仓方向</td><td>").append(InitRunner.publicParamsMap.get("holdSide").getString(order.getString("holdSide"))).append("</td></tr>");
  324. sb.append("<tr><td>杠杆倍数</td><td>").append(order.getString("leverage")).append("</td></tr>");
  325. sb.append("<tr><td>开仓均价</td><td>").append(order.getString("openPrice")).append("</td></tr>");
  326. sb.append("<tr><td>开仓时间</td><td>").append(DateUtils.longToString(order.getLong("openTime"))).append("</td></tr>");
  327. sb.append("<tr><td>此笔订单跟单人数</td><td>").append(order.getString("followerNum")).append("</td></tr>");
  328. sb.append("<tr><td>保证金</td><td>").append(order.getString("marginAmount")).append("</td></tr>");
  329. sb.append("<tr><td>止盈价</td><td>").append(order.getString("takeProfitPrice")).append("</td></tr>");
  330. sb.append("<tr><td>止损价</td><td>").append(order.getString("stopLossPrice")).append("</td></tr>");
  331. sb.append("<tr><td>交易员</td><td>").append("hale").append("</td></tr>");
  332. sb.append("</table>");
  333. break;
  334. }
  335. }
  336. } catch (Exception e) {
  337. }
  338. }
  339. // 交易员历史带单列表筛选
  340. if (sb.length() == 0) {
  341. for (int j = 1; j < 5; j++) {
  342. try {
  343. paramMap.put("pageNo", j + "");
  344. JSONObject response = requestApi4Common("/api/mix/v1/trace/report/order/historyList", null, JSONObject.toJSONString(paramMap), JsoupUtil.HTTP_POST, paramMap);
  345. JSONArray orderList = response.getJSONArray("data");
  346. for (int i = 0; i < orderList.size(); i++) {
  347. JSONObject order = orderList.getJSONObject(i);
  348. String trackingNo1 = order.getString("trackingNo");
  349. if (trackingNo.equals(trackingNo1)) {
  350. sb.append("<table border=\"1\" cellspacing=\"0\" style=\"font-size: 20px;\"><tr><th>键</th><th>值</th></tr>");
  351. sb.append("<tr><td>交易对</td><td>").append(order.getString("symbol")).append("</td></tr>");
  352. sb.append("<tr><td>持仓方向</td><td>").append(InitRunner.publicParamsMap.get("holdSide").getString(order.getString("holdSide"))).append("</td></tr>");
  353. sb.append("<tr><td>杠杆倍数</td><td>").append(order.getString("leverage")).append("</td></tr>");
  354. sb.append("<tr><td>开仓均价</td><td>").append(order.getString("openPrice")).append("</td></tr>");
  355. sb.append("<tr><td>开仓时间</td><td>").append(DateUtils.longToString(order.getLong("openTime"))).append("</td></tr>");
  356. sb.append("<tr><td>此笔订单跟单人数</td><td>").append(order.getString("followerNum")).append("</td></tr>");
  357. sb.append("<tr><td>保证金</td><td>").append(order.getString("marginAmount")).append("</td></tr>");
  358. sb.append("<tr><td>平仓均价</td><td>").append(order.getString("closePrice")).append("</td></tr>");
  359. sb.append("<tr><td>平仓时间</td><td>").append(DateUtils.longToString(order.getLong("closeTime"))).append("</td></tr>");
  360. sb.append("<tr><td>平仓数量</td><td>").append(order.getString("closeAmount")).append("</td></tr>");
  361. sb.append("<tr><td>交易员</td><td>").append("hale").append("</td></tr>");
  362. sb.append("</table>");
  363. break;
  364. }
  365. }
  366. } catch (Exception e) {
  367. }
  368. }
  369. }
  370. return sb.toString();
  371. }
  372. @Override
  373. public String orderDetail2(String orderId, String symbol) {
  374. Map<String, String> paramMap = new LinkedHashMap<>();
  375. paramMap.put("symbol", symbol);
  376. paramMap.put("orderId", orderId);
  377. StringBuffer sb = new StringBuffer("<table border=\"1\" cellspacing=\"0\" style=\"font-size: 20px;\"><tr><th>键</th><th>值</th></tr>");
  378. // 获取订单详情
  379. try {
  380. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  381. JSONObject response = requestApi4Common("/api/mix/v1/order/detail", signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  382. JSONObject order = response.getJSONObject("data");
  383. sb.append("<tr><td>交易对</td><td>").append(order.getString("symbol")).append("</td></tr>");
  384. sb.append("<tr><td>交易方向</td><td>").append(InitRunner.publicParamsMap.get("side").getString(order.getString("side"))).append("</td></tr>");
  385. sb.append("<tr><td>杠杆倍数</td><td>").append(order.getString("leverage")).append("</td></tr>");
  386. sb.append("<tr><td>成交均价</td><td>").append(order.getString("priceAvg")).append("</td></tr>");
  387. sb.append("<tr><td>委托价格</td><td>").append(order.getString("price")).append("</td></tr>");
  388. sb.append("<tr><td>手续费</td><td>").append(order.getString("fee")).append("</td></tr>");
  389. sb.append("<tr><td>订单状态</td><td>").append(InitRunner.publicParamsMap.get("state").getString(order.getString("state"))).append("</td></tr>");
  390. sb.append("<tr><td>交易类型</td><td>").append(InitRunner.publicParamsMap.get("orderType").getString(order.getString("orderType"))).append("</td></tr>");
  391. sb.append("<tr><td>总盈亏</td><td>").append(order.getString("totalProfits")).append("</td></tr>");
  392. sb.append("<tr><td>预设止盈价格</td><td>").append(order.getString("presetTakeProfitPrice")).append("</td></tr>");
  393. sb.append("<tr><td>预设止损价格</td><td>").append(order.getString("presetStopLossPrice")).append("</td></tr>");
  394. sb.append("<tr><td>创建时间</td><td>").append(DateUtils.longToString(order.getLong("cTime"))).append("</td></tr>");
  395. sb.append("<tr><td>更新时间</td><td>").append(DateUtils.longToString(order.getLong("uTime"))).append("</td></tr>");
  396. } catch (Exception e) {
  397. log.error("orderDetail2 error,orderId={},symbol={}", orderId, symbol, e);
  398. }
  399. sb.append("</table>");
  400. return sb.toString();
  401. }
  402. @Override
  403. public String monitorJob() {
  404. // BITGET开仓平仓监控报警
  405. scheduler.scheduleWithFixedDelay(() -> {
  406. if (!"1".equals(getMonitorJobStatus("bitget-mix-order"))) {
  407. return;
  408. }
  409. LocalDateTime endTime = LocalDateTime.now();
  410. // 全部历史委托列表
  411. Map<String, String> paramMap = new LinkedHashMap<>();
  412. paramMap.put("productType", "umcbl");
  413. paramMap.put("startTime", String.valueOf(DateUtils.localDateTimeToMilliseconds(endTime.minusMinutes(1))));
  414. paramMap.put("endTime", String.valueOf(DateUtils.localDateTimeToMilliseconds(endTime)));
  415. paramMap.put("pageSize", "100");
  416. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  417. try {
  418. JSONObject response = requestApi4Common("/api/mix/v1/order/historyProductType", signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  419. JSONArray orderList = response.getJSONObject("data").getJSONArray("orderList");
  420. if (null == orderList) {
  421. return;
  422. }
  423. for (int i = 0; i < orderList.size(); i++) {
  424. JSONObject order = orderList.getJSONObject(i);
  425. LocalDateTime cTime = DateUtils.longToLocalDateTime(order.getLong("cTime"));
  426. String orderId = order.getString("orderId");
  427. String symbol = order.getString("symbol");
  428. if (Duration.between(cTime, endTime).getSeconds() < 50 && !orderMap.containsKey(orderId)) {
  429. orderMap.put(orderId, "1");
  430. String content = "<div class=\"highlight\">交易对:" + order.getString("symbol") + "</div>" +
  431. "<div>交易方向:" + InitRunner.publicParamsMap.get("side").getString(order.getString("side")) + "</div>" +
  432. "<div>杠杆倍数:" + order.getString("leverage") + "</div>" +
  433. "<div>成交均价:" + order.getString("priceAvg") + "</div>" +
  434. "<div>委托价格:" + order.getString("price") + "</div>" +
  435. "<div>订单状态:" + InitRunner.publicParamsMap.get("state").getString(order.getString("state")) + "</div>" +
  436. "<div>订单类型:" + InitRunner.publicParamsMap.get("orderType").getString(order.getString("orderType")) + "</div>" +
  437. "<div class=\"gray\">订单时间:" + DateUtils.longToString(order.getLong("cTime")) + "</div>";
  438. JSONObject params = new JSONObject();
  439. params.put("title", (order.getString("side").contains("open") ? "BITGET合约开单" : "BITGET合约平单") + "报警");
  440. params.put("logUrl", "https://jav.lvzhiqiang.top/coin/orderDetail2/" + orderId + "/" + symbol);
  441. params.put("btnTxt", "订单详情");
  442. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(content, params, null);
  443. }
  444. }
  445. } catch (Exception e) {
  446. log.error("bitget-mix-order error", e);
  447. }
  448. }, 0, 5, TimeUnit.SECONDS);
  449. // OKX开仓平仓监控报警
  450. scheduler.scheduleWithFixedDelay(() -> {
  451. if (!"1".equals(getMonitorJobStatus("okx-mix-order"))) {
  452. return;
  453. }
  454. LocalDateTime endTime = LocalDateTime.now();
  455. // 查看历史持仓信息
  456. Map<String, String> paramMap = new LinkedHashMap<>();
  457. paramMap.put("instType", "SWAP");
  458. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  459. try {
  460. JSONObject response = requestApi4Common4OKX("/api/v5/account/positions-history", signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  461. JSONArray orderList = response.getJSONArray("data");
  462. for (int i = 0; i < orderList.size(); i++) {
  463. JSONObject order = orderList.getJSONObject(i);
  464. //LocalDateTime cTime = DateUtils.longToLocalDateTime(order.getLong("cTime"));
  465. String orderId = "okx" + order.getString("posId");
  466. String symbol = order.getString("ccy");
  467. if (!orderMap.containsKey(orderId)) {
  468. orderMap.put(orderId, "1");
  469. log.warn("okx ={}", order);
  470. String content = "<div class=\"highlight\">交易对:" + symbol + "</div>";
  471. JSONObject params = new JSONObject();
  472. params.put("title", "OKX报警");
  473. params.put("logUrl", "https://jav.lvzhiqiang.top/coin/orderDetail2/" + orderId + "/" + symbol);
  474. params.put("btnTxt", "订单详情");
  475. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(content, params, null);
  476. }
  477. }
  478. } catch (Exception e) {
  479. log.error("okx-mix-order error", e);
  480. }
  481. }, 0, 10, TimeUnit.SECONDS);
  482. scheduler.scheduleWithFixedDelay(() -> {
  483. if (!"1".equals(getMonitorJobStatus("bitget-mix-returnrate"))) {
  484. return;
  485. }
  486. // BITGET全部合约仓位信息V2
  487. Map<String, String> paramMap = new HashMap<>();
  488. paramMap.put("productType", "umcbl");
  489. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  490. try {
  491. JSONObject response = requestApi4Common("/api/mix/v1/position/allPosition-v2", signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  492. JSONArray mixList = response.getJSONArray("data");
  493. for (int i = 0; i < mixList.size(); i++) {
  494. JSONObject mixData = mixList.getJSONObject(i);
  495. String symbol = mixData.getString("symbol");
  496. String margin = mixData.getString("margin");
  497. String averageOpenPrice = mixData.getString("averageOpenPrice");
  498. String key = symbol + margin + averageOpenPrice;
  499. // 回报率=未实现盈亏/保证金
  500. // 持仓方向 long:多头 short:空头
  501. String holdSide = mixData.getString("holdSide");
  502. BigDecimal returnRate = new BigDecimal(mixData.getString("unrealizedPL")).divide(new BigDecimal(margin), 4, RoundingMode.HALF_UP);
  503. for (int j = 1; j <= 10; j++) {
  504. BigDecimal grid = BigDecimal.valueOf(0.5).multiply(BigDecimal.valueOf(j));
  505. BigDecimal minusGrid = BigDecimal.valueOf(-0.5).multiply(BigDecimal.valueOf(j));
  506. if (returnRate.compareTo(grid) < 0) {
  507. if (mixMap.containsKey(key)) {
  508. mixMap.get(key).put("returnRate", returnRate);
  509. } else {
  510. JSONObject jsonObject = new JSONObject();
  511. jsonObject.put("returnRate", returnRate);
  512. mixMap.put(key, jsonObject);
  513. }
  514. break;
  515. }
  516. if (returnRate.compareTo(grid) > 0) {
  517. if (mixMap.containsKey(key)) {
  518. mixMap.get(key).put("returnRate", returnRate);
  519. if (mixMap.get(key).containsKey(grid.toPlainString())) {
  520. continue;
  521. } else {
  522. mixMap.get(key).put(grid.toPlainString(), true);
  523. String requestUrl = mainUrl + "/api/mix/v1/market/ticker?symbol=" + symbol;
  524. String last = "--";
  525. try {
  526. Connection.Response responseTicker = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  527. last = JSONObject.parseObject(responseTicker.body()).getJSONObject("data").getString("last");
  528. } catch (Exception e) {
  529. }
  530. String content = "币对名称:" + symbol + "\n" +
  531. "持仓方向:" + InitRunner.publicParamsMap.get("holdSide").getString(mixData.getString("holdSide")) + "\n" +
  532. "杠杆倍数:" + mixData.getString("leverage") + "\n" +
  533. "开仓均价:" + mixData.getString("averageOpenPrice") + "\n" +
  534. "当前价格:" + last + "\n" +
  535. "回报率:" + returnRate.multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP).toPlainString() + ",超过" + grid.multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP).toPlainString();
  536. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4CHAT_BOT(content, null);
  537. }
  538. } else {
  539. JSONObject jsonObject = new JSONObject();
  540. jsonObject.put("returnRate", returnRate);
  541. jsonObject.put(grid.toPlainString(), true);
  542. mixMap.put(key, jsonObject);
  543. String requestUrl = mainUrl + "/api/mix/v1/market/ticker?symbol=" + symbol;
  544. String last = "--";
  545. try {
  546. Connection.Response responseTicker = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  547. last = JSONObject.parseObject(responseTicker.body()).getJSONObject("data").getString("last");
  548. } catch (Exception e) {
  549. }
  550. String content = "币对名称:" + symbol + "\n" +
  551. "持仓方向:" + InitRunner.publicParamsMap.get("holdSide").getString(mixData.getString("holdSide")) + "\n" +
  552. "杠杆倍数:" + mixData.getString("leverage") + "\n" +
  553. "开仓均价:" + mixData.getString("averageOpenPrice") + "\n" +
  554. "当前价格:" + last + "\n" +
  555. "回报率:" + returnRate.multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP).toPlainString() + ",超过" + grid.multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP).toPlainString();
  556. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4CHAT_BOT(content, null);
  557. //break;
  558. }
  559. }
  560. }
  561. }
  562. } catch (Exception e) {
  563. log.error("bitget-mix-returnrate error", e);
  564. }
  565. }, 0, 5, TimeUnit.SECONDS);
  566. // BITGET跟单员监控报警
  567. scheduler.scheduleWithFixedDelay(() -> {
  568. if (!"1".equals(getMonitorJobStatus("bitget-mix-trader"))) {
  569. return;
  570. }
  571. try {
  572. List<String> monitorTraderList = coinMapper.findMonitorTraderList();
  573. forkJoinPool5.submit(() -> monitorTraderList.parallelStream().forEach(e -> {
  574. LocalDateTime endTime = LocalDateTime.now();
  575. // 交易员当前带单列表
  576. Map<String, String> paramMap = new LinkedHashMap<>();
  577. String[] split = e.split("\\|");
  578. paramMap.put("traderId", split[0]);
  579. paramMap.put("pageNo", "1");
  580. paramMap.put("pageSize", "20");
  581. try {
  582. JSONObject response = requestApi4Common("/api/mix/v1/trace/report/order/currentList", null, JSONObject.toJSONString(paramMap), JsoupUtil.HTTP_POST, paramMap);
  583. JSONArray orderList = response.getJSONArray("data");
  584. if (null != orderList) {
  585. for (int i = 0; i < orderList.size(); i++) {
  586. JSONObject order = orderList.getJSONObject(i);
  587. LocalDateTime openTime = DateUtils.longToLocalDateTime(order.getLong("openTime"));
  588. String trackingNo = order.getString("trackingNo");
  589. if (Duration.between(openTime, endTime).getSeconds() < 50 && !orderMap.containsKey(trackingNo)) {
  590. orderMap.put(trackingNo, "1");
  591. String content = "<div class=\"highlight\">交易对:" + order.getString("symbol") + "</div>" +
  592. "<div>持仓方向:" + InitRunner.publicParamsMap.get("holdSide").getString(order.getString("holdSide")) + "</div>" +
  593. "<div>杠杆倍数:" + order.getString("leverage") + "</div>" +
  594. "<div>开仓均价:" + order.getString("openPrice") + "</div>" +
  595. "<div>止盈价:" + order.getString("takeProfitPrice") + "</div>" +
  596. "<div>止损价:" + order.getString("stopLossPrice") + "</div>" +
  597. "<div >交易员:" + split[1] + "</div>" +
  598. "<div class=\"gray\">开仓时间:" + DateUtils.longToString(order.getLong("openTime")) + "</div>";
  599. JSONObject params = new JSONObject();
  600. params.put("title", "BITGET交易员开单报警");
  601. params.put("logUrl", "https://jav.lvzhiqiang.top/coin/orderDetail/" + order.getString("trackingNo"));
  602. params.put("btnTxt", "跟单详情");
  603. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(content, params, null);
  604. }
  605. }
  606. }
  607. } catch (Exception ex) {
  608. log.error("bitget-mix-trader error,param={}", paramMap, ex);
  609. }
  610. })).join();
  611. } catch (Exception e) {
  612. log.error("bitget-mix-trader top error", e);
  613. }
  614. }, 0, 3, TimeUnit.SECONDS);
  615. // 星球日报新闻快讯监控报警
  616. scheduler.scheduleWithFixedDelay(() -> {
  617. if (!"1".equals(getMonitorJobStatus("news-odaily"))) {
  618. return;
  619. }
  620. try {
  621. Connection.Response response = JsoupUtil.requestBody("https://www.odaily.news/v1/openapi/feeds", JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  622. JSONObject result = JSONObject.parseObject(response.body());
  623. JSONArray newsList = result.getJSONObject("data").getJSONArray("arr_news");
  624. LocalDateTime endTime = LocalDateTime.now();
  625. for (int i = 0; i < newsList.size(); i++) {
  626. if (i == 5) {
  627. break;
  628. }
  629. JSONObject news = newsList.getJSONObject(i);
  630. String id = "Odaily" + news.getString("id");
  631. String publishedAt = news.getString("published_at");
  632. LocalDateTime publishedAtTime = DateUtils.stringToLocalDateTime(publishedAt);
  633. if (Duration.between(publishedAtTime, endTime).getSeconds() < 20 && !orderMap.containsKey(id)) {
  634. orderMap.put(id, "1");
  635. String type = news.getString("type");
  636. String title = news.getString("title");
  637. String link = news.getString("link");
  638. String content = "";
  639. JSONObject params = new JSONObject();
  640. params.put("title", "Odaily监控报警");
  641. params.put("btnTxt", "新闻详情");
  642. params.put("logUrl", link);
  643. params.put("user", "@all");
  644. params.put("agentId", 1000004);
  645. if ("newsflashes".equals(type)) {
  646. String newsUrl = news.getString("news_url");
  647. content = "<div class=\"highlight\">标题:" + title + "</div>" +
  648. "<div>类型:" + "新闻快讯" + "</div>" +
  649. "<div>发布时间:" + publishedAt + "</div>" +
  650. "<div class=\"gray\">描述:" + news.getString("description").replace("\n", "&nbsp;&nbsp;&nbsp;&nbsp;") + "</div>";
  651. } else if ("posts".equals(type)) {
  652. content = "<div class=\"highlight\">标题:" + title + "</div>" +
  653. "<div>类型:" + "帖子" + "</div>" +
  654. "<div>发布时间:" + publishedAt + "</div>" +
  655. "<div class=\"gray\">描述:" + news.getString("summary").replace("\n", "&nbsp;&nbsp;&nbsp;&nbsp;") + "</div>";
  656. }
  657. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(content, params, wxCpService4News);
  658. }
  659. }
  660. } catch (Exception e) {
  661. log.error("news-odaily top error", e);
  662. }
  663. }, 0, 5, TimeUnit.SECONDS);
  664. // 律动日报新闻快讯监控报警
  665. scheduler.scheduleWithFixedDelay(() -> {
  666. if (!"1".equals(getMonitorJobStatus("news-theblockbeats"))) {
  667. return;
  668. }
  669. try {
  670. Map<String, String> paramMap = new HashMap<>();
  671. paramMap.put("size", "10");
  672. paramMap.put("page", "1");
  673. Connection.Response response = JsoupUtil.requestBody("https://api.theblockbeats.news/v1/open-api/open-flash", JsoupUtil.HTTP_GET, InitRunner.proxy, null, paramMap);
  674. JSONObject result = JSONObject.parseObject(response.body());
  675. JSONArray newsList = result.getJSONObject("data").getJSONArray("data");
  676. LocalDateTime endTime = LocalDateTime.now();
  677. for (int i = 0; i < newsList.size(); i++) {
  678. if (i == 5) {
  679. break;
  680. }
  681. JSONObject news = newsList.getJSONObject(i);
  682. String id = "BlockBeats" + news.getString("id");
  683. String createTime = news.getString("create_time");
  684. LocalDateTime createTimeTime = DateUtils.longToLocalDateTime_(Long.valueOf(createTime));
  685. createTime = DateUtils.localDateTimeToString(createTimeTime);
  686. if (Duration.between(createTimeTime, endTime).getSeconds() < 20 && !orderMap.containsKey(id)) {
  687. orderMap.put(id, "1");
  688. String title = news.getString("title");
  689. String content = news.getString("content");
  690. String pic = news.getString("pic");
  691. String link = news.getString("link");
  692. String url = news.getString("url");
  693. JSONObject params = new JSONObject();
  694. params.put("title", "BlockBeats监控报警");
  695. params.put("btnTxt", "新闻详情");
  696. params.put("logUrl", link);
  697. params.put("user", "@all");
  698. params.put("agentId", 1000004);
  699. if (StringUtils.isEmpty(pic)) {
  700. // 文本卡片
  701. String contentStr = "<div class=\"highlight\">标题:" + title + "</div>" +
  702. "<div>类型:" + "新闻快讯" + "</div>" +
  703. "<div>发布时间:" + createTime + "</div>" +
  704. "<div class=\"gray\">描述:" + content.replace("\n", "&nbsp;&nbsp;&nbsp;&nbsp;") + "</div>";
  705. if (title.contains("Upbit") || title.contains("upbit")) {
  706. JSONObject params4Upbit = new JSONObject();
  707. params4Upbit.put("title", "BlockBeats监控报警");
  708. params4Upbit.put("logUrl", link);
  709. params4Upbit.put("btnTxt", "新闻详情");
  710. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(contentStr, params4Upbit, null);
  711. }
  712. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(contentStr, params, wxCpService4News);
  713. } else {
  714. // 图文
  715. NewArticle article1 = new NewArticle();
  716. article1.setUrl(link);
  717. article1.setPicUrl(pic);
  718. article1.setDescription(content);
  719. article1.setTitle(title);
  720. if (title.contains("Upbit") || title.contains("upbit")) {
  721. JSONObject params4Upbit = new JSONObject();
  722. params4Upbit.put("title", "BlockBeats监控报警");
  723. params4Upbit.put("logUrl", link);
  724. params4Upbit.put("btnTxt", "新闻详情");
  725. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4NEWS(params4Upbit, null, article1);
  726. }
  727. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4NEWS(params, wxCpService4News, article1);
  728. }
  729. }
  730. }
  731. } catch (Exception e) {
  732. log.error("news-theblockbeats top error", e);
  733. }
  734. }, 0, 5, TimeUnit.SECONDS);
  735. // coingecko
  736. scheduler.scheduleAtFixedRate(() -> {
  737. if (!"1".equals(getMonitorJobStatus("watchlist-coingecko-cmc"))) {
  738. return;
  739. }
  740. int MAX_NUMBER = 100;
  741. Map<String, Object> params = new HashMap<>();
  742. params.put("sortField", Collections.singletonList("create_time"));
  743. params.put("sort", "desc");
  744. List<CoinWatchlist> watchlistListCKO = coinMapper.findWatchlistList(params);
  745. Stream.iterate(0, n -> n + 1).limit((watchlistListCKO.size() + MAX_NUMBER - 1) / MAX_NUMBER)
  746. .forEach(i -> {
  747. try {
  748. Thread.sleep(2000L);
  749. } catch (InterruptedException e) {
  750. throw new RuntimeException(e);
  751. }
  752. Map<String, CoinWatchlist> coinWatchlistMap4CoingeckoId = watchlistListCKO.stream().skip((long) i * MAX_NUMBER).limit(MAX_NUMBER).collect(Collectors.toMap(CoinWatchlist::getCoingeckoId, coinWatchlist -> coinWatchlist));
  753. parseWatchlistMap4Coingecko(coinWatchlistMap4CoingeckoId);
  754. });
  755. List<CoinWatchlist> watchlistListCMC = coinMapper.findWatchlistList(params);
  756. Stream.iterate(0, n -> n + 1).limit((watchlistListCMC.size() + MAX_NUMBER - 1) / MAX_NUMBER)
  757. .forEach(i -> {
  758. try {
  759. Thread.sleep(2000L);
  760. } catch (InterruptedException e) {
  761. throw new RuntimeException(e);
  762. }
  763. Map<Long, CoinWatchlist> coinWatchlistMap4CmcId = watchlistListCMC.stream().skip((long) i * MAX_NUMBER).limit(MAX_NUMBER).collect(Collectors.toMap(CoinWatchlist::getCmcId, coinWatchlist -> coinWatchlist));
  764. parseWatchlistMap4CmC(coinWatchlistMap4CmcId);
  765. });
  766. }, 0, 1, TimeUnit.HOURS);
  767. // Upbit交易所监控报警
  768. scheduler.scheduleWithFixedDelay(() -> {
  769. if (!"1".equals(getMonitorJobStatus("upbit-digitalasset-notices"))) {
  770. return;
  771. }
  772. try {
  773. Map<String, String> paramMap = new HashMap<>();
  774. paramMap.put("page", "1");
  775. paramMap.put("per_page", "20");
  776. paramMap.put("thread_name", "general");
  777. Connection.Response response = JsoupUtil.requestBody("https://api-manager.upbit.com/api/v1/notices", JsoupUtil.HTTP_GET, InitRunner.proxy, null, paramMap);
  778. JSONObject result = JSONObject.parseObject(response.body());
  779. JSONArray noticeList = result.getJSONObject("data").getJSONArray("list");
  780. for (int i = 0; i < noticeList.size(); i++) {
  781. if (i == 5) {
  782. break;
  783. }
  784. JSONObject notice = noticeList.getJSONObject(i);
  785. String idOri = notice.getString("id");
  786. String id = "Upbit" + idOri;
  787. String title = notice.getString("title");
  788. if (title.contains("New digital asset on KRW Market") && !orderMap.containsKey(id)) {
  789. orderMap.put(id, "1");
  790. String createTime = notice.getString("created_at");
  791. String updateTime = notice.getString("updated_at");
  792. String viewCount = notice.getString("view_count");
  793. JSONObject params = new JSONObject();
  794. params.put("title", "Upbit监控报警");
  795. params.put("btnTxt", "通知详情");
  796. params.put("logUrl", "https://sg-api-manager.upbit.com/api/v1/notices/" + idOri);
  797. // 文本卡片
  798. String contentStr = "<div class=\"highlight\">标题:" + title + "</div>" +
  799. "<div>发布时间:" + createTime + "</div>" +
  800. "<div>更新时间:" + updateTime + "</div>" +
  801. "<div class=\"gray\">查看次数:" + viewCount + "</div>";
  802. SpringUtils.getBean(CoinServiceImpl.class).monitorAlarm4APP_TEXT_CARD(contentStr, params, null);
  803. }
  804. }
  805. } catch (Exception e) {
  806. log.error("upbit-digitalasset-notices top error", e);
  807. try {
  808. Thread.sleep(600000L);
  809. } catch (InterruptedException ex) {
  810. }
  811. }
  812. }, 0, 5, TimeUnit.SECONDS);
  813. return null;
  814. }
  815. public void parseWatchlistMap4Coingecko(Map<String, CoinWatchlist> watchlistMap4Coingecko) {
  816. String coingeckoCoinsMarketsUrl = InitRunner.dicCodeMap.get("coingecko_coins_markets_url").getCodeValue();
  817. Map<String, String> headerMap = new HashMap<>();
  818. headerMap.put("Accept", "application/json");
  819. headerMap.put("Accept-Encoding", "deflate,gzip");
  820. Map<String, String> paramMap = new LinkedHashMap<>();
  821. paramMap.put("ids", StringUtils.join(watchlistMap4Coingecko.keySet(), ","));
  822. paramMap.put("vs_currency", "usd");
  823. AtomicInteger i = new AtomicInteger();
  824. try {
  825. Connection.Response response = JsoupUtil.requestBody(coingeckoCoinsMarketsUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, headerMap, paramMap);
  826. JSONArray result = JSONArray.parseArray(response.body());
  827. for (int j = 0; j < result.size(); j++) {
  828. JSONObject marketData = result.getJSONObject(j);
  829. String id = marketData.getString("id");
  830. if (watchlistMap4Coingecko.containsKey(id)) {
  831. CoinWatchlist coinWatchlist = watchlistMap4Coingecko.get(id);
  832. // 总市值排名
  833. if (marketData.containsKey("market_cap_rank") && null != marketData.get("market_cap_rank")) {
  834. Integer totalMarketRanking = marketData.getInteger("market_cap_rank");
  835. coinWatchlist.setTotalMarketRanking(totalMarketRanking);
  836. }
  837. // 总市值
  838. if (marketData.containsKey("market_cap") && null != marketData.get("market_cap")) {
  839. BigDecimal totalMarketValue = marketData.getBigDecimal("market_cap").setScale(2, RoundingMode.HALF_UP);
  840. coinWatchlist.setTotalMarketValue(totalMarketValue);
  841. }
  842. // 市场价格
  843. if (marketData.containsKey("current_price") && null != marketData.get("current_price")) {
  844. String markPrice = marketData.getBigDecimal("current_price").toPlainString();
  845. coinWatchlist.setMarkPrice(markPrice);
  846. }
  847. // 24小时价格变化
  848. if (marketData.containsKey("price_change_percentage_24h") && null != marketData.get("price_change_percentage_24h")) {
  849. BigDecimal priceChangePercentage24h = marketData.getBigDecimal("price_change_percentage_24h").setScale(2, RoundingMode.HALF_UP);
  850. coinWatchlist.setPriceChangePercentage24h(priceChangePercentage24h);
  851. }
  852. // 历史最高价格
  853. if (marketData.containsKey("ath") && null != marketData.get("ath")) {
  854. String highestHistoricalPrice = marketData.getBigDecimal("ath").toPlainString();
  855. coinWatchlist.setHighestHistoricalPrice(highestHistoricalPrice);
  856. }
  857. // 历史最高点涨幅比例
  858. if (marketData.containsKey("ath_change_percentage") && null != marketData.get("ath_change_percentage")) {
  859. BigDecimal athChangePercentage = marketData.getBigDecimal("ath_change_percentage").setScale(2, RoundingMode.HALF_UP);
  860. coinWatchlist.setAthChangePercentage(athChangePercentage);
  861. }
  862. // 历史最高日期
  863. if (marketData.containsKey("ath_date") && null != marketData.get("ath_date")) {
  864. LocalDate highestHistoricalDate = LocalDate.parse(marketData.getString("ath_date"), DateUtils.utcTimeFormatter);
  865. coinWatchlist.setHighestHistoricalDate(highestHistoricalDate);
  866. }
  867. // 历史最低价格
  868. if (marketData.containsKey("atl") && null != marketData.get("atl")) {
  869. String lowestHistoricalPrice = marketData.getString("atl");
  870. coinWatchlist.setLowestHistoricalPrice(lowestHistoricalPrice);
  871. }
  872. // 历史最低点涨幅比例
  873. if (marketData.containsKey("atl_change_percentage") && null != marketData.get("atl_change_percentage")) {
  874. BigDecimal atlChangePercentage = marketData.getBigDecimal("atl_change_percentage").setScale(2, RoundingMode.HALF_UP);
  875. coinWatchlist.setAtlChangePercentage(atlChangePercentage);
  876. }
  877. // 历史最低日期
  878. if (marketData.containsKey("atl_date") && null != marketData.get("atl_date")) {
  879. LocalDate lowestHistoricalDate = LocalDate.parse(marketData.getString("atl_date"), DateUtils.utcTimeFormatter);
  880. coinWatchlist.setLowestHistoricalDate(lowestHistoricalDate);
  881. }
  882. // 涨幅倍数
  883. if (StringUtils.isNotEmpty(coinWatchlist.getHighestHistoricalPrice()) && StringUtils.isNotEmpty(coinWatchlist.getLowestHistoricalPrice())) {
  884. BigDecimal increaseMultiple = new BigDecimal(coinWatchlist.getHighestHistoricalPrice()).divide(new BigDecimal(coinWatchlist.getLowestHistoricalPrice()), 0, RoundingMode.HALF_UP);
  885. coinWatchlist.setIncreaseMultiple(increaseMultiple.intValue());
  886. }
  887. // 发行日期
  888. // 发行天数
  889. if (coinWatchlist.getIssuingDate() != null) {
  890. long totalDays = ChronoUnit.DAYS.between(coinWatchlist.getIssuingDate(), LocalDate.now());
  891. coinWatchlist.setIssuingDays((int) totalDays);
  892. }
  893. coinMapper.updateCoinWatchlist(coinWatchlist);
  894. }
  895. }
  896. } catch (Exception e) {
  897. log.error("parseWatchlistMap4Coingecko error,size={},i={}", watchlistMap4Coingecko.size(), i.get(), e);
  898. }
  899. }
  900. public void parseWatchlistMap4CmC(Map<Long, CoinWatchlist> watchlistMap4CmC) {
  901. String coinmarketcapApikey = InitRunner.dicCodeMap.get("coinmarketcap_apikey").getCodeValue();
  902. String coinmarketcapQuotesLatestUrl = InitRunner.dicCodeMap.get("coinmarketcap_quotes_latest_url").getCodeValue();
  903. Map<String, String> headerMap = new HashMap<>();
  904. headerMap.put("Accept", "application/json");
  905. headerMap.put("Accept-Encoding", "deflate,gzip");
  906. headerMap.put("X-CMC_PRO_API_KEY", coinmarketcapApikey);
  907. Map<String, String> paramMap = new LinkedHashMap<>();
  908. paramMap.put("id", StringUtils.join(watchlistMap4CmC.keySet(), ","));
  909. AtomicInteger i = new AtomicInteger();
  910. try {
  911. Connection.Response response = JsoupUtil.requestBody(coinmarketcapQuotesLatestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, headerMap, paramMap);
  912. JSONObject result = JSONObject.parseObject(response.body());
  913. JSONObject dataJO = result.getJSONObject("data");
  914. watchlistMap4CmC.forEach((key, value) -> {
  915. i.getAndIncrement();
  916. if (dataJO.containsKey(key)) {
  917. JSONObject jsonObject = dataJO.getJSONObject(String.valueOf(key));
  918. value.setTotalMarketRanking(jsonObject.getInteger("cmc_rank"));
  919. BigDecimal totalMarketValue = jsonObject.getJSONObject("quote").getJSONObject("USD").getBigDecimal("market_cap").setScale(2, RoundingMode.HALF_UP);
  920. if (totalMarketValue.compareTo(BigDecimal.ZERO) == 0) {
  921. totalMarketValue = jsonObject.getBigDecimal("self_reported_market_cap").setScale(2, RoundingMode.HALF_UP);
  922. }
  923. value.setTotalMarketValue(totalMarketValue);
  924. // cmc_url
  925. value.setCmcUrl(jsonObject.getString("slug"));
  926. // coingecko_url
  927. if (StringUtils.isEmpty(value.getCoingeckoUrl())) {
  928. value.setCoingeckoUrl(value.getCmcUrl());
  929. }
  930. coinMapper.updateCoinWatchlist(value);
  931. // 解析CoinWatchlistOther
  932. CoinWatchlistOther coinWatchlistOther = new CoinWatchlistOther();
  933. coinWatchlistOther.setSymbol(value.getSymbol());
  934. if (null != jsonObject.get("max_supply")) {
  935. coinWatchlistOther.setMaxSupply(jsonObject.getBigDecimal("max_supply").setScale(0, RoundingMode.HALF_UP).toPlainString());
  936. }
  937. if (null != jsonObject.get("circulating_supply")) {
  938. coinWatchlistOther.setCirculatingSupply(jsonObject.getBigDecimal("circulating_supply").setScale(0, RoundingMode.HALF_UP).toPlainString());
  939. if ("0".equals(coinWatchlistOther.getCirculatingSupply()) && null != jsonObject.get("self_reported_circulating_supply")) {
  940. coinWatchlistOther.setCirculatingSupply(jsonObject.getBigDecimal("self_reported_circulating_supply").setScale(0, RoundingMode.HALF_UP).toPlainString());
  941. }
  942. }
  943. if (null != coinWatchlistOther.getMaxSupply() && null != coinWatchlistOther.getCirculatingSupply()) {
  944. coinWatchlistOther.setCirculatingRate(new BigDecimal(coinWatchlistOther.getCirculatingSupply()).divide(new BigDecimal(coinWatchlistOther.getMaxSupply()), 3, RoundingMode.HALF_UP).multiply(new BigDecimal("100")).toPlainString());
  945. }
  946. if (null != jsonObject.get("total_supply")) {
  947. coinWatchlistOther.setTotalSupply(jsonObject.getBigDecimal("total_supply").setScale(0, RoundingMode.HALF_UP).toPlainString());
  948. }
  949. JSONObject usdQuote = jsonObject.getJSONObject("quote").getJSONObject("USD");
  950. if (null != usdQuote.get("market_cap")) {
  951. coinWatchlistOther.setMarketCap(usdQuote.getBigDecimal("market_cap").setScale(0, RoundingMode.HALF_UP).toPlainString());
  952. if ("0".equals(coinWatchlistOther.getMarketCap()) && null != jsonObject.get("self_reported_market_cap")) {
  953. coinWatchlistOther.setMarketCap(jsonObject.getBigDecimal("self_reported_market_cap").setScale(0, RoundingMode.HALF_UP).toPlainString());
  954. }
  955. }
  956. if (null != usdQuote.get("fully_diluted_market_cap")) {
  957. coinWatchlistOther.setFullyDilutedMarketCap(usdQuote.getBigDecimal("fully_diluted_market_cap").setScale(0, RoundingMode.HALF_UP).toPlainString());
  958. }
  959. coinMapper.insertOrUpdateCoinWatchlistOther(coinWatchlistOther);
  960. }
  961. });
  962. } catch (Exception e) {
  963. log.error("parseWatchlistMap4CmC error,size={},i={}", watchlistMap4CmC.size(), i.get(), e);
  964. }
  965. }
  966. @Override
  967. @Async("coinTaskExecutor")
  968. public void monitorAlarm4APP_TEXT_CARD(String content, JSONObject params, WxCpService wxCpServiceFinal) {
  969. // 文本卡片模式发消息
  970. String title = "监控告警明细";
  971. if (params.containsKey("title")) {
  972. title = params.getString("title");
  973. }
  974. String logUrl = "https://lvzhiqiang.top";
  975. if (params.containsKey("logUrl")) {
  976. logUrl = params.getString("logUrl");
  977. }
  978. String btnTxt = "日志详情";
  979. if (params.containsKey("btnTxt")) {
  980. btnTxt = params.getString("btnTxt");
  981. }
  982. String user = "LvZhiQiang";
  983. if (params.containsKey("user")) {
  984. user = params.getString("user");
  985. }
  986. String party = "";
  987. if (params.containsKey("party")) {
  988. party = params.getString("party");
  989. }
  990. String tag = "";
  991. if (params.containsKey("tag")) {
  992. tag = params.getString("tag");
  993. }
  994. Integer agentId = properties.getAgentId();
  995. if (params.containsKey("agentId")) {
  996. agentId = params.getInteger("agentId");
  997. }
  998. if (wxCpServiceFinal == null) {
  999. wxCpServiceFinal = wxCpService;
  1000. }
  1001. WxCpMessage wxCpMessage = WxCpMessage.TEXTCARD().agentId(agentId)
  1002. .toUser(user)
  1003. .toParty(party)
  1004. .toTag(tag)
  1005. .title(title).description(content)
  1006. .url(logUrl).btnTxt(btnTxt)
  1007. .build();
  1008. try {
  1009. log.info("企业微信推送消息,send message: {}", wxCpMessage);
  1010. WxCpMessageSendResult sendResult = wxCpServiceFinal.getMessageService().send(wxCpMessage);
  1011. log.info("企业微信推送消息成功,send result: {}", sendResult);
  1012. } catch (WxErrorException e) {
  1013. log.error("企业微信推送消息失败!Detail: ", e);
  1014. }
  1015. }
  1016. @Override
  1017. @Async("coinTaskExecutor")
  1018. public void monitorAlarm4NEWS(JSONObject params, WxCpService wxCpServiceFinal, NewArticle... articles) {
  1019. // 图文消息
  1020. String title = "监控告警明细";
  1021. if (params.containsKey("title")) {
  1022. title = params.getString("title");
  1023. }
  1024. String logUrl = "https://lvzhiqiang.top";
  1025. if (params.containsKey("logUrl")) {
  1026. logUrl = params.getString("logUrl");
  1027. }
  1028. String btnTxt = "日志详情";
  1029. if (params.containsKey("btnTxt")) {
  1030. btnTxt = params.getString("btnTxt");
  1031. }
  1032. String user = "LvZhiQiang";
  1033. if (params.containsKey("user")) {
  1034. user = params.getString("user");
  1035. }
  1036. String party = "";
  1037. if (params.containsKey("party")) {
  1038. party = params.getString("party");
  1039. }
  1040. String tag = "";
  1041. if (params.containsKey("tag")) {
  1042. tag = params.getString("tag");
  1043. }
  1044. Integer agentId = properties.getAgentId();
  1045. if (params.containsKey("agentId")) {
  1046. agentId = params.getInteger("agentId");
  1047. }
  1048. if (wxCpServiceFinal == null) {
  1049. wxCpServiceFinal = wxCpService;
  1050. }
  1051. WxCpMessage wxCpMessage = WxCpMessage.NEWS().agentId(agentId)
  1052. .toUser(user)
  1053. .toParty(party)
  1054. .toTag(tag)
  1055. .addArticle(articles)
  1056. .build();
  1057. try {
  1058. log.info("企业微信推送消息,send message: {}", wxCpMessage);
  1059. WxCpMessageSendResult sendResult = wxCpServiceFinal.getMessageService().send(wxCpMessage);
  1060. log.info("企业微信推送消息成功,send result: {}", sendResult);
  1061. } catch (WxErrorException e) {
  1062. log.error("企业微信推送消息失败!Detail: ", e);
  1063. }
  1064. }
  1065. @Override
  1066. @Async("coinTaskExecutor")
  1067. public void monitorAlarm4CHAT_BOT(String content, JSONObject params) {
  1068. // 调用企业微信群聊机器人发消息
  1069. WxCpGroupRobotService groupRobotService = wxCpService.getGroupRobotService();
  1070. String webhookUrl = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=082970da-2a33-422a-81f6-15f9bde87940";
  1071. List<String> userList = Collections.singletonList("LvZhiQiang");
  1072. if (params != null && params.containsKey("user")) {
  1073. userList = Arrays.asList(params.getString("user").split("[|,]"));
  1074. }
  1075. try {
  1076. log.info("企业微信推送消息,send content: {}, userIdSet: {}", content, userList);
  1077. groupRobotService.sendText(webhookUrl, content, userList, Collections.emptyList());
  1078. log.info("企业微信推送消息成功");
  1079. } catch (WxErrorException e) {
  1080. log.error("企业微信推送消息失败!Detail: ", e);
  1081. }
  1082. }
  1083. @Override
  1084. @Async("coinTaskExecutor")
  1085. public void monitorAlarm(String content, String jobAlarmMode) {
  1086. // 判断告警模式
  1087. if (StringUtils.isEmpty(JOB_ALARM_MODE)) {
  1088. jobAlarmMode = JOB_ALARM_MODE;
  1089. }
  1090. // 文本卡片模式发消息
  1091. if (JOB_ALARM_MODE_APP_TEXT_CARD.equals(jobAlarmMode)) {
  1092. String title = "监控告警明细";
  1093. String logUrl = "https://lvzhiqiang.top";
  1094. String btnTxt = "日志详情";
  1095. WxCpMessage wxCpMessage = WxCpMessage.TEXTCARD().agentId(properties.getAgentId())
  1096. .toUser("LvZhiQiang")
  1097. .toParty("")
  1098. .toTag("")
  1099. .title(title).description(content)
  1100. .url(logUrl).btnTxt(btnTxt)
  1101. .build();
  1102. try {
  1103. log.info("企业微信推送消息,send message: {}", wxCpMessage);
  1104. WxCpMessageSendResult sendResult = wxCpService.getMessageService().send(wxCpMessage);
  1105. log.info("企业微信推送消息成功,send result: {}", sendResult);
  1106. } catch (WxErrorException e) {
  1107. log.error("企业微信推送消息失败!Detail: ", e);
  1108. }
  1109. }
  1110. // 调用企业微信群聊机器人发消息
  1111. if (JOB_ALARM_MODE_CHAT_BOT.equals(jobAlarmMode)) {
  1112. WxCpGroupRobotService groupRobotService = wxCpService.getGroupRobotService();
  1113. String webhookUrl = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=082970da-2a33-422a-81f6-15f9bde87940";
  1114. try {
  1115. log.info("企业微信推送消息,send content: {}, userIdSet: {}", content, "LvZhiQiang");
  1116. groupRobotService.sendText(webhookUrl, content, Collections.singletonList("LvZhiQiang"), Collections.emptyList());
  1117. log.info("企业微信推送消息成功");
  1118. } catch (WxErrorException e) {
  1119. log.error("企业微信推送消息失败!Detail: ", e);
  1120. }
  1121. }
  1122. }
  1123. /**
  1124. * 请求通用API方法
  1125. */
  1126. private JSONObject requestApi4Common(String requestPath, String signQueryString, String signBody, String httpMethod, Map<String, String> paramMap) {
  1127. String timestamp = String.valueOf(System.currentTimeMillis());
  1128. Map<String, String> headerMap = new HashMap<>();
  1129. headerMap.putAll(basicHeaderMap);
  1130. try {
  1131. String accessSign = CheckSign4Bitget.generate(timestamp, httpMethod, requestPath, signQueryString, signBody, secretKey);
  1132. headerMap.put("ACCESS-TIMESTAMP", timestamp);
  1133. headerMap.put("ACCESS-SIGN", accessSign);
  1134. } catch (CloneNotSupportedException e) {
  1135. throw new RuntimeException(e);
  1136. } catch (InvalidKeyException e) {
  1137. throw new RuntimeException(e);
  1138. } catch (UnsupportedEncodingException e) {
  1139. throw new RuntimeException(e);
  1140. }
  1141. try {
  1142. String requestUrl = mainUrl + requestPath;
  1143. if (httpMethod.equals(JsoupUtil.HTTP_GET)) {
  1144. Connection.Response response = JsoupUtil.requestBody(requestUrl, httpMethod, InitRunner.proxy, headerMap, paramMap);
  1145. return JSONObject.parseObject(response.body());
  1146. } else {
  1147. Connection.Response response = JsoupUtil.requestBodyJSON(requestUrl, httpMethod, InitRunner.proxy, null, headerMap, paramMap);
  1148. return JSONObject.parseObject(response.body());
  1149. }
  1150. } catch (Exception e) {
  1151. throw new RuntimeException(e);
  1152. }
  1153. }
  1154. private JSONObject requestApi4Common4OKX(String requestPath, String signQueryString, String signBody, String httpMethod, Map<String, String> paramMap) {
  1155. String timestamp = DateUtils.getUTCTimeStr();
  1156. Map<String, String> headerMap = new HashMap<>();
  1157. headerMap.putAll(basicHeaderMap4OKX);
  1158. try {
  1159. String accessSign = CheckSign4OKX.generate(timestamp, httpMethod, requestPath, signQueryString, signBody, secretKey4OKX);
  1160. headerMap.put("OK-ACCESS-TIMESTAMP", timestamp);
  1161. headerMap.put("OK-ACCESS-SIGN", accessSign);
  1162. } catch (CloneNotSupportedException e) {
  1163. throw new RuntimeException(e);
  1164. } catch (InvalidKeyException e) {
  1165. throw new RuntimeException(e);
  1166. } catch (UnsupportedEncodingException e) {
  1167. throw new RuntimeException(e);
  1168. }
  1169. try {
  1170. String requestUrl = "https://www.okx.com" + requestPath;
  1171. if (httpMethod.equals(JsoupUtil.HTTP_GET)) {
  1172. Connection.Response response = JsoupUtil.requestBody(requestUrl, httpMethod, InitRunner.proxy, headerMap, paramMap);
  1173. return JSONObject.parseObject(response.body());
  1174. } else {
  1175. Connection.Response response = JsoupUtil.requestBodyJSON(requestUrl, httpMethod, InitRunner.proxy, null, headerMap, paramMap);
  1176. return JSONObject.parseObject(response.body());
  1177. }
  1178. } catch (Exception e) {
  1179. throw new RuntimeException(e);
  1180. }
  1181. }
  1182. @Override
  1183. public Object mainSearch(JSONObject params) throws Exception {
  1184. JSONArray result = new JSONArray();
  1185. if (params.getString("nameEn").equals("allPositionv2")) {
  1186. Map<String, String> paramMap = new HashMap<>();
  1187. paramMap.put("productType", "umcbl");
  1188. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  1189. JSONObject response = requestApi4Common(params.getString("url"), signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  1190. result = response.getJSONArray("data");
  1191. renderMainSearch4AllPositionv2(result, params.getInteger("unrealizedPLSort"));
  1192. } else if (params.getString("nameEn").equals("orderMarginCoinCurrent")) {
  1193. Map<String, String> paramMap = new LinkedHashMap<>();
  1194. paramMap.put("productType", "umcbl");
  1195. paramMap.put("marginCoin", "USDT");
  1196. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  1197. JSONObject response = requestApi4Common(params.getString("url"), signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  1198. result = response.getJSONArray("data");
  1199. renderMainSearch4OrderMarginCoinCurrent(result, params.getInteger("chaRateSort"));
  1200. } else if (params.getString("nameEn").equals("orderHistoryProductType")) {
  1201. PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true);
  1202. List<CoinHistoryOrder> historyOrderList = coinMapper.findHistoryOrderList(params.toJavaObject(Map.class));
  1203. PageInfo<CoinHistoryOrder> historyOrderPageInfo = new PageInfo<>(historyOrderList);
  1204. renderMainSearch4OrderHistoryProductType(historyOrderList);
  1205. //result = (JSONArray) JSON.toJSON(historyOrderList);
  1206. return historyOrderPageInfo;
  1207. } else if (params.getString("nameEn").equals("traderList")) {
  1208. PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true);
  1209. List<CoinTrader> mixTraderList = coinMapper.findMixTraderList(params.toJavaObject(Map.class));
  1210. PageInfo<CoinTrader> coinTraderPageInfo = new PageInfo<>(mixTraderList);
  1211. renderMainSearch4TraderList(mixTraderList);
  1212. //result = (JSONArray) JSON.toJSON(mixTraderList);
  1213. return coinTraderPageInfo;
  1214. } else if (params.getString("nameEn").equals("watchlist")) {
  1215. PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true);
  1216. if (params.containsKey("sortField")) {
  1217. params.put("sortField", Arrays.asList(params.getString("sortField").split(",")));
  1218. }
  1219. List<CoinWatchlist> watchlistList = coinMapper.findWatchlistList(params.toJavaObject(Map.class));
  1220. PageInfo<CoinWatchlist> watchlistPageInfo = new PageInfo<>(watchlistList);
  1221. renderMainSearch4Watchlist(watchlistList);
  1222. return watchlistPageInfo;
  1223. } else if (params.getString("nameEn").equals("image")) {
  1224. PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true);
  1225. List<FileImage> fileImageList = coinMapper.findImageList(params.toJavaObject(Map.class));
  1226. PageInfo<FileImage> imagePageInfo = new PageInfo<>(fileImageList);
  1227. renderMainSearch4Image(fileImageList);
  1228. return imagePageInfo;
  1229. } else if (params.getString("nameEn").equals("cmcmap")) {
  1230. PageHelper.startPage(params.getInteger("pageNo"), params.getInteger("pageSize"), true);
  1231. List<CoinCmcMap> cmcMapList = coinMapper.findCmcMapList(params.toJavaObject(Map.class));
  1232. PageInfo<CoinCmcMap> cmcMapPageInfo = new PageInfo<>(cmcMapList);
  1233. renderMainSearch4CmcMap(cmcMapList);
  1234. return cmcMapPageInfo;
  1235. } else if (params.getString("nameEn").equals("monitorCurrency")) {
  1236. List<CoinMonitorCurrency> monitorCurrencyList = coinMapper.findMonitorCurrencyList();
  1237. Map<String, JSONArray> resultMulti = new ConcurrentHashMap<>();
  1238. Arrays.stream(params.getString("url").split(",")).parallel().forEach(e -> {
  1239. String requestUrl = mainUrl + e;
  1240. try {
  1241. Connection.Response response = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  1242. resultMulti.put(e, JSONObject.parseObject(response.body()).getJSONArray("data"));
  1243. } catch (Exception ex) {
  1244. throw new RuntimeException(ex);
  1245. }
  1246. });
  1247. result = renderMainSearch4MonitorCurrency(resultMulti, monitorCurrencyList, params.getInteger("changeUtcSort"));
  1248. } else if (params.getString("nameEn").equals("currentPlan")) {
  1249. Map<String, String> paramMap = new LinkedHashMap<>();
  1250. paramMap.put("productType", "umcbl");
  1251. paramMap.put("isPlan", "plan");
  1252. String signQueryString = paramMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
  1253. JSONObject response = requestApi4Common(params.getString("url"), signQueryString, null, JsoupUtil.HTTP_GET, paramMap);
  1254. result = response.getJSONArray("data");
  1255. renderMainSearch4CurrentPlan(result, params.getInteger("chaRateSort"));
  1256. }
  1257. return result;
  1258. }
  1259. private void renderMainSearch4CmcMap(List<CoinCmcMap> cmcMapList) {
  1260. for (CoinCmcMap coinCmcMap : cmcMapList) {
  1261. String platform = coinCmcMap.getPlatform();
  1262. if (StringUtils.isNotEmpty(platform)) {
  1263. JSONObject jsonObject = JSONObject.parseObject(platform);
  1264. jsonObject.remove("token_address");
  1265. coinCmcMap.setPlatform(jsonObject.toJSONString());
  1266. }
  1267. }
  1268. }
  1269. private void renderMainSearch4Image(List<FileImage> fileImageList) {
  1270. // String ftpBaseurl = InitRunner.dicCodeMap.get("ftp_baseurl").getCodeValue();
  1271. for (FileImage fileImage : fileImageList) {
  1272. // String newPath = "<a target=\"_blank\" href=\" " + ftpBaseurl + fileImage.getPath() + "\">" + fileImage.getNewName() + "</a>";
  1273. // fileImage.setNewName(newPath);
  1274. // fileImage.setOldName("<span class=\"primary\" avid=\"" + fileImage.getId() + " \" >" + fileImage.getOldName() + " </span>");
  1275. fileImage.setRemark("<span class=\"primary\" avid=\"" + fileImage.getId() + " \" >" + fileImage.getRemark() + " </span>");
  1276. }
  1277. }
  1278. private void renderMainSearch4Watchlist(List<CoinWatchlist> watchlistList) {
  1279. BigDecimal bigDecimal10000 = new BigDecimal("10000");
  1280. // 赛道分类预处理
  1281. List<String> trackCategoryList = coinApiConfigMapper.findTrackCategoryList();
  1282. Map<String, String> trackCategoryMap = new HashMap<>();
  1283. List<String> colorList = coinApiConfigMapper.findColorStyleList();
  1284. int j = 0;
  1285. for (int i = 0; i < trackCategoryList.size(); i++) {
  1286. if (j > colorList.size() - 1) {
  1287. j = 0;
  1288. }
  1289. trackCategoryMap.put(trackCategoryList.get(i), colorList.get(j));
  1290. j++;
  1291. }
  1292. String[] scoreTitleArr = {"很差", "较差", "还行", "推荐", "力荐"};
  1293. String regex = "[+-]?\\d*\\.?\\d*[eE][+-]?\\d+"; // 科学计数法正则表达式
  1294. for (CoinWatchlist coinWatchlist : watchlistList) {
  1295. // 优先级
  1296. Integer score = Integer.valueOf(coinWatchlist.getScore());
  1297. score = score > 5 ? 5 : score;
  1298. StringBuffer scoreSB = new StringBuffer("<ul class=\"rating\">");
  1299. for (int i = 0; i < scoreTitleArr.length; i++) {
  1300. String style = (i + 1) <= score ? "fa-star" : "fa-star-o";
  1301. scoreSB.append("<li id=\"" + coinWatchlist.getSymbol() + "\" title=\"" + scoreTitleArr[i] + "\" val=\"" + (i + 1) + "\"><i class=\"fa " + style + "\"></i></li>");
  1302. }
  1303. scoreSB.append("</ul>");
  1304. coinWatchlist.setScore(scoreSB.toString());
  1305. // 流通市值
  1306. if (null != coinWatchlist.getTotalMarketValue()) {
  1307. BigDecimal divide = coinWatchlist.getTotalMarketValue().divide(bigDecimal10000, 8, RoundingMode.HALF_UP);
  1308. if (divide.compareTo(bigDecimal10000) <= 0) {
  1309. coinWatchlist.setTotalMarketValueStr(divide.setScale(2, RoundingMode.HALF_UP) + "万");
  1310. } else {
  1311. divide = divide.divide(bigDecimal10000, 2, RoundingMode.HALF_UP);
  1312. coinWatchlist.setTotalMarketValueStr(divide + "亿");
  1313. }
  1314. coinWatchlist.setTotalMarketValueStr("<span class=\"primary\" avid=\"" + coinWatchlist.getSymbol() + "\" >" + coinWatchlist.getTotalMarketValueStr() + " </span>");
  1315. }
  1316. // 赛道分类
  1317. String[] trackCategoryArr = coinWatchlist.getTrackCategory().split(",");
  1318. StringBuffer sb = new StringBuffer();
  1319. String trackCategoryLength = "";
  1320. for (int i = 0; i < trackCategoryArr.length; i++) {
  1321. if (i == trackCategoryArr.length - 1) {
  1322. if (trackCategoryArr.length > 3 && trackCategoryLength.length() > 20) {
  1323. sb.append("<br>");
  1324. }
  1325. sb.append("<span class=\"selected-value\" style=\"" + trackCategoryMap.get(trackCategoryArr[i]) + " \" >" + trackCategoryArr[i] + " </span>");
  1326. } else {
  1327. trackCategoryLength += trackCategoryArr[i];
  1328. sb.append("<span class=\"selected-value\" style=\"margin-right:0.3em;" + trackCategoryMap.get(trackCategoryArr[i]) + " \" >" + trackCategoryArr[i] + " </span>");
  1329. }
  1330. }
  1331. coinWatchlist.setTrackCategoryStyle(" style=\"padding:0em 0.3em;\"");
  1332. coinWatchlist.setTrackCategory(sb.toString());
  1333. // 名称
  1334. coinWatchlist.setSymbolStyle(" style=\"background-color:rgba(70,169,244,.72);font-weight: bold;\"");
  1335. // 市场价格
  1336. coinWatchlist.setMarkPriceStyle(" style=\"color:#252B31;background-color:#C4ADE9;\"");
  1337. if (StringUtils.isNotEmpty(coinWatchlist.getMarkPrice())) {
  1338. coinWatchlist.setMarkPrice(new BigDecimal(coinWatchlist.getMarkPrice()).divide(BigDecimal.ONE, new MathContext(3)).toPlainString());
  1339. }
  1340. if (StringUtils.isNotEmpty(coinWatchlist.getHighestHistoricalPrice())) {
  1341. if (!coinWatchlist.getHighestHistoricalPrice().matches(regex)) {
  1342. coinWatchlist.setHighestHistoricalPrice(new BigDecimal(coinWatchlist.getHighestHistoricalPrice()).divide(BigDecimal.ONE, new MathContext(3)).toPlainString());
  1343. }
  1344. }
  1345. if (StringUtils.isNotEmpty(coinWatchlist.getLowestHistoricalPrice())) {
  1346. if (!coinWatchlist.getLowestHistoricalPrice().matches(regex)) {
  1347. coinWatchlist.setLowestHistoricalPrice(new BigDecimal(coinWatchlist.getLowestHistoricalPrice()).divide(BigDecimal.ONE, new MathContext(3)).toPlainString());
  1348. }
  1349. }
  1350. // 24小时价格变化
  1351. if (coinWatchlist.getPriceChangePercentage24h() == null) {
  1352. } else if (coinWatchlist.getPriceChangePercentage24h().compareTo(BigDecimal.ZERO) < 0) {
  1353. coinWatchlist.setPriceChangePercentage24hStyle(" style=\"color:#000000;background-color:#f1a8a4;\"");
  1354. } else {
  1355. coinWatchlist.setPriceChangePercentage24hStyle(" style=\"color:#000000;background-color:#aad6f5;\"");
  1356. }
  1357. /*// 历史最高点涨幅比例
  1358. if (coinWatchlist.getAthChangePercentage() == null) {
  1359. } else if (coinWatchlist.getAthChangePercentage().compareTo(BigDecimal.ZERO) < 0) {
  1360. coinWatchlist.setAthChangePercentageStyle(" style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1361. } else {
  1362. coinWatchlist.setAthChangePercentageStyle(" style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1363. }
  1364. // 历史最低点涨幅比例
  1365. if (coinWatchlist.getAtlChangePercentage() == null) {
  1366. } else if (coinWatchlist.getAtlChangePercentage().compareTo(BigDecimal.ZERO) < 0) {
  1367. coinWatchlist.setAtlChangePercentageStyle(" style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1368. } else {
  1369. coinWatchlist.setAtlChangePercentageStyle(" style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1370. }*/
  1371. // 涨幅倍数2
  1372. if (StringUtils.isNotEmpty(coinWatchlist.getHighestHistoricalPrice()) && StringUtils.isNotEmpty(coinWatchlist.getMarkPrice())) {
  1373. BigDecimal increaseMultiple = new BigDecimal(coinWatchlist.getHighestHistoricalPrice()).divide(new BigDecimal(coinWatchlist.getMarkPrice()), 1, RoundingMode.HALF_UP);
  1374. coinWatchlist.setIncreaseMultiple2(increaseMultiple.toPlainString());
  1375. if (increaseMultiple.compareTo(new BigDecimal("50")) >= 0) {
  1376. coinWatchlist.setIncreaseMultiple2Style(" style=\"color:#000000;background-color:#5a964e;\"");
  1377. } else if (increaseMultiple.compareTo(new BigDecimal("10")) >= 0) {
  1378. coinWatchlist.setIncreaseMultiple2Style(" style=\"color:#000000;background-color:#9cc494;\"");
  1379. } else {
  1380. coinWatchlist.setIncreaseMultiple2Style(" style=\"color:#000000;background-color:#dae8d7;\"");
  1381. }
  1382. }
  1383. }
  1384. }
  1385. private void renderMainSearch4TraderList(List<CoinTrader> mixTraderList) {
  1386. for (CoinTrader mixTrader : mixTraderList) {
  1387. mixTrader.setLastTradeTime(DateUtils.longToString(Long.valueOf(mixTrader.getLastTradeTime())));
  1388. }
  1389. }
  1390. /**
  1391. * 渲染获取当前计划委托(止盈止损)列表
  1392. *
  1393. * @param result
  1394. */
  1395. private void renderMainSearch4CurrentPlan(JSONArray result, Integer chaRateSort) {
  1396. forkJoinPool3.submit(() -> result.parallelStream().forEach(e -> {
  1397. JSONObject jsonObject = (JSONObject) e;
  1398. // 币对名称
  1399. String symbol = jsonObject.getString("symbol");
  1400. jsonObject.put("symbol", "<strong style=\"background-color:#F1B90d;\"><font color=\"#242A30\">" + symbol.replace("USDT_UMCBL", "") + "</font></strong>USDT_UMCBL");
  1401. // 订单状态
  1402. jsonObject.put("status", InitRunner.publicParamsMap.get("status").getString(jsonObject.getString("status")));
  1403. // 交易类型
  1404. jsonObject.put("orderType", InitRunner.publicParamsMap.get("orderType").getString(jsonObject.getString("orderType")));
  1405. // 订单类型
  1406. jsonObject.put("planType", InitRunner.publicParamsMap.get("planType").getString(jsonObject.getString("planType")));
  1407. // 开单方向
  1408. String side = jsonObject.getString("side");
  1409. jsonObject.put("side", InitRunner.publicParamsMap.get("side").getString(side));
  1410. if (side.equals("open_long")) {
  1411. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1412. } else if (side.equals("open_short")) {
  1413. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1414. } else {
  1415. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#F0F0F0;\"");
  1416. }
  1417. // 触发类型
  1418. jsonObject.put("triggerType", InitRunner.publicParamsMap.get("triggerType").getString(jsonObject.getString("triggerType")));
  1419. jsonObject.put("cTime", DateUtils.longToString(jsonObject.getLong("cTime")));
  1420. jsonObject.put("uTime", StringUtils.isEmpty(jsonObject.getString("uTime")) ? "--" : DateUtils.longToString(jsonObject.getLong("uTime")));
  1421. // 获取合约标记价格
  1422. String requestUrl = mainUrl + "/api/mix/v1/market/mark-price?symbol=" + symbol;
  1423. try {
  1424. Connection.Response response = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  1425. String markPrice = JSONObject.parseObject(response.body()).getJSONObject("data").getString("markPrice");
  1426. BigDecimal chaRate = BigDecimal.ZERO;
  1427. BigDecimal triggerPriceDecimal = new BigDecimal(jsonObject.getString("triggerPrice"));
  1428. BigDecimal markPriceDecimal = new BigDecimal(markPrice);
  1429. if (markPriceDecimal.compareTo(triggerPriceDecimal) < 0) {
  1430. chaRate = markPriceDecimal.divide(triggerPriceDecimal, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
  1431. } else if (markPriceDecimal.compareTo(triggerPriceDecimal) > 0) {
  1432. chaRate = triggerPriceDecimal.divide(markPriceDecimal, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
  1433. }
  1434. jsonObject.put("markPrice", markPrice);
  1435. jsonObject.put("markPriceStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\"");
  1436. jsonObject.put("chaRate", chaRate);
  1437. jsonObject.put("chaRateStyle", " style=\"color:#FFFFFF;background-color:#5EA294;\"");
  1438. } catch (Exception ex) {
  1439. throw new RuntimeException(ex);
  1440. }
  1441. })).join();
  1442. if (chaRateSort != 0) {
  1443. Collections.sort(result, (o1, o2) -> chaRateSort * (((JSONObject) o1).getBigDecimal("chaRate").compareTo(((JSONObject) o2).getBigDecimal("chaRate"))));
  1444. }
  1445. }
  1446. /**
  1447. * 渲染监控币种列表
  1448. *
  1449. * @param monitorCurrencyList
  1450. */
  1451. private JSONArray renderMainSearch4MonitorCurrency(Map<String, JSONArray> resultMulti, List<CoinMonitorCurrency> monitorCurrencyList, Integer changeUtcSort) {
  1452. Map<String, CoinMonitorCurrency> monitorCurrencyMap4Mix = monitorCurrencyList.stream().filter(e -> e.getType().equals("2")).collect(Collectors.toMap(CoinMonitorCurrency::getSymbol, Function.identity(), (key1, key2) -> key1));
  1453. Set<String> symbolSet4Mix = monitorCurrencyMap4Mix.keySet();
  1454. Map<String, CoinMonitorCurrency> monitorCurrencyMap4Spot = monitorCurrencyList.stream().filter(e -> e.getType().contains("1")).collect(Collectors.toMap(CoinMonitorCurrency::getSymbol, Function.identity(), (key1, key2) -> key1));
  1455. Set<String> symbolSet4Spot = monitorCurrencyMap4Spot.keySet();
  1456. JSONArray array4Spot = resultMulti.get("/api/spot/v1/market/tickers").stream()
  1457. .filter(iter -> symbolSet4Spot.contains(((JSONObject) iter).getString("symbol")))
  1458. .collect(Collectors.toCollection(JSONArray::new));
  1459. JSONArray array4Mix = resultMulti.get("/api/mix/v1/market/tickers?productType=umcbl").stream()
  1460. .filter(iter -> symbolSet4Mix.contains(((JSONObject) iter).getString("symbol")))
  1461. .collect(Collectors.toCollection(JSONArray::new));
  1462. forkJoinPool.submit(() -> array4Spot.parallelStream().forEach(e -> {
  1463. JSONObject jsonObject = (JSONObject) e;
  1464. jsonObject.put("changeUtc", jsonObject.getBigDecimal("changeUtc").multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
  1465. jsonObject.put("change", jsonObject.getBigDecimal("change").multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
  1466. jsonObject.put("ts", DateUtils.longToString(jsonObject.getLong("ts")));
  1467. jsonObject.put("category", monitorCurrencyMap4Spot.get(jsonObject.getString("symbol")).getCategory());
  1468. // UTC0时涨跌幅
  1469. if (jsonObject.getBigDecimal("changeUtc").compareTo(BigDecimal.ZERO) < 0) {
  1470. jsonObject.put("changeUtcStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1471. } else {
  1472. jsonObject.put("changeUtcStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1473. }
  1474. // 24小时涨跌幅
  1475. if (jsonObject.getBigDecimal("change").compareTo(BigDecimal.ZERO) < 0) {
  1476. jsonObject.put("changeStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1477. } else {
  1478. jsonObject.put("changeStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1479. }
  1480. // 币对名称
  1481. String symbol = jsonObject.getString("symbol").replace("USDT", "");
  1482. if ("BTC".equals(symbol) || "ETH".equals(symbol)) {
  1483. jsonObject.put("symbol", "<strong style=\"background-color:#FF6EB4;\"><font color=\"#242A30\">" + symbol + "</font></strong>USDT");
  1484. } else {
  1485. jsonObject.put("symbol", "<strong style=\"background-color:#F1B90d;\"><font color=\"#242A30\">" + symbol + "</font></strong>USDT");
  1486. }
  1487. // 标记价格
  1488. jsonObject.put("closeStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\"");
  1489. // 基础币量 计价币量 usdt币量
  1490. jsonObject.put("baseVol", readableFileSize(jsonObject.getDouble("baseVol")));
  1491. jsonObject.put("quoteVol", readableFileSize(jsonObject.getDouble("quoteVol")));
  1492. jsonObject.put("usdtVol", readableFileSize(jsonObject.getDouble("usdtVol")));
  1493. })).join();
  1494. forkJoinPool.submit(() -> array4Mix.parallelStream().forEach(e -> {
  1495. JSONObject jsonObject = (JSONObject) e;
  1496. jsonObject.put("changeUtc", jsonObject.getBigDecimal("chgUtc").multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
  1497. jsonObject.put("change", jsonObject.getBigDecimal("priceChangePercent").multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
  1498. jsonObject.put("ts", DateUtils.longToString(jsonObject.getLong("timestamp")));
  1499. jsonObject.put("category", monitorCurrencyMap4Mix.get(jsonObject.getString("symbol")).getCategory());
  1500. // UTC0时涨跌幅
  1501. if (jsonObject.getBigDecimal("changeUtc").compareTo(BigDecimal.ZERO) < 0) {
  1502. jsonObject.put("changeUtcStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1503. } else {
  1504. jsonObject.put("changeUtcStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1505. }
  1506. // 24小时涨跌幅
  1507. if (jsonObject.getBigDecimal("change").compareTo(BigDecimal.ZERO) < 0) {
  1508. jsonObject.put("changeStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1509. } else {
  1510. jsonObject.put("changeStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1511. }
  1512. // 币对名称
  1513. String symbol = jsonObject.getString("symbol").replace("USDT_UMCBL", "");
  1514. jsonObject.put("symbol", "<strong style=\"background-color:#F1B90d;\"><font color=\"#242A30\">" + symbol + "</font></strong>USDT_UMCBL");
  1515. // 标记价格
  1516. jsonObject.put("close", jsonObject.getString("last"));
  1517. jsonObject.put("closeStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\"");
  1518. // 基础币量 计价币量 usdt币量
  1519. jsonObject.put("baseVol", readableFileSize(jsonObject.getDouble("baseVolume")));
  1520. jsonObject.put("quoteVol", readableFileSize(jsonObject.getDouble("quoteVolume")));
  1521. jsonObject.put("usdtVol", readableFileSize(jsonObject.getDouble("quoteVolume")));
  1522. // 其他字段兼容
  1523. jsonObject.put("openUtc0", jsonObject.getString("openUtc"));
  1524. jsonObject.put("buyOne", jsonObject.getString("bestBid"));
  1525. jsonObject.put("sellOne", jsonObject.getString("bestAsk"));
  1526. })).join();
  1527. array4Spot.addAll(array4Mix);
  1528. if (changeUtcSort != 0) {
  1529. Collections.sort(array4Spot, (o1, o2) -> changeUtcSort * (((JSONObject) o1).getBigDecimal("changeUtc").compareTo(((JSONObject) o2).getBigDecimal("changeUtc"))));
  1530. }
  1531. return array4Spot;
  1532. }
  1533. /**
  1534. * 渲染获取全部历史委托
  1535. *
  1536. * @param historyOrderList
  1537. */
  1538. private void renderMainSearch4OrderHistoryProductType(List<CoinHistoryOrder> historyOrderList) {
  1539. for (CoinHistoryOrder coinHistoryOrder : historyOrderList) {
  1540. // 币种名称
  1541. coinHistoryOrder.setSymbol(coinHistoryOrder.getSymbol().replace("USDT_UMCBL", ""));
  1542. // 订单状态
  1543. coinHistoryOrder.setState(InitRunner.publicParamsMap.get("state").getString(coinHistoryOrder.getState()));
  1544. // 开单方向
  1545. coinHistoryOrder.setSide(InitRunner.publicParamsMap.get("side").getString(coinHistoryOrder.getSide()));
  1546. // 总盈亏
  1547. String TotalProfits = "0E-8";
  1548. if (!coinHistoryOrder.getTotalProfits().contains("0E-8")) {
  1549. TotalProfits = new BigDecimal(coinHistoryOrder.getTotalProfits()).setScale(2, RoundingMode.HALF_UP).toPlainString();
  1550. }
  1551. coinHistoryOrder.setTotalProfits(TotalProfits);
  1552. // 手续费
  1553. String fee = "0E-8";
  1554. if (!coinHistoryOrder.getFee().contains("0E-8")) {
  1555. fee = new BigDecimal(coinHistoryOrder.getFee()).setScale(2, RoundingMode.HALF_UP).toPlainString();
  1556. }
  1557. coinHistoryOrder.setFee(fee);
  1558. // 持仓方向
  1559. coinHistoryOrder.setPosSide(InitRunner.publicParamsMap.get("posSide").getString(coinHistoryOrder.getPosSide()));
  1560. // 仓位模式
  1561. coinHistoryOrder.setMarginMode(InitRunner.publicParamsMap.get("marginMode").getString(coinHistoryOrder.getMarginMode()));
  1562. // 交易类型
  1563. coinHistoryOrder.setOrderType(InitRunner.publicParamsMap.get("orderType").getString(coinHistoryOrder.getOrderType()));
  1564. // 交易方向
  1565. coinHistoryOrder.setTradeSide(InitRunner.publicParamsMap.get("tradeSide").getString(coinHistoryOrder.getTradeSide()));
  1566. // 持仓模式
  1567. coinHistoryOrder.setHoldMode(InitRunner.publicParamsMap.get("holdMode").getString(coinHistoryOrder.getHoldMode()));
  1568. // orderSource
  1569. coinHistoryOrder.setOrderSource(InitRunner.publicParamsMap.get("orderSource").getString(coinHistoryOrder.getOrderSource()));
  1570. coinHistoryOrder.setCTime(DateUtils.longToString(Long.valueOf(coinHistoryOrder.getCTime())));
  1571. coinHistoryOrder.setUTime(DateUtils.longToString(Long.valueOf(coinHistoryOrder.getUTime())));
  1572. }
  1573. }
  1574. /**
  1575. * 渲染获取全部当前委托
  1576. *
  1577. * @param result
  1578. */
  1579. private void renderMainSearch4OrderMarginCoinCurrent(JSONArray result, Integer chaRateSort) {
  1580. forkJoinPool2.submit(() -> result.parallelStream().forEach(e -> {
  1581. JSONObject jsonObject = (JSONObject) e;
  1582. // 币对名称
  1583. String symbol = jsonObject.getString("symbol");
  1584. jsonObject.put("symbol", "<strong style=\"background-color:#F1B90d;\"><font color=\"#242A30\">" + symbol.replace("USDT_UMCBL", "") + "</font></strong>USDT_UMCBL");
  1585. // 订单状态
  1586. jsonObject.put("state", InitRunner.publicParamsMap.get("state").getString(jsonObject.getString("state")));
  1587. // 开单方向
  1588. String side = jsonObject.getString("side");
  1589. jsonObject.put("side", InitRunner.publicParamsMap.get("side").getString(side));
  1590. if (side.equals("open_long")) {
  1591. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1592. } else if (side.equals("open_short")) {
  1593. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1594. } else {
  1595. jsonObject.put("sideStyle", " style=\"color:#FFFFFF;background-color:#F0F0F0;\"");
  1596. }
  1597. // 交易类型
  1598. jsonObject.put("orderType", InitRunner.publicParamsMap.get("orderType").getString(jsonObject.getString("orderType")));
  1599. // 止盈止损
  1600. jsonObject.put("presetTakeProfitPrice", StringUtils.isEmpty(jsonObject.getString("presetTakeProfitPrice")) ? "--" : jsonObject.getString("presetTakeProfitPrice"));
  1601. jsonObject.put("presetStopLossPrice", StringUtils.isEmpty(jsonObject.getString("presetTakeProfitPrice")) ? "--" : jsonObject.getString("presetTakeProfitPrice"));
  1602. // 持仓模式
  1603. jsonObject.put("holdMode", InitRunner.publicParamsMap.get("holdMode").getString(jsonObject.getString("holdMode")));
  1604. // orderSource
  1605. jsonObject.put("orderSource", InitRunner.publicParamsMap.get("orderSource").getString(jsonObject.getString("orderSource")));
  1606. // 仓位模式
  1607. jsonObject.put("marginMode", InitRunner.publicParamsMap.get("marginMode").getString(jsonObject.getString("marginMode")));
  1608. jsonObject.put("cTime", DateUtils.longToString(jsonObject.getLong("cTime")));
  1609. jsonObject.put("uTime", DateUtils.longToString(jsonObject.getLong("uTime")));
  1610. // 获取合约标记价格
  1611. String requestUrl = mainUrl + "/api/mix/v1/market/mark-price?symbol=" + symbol;
  1612. try {
  1613. Connection.Response response = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  1614. String markPrice = JSONObject.parseObject(response.body()).getJSONObject("data").getString("markPrice");
  1615. BigDecimal chaRate = BigDecimal.ZERO;
  1616. if ("open_short".equals(side)) {
  1617. chaRate = new BigDecimal(markPrice).divide(new BigDecimal(jsonObject.getString("price")), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
  1618. } else if ("open_long".equals(side)) {
  1619. chaRate = new BigDecimal(jsonObject.getString("price")).divide(new BigDecimal(markPrice), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
  1620. }
  1621. jsonObject.put("markPrice", markPrice);
  1622. jsonObject.put("markPriceStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\"");
  1623. jsonObject.put("chaRate", chaRate);
  1624. jsonObject.put("chaRateStyle", " style=\"color:#FFFFFF;background-color:#5EA294;\"");
  1625. } catch (Exception ex) {
  1626. throw new RuntimeException(ex);
  1627. }
  1628. })).join();
  1629. if (chaRateSort != 0) {
  1630. Collections.sort(result, (o1, o2) -> chaRateSort * (((JSONObject) o1).getBigDecimal("chaRate").compareTo(((JSONObject) o2).getBigDecimal("chaRate"))));
  1631. }
  1632. }
  1633. /**
  1634. * 渲染获取全部合约仓位信息V2
  1635. *
  1636. * @param result
  1637. */
  1638. private void renderMainSearch4AllPositionv2(JSONArray result, Integer unrealizedPLSort) {
  1639. forkJoinPool4.submit(() -> result.parallelStream().forEach(e -> {
  1640. JSONObject jsonObject = (JSONObject) e;
  1641. // 币对名称
  1642. String symbol = jsonObject.getString("symbol");
  1643. jsonObject.put("symbol", "<strong style=\"background-color:#F1B90d;\"><font color=\"#242A30\">" + symbol.replace("USDT_UMCBL", "") + "</font></strong>USDT_UMCBL");
  1644. // 持仓方向
  1645. String holdSide = jsonObject.getString("holdSide");
  1646. jsonObject.put("holdSide", InitRunner.publicParamsMap.get("holdSide").getString(holdSide));
  1647. if (holdSide.equals("long")) {
  1648. jsonObject.put("holdSideStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1649. } else if (holdSide.equals("short")) {
  1650. jsonObject.put("holdSideStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1651. } else {
  1652. jsonObject.put("holdSideStyle", " style=\"color:#FFFFFF;background-color:#F0F0F0;\"");
  1653. }
  1654. // 保证金模式
  1655. jsonObject.put("marginMode", InitRunner.publicParamsMap.get("marginMode").getString(jsonObject.getString("marginMode")));
  1656. // 持仓模式
  1657. jsonObject.put("holdMode", InitRunner.publicParamsMap.get("holdMode").getString(jsonObject.getString("holdMode")));
  1658. // 最近更新时间 保证金数量 (保证金币种) 平均开仓价 未实现盈亏 预估强平价
  1659. jsonObject.put("cTime", DateUtils.longToString(jsonObject.getLong("cTime")));
  1660. jsonObject.put("margin", new BigDecimal(jsonObject.getString("margin")).setScale(4, RoundingMode.HALF_UP));
  1661. jsonObject.put("averageOpenPrice", new BigDecimal(jsonObject.getString("averageOpenPrice")).divide(BigDecimal.ONE, new MathContext(4)));
  1662. jsonObject.put("unrealizedPL", new BigDecimal(jsonObject.getString("unrealizedPL")).setScale(4, RoundingMode.HALF_UP));
  1663. jsonObject.put("liquidationPrice", new BigDecimal(jsonObject.getString("liquidationPrice")).divide(BigDecimal.ONE, new MathContext(4)));
  1664. // 未实现盈亏
  1665. if (jsonObject.getBigDecimal("unrealizedPL").compareTo(BigDecimal.ZERO) < 0) {
  1666. jsonObject.put("unrealizedPLStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1667. } else {
  1668. jsonObject.put("unrealizedPLStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1669. }
  1670. // 回报率=未实现盈亏/保证金
  1671. BigDecimal returnRate = jsonObject.getBigDecimal("unrealizedPL").divide(jsonObject.getBigDecimal("margin"), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
  1672. jsonObject.put("returnRate", returnRate);
  1673. if (returnRate.compareTo(BigDecimal.ZERO) < 0) {
  1674. jsonObject.put("returnRateStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1675. } else {
  1676. jsonObject.put("returnRateStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1677. }
  1678. // 获取当前资金费率
  1679. String requestUrl = mainUrl + "/api/mix/v1/market/current-fundRate?symbol=" + symbol;
  1680. try {
  1681. Connection.Response response = JsoupUtil.requestBody(requestUrl, JsoupUtil.HTTP_GET, InitRunner.proxy, null, null);
  1682. String fundingRate = JSONObject.parseObject(response.body()).getJSONObject("data").getString("fundingRate");
  1683. if (new BigDecimal(fundingRate).compareTo(BigDecimal.ZERO) < 0) {
  1684. jsonObject.put("fundingRateStyle", " style=\"color:#FFFFFF;background-color:#F1493F;\"");
  1685. } else {
  1686. jsonObject.put("fundingRateStyle", " style=\"color:#FFFFFF;background-color:#1DA2B4;\"");
  1687. }
  1688. jsonObject.put("fundingRate", new BigDecimal(fundingRate).multiply(BigDecimal.valueOf(100)).setScale(4, RoundingMode.HALF_UP).toPlainString() + "%");
  1689. } catch (Exception ex) {
  1690. throw new RuntimeException(ex);
  1691. }
  1692. // 标记价格
  1693. jsonObject.put("marketPriceStyle", " style=\"color:#252B31;background-color:#C4ADE9;font-weight:bold;\"");
  1694. })).join();
  1695. if (unrealizedPLSort != 0) {
  1696. Collections.sort(result, (o1, o2) -> unrealizedPLSort * (((JSONObject) o1).getBigDecimal("unrealizedPL").compareTo(((JSONObject) o2).getBigDecimal("unrealizedPL"))));
  1697. }
  1698. }
  1699. /**
  1700. * Java实现字节转换,可以自动转换为B、KB、MB、GB、TB
  1701. *
  1702. * @param size
  1703. * @return
  1704. */
  1705. private String readableFileSize(double size) {
  1706. if (size <= 0) {
  1707. return "0";
  1708. }
  1709. final String[] units = new String[]{"B", "K", "M", "G", "T"};
  1710. int digitGroups = (int) (Math.log10(size) / Math.log10(1000));
  1711. return df1.format(size / Math.pow(1000, digitGroups)) + units[digitGroups];
  1712. }
  1713. public String getMonitorJobStatus(String jobName) {
  1714. Map<String, JSONObject> monitorJobConfig = getMonitorJobConfig();
  1715. if (null != monitorJobConfig && monitorJobConfig.containsKey(jobName)) {
  1716. return monitorJobConfig.get(jobName).getString("job_status");
  1717. }
  1718. return null;
  1719. }
  1720. @Override
  1721. public Map<String, JSONObject> getMonitorJobConfig() {
  1722. String cacheKey = "coin:monitor:job:list";
  1723. // 1. 缓存有,直接返回
  1724. if (redisUtils.hasKey(cacheKey) && redisUtils.get(cacheKey) != null) {
  1725. return (Map<String, JSONObject>) redisUtils.get(cacheKey);
  1726. }
  1727. // 加锁防止同时对一个数据发送多次请求
  1728. RLock lock = redissonClient.getLock("lock:" + cacheKey);
  1729. try {
  1730. // 2. 尝试加分布式锁,最多等待30秒,上锁以后60秒自动解锁
  1731. boolean lockFlag = lock.tryLock(30, 60, TimeUnit.SECONDS);
  1732. if (lockFlag) {
  1733. // 3. 加锁成功,二次检查,缓存有,直接返回
  1734. if (redisUtils.hasKey(cacheKey) && redisUtils.get(cacheKey) != null) {
  1735. return (Map<String, JSONObject>) redisUtils.get(cacheKey);
  1736. }
  1737. // 4. 查数据库,并且按给定的时长加到缓存中
  1738. Map<String, JSONObject> monitorJobConfigMap = coinMapper.findMonitorJobConfig();
  1739. // 缓存监控任务配置信息
  1740. if (monitorJobConfigMap != null) {
  1741. redisUtils.set(cacheKey, monitorJobConfigMap, 60, TimeUnit.MINUTES);
  1742. }
  1743. return monitorJobConfigMap;
  1744. } else {
  1745. log.error("getMonitorJobConfig 加锁失败 error,lockFlag: false");
  1746. return null;
  1747. }
  1748. } catch (Exception e) {
  1749. log.error("getMonitorJobConfig Exception", e);
  1750. return InitRunner.monitorJobConfigMap;
  1751. } finally {
  1752. if (lock.isLocked() && lock.isHeldByCurrentThread()) {
  1753. lock.unlock();
  1754. }
  1755. }
  1756. }
  1757. @Override
  1758. public Object mainSearchDetail(String nameEn, String id) {
  1759. if ("image".equals(nameEn)) {
  1760. FileImage fileImage = coinMapper.findFileImageById(Long.valueOf(id));
  1761. String ftpBaseurl = InitRunner.dicCodeMap.get("ftp_baseurl").getCodeValue();
  1762. fileImage.setPath(ftpBaseurl + fileImage.getPath());
  1763. return fileImage;
  1764. } else if ("watchlist".equals(nameEn)) {
  1765. CoinWatchlist coinWatchlist = coinMapper.findWatchlistBySymbol(id);
  1766. coinWatchlist.setRemark(MarkdownToHtmlUtils.markdownToHtmlExtensions(coinWatchlist.getRemark()));
  1767. coinWatchlist.setFeixiaohaoUrl("https://www.feixiaohao.com/currencies/" + coinWatchlist.getFeixiaohaoUrl());
  1768. coinWatchlist.setCmcUrl("https://coinmarketcap.com/zh/currencies/" + coinWatchlist.getCmcUrl());
  1769. coinWatchlist.setCoingeckoUrl("https://www.coingecko.com/zh/%E6%95%B0%E5%AD%97%E8%B4%A7%E5%B8%81/" + coinWatchlist.getCoingeckoUrl());
  1770. return coinWatchlist;
  1771. }
  1772. return null;
  1773. }
  1774. @Override
  1775. public void debugTest() {
  1776. Map<String, Object> params = new HashMap<>();
  1777. params.put("sortField", Collections.singletonList("create_time"));
  1778. params.put("sort", "desc");
  1779. ArrayList symbolList = new ArrayList();
  1780. symbolList.add("PYTH");
  1781. symbolList.add("SNX");
  1782. symbolList.add("REEF");
  1783. List<CoinWatchlist> watchlistList = coinMapper.findWatchlistList(params);
  1784. watchlistList = watchlistList.stream().filter(x -> symbolList.contains(x.getSymbol())).collect(Collectors.toList());
  1785. //Map<String, CoinWatchlist> coinWatchlistMap4CoingeckoId = watchlistList.stream().collect(Collectors.toMap(CoinWatchlist::getCoingeckoId, coinWatchlist -> coinWatchlist));
  1786. //parseWatchlistMap4Coingecko(coinWatchlistMap4CoingeckoId);
  1787. Map<Long, CoinWatchlist> coinWatchlistMap4CmcId = watchlistList.stream().collect(Collectors.toMap(CoinWatchlist::getCmcId, coinWatchlist -> coinWatchlist));
  1788. parseWatchlistMap4CmC(coinWatchlistMap4CmcId);
  1789. }
  1790. @Override
  1791. @Async("coinTaskExecutor")
  1792. public void initWatchlist(CoinWatchlist coinWatchlist2) {
  1793. Map<String, Object> params = new HashMap<>();
  1794. params.put("symbol", coinWatchlist2.getSymbol());
  1795. List<CoinWatchlist> watchlistListCKO = coinMapper.findWatchlistList(params);
  1796. Map<String, CoinWatchlist> coinWatchlistMap4CoingeckoId = watchlistListCKO.stream().collect(Collectors.toMap(CoinWatchlist::getCoingeckoId, coinWatchlist -> coinWatchlist));
  1797. parseWatchlistMap4Coingecko(coinWatchlistMap4CoingeckoId);
  1798. List<CoinWatchlist> watchlistListCMC = coinMapper.findWatchlistList(params);
  1799. Map<Long, CoinWatchlist> coinWatchlistMap4CmcId = watchlistListCMC.stream().collect(Collectors.toMap(CoinWatchlist::getCmcId, coinWatchlist -> coinWatchlist));
  1800. parseWatchlistMap4CmC(coinWatchlistMap4CmcId);
  1801. }
  1802. }