debug: 백테스팅 실행 시 파라미터·시그널 생성 상세 로그 추가

인디케이터 파라미터 로드 현황, positionMode, 생성된 시그널 수를
INFO 레벨로 출력하여 서버에서 정확한 진단 가능하도록 한다.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Macbook
2026-06-11 17:54:14 +09:00
parent dafb1e49af
commit 8e75a6ce6a
@@ -88,6 +88,14 @@ public class BacktestingService {
}
// 요청에 포함된 파라미터가 있으면 DB 값 위에 덮어씀 (요청값 우선)
Map<String, Map<String, Object>> params = mergeIndicatorParams(dbParams, req.getIndicatorParams());
log.info("[Backtest] 인디케이터 파라미터 로드 — deviceId={}, dbParams키={}, reqParams키={}, 최종키={}",
req.getDeviceId(),
dbParams.keySet(),
req.getIndicatorParams() != null ? req.getIndicatorParams().keySet() : "null",
params.keySet());
if (params.containsKey("Stochastic")) {
log.info("[Backtest] Stochastic 파라미터: {}", params.get("Stochastic"));
}
if (params.isEmpty()) {
log.warn("[Backtest] 인디케이터 파라미터 없음 — deviceId={}, 지표 기본값 사용 가능성 있음", req.getDeviceId());
}
@@ -153,6 +161,8 @@ public class BacktestingService {
// positionMode: LONG_ONLY(기본) | SIGNAL_ONLY(포지션 락 우회)
String posMode = cfg.getPositionMode() != null ? cfg.getPositionMode() : "LONG_ONLY";
boolean signalOnly = "SIGNAL_ONLY".equals(posMode);
log.info("[Backtest] 실행 시작 — strategyId={}, bars={}, timeframe={}, positionMode={}, signalOnly={}",
req.getStrategyId(), series.getBarCount(), req.getTimeframe(), posMode, signalOnly);
double initCap = cfg.getInitialCapital() != null ? cfg.getInitialCapital().doubleValue() : 10_000_000.0;
double tradeSizePct = cfg.getTradeSizeValue() != null ? cfg.getTradeSizeValue().doubleValue() / 100.0 : 1.0;
@@ -175,15 +185,19 @@ public class BacktestingService {
// ── SIGNAL_ONLY 모드: 포지션·자금 상태 무관, 조건 충족 시마다 전체 시그널 생성 ──
if (signalOnly) {
if (entryRule.isSatisfied(i)) {
boolean entrySatisfied = entryRule.isSatisfied(i);
boolean exitSatisfied = exitRule.isSatisfied(i);
if (entrySatisfied) {
double effEntry = applySlippage(closePrice, cfg, true);
String buyType = "SHORT".equals(direction) ? "SHORT_ENTRY" : "BUY";
signals.add(buildFillSignal(time, buyType, effEntry, i, 1.0));
log.debug("[Backtest:SIGNAL_ONLY] BUY @ bar={} price={}", i, effEntry);
}
if (exitRule.isSatisfied(i)) {
if (exitSatisfied) {
double effExit = applySlippage(exitPrice, cfg, false);
String sellType = "SHORT".equals(direction) ? "SHORT_EXIT" : "SELL";
signals.add(buildFillSignal(time, sellType, effExit, i, 1.0));
log.debug("[Backtest:SIGNAL_ONLY] SELL @ bar={} price={}", i, effExit);
}
continue;
}
@@ -248,6 +262,11 @@ public class BacktestingService {
if (useLedger) ledger.markToMarket(closePrice);
}
long buyCnt = signals.stream().filter(s -> "BUY".equals(s.type()) || "SHORT_ENTRY".equals(s.type())).count();
long sellCnt = signals.stream().filter(s -> "SELL".equals(s.type()) || "SHORT_EXIT".equals(s.type())).count();
log.info("[Backtest] 완료 — strategyId={}, bars={}, 생성시그널={}개 (매수={}, 매도={}), positionMode={}",
req.getStrategyId(), barCount, signals.size(), buyCnt, sellCnt, posMode);
double lastMarkPrice = barCount > 0
? series.getBar(barCount - 1).getClosePrice().doubleValue() : 0.0;
double finalEquity = resolveFinalEquity(ledger, useLedger, cfg, initCap, equity, inPosition,