## 주요 기능
- 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>
content.read()로 body를 읽은 후 resp.get_encoding() 호출 시
aiohttp 내부 self._body(None) 접근으로 예외 발생.
이 예외가 except Exception에 잡혀 6번 retry + 지수 backoff(최대 ~22초)
→ asyncio.wait_for 캔슬 → TimeoutError → 504.
Content-Type 헤더에서 charset 직접 파싱으로 교체.
기본값 utf-8, SEC는 대부분 charset 미지정이므로 실질적으로 항상 utf-8 사용.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
index 페이지 + exhibit 두 단계를 15초에 커버하기엔 너무 빡빡함.
미캐시 시 index fetch 최대 8초 + exhibit fetch 최대 12초 = 최악 20초.
max_bytes 수정으로 메모리 고갈은 이미 해결되었으므로
deadline은 25초로 조정 (원래 30초보다 5초 단축).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- fetch_text에 max_bytes 파라미터 추가: Content-Length 헤더로 다운로드 전 사전 reject,
헤더 없으면 content.read(max_bytes+1)로 제한적 읽기
- ValueError는 즉시 raise (retry 없음 — 크기는 재시도해도 안 줄어듦)
- in-memory 캐시(_text_cache) 1MB 가드: 대형 응답은 디스크 캐시에만 저장
- MAX_EXHIBIT_SIZE 1MB → 5MB, fetch_text(max_bytes=...) 호출로 다운로드 전 체크
- exhibit deadline 30초 → 15초 (서비스 + 엔드포인트 + bulk)
- 신규 테스트 5개: Content-Length 사전 거부, body 제한 읽기, 메모리 캐시 가드,
소형 캐시 유지, ValueError no-retry
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- SECHttpClient._deadline(인스턴스 변수) → contextvars.ContextVar로 교체
asyncio task별 독립 데드라인으로 싱글턴 공유로 인한 레이스 컨디션 해결
- bulk exhibit에 Semaphore(4) + 전체 300s 타임아웃 추가
동시 50개 코루틴이 Semaphore(2)를 무제한 점유하던 문제 해결
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- sec_filings_service: 루프 전 accession_number 일괄 pre-fetch로 N+1 제거
- sec_filings_service: 500건 단위 청크 커밋으로 all-or-nothing 트랜잭션 방지
- sec_filings_service: bulk 인덱싱 시 코루틴별 독립 세션 생성으로 동시 세션 충돌 해결
- sec_filings_service: index_filings 데드라인 60s → 120s, bulk timeout 동일 적용
- sec_http_client: _TokenBucket(10 req/sec) 추가로 SEC EDGAR 과부하 방지
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- README/API_DOCUMENTATION에 `just run dev` 개발 서버 실행 방법 추가
- `GET /stocks/index/{index_name}` 엔드포인트 문서화 (sp500/nasdaq100)
- justfile을 프로젝트 파일로 문서에 포함
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GET /stocks/index/{index_name} 엔드포인트 추가.
sp500/nasdaq100 구성 종목을 Wikipedia에서 실시간 파싱하여 반환.
24시간 Redis 캐시 및 30초 타임아웃 적용.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
asyncio.gather로 동시에 실행되는 exhibit 항목들이 단일 DB 세션을
공유하면서 "concurrent operations are not permitted" 에러 발생.
각 _fetch_one 태스크가 AsyncSessionLocal()로 독립 세션 사용하도록 수정.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PostgreSQL is the actual DB; create_all handles schema creation.
The SQLite workaround added in the previous commit is not needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Expose SEC ACCEPTANCE-DATETIME so downstream consumers (filing_poller)
can populate Document.accepted_at_utc and Event.filed_at_utc.
- SECFiling model: add nullable accepted_at TIMESTAMP column
- main.py startup: ALTER TABLE migration for existing SQLite DBs
- sec_filings_service: extract acceptanceDateTime from SEC JSON, store on
INSERT and force_refresh UPDATE
- FilingSummary schema: add Optional[str] accepted_at field
- filings endpoint: map accepted_at as ISO 8601 string in response
- CHANGELOG: v3.0.1 entry
Existing rows have accepted_at=NULL; backfill with force_refresh=true.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
Add SEC Filings section to root HTML docs, example requests, key features,
and OpenAPI tags for Swagger UI / ReDoc visibility.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extract shared SECHttpClient from ETF fetcher (retry, backoff, cache, throttle)
and apply it to both ETF and core SEC services, fixing missing rate limiting.
Add SECFiling DB model, Pydantic schemas, SECFilingsService with auto-indexing,
and REST endpoints at /filings/search, /filings/documents, /filings/exhibit.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Upgrade Node base image from 18-alpine to 20-alpine (npm compatibility)
- Use npm ci instead of npm install for reliable dependency extraction
- Use named volumes for node_modules and .next to avoid anonymous volume issues
- Upgrade React/ReactDOM to ^19.0.0 for Next.js 15.4.6 compatibility
- Change Dockerfile to use --legacy-peer-deps for peer dep conflicts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>