CoinServiceImpl.java 127 KB

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