13 Commits (888dc5be91285d1e1a63afded28dd39e91a2aade)

Author SHA1 Message Date
I Luk Kim 888dc5be91 feat: 생존편향-0 FINRA PIT 데이터셋 — 상폐 가격 백필 + PIT 뷰 + 2020 갭 메우기
숏볼륨 신호 검정을 위한 편향-0 데이터셋 완성:
- alpaca_client/price_service: adjustment 파라미터 추가 (get_bars, get_multi_bars,
  get_or_fetch_multi_bars) — adjustment='all'로 분할+배당 조정 일봉 수집 지원
- scripts/backfill_alpaca_daily_pit.py: FINRA 22,722 심볼(상폐 포함) 전체에
  Alpaca SIP 일봉 백필, row-weighted 잔존편향 리포트 출력
- scripts/backfill_finra_2020_gap.py: 2020-04~10 COVID 갭 (~138 거래일) 메우기
- scripts/export_pit_panel.py: FINRA×Alpaca 조인 패널 → parquet 핸드오프 export
- alembic q8h9i0j1k2l3: pit_universe_membership VIEW 생성 (날짜별 PIT 종목 집합)
- docker-compose: ./scripts live-mount 추가 (app/alembic과 동일 패턴)
- docs/DATA_COVERAGE.md: 실측 커버리지·API 파라미터·PIT 한계 업데이트

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
I Luk Kim b4d8c97ba2 fix: Alpaca end 파라미터를 날짜 → datetime(UTC)으로 수정
날짜만 보내면(2026-04-15) Alpaca가 ET 자정(23:59 ET = 03:59 UTC)으로 해석해
당일 장 마감 후에도 "미래 end" 로 판정 → SIP 403 에러 발생.
RFC-3339 datetime 형식(2026-04-15T23:59:59Z)으로 변경하면
Alpaca가 UTC 기준으로 정확히 해석해 15분 규칙을 통과함.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim abfab0fa57 fix: OOM kill 방지 — Phase 4 경량화 + Semaphore 3으로 제한
Worker가 OOM kill로 죽는 것을 확인 (docker inspect: OOMKilled=true).
호스트 메모리: 물리 RAM ~68MB 여유, 스왑 97% 사용 상태.

원인: Phase 4에서 SELECT AlpacaPriceData (전체 ORM 객체) 로드.
- 75 ticker × 78 5분봉 = 5,850개 ORM 객체/요청
- SQLAlchemy ORM instrumentation으로 dict 대비 ~10x 메모리 소비
- Semaphore(10) → 최대 10개 요청 동시 처리 = 피크 수백 MB ~ 1GB

수정:
- Phase 4: SELECT * → SELECT 7 columns only (ticker, date, OHLCV)
  → result.all()로 경량 Row namedtuple 반환, ORM 객체 생성 없음
  → SQLAlchemy Row는 속성 접근(row.date, row.open 등) 지원 — 엔드포인트 호환
- Semaphore(10) → Semaphore(3): 동시 처리 3개로 제한
  → 피크 메모리 ~70% 감소

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim ffaed4fc9d fix: 서버 응답불능 — limit-concurrency 20 + asyncio yield points
BaseHTTPMiddleware는 요청당 2개 asyncio task를 생성한다.
--limit-concurrency 100은 여전히 200개 task가 event loop를 포화시켜
health check 포함 모든 요청이 block됐다.

변경 사항:
- docker-compose.yml: --limit-concurrency 100 → 20
  (세마포어 10개 처리 + 10개 대기, 나머지 20초과 시 503으로 즉시 반환)
- alpaca_price_service.py: upsert 루프 청크 사이 + Phase 4 ORM 일괄 생성 후
  await asyncio.sleep(0) 추가 — 동기 CPU 블로킹 중 event loop yield 보장

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim c203f3920b refactor: session-per-phase — DB 커넥션을 외부 API 호출 중 해제
서비스 메서드가 db: AsyncSession을 인자로 받아 외부 API 호출(yfinance 30-60s,
Alpaca HTTP) 중에도 DB 커넥션을 잡고 있던 구조를 제거.

변경 패턴 (Session-per-phase):
  Before: Endpoint(db) → Service(db) → DB check → API call(30s 세션 유지) → DB store
  After:  Endpoint()   → Service()   → DB check(세션1) → API call(세션 없음) → DB store(세션2)

