추세검색 설정 추가

This commit is contained in:
Macbook
2026-05-27 01:36:06 +09:00
parent c1bcf88c6c
commit 2e08c6b16f
42 changed files with 1507 additions and 226 deletions
@@ -64,10 +64,11 @@ public class TrendSearchService {
int wantBars = wantBarsForScan(r);
int minBars = requiredMinBars(r);
List<String> markets = fetchKrwMarketsByVolume(scanLimit);
KrwMarketSlice slice = fetchKrwMarketsByVolumeSlice(scanLimit);
List<String> markets = slice.markets();
if (markets.isEmpty()) return List.of();
Map<String, TickerSnap> tickers = fetchTickers(markets);
Map<String, TickerSnap> tickers = fetchTickers(markets, slice.koreanNames());
int minMatchRate = Math.min(100, Math.max(0, r.getMinMatchRate()));
int enabledFilters = countEnabledFilters(r);
boolean useBullishScore = r.isBullishTrendEnabled();
@@ -84,7 +85,8 @@ public class TrendSearchService {
BarSeries series = toSeries(candles, tf);
if (series.getBarCount() < minBars) continue;
TickerSnap tick = tickers.getOrDefault(market, TickerSnap.empty(market));
TickerSnap tick = tickers.getOrDefault(market,
TickerSnap.empty(market, slice.koreanNames().get(market)));
MetricValues m = computeMetrics(series, candles, tick, r);
if (useBullishScore) {
m.bullishTrendScore = computeBullishTrendScore(m, r);
@@ -133,8 +135,9 @@ public class TrendSearchService {
.market(market).matchRate(0).conditions(List.of()).build();
}
Map<String, TickerSnap> tickers = fetchTickers(List.of(market));
TickerSnap tick = tickers.getOrDefault(market, TickerSnap.empty(market));
Map<String, String> koreanNames = fetchKoreanNamesForMarkets(List.of(market));
Map<String, TickerSnap> tickers = fetchTickers(List.of(market), koreanNames);
TickerSnap tick = tickers.getOrDefault(market, TickerSnap.empty(market, koreanNames.get(market)));
BarSeries series = toSeries(candles, tf);
MetricValues m = computeMetrics(series, candles, tick, r);
if (r.isBullishTrendEnabled()) {
@@ -675,7 +678,37 @@ public class TrendSearchService {
// ── Upbit helpers ───────────────────────────────────────────────────────
private List<String> fetchKrwMarketsByVolume(int limit) {
private record KrwMarketSlice(List<String> markets, Map<String, String> koreanNames) {}
/** 거래대금 상위 KRW 마켓 + 한글명 (market/all 기준) */
private KrwMarketSlice fetchKrwMarketsByVolumeSlice(int limit) {
try {
Map<String, String> allNames = fetchKoreanNamesFromMarketAll();
if (allNames.isEmpty()) return new KrwMarketSlice(List.of(), Map.of());
List<String> krw = new ArrayList<>(allNames.keySet());
Map<String, TickerSnap> tickers = fetchTickers(krw, allNames);
List<String> sorted = krw.stream()
.sorted(Comparator.comparingDouble((String m) ->
tickers.getOrDefault(m, TickerSnap.empty(m, allNames.get(m))).accTradePrice24h).reversed())
.limit(limit)
.collect(Collectors.toList());
Map<String, String> names = new HashMap<>();
for (String m : sorted) {
names.put(m, allNames.getOrDefault(m, m.replace("KRW-", "")));
}
return new KrwMarketSlice(sorted, names);
} catch (Exception e) {
log.warn("[TrendSearch] market fetch fail: {}", e.getMessage());
List<String> fallback = List.of("KRW-BTC", "KRW-ETH", "KRW-XRP", "KRW-SOL", "KRW-ADA");
Map<String, String> names = new HashMap<>();
for (String m : fallback) names.put(m, m.replace("KRW-", ""));
return new KrwMarketSlice(fallback, names);
}
}
private Map<String, String> fetchKoreanNamesFromMarketAll() {
try {
WebClient client = webClientBuilder.baseUrl(upbitBaseUrl).build();
String marketsJson = client.get()
@@ -683,29 +716,36 @@ public class TrendSearchService {
.retrieve()
.bodyToMono(String.class)
.block();
if (marketsJson == null) return List.of();
if (marketsJson == null) return Map.of();
List<Map<String, Object>> all = objectMapper.readValue(marketsJson, new TypeReference<>() {});
List<String> krw = all.stream()
.map(m -> (String) m.get("market"))
.filter(m -> m != null && m.startsWith("KRW-"))
.collect(Collectors.toList());
if (krw.isEmpty()) return List.of();
Map<String, TickerSnap> tickers = fetchTickers(krw);
return krw.stream()
.sorted(Comparator.comparingDouble((String m) ->
tickers.getOrDefault(m, TickerSnap.empty(m)).accTradePrice24h).reversed())
.limit(limit)
.collect(Collectors.toList());
Map<String, String> names = new HashMap<>();
for (Map<String, Object> m : all) {
String market = (String) m.get("market");
if (market == null || !market.startsWith("KRW-")) continue;
Object kn = m.get("korean_name");
if (kn != null && !kn.toString().isBlank()) {
names.put(market, kn.toString());
}
}
return names;
} catch (Exception e) {
log.warn("[TrendSearch] market fetch fail: {}", e.getMessage());
return List.of("KRW-BTC", "KRW-ETH", "KRW-XRP", "KRW-SOL", "KRW-ADA");
log.warn("[TrendSearch] korean names fetch fail: {}", e.getMessage());
return Map.of();
}
}
private Map<String, TickerSnap> fetchTickers(List<String> markets) {
private Map<String, String> fetchKoreanNamesForMarkets(List<String> markets) {
if (markets.isEmpty()) return Map.of();
Map<String, String> all = fetchKoreanNamesFromMarketAll();
Map<String, String> out = new HashMap<>();
for (String m : markets) {
out.put(m, all.getOrDefault(m, m.replace("KRW-", "")));
}
return out;
}
private Map<String, TickerSnap> fetchTickers(List<String> markets, Map<String, String> koreanNames) {
if (markets.isEmpty()) return Map.of();
try {
WebClient client = webClientBuilder.baseUrl(upbitBaseUrl).build();
@@ -722,9 +762,12 @@ public class TrendSearchService {
JsonNode arr = objectMapper.readTree(json);
for (JsonNode n : arr) {
String market = n.path("market").asText();
String ko = koreanNames != null
? koreanNames.getOrDefault(market, market.replace("KRW-", ""))
: market.replace("KRW-", "");
map.put(market, new TickerSnap(
market,
n.path("korean_name").asText(market),
ko,
n.path("trade_price").asDouble(0),
n.path("signed_change_rate").asDouble(0) * 100,
n.path("acc_trade_price_24h").asDouble(0)
@@ -777,8 +820,11 @@ public class TrendSearchService {
private record TickerSnap(String market, String koreanName, double tradePrice,
double changeRate, double accTradePrice24h) {
static TickerSnap empty(String market) {
return new TickerSnap(market, market.replace("KRW-", ""), 0, 0, 0);
static TickerSnap empty(String market, String koreanName) {
String ko = koreanName != null && !koreanName.isBlank()
? koreanName
: market.replace("KRW-", "");
return new TickerSnap(market, ko, 0, 0, 0);
}
}