17 Commits (e2389eba31b8891c735edd330495e929748ba35f)

Author SHA1 Message Date
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 1f0017a4ea fix: company metadata placeholder 제거 — sector/industry 실제 값 보장
- 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>
4 months ago
I Luk Kim 59f78cd3ee fix: OOM → zombie worker 근본 수정 + 대량 스캔 방어 벡터 추가
- docker-compose: uvicorn --reload 제거 (PID 1 직접 실행) + mem_limit 2g→3g
  → OOM 시 컨테이너 종료 → restart:unless-stopped가 자동 복구
- /attention/event: Semaphore(8) + 10s fast-fail + wiki collector rate limit
- wiki_collector: Semaphore(2) + 0.25s min-interval + 429 log-burst 억제
- /filings/events: Semaphore(4) + 10s fast-fail (SEC HTML 다운로드 벡터 차단)
- sec_http_client: CIK 조회 실패 negative cache (1h) — 10MB dict 반복 스캔 제거

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 0c4446a6ec fix: 대량 스캔 시 서버 사망 방지 — 세마포어 fast-fail + limit-concurrency 조정
- Alpaca intraday: 세마포어 대기 10초 제한, 초과 시 429 즉시 반환
  (기존: 120초 대기하며 커넥션 슬롯 점유 → 이벤트 루프 포화)
- Filing search: 세마포어(8) + 10초 fast-fail 추가
  (기존: 동시성 제한 없이 A-Z 스캔 시 수백 개 요청 쌓임)
- limit-concurrency 50→25 (실제 처리량은 세마포어가 제한하므로 여유 슬롯 불필요)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim c0e656b226 refactor: Alpaca intraday 120초 타임아웃 + limit-concurrency 50 복원
asyncio.wait_for로 120초 타임아웃 명시, _do_fetch 내부 함수로 semaphore 래핑.
limit-concurrency를 20→50으로 복원 (세마포어 5개로 충분히 제어됨).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 months ago
I Luk Kim 8d1216aa81 fix: 서버 안정성 종합 — BaseHTTPMiddleware 제거 + gc.collect() + Semaphore(5)
반복적인 서버 응답불능의 근본 원인 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>
4 months ago
I Luk Kim 253086eaad fix: Python heap 누적 OOM 방지 — max-requests 500 + 컨테이너 메모리 2GB 제한
Python CPython 메모리 할당자는 freed 블록을 OS에 반환하지 않음.
수백 건의 백필 요청 처리 후 heap이 계속 성장 → 시스템 전체 OOM.

변경:
- --limit-max-requests 500: 500요청 후 uvicorn worker 자동 재시작
  → heap 초기화, 메모리 누수 방지
- mem_limit/memswap_limit 2g: 컨테이너 메모리 2GB 하드 캡
  → OS 전체가 아닌 컨테이너 안에서 OOM 제어
  → Docker restart: unless-stopped로 자동 복구

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 e25f7295a6 fix: 대규모 Alpaca 백필 시 uvicorn 이벤트 루프 포화 방지
동시 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>
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 c141b50543 chore: docker-compose에 ALPHA_VANTAGE_API_KEY 환경변수 추가 5 months ago
I Luk Kim 0752b29a63 perf: API 성능 개선 Round 2 — 9개 항목 (P6-P14)
Tier 1:
- P6: 미들웨어 request_info 지연 추출 (성공 경로에서 body/headers 읽기 제거)
- P7: GZip 압축 미들웨어 (minimum_size=1000, ~4x 압축)
- P8: Redis maxmemory 512MB + allkeys-lru 퇴거 정책

Tier 2:
- P9: _store_ticker_data() batch upsert 전환 (ON CONFLICT DO NOTHING)
- P10: DB 커넥션 풀 증가 (pool_size=20, max_overflow=30, 환경변수 설정)
- P11: Attention 엔드포인트 Redis 캐시 추가 (@with_cache ttl=3600)

Tier 3:
- P12: Dockerfile 멀티 워커 (--workers 4, dev는 --reload 오버라이드)
- P13: aiohttp ClientSession 싱글턴 공유 (4개 파일 7곳 TCP/TLS 재사용)
- P14: Pydantic model_validate → model_construct (bulk 경로 validation 스킵)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 months ago
I Luk Kim 4a0aff9ad6 fix(overlay): 다중 버그 수정 및 OpenAPI 문서 개선
## 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>
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
I Luk Kim 44f8979805 Fix frontend Docker dev environment for React 19 compatibility
- 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>
5 months ago
I Luk Kim 355aeb19d3 Initial commit with full project improvements
Security: config-based CORS, default secret warnings, sort_by validation
Error handling: debug logging in cache silent failures
Architecture: shared resolve_time_parameters, deduplicated logger init, unified route structure
Database: conditional SQLite/PostgreSQL engine, in-memory test DB, dialect-aware date formatting, optimized stats query
Docker: .dockerignore, pinned yfinance_plus commit
Dependencies: removed duplicates, added version upper bounds, removed unused axios
Frontend: custom _document/_error pages, adminApi client, Layout standardization, ESLint version update

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