13 Commits (main)

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 1d087b54a4 feat: day_gainers 5분봉 수집 + News v2 ingest + overlay 정리
주요 변경사항:

gainer snapshot (신규)
- gainer_snapshots 테이블: 5분 단위 intraday top-100 day_gainers 스냅샷
- APScheduler CronTrigger (*/5 min, Mon-Fri ET) + 장중 guard (9:30~16:00)
- alembic migration p7g8h9i0j1k2

/stocks/gainers 엔드포인트 (신규)
- yfinance day_gainers preset, 실시간(캐시 없음)
- yfinance_plus screen() wrapper — 모든 screen() 호출에 세션 풀 + 브라우저 지문 우회 적용

News v2 ingest (신규)
- Alpaca News + StockTwits + Finnhub 수집 파이프라인
- news_headlines 테이블 + scheduler (5분 realtime poll, 일 1회 backfill)

Overlay 정리
- 미사용 서브시스템 제거: wikimedia, youtube, google_trends, feature_builder, overlay_scorer
- Yahoo RSS 어댑터 유지, 파이프라인 단순화

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
I Luk Kim db9abd7786 feat: SEC Form 4 PIT + SC 13D/G activist ownership API
## 신규 엔드포인트
- GET /insider/form4/{ticker} — PIT-safe Form 4 거래 목록 (as_of 필수)
- GET /insider/form4/by-date/{date} — 특정 공시일 cross-ticker 거래
- GET /insider/form4/aggregate/{ticker} — 내부자 매수 집계 (window_days)
- GET /ownership/13dg/{ticker} — SC 13D/G activist 이벤트 (as_of 필수)
- GET /ownership/13dg/active — 현재 활성 activist 포지션 (window function PIT)

## 데이터 인프라
- activist_ownership_events 테이블 (42,329행, 최근 2년 bootstrap 완료)
- insider_transactions: is_ceo/is_cfo/is_c_suite/purchase_pct_of_holding/owner_relationship 컬럼 추가
- SECFullIndexService: company.idx fixed-width 파서, form345.zip 파서
- ActivistOwnershipService: SGML header issuer 해석 + cover-page XML/HTML enrich
- SEC ingest scheduler: 매 영업일 09:00 ET daily ingest + 30분 enrich job
- scripts: bootstrap_form4_by_ticker.py, bootstrap_form4_2y.py, bootstrap_13dg.py

## 버그 수정 (enrich 품질)
- filing_url double edgar/ prefix 제거
- XML: classPercent + amountBeneficiallyOwned + reportingPerson... 태그 추가
- HTML: 태그 제거 후 regex 적용 (CSS 10pt → shares 오인식 방지)
- HTML: ownership_pct > 100 방어 로직

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 7f28266bd6 feat: company metadata 안정화 — 실제 sector/industry/exchange 보강
- CompanyMetadataService 신규: Redis → registry → yfinance 순서 조회,
  semaphore(5) 보호, 24h cache, companies + registry UPSERT
- GET /company/{ticker} + POST /company/bulk (최대 100개, 세션 격리)
- companies 테이블에 exchange/country/market_cap 컬럼 추가 (alembic)
- CompanyInfo 스키마 exchange/country/market_cap 필드 추가
- _is_placeholder() 체크: 기존 "Technology/Software/XYZ Corporation" 행 재보강
- financial endpoint: financials/price 실패 시 company block은 유지 (500 방지)
- bulk endpoint: 각 ticker마다 독립 AsyncSession으로 동시 세션 충돌 방지
- scripts/backfill_registry_sector.py: 9376개 NULL-sector 일괄 보강 스크립트

검증: AU→Basic Materials, USAS→Basic Materials, CPRX→Healthcare,
      HE→Utilities, ACHR→Industrials, ZZZZZZ→404, AAPL 기존 데이터 유지

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 409966a478 fix: error_logs.path VARCHAR(500) → TEXT — 서버 응답 불가 해결
멀티 티커 요청 URL이 500자를 초과하면서 error_logs INSERT가
StringDataRightTruncationError로 실패, 미들웨어에서 매 에러 응답마다
DB rollback 대기가 발생해 서버가 응답 불가 수준으로 느려졌음 (2562회 반복).

- error_logs.path: String(500) → Text (model + migration j1b2c3d4e5f6)
- request_logs.path: String(500) → Text (model + migration i0a1b2c3d4e5)
- docker-compose: alembic/ + alembic.ini 볼륨 마운트 추가
  (이전에는 컨테이너 내부에만 있어 마이그레이션 적용 불가)
