trading-order-admin/src/main/java/com/yami/trading/api/controller/KlineController.java
@@ -181,30 +181,27 @@ map.put("low", low.setScale(decimal, RoundingMode.HALF_UP)); map.put("volume", kline.getVolume()); if (i == data.size() - 1) { BigDecimal periodHigh = high; BigDecimal periodLow = low; BigDecimal closePrice = close; Realtime realtime = DataCache.getLatestRealTime(symbol); if (realtime != null && realtime.getClose() != null && realtime.getClose().compareTo(BigDecimal.ZERO) > 0) { closePrice = realtime.getClose(); map.put("close", closePrice.setScale(decimal, RoundingMode.HALF_UP)); //if (line.equalsIgnoreCase(Kline.PERIOD_15MIN) || line.equalsIgnoreCase(Kline.PERIOD_30MIN) || line.equalsIgnoreCase(Kline.PERIOD_60MIN)) { if (i == data.size() - 1) { Realtime realtime = DataCache.getLatestRealTime(symbol); if (realtime != null && realtime.getClose() != null && realtime.getClose().compareTo(BigDecimal.ZERO) > 0) { map.put("close", realtime.getClose().setScale(decimal, RoundingMode.HALF_UP)); } BeforeClose beforeClose = dataDBService.getBeforeClose(kline.getSymbol(), line, ts, realtime); if (beforeClose != null) { map.put("high", beforeClose.getMaxClose().setScale(decimal, RoundingMode.HALF_UP)); map.put("low", beforeClose.getMinClose().setScale(decimal, RoundingMode.HALF_UP)); } BigDecimal openVal = (BigDecimal) map.get("open"); BigDecimal closeVal = (BigDecimal) map.get("close"); BigDecimal highVal = (BigDecimal) map.get("high"); BigDecimal lowVal = (BigDecimal) map.get("low"); if (openVal != null && closeVal != null) { map.put("high", highVal.max(openVal).max(closeVal)); map.put("low", lowVal.min(openVal).min(closeVal)); } } BeforeClose beforeClose = dataDBService.getBeforeClose(kline.getSymbol(), line, ts, realtime); if (beforeClose != null && beforeClose.getMaxClose() != null && beforeClose.getMaxClose().compareTo(BigDecimal.ZERO) > 0 && beforeClose.getMinClose() != null && beforeClose.getMinClose().compareTo(BigDecimal.ZERO) > 0) { periodHigh = periodHigh.max(beforeClose.getMaxClose()); periodLow = periodLow.min(beforeClose.getMinClose()); } if (closePrice != null && closePrice.compareTo(BigDecimal.ZERO) > 0) { periodHigh = periodHigh.max(closePrice); periodLow = periodLow.min(closePrice); } map.put("high", periodHigh.setScale(decimal, RoundingMode.HALF_UP)); map.put("low", periodLow.setScale(decimal, RoundingMode.HALF_UP)); } //} list.add(map); } return list; trading-order-bean/src/main/java/com/yami/trading/bean/item/domain/Item.java
@@ -178,6 +178,13 @@ } /** * 是否为平台自定义币(机器人行情) */ public boolean isFake() { return "1".equalsIgnoreCase(fake); } /** * 板块 */ private String board; trading-order-huobi/src/main/java/com.yami.trading.huobi/data/internal/DataDBServiceImpl.java
@@ -119,10 +119,10 @@ LocalDate yesterday = LocalDate.now(ZoneId.of("America/New_York")).minusDays(1); // 4. 判断是否为昨天 boolean isYesterday = tsDate.equals(yesterday); if (isYesterday) { return realtime; } boolean isYesterday = tsDate.equals(yesterday); if (isYesterday) { return realtime; } } // 没缓存重新保存 // 计算当天0点的时间戳(毫秒级) @@ -144,63 +144,65 @@ public BeforeClose getBeforeClose(String symbol, String line, Long ts, Realtime realtime) { BeforeClose beforeClose = (BeforeClose) redisTemplate.opsForValue().get(RedisKeys.REAL_TIME_BEFORE_CLOSE + symbol + line); //超出时间重新计算 if (beforeClose == null || ts > beforeClose.getTs()) { long queryTs = normalizeToMillis(ts); if (beforeClose == null || queryTs > beforeClose.getTs()) { long currentTimeStamp = System.currentTimeMillis(); RequestDataHelper.set("symbol", symbol); QueryWrapper<Realtime> queryWrapper = new QueryWrapper<Realtime>() .eq("symbol", symbol) .ge("ts", ts) .ge("ts", queryTs) .le("ts", currentTimeStamp) .select("MAX(close) as maxClose", "MIN(close) as minClose"); .select("MAX(CAST(close AS DECIMAL(20,8))) as maxClose", "MIN(CAST(close AS DECIMAL(20,8))) as minClose"); Map<String, Object> resultMap = realtimeService.getMap(queryWrapper); RequestDataHelper.clear(); beforeClose = new BeforeClose(); BigDecimal maxClose = extractAggregateValue(resultMap, "maxClose"); BigDecimal minClose = extractAggregateValue(resultMap, "minClose"); mergeRealtimeIntoBeforeClose(beforeClose, maxClose, minClose, realtime); beforeClose.setTs(ts); beforeClose.setTs(queryTs); if (resultMap != null && !resultMap.isEmpty()) { beforeClose.setMaxClose(convertToBigDecimal(resultMap.get("maxClose"))); beforeClose.setMinClose(convertToBigDecimal(resultMap.get("minClose"))); } redisTemplate.opsForValue().set(RedisKeys.REAL_TIME_BEFORE_CLOSE + symbol + line, beforeClose); } else if (realtime != null) { mergeRealtimeIntoBeforeClose(beforeClose, beforeClose.getMaxClose(), beforeClose.getMinClose(), realtime); redisTemplate.opsForValue().set(RedisKeys.REAL_TIME_BEFORE_CLOSE + symbol + line, beforeClose); } mergeRealtimeIntoBeforeClose(beforeClose, realtime, symbol, line); if (!isValidBeforeClose(beforeClose)) { return null; } return beforeClose; } private void mergeRealtimeIntoBeforeClose(BeforeClose beforeClose, BigDecimal maxClose, BigDecimal minClose, Realtime realtime) { BigDecimal max = maxClose; BigDecimal min = minClose; if (realtime != null && realtime.getClose() != null) { BigDecimal latest = realtime.getClose(); if (max == null || max.compareTo(BigDecimal.ZERO) <= 0) { max = latest; } else { max = max.max(latest); private void mergeRealtimeIntoBeforeClose(BeforeClose beforeClose, Realtime realtime, String symbol, String line) { if (realtime == null || realtime.getClose() == null || realtime.getClose().compareTo(BigDecimal.ZERO) <= 0) { return; } BigDecimal close = realtime.getClose(); if (!isValidBeforeClose(beforeClose)) { beforeClose.setMaxClose(close); beforeClose.setMinClose(close); } else { if (close.compareTo(beforeClose.getMaxClose()) > 0) { beforeClose.setMaxClose(close); } if (min == null || min.compareTo(BigDecimal.ZERO) <= 0) { min = latest; } else { min = min.min(latest); if (beforeClose.getMinClose().compareTo(BigDecimal.ZERO) <= 0 || close.compareTo(beforeClose.getMinClose()) < 0) { beforeClose.setMinClose(close); } } beforeClose.setMaxClose(max == null ? BigDecimal.ZERO : max); beforeClose.setMinClose(min == null ? BigDecimal.ZERO : min); redisTemplate.opsForValue().set(RedisKeys.REAL_TIME_BEFORE_CLOSE + symbol + line, beforeClose); } private BigDecimal extractAggregateValue(Map<String, Object> resultMap, String key) { if (resultMap == null || resultMap.isEmpty()) { return null; private boolean isValidBeforeClose(BeforeClose beforeClose) { return beforeClose != null && beforeClose.getMaxClose() != null && beforeClose.getMaxClose().compareTo(BigDecimal.ZERO) > 0 && beforeClose.getMinClose() != null && beforeClose.getMinClose().compareTo(BigDecimal.ZERO) > 0; } private long normalizeToMillis(Long ts) { if (ts == null) { return System.currentTimeMillis(); } if (resultMap.containsKey(key)) { return convertToBigDecimal(resultMap.get(key)); } for (Map.Entry<String, Object> entry : resultMap.entrySet()) { if (entry.getKey() != null && entry.getKey().equalsIgnoreCase(key)) { return convertToBigDecimal(entry.getValue()); } } return null; return String.valueOf(ts).length() <= 10 ? ts * 1000 : ts; } // 辅助方法:统一转换为BigDecimal,避免类型错误 trading-order-huobi/src/main/java/com.yami.trading.huobi/data/internal/KlineInitServiceImpl.java
@@ -35,32 +35,27 @@ public void klineInit(String symbols) { logger.info("当前开始初始化币对k线图: {}", symbols); if (!symbols.contains(",")) { Item bySymbol = itemService.findBySymbol(symbols); if (Item.cryptos.equalsIgnoreCase(bySymbol.getType())) { cryptosKlineService.init(symbols); } else if ("1".equalsIgnoreCase(bySymbol.getFake())) { fakeKlineInitService.saveInit(symbols); } else { saveInit(symbols); } initOne(symbols); } else { String[] symbolsArrays = symbols.split(","); for (String symbol : symbolsArrays) { threadPoolComponent.getExecutor().submit(() -> { logger.info("当前开始初始化币对单个k线图: {}", symbol); Item bySymbol = itemService.findBySymbol(symbol); if (Item.cryptos.equalsIgnoreCase(bySymbol.getType())) { cryptosKlineService.init(symbol); } else if ("1".equalsIgnoreCase(bySymbol.getFake())) { fakeKlineInitService.saveInit(symbols); } else { saveInit(symbol); } initOne(symbol); logger.info("当前开始初始化完成币对k线图: {}", symbol); }); } } } private void initOne(String symbol) { Item bySymbol = itemService.findBySymbol(symbol); if (bySymbol.isFake()) { fakeKlineInitService.saveInit(symbol); } else if (Item.cryptos.equalsIgnoreCase(bySymbol.getType())) { cryptosKlineService.init(symbol); } else { saveInit(symbol); } } @@ -68,7 +63,7 @@ private final void saveInit(String symbol) { Item bySymbol = itemService.findBySymbol(symbol); // 机器人的价格,无需初始化 if ("1".equalsIgnoreCase(bySymbol.getFake())) { if (bySymbol.isFake()) { return; } Map<String, List<Kline>> dailyWeekMonthHistoryMap = hobiDataService.getDailyWeekMonthHistory(symbol); trading-order-huobi/src/main/java/com.yami.trading.huobi/data/internal/KlineServiceImpl.java
@@ -207,12 +207,11 @@ } // 1分钟K线最新的5条数据,上个层级最近的几条数据 List<Kline> klineOneTop5 = new ArrayList<>(klineOne.subList(klineOne.size() - nums, klineOne.size())); Kline realtimeKline = klineOneTop5.get(klineOneTop5.size() - 1); Kline realtimeKline = klineOneTop5.get(nums - 1); if (realtimeKline == null) { return null; } long periodTs = klineOneTop5.get(0).getTs(); if (latestSameLineKline != null && latestSameLineKline.getTs() >= periodTs) { if (latestSameLineKline != null && latestSameLineKline.getTs() >= realtimeKline.getTs()) { return null; } if (latestSameLineKline != null) { @@ -222,8 +221,6 @@ if (klineOneTop5.isEmpty()) { return null; } periodTs = klineOneTop5.get(0).getTs(); realtimeKline = klineOneTop5.get(klineOneTop5.size() - 1); Double high = null; Double low = null; @@ -238,7 +235,7 @@ Kline kline = new Kline(); kline.setSymbol(symbol); kline.setTs(periodTs); kline.setTs(realtimeKline.getTs()); if (latestSameLineKline != null) { kline.setOpen(latestSameLineKline.getClose()); } else { @@ -681,8 +678,13 @@ if (latestKilne != null && latestKilne.getTs() >= realtime.getTs()) { return null; } long lastKlineTs = latestKilne.getTs(); realTimeList = realTimeList.stream().filter(r -> r.getTs() > lastKlineTs).collect(Collectors.toList()); if (latestKilne != null) { long lastKlineTs = latestKilne.getTs(); realTimeList = realTimeList.stream().filter(r -> r.getTs() > lastKlineTs).collect(Collectors.toList()); } if (realTimeList.isEmpty()) { return null; } Double high = null; Double low = null; for (Realtime realTime : realTimeList) { trading-order-huobi/src/main/java/com.yami.trading.huobi/data/internal/RemoteDataServiceImpl.java
@@ -78,7 +78,7 @@ @Override public List<Kline> kline(String symbol, String line) { Item bySymbol = itemService.findBySymbol(symbol); if(Item.cryptos.equals(bySymbol.getType())){ if (Item.cryptos.equals(bySymbol.getType()) && !bySymbol.isFake()) { return klineCryptos(symbol, line); } KlineTimeObject timeObject = DataCache.getKline(symbol, line); trading-order-huobi/src/main/java/com.yami.trading.huobi/data/klinejob/Kline15MinuteJob.java
@@ -36,7 +36,7 @@ for (int i = 0; i < item_list.size(); i++) { Item item = item_list.get(i); if (MarketOpenChecker.isMarketOpenByItemCloseType(item.getOpenCloseType()) && item.getType().equals("cryptos")) { if (Item.cryptos.equalsIgnoreCase(item.getType())) { if (Item.cryptos.equalsIgnoreCase(item.getType()) && !item.isFake()) { cryptosKlineService.saveOne(item.getSymbol(), Kline.PERIOD_15MIN); } else { klineService.saveKline15Minute(item.getSymbol(), Kline.PERIOD_15MIN); trading-order-huobi/src/main/java/com.yami.trading.huobi/data/klinejob/Kline1DayJob.java
@@ -37,7 +37,7 @@ for (int i = 0; i < item_list.size(); i++) { Item item = item_list.get(i); if (MarketOpenChecker.isMarketOpenByItemCloseType(item.getOpenCloseType()) && item.getType().equals("cryptos")) { if (Item.cryptos.equalsIgnoreCase(item.getType())) { if (Item.cryptos.equalsIgnoreCase(item.getType()) && !item.isFake()) { cryptosKlineService.saveOne(item.getSymbol(), Kline.PERIOD_1DAY); } else { klineService.saveKline1Day(item.getSymbol(), Kline.PERIOD_1DAY); trading-order-huobi/src/main/java/com.yami.trading.huobi/data/klinejob/Kline1MinuteJob.java
@@ -38,7 +38,7 @@ Item item = item_list.get(i); if (MarketOpenChecker.isMarketOpenByItemCloseType(item.getOpenCloseType()) && item.getType().equals("cryptos")) { //log.debug("--------------- 1min kline --------- {}", item.getSymbol()); if (Item.cryptos.equalsIgnoreCase(item.getType())) { if (Item.cryptos.equalsIgnoreCase(item.getType()) && !item.isFake()) { cryptosKlineService.saveOne(item.getSymbol(), Kline.PERIOD_1MIN); } else { klineService.saveKline1Minute(item.getSymbol(), Kline.PERIOD_1MIN); trading-order-huobi/src/main/java/com.yami.trading.huobi/data/klinejob/Kline1MonJob.java
@@ -37,7 +37,7 @@ for (int i = 0; i < item_list.size(); i++) { Item item = item_list.get(i); if (MarketOpenChecker.isMarketOpenByItemCloseType(item.getOpenCloseType()) && item.getType().equals("cryptos")) { if (Item.cryptos.equalsIgnoreCase(item.getType())) { if (Item.cryptos.equalsIgnoreCase(item.getType()) && !item.isFake()) { cryptosKlineService.saveOne(item.getSymbol(), Kline.PERIOD_1MON); } else { klineService.saveKline1Mon(item.getSymbol(), Kline.PERIOD_1MON); trading-order-huobi/src/main/java/com.yami.trading.huobi/data/klinejob/Kline1QuarterJob.java
@@ -37,11 +37,10 @@ for (int i = 0; i < item_list.size(); i++) { Item item = item_list.get(i); if (MarketOpenChecker.isMarketOpenByItemCloseType(item.getOpenCloseType()) && item.getType().equals("cryptos")) { if (Item.cryptos.equalsIgnoreCase(item.getType())) { if (Item.cryptos.equalsIgnoreCase(item.getType()) && !item.isFake()) { continue; } else { klineService.saveKlineQuarter(item.getSymbol(), Kline.PERIOD_QUARTER); } klineService.saveKlineQuarter(item.getSymbol(), Kline.PERIOD_QUARTER); } stopWatch.stop(); trading-order-huobi/src/main/java/com.yami.trading.huobi/data/klinejob/Kline1WeekJob.java
@@ -37,7 +37,7 @@ for (int i = 0; i < item_list.size(); i++) { Item item = item_list.get(i); if (MarketOpenChecker.isMarketOpenByItemCloseType(item.getOpenCloseType()) && item.getType().equals("cryptos")) { if (Item.cryptos.equalsIgnoreCase(item.getType())) { if (Item.cryptos.equalsIgnoreCase(item.getType()) && !item.isFake()) { cryptosKlineService.saveOne(item.getSymbol(), Kline.PERIOD_1WEEK); } else { klineService.saveKline1Week(item.getSymbol(), Kline.PERIOD_1WEEK); trading-order-huobi/src/main/java/com.yami.trading.huobi/data/klinejob/Kline1YearJob.java
@@ -37,11 +37,10 @@ for (int i = 0; i < item_list.size(); i++) { Item item = item_list.get(i); if (MarketOpenChecker.isMarketOpenByItemCloseType(item.getOpenCloseType()) && item.getType().equals("cryptos")) { if (Item.cryptos.equalsIgnoreCase(item.getType())) { continue; } else { klineService.saveKline1Mon(item.getSymbol(), Kline.PERIOD_YEAR); if (Item.cryptos.equalsIgnoreCase(item.getType()) && !item.isFake()) { continue; } klineService.saveKlineYear(item.getSymbol(), Kline.PERIOD_YEAR); } stopWatch.stop(); trading-order-huobi/src/main/java/com.yami.trading.huobi/data/klinejob/Kline2HourJob.java
@@ -37,11 +37,11 @@ for (int i = 0; i < item_list.size(); i++) { Item item = item_list.get(i); if (MarketOpenChecker.isMarketOpenByItemCloseType(item.getOpenCloseType()) && item.getType().equals("cryptos")) { if (Item.cryptos.equalsIgnoreCase(item.getType()) || Item.forex.equalsIgnoreCase(item.getType())) { if (Item.forex.equalsIgnoreCase(item.getType()) || (Item.cryptos.equalsIgnoreCase(item.getType()) && !item.isFake())) { continue; } else { klineService.saveKline2Hour(item.getSymbol(), Kline.PERIOD_2HOUR); } klineService.saveKline2Hour(item.getSymbol(), Kline.PERIOD_2HOUR); } } stopWatch.stop(); trading-order-huobi/src/main/java/com.yami.trading.huobi/data/klinejob/Kline30MinuteJob.java
@@ -37,7 +37,7 @@ for (int i = 0; i < item_list.size(); i++) { Item item = item_list.get(i); if (MarketOpenChecker.isMarketOpenByItemCloseType(item.getOpenCloseType()) && item.getType().equals("cryptos")) { if (Item.cryptos.equalsIgnoreCase(item.getType())) { if (Item.cryptos.equalsIgnoreCase(item.getType()) && !item.isFake()) { cryptosKlineService.saveOne(item.getSymbol(), Kline.PERIOD_30MIN); } else { klineService.saveKline30Minute(item.getSymbol(), Kline.PERIOD_30MIN); trading-order-huobi/src/main/java/com.yami.trading.huobi/data/klinejob/Kline4HourJob.java
@@ -37,7 +37,7 @@ for (int i = 0; i < item_list.size(); i++) { Item item = item_list.get(i); if (MarketOpenChecker.isMarketOpenByItemCloseType(item.getOpenCloseType()) && item.getType().equals("cryptos")) { if (Item.cryptos.equalsIgnoreCase(item.getType())) { if (Item.cryptos.equalsIgnoreCase(item.getType()) && !item.isFake()) { cryptosKlineService.saveOne(item.getSymbol(), Kline.PERIOD_4HOUR); } else { klineService.saveKline4Hour(item.getSymbol(), Kline.PERIOD_4HOUR); trading-order-huobi/src/main/java/com.yami.trading.huobi/data/klinejob/Kline5DayJob.java
@@ -37,7 +37,7 @@ for (int i = 0; i < item_list.size(); i++) { Item item = item_list.get(i); if (MarketOpenChecker.isMarketOpenByItemCloseType(item.getOpenCloseType()) && item.getType().equals("cryptos")) { if (!Item.cryptos.equalsIgnoreCase(item.getType())) { if (!Item.cryptos.equalsIgnoreCase(item.getType()) || item.isFake()) { klineService.saveKline5Day(item.getSymbol(), Kline.PERIOD_5DAY); } } trading-order-huobi/src/main/java/com.yami.trading.huobi/data/klinejob/Kline5MinuteJob.java
@@ -37,7 +37,7 @@ for (int i = 0; i < item_list.size(); i++) { Item item = item_list.get(i); if (MarketOpenChecker.isMarketOpenByItemCloseType(item.getOpenCloseType()) && item.getType().equals("cryptos")) { if (Item.cryptos.equalsIgnoreCase(item.getType())) { if (Item.cryptos.equalsIgnoreCase(item.getType()) && !item.isFake()) { cryptosKlineService.saveOne(item.getSymbol(), Kline.PERIOD_5MIN); } else { klineService.saveKline5Minute(item.getSymbol(), Kline.PERIOD_5MIN); trading-order-huobi/src/main/java/com.yami.trading.huobi/data/klinejob/Kline60MinuteJob.java
@@ -37,7 +37,7 @@ for (int i = 0; i < item_list.size(); i++) { Item item = item_list.get(i); if (MarketOpenChecker.isMarketOpenByItemCloseType(item.getOpenCloseType()) && item.getType().equals("cryptos")) { if (Item.cryptos.equalsIgnoreCase(item.getType())) { if (Item.cryptos.equalsIgnoreCase(item.getType()) && !item.isFake()) { cryptosKlineService.saveOne(item.getSymbol(), Kline.PERIOD_60MIN); } else { klineService.saveKline60Minute(item.getSymbol(), Kline.PERIOD_60MIN);