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

# Implementation Plan - REVIEW_REVIEW_KIS_US_DAILY

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

구현 완료의 마지막 단계는 active `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용, 결정, 검증 출력으로 채우는 것이다. 검증 명령은 이 plan의 계약이므로 그대로 실행하고 stdout/stderr를 review stub에 붙인다. finalization, archive, `complete.log` 작성은 code-review 전용이다. 사용자-only 결정, 사용자 소유 외부 환경/secret, 또는 범위 충돌로 막히면 active review stub의 `사용자 리뷰 요청`에 근거를 쓰고 멈춘다. 직접 사용자에게 질문하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 증거 공백은 사용자 리뷰 요청이 아니다.

## 배경

이전 follow-up은 US/NASDAQ request가 domestic KIS endpoint로 떨어지는 결함을 고쳤다. 하지만 overseas `dailyprice`는 `BYMD` 기준 역방향 응답이고, 현재 구현은 `To`만 전달한 뒤 모든 output row를 저장한다. `DailyBarRequest`와 socket import surface의 inclusive date-range 계약을 지키려면 US path도 `From`/`To` 범위 밖 bar를 반환하지 않아야 한다.

## 사용자 리뷰 요청 흐름

구현 중 차단은 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 직접 사용자에게 질문하지 않는다. 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/worker-smoke.md`
- `agent-test/local/domain-model-smoke.md`
- `agent-task/m-us-market-expansion/02+01_kis_us_daily_bars/plan_cloud_G07_1.log`
- `agent-task/m-us-market-expansion/02+01_kis_us_daily_bars/code_review_cloud_G07_1.log`
- `services/worker/internal/marketdata/importer/importer.go`
- `services/worker/internal/socket/market.go`
- `services/worker/internal/providers/kis/live_client.go`
- `services/worker/internal/providers/kis/live_client_test.go`
- `services/worker/internal/providers/kis/daily_itemchartprice.go`
- `services/worker/testdata/providers/kis/overseas_dailyprice_response.sample.json`

### 테스트 환경 규칙

`test_env=local`. worker-smoke가 적용된다. focused provider regression은 `go test -count=1 -run ... ./services/worker/internal/providers/kis`로 실행하고, worker surface confidence는 `go test -count=1 ./services/worker/...`, workspace confidence는 `bin/test`로 확인한다. live credential smoke는 범위 밖이다.

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

- `TestLiveProviderFetchesAndNormalizesOverseasDailyBars`와 `TestLiveProviderImportStoresUSDailyBars`는 overseas endpoint 사용과 US instrument/currency를 검증하지만, response에 `From` 이전 row가 있을 때 저장되지 않는지 검증하지 않는다.
- `fetchOverseasDailyBars`는 현재 `baseDate`만 받아 `From`을 필터링할 수 없다.

### 심볼 참조

Renamed/removed symbol: none. Expected changed call sites: `FetchDailyBars` -> `fetchOverseasDailyBars` signature, possible new date-range filter helper used only inside `services/worker/internal/providers/kis/live_client.go`.

### 분할 판단

이 작업은 기존 split subtask `02+01_kis_us_daily_bars`의 두 번째 review follow-up이다. Required 이슈가 하나이고 수정 범위가 `services/worker/internal/providers/kis` 내부 helper/test에 한정되어 새 subtask로 나누지 않는다.

### 범위 결정 근거

이 follow-up은 overseas dailyprice 결과의 date range 필터링만 다룬다. endpoint/TR_ID, EXCD mapping, US instrument name/asset type enrichment, shared query/backtest split은 변경하지 않는다.

### 빌드 등급

`local-G06`: 결함은 명확하고 deterministic unit test로 재현 가능하며 변경 범위가 provider helper와 test에 한정된다.

## 구현 체크리스트

