backend:
- fetch/kis.py: fetch_orderbook(code) — KIS FHKST01010200 (inquire-asking-price-exp-ccn)
호출. output1 의 askp1~10/bidp1~10 + 잔량 + 합계, output2 의 현재가/전일대비 파싱.
- api/orderbook.py: GET /api/orderbook/{code}. symbols 매칭 안 되면 404, KIS 키 없으면
skipped_missing_key 상태로 빈 호가 반환 (502 와 구분).
- main.py: orderbook_router 등록.
frontend:
- lib/api.ts: OrderbookResponse 타입 + api.orderbook(code).
- components/OrderbookPanel.tsx: 매도 10단계(위→아래 가격 내림차순) / 현재가(굵게, 전일대비)
/ 매수 10단계. 잔량 막대는 사이드별 max 정규화로 셀 안에서 자라남. ask=파랑, bid=빨강
(한국 거래소 관행). 10초 폴링.
- app/[code]/page.tsx: 사이드바에 SymbolSidebar 다음으로 OrderbookPanel 마운트.
실시간 ws 가 아니라 HTTP 폴링이라 KIS rate-limit 친화적. 표시 전용 — 주문/체결 endpoint 는
일절 import 하지 않음 (kis.py 문서 정책).
reviewer 지적사항 반영:
1. KIS 분봉이 한 번에 30개만 와서 10분봉이 최대 3개만 나오던 문제 →
fetch_minute_range() 추가. FID_INPUT_HOUR_1 을 30분씩 후퇴시키며
페이지네이션, 중복 ts 자연 dedupe, max_pages=20 으로 무한루프 방지.
_ensure_intraday_fresh 는 last_ts+1m ~ now 빈 구간만 채우므로 평소엔
1~2 페이지로 끝남.
2. 장외/주말에 매번 KIS 를 때리던 문제 →
- 주말: 'weekend' 반환, KIS 안 부름 (분봉 endpoint 는 당일만 지원)
- 평일 장외 + 오늘 데이터 있음: 'cached_closed' 반환
- 장중 + 10분 이내: 'fresh' 반환
토큰/조회 제한 다시 밟지 않음.
3. 프론트 horizon 입력 60 vs 백엔드 30 불일치 →
PredictionPanel 의 cap 을 30 으로 맞춤. 백엔드 predict.py 의 학습/검증
범위와 일치. placeholder 도 '1~30' 으로 명시.
- backend/app/api/chart.py: interval=10m|1d|1w|1mo. 10m 은 ohlcv_1m 을
time_bucket(10min) 으로 집계, stale(>10분) 이면 KIS 분봉 fetch 후 재조회.
1w/1mo 는 ohlcv_daily 를 date_trunc 로 집계. today 필드 추가.
- backend/app/fetch/kis.py: fetch_minute_price() 추가 (tr_id FHKST03010200).
KIS 응답 KST 시각을 tz-aware datetime 으로 변환, 오름차순 정렬.
- web/lib/api.ts: ChartInterval 타입, getChart(interval), predict(horizons[]).
- web/components/StockChart.tsx: 10m 이면 timeVisible. 일·주·월에서 오늘
화살표 마커 표시. ISO datetime 도 파싱.
- web/components/PredictionPanel.tsx: 단기/중기/장기 프리셋 + 사용자 직접
지정 (예: 1,2,3,7). API 에 horizons 배열 전달.
- web/app/[code]/page.tsx: interval 칩 (10분/일/주/월). 10m 일 때 60초마다
폴링. interval 별 기본 lookback (10m=1, 1d=180, 1w=730, 1mo=1825).
토큰 발급 1분/1일 제한 (EGW00133 등 403) 회피:
- _load_disk_cache / _save_disk_cache 로 /app/.cache/kis_token.json 영속화
· ./backend:/app 바인드 마운트로 호스트 backend/.cache/ 에 저장
· backend/.cache/ 는 .gitignore 에 이미 포함됨 (secret 비커밋)
· app_key prefix 캐시 무효화 키 (.env 갱신 시 자동 폐기)
· atomic write (tmp → rename) + 0600 권한
- get_token: 메모리 → 디스크 → 신규 발급 순으로 fallback
- 컨테이너 재기동해도 24시간 유효 토큰 재사용 → 발급 폭주 방지
restart.bat:
- restart → up -d --force-recreate --no-deps backend web
· restart 는 env_file 재로드 안 함 (.env 의 KIS_APP_KEY 변경이 무시됨)
· up -d 는 새 인스턴스 생성하며 env_file 다시 읽음
· --no-deps 로 db 는 절대 건드리지 않음 (postgres_data 영속)
10종목 시드 + pykrx OHLCV / 외인·기관 거래대금, KIS read-only EOD, OpenDART
공시, 네이버 금융 뉴스 스크레이퍼, 구글 뉴스 RSS, yfinance 거시(KOSPI/KOSDAQ/
USDKRW/US10Y) fetcher 를 추가하고 refresh_one / daily_batch / backfill /
APScheduler(16:00 KST) 파이프라인으로 묶음.
- backend/app/seed: 10종목 시드 (대형/고변동/테마/플랫폼/방어)
- backend/app/fetch: pykrx, kis, dart, news, macro, symbols_seed
- backend/app/pipelines: refresh_one, daily_batch, backfill(CLI), scheduler
- backend/app/api/refresh.py: POST /api/refresh/{code}?lookback_days=N
- backend/app/main.py: lifespan 으로 스케줄러 기동/종료, /health/keys 추가
- README: .env 복사 안내 보강
스모크 테스트 (실제 키 사용) 결과:
KIS token : ok (token 346자 발급)
KIS daily : 005930 11rows
DART list : 005930 30일 10건
Naver news : 005930 12건
Google RSS : "삼성전자" 92건
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>