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

# Code Review Reference - KIS_US_DAILY

> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> Complete implementation-owned sections, then stop with active files in place and report ready for review. Do not finalize, archive, write `complete.log`, ask the user directly, or call `request_user_input`.

## 개요

date=2026-06-06
task=m-us-market-expansion/02+01_kis_us_daily_bars, plan=0, tag=KIS_US_DAILY

## 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

## 이 파일을 읽는 리뷰 에이전트에게

> **[REVIEW AGENT ONLY]** 종결 절차는 코드리뷰 에이전트 전용이다.

각 항목의 구현을 실제 소스 파일과 대조하고 검증 출력이 코드와 일치하는지 확인한다. PASS이면 완료 이벤트 메타데이터만 보고하고 roadmap 수정은 런타임 책임이다.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [KIS_US_DAILY-1] Provider Capability Gate | [x] |
| [KIS_US_DAILY-2] KIS Overseas Daily Normalization | [x] |
| [KIS_US_DAILY-3] Worker Import Path For US Watchlists | [x] |

## 구현 체크리스트

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

## 코드리뷰 전용 체크리스트

> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.

- [x] 판정을 append한다.
- [x] active review/plan 파일을 `*.log`로 아카이브한다.
- [x] `.gitignore` Agent-Ops block을 확인한다.
- [ ] PASS이면 `complete.log`를 작성하고 task directory를 archive로 이동한다.
- [ ] PASS이면 완료 이벤트 메타데이터를 보고한다.
- [x] WARN/FAIL이면 다음 active plan/review 또는 `USER_REVIEW.md`를 작성한다.

## 계획 대비 변경 사항

- Capability 어휘를 새 `capability` 패키지 대신 `packages/domain/market/types.go`에 `market.ProviderCapability`로 추가했다. 근거: 공통 규칙 "새 파일 생성보다 기존 파일 수정 우선" + domain이 이미 `GetVenueMetadata`로 venue→market/timezone vocabulary를 소유하므로, transport/persistence-free 순수 매처는 domain altitude에 맞는다.
- 계획 pseudo-code의 `capability.RequireDailyBars(provider, selector)`를 `market.ProviderCapability`의 메서드로 구현했다(`cap.RequireDailyBars(provider, selector)`). `RegisterDailyBarImportHandler`의 `expectedProvider market.Provider` 파라미터를 `capability market.ProviderCapability`로 교체했고, `main.go` 와이어링을 `kis.Capability()`로 갱신했다.
- Capability gate는 venue를 필수로 본다. KIS US daily import는 venue로 overseas exchange(NAS/NYS)를 해석해야 하므로 venue 없는 daily import는 거절한다. 이에 맞춰 기존 jobs 테스트의 venue 없는 payload(override/mismatch 케이스)에 `venue`/`market`을 추가했다(테스트 의도는 유지).
- overseas endpoint는 계획 후보(`.../dailyprice`, `HHDFS76240000`)를 로컬 KIS 캐시 `examples_llm/overseas_stock/dailyprice`로 대조해 확정했다. `inquire-daily-chartprice`(FHKST03030100)는 지수 구성종목만 조회 가능하다는 공식 주석 때문에 개별 US 종목(AAPL/SPY)에는 `dailyprice`를 선택했다. 코드는 fixture assertion으로 endpoint/TR_ID/field(xymd, clos)를 잠근다.
- LiveProvider의 실제 overseas REST 호출 와이어링은 구현하지 않았다(plan 범위 밖: live credential smoke, 후속 split). KIS_US_DAILY-2는 fixture decoder/normalizer까지, KIS_US_DAILY-3은 fake provider/store 기반 테스트까지만 다룬다.

## 주요 설계 결정

