전략평가 개선
This commit is contained in:
@@ -5,6 +5,7 @@ import com.goldenchart.service.BarCloseStrategyEvaluationService;
|
||||
import com.goldenchart.storage.Ta4jStorage;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.ta4j.core.Bar;
|
||||
import org.ta4j.core.BarSeries;
|
||||
@@ -18,6 +19,7 @@ import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* 업비트 WebSocket 틱(Trade) 데이터를 수신하여 분봉 단위 캔들을 조립하는 빌더.
|
||||
@@ -39,6 +41,14 @@ public class BarBuilder {
|
||||
private final BarCloseStrategyEvaluationService barCloseEvaluation;
|
||||
private final com.goldenchart.service.PaperOrderMatcherService paperOrderMatcher;
|
||||
|
||||
/**
|
||||
* [항목4] Phase 2 비동기 실행용 스레드 풀.
|
||||
* 웹소켓 수신 스레드가 전략 평가 연산에 블로킹되지 않도록 분리한다.
|
||||
*/
|
||||
@org.springframework.beans.factory.annotation.Autowired
|
||||
@Qualifier("barCloseEvalExecutor")
|
||||
private Executor barCloseEvalExecutor;
|
||||
|
||||
/** 상위 집계 타임프레임 (분 단위 집계 주기: 3, 5, 10, 15, 30, 60, 240, 1440) */
|
||||
private static final int[] UPPER_MINUTES = {3, 5, 10, 15, 30, 60, 240, 10080, 1440};
|
||||
private static final String[] UPPER_TYPES = {"3m", "5m", "10m", "15m", "30m", "1h", "4h", "1w", "1d"};
|
||||
@@ -150,18 +160,34 @@ public class BarBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Phase 2: 모든 봉이 확정된 후 전략 평가 트리거 ─────────────────────
|
||||
// ── Phase 2: 모든 봉이 확정된 후 전략 평가 트리거 (비동기) ──────────────
|
||||
//
|
||||
// [항목4] 웹소켓 수신 스레드(업비트 연결 유지 담당)가 전략 평가 연산 때문에 블로킹되면
|
||||
// 다음 분봉 시작 틱을 제때 수신하지 못할 수 있다. Phase 1 완료 후 Phase 2 를
|
||||
// 전용 스레드 풀(barCloseEvalExecutor)에 제출하여 WebSocket 파이프라인을 즉시 반환한다.
|
||||
//
|
||||
// 스레드 안전성: Ta4jStorage·positionOpenCache 는 ConcurrentHashMap, maturedIdx 는
|
||||
// Phase 1 에서 이미 확정된 값이므로 비동기 실행 중 Phase 1 이 재실행되어 인덱스가 바뀌어도
|
||||
// 올바른 봉을 참조한다.
|
||||
|
||||
if (barCloseEvaluation.shouldEvaluateOnClose(market, "1m")) {
|
||||
BarSeries series1m = ta4jStorage.getOrCreate(market, "1m");
|
||||
barCloseEvaluation.onMaturedBarClose(market, "1m", series1m.getEndIndex(), bar);
|
||||
}
|
||||
|
||||
for (UpperCloseInfo info : closedUppers) {
|
||||
if (barCloseEvaluation.shouldEvaluateOnClose(market, info.type())) {
|
||||
barCloseEvaluation.onMaturedBarClose(market, info.type(), info.maturedIdx(), info.bar());
|
||||
final Bar confirmedBar = bar;
|
||||
final java.util.List<UpperCloseInfo> closedUppersSnap
|
||||
= 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);
|
||||
}
|
||||
for (UpperCloseInfo info : closedUppersSnap) {
|
||||
if (barCloseEvaluation.shouldEvaluateOnClose(market, info.type())) {
|
||||
barCloseEvaluation.onMaturedBarClose(market, info.type(), info.maturedIdx(), info.bar());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[BarBuilder] Phase 2 전략 평가 오류 market={}: {}", market, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Phase 1 에서 마감된 상위봉 정보를 Phase 2 로 전달하는 내부 레코드 */
|
||||
|
||||
Reference in New Issue
Block a user