package com.yami.trading.huobi.data;
|
|
import cn.hutool.core.collection.CollectionUtil;
|
import com.yami.trading.bean.data.domain.Kline;
|
import com.yami.trading.bean.item.domain.Item;
|
import com.yami.trading.huobi.hobi.HobiDataService;
|
import com.yami.trading.huobi.tradingview.TradingViewSymbolResolver;
|
import com.yami.trading.huobi.tradingview.service.TradingViewService;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
|
import java.util.ArrayList;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeoutException;
|
|
/**
|
* 非加密货币 K 线远程拉取:TradingView 多 symbol 重试 → Hobi 数据源兜底。
|
*/
|
@Slf4j
|
@Service
|
public class NonCryptoKlineRemoteService {
|
|
private static final int TV_PER_SYMBOL_TIMEOUT_SECONDS = 5;
|
|
@Autowired
|
private TradingViewService tradingViewService;
|
@Autowired
|
private HobiDataService hobiDataService;
|
|
public List<Kline> fetch(Item item, String requestLine, String twLine) {
|
List<Kline> tvData = fetchFromTradingView(item, twLine, requestLine);
|
if (CollectionUtil.isNotEmpty(tvData)) {
|
return tvData;
|
}
|
return fetchFromHobi(item, requestLine);
|
}
|
|
private List<Kline> fetchFromTradingView(Item item, String twLine, String requestLine) {
|
List<String> candidates = TradingViewSymbolResolver.resolveCandidates(item);
|
for (String tvSymbol : candidates) {
|
try {
|
Map<String, List<com.yami.trading.huobi.tradingview.api.model.Kline>> klineData =
|
tradingViewService.getKlineData(tvSymbol, twLine)
|
.get(TV_PER_SYMBOL_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
List<com.yami.trading.huobi.tradingview.api.model.Kline> klines = klineData.get(tvSymbol);
|
if ((klines == null || klines.isEmpty()) && klineData != null && !klineData.isEmpty()) {
|
klines = klineData.values().stream()
|
.filter(list -> list != null && !list.isEmpty())
|
.findFirst()
|
.orElse(null);
|
}
|
if (klines == null || klines.isEmpty()) {
|
continue;
|
}
|
log.info("TradingView kline hit, symbol={}, tvSymbol={}, bars={}", item.getSymbol(), tvSymbol, klines.size());
|
return convertTvKlines(klines, item, requestLine);
|
} catch (TimeoutException e) {
|
log.warn("TradingView kline timeout, symbol={}, tvSymbol={}, twLine={}", item.getSymbol(), tvSymbol, twLine);
|
} catch (Exception e) {
|
log.warn("TradingView kline error, symbol={}, tvSymbol={}, twLine={}", item.getSymbol(), tvSymbol, twLine, e);
|
}
|
}
|
return new ArrayList<>();
|
}
|
|
private List<Kline> convertTvKlines(List<com.yami.trading.huobi.tradingview.api.model.Kline> klines,
|
Item item, String requestLine) {
|
List<Kline> data = new ArrayList<>();
|
String period = requestLine != null ? requestLine : Kline.PERIOD_1MIN;
|
for (com.yami.trading.huobi.tradingview.api.model.Kline k : klines) {
|
Kline kline = new Kline();
|
kline.setSymbol(item.getSymbolData());
|
kline.setTs(k.getTimestamp());
|
kline.setOpen(k.getOpen());
|
kline.setHigh(k.getHigh());
|
kline.setLow(k.getLow());
|
kline.setClose(k.getClose());
|
kline.setPeriod(period);
|
kline.setAmount(0D);
|
kline.setVolume(k.getVolume());
|
data.add(kline);
|
}
|
return data;
|
}
|
|
private List<Kline> fetchFromHobi(Item item, String line) {
|
String symbol = item.getSymbol();
|
try {
|
List<Kline> klines = fetchPeriodFromHobi(symbol, line);
|
if (CollectionUtil.isNotEmpty(klines)) {
|
log.info("Hobi kline fallback hit, symbol={}, line={}, bars={}", symbol, line, klines.size());
|
}
|
return klines == null ? new ArrayList<>() : klines;
|
} catch (Exception e) {
|
log.warn("Hobi kline fallback failed, symbol={}, line={}", symbol, line, e);
|
return new ArrayList<>();
|
}
|
}
|
|
private List<Kline> fetchPeriodFromHobi(String symbol, String line) {
|
if (line == null) {
|
return new ArrayList<>();
|
}
|
switch (line.toLowerCase()) {
|
case "1min":
|
return hobiDataService.getTimeseriesOneMinute(symbol);
|
case "5min":
|
return hobiDataService.getTimeseriesFiveMinute(symbol);
|
case "15min":
|
return hobiDataService.getTimeseriesFifteenMinute(symbol);
|
case "30min":
|
return hobiDataService.getTimeseriesThirtyMinute(symbol);
|
case "60min":
|
return hobiDataService.getTimeseriesForOneHourly(symbol);
|
case "120min":
|
case "2hour":
|
return hobiDataService.getTimeseriesForTwoHourly(symbol);
|
case "4hour":
|
return hobiDataService.getTimeseriesForFourHourly(symbol);
|
case "1day":
|
case "5day":
|
case "1week":
|
case "1mon":
|
case "quarter":
|
case "year":
|
Map<String, List<Kline>> dailyMap = hobiDataService.getDailyWeekMonthHistory(symbol);
|
if (dailyMap == null) {
|
return new ArrayList<>();
|
}
|
List<Kline> periodData = dailyMap.get(line);
|
return periodData == null ? new ArrayList<>() : periodData;
|
default:
|
return hobiDataService.getTimeseriesOneMinute(symbol);
|
}
|
}
|
}
|