- middleware: 요청마다 찍히던 logger.info → logger.debug 로 변경

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 25603c390c feat: 8-K filing parser — Item 추출 + event 생성
AVGO 8-K (accession 0001193125-26-144028, Item 8.01, Google TPU 공급계약)이
DB에 수집은 되었으나 파싱/event 생성이 불가했던 문제 해결.

구현:
- sec_8k_parser.py: 8-K primary document HTML 파싱
  - extract_items(): regex 기반 Item 헤더 추출, 목차 중복 제거 (last-wins)
  - _strip_ixbrl_viewer(): documents_json의 /ix?doc=... URL → 직접 URL 변환
  - _find_primary_doc_url(): primary_document_url 우선 사용 (iXBRL viewer 회피)
  - Item 8.01 단독 filing: exhibit(9.01) 없이 본문에서 직접 content 추출
  - Item 9.01 skip, 나머지는 ITEM_EVENT_MAP으로 event_type 분류
  - Exhibit enrichment: 2.02/7.01/8.01 + EX-99.1 있을 때 exhibit content 우선
- sec_filing_events 테이블 신설 (UniqueConstraint: accession_number + item_number)
- sec_filings 테이블에 parsed_status / items_json 컬럼 추가
- index_filings() 완료 후 신규 8-K auto-parse 트리거
- GET /filings/events/{ticker}: lazy parse + 조회
- POST /filings/events/parse/bulk: backfill용 일괄 파싱
- FilingSummary에 parsed_status / items 필드 포함
- alembic migration: g8a9b0c1d2e3
- 테스트 22개 추가 (extract_items, strip_ixbrl, find_primary_doc, parse_filing)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 3eb736445b feat: PIT dividend calendar + earnings surprise calendar endpoint
dividend calendar:
- DividendCalendar 모델 (PIT revisioned: as_of_date 컬럼으로 lookahead bias 방지)
- FINRA 5yr 데이터 backfill + yfinance 갱신 지원
- GET /dividends/calendar/{ticker}, POST /dividends/calendar/bulk
- alembic migration: f7a8b9c0d1e2

earnings:
- GET /earnings/calendar/{ticker}: ex-dividend 방식 earnings calendar 제공
- EarningsSurprise 모델에 fiscal_date 인덱스 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim b3092a0d5e feat: 백테스팅 유니버스 과거 시점 주식 스크리닝 (Historical Stock Universe)
## 새 기능
- GET  /universe/screen — 과거 날짜 기준 시총/섹터/거래소 필터링
- GET  /universe/registry — 추적 종목 목록 조회
- POST /universe/admin/discover — yfinance screener로 US 주식 자동 등록
- POST /universe/admin/build-snapshots — SEC EDGAR × yfinance 월별 시총 스냅샷 생성

## 데이터 모델
- universe_ticker_registry: 종목 마스터 (ticker, name, cik, sector, industry, exchange)
- universe_snapshot: 월별 스냅샷 (ticker, snapshot_date, market_cap, close_price, shares_outstanding)
  - 인덱스: (snapshot_date, market_cap) — 핵심 스크리닝 쿼리 최적화
  - ~4000종목 × 120개월 ≈ 480K 행 예상

## 데이터 흐름
1. SEC EDGAR companyfacts → shares_outstanding (최신, 주가분할 반영)
2. yfinance bulk download 1mo interval → 월별 종가
3. market_cap = latest_shares × close_price (yfinance 분할조정 가격과 일관성)

## 제한사항
- Survivorship bias: 현재 상장 종목만 (상폐 종목 미포함)
- 자사주 매입으로 과거 시총 ~20% 오차 가능 (분할 오차 방지가 주목적)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim e2cd16035a feat: Form 4 내부자 거래 + Earnings Surprise + FINRA 확장
F1: SEC Form 4 내부자 거래 (insider transactions)
- GET /insider/transactions/{symbol} — Form 4 거래 내역 조회
- GET /insider/summary/{symbol} — 3/6/12개월 매수/매도 집계
- SEC EDGAR submissions JSON → Form 4 XML 파싱 → DB 저장
- 자동 인덱싱 (첫 조회 시 SEC에서 페치)
- joint filing, derivative/non-derivative 거래 모두 지원

F2: Earnings Surprise (Alpha Vantage)
- GET /earnings/surprise/{symbol} — 분기별 EPS surprise
- reported EPS vs estimated EPS, beat/miss streak 계산
- ALPHA_VANTAGE_API_KEY 환경변수 필요 (무료 tier: 25 req/day)
- DB 캐싱으로 반복 호출 시 API 절약

