직접 DB/서비스에 접근하는 스크립트 패턴을 REST API 패턴으로 교체:
- 삭제: scripts/backfill_finra_2020_gap.py (기존 POST /finra/admin/ingest으로 충분)
- 삭제: scripts/backfill_alpaca_daily_pit.py
- 삭제: scripts/export_pit_panel.py (외부 DB 연결으로 대체)
- 추가: POST /api/v1/alpaca/admin/backfill-pit
BackgroundTasks 패턴, FINRA 전체 PIT 심볼 백필, adjustment=all
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1. materialize_features identity-map 버그 수정
- upsert commit 후 populate_existing=True로 재SELECT
- resolve_entity와 동일 패턴 (SQLAlchemy async stale cache)
2. @with_cache 조건부 TTL — null 응답 캐시 단축
- wiki.views=null → TTL 300s (5분)
- 완전한 응답 → TTL 3600s (1시간)
- 기존: null 응답이 1시간 캐시되어 재수집 영구 차단
3. 재실체화 조건 확장: wiki_zscore_20d is None도 재트리거
- wiki_views는 있지만 lookback 부족으로 zscore만 null인 케이스 처리
4. POST /admin/entity/{ticker}/override 엔드포인트 추가
- wiki_title 수동 지정 + is_manual_override=True 설정
- CSCO→Cisco, DKNG→DraftKings 잘못된 매핑 수정용
5. entity_resolver: 소송 페이지 패턴 억제
- "X v. Y" 형식 제목 score=0.05 (예: FSF v. Cisco Systems)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
[사안 A] total_available 정확성 수정
- raw.get('count')(페이지당 행수=page_size)를 raw.get('total')(실제 매칭
총수)보다 먼저 읽어 total_available=250/total_pages=1로 잘못 보고됨
- 'total' 우선으로 교정 (screen_stocks + screen_preset)
- 732행 집합에서 total_available 250→732, total_pages 1→3 검증
[사안 B] min_dollar_volume opt-in 파라미터 신규
- 가격(regularMarketPrice) × averageDailyVolume3Month 기준 달러거래량 필터
- 기본 OFF: 미지정 시 기존 경로 byte 동일 (회귀 0 검증)
- 지정 시 min_avg_volume을 Yahoo에 전달하지 않음 (고가·저주식수 우량주
BLK/KLAC가 소스에서 잘리는 것 방지) → 달러거래량 게이트가 대체
- _collect_all_quotes로 전체 매칭 집합 페이지네이션(24p/6000행 상한,
초과 시 truncated 플래그) 후 post-filter + 서버측 재정렬
- @with_cache key_params에 min_dollar_volume 등록 (미지정/지정 캐시 분리)
- 검증: min_dollar_volume=1e8 시 BLK/KLAC(p1)·URI/GRMN(p2) 전부 포함,
exclude_types·market_cap_min·price_min 동시 정상 적용
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
gainer_snapshots 테이블을 읽는 엔드포인트가 없어 백테스팅이 불가능했던 문제 해결.
?at=<ISO8601> 생략 시 최신 snapshot, as-of semantics로 장외 시각도 자연스럽게 처리.
openapi.json 동기화.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- /form4/{ticker}, /form4/aggregate/{ticker}: DB에 ticker 데이터 없으면 SEC에서 800일치 자동 fetch (index_form4s)
- force_refresh 파라미터 추가로 on-demand 재인덱싱 지원
- Form4Response, Form4AggregateResponse에 metadata 필드 추가 (auto_fetched, fetched_count)
- bootstrap_form4_by_ticker.py: --tickers 옵션 추가로 특정 ticker만 targeted backfill 가능
- audit_form4_coverage.py: universe 커버리지 검증 스크립트 신규
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- financial.py: 캐시 히트 시 company 블록을 CMS에서 항상 refresh
(financial:data:* 캐시에 stale company 블록이 임베드된 문제 해결)
- financial_service.py: _is_placeholder() 확장 — exchange=null인
Technology/Software 종목도 placeholder로 감지 (AVGO-류 미검출 해결)
- company_metadata_service.py: _sync_fetch() 개선 — yfinance 첫 번째
호출은 auth 토큰 없이 partial 응답을 반환할 수 있으므로 sector가
없는 EQUITY 종목에 대해 한 번 retry (auth 캐시 후 full data 획득)
- docker-compose.yml: 컨테이너 시작 시 yfinance_plus 로컬 버전을
site-packages에 자동 복사 (docker restart 후 override 소실 방지)
검증: USAS/CPRX/HE/ACHR/AVGO 모두 실제 sector/industry/exchange 반환
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
반복적인 서버 응답불능의 근본 원인 2가지를 해결:
1. BaseHTTPMiddleware → Pure ASGI 미들웨어 (app/middleware/error_logger.py)
- BaseHTTPMiddleware.call_next()가 요청당 asyncio task 2개 생성
- 20 동시연결 × 2 = 40 tasks → event loop scheduler 포화 → health check 타임아웃
- __call__(scope, receive, send) + send_wrapper 패턴으로 교체
- 요청당 단일 task, X-Request-ID 헤더 주입, 에러 응답 body 캡처 유지
- 불필요한 의존성 제거: BaseHTTPMiddleware, Request, Callable, get_db, AsyncSession
2. gc.collect() 추가 + --limit-max-requests 제거 (alpaca.py, docker-compose.yml)
- --limit-max-requests 500: 500요청 후 단일 worker 종료 → 서비스 gap 발생
- 대신 intraday 요청 처리 후 gc.collect()로 Python heap 명시적 회수
- 장시간 백필 중 메모리 누적 방지, worker 재시작 없이 안정 운영
3. Semaphore(3 → 5): BaseHTTPMiddleware 제거로 task 수 절반 → 처리량 복원
유지: mem_limit 2g, --limit-concurrency 20, Phase 4 경량 쿼리, request_logs 7일 retention
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
동시 Alpaca intraday 요청이 50개 이상 쌓이면 BaseHTTPMiddleware의
task 스케줄링 오버헤드로 이벤트 루프가 응답불능 상태가 되는 현상 수정.
- alpaca.py: _INTRADAY_SEMAPHORE(10) 추가 — /intraday, /intraday/today
양쪽 핸들러를 감쌈. 11번째 이후 요청은 세마포어 대기(비용 없음)
- docker-compose.yml: --limit-concurrency 100 추가 — 100개 초과 시
uvicorn이 503 반환, health 엔드포인트 보호
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- GET /price/data를 AlpacaPriceService 대신 PriceDataService(yfinance)로 교체
- ?ticker= alias 추가 (기존 ?tickers= 유지, 인터페이스 호환)
- _bulk_fetch_price_data: yfinance end 파라미터 exclusive 미반영 (+1일 누락) 수정
- get_multi_ticker_daily_bars: end_dt를 min.time(00:00) → max.time(23:59:59)으로 수정
(DB 쿼리 date <= end_dt 에서 당일 레코드가 필터링되던 버그)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
서비스 메서드가 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>
기존: end_date >= today 이면 무조건 400
변경: 장 마감(오후 4시 ET) 후에는 당일 날짜도 SIP로 조회 가능
- zoneinfo.ZoneInfo("America/New_York") 기반 ET 시각 체크
- 장 중 당일 요청 시 /intraday/today 안내 메시지
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
제거: /bars/{ticker}, /data/{ticker}, /intraday/{ticker}, /snapshot/{ticker}
유지:
- GET /alpaca/status
- GET /alpaca/intraday (SIP, 과거, 멀티 종목)
- GET /alpaca/intraday/today (IEX, 당일, 멀티 종목)
- GET /alpaca/snapshot (IEX, 단일/멀티 통합)
openapi.json 업데이트 (95 → 91 endpoints)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both GET /price/data and GET /alpaca/intraday:
- description에 배치 제한 설명 추가 (100개/요청, 자동 분할, 응답시간 선형 증가)
- 502 에러 메시지에 심볼 수 초과 가능성 힌트 추가
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GET /price/intraday (Yahoo Finance):
- 15분 지연, 1m=7일/5m-30m=60일/1h=730일 한계
- 백테스트 용도, 실시간 전략 부적합
GET /alpaca/intraday (Alpaca IEX):
- 실시간, IEX 피드 2~5% 커버리지
- 거래량 낮게 표시, 가격 레벨은 유사
- ORB 등 당일 실시간 전략 용도
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Alpaca free plan blocks recent SIP data (403 on same-day requests).
Replace with Yahoo Finance which has no subscription requirement.
- PriceDataService.get_multi_intraday(): yf.download() in chunks of 50,
handles both single (flat DataFrame) and multi-ticker (MultiIndex) cases
- GET /price/intraday?tickers=...&interval=5m&start_date=...&end_date=...
→ same AlpacaMultiBarsResponse format (bars: {sym → [{timestamp,ohlcv}]})
→ source="YAHOO_FINANCE", Redis 5-min TTL cache
- /alpaca/intraday still exists for historical data (works on free plan)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Same get_or_fetch_multi_bars() approach as daily bars:
- stores intraday rows in AlpacaPriceData (full timestamp as PK component)
- subsequent requests for historical periods served from DB
- same-day requests during market hours always hit Alpaca (max_date < end_dt)
- force_refresh=true bypasses DB check
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
- GET /alpaca/snapshot/{ticker} — 단일 티커 실시간 가격/bid-ask/OHLCV/등락률
- GET /alpaca/snapshot?tickers=A,B — 최대 100개 멀티 티커 일괄 조회
- AlpacaClient.get_snapshot / get_snapshots 메서드 추가
- AlpacaSnapshotResponse / AlpacaMultiSnapshotResponse 스키마 추가
- docs/PYTHON_CLIENT.md 사용 예시 업데이트
- 캐시 없음 — 매 요청마다 Alpaca API 직접 호출
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- POST /filings/events/parse/{accession_number}: 특정 filing 강제 재파싱
(parsed_status 무관하게 pending으로 리셋 후 즉시 파싱)
- POST /filings/events/parse/bulk에 force_reparse: bool 추가
(true이면 succeeded/failed도 pending으로 리셋 후 재처리)
- BulkParseRequest에 force_reparse 필드 추가
사용법:
curl -X POST /api/v1/filings/events/parse/0001193125-26-144028
curl -X POST /api/v1/filings/events/parse/bulk -d '{"tickers":["AVGO"],"force_reparse":true}'
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
asyncpg 커넥션은 event loop에 바인딩됨. API의 AsyncSessionLocal을
다른 loop에서 사용하면 'Future attached to a different loop' 오류 발생.
→ build thread 내에서 전용 engine + session factory 생성.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
BackgroundTask가 FastAPI event loop을 공유해서 deadlock 발생 →
별도 thread에서 새 asyncio event loop으로 실행하도록 변경.
- API event loop 완전 분리
- DB 커넥션 풀 독립적 사용 (per-batch factory session)
- 빌드 중 API 정상 응답 유지
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
## 새 기능
- 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>
1. Earnings Surprise: Alpha Vantage → SEC EDGAR XBRL 전환
- companyfacts API에서 EarningsPerShareDiluted/Basic 추출
- QoQ surprise 계산 (현재 EPS - 이전 분기 EPS)
- 15일 이내 중복 분기 제거 (10-Q 우선)
- force_refresh 시 기존 데이터 삭제 후 재인덱싱
- API 키 불필요, 2009년~ 커버리지
2. Insider transactions: days max 1095→3650, older pages 3→10
3. FINRA: 이전 커밋에서 이미 days=3650, limit=10000 적용됨
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>
- docs/DATA_COVERAGE.md 신규 생성: 엔드포인트별 실제 DB 보유 범위,
이론적 최대 범위, 백필 방법, SQL 확인 쿼리 포함
- FINRA/Alpaca/stocks/filings 엔드포인트 description에 데이터 범위 및
백필 방법 안내 추가 (Swagger UI에 표시됨)
현재 백필 필요 항목:
- FINRA: 2026-02-10~ 28거래일만 존재 → 2025년치 백필 권장
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## Overlay 버그 수정
- **collect_all 동시성 오류**: asyncio.gather로 공유 DB 세션에 동시 접근 → SQLAlchemy 오류
발생. 어댑터를 순차 실행으로 변경
- **feedparser/apscheduler/pytrends 미설치**: Docker 이미지 재빌드로 패키지 영구 포함
- **중복 job log 항목**: _log_job이 매번 새 행 삽입 → running+completed 중복 생성.
기존 running 행을 업데이트하도록 수정
- **admin/health 잘못된 job_type**: yahoo_rss/wikimedia 등 존재하지 않는 타입 조회.
실제 로깅되는 collect_all/feature_build만 조회하도록 수정
- **source_presence 항상 false**: z-score가 계산 불가능하면(2일 미만 데이터) source가
false로 표시됨. 실제 데이터 존재 여부(headline_count_24h > 0 등)로 판단하도록 수정
- **top-movers 심볼 중복**: 파이프라인 실행 횟수만큼 같은 심볼 반복 출력.
심볼별 최신 레코드만 조회하는 서브쿼리로 수정
- **YouTube None 곱셈 오류**: view_count * channel_weight에서 None이면 TypeError.
(or 0) / (or 0.5) 가드 추가
## Trends 기능 수정
- **ThemeTopicMap 자동 시딩**: 파이프라인 최초 실행 시 TOP_50_SYMBOLS에 대한
기본 topic 매핑 자동 생성
- **GOOGLE_TRENDS_ENABLED=true**: docker-compose.yml에 환경변수 추가
- **theme_heat_z 항상 null**: feature_builder에 build_trends_features() 메서드
누락 → OverlayTrendObservation 데이터가 점수에 반영 안 됨. 메서드 추가 및 연결
- **POST /admin/seed-topics**: ThemeTopicMap 수동 시딩용 admin 엔드포인트 추가
## OpenAPI 문서 개선
- 모든 엔드포인트에 summary/description 추가 (filings, news, database, etf, stocks,
screener, fred, attention, overlay)
- Pydantic 스키마에 json_schema_extra example 추가 (attention, filing)
- 누락된 태그 6개 추가 (attention, attention-admin, database, fred, error-logs,
request-logs)
- 루트(/) 랜딩 페이지를 Swagger UI로 리다이렉트로 교체
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>