- [ ] [REVIEW_REVIEW_KIS_US_DAILY-1] KIS overseas dailyprice 결과를 requested inclusive `From`/`To` local date range로 필터링하고, `From` 이전 row가 저장되지 않는 regression test를 추가한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_REVIEW_KIS_US_DAILY-1] Overseas Daily Date Range Filter

문제: [services/worker/internal/providers/kis/live_client.go](/config/workspace/alt/services/worker/internal/providers/kis/live_client.go:394)는 `from`/`to`를 둘 다 계산하지만, US branch는 [services/worker/internal/providers/kis/live_client.go](/config/workspace/alt/services/worker/internal/providers/kis/live_client.go:404)에서 `to`만 넘긴다. [services/worker/internal/providers/kis/live_client.go](/config/workspace/alt/services/worker/internal/providers/kis/live_client.go:439)는 `BYMD` 기준 응답 row 전체를 반환하므로 `DailyBarRequest`의 inclusive date-range 계약을 깬다.

해결 방법:

```go
// Before
case market.VenueNASDAQ, market.VenueNYSE:
    return p.fetchOverseasDailyBars(ctx, request.Selector.Venue, request.Selector.Symbols, to)
```

```go
// After
case market.VenueNASDAQ, market.VenueNYSE:
    return p.fetchOverseasDailyBars(ctx, request.Selector.Venue, request.Selector.Symbols, from, to)
```

`fetchOverseasDailyBars`는 `fromDate`/`toDate` 문자열을 받아 `NormalizeOverseasDailyBars` 결과를 `bar.Timestamp.Format(kisDateLayout) >= fromDate && <= toDate`로 필터링한다. `time.Time` instant 비교만 쓰면 socket/job이 UTC midnight으로 만든 request와 New York midnight bar의 offset 차이 때문에 `To` 날짜 row를 잘못 버릴 수 있으므로, provider daily-bar 의미에 맞춰 venue-local date string으로 비교한다.

수정 파일 및 체크리스트:

- [ ] `services/worker/internal/providers/kis/live_client.go`: `fetchOverseasDailyBars` signature를 `fromDate, toDate string` 포함으로 바꾸고 호출부를 갱신한다.
- [ ] `services/worker/internal/providers/kis/live_client.go`: normalized overseas bars를 inclusive date string range로 필터링하는 helper를 추가한다.
- [ ] `services/worker/internal/providers/kis/live_client_test.go`: overseas response fixture를 test 안에서 확장해 `20240527` row를 추가하고, `From=20240528`, `To=20240529` import가 2 bars만 저장함을 검증하는 regression test를 추가한다.
- [ ] 기존 `TestLiveProviderFetchesAndNormalizesOverseasDailyBars`와 `TestLiveProviderImportStoresUSDailyBars`를 유지한다.

테스트 작성: 작성한다. 새 test 이름은 `TestLiveProviderFiltersOverseasDailyBarsToRequestedRange`로 둔다. fixture 파일 자체를 바꾸기보다 test-local response body를 만들어 기존 fixture + out-of-range row를 조합한다.

중간 검증:

```bash
go test -count=1 -run 'TestLiveProvider(FiltersOverseasDailyBarsToRequestedRange|FetchesAndNormalizesOverseasDailyBars|ImportStoresUSDailyBars)' ./services/worker/internal/providers/kis
```

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `services/worker/internal/providers/kis/live_client.go` | REVIEW_REVIEW_KIS_US_DAILY-1 |
| `services/worker/internal/providers/kis/live_client_test.go` | REVIEW_REVIEW_KIS_US_DAILY-1 |

## 최종 검증

```bash
go test -count=1 -run 'TestLiveProvider(FiltersOverseasDailyBarsToRequestedRange|FetchesAndNormalizesOverseasDailyBars|ImportStoresUSDailyBars)' ./services/worker/internal/providers/kis
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
git diff --name-only -- '*.go' | xargs -r gofmt -l
```

`go test -count=1`은 fresh 실행을 요구한다. 마지막 `gofmt -l` 명령은 출력이 없어야 통과다.

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