Catalyst Data Collection Plan
바이오 트레이딩에서 가장 중요한 두 이벤트 — PDUFA date와 Phase readout — 를 공식/준공식 소스에서 전수 수집해 biotech.duckdb 내 통합 catalyst 테이블로 저장한다.
현재 보유 데이터는 clinical_trials.primary_completion_date 정도가 전부로, 실제 공시 기반 catalyst 이벤트는 없음. 이를 4개 소스 통합으로 보강.
Scope & Target
- 기간: 2017-01-01 ~ 현재 (+ forward-looking 미래 PDUFA 포함)
- 대상 종목:
universe테이블에 한 번이라도 포함된 모든 바이오 ticker (+excluded_symbols도 커버해서 survivorship bias 방지) - 이벤트 타입:
pdufa— FDA 승인 결정일phase_readout— Phase 1/2/3 topline 발표adcom— FDA AdCom 회의chmp— EMA CHMP opinion (옵션, 후순위)interim— interim readout (옵션)
- 커버리지 목표:
- PDUFA: 95%+
- Phase 2/3 readout: 85%+
- Phase 1 readout: 60%+ (중요도 낮음)
Target Schema
DuckDB biotech.duckdb 에 신규 테이블 catalysts 와 감사용 catalyst_sources 2개 추가.
catalysts (primary — merge된 결과)
CREATE TABLE catalysts (
id BIGINT PRIMARY KEY,
symbol VARCHAR NOT NULL,
catalyst_type VARCHAR NOT NULL, -- 'pdufa' | 'phase_readout' | 'adcom' | 'chmp' | 'interim'
phase VARCHAR, -- 'Phase 1' | 'Phase 2' | 'Phase 3' | NULL
drug_name VARCHAR,
indication VARCHAR,
nct_id VARCHAR,
event_date DATE NOT NULL, -- 실제 이벤트 발생일
announced_date DATE NOT NULL, -- 예정일이 최초 공시된 날 (look-ahead 방지 필수)
original_event_date DATE, -- PDUFA 연기 등 change tracking
outcome VARCHAR, -- 'positive' | 'negative' | 'mixed' | 'pending'
source VARCHAR NOT NULL, -- 최종 채택 소스
source_confidence INTEGER NOT NULL, -- 1(최고) ~ 4(최저)
source_url VARCHAR,
notes TEXT,
collected_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL
);
CREATE UNIQUE INDEX catalysts_natural_key ON catalysts(symbol, catalyst_type, event_date, COALESCE(nct_id, ''));
CREATE INDEX catalysts_symbol_date ON catalysts(symbol, event_date);
CREATE INDEX catalysts_type ON catalysts(catalyst_type);catalyst_sources (audit — 소스별 raw)
CREATE TABLE catalyst_sources (
id BIGINT PRIMARY KEY,
catalyst_id BIGINT REFERENCES catalysts(id),
source VARCHAR NOT NULL, -- 'edgar_8k' | 'biopharmacatalyst' | 'prnewswire' | 'clinicaltrials_gov' | 'manual'
event_date DATE NOT NULL,
announced_date DATE,
outcome VARCHAR,
raw_payload JSON, -- 원본 응답 (디버깅용)
source_url VARCHAR,
collected_at TIMESTAMP NOT NULL
);핵심 원칙: announced_date 필수
- 모든 row는 “이 정보가 언제 대중에게 공개됐는가” 를 알아야 함
- 없는 row는 백테스트에서 제외 (conservative)
- PDUFA의 경우: NDA/BLA acceptance PR 공시일
- Phase readout 의 경우: 회사 가이던스 발표일 (보통 2~4개월 전)
4개 소스 전략
| # | Source | 비용 | 커버리지 | 품질(1~4) | 역할 |
|---|---|---|---|---|---|
| 1 | EDGAR 8-K (Item 7.01 / 8.01) | 무료 | ~100% (미 상장사) | 1 (최고) | Primary |
| 2 | BioPharma Catalyst (historical) | $99~249/월 | 과거 5년+ 큐레이션 | 2 | 최종 검증/보강 |
| 3 | PR Newswire / BusinessWire | 무료 (스크래핑) | 부분 (PR 낸 것만) | 3 | EDGAR 보조 |
| 4 | ClinicalTrials.gov API | 무료 | 광범위 but proxy | 4 (최저) | baseline fallback |
Source 1 — EDGAR 8-K (Primary) 🥇
왜 1순위: 미 상장 바이오는 주요 임상 결과 / FDA 결정을 법적으로 8-K 공시 의무. 신뢰도 최고.
대상 공시
- Item 7.01 (Regulation FD Disclosure) — Phase readout / 임상 업데이트
- Item 8.01 (Other Events) — PDUFA 일정 확정, FDA acceptance, AdCom 확정
수집 방법
profile테이블에서 symbol → CIK 매핑 확보- EDGAR full-text search API:
https://efts.sec.gov/LATEST/search-index?q=...&forms=8-K&dateRange=... - 쿼리 키워드 세트:
- PDUFA:
"PDUFA","Prescription Drug User Fee Act","FDA acceptance" - Phase readout:
"topline","primary endpoint","Phase 2 results","Phase 3 results","met the primary endpoint","did not meet","missed"
- PDUFA:
- 각 8-K 페이지에서
exhibit 99.1(press release) URL 추출 - 본문 HTML 다운로드 (rate limit 10 req/sec 준수)
파싱 로직
- event_date = 공시일
- announced_date = 본문의
previously announced,as previously disclosed,in [month] [year]문구에서 역추출 - outcome = 첫 3문단 키워드 매칭
- drug_name / indication = 본문 NER (regex + LLM 하이브리드)
활용 자산: 기존 sec_filings 테이블에 8-K 메타는 이미 있을 것 → 본문 수집 및 파싱만 추가.
예상 작업량: 23일 (CIK 매핑 0.5d + fetcher 1d + parser 11.5d)
Source 2 — BioPharma Catalyst (최종 검증) 💰
왜 쓰나: 전문 editors 가 큐레이션한 과거 catalyst 캘린더. 커버리지와 정확도가 동시에 높음. 단 유료.
접근
https://www.biopharmcatalyst.com/calendars/historical-catalyst-calendar— 구독 필요- CSV export 기능 있음
- API 없음 → HTML 파싱 또는 CSV import
전략
- Phase A~C (EDGAR + PR + CT.gov) 로 baseline 만든 뒤
- 1~2개월 $99 플랜 결제 → 과거 데이터 일괄 export
- 기존 데이터와 cross-check → 누락/불일치 측정
- 보강 후 구독 해지
예상 작업량: 구독 후 importer 개발 1일
Source 3 — PR Newswire / BusinessWire 🗞
왜 쓰나: 회사가 직접 발표하는 보도자료. EDGAR 8-K 보다 빠를 때 있음. 무료.
대상
- PR Newswire 헬스 섹션:
https://www.prnewswire.com/news-releases/health-latest-news/health-latest-news-list/ - BusinessWire:
https://www.businesswire.com/portal/site/home/news/industries/health/ - 각 사이트 RSS 피드 + historical archive
수집 방법
- RSS 피드 매일 cron으로 긁음
- historical backfill: archive 페이지 순회 (2017-01-01 부터)
- 필터 키워드:
"topline data","primary endpoint","Phase 1/2/3 results","PDUFA","FDA acceptance","BLA","NDA","AdCom"
- 본문에서 symbol 추출: 상단의
(Nasdaq: XYZ)/(NYSE: XYZ)형식 regex
파싱 로직
- event_date = 보도자료 발행일
- announced_date = 본문에서
previously announced on [date]추출 (없으면 event_date 사용, source_confidence 낮춤) - outcome = 첫 두 문단 키워드
운영 주의
- User-Agent 로테이션 (bot 차단 대비)
- Rate limit: 사이트당 1 req/sec 이하
- Proxy 고려 (특히 historical backfill)
예상 작업량: 2~3일
Source 4 — ClinicalTrials.gov (baseline fallback)
역할: 다른 소스에서 놓친 trial readout 을 보완하는 최후 수단.
수집 방법
- 이미
clinical_trials테이블에 데이터 있음 → 새 API 호출 불필요 - 정규화 규칙:
primary_completion_date→event_date(추정)announced_date = primary_completion_date - 90 days(conservative 추정)outcome = NULL(이 소스로는 결과 모름)source_confidence = 4
주의: 이 데이터는 “언제쯤 발표될 가능성이 있다” 수준. 실제 readout 날짜와 수개월 어긋남. backtest 에 단독 사용 시 결과 신뢰 낮음.
Deduplication & Merge
같은 이벤트가 여러 소스에 동시에 존재. 예: Phase 3 readout 이 EDGAR 8-K + PR Newswire + BioPharma Catalyst 모두에.
Merge 키
(symbol, catalyst_type, event_date ± 3 days, nct_id) — 날짜 3일 오차 허용, nct_id 가 있으면 추가 구분자.
규칙
- 최종 row 는 source_confidence 최저(=최고 품질) 소스 기준
- 나머지 소스는
catalyst_sources감사 테이블에 raw 저장 announced_date는 가장 이른 날짜 채택 (먼저 공시된 시점이 진실)outcome충돌 시 confidence 1 우선
아키텍처
biotech/data/ingest/catalyst/
├── common/
│ ├── schema.py # DDL + migration
│ ├── symbol_mapper.py # 회사명 → symbol
│ └── cik_mapper.py # symbol → CIK
├── sources/
│ ├── edgar_8k.py # Source 1
│ ├── biopharma.py # Source 2
│ ├── prnewswire.py # Source 3
│ └── clinicaltrials.py # Source 4
├── normalizer.py # 공통 normalizer
├── merger.py # dedup + priority resolution
├── quality.py # coverage / conflict dashboard
└── cli.py # 실행 엔트리포인트
운영
- 초기 backfill:
python -m catalyst.cli backfill --source all --start 2017-01-01 - 증분 갱신:
- PR Newswire: 매일 cron (1시간 간격 권장 — 빠른 pickup)
- EDGAR: 주 1회 cron
- CT.gov: 월 1회
- DuckDB write:
INSERT ... ON CONFLICT DO UPDATE스타일 upsert
작업 분해
Phase A — Schema + baseline (1일)
-
catalysts,catalyst_sourcesDDL + 마이그레이션 - ClinicalTrials.gov 기존 데이터 → catalysts 파생 (source=clinicaltrials_gov, conf=4)
-
profile.cik존재 여부 확인, 없으면 EDGAR 에서 CIK 매핑
Phase B — EDGAR 8-K Primary (2~3일)
- EDGAR full-text search 쿼리 개발
- 8-K 본문 fetcher (rate limit, disk caching)
- Item 7.01 / 8.01 parser (regex + LLM 하이브리드)
- 샘플 50건 수동 검증 → 정확도 95% 이상 확인
Phase C — PR Newswire / BusinessWire (2~3일)
- RSS 피드 리스너
- Historical archive backfill scraper
- User-Agent rotation + rate limit
- 회사명 → symbol 매핑 (profile 테이블 활용)
- Cron 구성
Phase D — Merger + Backfill (1~2일)
- Dedup 로직 구현
- 전체 backfill 1회 (2017~현재)
- Quality 대시보드: 소스별 커버리지, 충돌 건수, outcome 분포 집계
Phase E — BioPharma Catalyst (옵션, 1일)
- Phase D 결과 gap 확인
- 부족하면 1개월 구독 → CSV import → cross-check → 보강
- 구독 해지
Phase F — Ongoing 운영 (지속)
- Cron 모니터링
- 실패 알림 (slack/discord)
- 월간 quality report
품질 검증
Sample spot-check (필수)
각 소스별로 30~50건 샘플링 → 회사 IR 페이지 / 원문 PR 과 수동 비교. 정확도 95% 미만이면 파서 재점검.
Coverage metric
- universe 종목 중 catalyst 보유 비율
- 목표: PDUFA 95%+, Phase 2/3 readout 85%+
Look-ahead check
- 전수:
announced_date < event_date검증 announced_dateNULL row 는 백테스트에서 제외
Change tracking
- PDUFA 연기는 흔함 →
original_event_date보존 + 로그 - 변경 이력은
catalyst_sources감사 테이블로 추적
리스크 & 주의
- EDGAR 파싱 정확도: Item 7.01 본문은 자연어. regex만으로는 부족 가능 → LLM 보조 파싱 검토 (비용 약간 발생).
- PR Newswire bot 차단: 대량 요청 시 IP 차단. rate limit + proxy + UA rotation 필수.
- Survivorship bias: 상장폐지 회사의 과거 이벤트 누락 주의.
excluded_symbols도 커버. - Ambiguous events: Phase 2b, Phase 2/3 조합, interim 등 → 별도 tag 또는 분류 가이드 필요.
- PDUFA 변경: 한 번 공시된 PDUFA date 연기 빈번 →
original_event_date+ 변경 이력 저장 필수. - EDGAR API rate limit: SEC 정책상 10 req/sec. 대량 backfill 시 며칠 걸림.
- 회사명 → symbol 매핑 실패: 작은 바이오는 profile 에 없을 수 있음 → manual override 테이블 필요.
일정
- Week 1: Phase A + Phase B (EDGAR)
- Week 2: Phase C (PR Newswire) + Phase D (merger + quality)
- Week 3: 운영 cron 정착 + sampling 검증
- Week 4 (선택): Phase E (BioPharma Catalyst) + 최종 보강
백테스트 활용 (간단 요약)
이 데이터는 주로 v13.1 (event-window pre-exit) 과 v13.2 (post-catalyst IV crush exit) 에 쓰임. 자세한 backtest 설계는 docs/plans/v13-plan.md 참조.
핵심 활용 포인트:
- 백테스트 엔트리 시점 T 에서 해당 symbol 의 향후 가장 가까운 catalyst 까지의 일수 를 조회
announced_date <= T만 유효 (look-ahead bias 방지)- v13.1: catalyst D-N 일 도달 시 청산
- v13.2: catalyst +1 거래일 종가 청산
- outcome 정보는 entry 시점에는 모름 → entry 의사결정에 사용 불가. 단, 사후 분석 에서 “positive readout 전에 많이 빠진 전략” vs “negative readout 피한 전략” 구분용으로 활용.
이 외에 clinical_trials + catalysts 조인으로 “Phase 2 → Phase 3 transition 이벤트” 같은 새로운 엔트리/exit 피쳐 엔지니어링도 가능.
다음 액션
승인 나면 bio-v2-data 채널에서:
- Phase A Schema DDL 확정
profile.cik컬럼 존재 확인, 없으면 CIK 매핑 선작업- ClinicalTrials.gov 파생 백필 (하루짜리 작업)
- EDGAR fetcher 개발 착수