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.
290 lines
6.6 KiB
Markdown
290 lines
6.6 KiB
Markdown
# Phase 1 아키텍처 및 저장소 구조 계획
|
|
|
|
## 1. 설계 원칙
|
|
|
|
이 프로젝트는 무료 데이터 기반의 미국 주식 이벤트 스윙 시스템입니다.
|
|
Phase 1의 핵심 원칙은 다음과 같습니다.
|
|
|
|
1. **원문(raw)을 절대 버리지 않는다.**
|
|
2. **운영 상태(PostgreSQL)와 연구용 시계열(DuckDB/Parquet)을 분리한다.**
|
|
3. **모든 adapter는 idempotent 해야 한다.**
|
|
4. **LLM은 규칙 기반 파서를 대체하지 않고 보강한다.**
|
|
5. **실패 시 no-trade/no-write가 기본값이다.**
|
|
6. **모든 서비스는 독립적으로 재실행 가능해야 한다.**
|
|
7. **표준화된 event/document identifier 없이는 downstream으로 보내지 않는다.**
|
|
|
|
## 2. 권장 저장소 구조
|
|
|
|
```text
|
|
repo/
|
|
apps/
|
|
collector/
|
|
sec_collector/
|
|
alpaca_collector/
|
|
fred_collector/
|
|
finra_collector/
|
|
parser/
|
|
filing_parser/
|
|
xbrl_parser/
|
|
event_parser/
|
|
feature_builder/
|
|
market_features/
|
|
event_features/
|
|
backtester/
|
|
live_trader/
|
|
libs/
|
|
adapters/
|
|
sec/
|
|
alpaca/
|
|
fred/
|
|
finra/
|
|
common/
|
|
config.py
|
|
logging.py
|
|
time_utils.py
|
|
ids.py
|
|
retries.py
|
|
file_store.py
|
|
db/
|
|
models.py
|
|
migrations/
|
|
schemas/
|
|
parser_event.schema.json
|
|
source_record.schema.json
|
|
llm/
|
|
prompts/
|
|
validator.py
|
|
cache.py
|
|
tests/
|
|
unit/
|
|
integration/
|
|
replay/
|
|
fixtures/
|
|
configs/
|
|
app.yaml
|
|
env.example
|
|
symbols.yaml
|
|
data/
|
|
raw/
|
|
staging/
|
|
parquet/
|
|
docs/
|
|
```
|
|
|
|
## 3. 실행 단위
|
|
|
|
초기에는 서비스별 독립 실행 파일로 둡니다.
|
|
|
|
- `python -m apps.collector.sec_collector.main`
|
|
- `python -m apps.collector.alpaca_collector.main`
|
|
- `python -m apps.parser.event_parser.main`
|
|
- `python -m apps.feature_builder.market_features.main`
|
|
|
|
초기에는 큐 시스템을 도입하지 않고, **명시적 스케줄 실행**을 사용합니다.
|
|
큐는 Phase 3 이후 필요 시 Redis/Celery 또는 경량 작업 큐를 검토합니다.
|
|
|
|
## 4. 저장 계층
|
|
|
|
### 4.1 Raw Zone
|
|
|
|
원문을 그대로 저장합니다.
|
|
|
|
경로 예시:
|
|
|
|
```text
|
|
data/raw/sec/2026-03-12/{cik}/{accession}/index.json
|
|
data/raw/sec/2026-03-12/{cik}/{accession}/filing.txt
|
|
data/raw/sec/2026-03-12/{cik}/{accession}/exhibit_99_1.html
|
|
data/raw/alpaca/bars/daily/2026-03-12/{symbol}.json
|
|
data/raw/fred/2026-03-12/{series_id}.json
|
|
data/raw/finra/2026-03-12/daily_short_sale_volume.txt
|
|
```
|
|
|
|
원칙:
|
|
- 원문은 수정하지 않습니다.
|
|
- 적재 시 수집 메타데이터를 sidecar JSON으로 함께 저장합니다.
|
|
- raw 저장 성공 전에는 structured write를 하지 않습니다.
|
|
|
|
### 4.2 Staging Zone
|
|
|
|
파싱 전 정규화 중간 결과를 저장합니다.
|
|
|
|
예:
|
|
- HTML → text 추출
|
|
- filing metadata
|
|
- extracted exhibit list
|
|
- temporary parsed items
|
|
|
|
### 4.3 Structured Zone
|
|
|
|
운영용 PostgreSQL + 연구용 Parquet로 저장합니다.
|
|
|
|
PostgreSQL:
|
|
- job 상태
|
|
- 문서 메타
|
|
- 이벤트 레코드
|
|
- parser 결과
|
|
- feature snapshot
|
|
- 주문/포지션 상태
|
|
|
|
Parquet/DuckDB:
|
|
- 바 시계열
|
|
- 대량 feature matrix
|
|
- 라벨링 데이터
|
|
- 실험/백테스트 산출물
|
|
|
|
## 5. 모듈 경계
|
|
|
|
### 5.1 Source Adapter
|
|
|
|
역할:
|
|
- 외부 원천에서 raw 데이터를 수집
|
|
- 최소 메타데이터 부착
|
|
- raw 저장
|
|
- source checksum 생성
|
|
|
|
금지:
|
|
- 전략 판단
|
|
- event scoring
|
|
- 트레이드 신호 생성
|
|
|
|
### 5.2 Normalizer
|
|
|
|
역할:
|
|
- raw를 내부 공통 포맷으로 정규화
|
|
- source-specific field를 canonical field로 변환
|
|
|
|
예:
|
|
- SEC accession → internal document_id
|
|
- Alpaca bar payload → canonical OHLCV schema
|
|
|
|
### 5.3 Parser
|
|
|
|
역할:
|
|
- 문서 기반 구조화
|
|
- 규칙 기반 이벤트 탐지
|
|
- 선택적으로 LLM 보강
|
|
|
|
### 5.4 Feature Builder
|
|
|
|
역할:
|
|
- 이벤트 특징 생성
|
|
- 시장 특징 생성
|
|
- 레짐 특징 생성
|
|
- attention 특징 생성(Phase 1에서는 인터페이스만)
|
|
|
|
### 5.5 Ranker
|
|
|
|
Phase 1에서는 실제 점수 산출보다 **입력 포맷 정의**까지만 합니다.
|
|
|
|
### 5.6 Execution Layer
|
|
|
|
Phase 1에서는 주문 실행을 하지 않고, **order_plan schema**만 정의합니다.
|
|
|
|
## 6. 공통 식별자 규칙
|
|
|
|
### 6.1 symbol_master_id
|
|
- 내부 고유 종목 식별자
|
|
- v1에서는 `ticker + venue + start_date` 조합 허용
|
|
|
|
### 6.2 document_id
|
|
형식:
|
|
|
|
```text
|
|
DOC::{source}::{issuer_id}::{event_date}::{accession_or_hash}
|
|
```
|
|
|
|
예:
|
|
|
|
```text
|
|
DOC::SEC::0000789019::2026-01-28::0001193125-26-027198
|
|
```
|
|
|
|
### 6.3 event_id
|
|
형식:
|
|
|
|
```text
|
|
EVT::{issuer_id}::{event_type}::{primary_document_id}
|
|
```
|
|
|
|
### 6.4 job_run_id
|
|
UUID 사용
|
|
|
|
## 7. 시간/달력 정책
|
|
|
|
- 내부 표준 타임존은 **UTC 저장 + US/Eastern 파생 컬럼**입니다.
|
|
- 거래일 계산은 반드시 거래소 달력을 사용합니다.
|
|
- raw 수집 시각, source published 시각, parsed event 시각을 구분합니다.
|
|
- `filed_at`, `accepted_at`, `collected_at`, `normalized_at`, `parsed_at` 필드를 각각 유지합니다.
|
|
|
|
## 8. 환경 변수 정책
|
|
|
|
필수 환경 변수 예시:
|
|
|
|
```text
|
|
APP_ENV=dev
|
|
POSTGRES_DSN=postgresql://...
|
|
DUCKDB_PATH=/app/data/parquet/research.duckdb
|
|
DATA_ROOT=/app/data
|
|
ALPACA_API_KEY=...
|
|
ALPACA_SECRET_KEY=...
|
|
FRED_API_KEY=...
|
|
OPENAI_API_KEY=...
|
|
LOG_LEVEL=INFO
|
|
SEC_USER_AGENT=project-name contact-email
|
|
```
|
|
|
|
원칙:
|
|
- 민감정보는 코드/문서에 하드코딩 금지
|
|
- `.env`는 로컬 전용, 배포에는 secret 주입 사용
|
|
- example 파일에는 placeholder만 포함
|
|
|
|
## 9. Docker Compose 기본 구성
|
|
|
|
서비스:
|
|
- `postgres`
|
|
- `app` (개발용 Python image)
|
|
- 필요 시 `adminer` 또는 경량 DB UI
|
|
|
|
초기에는 메시지 큐/오브젝트 스토리지는 넣지 않습니다.
|
|
|
|
## 10. 로그와 메트릭 정책
|
|
|
|
모든 서비스는 JSON log를 기본으로 합니다.
|
|
|
|
필수 필드:
|
|
- timestamp
|
|
- level
|
|
- service
|
|
- job_run_id
|
|
- source
|
|
- entity_id
|
|
- message
|
|
- error_class
|
|
- retry_count
|
|
|
|
메트릭 예시:
|
|
- source fetch count
|
|
- source fetch latency
|
|
- normalized record count
|
|
- parser success/fail count
|
|
- schema validation fail count
|
|
- duplicate skip count
|
|
|
|
## 11. 장애 처리 원칙
|
|
|
|
- raw 수집 실패 → structured write 금지
|
|
- schema validation 실패 → parser 결과 폐기, 원문은 유지
|
|
- DB write 실패 → retry 후 stop
|
|
- 외부 API rate limit → backoff
|
|
- 알 수 없는 필드 추가 → warning + raw 보존
|
|
|
|
## 12. Phase 1 수용 기준
|
|
|
|
- 로컬에서 `make bootstrap` 수준 명령으로 개발 환경 구축 가능
|
|
- sample SEC filing을 1회 수집 후 raw/staging/structured에 적재 가능
|
|
- sample Alpaca bar 데이터를 canonical schema로 변환 가능
|
|
- sample FRED/FINRA 데이터를 일자별로 적재 가능
|
|
- parser가 schema-valid JSON을 생성 가능
|
|
- 모든 경로가 재실행 시 중복 없이 안정 동작
|