변경 파일:
- alpaca_price_service.py: fetch_and_store_bars, get_or_fetch_multi_bars에서 db 제거
- price_data_service.py: get_or_update_price_data, get_multiple_tickers_data_optimized에서
  db 제거; _fetch_price_data(새), _bulk_fetch_price_data(새, lambda 클로저 버그 수정);
  _fetch_and_store_price_data, _bulk_fetch_and_store_price_data, get_multiple_tickers_data 제거
- alpaca.py, price.py: Depends(get_db) 제거 (GET /latest 제외)
- financial_service.py, real_sec_financial_service.py: 호출 인자 정리

결과: "idle in transaction" 커넥션 0개, 풀 고갈 원인 근본 해결

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 5bcba8632c fix: fetch_and_store_bars() interval 컬럼 추가 + CHUNK 분할
- interval 누락으로 uq_alpaca_price_data constraint 위반 방지
- 단일 INSERT → CHUNK=2300 분할 (14 params/row, asyncpg 한도 대비)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim e6ea1abb0e fix: asyncpg param 한도 초과 — INSERT CHUNK 2900→2300
interval 컬럼 추가로 row당 파라미터 14개로 증가.
2900 × 14 = 40,600 > 32,767 → InterfaceError 발생.
2300 × 14 = 32,200으로 안전 범위 내 수정.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim af26df8895 feat: AlpacaPriceData에 interval 컬럼 추가 — 일봉/분봉 DB 충돌 해결
- alpaca_price_data 테이블에 interval 컬럼 추가 (VARCHAR, default '1d')
- unique constraint: (ticker, date) → (ticker, date, interval)
- get_or_fetch_multi_bars(): interval 필터로 coverage 체크 및 DB 조회
- insert rows에 interval 포함
- asyncpg param limit 대비 CHUNK 3000→2900 (파라미터 11개/행)
- alembic 마이그레이션: h9b0c1d2e3f4

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 800bb9b4a6 fix: /alpaca/intraday/{ticker} SIP 피드로 변경 + 어제까지 날짜 제한
- fetch_bars_raw()에 feed 파라미터 추가
- /intraday/{ticker}: IEX → SIP, end_date >= 오늘이면 400 에러
- 캐시 TTL: 5분 → 24시간 (과거 데이터는 변하지 않음)
- openapi.json 업데이트

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 8e58fdffa5 feat: Alpaca intraday 엔드포인트 분리 — /intraday (SIP, 과거) + /intraday/today (IEX, 당일)
- GET /alpaca/intraday: SIP 피드로 변경, end_date >= 오늘이면 400 에러, 백테스트용
- GET /alpaca/intraday/today: 신규, IEX 피드, 오늘 당일 실시간 전용, force_refresh=True
- AlpacaPriceService.get_or_fetch_multi_bars()에 feed 파라미터 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 2081611884 fix: multi-ticker daily bars — DB storage + DB-first fetch logic
- AlpacaPriceService.get_or_fetch_multi_bars(): checks DB max_date per
  ticker, only fetches missing ranges from Alpaca, upserts with chunking
  (3000 rows/chunk, asyncpg 32767-param limit) then reads back from DB
- GET /price/data endpoint: now uses service + Depends(get_db); subsequent
  calls for same date range skip Alpaca entirely
- force_refresh=true bypasses DB check and re-fetches all from Alpaca

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 2c4b4d583d perf: API 성능 개선 Round 3 — 8개 항목 (P15-P22)
P15: FINRA ingest N+1 쿼리 → chunked batch upsert (on_conflict_do_nothing)
P16: Request log 통계 4개 COUNT 쿼리 → 단일 case() 집계 쿼리
P17: stocks/52-week-gainers (1h), stocks/trending (30m) 캐시 추가
P18: Alpaca bars/data 캐시 TTL None→86400 (과거 불변 데이터)
P19: Request log flush 배치 100→500, 간격 1s→2s
P20: Overlay feature_builder matched_symbols Python 필터 → DB JSONB @> 연산자
P21: with_cache Pydantic 모델 캐시 키에 model_dump_json() 사용
P22: Alpaca price 저장 SELECT+filter → batch upsert (on_conflict_do_nothing)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim bf6932ca2f Add Alpaca-specific DB table and Redis caching for Alpaca/FINRA endpoints
Separate Alpaca price data into dedicated AlpacaPriceData table to avoid
UniqueConstraint('ticker', 'date') conflicts with Yahoo Finance PriceData.
Add Redis caching (build_cache_key/get_cached_response/set_cached_response)
to 3 Alpaca endpoints and 2 FINRA query endpoints with appropriate TTLs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago