fix: 백테스팅 시 인디케이터 파라미터를 DB에서 자동 로드

BacktestingService에 IndicatorSettingsService를 주입하여 백테스팅
실행 시 deviceId 기반으로 DB 저장 인디케이터 설정을 자동으로 로드한다.

- 프론트엔드가 indicatorParams를 전달하지 않아도 DB 저장값 사용 보장
- 요청에 포함된 indicatorParams는 DB값 위에 override로 적용(우선순위 높음)
- 어떠한 경우에도 백엔드 하드코딩 기본값만으로 테스트되는 상황 방지
- mergeIndicatorParams() 헬퍼로 DB+요청 파라미터 내부 필드 단위 병합

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Macbook
2026-06-11 17:48:21 +09:00
parent 9d9c14c2b1
commit dafb1e49af
2 changed files with 48 additions and 4 deletions
@@ -45,6 +45,7 @@ public class BacktestingService {
private final GcStrategyRepository strategyRepository;
private final GcBacktestResultRepository resultRepository;
private final ObjectMapper objectMapper;
private final IndicatorSettingsService indicatorSettingsService;
private static final BacktestSettingsDto DEFAULT_SETTINGS = new BacktestSettingsDto();
@@ -76,8 +77,20 @@ public class BacktestingService {
}
}
Map<String, Map<String, Object>> params = req.getIndicatorParams() != null
? req.getIndicatorParams() : Map.of();
// DB 저장 인디케이터 설정을 기본으로 로드 (디바이스별 저장값 → 하드코딩 기본값 방지)
Map<String, Map<String, Object>> dbParams = Map.of();
if (req.getDeviceId() != null && !req.getDeviceId().isBlank()) {
try {
dbParams = indicatorSettingsService.getAll(null, req.getDeviceId());
} catch (Exception e) {
log.warn("[Backtest] 인디케이터 설정 DB 로드 실패 (deviceId={}): {}", req.getDeviceId(), e.getMessage());
}
}
// 요청에 포함된 파라미터가 있으면 DB 값 위에 덮어씀 (요청값 우선)
Map<String, Map<String, Object>> params = mergeIndicatorParams(dbParams, req.getIndicatorParams());
if (params.isEmpty()) {
log.warn("[Backtest] 인디케이터 파라미터 없음 — deviceId={}, 지표 기본값 사용 가능성 있음", req.getDeviceId());
}
BacktestSettingsDto cfg = req.getSettings() != null ? req.getSettings() : DEFAULT_SETTINGS;
@@ -814,6 +827,36 @@ public class BacktestingService {
};
}
/**
* DB 기본 파라미터 위에 요청 파라미터를 병합한다 (요청값 우선).
*
* <ul>
* <li>base: DB에서 로드한 사용자 저장 인디케이터 파라미터</li>
* <li>override: 요청에 포함된 파라미터 (없으면 null)</li>
* </ul>
* 같은 indicatorType 키가 있을 경우 override 내부 필드가 base를 덮어씌운다.
*/
private static Map<String, Map<String, Object>> mergeIndicatorParams(
Map<String, Map<String, Object>> base,
Map<String, Map<String, Object>> override) {
if (override == null || override.isEmpty()) return base;
if (base == null || base.isEmpty()) return override;
Map<String, Map<String, Object>> merged = new java.util.LinkedHashMap<>(base);
override.forEach((type, overrideParams) -> {
Map<String, Object> baseParams = merged.get(type);
if (baseParams == null || baseParams.isEmpty()) {
merged.put(type, overrideParams);
} else {
Map<String, Object> inner = new java.util.LinkedHashMap<>(baseParams);
inner.putAll(overrideParams);
merged.put(type, inner);
}
});
return merged;
}
private BacktestResponse emptyResponse(String reason) {
log.warn("[BacktestingService] 빈 결과: {}", reason);
BacktestAnalysisDto emptyAnalysis = BacktestAnalysisDto.builder().build();