전략평가 시 부하문제 개선

This commit is contained in:
Macbook
2026-06-05 23:53:00 +09:00
parent 032ec69290
commit 1d4e606f77
7 changed files with 120 additions and 26 deletions
@@ -56,6 +56,17 @@ public class BarBuilder {
/** 진행 중인 1분봉 상태 (market → PartialBar) */
private final ConcurrentHashMap<String, PartialBar> partialBars = new ConcurrentHashMap<>();
/**
* Bar 생성용 공유 팩토리 BarSeries.
* barBuilder()는 Series 상태를 읽지 않고 BarBuilderFactory·NumFactory 만 참조하므로
* 싱글턴으로 재사용해도 안전하다. 매번 BaseBarSeriesBuilder를 생성하는 낭비를 제거.
*/
private static final org.ta4j.core.BarSeries BAR_FACTORY_SERIES =
new org.ta4j.core.BaseBarSeriesBuilder()
.withBarBuilderFactory(new TimeBarBuilderFactory())
.withNumFactory(DoubleNumFactory.getInstance())
.build();
// ── 공개 API ──────────────────────────────────────────────────────────────
/**
@@ -175,14 +186,15 @@ public class BarBuilder {
= java.util.Collections.unmodifiableList(closedUppers);
barCloseEvalExecutor.execute(() -> {
try {
if (barCloseEvaluation.shouldEvaluateOnClose(market, "1m")) {
BarSeries series1m = ta4jStorage.getOrCreate(market, "1m");
barCloseEvaluation.onMaturedBarClose(market, "1m", series1m.getEndIndex(), confirmedBar);
}
// shouldEvaluateOnClose → onMaturedBarClose 를 분리 호출하면 findActiveByMarket DB 쿼리가
// 타임프레임 수(최대 9+1=10)만큼 중복 실행된다.
// onMaturedBarClose 내부가 이미 activeSettings 조회 + 빈 경우 early-return 처리하므로
// shouldEvaluateOnClose 선행 체크를 제거하여 DB 쿼리를 타임프레임당 1회로 절감한다.
BarSeries series1m = ta4jStorage.getOrCreate(market, "1m");
barCloseEvaluation.onMaturedBarClose(market, "1m", series1m.getEndIndex(), confirmedBar);
for (UpperCloseInfo info : closedUppersSnap) {
if (barCloseEvaluation.shouldEvaluateOnClose(market, info.type())) {
barCloseEvaluation.onMaturedBarClose(market, info.type(), info.maturedIdx(), info.bar());
}
barCloseEvaluation.onMaturedBarClose(market, info.type(), info.maturedIdx(), info.bar());
}
} catch (Exception e) {
log.error("[BarBuilder] Phase 2 전략 평가 오류 market={}: {}", market, e.getMessage(), e);
@@ -232,15 +244,11 @@ public class BarBuilder {
}
}
/** PartialBar → Ta4j Bar 변환 */
/** PartialBar → Ta4j Bar 변환 — BAR_FACTORY_SERIES를 재사용하여 임시 BarSeries 생성 제거 */
private Bar buildTa4jBar(PartialBar partial, Duration duration, ZonedDateTime endZdt) {
BarSeries tmp = new org.ta4j.core.BaseBarSeriesBuilder()
.withBarBuilderFactory(new TimeBarBuilderFactory())
.withNumFactory(DoubleNumFactory.getInstance()) // Ta4j 0.22
.build();
// Ta4j 0.22: barBuilder().endTime() 은 Instant 파라미터
Instant endInstant = endZdt.toInstant();
return tmp.barBuilder()
return BAR_FACTORY_SERIES.barBuilder()
.timePeriod(duration)
.endTime(endInstant)
.openPrice(partial.open)
@@ -117,6 +117,16 @@ public class UpbitWebSocketClient {
return wsClient != null && wsClient.isOpen();
}
/** 마지막 연결 성공 시각 (ms) — staleness monitor 유예 기간 계산용 */
public long getConnectedAtMs() {
return connectedAtMs;
}
/** 마지막 메시지 수신 시각 (ms) — WS 전체 건강 상태 확인용 */
public long getLastMessageAtMs() {
return lastMessageAtMs;
}
public Map<String, Object> monitorSnapshot() {
Map<String, Object> m = new LinkedHashMap<>();
m.put("connected", isConnected());
@@ -199,6 +209,8 @@ public class UpbitWebSocketClient {
log.info("[UpbitWebSocketClient] 연결 성공");
reconnectDelaySec = 2; // 재연결 딜레이 리셋
connectedAtMs = System.currentTimeMillis();
// 이전 연결의 오래된 타임스탬프를 초기화하여 재연결 직후 즉시 stale 판단 → 재연결 루프 방지
activityTracker.resetAll();
sendSubscribePacket();
}