alt/agent-roadmap/archive/sdd/backtest-loop/backtest-multi-timeframe-coverage/SDD.md

126 lines
13 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# SDD: Backtest Multi-Timeframe Coverage
## 위치
- Milestone: `agent-roadmap/archive/phase/backtest-loop/milestones/backtest-multi-timeframe-coverage.md`
- Phase: `agent-roadmap/phase/backtest-loop/PHASE.md`
## 상태
[승인됨]
## SDD 잠금
- 상태: 해제
- 사용자 리뷰: 없음
- 잠금 항목: 없음
## 문제 / 비목표
- 문제: ALT의 일봉 MVP 경로는 contracts, domain, storage, CLI scenario, backtest run이 닫혀 있지만 월봉과 분봉은 vocabulary, provider capability, 저장 key, backtest selector, freshness/readiness 의미가 함께 확정되어야 한다.
- 비목표:
- 여러 timeframe을 동시에 참조하는 strategy DSL 또는 factor language 설계
- tick data, order book, 실시간 streaming bar construction
- Flutter 운영 화면 구현
- 실거래 order routing 또는 broker execution 변경
- 외부 provider credential 값이나 개인 secret 문서화
## Source of Truth
| 영역       | 기준                                               | 메모                                                                          |
| -------------------| --------------------------------------------------------------------------------------------------| ---------------------------------------------------------------------------------------------------------------------------------------------------------|
| Roadmap      | `agent-roadmap/archive/phase/backtest-loop/milestones/backtest-multi-timeframe-coverage.md`   | Milestone 목표, 기능 Task, 범위 제외, 완료 후보 반영 기준                                                |
| Code       | `packages/contracts/proto/alt/v1/common.proto`                          | `Timeframe` enum 원천. 현재 `DAILY`, `MINUTE_1`, `MINUTE_5`가 있으며 월봉 추가가 필요하다.                               |
| Code       | `packages/domain/market/types.go`                                | domain `market.Timeframe` 원천. 현재 `1d`, `1m`, `5m`가 있다.                                              |
| Code       | `packages/contracts/proto/alt/v1/market.proto`, `packages/contracts/proto/alt/v1/backtest.proto` | `Bar`, `ListBarsRequest`, `BacktestRunSpec`의 timeframe 전달 계약                                            |
| Code       | `apps/cli/internal/operator/scenario.go`                             | scenario string vocabulary와 matrix validation 기준                                                   |
| Code       | `services/worker/internal/storage/postgres/queries/queries.sql`                 | `bars` key는 `(instrument_id, timeframe, timestamp)`이며 timeframe별 독립 저장 기준이다.                                |
| Code       | `services/worker/internal/storage/postgres/migrate_test.go`                   | `bars` table은 normalized OHLCV 저장 계약을 유지하며 provider raw payload를 섞지 않는다.                                |
| Code       | `services/worker/internal/backtest/bar_source.go`                        | backtest run selector가 storage에서 timeframe별 bars를 읽는 기준이다.                                          |
| External Provider | KIS                                               | provider capability와 실제 지원 timeframe을 명시적으로 accepted/rejected로 판정해야 한다.                                |
| User Decision   | `user_review_0.log`                                       | D01: 분봉 1차 import/backtest 검증 baseline은 `1m`/`5m`를 함께 포함한다. D02: 월봉 source of truth는 일봉 기반 deterministic aggregation 하나로 정한다. |
## State Machine
| 상태 | 진입 조건 | 다음 상태 | 근거 |
|------|-----------|-----------|------|
| `candidate` | monthly/daily/minute timeframe이 scenario 또는 API 입력에 등장한다. | `accepted` 또는 `rejected` | proto/domain/CLI validation, provider capability matrix |
| `rejected` | provider, market, venue, asset type, timeframe 조합이 지원되지 않는다. | terminal | typed error, stable text/JSONL rejected fixture |
| `accepted` | 조합이 capability matrix를 통과한다. | `imported` 또는 `aggregated` | provider import 또는 deterministic aggregation action |
| `imported` | provider 원천 bars가 normalized OHLCV로 저장된다. | `stored` | importer result, `bars` upsert |
| `aggregated` | 일봉 fixture 또는 저장 데이터에서 월봉 OHLCV가 결정적으로 생성된다. | `stored` | aggregation provenance, deterministic monthly fixture |
| `stored` | `bars``(instrument_id, timeframe, timestamp)` key로 저장된다. | `backtest-selectable` 또는 `readiness-checked` | storage query, list bars scenario |
| `backtest-selectable` | `BacktestRunSpec.timeframe`와 selector가 저장 bars를 찾는다. | `backtest-run-terminal` | matrix dry-run, backtest run/result fixture |
| `readiness-checked` | collection freshness가 timeframe별 latest/missing/gap/duplicate를 계산한다. | terminal | CLI text/JSONL freshness output |
| `backtest-run-terminal` | engine이 selected bars로 run을 완료한다. | terminal | deterministic result, run status/result summary |
## Interface Contract
- 계약 원문: 없음
- 입력:
- `Timeframe`: proto enum과 domain value, CLI string의 동일 의미 매핑. `daily`, `minute_1`, `minute_5` 기존 vocabulary는 호환 유지하고 `monthly`를 추가한다. 분봉 1차 import/backtest 검증 baseline에는 `minute_1``minute_5`를 함께 포함한다.
- `provider_capability`: provider, market, venue, asset type, timeframe별 accepted/rejected와 거부 사유.
- `bar_source`: daily/minute provider 원천 bars 또는 월봉 deterministic aggregation 결과. 월봉 source of truth는 일봉 기반 deterministic aggregation 하나로 둔다.
- `BacktestRunSpec.timeframe`: backtest matrix와 run selector가 사용할 단일 timeframe.
- `freshness_request`: universe, timeframe, from/to window, expected dates.
- 출력:
- `Bar`: instrument id, timeframe, timestamp, normalized OHLCV.
- `provenance`: 월봉이 어떤 일봉 입력 범위와 aggregation 규칙에서 생성되었는지 확인 가능한 근거. 1차 완료 근거는 stable CLI/JSONL 또는 fixture output에 남기고, durable metadata가 필요하면 normalized `bars` key를 깨지 않는 별도 metadata 경계를 둔다.
- `readiness`: timeframe별 latest, missing, gap, duplicate, provider delay 상태.
- `backtest_result`: timeframe별 deterministic run id, status, result summary.
- 금지:
- unsupported provider/timeframe 조합을 daily로 조용히 fallback하지 않는다.
- monthly/daily/minute bars를 같은 query나 result에서 timeframe 구분 없이 섞지 않는다.
- provider raw payload를 `bars` table의 정규화 계약에 섞지 않는다.
- 여러 timeframe을 동시에 참조하는 전략 DSL은 이번 Milestone에서 확정하지 않는다.
## Acceptance Scenarios
| ID | Milestone Task | Given | When | Then |
|----|----------------|-------|------|------|
| S01 | `timeframe-vocab` | 기존 `daily`, `minute_1`, `minute_5` vocabulary와 신규 monthly 요구가 있다. | proto/domain/CLI mapping을 검증한다. | monthly가 추가되고 기존 daily/minute compatibility가 유지된다. |
| S02 | `capability-matrix` | KIS KR/US venue와 monthly/daily/minute 후보가 있다. | capability matrix validation을 실행한다. | accepted/rejected case와 거부 사유가 명시적으로 나온다. |
| S03 | `minute-ingest` | 분봉 import 요청 또는 미지원 조합 fixture가 있다. | provider import 또는 reject path를 실행한다. | success 또는 typed rejection이 stable text/JSONL로 남는다. |
| S04 | `monthly-bars` | 동일 daily fixture가 있다. | 월봉 deterministic aggregation을 실행한다. | deterministic monthly OHLCV와 provenance가 남는다. |
| S05 | `store-query` | 동일 instrument의 monthly/daily/minute bars가 있다. | 저장 후 timeframe별 조회를 실행한다. | 각 timeframe이 독립 조회되고 서로 섞이지 않는다. |
| S06 | `run-selector` | monthly/daily/minute scenario matrix가 있다. | dry-run validation과 matrix expand를 실행한다. | timeframe별 run id와 기간 검증 결과가 생성된다. |
| S07 | `fill-policy` | monthly/daily/minute fixture bars와 동일 전략이 있다. | backtest engine fixture를 실행한다. | timeframe별 deterministic result가 나온다. |
| S08 | `freshness-readiness` | monthly/daily/minute freshness fixture가 있다. | collection freshness/readiness scenario를 실행한다. | latest/missing/gap/duplicate 상태가 timeframe별로 구분된다. |
## Evidence Map
| Scenario | Required Evidence | `agent-task` 연결 | `Spec Completion` 기대 |
|----------|-------------------|------------------|---------------------------|
| S01 | contracts/domain/CLI mapping test | `agent-task/m-backtest-multi-timeframe-coverage/...` | `timeframe-vocab` task와 monthly 추가 및 기존 daily/minute compatibility test 결과 |
| S02 | capability matrix accepted/rejected tests | `agent-task/m-backtest-multi-timeframe-coverage/...` | `capability-matrix` task와 KIS KR/US case 결과 |
| S03 | minute import success 또는 typed reject fixture | `agent-task/m-backtest-multi-timeframe-coverage/...` | `minute-ingest` task와 stable text/JSONL evidence |
| S04 | deterministic monthly OHLCV fixture와 provenance evidence | `agent-task/m-backtest-multi-timeframe-coverage/...` | `monthly-bars` task와 D02 반영 결과 |
| S05 | storage/query test for `(instrument_id, timeframe, timestamp)` isolation | `agent-task/m-backtest-multi-timeframe-coverage/...` | `store-query` task와 독립 조회 결과 |
| S06 | matrix expand and dry-run validation output | `agent-task/m-backtest-multi-timeframe-coverage/...` | `run-selector` task와 monthly/daily/minute run id evidence |
| S07 | deterministic backtest fixture results | `agent-task/m-backtest-multi-timeframe-coverage/...` | `fill-policy` task와 timeframe별 result evidence |
| S08 | freshness/readiness fixture text/JSONL | `agent-task/m-backtest-multi-timeframe-coverage/...` | `freshness-readiness` task와 timeframe별 latest/missing/gap/duplicate evidence |
## Cross-repo Dependencies
- 없음
## Drift Check
- [x] Milestone 기능 Task와 Acceptance Scenario가 일치한다.
- [x] Evidence Map이 plan/code-review/complete.log에서 검증 가능하다.
- [x] agent-contract를 쓰는 경우 SDD에 계약 원문을 복제하지 않았다.
- [x] 사용자 리뷰 항목은 `user_review_0.log`로 해결 기록을 남겼다.
## 사용자 리뷰 이력
- `user_review_0.log`: D01은 `1m`/`5m` 1차 baseline 포함으로 결정했고, D02는 월봉 source of truth를 일봉 기반 deterministic aggregation 하나로 결정했다.
## 작업 컨텍스트
- 표준선: 일봉 MVP 경로는 이미 완료된 기준선이며 multi-timeframe은 기존 daily 경로를 깨지 않고 additive하게 확장한다.
- 표준선: proto-socket과 `packages/contracts/proto`가 ALT runtime 사이의 통신 기준이다.
- 표준선: `services/api`는 얇은 control plane으로 유지하고 provider import, aggregation, backtest execution은 worker 경계에 둔다.
- 표준선: provider가 직접 제공하지 않는 timeframe은 deterministic aggregation 또는 typed rejection 중 하나로만 처리한다.
- 표준선: 월봉 source of truth는 provider 월봉 원천 데이터와 병행하지 않고 일봉 기반 deterministic aggregation 하나로 둔다.
- 표준선: 백테스트는 먼저 단일 timeframe run을 확실히 닫고 multi-timeframe composition은 후속 Milestone으로 미룬다.
- 후속 SDD: `agent-roadmap/sdd/backtest-loop/scheduled-market-data-refresh/SDD.md`