Sisc1 56 be 백테스팅 실행 api 구현#125
Hidden character warning
Conversation
customUserDetails에 user 추가 후, 반환시 해당 객체 넣음으로써 해결
userRepository 이용하여 DB 접근으로 해결
…IT/sisc-web into SISC1-56-BE-백테스팅-실행-API-구현
# Conflicts: # .gitignore # backend/src/main/java/org/sejongisc/backend/backtest/repository/BacktestRunRepository.java # backend/src/main/java/org/sejongisc/backend/template/service/TemplateService.java # backend/src/main/java/org/sejongisc/backend/user/entity/User.java
# Conflicts: # backend/src/main/java/org/sejongisc/backend/backtest/service/BacktestingEngine.java # backend/src/main/java/org/sejongisc/backend/backtest/service/Ta4jHelperService.java
Walkthrough백테스트 API와 엔진에 기능 추가 및 리팩토링이 이루어졌습니다: 티커 목록 조회 엔드포인트 추가, DTO 필드 재배치(기본 청산 기간 이동), 강제 청산 로직 및 비율 기반 매매·메트릭(Sharpe, 평균 보유일) 계산이 엔진에 도입되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Controller as BacktestController
participant Service as BacktestService
participant Repo as PriceDataRepository
participant Engine as BacktestingEngine
participant RunRepo as BacktestRunRepository
Client->>Controller: GET /api/backtest/stocks/info
Controller->>Service: getBacktestStockInfo()
Service->>Repo: findDistinctTickers()
Repo-->>Service: List<String> (티커)
Service-->>Controller: BacktestResponse(availableTickers)
Controller-->>Client: 200 OK
Note over Client,RunRepo: 백테스트 실행 흐름 (요약)
Client->>Controller: POST /api/backtest/run (BacktestRunRequest)
Controller->>Service: runBacktest(request)
Service->>RunRepo: save(BacktestRun)
RunRepo-->>Service: saved BacktestRun
Service->>Engine: execute(backtestRun)
rect rgb(220,245,230)
Engine->>Engine: set RUNNING, startedAt
Engine->>Engine: params <- backtestRun.getParamsJson()
Engine->>Engine: load price data
Engine->>Engine: create BarSeries (Ta4j)
loop for each bar
Engine->>Engine: evaluate buyRule / sellRule
Engine->>Engine: if held > defaultExitDays -> SELL_FORCED
Engine->>Engine: apply buyRatio / sellRatio trades, update cash/shares
Engine->>Engine: compute daily portfolio value, update drawdown
end
Engine->>Engine: calculate Sharpe, avgHoldDays, metrics
end
Engine->>RunRepo: save(metrics, set COMPLETED/FAILED, finishedAt)
Engine-->>Service: execution finished
Estimated code review effort🎯 4 (Complex) | ⏱️ ~40-70 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
backend/src/main/java/org/sejongisc/backend/backtest/service/BacktestingEngine.java (1)
222-242: SELL_FORCED 타입도 평균 보유 기간 계산에 포함해야 합니다.Line 230에서
TradeLog.Type.SELL만 처리하고 있는데,SELL_FORCED도 매도 이벤트이므로 평균 보유 기간 계산에 포함되어야 합니다.for (TradeLog log : tradeLogs) { if (log.type == TradeLog.Type.BUY) { currentBuyTime = log.time; - } else if (log.type == TradeLog.Type.SELL && currentBuyTime != null) { + } else if ((log.type == TradeLog.Type.SELL || log.type == TradeLog.Type.SELL_FORCED) && currentBuyTime != null) { long days = java.time.temporal.ChronoUnit.DAYS.between(currentBuyTime.toLocalDate(), log.time.toLocalDate()); holdDurations.add(days); currentBuyTime = null; } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
backend/src/main/java/org/sejongisc/backend/backtest/controller/BacktestController.java(2 hunks)backend/src/main/java/org/sejongisc/backend/backtest/dto/BacktestRequest.java(0 hunks)backend/src/main/java/org/sejongisc/backend/backtest/dto/BacktestResponse.java(2 hunks)backend/src/main/java/org/sejongisc/backend/backtest/dto/BacktestRunRequest.java(2 hunks)backend/src/main/java/org/sejongisc/backend/backtest/dto/TradeLog.java(1 hunks)backend/src/main/java/org/sejongisc/backend/backtest/entity/BacktestRunMetrics.java(1 hunks)backend/src/main/java/org/sejongisc/backend/backtest/service/BacktestService.java(3 hunks)backend/src/main/java/org/sejongisc/backend/backtest/service/BacktestingEngine.java(5 hunks)backend/src/main/java/org/sejongisc/backend/backtest/service/Ta4jHelperService.java(3 hunks)backend/src/main/java/org/sejongisc/backend/stock/repository/PriceDataRepository.java(2 hunks)
💤 Files with no reviewable changes (1)
- backend/src/main/java/org/sejongisc/backend/backtest/dto/BacktestRequest.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-19T05:02:44.318Z
Learnt from: daye200
Repo: SISC-IT/sisc-web PR: 120
File: backend/src/main/java/org/sejongisc/backend/stock/repository/ExecutionRepository.java:11-12
Timestamp: 2025-11-19T05:02:44.318Z
Learning: Spring Data JPA supports Java record-based DTO projections in derived query methods (e.g., findAllByOrderByFillDateDesc() returning List<TradeLogDto>) without requiring explicit Query annotations, as long as the record's constructor parameters match the entity's field names.
Applied to files:
backend/src/main/java/org/sejongisc/backend/stock/repository/PriceDataRepository.java
🧬 Code graph analysis (1)
backend/src/main/java/org/sejongisc/backend/backtest/service/BacktestingEngine.java (1)
backend/src/main/java/org/sejongisc/backend/backtest/dto/TradeLog.java (1)
TradeLog(6-23)
🔇 Additional comments (14)
backend/src/main/java/org/sejongisc/backend/backtest/service/Ta4jHelperService.java (2)
39-39: 타임존 명시화가 잘 되었습니다.시스템 기본 타임존 대신
Asia/Seoul을 명시적으로 사용하여 서버 환경에 관계없이 일관된 동작을 보장합니다. 한국 주식 시장 데이터를 다루는 경우 적절한 변경입니다.
34-36: 원본 리뷰 의견은 부정확합니다.
createBarSeries메서드를 호출하는BacktestingEngine.java라인 67-69에서 이미 빈 리스트 검증이 이루어지고 있습니다. 호출 전에priceDataList.isEmpty()를 확인하여 비어있으면CustomException(ErrorCode.PRICE_DATA_NOT_FOUND)을 발생시키므로, 빈 리스트가createBarSeries에 도달하지 않습니다. 따라서getFirst()에서NoSuchElementException이 발생할 위험은 없습니다.Likely an incorrect or invalid review comment.
backend/src/main/java/org/sejongisc/backend/backtest/dto/TradeLog.java (1)
7-11: LGTM! 강제 청산 타입이 명확하게 정의되었습니다.
SELL_FORCED열거형 값 추가로 기본 청산 기간에 의한 강제 매도를 일반 매도와 구분할 수 있게 되었습니다. BacktestingEngine의 강제 청산 로직과 잘 통합됩니다.backend/src/main/java/org/sejongisc/backend/backtest/dto/BacktestRunRequest.java (1)
39-42: 주석 처리된 buyRatio, sellRatio 필드의 처리 계획을 확인하세요.BacktestingEngine(line 90-91)에서 이 값들이 하드코딩(10, 100)되어 있습니다. 향후 DTO에서 받을 계획이라면 TODO를 명확히 하고, 그렇지 않다면 주석을 제거하는 것이 좋습니다.
backend/src/main/java/org/sejongisc/backend/backtest/service/BacktestingEngine.java (2)
46-47: LGTM! 메서드 시그니처 개선이 합리적입니다.
BacktestRun객체를 직접 받도록 변경하여 불필요한 데이터베이스 조회를 제거했습니다. 이는 성능과 코드 명확성 측면에서 개선입니다.
247-256: 유효성 검증 로직이 잘 구현되었습니다.퍼센티지를 비율로 변환하는 헬퍼 함수가 null 체크와 범위 검증을 포함하여 안전하게 구현되었습니다.
backend/src/main/java/org/sejongisc/backend/backtest/dto/BacktestResponse.java (1)
12-12: LGTM! 백테스트 주식 정보 제공을 위한 필드가 추가되었습니다.
availableTickers필드 추가로 백테스트에 사용 가능한 종목 목록을 클라이언트에 제공할 수 있게 되었습니다.Also applies to: 22-22
backend/src/main/java/org/sejongisc/backend/stock/repository/PriceDataRepository.java (1)
18-22: LGTM! JPQL 쿼리가 올바르게 구현되었습니다.
DISTINCT를 사용하여 중복 없이 모든 티커를 조회하는 쿼리가 명확하게 구현되었습니다. JavaDoc 문서화도 적절합니다.Based on learnings
backend/src/main/java/org/sejongisc/backend/backtest/controller/BacktestController.java (2)
30-37: 엔드포인트 인증 요구사항을 확인하세요.
getBacktestStockInfo엔드포인트가 인증 없이 호출 가능하도록 구현되어 있습니다. 다른 엔드포인트들은@AuthenticationPrincipal을 사용하는데, 이 엔드포인트는 공개 API로 의도된 것인지 확인이 필요합니다.만약 인증이 필요하다면 다음과 같이 수정하세요:
-public ResponseEntity<BacktestResponse> getBacktestStockInfo() { +public ResponseEntity<BacktestResponse> getBacktestStockInfo(@AuthenticationPrincipal CustomUserDetails customUserDetails) { return ResponseEntity.ok(backtestService.getBacktestStockInfo()); }
85-85: LGTM! 예제에 defaultExitDays가 추가되었습니다.Swagger 예제에 새로운
defaultExitDays필드가 포함되어 API 사용자가 이 파라미터의 사용법을 명확히 이해할 수 있습니다.backend/src/main/java/org/sejongisc/backend/backtest/entity/BacktestRunMetrics.java (1)
40-54: LGTM! 정적 팩토리 메서드가 잘 구현되었습니다.
fromDto팩토리 메서드를 사용하여 메트릭 객체 생성 로직을 캡슐화했습니다. 빌더 패턴과 결합하여 가독성과 유지보수성이 향상되었습니다.backend/src/main/java/org/sejongisc/backend/backtest/service/BacktestService.java (3)
38-38: LGTM! 새로운 의존성이 적절히 추가되었습니다.
PriceDataRepository의존성을 추가하여 주식 정보 조회 기능을 지원합니다.
40-46: LGTM! 주식 정보 조회 메서드가 간결하게 구현되었습니다.빌더 패턴을 사용하여 사용 가능한 티커 목록을 담은 응답 객체를 생성합니다.
@Transactional어노테이션도 적절히 적용되었습니다.
119-119: LGTM! BacktestingEngine 호출이 개선되었습니다.ID 대신
BacktestRun객체를 직접 전달하도록 변경하여 불필요한 데이터베이스 조회를 제거했습니다.
backend/src/main/java/org/sejongisc/backend/backtest/dto/BacktestRunRequest.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/backtest/service/BacktestingEngine.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/backtest/service/BacktestingEngine.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/backtest/service/Ta4jHelperService.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/backtest/service/Ta4jHelperService.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
backend/src/main/java/org/sejongisc/backend/backtest/service/BacktestingEngine.java (1)
222-242: 강제 매도(SELL_FORCED)가 평균 보유 기간 계산에서 누락되었습니다.Line 230에서
TradeLog.Type.SELL만 확인하고 있어, 기본 청산 기간에 의한 강제 매도(SELL_FORCED)가 평균 보유 기간 계산에 포함되지 않습니다. 이는 계산 결과를 부정확하게 만듭니다.- } else if (log.type == TradeLog.Type.SELL && currentBuyTime != null) { + } else if ((log.type == TradeLog.Type.SELL || log.type == TradeLog.Type.SELL_FORCED) && currentBuyTime != null) { long days = java.time.temporal.ChronoUnit.DAYS.between(currentBuyTime.toLocalDate(), log.time.toLocalDate()); holdDurations.add(days); currentBuyTime = null;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
backend/src/main/java/org/sejongisc/backend/backtest/service/BacktestingEngine.java(5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
backend/src/main/java/org/sejongisc/backend/backtest/service/BacktestingEngine.java (1)
backend/src/main/java/org/sejongisc/backend/backtest/dto/TradeLog.java (1)
TradeLog(6-23)
🔇 Additional comments (7)
backend/src/main/java/org/sejongisc/backend/backtest/service/BacktestingEngine.java (7)
46-69: 메서드 시그니처 변경 및 초기 설정이 잘 구현되었습니다.
BacktestRun객체를 직접 받아 불필요한 repository 조회를 제거한 점이 좋습니다. 상태 전환과 가격 데이터 검증 로직도 적절합니다.
71-93: Ta4j 설정 및 변수 초기화가 적절합니다.BarSeries 생성, 룰 빌딩, 시뮬레이션 변수 초기화가 잘 구현되었습니다. 기본 청산 기간을 위한
buyBarIndex와defaultExitDays추가도 합리적입니다.참고: Lines 90-91의 TODO 주석은 이전 리뷰에서 이미 지적되었습니다.
95-109: 메인 루프 및 기본 청산 로직이 올바르게 구현되었습니다.기본 청산 기간 조건을 확인하는 로직이 적절하며,
buyBarIndex를 사용하여 매수 시점부터의 경과 일수를 추적하는 방식이 합리적입니다.
111-127: 매수 로직이 적절하게 구현되었습니다.매수 비중 적용, 거래 가능 주식 수 계산, 거래 로그 기록, 자산 업데이트가 올바르게 처리되었습니다. 첫 매수 시에만
buyBarIndex를 설정하는 로직도 적절합니다.참고: Line 114의
currentClosePrice로 나누기 연산에 대한 0 체크는 이전 리뷰에서 이미 지적되었습니다.
143-174: 일일 지표 계산 및 예외 처리가 적절합니다.포트폴리오 가치 및 수익률 계산, MDD 업데이트, 최종 지표 저장, 예외 처리가 모두 올바르게 구현되었습니다.
247-256: 퍼센트를 비율로 변환하는 헬퍼 메서드가 잘 구현되었습니다.입력 검증과 기본값 처리, 변환 로직이 모두 적절하게 구현되었습니다.
180-220: 검토 의견이 부정확합니다.프로젝트의
backend/build.gradle파일에서languageVersion = JavaLanguageVersion.of(21)로 명시적으로 설정되어 있습니다. Java 21은List.getLast()메서드를 지원하므로, 라인 184의 코드는 완전히 호환성 있으며 올바릅니다. 수정이 필요 없습니다.Likely an incorrect or invalid review comment.
Summary by CodeRabbit
New Features
Refactor
✏️ Tip: You can customize this high-level summary in your review settings.