# Milestone: Scheduled Market Data Refresh

## 위치

- Roadmap: `agent-roadmap/ROADMAP.md`
- Phase: `agent-roadmap/phase/backtest-loop/PHASE.md`

## 목표

원격 서버에서 선택 universe의 market data import를 수동 scenario 실행에만 의존하지 않고 주기적으로 실행한다. 스케줄 tick은 provider 호출, DB upsert, freshness/gap 확인, backtest input readiness까지 headless 산출물로 남겨 운영자가 현재 데이터 상태를 확인할 수 있게 한다.

## 상태

[계획]

## 승격 조건

- 없음

## 구현 잠금

- 상태: 해제
- SDD: 필요
- SDD 문서: `agent-roadmap/sdd/backtest-loop/scheduled-market-data-refresh/SDD.md`
- SDD 사유: 원격 scheduler가 외부 provider 호출, DB write, retry/idempotency, config/env, field smoke에 영향을 준다.
- 잠금 해제 조건: 아래 체크리스트
  - [x] SDD 잠금이 해제되어 있다.
  - [x] SDD 사용자 리뷰가 없거나 승인/해결되었다.
  - [x] Acceptance Scenario가 Milestone 기능 Task와 연결되어 있다.
  - [x] Evidence Map이 plan의 `Spec Targets`와 완료 시 `Spec Completion`으로 검증 가능하게 연결되어 있다.
- 결정 필요: 없음
- 결정 반영:
  - D01: scheduler는 `services/worker` 내장 loop가 1차 실행 주체다.
  - OS/systemd는 worker process supervision만 맡고, external cron은 사용하지 않는다.
  - worker scheduler는 설정된 한도 안에서 병렬 수집을 지원해야 한다.

## 범위

- 원격 worker/API runtime 위에서 실행할 scheduled market data refresh 경계. 일정 판단과 상태 관리는 worker 내부에 둔다.
- named universe별 provider, selector, timeframe, cadence, timezone, backfill window, parallelism limit 설정
- 스케줄 tick마다 `import_daily_bars` 또는 후속 import action을 실행하고 DB upsert idempotency를 유지하는 흐름
- tick 안에서 여러 수집 작업을 설정된 한도 안에서 병렬 실행하는 흐름
- provider 지연, 결측, gap, duplicate 상태를 freshness/readiness 결과로 남기는 확인 표면
- remote runner에서 migration, runtime startup, scheduler tick, freshness query, backtest input selector를 검증하는 headless smoke

## 기능

### Epic: [schedule] Scheduled refresh orchestration

원격 서버에서 선택 universe import를 주기 실행하고, 재시작/중복 실행에도 DB 상태가 안정적으로 유지되게 한다.

- [ ] [schedule-config] named universe별 provider, selector, timeframe, cadence, timezone, backfill window, parallelism limit를 선언하고 validate/dry-run에서 지원하지 않는 provider/timeframe 조합을 거부한다. 검증: schedule config validate가 next window와 rejected combination을 text/JSONL로 출력한다.
- [ ] [scheduled-runner] 원격 worker runtime에서 내장 scheduler tick이 market data import job을 실행하고, 병렬 수집과 재시작 또는 tick 중복에도 같은 bar key를 중복 저장하지 않는다. 검증: worker scheduler tick 2회 실행 후 병렬 item별 result와 `bars` key 중복 없는 freshness/readiness 출력이 남는다.
- [ ] [retry-backfill] provider 지연, 결측, gap 결과에 따라 retry와 backfill window를 계산하고 실패를 stale/error 상태로 남긴다. 검증: fixture 또는 local smoke에서 missing/gap/provider delay 케이스가 stable text/JSONL로 구분된다.

### Epic: [handoff] Headless readiness handoff

스케줄 결과를 운영자가 화면 없이 확인하고, backtest selector가 주기 적재 데이터를 사용할 수 있게 한다.

- [ ] [refresh-status] last_success, last_error, next_run, imported bar count, missing/gap/duplicate/provider_delay 상태를 CLI/JSONL로 확인할 수 있게 한다. 검증: status scenario가 success, stale, error 케이스를 구분한다.
- [ ] [backtest-readiness] scheduler가 적재한 데이터에서 backtest input selector와 result scenario가 실행될 수 있음을 원격 smoke로 검증한다. 검증: remote runner에서 scheduler tick 이후 backtest matrix 또는 result summary scenario가 exit code `0`을 기록한다.

## 완료 리뷰

- 상태: 없음
- 요청일: 없음
- 완료 근거: 없음
- 리뷰 필요:
  - [ ] 사용자가 완료 결과를 확인했다
  - [ ] archive 이동을 승인했다
- 리뷰 코멘트: 없음

## 범위 제외

- Flutter 운영 화면 구현
- 새 strategy 판단 로직 또는 투자 의사결정 알고리즘 설계
- paper/live order routing과 실거래 adapter 변경
- 월봉/분봉 provider import와 backtest engine 지원 확장 전체. 현재 scheduler는 지원되는 import/backtest timeframe을 주기 실행 대상으로 삼고, 월봉/분봉 확대는 `Backtest Multi-Timeframe Coverage`에서 다룬다.
- 외부 provider credential 값이나 개인 secret 문서화

## 작업 컨텍스트

- 관련 경로: `services/worker/`, `services/api/`, `apps/cli/`, `packages/contracts/`, `packages/domain/`, `deployments/local/`, `agent-test/local/`
- 표준선(선택): 운영 기능은 먼저 화면 없이 CLI, YAML scenario, JSONL/text output, fixture, remote smoke로 검증한다.
- 표준선(선택): scheduled import는 provider 호출과 DB upsert를 idempotent하게 유지하고, 동일 tick 재실행이 중복 row를 만들지 않아야 한다.
- 표준선(선택): scheduler config에는 secret 값을 넣지 않고, 원격 runtime env/SOPS 주입 경계를 따른다.
- 선행 작업: Backtest Data Collection Infrastructure, Backtest Scenario Automation
- 후속 작업: Backtest Multi-Timeframe Coverage, Data Quality Monitoring 후보
- 결정: scheduler는 `services/worker` 내장 loop가 1차 실행 주체다. OS/systemd는 worker process supervision만 맡고 external cron은 사용하지 않는다. worker scheduler는 설정된 한도 안에서 병렬 수집을 지원해야 한다.