F3: FINRA Short Volume 확장
- days 파라미터: max 365 → 3650 (10년)
- limit 파라미터: max 1000 → 10000
- 5년치 백필 완료 반영

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 6a98cb2d94 perf: API 성능 개선 — 로그 분석 기반 6개 항목 수정
P0 — 500 에러 대폭 감소 (1.1% → 0.11%)
- price.py / financial.py: except HTTPException: raise 추가 →
  HTTPException(404)이 except Exception에 잡혀 500으로 재포장되던 버그 수정
- real_sec_financial_service: _get_price_data_for_period에 try/except 추가 →
  yfinance 실패가 financial 엔드포인트 500으로 전파되지 않도록 방어
- price.py: TimeoutError 별도 핸들러 추가 → yfinance 타임아웃 시 503 반환

P1 — _store_price_data() N+1 → batch upsert
- price_data_service: 252회 개별 SELECT+INSERT 루프를
  pg_insert(PriceData).on_conflict_do_nothing('uq_price_data') 단일 쿼리로 교체

P2 — financial/data 캐시 TTL 1h → 24h
- financial.py: 재무 데이터는 분기 발표 주기 → _FIN_TTL = 86400

P3 — 과거 가격 데이터 TTL 연장
- price.py: end_date < today-1 이면 TTL=7일, 나머지 1h 유지

P4 — 요청 로그 비동기 배치 처리
- error_logger.py: asyncio.Queue(10_000) 추가, _log_request를 put_nowait으로
  변경 (논블로킹), 백그라운드 _request_log_flusher 코루틴 (1s/100건마다 flush)
- main.py: 앱 시작 시 start_request_log_flusher() 호출

P5 — 누락 DB 인덱스 추가
- financial.py: CalculatedMetrics에 calculation_date, period_date 단독 인덱스
- attention.py: AttentionFeaturesDaily에 ticker 단독 인덱스
- alembic b3c4d5e6f7a8: 위 3개 인덱스 생성 마이그레이션 (idempotent)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 8c9abe3e31 feat(attention): Attention 서브시스템 추가 — 이벤트 중심 Wikipedia/GDELT 관심도 피처
## 주요 기능
- Entity resolver: ticker → canonical name → Wikipedia 매칭
  - SEC company_tickers.json fallback으로 placeholder name 자동 수정
  - all-caps SEC 이름 title-case 변환, "Com" suffix 처리
- Wikipedia 페이지뷰 수집 + spike_10d / zscore_20d 피처 계산
- GDELT V2 DOC API 뉴스 기사 수집 (2017-01-01 이후)

## GDELT rate limit 제약 강제
- /event/{ticker} 온디맨드 GDELT 수집 제거 (IP ban 방지)
- 프로세스 전역 asyncio.Lock + 10초 최소 간격 강제
- 429 시 exponential backoff (30→60→120s)
- news.gdelt_status 필드로 클라이언트에 수집 상태 명시
  ('collected' | 'not_collected' | 'not_available')

## API
- GET  /api/v1/attention/event/{ticker}?event_date=YYYY-MM-DD
- GET  /api/v1/attention/entity/{ticker}
- POST /api/v1/attention/admin/resolve/{ticker}
- POST /api/v1/attention/admin/collect/wiki/{ticker}
- POST /api/v1/attention/admin/collect/gdelt/{ticker}  ← scheduler 전용

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 months ago
I Luk Kim 9e2a8aba47 Codebase improvements: caching decorator, Pydantic v2, DB indexes, connection pooling, Alembic
- Add @with_cache() decorator to eliminate ~15-line caching boilerplate per endpoint
- Apply decorator to 8 existing endpoints (stocks, alpaca, finra) and add caching
  to 6 previously uncached endpoints (news, etf, filings) with appropriate TTLs
- Migrate all @validator to @field_validator (Pydantic v2), deduplicate validation
  logic into shared functions in validators.py
- Fix datetime.utcnow() → datetime.now(timezone.utc), remove unused uuid import
- Convert ErrorLogResponse class Config → model_config = ConfigDict(...)
- Add health check exception logging instead of silent pass
- Add data_source indexes to PriceData and FinancialData tables
- Initialize Alembic with async engine configuration
- Add persistent HTTP sessions for SEC client (aiohttp) and FRED proxy (httpx)
- Add response_model schemas for Alpaca bars/intraday and news-only/social-only

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