<!-- task=m-us-market-expansion/02+01_kis_us_daily_bars plan=0 tag=KIS_US_DAILY -->

# Implementation Plan - KIS_US_DAILY

## 이 파일을 읽는 구현 에이전트에게

구현 완료의 마지막 단계는 active `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용, 결정, 검증 출력으로 채우는 것이다. `01_market_foundation`의 `complete.log`가 없으면 구현을 시작하지 말고 active review stub에 blocker를 기록한다. 사용자-only 결정이 필요하면 review stub의 `사용자 리뷰 요청` 섹션에 근거를 쓰고 멈춘다. 직접 사용자에게 질문하지 않는다. finalization은 code-review 전용이다.

## 배경

초기 US data provider는 KIS로 고정됐지만, provider가 곧 market을 뜻하지 않도록 capability boundary를 유지해야 한다. 현재 KIS daily chart code는 domestic endpoint와 KST timestamp 전제만 갖는다. 이 작업은 KIS의 US daily bar path를 fixture-first로 추가하고, provider capability가 KR/US 지원 범위를 명시하게 한다.

## 사용자 리뷰 요청 흐름

구현 중 차단은 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 후속 에이전트가 docs 확인, fixture 수집, 명령 재실행으로 해결할 수 있는 증거 공백은 사용자 리뷰 요청이 아니다. code-review가 실제 `USER_REVIEW.md` 작성 여부를 결정한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/trading-expansion/milestones/us-market-expansion.md`
- Task ids:
  - `us-daily-bars`: KIS를 초기 provider로 사용해 미국장 일봉 데이터를 normalized bar model로 저장
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-test/local/rules.md`
- `agent-test/local/domain-model-smoke.md`
- `agent-test/local/worker-smoke.md`
- `packages/domain/market/types.go`
- `services/worker/internal/providers/kis/daily_itemchartprice.go`
- `services/worker/internal/providers/kis/daily_itemchartprice_test.go`
- `services/worker/internal/marketdata/importer/importer.go`
- `services/worker/internal/marketdata/importer/importer_test.go`
- `services/worker/internal/jobs/marketdata_jobs.go`
- `services/worker/internal/jobs/marketdata_jobs_test.go`
- `services/worker/internal/socket/market.go`
- `services/worker/internal/socket/market_test.go`
- `services/worker/testdata/providers/kis/daily_itemchartprice_request.sample.json`
- `services/worker/testdata/providers/kis/daily_itemchartprice_response.sample.json`

### 테스트 환경 규칙

`test_env=local`. worker-smoke와 domain-model-smoke를 적용한다. worker 변경의 기본 검증은 `go test ./services/worker/...`, provider/importer/job/socket focused 검증은 `go test -count=1`로 실행한다. live KIS credential이 필요한 검증은 범위 밖이며 tracked file에 secret을 쓰지 않는다.

### 테스트 커버리지 공백

- KIS normalizer는 domestic response fixture만 검증한다.
- importer/job/socket은 provider/selector/date pass-through는 검증하지만 provider capability로 market/venue 지원 여부를 검증하지 않는다.
- US daily bars가 USD/NASDAQ timestamp/currency로 저장되는 integration path가 없다.

### 심볼 참조

Renamed/removed symbol: none. New symbols expected: KIS overseas daily request/response structs, KIS daily capability declaration, capability validation helper.

### 분할 판단

Shared task group: `m-us-market-expansion`. This subtask is `02+01_kis_us_daily_bars` and depends on `01_market_foundation`. Predecessor status at plan creation: missing active/archived `complete.log`; implementation must wait for `agent-task/m-us-market-expansion/01_market_foundation/complete.log` or matching archived complete log.

### 범위 결정 근거

이 작업은 KIS US daily bars와 provider capability만 다룬다. shared query fixtures와 backtest smoke는 후속 split에서 다룬다. live credential smoke와 real REST 호출은 private/env 준비가 필요하므로 fixture/unit 중심으로 제한한다.

### 빌드 등급

`cloud-G07`: external provider shape, capability boundary, worker import pipeline이 연결되어 있고 KIS API fixture 해석 리스크가 있다.

## 구현 체크리스트

- [ ] [KIS_US_DAILY-1] provider capability를 도입해 KIS가 KR/US daily bars 지원 범위를 선언하고 unsupported market/venue 조합을 import 전에 거절한다. 검증: 한국장 pipeline을 깨지 않고 provider capability가 미국장 daily bar import를 허용한다.
- [ ] [KIS_US_DAILY-2] KIS US daily bar fixture decoder/normalizer를 추가해 USD/NASDAQ/NYSE bar를 normalized model로 만든다.
- [ ] [KIS_US_DAILY-3] worker socket/job/importer 경로가 US watchlist import request를 KIS provider로 전달하고 instrument/bar upsert count를 보존한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 의존 관계 및 구현 순서

`02+01_kis_us_daily_bars`는 디렉터리명 기준으로 `01_market_foundation`의 `complete.log`가 필요하다. 추가 hidden dependency를 만들지 않는다.

### [KIS_US_DAILY-1] Provider Capability Gate

문제: [services/worker/internal/jobs/marketdata_jobs.go](/config/workspace/alt/services/worker/internal/jobs/marketdata_jobs.go:106)은 `expectedProvider` 문자열만 비교하고, provider가 특정 market/venue/daily-bars capability를 지원하는지 확인하지 않는다. Kiwoom 같은 후속 provider가 KR만 지원해도 같은 boundary에 붙일 방법이 약하다.

해결 방법:

```go
// Before services/worker/internal/jobs/marketdata_jobs.go:112
if market.Provider(p.Provider) != expectedProvider {
    return fmt.Errorf("daily bar import payload: provider %q does not match expected %q", p.Provider, expectedProvider)
}
```

```go
// After
if err := capability.RequireDailyBars(market.Provider(p.Provider), p.selector()); err != nil {
    return err
}
```

수정 파일 및 체크리스트:

- [ ] `packages/domain/market/types.go` 또는 worker provider package: provider capability vocabulary와 matcher 추가.
- [ ] `services/worker/internal/providers/kis/*`: KIS capability가 KR/KRX와 US/NASDAQ/NYSE daily bars를 지원한다고 선언.
- [ ] `services/worker/internal/jobs/marketdata_jobs.go`: provider mismatch와 unsupported market/venue/capability를 분리해 validation.
- [ ] `services/worker/internal/jobs/marketdata_jobs_test.go`: KIS US 허용, unsupported provider/venue 거절, KR 기존 payload 유지 test 추가.

테스트 작성: 작성한다. Kiwoom 구현은 하지 않지만 test fixture에서 KR-only fake capability가 US를 거절하는 사례를 둔다.

중간 검증:

```bash
go test -count=1 ./packages/domain/... ./services/worker/internal/jobs
```

### [KIS_US_DAILY-2] KIS Overseas Daily Normalization

문제: [services/worker/internal/providers/kis/daily_itemchartprice.go](/config/workspace/alt/services/worker/internal/providers/kis/daily_itemchartprice.go:14)는 domestic endpoint/TR_ID만 갖고, [lines 89-97](/config/workspace/alt/services/worker/internal/providers/kis/daily_itemchartprice.go:89)는 KST/KRW 전제를 적용한다.

해결 방법: fixture-first로 overseas daily request/response structs를 추가한다. endpoint/TR_ID는 구현 시 official KIS docs 또는 captured fixture와 대조해 확정한다. 검색 후보는 `/uapi/overseas-price/v1/quotations/dailyprice`, `HHDFS76240000`이지만, 코드에는 fixture assertion으로 잠근다. timestamp는 domain venue metadata의 timezone을 사용하고 price currency는 instrument currency를 사용한다.

수정 파일 및 체크리스트:

- [ ] `services/worker/internal/providers/kis/daily_itemchartprice.go`: domestic/overseas decoder와 normalizer를 분리하거나 공통 `NormalizeDailyBars`가 venue metadata를 사용하도록 확장.
- [ ] `services/worker/testdata/providers/kis/*`: US daily request/response/expected normalized fixture 추가.
- [ ] `services/worker/internal/providers/kis/daily_itemchartprice_test.go`: USD/NASDAQ/NYSE fixture decode와 normalized bar assertion 추가.

테스트 작성: 작성한다. AAPL 또는 SPY fixture로 `Instrument.ID=NASDAQ:AAPL`, `Currency=USD`, `AssetType` 보존, timestamp timezone을 검증한다.

중간 검증:

```bash
go test -count=1 ./services/worker/internal/providers/kis
```

### [KIS_US_DAILY-3] Worker Import Path For US Watchlists

문제: [services/worker/internal/socket/market.go](/config/workspace/alt/services/worker/internal/socket/market.go:69)은 US enum을 변환할 수 있지만 tests의 `validImportRequest()`는 KR/KRX만 다룬다. importer tests도 KR sample data 중심이다.

해결 방법: US watchlist request를 socket/job/importer tests에 추가한다. provider가 US instrument와 USD bars를 반환하면 importer가 instrument first, bars after 순서로 storage upsert count를 보존해야 한다.

수정 파일 및 체크리스트:

- [ ] `services/worker/internal/socket/market_test.go`: `Market_US` + `Venue_NASDAQ` import conversion test 추가.
- [ ] `services/worker/internal/marketdata/importer/importer_test.go`: US sample bars와 asset_type persistence path 추가.
- [ ] `services/worker/internal/jobs/marketdata_jobs_test.go`: payload `market=US`, `venue=NASDAQ`, `symbols=["AAPL"]` dispatch test 추가.

테스트 작성: 작성한다. live provider 호출 없이 fake provider/store로 deterministic count와 request conversion을 검증한다.

중간 검증:

```bash
go test -count=1 ./services/worker/internal/socket ./services/worker/internal/marketdata/importer ./services/worker/internal/jobs
```

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `packages/domain/market/types.go` | KIS_US_DAILY-1 |
| `services/worker/internal/providers/kis/daily_itemchartprice.go` | KIS_US_DAILY-1, KIS_US_DAILY-2 |
| `services/worker/internal/providers/kis/daily_itemchartprice_test.go` | KIS_US_DAILY-2 |
| `services/worker/testdata/providers/kis/*` | KIS_US_DAILY-2 |
| `services/worker/internal/jobs/marketdata_jobs.go` | KIS_US_DAILY-1 |
| `services/worker/internal/jobs/marketdata_jobs_test.go` | KIS_US_DAILY-1, KIS_US_DAILY-3 |
| `services/worker/internal/socket/market_test.go` | KIS_US_DAILY-3 |
| `services/worker/internal/marketdata/importer/importer_test.go` | KIS_US_DAILY-3 |

## 최종 검증

```bash
go test -count=1 ./packages/domain/...
go test -count=1 ./services/worker/internal/providers/kis ./services/worker/internal/marketdata/importer ./services/worker/internal/jobs ./services/worker/internal/socket
go test -count=1 ./services/worker/...
bin/test
git diff --check
```

`go test` cache는 허용하지 않는다. live KIS credential 검증은 이 plan의 pass condition이 아니다.

모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