- `market.ProviderCapability{Provider, Venues, Timeframes}` + `SupportsDailyBars(venue)` + `RequireDailyBars(provider, selector)`로 provider≠market 경계를 표현했다. KIS는 KRX/NASDAQ/NYSE를 한 capability에 선언하고, KR-only 후속 provider(예: Kiwoom)는 좁은 capability로 US를 자동 거절한다(코드 추가 없이 test fixture로 검증).
- overseas timestamp는 instrument venue metadata의 timezone을 사용한다(`venueLocation` helper). KRX는 tzdata 비의존 고정 +09:00, US venue는 `time.LoadLocation("America/New_York")`. 재현성을 위해 kis 패키지에 `_ "time/tzdata"` blank import를 추가해 host zoneinfo 유무와 무관하게 EDT/EST가 결정적으로 해석되게 했다. price currency는 instrument currency(USD)를 사용한다.
- domestic `NormalizeDailyBars`는 그대로 두고 `NormalizeOverseasDailyBars`를 분리 추가했다(KR fixture/테스트 무손상). 응답 envelope는 output1/output2로 동일하지만 OHLCV 필드명(xymd/clos/tvol)이 달라 별도 struct/decoder가 필요했다.
- importer/store는 in-memory store가 instrument struct 전체를 보존하므로 `AssetType`(equity/etf)이 instrument-first→bars-after upsert 경로에서 유지됨을 테스트로 잠갔다.

## 사용자 리뷰 요청

- 상태: 없음
- 사유 유형: 없음
- 결정 필요: 없음
- 차단 근거: 없음
- 실행한 검증/명령: 없음
- 자동 후속 불가 이유: 없음
- 재개 조건: 없음

## 리뷰어를 위한 체크포인트

- provider capability가 `provider != market` 원칙을 지키는지 확인한다.
- KIS US fixture가 endpoint/TR_ID/field mapping을 test로 잠그는지 확인한다.
- KR existing daily import tests가 계속 통과하는지 확인한다.
- live secret 없이 deterministic fixture/unit 검증만으로 PASS를 주장하는지 확인한다.

## 검증 결과

### KIS_US_DAILY-1 중간 검증
```text
$ go test -count=1 ./packages/domain/... ./services/worker/internal/jobs
ok  	git.toki-labs.com/toki/alt/packages/domain/backtest	0.002s
ok  	git.toki-labs.com/toki/alt/packages/domain/market	0.002s
ok  	git.toki-labs.com/toki/alt/services/worker/internal/jobs	0.002s
```

### KIS_US_DAILY-2 중간 검증
```text
$ go test -count=1 ./services/worker/internal/providers/kis
ok  	git.toki-labs.com/toki/alt/services/worker/internal/providers/kis	0.008s
```

### KIS_US_DAILY-3 중간 검증
```text
$ go test -count=1 ./services/worker/internal/socket ./services/worker/internal/marketdata/importer ./services/worker/internal/jobs
ok  	git.toki-labs.com/toki/alt/services/worker/internal/socket	0.060s
ok  	git.toki-labs.com/toki/alt/services/worker/internal/marketdata/importer	0.002s
ok  	git.toki-labs.com/toki/alt/services/worker/internal/jobs	0.002s
```

