trading-order-admin/src/main/java/com/yami/trading/api/controller/KlineController.java
@@ -129,24 +129,24 @@ private List<Map<String, Object>> build(List<Kline> data, String line, String symbol) { Collections.sort(data); // 相同时间戳保留最新一条(进行中K线会覆盖缓存中的旧数据) Map<Long, Kline> latestByTs = new LinkedHashMap<>(); for (Kline kline : data) { latestByTs.put(kline.getTs(), kline); } data = new ArrayList<>(latestByTs.values()); Collections.sort(data); int len = data.size(); for (int i = 1; i < len; i++) { data.get(i).setOpen(data.get(i - 1).getClose()); } Set<Long> tsSet = new HashSet<Long>(); List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); Item bySymbol = itemService.findBySymbol(symbol); for (int i = 0; i < data.size(); i++) { Kline kline = data.get(i); Long ts = kline.getTs(); if (tsSet.contains(ts)) { continue; } else { tsSet.add(ts); } String fake = bySymbol.getFake(); // if("1".equalsIgnoreCase(fake)){ // klineService.smoothlyKline(kline, 0.99); @@ -181,20 +181,32 @@ map.put("low", low.setScale(decimal, RoundingMode.HALF_UP)); map.put("volume", kline.getVolume()); //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) { map.put("close", realtime.getClose().setScale(decimal, RoundingMode.HALF_UP)); if (i == data.size() - 1) { Realtime realtime = DataCache.getLatestRealTime(symbol); if (realtime != null && realtime.getClose() != null) { close = realtime.getClose(); map.put("close", close.setScale(decimal, RoundingMode.HALF_UP)); if (close.compareTo(high) > 0) { high = close; } 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)); if (close.compareTo(low) < 0) { low = close; } } //} BeforeClose beforeClose = dataDBService.getBeforeClose(kline.getSymbol(), line, ts, realtime); if (beforeClose != null) { if (beforeClose.getMaxClose() != null && beforeClose.getMaxClose().compareTo(BigDecimal.ZERO) > 0) { high = beforeClose.getMaxClose(); } if (beforeClose.getMinClose() != null && beforeClose.getMinClose().compareTo(BigDecimal.ZERO) > 0) { low = beforeClose.getMinClose(); } } map.put("high", high.setScale(decimal, RoundingMode.HALF_UP)); map.put("low", low.setScale(decimal, RoundingMode.HALF_UP)); } list.add(map); } return list; trading-order-huobi/src/main/java/com.yami.trading.huobi/data/internal/DataDBServiceImpl.java
@@ -146,43 +146,96 @@ BeforeClose beforeClose = (BeforeClose) redisTemplate.opsForValue().get(RedisKeys.REAL_TIME_BEFORE_CLOSE + symbol + line); //超出时间重新计算 if (beforeClose == null || ts > beforeClose.getTs()) { // 直接获取当前时间的毫秒级时间戳(系统默认时区,但值是全球统一的) long currentTimeStamp = System.currentTimeMillis(); // 如果需要严格基于东京时区的当前时间戳(结果和上面一致,因为时间戳是UTC绝对时间) //long currentTokyoTimeStamp = Instant.now().atZone(ZoneId.of("America/New_York")).toInstant().toEpochMilli(); RequestDataHelper.set("symbol", symbol); QueryWrapper<Realtime> queryWrapper = new QueryWrapper<Realtime>() .eq("symbol", symbol) // 直接写数据库字段名(需和表字段一致) .ge("ts", ts) .le("ts", currentTimeStamp) .select("MAX(CAST(close AS DECIMAL(10,4))) as maxClose", "MIN(CAST(close AS DECIMAL(10,4))) as minClose"); // 4. 执行聚合查询,用selectMap接收结果(键值对:maxClose/minClose -> 对应值) Map<String, Object> resultMap = realtimeService.getMap(queryWrapper); RequestDataHelper.clear(); beforeClose = new BeforeClose(); if (resultMap == null || resultMap.isEmpty()) { return beforeClose; beforeClose = calcBeforeClose(symbol, line, ts); if (beforeClose.getMaxClose().compareTo(BigDecimal.ZERO) > 0) { redisTemplate.opsForValue().set(RedisKeys.REAL_TIME_BEFORE_CLOSE + symbol + line, beforeClose); } beforeClose.setMaxClose(convertToBigDecimal(resultMap.get("maxClose"))); beforeClose.setMinClose(convertToBigDecimal(resultMap.get("minClose"))); beforeClose.setTs(ts); } if (realtime != null && realtime.getClose() != null) { if (beforeClose.getMaxClose() == null || beforeClose.getMaxClose().compareTo(BigDecimal.ZERO) <= 0) { beforeClose.setMaxClose(realtime.getClose()); } else if (realtime.getClose().compareTo(beforeClose.getMaxClose()) > 0) { beforeClose.setMaxClose(realtime.getClose()); } if (beforeClose.getMinClose() == null || beforeClose.getMinClose().compareTo(BigDecimal.ZERO) <= 0) { beforeClose.setMinClose(realtime.getClose()); } else if (realtime.getClose().compareTo(beforeClose.getMinClose()) < 0) { beforeClose.setMinClose(realtime.getClose()); } redisTemplate.opsForValue().set(RedisKeys.REAL_TIME_BEFORE_CLOSE + symbol + line, beforeClose); } return beforeClose; } if (realtime != null) { if (realtime.getClose().compareTo(beforeClose.getMaxClose()) > 0) { beforeClose.setMaxClose(realtime.getClose()); redisTemplate.opsForValue().set(RedisKeys.REAL_TIME_BEFORE_CLOSE + symbol + line, beforeClose); private BeforeClose calcBeforeClose(String symbol, String line, Long ts) { BeforeClose beforeClose = new BeforeClose(); beforeClose.setTs(ts); int interval = sysparaService.find("data_interval").getInteger() / 1000; if (interval <= 0) { interval = 1; } HighLow highLow = null; switch (line) { case Kline.PERIOD_1MIN: highLow = HighLowHandle.get(symbol, 60 / interval, interval); break; case Kline.PERIOD_5MIN: highLow = HighLowHandle.get(symbol, (60 * 5) / interval, interval); break; case Kline.PERIOD_15MIN: highLow = HighLowHandle.get(symbol, (60 * 15) / interval, interval); break; case Kline.PERIOD_30MIN: highLow = HighLowHandle.get(symbol, (60 * 30) / interval, interval); break; case Kline.PERIOD_60MIN: highLow = HighLowHandle.get(symbol, (60 * 60) / interval, interval); break; case Kline.PERIOD_4HOUR: highLow = HighLowHandle.get(symbol, (60 * 60 * 4) / interval, interval); break; case Kline.PERIOD_1DAY: highLow = HighLowHandle.get(symbol, (60 * 60 * 24) / interval, interval); break; case Kline.PERIOD_1WEEK: highLow = HighLowHandle.getByDay(symbol, 7); break; case Kline.PERIOD_1MON: highLow = HighLowHandle.getByDay(symbol, 30); break; default: break; } if (highLow != null && highLow.getHigh() != null) { beforeClose.setMaxClose(highLow.getHigh()); } if (highLow != null && highLow.getLow() != null) { beforeClose.setMinClose(highLow.getLow()); } if (beforeClose.getMaxClose().compareTo(BigDecimal.ZERO) > 0) { return beforeClose; } long currentTimeStamp = System.currentTimeMillis(); RequestDataHelper.set("symbol", symbol); QueryWrapper<Realtime> queryWrapper = new QueryWrapper<Realtime>() .eq("symbol", symbol) .ge("ts", ts) .le("ts", currentTimeStamp) .select("MAX(CAST(close AS DECIMAL(10,4))) as maxClose", "MIN(CAST(close AS DECIMAL(10,4))) as minClose"); Map<String, Object> resultMap = realtimeService.getMap(queryWrapper); RequestDataHelper.clear(); if (resultMap != null && !resultMap.isEmpty()) { BigDecimal maxClose = convertToBigDecimal(resultMap.get("maxClose")); BigDecimal minClose = convertToBigDecimal(resultMap.get("minClose")); if (maxClose.compareTo(BigDecimal.ZERO) > 0) { beforeClose.setMaxClose(maxClose); } if (realtime.getClose().compareTo(beforeClose.getMinClose()) < 0) { beforeClose.setMinClose(realtime.getClose()); redisTemplate.opsForValue().set(RedisKeys.REAL_TIME_BEFORE_CLOSE + symbol + line, beforeClose); if (minClose.compareTo(BigDecimal.ZERO) > 0) { beforeClose.setMinClose(minClose); } } System.out.println("realtime.getClose():" + realtime.getClose() + "==" + beforeClose); return beforeClose; } trading-order-huobi/src/main/java/com.yami.trading.huobi/data/internal/KlineServiceImpl.java
@@ -29,6 +29,9 @@ import java.math.RoundingMode; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.time.Instant; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.*; import java.util.stream.Collectors; @@ -180,6 +183,165 @@ public Kline buildKline(String symbol, String line, String smallLevelLine, int nums) { if (Kline.PERIOD_5DAY.equals(line)) { return buildKlineLegacy(symbol, line, smallLevelLine, nums); } return buildInProgressKline(symbol, line, smallLevelLine); } /** * 按当前周期起点拼接进行中的K线(ts 对齐到周期开盘时间,如 5 分钟线的 9:35) */ private Kline buildInProgressKline(String symbol, String line, String smallLevelLine) { try { KlineTimeObject timeObject = DataCache.getKline(symbol, line); if (timeObject == null) { return null; } List<Kline> klineList = timeObject.getKline(); Item item = itemService.findBySymbol(symbol); Kline latestSameLineKline = null; if (klineList != null && !klineList.isEmpty()) { latestSameLineKline = klineList.get(klineList.size() - 1); } else if (item.getFake().equalsIgnoreCase("0")) { return null; } Realtime latestRealtime = DataCache.getLatestRealTime(symbol); if (latestRealtime == null) { latestRealtime = DataCache.getRealtime(symbol); } if (latestRealtime == null || latestRealtime.getClose() == null) { return null; } long currentPeriodTs = alignPeriodStartTs(line, latestRealtime.getTs()); if (latestSameLineKline != null && latestSameLineKline.getTs() != null && latestSameLineKline.getTs() > currentPeriodTs) { return null; } KlineTimeObject smallObject = DataCache.getKline(symbol, smallLevelLine); List<Kline> periodBars = new ArrayList<>(); if (smallObject != null && smallObject.getKline() != null) { periodBars = smallObject.getKline().stream() .filter(k -> k.getTs() != null && k.getTs() >= currentPeriodTs) .collect(Collectors.toList()); } Kline kline = new Kline(); kline.setSymbol(symbol); kline.setPeriod(line); kline.setTs(currentPeriodTs); if (latestSameLineKline != null && latestSameLineKline.getTs() != null && latestSameLineKline.getTs() < currentPeriodTs) { kline.setOpen(latestSameLineKline.getClose()); } else if (!periodBars.isEmpty() && periodBars.get(0).getOpen() != null) { kline.setOpen(periodBars.get(0).getOpen()); } else if (latestRealtime.getOpen() != null) { kline.setOpen(latestRealtime.getOpen()); } else { kline.setOpen(latestRealtime.getClose()); } if (!periodBars.isEmpty()) { Double high = null; Double low = null; for (Kline bar : periodBars) { if (bar.getHigh() != null) { if (high == null || high <= bar.getHigh().doubleValue()) { high = bar.getHigh().doubleValue(); } } if (bar.getLow() != null) { if (low == null || low >= bar.getLow().doubleValue()) { low = bar.getLow().doubleValue(); } } } kline.setHigh(high == null ? latestRealtime.getClose() : new BigDecimal(high)); kline.setLow(low == null ? latestRealtime.getClose() : new BigDecimal(low)); kline.setClose(periodBars.get(periodBars.size() - 1).getClose()); kline.setVolume(periodBars.stream() .map(Kline::getVolume) .filter(Objects::nonNull) .reduce(BigDecimal.ZERO, BigDecimal::add)); kline.setAmount(periodBars.stream() .map(Kline::getAmount) .filter(Objects::nonNull) .reduce(BigDecimal.ZERO, BigDecimal::add)); } else { kline.setHigh(latestRealtime.getClose()); kline.setLow(latestRealtime.getClose()); kline.setClose(latestRealtime.getClose()); kline.setVolume(latestRealtime.getVolume()); kline.setAmount(latestRealtime.getAmount()); } kline.setClose(latestRealtime.getClose()); if (latestRealtime.getClose().compareTo(kline.getHigh()) > 0) { kline.setHigh(latestRealtime.getClose()); } if (latestRealtime.getClose().compareTo(kline.getLow()) < 0) { kline.setLow(latestRealtime.getClose()); } repairKline(kline); if (kline.getOpen().compareTo(BigDecimal.ZERO) == 0 || kline.getClose().compareTo(BigDecimal.ZERO) == 0) { return null; } return kline; } catch (Exception e) { logger.error("buildInProgressKline error: {}, {}", symbol, line, e); } return null; } /** * 将时间戳对齐到K线周期起点(UTC,与火币 id 字段一致) * 1week:周一 00:00 UTC;1mon:每月 1 日 00:00 UTC */ private long alignPeriodStartTs(String line, long tsMillis) { ZonedDateTime zdt = Instant.ofEpochMilli(tsMillis).atZone(ZoneOffset.UTC); switch (line) { case Kline.PERIOD_1MIN: return zdt.withSecond(0).withNano(0).toInstant().toEpochMilli(); case Kline.PERIOD_5MIN: { int minute = zdt.getMinute(); return zdt.withMinute(minute - minute % 5).withSecond(0).withNano(0).toInstant().toEpochMilli(); } case Kline.PERIOD_15MIN: { int minute = zdt.getMinute(); return zdt.withMinute(minute - minute % 15).withSecond(0).withNano(0).toInstant().toEpochMilli(); } case Kline.PERIOD_30MIN: { int minute = zdt.getMinute(); return zdt.withMinute(minute - minute % 30).withSecond(0).withNano(0).toInstant().toEpochMilli(); } case Kline.PERIOD_60MIN: return zdt.withMinute(0).withSecond(0).withNano(0).toInstant().toEpochMilli(); case Kline.PERIOD_1DAY: return zdt.withHour(0).withMinute(0).withSecond(0).withNano(0).toInstant().toEpochMilli(); case Kline.PERIOD_1WEEK: { zdt = zdt.withHour(0).withMinute(0).withSecond(0).withNano(0); int dayOfWeek = zdt.getDayOfWeek().getValue(); return zdt.minusDays(dayOfWeek - 1L).toInstant().toEpochMilli(); } case Kline.PERIOD_1MON: return zdt.withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0).toInstant().toEpochMilli(); case Kline.PERIOD_2HOUR: return zdt.withMinute(0).withSecond(0).withNano(0).toInstant().toEpochMilli(); case Kline.PERIOD_4HOUR: { int hour = zdt.getHour(); return zdt.withHour(hour - hour % 4).withMinute(0).withSecond(0).withNano(0).toInstant().toEpochMilli(); } default: return zdt.withSecond(0).withNano(0).toInstant().toEpochMilli(); } } private Kline buildKlineLegacy(String symbol, String line, String smallLevelLine, int nums) { try { // 取5分钟K线全部数据集合 KlineTimeObject timeObject = DataCache.getKline(symbol, line); @@ -211,14 +373,27 @@ if (realtimeKline == null) { return null; } if (latestSameLineKline != null && latestSameLineKline.getTs() >= realtimeKline.getTs()) { if (latestSameLineKline != null && latestSameLineKline.getTs() > realtimeKline.getTs()) { return null; } if (latestSameLineKline != null) { long latestSameLineKlineTs = latestSameLineKline.getTs(); klineOneTop5 = klineOneTop5.stream().filter(r -> r.getTs() > latestSameLineKlineTs).collect(Collectors.toList()); if (latestSameLineKlineTs == realtimeKline.getTs()) { klineOneTop5 = klineOne.stream() .filter(r -> r.getTs() >= latestSameLineKlineTs) .collect(Collectors.toList()); if (klineOneTop5.size() > nums) { klineOneTop5 = new ArrayList<>(klineOneTop5.subList(klineOneTop5.size() - nums, klineOneTop5.size())); } } else { klineOneTop5 = klineOneTop5.stream() .filter(r -> r.getTs() > latestSameLineKlineTs) .collect(Collectors.toList()); } } if (klineOneTop5.isEmpty()) { return null; } Double high = null; Double low = null; @@ -673,11 +848,16 @@ latestKilne = klineList.get(klineList.size() - 1); } Realtime realtime = realTimeList.get(realTimeList.size() - 1); if (latestKilne != null && latestKilne.getTs() >= realtime.getTs()) { long currentMinuteTs = alignPeriodStartTs(Kline.PERIOD_1MIN, realtime.getTs()); if (latestKilne != null && latestKilne.getTs() != null && latestKilne.getTs() > currentMinuteTs) { return null; } long lastKlineTs = latestKilne.getTs(); realTimeList = realTimeList.stream().filter(r -> r.getTs() > lastKlineTs).collect(Collectors.toList()); realTimeList = realTimeList.stream() .filter(r -> r.getTs() != null && r.getTs() >= currentMinuteTs) .collect(Collectors.toList()); if (realTimeList.isEmpty()) { realTimeList = Collections.singletonList(realtime); } Double high = null; Double low = null; for (Realtime realTime : realTimeList) { @@ -692,11 +872,11 @@ // 保存K线到数据库 Kline kline = new Kline(); kline.setSymbol(symbol); kline.setTs(realtime.getTs()); if (latestKilne != null) { kline.setTs(currentMinuteTs); if (latestKilne != null && latestKilne.getTs() != null && latestKilne.getTs() < currentMinuteTs) { kline.setOpen(latestKilne.getClose()); } else { kline.setOpen(realTimeList.get(0).getOpen()); kline.setOpen(realTimeList.get(0).getOpen() != null ? realTimeList.get(0).getOpen() : realtime.getClose()); } kline.setHigh(new BigDecimal(high)); kline.setLow(new BigDecimal(low)); trading-order-huobi/src/main/java/com.yami.trading.huobi/data/internal/RemoteDataServiceImpl.java
@@ -73,10 +73,6 @@ @Override public List<Kline> kline(String symbol, String line) { Item bySymbol = itemService.findBySymbol(symbol); if(Item.cryptos.equals(bySymbol.getType())){ return klineCryptos(symbol, line); } KlineTimeObject timeObject = DataCache.getKline(symbol, line); List<Kline> list = new ArrayList<Kline>(); if (timeObject != null) { @@ -85,7 +81,7 @@ List<Kline> list_clone = new ArrayList<Kline>(); try { for (int i = 0; i < list.size(); i++) { if(list.get(i) == null){ if (list.get(i) == null) { continue; } Kline kline = (Kline) list.get(i).clone(); @@ -96,71 +92,61 @@ } Realtime realtime = DataCache.getLatestRealTime(symbol); if (realtime != null) { Kline kline = null; if (KlineConstant.PERIOD_1MIN.equals(line)) { kline = klineService.bulidKline1Minute(realtime, KlineConstant.PERIOD_1MIN); } else if (KlineConstant.PERIOD_5MIN.equals(line)) { kline = klineService.bulidKline5Minute(realtime, KlineConstant.PERIOD_5MIN); } else if (KlineConstant.PERIOD_15MIN.equals(line)) { kline = klineService.bulidKline15Minute(realtime, KlineConstant.PERIOD_15MIN); } else if (KlineConstant.PERIOD_30MIN.equals(line)) { kline = klineService.bulidKline30Minute(realtime, KlineConstant.PERIOD_30MIN); } else if (KlineConstant.PERIOD_60MIN.equals(line)) { kline = klineService.bulidKline60Minute(realtime, KlineConstant.PERIOD_60MIN); } else if (KlineConstant.PERIOD_4HOUR.equals(line)) { kline = klineService.bulidKline4Hour(realtime, KlineConstant.PERIOD_4HOUR); } else if (KlineConstant.PERIOD_1DAY.equals(line)) { kline = klineService.bulidKline1Day(realtime, KlineConstant.PERIOD_1DAY); } else if (KlineConstant.PERIOD_5DAY.equals(line)) { kline = klineService.bulidKline5Day(realtime, KlineConstant.PERIOD_5DAY); } else if (KlineConstant.PERIOD_1WEEK.equals(line)) { kline = klineService.bulidKline1Week(realtime, KlineConstant.PERIOD_1WEEK); } else if (KlineConstant.PERIOD_1MON.equals(line)) { kline = klineService.bulidKline1Mon(realtime, KlineConstant.PERIOD_1MON); } else if (KlineConstant.PERIOD_QUARTER.equals(line)) { kline = klineService.bulidKline1Mon(realtime, KlineConstant.PERIOD_QUARTER); } else if (KlineConstant.PERIOD_YEAR.equals(line)) { kline = klineService.bulidKline1Mon(realtime, KlineConstant.PERIOD_YEAR); } if (null != kline) { list_clone.add(kline); } if (realtime == null) { realtime = DataCache.getRealtime(symbol); } // 按时间升序 if (realtime != null) { appendOrReplaceKline(list_clone, buildCurrentKline(realtime, line)); } Collections.sort(list_clone); return list_clone; } private Kline buildCurrentKline(Realtime realtime, String line) { if (KlineConstant.PERIOD_1MIN.equals(line)) { return klineService.bulidKline1Minute(realtime, KlineConstant.PERIOD_1MIN); } else if (KlineConstant.PERIOD_5MIN.equals(line)) { return klineService.bulidKline5Minute(realtime, KlineConstant.PERIOD_5MIN); } else if (KlineConstant.PERIOD_15MIN.equals(line)) { return klineService.bulidKline15Minute(realtime, KlineConstant.PERIOD_15MIN); } else if (KlineConstant.PERIOD_30MIN.equals(line)) { return klineService.bulidKline30Minute(realtime, KlineConstant.PERIOD_30MIN); } else if (KlineConstant.PERIOD_60MIN.equals(line)) { return klineService.bulidKline60Minute(realtime, KlineConstant.PERIOD_60MIN); } else if (KlineConstant.PERIOD_4HOUR.equals(line)) { return klineService.bulidKline4Hour(realtime, KlineConstant.PERIOD_4HOUR); } else if (KlineConstant.PERIOD_1DAY.equals(line)) { return klineService.bulidKline1Day(realtime, KlineConstant.PERIOD_1DAY); } else if (KlineConstant.PERIOD_5DAY.equals(line)) { return klineService.bulidKline5Day(realtime, KlineConstant.PERIOD_5DAY); } else if (KlineConstant.PERIOD_1WEEK.equals(line)) { return klineService.bulidKline1Week(realtime, KlineConstant.PERIOD_1WEEK); } else if (KlineConstant.PERIOD_1MON.equals(line)) { return klineService.bulidKline1Mon(realtime, KlineConstant.PERIOD_1MON); } else if (KlineConstant.PERIOD_QUARTER.equals(line)) { return klineService.bulidKlineQuarter(realtime, KlineConstant.PERIOD_QUARTER); } else if (KlineConstant.PERIOD_YEAR.equals(line)) { return klineService.bulidKlineYear(realtime, KlineConstant.PERIOD_YEAR); } return null; } public List<Kline> klineCryptos(String symbol, String line) { KlineTimeObject timeObject = DataCache.getKline(symbol, line); List<Kline> list = new ArrayList<Kline>(); if (timeObject != null) { list = timeObject.getKline(); return kline(symbol, line); } private void appendOrReplaceKline(List<Kline> list, Kline kline) { if (kline == null) { return; } List<Kline> list_clone = new ArrayList<Kline>(); try { for (int i = 0; i < list.size(); i++) { Kline kline = (Kline) list.get(i).clone(); list_clone.add(kline); for (int i = list.size() - 1; i >= 0; i--) { Kline existing = list.get(i); if (existing.getTs() != null && existing.getTs().equals(kline.getTs())) { list.set(i, kline); return; } } catch (CloneNotSupportedException e) { e.printStackTrace(); } Realtime realtime = DataCache.getRealtime(symbol); Kline hobiOne = DataCache.getKline_hobi().get(symbol + "_" + line); Kline lastOne = null; if (list != null && list.size() > 0) { lastOne = list.get(list.size() - 1); } if (realtime != null && hobiOne != null && lastOne != null) { list_clone.add(this.klineService.bulidKline(realtime, lastOne, hobiOne, line)); } Collections.sort(list_clone); // 按时间升序 return list_clone; list.add(kline); } @Override