You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

72 lines
2.5 KiB
Python

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"""add pit_universe_membership view
Revision ID: q8h9i0j1k2l3
Revises: p7g8h9i0j1k2
Create Date: 2026-05-30
PIT(Point-in-Time) 유니버스 멤버십 뷰.
finra_short_volume에서 (date, symbol)을 그대로 노출하는 뷰.
각 날짜에 실제 거래되던 종목 집합 = PIT 멤버십.
생존편향-0 달성 근거:
- finra_short_volume은 2018-08-01 ~ 현재, 22,722개 심볼 보유
- 그 중 16,143개는 현 활성 유니버스(9,635)에 없는 상폐/합병/과거 종목
- SIVB(2023-03-09), SBNY(2023-03-10), FRC(2023-04-28), TWTR(2022-10-27) 등
실제 상폐일에 정확히 끊김 → 생존편향 없이 PIT 멤버십 표현
사용 예:
-- 특정 날짜에 거래된 종목 집합 (PIT 유니버스)
SELECT DISTINCT symbol
FROM pit_universe_membership
WHERE d = '2023-03-09'
ORDER BY symbol;
-- 공매도비율 × PIT 멤버십 (생존편향-0 횡단면)
SELECT p.d, p.symbol, p.short_ratio, a.close
FROM pit_universe_membership p
LEFT JOIN alpaca_price_data a
ON a.ticker = p.symbol AND a.date::date = p.d AND a.interval = '1d'
WHERE p.d = '2023-03-09'
ORDER BY p.short_ratio;
한계:
- 티커 재활용: BBBY(2023-05 파산 → 2025-08 다른 엔티티 재사용) 등
심볼 기준 PIT는 상폐 경계에서 두 회사를 혼동할 수 있음.
엄밀 해결은 CUSIP/PERMNO(유료) 매핑 필요.
- 2020-04-01 ~ 2020-10-31 데이터 결손 (COVID 갭).
backfill_finra_2020_gap.py 실행 후 해소 가능.
- NMS 슈퍼셋: ETF/ADR/워런트(/U, /WS) 포함.
분석단에서 심볼 패턴 필터 권장.
"""
from typing import Sequence, Union
from alembic import op
revision: str = "q8h9i0j1k2l3"
down_revision: Union[str, Sequence[str], None] = "p7g8h9i0j1k2"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# CREATE OR REPLACE VIEW: idempotent, safe to re-run
# 성능: idx_finra_date (date), idx_finra_symbol_date (symbol, date) 인덱스를
# Postgres가 베이스 테이블에서 자동 활용하므로 별도 뷰 인덱스 불필요
op.execute("""
CREATE OR REPLACE VIEW pit_universe_membership AS
SELECT
date::date AS d,
symbol,
total_volume,
short_volume,
short_exempt_volume,
short_ratio,
market
FROM finra_short_volume
""")
def downgrade() -> None:
op.execute("DROP VIEW IF EXISTS pit_universe_membership")