diff --git a/README.md b/README.md index dbc69c0..0624a79 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ALT -ALT는 개인용 quant system workspace다. 한국장 일봉 기반 MVP에서 시작해, 장기적으로 미국장, 백테스트, 데이터 정규화, 페이퍼 트레이딩, 포트폴리오/리스크 관리, Flutter 클라이언트까지 확장할 수 있는 운영형 구조를 목표로 한다. +ALT는 개인용 quant system workspace다. 주식형 instrument의 venue-agnostic 일봉 데이터 기반에서 시작해, 장기적으로 여러 주식시장, 백테스트, 데이터 정규화, 페이퍼 트레이딩, 포트폴리오/리스크 관리, Flutter 클라이언트까지 확장할 수 있는 운영형 구조를 목표로 한다. Go 서비스와 도구는 루트 `go.work`로 묶고, Flutter가 web/mobile/desktop 클라이언트 표면을 담당한다. 실시간 통신은 sibling workspace의 `../proto-socket/go`를 transport로 사용하며, ALT 애플리케이션 계약은 `packages/contracts/proto`의 protobuf schema를 원천으로 둔다. @@ -15,6 +15,7 @@ Go 서비스와 도구는 루트 `go.work`로 묶고, Flutter가 web/mobile/desk - `packages/domain`에는 market/backtest vocabulary와 value object skeleton이 있다. - PostgreSQL 17과 Redis 7 local stack은 `deployments/local/docker-compose.yml`에 있다. - 로드맵 진입점은 `agent-roadmap/current.md`다. 일반 작업에서는 전체 `ROADMAP.md`를 매번 읽지 않고, 활성 Milestone 문서를 선택해 읽는다. +- market data의 현재 core 범위는 주식/ETF/지수처럼 주식시장 개념을 공유하는 instrument다. Crypto는 거래 구조가 달라 core 주식 모델에 넣지 않고, 향후 별도 asset-class domain에서 공유 가능한 일부 파이프라인만 재사용할지 검토한다. ## 빠른 시작 @@ -87,6 +88,8 @@ AI agent는 작업 전에 루트 지침과 관련 domain rule을 먼저 확인 - `proto-socket`은 transport layer다. ALT application message는 `packages/contracts/proto`에 둔다. - `packages/domain`은 transport나 persistence detail에 의존하지 않는다. +- market domain은 한국장/KIS 필드에 직접 종속되지 않는 global equity venue-agnostic vocabulary를 우선한다. +- 장별 특수값은 canonical column으로 끌어올리기보다 provider adapter, venue metadata, raw payload, 또는 별도 확장 metadata에 격리한다. - `services/api`는 socket/session boundary를 담당한다. - `services/worker`는 오래 걸리거나 비동기적인 데이터/백테스트 작업을 담당한다. - `apps/client`는 Flutter feature-first 구조를 사용한다. @@ -98,6 +101,8 @@ AI agent는 작업 전에 루트 지침과 관련 domain rule을 먼저 확인 - protobuf generated output은 source schema에서 생성하고 손으로 편집하지 않는다. - 새 Go module을 추가하면 `go.work`에 등록한다. - 로컬 secret이나 개인 설정 파일은 커밋하지 않는다. +- KIS 같은 provider credential은 실제 KIS smoke/live 단계에서만 필요하며, 그때도 `.env` 파일에 저장하지 않고 1Password CLI 또는 service account를 통해 실행 시점에 주입한다. +- 현재 KIS adapter mapping 참조는 로컬 cache의 공식 API 샘플 repo를 우선한다. Credential 없는 mock 검증용 샘플은 `services/worker/testdata/providers/kis/` 아래에 secret-free JSON fixture로 둔다. ## 환경 변수 @@ -111,10 +116,26 @@ AI agent는 작업 전에 루트 지침과 관련 domain rule을 먼저 확인 | `DATABASE_URL` | worker에서 사용할 PostgreSQL URL. local fallback 있음. | 아니오 | | `REDIS_URL` | worker에서 사용할 Redis URL. local fallback 있음. | 아니오 | +## Secret 관리 + +ALT의 provider credential은 실제 provider 호출 단계에서 1Password를 기본 secret source로 둔다. 현재 mock/fixture 테스트 단계에서는 KIS credential이 필요하지 않다. 실제 KIS smoke가 필요해지면 로컬 개발에서는 `op run -- ` 형태로 필요한 process에만 값을 주입하고, 자동화 환경에서는 개인 계정이 아닌 1Password service account를 사용한다. + +Tracked 문서와 fixture에는 실제 key, token, account number, secret reference를 남기지 않는다. 환경별 secret item 이름, vault 이름, 계정별 운영 메모가 필요하면 git 추적 대상이 아닌 `agent-ops/rules/private/`에 둔다. + +## Provider Reference and Fixture + +KIS API 참조는 공식 샘플 repo의 로컬 cache를 우선한다. cache 위치와 갱신 방법은 git 추적 제외 private rule인 `agent-ops/rules/private/kis-api-local-cache.md`에 둔다. + +Mock 테스트와 adapter mapping에 필요한 샘플은 `services/worker/testdata/providers/kis/` 아래에 secret-free JSON fixture로 둔다. 현재는 Postman export를 필수 입력으로 보지 않는다. + +SQLite 보관은 샘플 수가 많아져 검색, 인덱싱, 비교 쿼리가 실제로 필요해진 뒤 검토한다. 초기 adapter mapping과 테스트 fixture에는 diff 가능한 JSON이 기본이다. + ## 참고 문서 - `apps/client/README.md` - `packages/contracts/README.md` +- `services/worker/README.md` +- `services/worker/testdata/providers/kis/README.md` - `agent-ops/rules/project/rules.md` - `agent-roadmap/current.md` - `agent-ops/skills/common/create-roadmap/SKILL.md` diff --git a/agent-roadmap/phase/data-foundation/milestones/korea-daily-data-foundation.md b/agent-roadmap/phase/data-foundation/milestones/korea-daily-data-foundation.md index 85db8bb..1cbdd77 100644 --- a/agent-roadmap/phase/data-foundation/milestones/korea-daily-data-foundation.md +++ b/agent-roadmap/phase/data-foundation/milestones/korea-daily-data-foundation.md @@ -7,7 +7,7 @@ ## 목표 -한국장 일봉 데이터를 첫 MVP market data로 삼아 provider-neutral import, normalization, persistence 파이프라인을 만든다. 한투/KIS는 초기 provider 후보로 보되, domain model은 한국장에만 종속되지 않게 유지한다. +주식형 instrument의 일봉 데이터를 첫 MVP market data로 삼아 provider-neutral import, normalization, persistence 파이프라인을 만든다. 한국장/KIS는 첫 provider와 venue 검증 단위로 보되, domain model은 특정 장이나 KIS 응답 필드에 종속되지 않게 유지한다. Crypto는 현재 core 주식 모델 범위에서 제외하고, 향후 별도 asset-class domain에서 공유 가능한 일부 파이프라인만 재사용할지 검토한다. ## 상태 @@ -15,45 +15,49 @@ ## 구현 잠금 -- 상태: 잠금 -- 결정 필요: 아래 체크리스트 - - [ ] MVP에서 우선 수집할 한국장 universe를 watchlist, KOSPI/KOSDAQ 전체, 또는 특정 섹터 중 무엇으로 둘지 결정한다. - - [ ] KIS credential과 계정 환경을 local secret/env 기준으로 어떻게 제공할지 결정한다. +- 상태: 해제 +- 결정 필요: 없음 +- 확정된 기준: + - 주식/ETF/지수처럼 주식시장 개념을 공유하는 instrument를 venue-agnostic core 대상으로 둔다. + - Crypto는 현재 core 주식 모델에 포함하지 않고, 나중에 별도 asset-class domain으로 분리 검토한다. + - Universe는 watchlist, exchange-wide, sector-like selector를 갈아 끼울 수 있는 provider-neutral selector로 설계하고, 특정 selector를 instrument identity에 박지 않는다. + - 현재 검증은 KIS credential 없는 mock/fixture 테스트를 우선한다. + - 실제 KIS smoke 단계에서만 credential을 준비하며, 그때도 `.env`가 아니라 1Password CLI 또는 service account 기반 runtime injection으로 제공한다. + - KIS adapter mapping 참조는 로컬 cache의 공식 API 샘플 repo를 우선하고, Postman export는 필수 입력으로 보지 않는다. + - KIS mock/sample 데이터는 secret을 제거한 JSON artifact로 보관하고, SQLite는 대량 fixture 검색이 필요해진 뒤 검토한다. ## 범위 -- KRX/Korea daily bar import pipeline -- provider symbol mapping +- KRX/Korea daily bar import pipeline as the first venue path +- venue-agnostic equity instrument identity and provider symbol mapping +- provider-neutral universe selector - normalized instrument/bar storage - trading calendar/timezone/currency 기본 처리 - KIS adapter boundary +- local KIS API sample repo reference handling +- sanitized mock/sample response artifact handling +- deferred 1Password-injected secret boundary for live smoke - fixture 또는 mock provider를 통한 credential 없는 검증 경로 -## 필수 기능 +## 기능 ### Epic: [market-data] Korea daily market data 한국장 일봉 데이터의 provider-neutral 수집, 정규화, 저장, 검증 흐름을 묶는다. -- [ ] [instrument-map] provider symbol과 ALT instrument identity를 매핑한다. -- [ ] [daily-bars] 한국장 일봉 OHLCV를 normalized bar 모델로 저장한다. +- [ ] [instrument-map] provider symbol과 ALT의 venue-agnostic equity instrument identity를 매핑한다. +- [ ] [universe-selector] watchlist, exchange-wide, sector-like 입력을 provider-neutral universe selector로 다룬다. +- [ ] [daily-bars] 한국장 일봉 OHLCV를 normalized bar 모델로 저장한다. 검증: 선택된 equity universe의 일봉 입력이 저장소에 들어가고, 같은 입력을 다시 import해도 중복/갱신 정책이 일관된다. - [ ] [calendar-rules] `Asia/Seoul`, KRX, KRW 기준의 최소 calendar/currency 처리를 갖는다. -- [ ] [kis-adapter] KIS provider adapter 경계가 worker 내부에 격리된다. +- [ ] [kis-adapter] KIS provider adapter 경계가 worker 내부에 격리된다. 검증: 로컬 cache의 KIS API 샘플 repo와 sanitized mock/sample response를 기준으로 request/auth/response mapping을 확정할 수 있고 secret 값은 저장하지 않는다. - [ ] [mock-provider] KIS credential 없이도 pipeline을 검증할 fixture/mock provider가 있다. -- [ ] [data-check] import 결과를 재현 가능하게 검증하는 테스트 또는 CLI/smoke 명령이 있다. - -## 완료 기준 - -- [ ] 선택된 한국장 universe의 일봉 데이터가 저장소에 들어간다. -- [ ] 같은 입력을 다시 import해도 중복/갱신 정책이 일관된다. -- [ ] normalized data가 backtest milestone에서 소비 가능한 형태로 조회된다. -- [ ] `bin/test`와 관련 worker 검증이 통과한다. +- [ ] [data-check] import 결과를 재현 가능하게 검증하는 테스트 또는 CLI/smoke 명령이 있다. 검증: normalized data가 backtest milestone에서 소비 가능한 형태로 조회되고 `bin/test`와 관련 worker 검증이 통과한다. ## 완료 리뷰 - 상태: 없음 - 요청일: 없음 -- 완료 근거: 아직 필수 기능과 완료 기준이 충족되지 않았다. +- 완료 근거: 아직 기능 Task와 각 Task의 검증 조건이 충족되지 않았다. - 리뷰 필요: - [ ] 사용자가 완료 결과를 확인했다 - [ ] archive 이동을 승인했다 @@ -65,11 +69,16 @@ - 분봉/틱/실시간 streaming - 실거래 주문 API - 고급 corporate action adjustment +- crypto/디지털 자산 core model 통합 +- 실제 KIS secret 값 저장 또는 문서화 ## 작업 컨텍스트 -- 관련 경로: `services/worker/`, `packages/domain/market/`, `packages/contracts/proto/alt/v1/market.proto` +- 관련 경로: `services/worker/`, `services/worker/testdata/providers/kis/`, `packages/domain/market/`, `packages/contracts/proto/alt/v1/market.proto` - 표준선(선택): 첫 데이터 단위는 일봉이고, 구조는 분봉/실시간 확장을 막지 않는다. +- 표준선(선택): canonical model은 global equity venue-agnostic vocabulary를 우선하고, 한국장/KIS 특수 필드는 adapter, venue metadata, raw payload, 또는 명시적 extension metadata에 격리한다. +- 표준선(선택): KIS API mapping은 로컬 cache의 공식 샘플 repo를 먼저 보고, mock/sample artifact는 sanitized JSON을 기본으로 둔다. SQLite는 대량 fixture 검색이나 쿼리 비교가 필요해질 때 도입한다. +- 표준선(선택): mock/fixture 테스트에는 KIS credential이 필요 없다. 실제 KIS smoke 단계에서만 1Password CLI 또는 service account로 실행 시점에 주입하며 `.env`에 저장하지 않는다. - 선행 작업: Persistence and Worker Backbone - 후속 작업: Backtest Engine Baseline -- 확인 필요: MVP universe와 KIS credential 제공 방식 +- 확인 필요: mock 테스트에 사용할 최소 KIS daily bar fixture와 expected normalized bar 사례 diff --git a/agent-task/m-korea-daily-data-foundation/01_provider_foundation/CODE_REVIEW-cloud-G07.md b/agent-task/m-korea-daily-data-foundation/01_provider_foundation/CODE_REVIEW-cloud-G07.md new file mode 100644 index 0000000..4fd63e9 --- /dev/null +++ b/agent-task/m-korea-daily-data-foundation/01_provider_foundation/CODE_REVIEW-cloud-G07.md @@ -0,0 +1,109 @@ + + +# Code Review Reference - KIS_FOUNDATION + +> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.** +> The task is NOT complete until every implementation-owned section below is filled in. +> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving. +> Fill implementation-owned sections, then stop with active files in place and report ready for review. +> If implementation is blocked by a user-only decision, external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. +> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume. +> Follow the ownership table at the bottom of this file for which sections you own. + +## 개요 + +date=2026-05-29 +task=m-korea-daily-data-foundation/01_provider_foundation, plan=0, tag=KIS_FOUNDATION + +## 이 파일을 읽는 리뷰 에이전트에게 + +> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다. + +각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요. +리뷰 완료는 plan skill의 code-review 절차를 따른다. + +--- + +## 구현 항목별 완료 여부 + +| 항목 | 완료 여부 | +|------|---------| +| [KIS_FOUNDATION-1] Domain Selector Vocabulary | [ ] | +| [KIS_FOUNDATION-2] KIS Fixture Decoder | [ ] | +| [KIS_FOUNDATION-3] KIS Row Normalization | [ ] | + +## 구현 체크리스트 + +- [ ] `packages/domain/market`에 provider-neutral universe selector와 provider symbol vocabulary를 추가하고 unit test를 작성한다. +- [ ] `services/worker/internal/providers/kis` 또는 동등한 worker-owned provider package에 KIS daily chart fixture decoder와 normalization 함수를 추가한다. +- [ ] KIS `output2` rows를 `market.Bar`로 변환할 때 `Asia/Seoul`, `KRX`, `KRW`, `TimeframeDaily` 기본선을 테스트한다. +- [ ] fixture JSON 전체가 secret-free이고 parser test에 사용되는지 검증한다. +- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. + +## 코드리뷰 전용 체크리스트 + +> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다. +> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다. + +- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다. +- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다. +- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G07_N.log`로 아카이브한다. +- [ ] active `PLAN-*-G??.md`를 `plan_cloud_G07_M.log`로 아카이브한다. +- [ ] PASS이면 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다. +- [ ] PASS이면 active task 디렉터리를 archive로 이동한다. +- [ ] PASS이고 task group이 `m-`이면 완료 이벤트 메타데이터를 보고하고 roadmap 수정은 런타임에 맡긴다. +- [ ] WARN/FAIL이면 다음 active plan/review 또는 `USER_REVIEW.md`를 작성한다. + +## 계획 대비 변경 사항 + +_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._ + +## 주요 설계 결정 + +_구현 에이전트가 주요 설계 결정 사항을 기록한다._ + +## 사용자 리뷰 요청 + +_기본값은 `없음`이다. 구현 중 사용자 결정, 외부 환경 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. code-review가 이 내용을 검증해 `USER_REVIEW.md`를 작성한다._ + +- 상태: 없음 +- 사유 유형: 없음 +- 결정 필요: 없음 +- 차단 근거: 없음 +- 실행한 검증/명령: 없음 +- 재개 조건: 없음 + +## 리뷰어를 위한 체크포인트 + +- domain package가 worker/protobuf/storage dependency를 새로 갖지 않는지 확인한다. +- KIS fixture normalization이 KRX/KRW/Asia-Seoul 일봉 기준을 테스트로 고정하는지 확인한다. +- secret-like 값이 fixture에 들어가지 않았는지 확인한다. + +## 검증 결과 + +_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._ + +### KIS_FOUNDATION-1 중간 검증 +```bash +$ cd packages/domain && go test -count=1 ./market +``` + +### KIS_FOUNDATION-2 중간 검증 +```bash +$ cd services/worker && go test -count=1 ./internal/providers/kis +``` + +### KIS_FOUNDATION-3 중간 검증 +```bash +$ cd services/worker && go test -count=1 ./internal/providers/kis +``` + +### 최종 검증 +```bash +$ cd packages/domain && go test -count=1 ./... +$ cd services/worker && go test -count=1 ./internal/providers/kis +``` + +--- + +> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?** diff --git a/agent-task/m-korea-daily-data-foundation/01_provider_foundation/PLAN-cloud-G07.md b/agent-task/m-korea-daily-data-foundation/01_provider_foundation/PLAN-cloud-G07.md new file mode 100644 index 0000000..b916fb1 --- /dev/null +++ b/agent-task/m-korea-daily-data-foundation/01_provider_foundation/PLAN-cloud-G07.md @@ -0,0 +1,184 @@ + + +# Plan - KIS Provider Foundation + +## 이 파일을 읽는 구현 에이전트에게 + +`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우는 것이 구현의 마지막 단계다. 구현 중 사용자 결정, 외부 환경, 범위 충돌로 막히면 active review stub의 `사용자 리뷰 요청`에 정확한 근거를 남기고 멈춘다. `USER_REVIEW.md`, `complete.log`, archive 이동은 code-review 전용이다. + +## 배경 + +Korea Daily Data Foundation은 KIS credential 없이 mock/fixture 기반으로 먼저 검증한다. 현재 domain model은 instrument와 bar만 있고 provider selector, KIS daily response normalization, KRX date/currency 기본선이 없다. 이 작업은 후속 import/storage pipeline이 소비할 안정적인 provider boundary를 만든다. + +## 사용자 리뷰 요청 흐름 + +구현 중 blocker는 active `CODE_REVIEW-cloud-G07.md`의 `사용자 리뷰 요청` 섹션에 기록한다. code-review가 그 요청을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다. + +## 분석 결과 + +### 읽은 파일 + +- `agent-roadmap/current.md` +- `agent-roadmap/phase/data-foundation/PHASE.md` +- `agent-roadmap/phase/data-foundation/milestones/korea-daily-data-foundation.md` +- `agent-ops/rules/project/domain/domain-model/rules.md` +- `agent-ops/rules/project/domain/worker/rules.md` +- `packages/domain/market/types.go` +- `services/worker/internal/storage/ports.go` +- `services/worker/internal/storage/postgres/mapping.go` +- `services/worker/internal/storage/postgres/mapping_test.go` +- `services/worker/testdata/providers/kis/README.md` +- `services/worker/testdata/providers/kis/daily_itemchartprice_request.sample.json` +- `services/worker/testdata/providers/kis/daily_itemchartprice_response.sample.json` +- `services/worker/testdata/providers/kis/daily_bars_normalized.expected.json` +- `.agent-cache/koreainvestment/open-trading-api/examples_llm/domestic_stock/inquire_daily_itemchartprice/inquire_daily_itemchartprice.py` +- `.agent-cache/koreainvestment/open-trading-api/examples_llm/domestic_stock/inquire_daily_itemchartprice/chk_inquire_daily_itemchartprice.py` + +### 테스트 커버리지 공백 + +- Provider-neutral universe selector: 기존 테스트 없음. 새 domain unit test 필요. +- KIS daily chart response normalization: 기존 테스트 없음. 새 worker provider fixture test 필요. +- KRX daily timestamp/currency default: 기존 테스트 없음. 새 worker provider fixture test 필요. + +### 심볼 참조 + +none. 이 계획은 신규 타입/패키지 추가를 우선하며 기존 심볼 rename/remove는 하지 않는다. + +### 분할 판단 + +Split decision policy를 먼저 평가했다. 공유 foundation 없이 import/storage/job을 바로 만들면 후속 작업이 provider API를 추정하게 되므로 분할한다. + +- 공통 task group: `m-korea-daily-data-foundation` +- 현재 subtask: `01_provider_foundation`, 의존 없음 +- 후속 subtask: `02+01_import_storage_pipeline`, `01_provider_foundation` 완료 필요 +- 후속 subtask: `03+01,02_data_check_smoke`, `01_provider_foundation`과 `02+01_import_storage_pipeline` 완료 필요 + +### 범위 결정 근거 + +실제 KIS HTTP 호출, credential/1Password wiring, PostgreSQL migration, CLI smoke는 제외한다. 이 작업은 provider-neutral selector와 KIS mock fixture normalization까지만 닫는다. + +### 빌드 등급 + +build=`cloud-G07`, review=`cloud-G07`. domain과 worker 경계를 새로 만들고 후속 storage 작업의 API가 되므로 cloud review가 필요하다. + +## 구현 체크리스트 + +- [ ] `packages/domain/market`에 provider-neutral universe selector와 provider symbol vocabulary를 추가하고 unit test를 작성한다. +- [ ] `services/worker/internal/providers/kis` 또는 동등한 worker-owned provider package에 KIS daily chart fixture decoder와 normalization 함수를 추가한다. +- [ ] KIS `output2` rows를 `market.Bar`로 변환할 때 `Asia/Seoul`, `KRX`, `KRW`, `TimeframeDaily` 기본선을 테스트한다. +- [ ] fixture JSON 전체가 secret-free이고 parser test에 사용되는지 검증한다. +- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. + +### [KIS_FOUNDATION-1] Domain Selector Vocabulary + +문제: [types.go](/config/workspace/alt/packages/domain/market/types.go:37)는 `Instrument.ProviderSymbols`만 있고 watchlist, exchange-wide, sector-like selector를 표현할 타입이 없다. + +해결 방법: `packages/domain/market/types.go`에 `Provider`, `UniverseSelectorKind`, `UniverseSelector`를 추가한다. 기존 `Instrument.ProviderSymbols map[string]string`은 유지하고, 새 타입은 provider-neutral request vocabulary로만 둔다. + +Before: + +```go +type Instrument struct { + ID InstrumentID + Market Market + Venue Venue + Symbol string + Name string + Currency Currency + ProviderSymbols map[string]string +} +``` + +After: + +```go +type Provider string + +const ProviderKIS Provider = "kis" + +type UniverseSelectorKind string + +const ( + UniverseSelectorWatchlist UniverseSelectorKind = "watchlist" + UniverseSelectorExchange UniverseSelectorKind = "exchange" + UniverseSelectorSector UniverseSelectorKind = "sector" +) + +type UniverseSelector struct { + Kind UniverseSelectorKind + Market Market + Venue Venue + Symbols []string + Name string +} +``` + +수정 파일 및 체크리스트: + +- [ ] `packages/domain/market/types.go`에 타입/상수 추가 +- [ ] `packages/domain/market/types_test.go` 추가 + +테스트 작성: `TestUniverseSelectorVocabulary`를 추가해 watchlist/exchange/sector 값이 안정적인 string 값을 갖는지 확인한다. + +중간 검증: + +```bash +cd packages/domain && go test -count=1 ./market +``` + +### [KIS_FOUNDATION-2] KIS Fixture Decoder + +문제: [daily_itemchartprice_response.sample.json](/config/workspace/alt/services/worker/testdata/providers/kis/daily_itemchartprice_response.sample.json:1)은 fixture로 존재하지만 이를 KIS response shape로 decode하는 worker package가 없다. + +해결 방법: worker 내부에 `internal/providers/kis` package를 만들고 KIS daily item chart request/response structs, fixture loader test, response validation을 둔다. + +수정 파일 및 체크리스트: + +- [ ] `services/worker/internal/providers/kis/daily_itemchartprice.go` 추가 +- [ ] `services/worker/internal/providers/kis/daily_itemchartprice_test.go` 추가 +- [ ] fixture path는 `services/worker/testdata/providers/kis/*.json`을 사용 + +테스트 작성: `TestLoadDailyItemChartPriceFixture`에서 request endpoint/TR ID와 response `output1`, `output2` row count를 검증한다. + +중간 검증: + +```bash +cd services/worker && go test -count=1 ./internal/providers/kis +``` + +### [KIS_FOUNDATION-3] KIS Row Normalization + +문제: KIS row의 `stck_bsop_date`, `stck_oprc`, `stck_hgpr`, `stck_lwpr`, `stck_clpr`, `acml_vol`을 ALT `market.Bar`로 바꾸는 규칙이 없다. + +해결 방법: `NormalizeDailyBars`를 추가해 KIS rows를 `market.Bar`로 변환한다. 날짜는 `Asia/Seoul` 자정, currency는 `KRW`, timeframe은 `1d`, instrument id는 provider symbol metadata에서 받은 `KRX:`을 사용한다. + +수정 파일 및 체크리스트: + +- [ ] `services/worker/internal/providers/kis/daily_itemchartprice.go`에 normalization 함수 추가 +- [ ] `services/worker/internal/providers/kis/daily_itemchartprice_test.go`에 expected normalized fixture 대조 추가 + +테스트 작성: `TestNormalizeDailyItemChartPriceBars`에서 [daily_bars_normalized.expected.json](/config/workspace/alt/services/worker/testdata/providers/kis/daily_bars_normalized.expected.json:1)의 두 bar와 완전 대조한다. + +중간 검증: + +```bash +cd services/worker && go test -count=1 ./internal/providers/kis +``` + +## 수정 파일 요약 + +| 파일 | 항목 | +|------|------| +| `packages/domain/market/types.go` | KIS_FOUNDATION-1 | +| `packages/domain/market/types_test.go` | KIS_FOUNDATION-1 | +| `services/worker/internal/providers/kis/daily_itemchartprice.go` | KIS_FOUNDATION-2, KIS_FOUNDATION-3 | +| `services/worker/internal/providers/kis/daily_itemchartprice_test.go` | KIS_FOUNDATION-2, KIS_FOUNDATION-3 | + +## 최종 검증 + +```bash +cd packages/domain && go test -count=1 ./... +cd services/worker && go test -count=1 ./internal/providers/kis +``` + +모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다. diff --git a/agent-task/m-korea-daily-data-foundation/02+01_import_storage_pipeline/CODE_REVIEW-cloud-G07.md b/agent-task/m-korea-daily-data-foundation/02+01_import_storage_pipeline/CODE_REVIEW-cloud-G07.md new file mode 100644 index 0000000..0c12368 --- /dev/null +++ b/agent-task/m-korea-daily-data-foundation/02+01_import_storage_pipeline/CODE_REVIEW-cloud-G07.md @@ -0,0 +1,92 @@ + + +# Code Review Reference - KIS_IMPORT + +> **[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. +> Finalization is review-agent-only. + +## 개요 + +date=2026-05-29 +task=m-korea-daily-data-foundation/02+01_import_storage_pipeline, plan=0, tag=KIS_IMPORT + +## 이 파일을 읽는 리뷰 에이전트에게 + +> **[REVIEW AGENT ONLY]** 종결 절차는 코드리뷰 에이전트 전용이다. + +--- + +## 구현 항목별 완료 여부 + +| 항목 | 완료 여부 | +|------|---------| +| [KIS_IMPORT-1] Import Service | [ ] | +| [KIS_IMPORT-2] Idempotent Re-import | [ ] | +| [KIS_IMPORT-3] Worker Job Boundary | [ ] | + +## 구현 체크리스트 + +- [ ] `01_provider_foundation`의 `complete.log`를 확인하고 provider API를 그대로 사용한다. +- [ ] worker 내부에 daily bar import service를 추가해 selector -> instruments/bars -> store upsert 흐름을 구현한다. +- [ ] 같은 fixture를 두 번 import해도 instrument/bar 결과가 중복 없이 갱신되는 test를 작성한다. +- [ ] `KindImportDailyBars` payload decode와 handler registration을 실제 import service에 연결할 수 있게 분리한다. +- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. + +## 코드리뷰 전용 체크리스트 + +> **[REVIEW AGENT ONLY]** 구현 에이전트는 수정하지 않는다. + +- [ ] `코드리뷰 결과`에 판정을 append한다. +- [ ] active plan/review를 log로 아카이브한다. +- [ ] PASS이면 `complete.log` 작성 후 task directory를 archive로 이동한다. +- [ ] PASS이고 task group이 `m-`이면 완료 이벤트 메타데이터를 보고한다. +- [ ] WARN/FAIL이면 다음 active plan/review 또는 `USER_REVIEW.md`를 작성한다. + +## 계획 대비 변경 사항 + +_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._ + +## 주요 설계 결정 + +_구현 에이전트가 주요 설계 결정 사항을 기록한다._ + +## 사용자 리뷰 요청 + +_기본값은 `없음`이다. 구현 중 사용자 결정, 외부 환경 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다._ + +- 상태: 없음 +- 사유 유형: 없음 +- 결정 필요: 없음 +- 차단 근거: 없음 +- 실행한 검증/명령: 없음 +- 재개 조건: 없음 + +## 리뷰어를 위한 체크포인트 + +- `01_provider_foundation/complete.log` 확인 없이 구현이 시작되지 않았는지 확인한다. +- Import service가 provider와 storage를 느슨하게 결합했는지 확인한다. +- 같은 fixture 재import가 중복을 만들지 않는지 테스트 출력으로 확인한다. + +## 검증 결과 + +### KIS_IMPORT-1 중간 검증 +```bash +$ cd services/worker && go test -count=1 ./internal/marketdata/importer +``` + +### KIS_IMPORT-2 중간 검증 +```bash +$ cd services/worker && go test -count=1 ./internal/marketdata/importer +``` + +### KIS_IMPORT-3 중간 검증 +```bash +$ cd services/worker && go test -count=1 ./internal/jobs ./internal/marketdata/importer +``` + +### 최종 검증 +```bash +$ test -f agent-task/m-korea-daily-data-foundation/01_provider_foundation/complete.log +$ cd services/worker && go test -count=1 ./internal/jobs ./internal/marketdata/importer ./internal/storage/... +``` diff --git a/agent-task/m-korea-daily-data-foundation/02+01_import_storage_pipeline/PLAN-cloud-G07.md b/agent-task/m-korea-daily-data-foundation/02+01_import_storage_pipeline/PLAN-cloud-G07.md new file mode 100644 index 0000000..1b24c51 --- /dev/null +++ b/agent-task/m-korea-daily-data-foundation/02+01_import_storage_pipeline/PLAN-cloud-G07.md @@ -0,0 +1,141 @@ + + +# Plan - Daily Bar Import Storage Pipeline + +## 이 파일을 읽는 구현 에이전트에게 + +이 subtask는 `agent-task/m-korea-daily-data-foundation/01_provider_foundation/complete.log`가 생긴 뒤 시작한다. 구현 완료 전 active `CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션을 채운다. blocker가 있으면 review stub의 `사용자 리뷰 요청`에 근거를 기록하고 멈춘다. + +## 배경 + +Foundation provider가 KIS mock rows를 `market.Instrument`와 `market.Bar`로 만들면, worker는 이를 idempotent하게 저장하는 import pipeline이 필요하다. 현재 storage port는 instrument/bar upsert를 제공하지만 import orchestration, selector payload, idempotent 검증이 없다. + +## 사용자 리뷰 요청 흐름 + +구현 중 blocker는 active `CODE_REVIEW-cloud-G07.md`의 `사용자 리뷰 요청` 섹션에 기록한다. code-review가 검증과 종료 처리를 소유한다. + +## 분석 결과 + +### 읽은 파일 + +- `services/worker/internal/storage/ports.go` +- `services/worker/internal/storage/postgres/store.go` +- `services/worker/internal/storage/postgres/mapping.go` +- `services/worker/internal/storage/postgres/queries/queries.sql` +- `services/worker/internal/jobs/job.go` +- `services/worker/internal/jobs/builtin.go` +- `services/worker/internal/jobs/runner.go` +- `services/worker/internal/jobs/runner_test.go` +- `services/worker/testdata/providers/kis/*.json` +- `agent-task/m-korea-daily-data-foundation/01_provider_foundation/PLAN-cloud-G07.md` + +### 테스트 커버리지 공백 + +- Import orchestration: 기존 테스트 없음. in-memory store와 mock provider test 필요. +- Idempotent re-import: storage upsert는 있으나 import-level 중복/갱신 정책 테스트 없음. +- Worker job payload: built-in handler는 placeholder라 payload decode test 없음. + +### 심볼 참조 + +none. 기존 job kind는 유지하고 handler wiring을 보강한다. + +### 분할 판단 + +`02+01_import_storage_pipeline`은 directory name상 `01_provider_foundation`에 의존한다. Provider API와 normalization이 완료되어야 import service가 안정적으로 컴파일된다. + +### 범위 결정 근거 + +PostgreSQL schema 변경은 우선 제외한다. 기존 `instruments`와 `bars` 테이블, `UpsertInstrument`, `UpsertBar`로 idempotent 저장을 검증한다. 실제 KIS HTTP client와 credential wiring도 제외한다. + +### 빌드 등급 + +build=`cloud-G07`, review=`cloud-G07`. worker orchestration과 storage idempotency를 다루므로 storage/behavior review가 필요하다. + +## 의존 관계 및 구현 순서 + +1. `agent-task/m-korea-daily-data-foundation/01_provider_foundation/complete.log` 확인 +2. import service 구현 +3. job payload/handler wiring 구현 +4. worker tests 실행 + +## 구현 체크리스트 + +- [ ] `01_provider_foundation`의 `complete.log`를 확인하고 provider API를 그대로 사용한다. +- [ ] worker 내부에 daily bar import service를 추가해 selector -> instruments/bars -> store upsert 흐름을 구현한다. +- [ ] 같은 fixture를 두 번 import해도 instrument/bar 결과가 중복 없이 갱신되는 test를 작성한다. +- [ ] `KindImportDailyBars` payload decode와 handler registration을 실제 import service에 연결할 수 있게 분리한다. +- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. + +### [KIS_IMPORT-1] Import Service + +문제: [ports.go](/config/workspace/alt/services/worker/internal/storage/ports.go:11)는 store interface만 있고 provider output을 저장하는 orchestration이 없다. + +해결 방법: `services/worker/internal/marketdata/importer` 또는 동등한 worker-owned package를 추가한다. `Importer.ImportDailyBars(ctx, selector)`는 provider에서 instruments/bars를 받고 `InstrumentStore.UpsertInstrument`, `BarStore.UpsertBar`를 호출한다. + +수정 파일 및 체크리스트: + +- [ ] `services/worker/internal/marketdata/importer/importer.go` 추가 +- [ ] `services/worker/internal/marketdata/importer/importer_test.go` 추가 + +테스트 작성: fake provider와 in-memory stores로 instrument 1개, bar 2개가 저장되는지 검증한다. + +중간 검증: + +```bash +cd services/worker && go test -count=1 ./internal/marketdata/importer +``` + +### [KIS_IMPORT-2] Idempotent Re-import + +문제: [queries.sql](/config/workspace/alt/services/worker/internal/storage/postgres/queries/queries.sql:21)의 `UpsertBar`는 DB upsert를 제공하지만 import service가 같은 fixture를 다시 처리할 때 정책이 유지되는지 테스트가 없다. + +해결 방법: importer test에서 같은 provider result를 두 번 import한다. in-memory store는 primary key `(instrument_id,timeframe,timestamp)`로 replace하도록 구현하고 최종 bar 수가 2개인지 확인한다. + +수정 파일 및 체크리스트: + +- [ ] `services/worker/internal/marketdata/importer/importer_test.go`에 재실행 test 추가 + +테스트 작성: `TestImporterIsIdempotentForSameDailyBars`. + +중간 검증: + +```bash +cd services/worker && go test -count=1 ./internal/marketdata/importer +``` + +### [KIS_IMPORT-3] Worker Job Boundary + +문제: [builtin.go](/config/workspace/alt/services/worker/internal/jobs/builtin.go:11)는 `import_daily_bars`를 log-only placeholder로 처리한다. + +해결 방법: 기존 `RegisterBuiltins`는 placeholder count test를 유지하되, 실제 import handler를 등록할 수 있는 별도 function 또는 option을 추가한다. payload는 provider, selector kind, symbols/date range를 포함하는 struct로 decode한다. + +수정 파일 및 체크리스트: + +- [ ] `services/worker/internal/jobs/builtin.go` 또는 새 `marketdata_jobs.go`에 import handler registration 추가 +- [ ] `services/worker/internal/jobs/runner_test.go` 또는 새 test에 payload decode/dispatch 검증 추가 + +테스트 작성: `TestRegisterDailyBarImportHandlerDispatchesImporter`. + +중간 검증: + +```bash +cd services/worker && go test -count=1 ./internal/jobs ./internal/marketdata/importer +``` + +## 수정 파일 요약 + +| 파일 | 항목 | +|------|------| +| `services/worker/internal/marketdata/importer/importer.go` | KIS_IMPORT-1, KIS_IMPORT-2 | +| `services/worker/internal/marketdata/importer/importer_test.go` | KIS_IMPORT-1, KIS_IMPORT-2 | +| `services/worker/internal/jobs/builtin.go` 또는 새 job file | KIS_IMPORT-3 | +| `services/worker/internal/jobs/*_test.go` | KIS_IMPORT-3 | + +## 최종 검증 + +```bash +test -f agent-task/m-korea-daily-data-foundation/01_provider_foundation/complete.log +cd services/worker && go test -count=1 ./internal/jobs ./internal/marketdata/importer ./internal/storage/... +``` + +모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다. diff --git a/agent-task/m-korea-daily-data-foundation/03+01,02_data_check_smoke/CODE_REVIEW-cloud-G07.md b/agent-task/m-korea-daily-data-foundation/03+01,02_data_check_smoke/CODE_REVIEW-cloud-G07.md new file mode 100644 index 0000000..7a1db02 --- /dev/null +++ b/agent-task/m-korea-daily-data-foundation/03+01,02_data_check_smoke/CODE_REVIEW-cloud-G07.md @@ -0,0 +1,87 @@ + + +# Code Review Reference - KIS_SMOKE + +> **[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. +> Finalization is review-agent-only. + +## 개요 + +date=2026-05-29 +task=m-korea-daily-data-foundation/03+01,02_data_check_smoke, plan=0, tag=KIS_SMOKE + +## 이 파일을 읽는 리뷰 에이전트에게 + +> **[REVIEW AGENT ONLY]** 종결 절차는 코드리뷰 에이전트 전용이다. + +--- + +## 구현 항목별 완료 여부 + +| 항목 | 완료 여부 | +|------|---------| +| [KIS_SMOKE-1] Mock Data Check Command | [ ] | +| [KIS_SMOKE-2] Whole Worker Regression | [ ] | + +## 구현 체크리스트 + +- [ ] 선행 `01_provider_foundation`과 `02+01_import_storage_pipeline`의 `complete.log`를 확인한다. +- [ ] mock fixture import와 normalized bar 조회를 실행하는 CLI 또는 worker command를 추가한다. +- [ ] command output이 instrument count, bar count, first/last timestamp를 안정적으로 출력하도록 테스트한다. +- [ ] `bin/test` 또는 focused worker command가 credential 없이 통과함을 검증한다. +- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. + +## 코드리뷰 전용 체크리스트 + +> **[REVIEW AGENT ONLY]** 구현 에이전트는 수정하지 않는다. + +- [ ] `코드리뷰 결과`에 판정을 append한다. +- [ ] active plan/review를 log로 아카이브한다. +- [ ] PASS이면 `complete.log` 작성 후 task directory를 archive로 이동한다. +- [ ] PASS이고 task group이 `m-`이면 완료 이벤트 메타데이터를 보고한다. +- [ ] WARN/FAIL이면 다음 active plan/review 또는 `USER_REVIEW.md`를 작성한다. + +## 계획 대비 변경 사항 + +_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._ + +## 주요 설계 결정 + +_구현 에이전트가 주요 설계 결정 사항을 기록한다._ + +## 사용자 리뷰 요청 + +_기본값은 `없음`이다. 구현 중 사용자 결정, 외부 환경 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다._ + +- 상태: 없음 +- 사유 유형: 없음 +- 결정 필요: 없음 +- 차단 근거: 없음 +- 실행한 검증/명령: 없음 +- 재개 조건: 없음 + +## 리뷰어를 위한 체크포인트 + +- 실제 KIS credential/1Password가 smoke command에 필요하지 않은지 확인한다. +- stdout contract가 안정적이고 테스트로 고정되는지 확인한다. +- `bin/test` 결과가 실제 출력으로 기록되는지 확인한다. + +## 검증 결과 + +### KIS_SMOKE-1 중간 검증 +```bash +$ cd apps/cli && go test -count=1 ./... +``` + +### KIS_SMOKE-2 중간 검증 +```bash +$ bin/test +``` + +### 최종 검증 +```bash +$ test -f agent-task/m-korea-daily-data-foundation/01_provider_foundation/complete.log +$ test -f agent-task/m-korea-daily-data-foundation/02+01_import_storage_pipeline/complete.log +$ bin/test +``` diff --git a/agent-task/m-korea-daily-data-foundation/03+01,02_data_check_smoke/PLAN-cloud-G07.md b/agent-task/m-korea-daily-data-foundation/03+01,02_data_check_smoke/PLAN-cloud-G07.md new file mode 100644 index 0000000..e3c10fa --- /dev/null +++ b/agent-task/m-korea-daily-data-foundation/03+01,02_data_check_smoke/PLAN-cloud-G07.md @@ -0,0 +1,122 @@ + + +# Plan - Mock Data Check Smoke + +## 이 파일을 읽는 구현 에이전트에게 + +이 subtask는 `01_provider_foundation`과 `02+01_import_storage_pipeline` 완료 후 시작한다. 구현 완료 전 active review stub의 구현 에이전트 소유 섹션을 채운다. 실제 KIS credential이나 1Password 준비를 요구하지 않는다. + +## 배경 + +Milestone의 `data-check`는 normalized data가 backtest milestone에서 소비 가능한 형태로 조회되고 관련 검증이 통과해야 한다. 현재는 mock fixture 기반 흐름을 사람이 반복 실행할 수 있는 smoke 표면이 없다. + +## 사용자 리뷰 요청 흐름 + +구현 중 blocker는 active `CODE_REVIEW-cloud-G07.md`의 `사용자 리뷰 요청` 섹션에 기록한다. code-review가 검증과 종료 처리를 소유한다. + +## 분석 결과 + +### 읽은 파일 + +- `bin/dev` +- `bin/test` +- `bin/worker-storage-check` +- `apps/cli/cmd/alt/main.go` +- `services/worker/cmd/alt-worker/main.go` +- `services/worker/internal/jobs/builtin.go` +- `services/worker/testdata/providers/kis/*.json` +- `agent-task/m-korea-daily-data-foundation/01_provider_foundation/PLAN-cloud-G07.md` +- `agent-task/m-korea-daily-data-foundation/02+01_import_storage_pipeline/PLAN-cloud-G07.md` + +### 테스트 커버리지 공백 + +- End-to-end mock import command: 기존 없음. CLI 또는 worker command test 필요. +- Human-runnable data check output: 기존 없음. stdout contract test 필요. +- Full worker package regression after import pipeline: 기존 `bin/test`는 있으나 feature-specific smoke 없음. + +### 심볼 참조 + +none. 기존 `bin/test`와 storage check는 유지한다. + +### 분할 판단 + +`03+01,02_data_check_smoke`는 directory name상 `01_provider_foundation`과 `02+01_import_storage_pipeline`에 의존한다. Smoke command는 실제 provider/import API가 완료된 뒤 작성해야 한다. + +### 범위 결정 근거 + +실제 KIS network smoke, 1Password credential injection, Postgres Docker orchestration은 제외한다. 이 작업은 mock fixture를 읽어 normalized daily bar 조회 가능성을 보여주는 local command/test만 만든다. + +### 빌드 등급 + +build=`cloud-G07`, review=`cloud-G07`. terminal-facing smoke output과 bin/CLI behavior가 포함된다. + +## 의존 관계 및 구현 순서 + +1. `agent-task/m-korea-daily-data-foundation/01_provider_foundation/complete.log` 확인 +2. `agent-task/m-korea-daily-data-foundation/02+01_import_storage_pipeline/complete.log` 확인 +3. mock smoke command 구현 +4. full verification 실행 + +## 구현 체크리스트 + +- [ ] 선행 `01_provider_foundation`과 `02+01_import_storage_pipeline`의 `complete.log`를 확인한다. +- [ ] mock fixture import와 normalized bar 조회를 실행하는 CLI 또는 worker command를 추가한다. +- [ ] command output이 instrument count, bar count, first/last timestamp를 안정적으로 출력하도록 테스트한다. +- [ ] `bin/test` 또는 focused worker command가 credential 없이 통과함을 검증한다. +- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. + +### [KIS_SMOKE-1] Mock Data Check Command + +문제: [apps/cli main](/config/workspace/alt/apps/cli/cmd/alt/main.go:8)은 `version` 외에 운영자 command가 없고, worker import 결과를 재현 가능하게 확인할 표면도 없다. + +해결 방법: 가장 작은 표면으로 `apps/cli` 또는 `services/worker/cmd`에 mock data check command를 추가한다. command는 KIS fixture를 import pipeline에 넣고 in-memory store에서 bars를 조회한 뒤 stable summary를 출력한다. + +수정 파일 및 체크리스트: + +- [ ] `apps/cli/cmd/alt/main.go` 또는 새 `services/worker/cmd/alt-worker-data-check/main.go` 추가/수정 +- [ ] command test 추가 + +테스트 작성: stdout에 `instrument_count=1`, `bar_count=2`, `first=2024-05-27`, `last=2024-05-28`이 있는지 확인한다. + +중간 검증: + +```bash +cd apps/cli && go test -count=1 ./... +``` + +### [KIS_SMOKE-2] Whole Worker Regression + +문제: mock provider/import/smoke가 여러 package를 가로지른 뒤 worker 전체 regression 명령이 명시되어 있지 않다. + +해결 방법: focused tests와 `bin/test`를 모두 실행한다. sqlc generated drift는 storage query 변경이 있을 때만 `bin/worker-storage-check`를 추가 실행한다. + +수정 파일 및 체크리스트: + +- [ ] smoke command 구현 후 `bin/test` 통과 확인 +- [ ] storage query/migration 변경이 있었다면 `bin/worker-storage-check` 실행 + +테스트 작성: 별도 새 테스트는 KIS_SMOKE-1에 포함한다. + +중간 검증: + +```bash +bin/test +``` + +## 수정 파일 요약 + +| 파일 | 항목 | +|------|------| +| `apps/cli/cmd/alt/main.go` 또는 `services/worker/cmd/alt-worker-data-check/main.go` | KIS_SMOKE-1 | +| command test file | KIS_SMOKE-1 | +| `bin/*` only if an entrypoint is needed | KIS_SMOKE-2 | + +## 최종 검증 + +```bash +test -f agent-task/m-korea-daily-data-foundation/01_provider_foundation/complete.log +test -f agent-task/m-korea-daily-data-foundation/02+01_import_storage_pipeline/complete.log +bin/test +``` + +모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다. diff --git a/services/worker/README.md b/services/worker/README.md index 29b6b55..73e3640 100644 --- a/services/worker/README.md +++ b/services/worker/README.md @@ -2,6 +2,23 @@ The Worker service handles background tasks, such as market data imports, data normalization, and backtest execution. +## Market Data Provider Boundary + +ALT starts with KIS/Korea daily bars as the first provider and venue validation path, but the worker boundary should keep the canonical model equity venue-agnostic. KRX-specific or KIS-specific fields belong in provider adapters, venue metadata, raw payload storage, or explicit extension metadata instead of becoming required core columns. + +Crypto is out of scope for the current stock-oriented market model. If a later crypto data domain is added, it may reuse generic import-run, provider mapping, raw-payload, and OHLCV pipeline concepts, but pair/funding/24-7 market semantics should not be forced into the equity model. + +## Provider Reference and Mock Fixtures + +KIS adapter mapping should start from the locally cached official sample repository, not from a required Postman export. The cache path is documented in the private rules because it is local machine state, not an ALT source dependency. + +- Store mock-provider request/response fixtures under `services/worker/testdata/providers/kis/`. +- Remove app keys, tokens, account numbers, personal identifiers, and live secret references before committing any fixture. +- Prefer JSON for initial adapter mapping because it is readable, diffable, and close to the KIS REST payload shape. +- Consider SQLite only after the fixture corpus is large enough to need indexed lookup or query-based comparison. + +Provider credentials are not required for mock tests. When live KIS smoke tests are added later, credentials should be supplied at runtime through 1Password CLI or a later secret-provider implementation. Do not use checked-in `.env` files for provider secrets. + ## Redis Usage Guidelines ### Purpose diff --git a/services/worker/testdata/providers/kis/README.md b/services/worker/testdata/providers/kis/README.md new file mode 100644 index 0000000..966fce0 --- /dev/null +++ b/services/worker/testdata/providers/kis/README.md @@ -0,0 +1,39 @@ +# KIS Provider Fixtures + +This directory is the landing zone for KIS mock-provider fixtures used by worker adapter tests and mapping work. + +## Source Reference + +Use the locally cached official KIS sample repository first: + +- `.agent-cache/koreainvestment/open-trading-api` + +The first daily-bar mapping reference is: + +- `examples_llm/domestic_stock/inquire_daily_itemchartprice/inquire_daily_itemchartprice.py` +- `examples_llm/kis_auth.py` +- `kis_devlp.yaml` +- `stocks_info/` + +Postman exports are not required for the current milestone now that the official API sample repo is available locally. + +## Mock Test Fixtures + +Keep fixtures small, deterministic, and secret-free. The current minimum set is: + +- `daily_itemchartprice_request.sample.json`: KIS daily chart request parameters. +- `daily_itemchartprice_response.sample.json`: KIS-like response payload with `output1` metadata and `output2` OHLCV rows. +- `daily_bars_normalized.expected.json`: expected normalized daily bars used by worker tests. +- `provider_symbols.sample.json`: provider symbol metadata for watchlist, exchange-wide, and sector-like selector tests. + +## Sanitization + +Before committing fixtures, remove: + +- app keys, app secrets, access tokens, refresh tokens, approval keys +- account numbers or personal identifiers +- live authorization headers +- local hostnames or machine-specific paths +- 1Password secret references or item names + +Runtime credentials are not required for mock tests. Later live KIS smoke tests should inject credentials at runtime through 1Password CLI or a later secret-provider implementation. diff --git a/services/worker/testdata/providers/kis/daily_bars_normalized.expected.json b/services/worker/testdata/providers/kis/daily_bars_normalized.expected.json new file mode 100644 index 0000000..7955064 --- /dev/null +++ b/services/worker/testdata/providers/kis/daily_bars_normalized.expected.json @@ -0,0 +1,37 @@ +{ + "instrument": { + "id": "KRX:005930", + "market": "KR", + "venue": "KRX", + "symbol": "005930", + "name": "Samsung Electronics", + "currency": "KRW", + "provider_symbols": { + "kis": "005930" + } + }, + "bars": [ + { + "instrument_id": "KRX:005930", + "timeframe": "1d", + "timestamp": "2024-05-27T00:00:00+09:00", + "open": "74800", + "high": "75600", + "low": "74400", + "close": "75000", + "volume": "9100000", + "currency": "KRW" + }, + { + "instrument_id": "KRX:005930", + "timeframe": "1d", + "timestamp": "2024-05-28T00:00:00+09:00", + "open": "75200", + "high": "76200", + "low": "75100", + "close": "76000", + "volume": "10500000", + "currency": "KRW" + } + ] +} diff --git a/services/worker/testdata/providers/kis/daily_itemchartprice_request.sample.json b/services/worker/testdata/providers/kis/daily_itemchartprice_request.sample.json new file mode 100644 index 0000000..a81da67 --- /dev/null +++ b/services/worker/testdata/providers/kis/daily_itemchartprice_request.sample.json @@ -0,0 +1,13 @@ +{ + "provider": "kis", + "endpoint": "/uapi/domestic-stock/v1/quotations/inquire-daily-itemchartprice", + "tr_id": "FHKST03010100", + "params": { + "FID_COND_MRKT_DIV_CODE": "J", + "FID_INPUT_ISCD": "005930", + "FID_INPUT_DATE_1": "20240527", + "FID_INPUT_DATE_2": "20240528", + "FID_PERIOD_DIV_CODE": "D", + "FID_ORG_ADJ_PRC": "0" + } +} diff --git a/services/worker/testdata/providers/kis/daily_itemchartprice_response.sample.json b/services/worker/testdata/providers/kis/daily_itemchartprice_response.sample.json new file mode 100644 index 0000000..cf801d9 --- /dev/null +++ b/services/worker/testdata/providers/kis/daily_itemchartprice_response.sample.json @@ -0,0 +1,43 @@ +{ + "rt_cd": "0", + "msg_cd": "MCA00000", + "msg1": "success", + "output1": { + "hts_kor_isnm": "Samsung Electronics", + "stck_shrn_iscd": "005930", + "stck_prpr": "76000", + "prdy_vrss": "1000", + "prdy_vrss_sign": "2", + "prdy_ctrt": "1.33", + "acml_vol": "10500000", + "acml_tr_pbmn": "798000000000" + }, + "output2": [ + { + "stck_bsop_date": "20240527", + "stck_oprc": "74800", + "stck_hgpr": "75600", + "stck_lwpr": "74400", + "stck_clpr": "75000", + "acml_vol": "9100000", + "acml_tr_pbmn": "682500000000", + "flng_cls_code": "00", + "prtt_rate": "0.00", + "mod_yn": "N", + "revl_issu_reas": "" + }, + { + "stck_bsop_date": "20240528", + "stck_oprc": "75200", + "stck_hgpr": "76200", + "stck_lwpr": "75100", + "stck_clpr": "76000", + "acml_vol": "10500000", + "acml_tr_pbmn": "798000000000", + "flng_cls_code": "00", + "prtt_rate": "0.00", + "mod_yn": "N", + "revl_issu_reas": "" + } + ] +} diff --git a/services/worker/testdata/providers/kis/provider_symbols.sample.json b/services/worker/testdata/providers/kis/provider_symbols.sample.json new file mode 100644 index 0000000..13d4345 --- /dev/null +++ b/services/worker/testdata/providers/kis/provider_symbols.sample.json @@ -0,0 +1,43 @@ +{ + "provider": "kis", + "selectors": [ + { + "kind": "watchlist", + "symbols": ["005930", "000660"] + }, + { + "kind": "exchange", + "venue": "KRX", + "symbols": ["005930", "000660"] + }, + { + "kind": "sector", + "name": "semiconductors", + "symbols": ["005930", "000660"] + } + ], + "instruments": [ + { + "id": "KRX:005930", + "market": "KR", + "venue": "KRX", + "symbol": "005930", + "name": "Samsung Electronics", + "currency": "KRW", + "provider_symbols": { + "kis": "005930" + } + }, + { + "id": "KRX:000660", + "market": "KR", + "venue": "KRX", + "symbol": "000660", + "name": "SK Hynix", + "currency": "KRW", + "provider_symbols": { + "kis": "000660" + } + } + ] +}