backtest-multi-timeframe-coverage와 scheduled-market-data-refresh의 SDD 및 마일스톤 문서 수정 USER_REVIEW 파일 삭제 (검증 완료로 간주)
9.9 KiB
9.9 KiB
SDD: Scheduled Market Data Refresh
위치
- Milestone:
agent-roadmap/phase/backtest-loop/milestones/scheduled-market-data-refresh.md - Phase:
agent-roadmap/phase/backtest-loop/PHASE.md
상태
[승인됨]
SDD 잠금
- 상태: 해제
- 사용자 리뷰: 없음
- 잠금 항목: 없음
문제 / 비목표
- 문제: market data import가 수동 scenario 실행에만 의존하면 원격 서버의 backtest input freshness를 반복 확인하기 어렵다. scheduler는 provider 호출, DB upsert, retry/backfill, freshness/readiness, remote smoke까지 같은 운영 경계로 묶어야 한다.
- 비목표:
- Flutter 운영 화면 구현
- 새 strategy 판단 로직 또는 투자 의사결정 알고리즘 설계
- paper/live order routing과 실거래 adapter 변경
- 월봉/분봉 provider import와 backtest engine 지원 확장 전체
- 외부 provider credential 값이나 개인 secret 문서화
Source of Truth
| 영역 | 기준 | 메모 |
|---|---|---|
| Roadmap | agent-roadmap/phase/backtest-loop/milestones/scheduled-market-data-refresh.md |
Milestone 목표, 기능 Task, 범위 제외, 완료 후보 반영 기준 |
| Code | services/worker/cmd/alt-worker/main.go |
worker runtime wiring과 job runner 등록 기준 |
| Code | services/worker/internal/config/config.go |
worker runtime env/config source of truth. scheduler config는 secret 값을 직접 보관하지 않는다. |
| Code | services/worker/internal/jobs/marketdata_jobs.go |
import_daily_bars job payload, provider capability gate, importer dispatch 기준 |
| Code | services/worker/internal/storage/postgres/queries/queries.sql |
bars upsert idempotency 기준 |
| Code | apps/cli/internal/operator/scenario.go |
scenario validation, collection_freshness, matrix/readiness smoke 기준 |
| Code | apps/cli/testdata/operator/headless_validation.md |
operator-facing headless evidence key와 scenario handoff 기준 |
| External Provider | KIS | scheduled tick이 호출할 provider이며 delay, gap, missing, typed error를 상태로 남겨야 한다. |
| User Decision | user_review_0.log |
D01: scheduler는 services/worker 내장 loop가 1차 실행 주체다. OS/systemd는 process supervision만 맡고 external cron은 사용하지 않는다. worker scheduler는 설정된 한도 안에서 병렬 수집을 지원해야 한다. |
State Machine
| 상태 | 진입 조건 | 다음 상태 | 근거 |
|---|---|---|---|
config-candidate |
schedule config가 named universe, provider, selector, timeframe, cadence, timezone, backfill window, parallelism limit를 선언한다. | config-valid 또는 config-rejected |
validate/dry-run output |
config-rejected |
필수 필드 누락 또는 provider/timeframe 조합 미지원이다. | terminal | typed validation error, stable text/JSONL |
config-valid |
schedule config가 validation을 통과한다. | scheduled |
next window calculation |
scheduled |
worker 내장 scheduler loop에서 tick 시간이 도래하거나 manual tick smoke가 실행된다. | dispatching 또는 skipped |
scheduler log/status |
dispatching |
worker scheduler가 tick lock을 획득하고 universe를 수집 작업 단위로 펼친다. | running |
bounded parallel dispatch log/status |
skipped |
동일 tick이 이미 running이거나 lock/idempotency guard가 막는다. | terminal | duplicate tick evidence |
running |
하나 이상의 import job이 설정된 병렬 한도 안에서 provider 호출과 DB upsert를 실행한다. | succeeded, stale, 또는 failed |
job result, importer result, DB state |
succeeded |
import와 freshness/readiness가 expected window를 만족한다. | ready-for-backtest |
status JSONL, freshness output |
stale |
provider delay, missing, gap이 남아 backfill/retry가 필요하다. | scheduled 또는 terminal |
retry/backfill calculation, stale status |
failed |
provider, config, DB, runtime 오류가 발생한다. | scheduled 또는 terminal |
last_error, error status |
ready-for-backtest |
scheduler가 적재한 데이터로 selector가 bars를 찾는다. | terminal | backtest matrix/result smoke exit code 0 |
Interface Contract
- 계약 원문: 없음
- 입력:
schedule_config: named universe별 provider, selector, timeframe, cadence, timezone, backfill window, parallelism limit.tick: worker 내장 scheduler loop 또는 manual smoke로 실행되는 refresh trigger. 1차 구현에서 external cron은 사용하지 않는다.provider_capability: provider, market, venue, timeframe별 accepted/rejected 판정.runtime_env: provider credential과 DB URL은 config 파일에 쓰지 않고 원격 runtime env/SOPS 주입 경계에서 제공한다.freshness_window: latest, expected dates, missing/gap/duplicate/provider delay를 계산할 기간.
- 출력:
refresh_status: last_success, last_error, next_run, imported bar count, missing/gap/duplicate/provider_delay.job_result: import job success/stale/error와 retry/backfill decision, parallel dispatch item별 result.readiness: backtest selector가 scheduled import 결과를 사용할 수 있는지에 대한 headless result.remote_smoke: migration, runtime startup, tick, freshness query, backtest selector/result scenario exit code.
- 금지:
- external cron을 1차 scheduler 실행 주체로 사용하지 않는다.
- OS/systemd timer가 schedule state나 tick 판단을 소유하지 않는다. OS/systemd는 worker process supervision까지만 맡는다.
- schedule config에 secret 값을 저장하지 않는다.
- 동일 tick 재실행이 duplicate
barsrow를 만들지 않는다. - provider delay나 gap을 성공으로 조용히 숨기지 않는다.
- scheduler가 Flutter 화면 구현을 전제하지 않는다.
- 월봉/분봉 import 확장은 이 Milestone에서 새로 확정하지 않고 지원되는 timeframe만 대상으로 삼는다.
Acceptance Scenarios
| ID | Milestone Task | Given | When | Then |
|---|---|---|---|---|
| S01 | schedule-config |
named universe별 provider, selector, timeframe, cadence, timezone, backfill window가 있다. | validate/dry-run을 실행한다. | next window와 rejected combination이 text/JSONL로 출력된다. |
| S02 | scheduled-runner |
원격 worker runtime, idempotent bars upsert, 병렬 수집 대상이 있다. |
worker 내장 scheduler tick을 2회 실행하고 병렬 수집을 수행한다. | external cron 없이 duplicate row 없이 item별 job result와 freshness/readiness output이 남는다. |
| S03 | retry-backfill |
provider delay, missing, gap fixture가 있다. | retry/backfill calculation을 실행한다. | stale/error 상태와 backfill window가 stable text/JSONL로 구분된다. |
| S04 | refresh-status |
success, stale, error 상태가 있다. | status scenario를 실행한다. | last_success, last_error, next_run, bar count, missing/gap/duplicate/provider_delay가 구분된다. |
| S05 | backtest-readiness |
scheduler tick이 bars를 적재했다. | remote runner에서 backtest matrix 또는 result summary scenario를 실행한다. | exit code 0과 result/readiness evidence가 남는다. |
Evidence Map
| Scenario | Required Evidence | agent-task 연결 |
Spec Completion 기대 |
|---|---|---|---|
| S01 | schedule config validate test와 dry-run text/JSONL | agent-task/m-scheduled-market-data-refresh/... |
schedule-config task와 next window/rejected combination evidence |
| S02 | worker 내장 scheduler tick smoke, bounded parallel dispatch output, bars key 중복 없음, freshness/readiness output |
agent-task/m-scheduled-market-data-refresh/... |
scheduled-runner task와 2회 tick idempotency 및 병렬 수집 evidence |
| S03 | missing/gap/provider delay fixture 또는 local smoke | agent-task/m-scheduled-market-data-refresh/... |
retry-backfill task와 stale/error/backfill evidence |
| S04 | success/stale/error status scenario expected JSONL | agent-task/m-scheduled-market-data-refresh/... |
refresh-status task와 status field evidence |
| S05 | remote runner smoke after scheduler tick | agent-task/m-scheduled-market-data-refresh/... |
backtest-readiness task와 exit code 0 evidence |
Cross-repo Dependencies
- 없음
Drift Check
- Milestone 기능 Task와 Acceptance Scenario가 일치한다.
- Evidence Map이 plan/code-review/complete.log에서 검증 가능하다.
- agent-contract를 쓰는 경우 SDD에 계약 원문을 복제하지 않았다.
- 사용자 리뷰 항목은
user_review_0.log로 해결 기록을 남겼다.
사용자 리뷰 이력
user_review_0.log: D01은services/worker내장 scheduler loop를 1차 실행 주체로 결정했고, external cron은 사용하지 않으며, worker scheduler는 설정된 한도 안에서 병렬 수집을 지원해야 한다.
작업 컨텍스트
- 표준선: 운영 기능은 먼저 화면 없이 CLI, YAML scenario, JSONL/text output, fixture, remote smoke로 검증한다.
- 표준선: scheduled import는 provider 호출과 DB upsert를 idempotent하게 유지하고 동일 tick 재실행이 중복 row를 만들지 않아야 한다.
- 표준선:
services/worker가 데이터 수집, 정규화, backtest, scheduled job처럼 오래 걸리거나 비동기적인 일을 담당한다. - 표준선: scheduler의 일정 판단과 상태 관리는 worker 내부에 둔다. OS/systemd는 worker process supervision만 맡고 external cron은 쓰지 않는다.
- 표준선: worker scheduler는 provider/API 부하를 제어할 수 있는 bounded parallelism으로 여러 수집 작업을 병렬 실행할 수 있어야 한다.
- 표준선: scheduler config에는 secret 값을 넣지 않고 원격 runtime env/SOPS 주입 경계를 따른다.
- 표준선:
services/api는 client-facing 요청을 worker 실행/조회 경계로 중계하는 control plane으로 남긴다. - 후속 SDD: 없음