### 최종 검증
```text
$ go test -count=1 ./packages/domain/...
ok  	git.toki-labs.com/toki/alt/packages/domain/backtest	0.002s
ok  	git.toki-labs.com/toki/alt/packages/domain/market	0.002s

$ go test -count=1 ./services/worker/internal/providers/kis ./services/worker/internal/marketdata/importer ./services/worker/internal/jobs ./services/worker/internal/socket
ok  	git.toki-labs.com/toki/alt/services/worker/internal/providers/kis	0.009s
ok  	git.toki-labs.com/toki/alt/services/worker/internal/marketdata/importer	0.003s
ok  	git.toki-labs.com/toki/alt/services/worker/internal/jobs	0.003s
ok  	git.toki-labs.com/toki/alt/services/worker/internal/socket	0.059s

$ go test -count=1 ./services/worker/...
ok  	git.toki-labs.com/toki/alt/services/worker/cmd/alt-worker	0.014s
?   	git.toki-labs.com/toki/alt/services/worker/cmd/alt-worker-data-check	[no test files]
ok  	git.toki-labs.com/toki/alt/services/worker/cmd/alt-worker-migrate	0.003s
ok  	git.toki-labs.com/toki/alt/services/worker/internal/backtest	0.014s
ok  	git.toki-labs.com/toki/alt/services/worker/internal/config	0.002s
ok  	git.toki-labs.com/toki/alt/services/worker/internal/contracts	0.015s
ok  	git.toki-labs.com/toki/alt/services/worker/internal/jobs	0.012s
ok  	git.toki-labs.com/toki/alt/services/worker/internal/marketdata/datacheck	0.013s
ok  	git.toki-labs.com/toki/alt/services/worker/internal/marketdata/importer	0.013s
ok  	git.toki-labs.com/toki/alt/services/worker/internal/papertrading	0.004s
ok  	git.toki-labs.com/toki/alt/services/worker/internal/providers/kis	0.023s
ok  	git.toki-labs.com/toki/alt/services/worker/internal/rediskeys	0.012s
ok  	git.toki-labs.com/toki/alt/services/worker/internal/socket	0.061s
?   	git.toki-labs.com/toki/alt/services/worker/internal/storage	[no test files]
ok  	git.toki-labs.com/toki/alt/services/worker/internal/storage/postgres	0.066s
?   	git.toki-labs.com/toki/alt/services/worker/internal/storage/postgres/sqlc	[no test files]

$ bin/test
... (contracts-check + go test for contracts/domain/api/worker/cli modules + flutter test)
00:02 +60: All tests passed!

$ git diff --check
OK: no whitespace errors (no output)
```

### 참고: gofmt
```text
$ gofmt -l <변경 패키지들>
services/worker/internal/socket/paper_test.go
```
`paper_test.go`는 이번 변경 범위 밖의 기존 미포맷 파일이며 diff에 포함되지 않는다. 공통 규칙(기존 변경 되돌리지 않음, 요청 범위 밖 변경 금지)에 따라 손대지 않았다. 변경한 Go 파일은 모두 gofmt clean이다.

---

> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section?**

## 코드리뷰 결과

- 종합 판정: FAIL
- 차원별 평가:
  - correctness: Fail
  - completeness: Fail
  - test coverage: Fail
  - API contract: Fail
  - code quality: Pass
  - plan deviation: Fail
  - verification trust: Pass
- 발견된 문제:
  - Required: [services/worker/internal/providers/kis/live_client.go](/config/workspace/alt/services/worker/internal/providers/kis/live_client.go:306) still routes every `LiveProvider.FetchDailyBars` request through the domestic `FetchDailyItemChartPrice` endpoint and [services/worker/internal/providers/kis/live_client.go](/config/workspace/alt/services/worker/internal/providers/kis/live_client.go:315) normalizes with the KR domestic normalizer. Because [services/worker/internal/providers/kis/daily_itemchartprice.go](/config/workspace/alt/services/worker/internal/providers/kis/daily_itemchartprice.go:32) advertises NASDAQ/NYSE support and [services/worker/cmd/alt-worker/main.go](/config/workspace/alt/services/worker/cmd/alt-worker/main.go:82) wires that capability into the real worker, a US import job is accepted but then fetched as a domestic KIS request and stored as a `KRX:*`/KRW instrument via [services/worker/internal/providers/kis/live_client.go](/config/workspace/alt/services/worker/internal/providers/kis/live_client.go:324). Fix by either routing NASDAQ/NYSE requests through the overseas `dailyprice` endpoint plus `NormalizeOverseasDailyBars`, with a regression test proving `AAPL` hits `OverseasDailyPricePath` and yields `NASDAQ:AAPL`/USD bars, or by not advertising US venues in the live worker capability until that provider path exists.
- 리뷰어 검증:
  - `go test -count=1 ./packages/domain/...` PASS
  - `go test -count=1 ./services/worker/internal/providers/kis ./services/worker/internal/marketdata/importer ./services/worker/internal/jobs ./services/worker/internal/socket` PASS
  - `go test -count=1 ./services/worker/...` PASS
  - `bin/test` PASS
  - `git diff --check` PASS
  - `git diff --name-only -- '*.go' | xargs -r gofmt -l` PASS, no output
- 다음 단계: FAIL 후속 계획을 작성해 live KIS US daily provider path와 regression coverage를 완성한다.
