# Phase 1 서비스 계약서 ## 1. 목적 이 문서는 각 서비스와 adapter가 어떤 입력을 받고 어떤 출력을 내야 하는지 정의합니다. AI 코딩 에이전트는 이 계약을 기준으로 인터페이스를 고정해야 합니다. ## 2. 공통 원칙 - 모든 서비스는 명시적 입력 객체를 받습니다. - 모든 서비스는 typed result 또는 typed error를 반환합니다. - 외부 API 호출 결과는 raw 저장 이후에만 정규화됩니다. - 서비스는 가능하면 pure function 형태를 유지합니다. - DB write는 application layer에서 수행하고, parser/adapter core는 side-effect를 최소화합니다. ## 3. 공통 타입 ### 3.1 FetchRequest ```json { "source": "sec", "entity": "issuer|symbol|series|date", "key": "0000789019", "start_date": "2026-01-01", "end_date": "2026-03-31", "run_mode": "backfill|daily|replay" } ``` ### 3.2 FetchResult ```json { "status": "success|partial|failed|skipped", "raw_object_ids": ["RAW::..."], "records_seen": 3, "records_written": 3, "warnings": [], "errors": [] } ``` ### 3.3 NormalizeResult ```json { "status": "success|failed", "canonical_records": [{"...": "..."}], "warnings": [], "errors": [] } ``` ## 4. SEC adapter 계약 ### 4.1 입력 - CIK 또는 accession - 기간 또는 개별 filing key - run_mode ### 4.2 출력 - submissions raw object - filing raw object - exhibit raw object list - canonical document metadata ### 4.3 함수 예시 ```python def fetch_submissions(cik: str) -> FetchResult: ... def fetch_filing(accession_no: str, cik: str) -> FetchResult: ... def normalize_filing(raw_object_id: str) -> NormalizeResult: ... ``` ### 4.4 실패 규칙 - HTTP 실패 → retryable error - 404 → non-retryable warning - parsing 실패 → raw는 유지, normalize fail ## 5. Alpaca adapter 계약 ### 5.1 입력 - symbol list - timeframe - start/end ### 5.2 출력 - canonical OHLCV record list ### 5.3 함수 예시 ```python def fetch_bars(symbols: list[str], timeframe: str, start: str, end: str) -> FetchResult: ... def normalize_bars(raw_object_id: str) -> NormalizeResult: ... ``` ## 6. FRED adapter 계약 ```python def fetch_series(series_id: str, start: str | None = None, end: str | None = None) -> FetchResult: ... ``` 출력: - series metadata - observation records ## 7. FINRA adapter 계약 ```python def fetch_daily_short_volume(trade_date: str) -> FetchResult: ... def normalize_daily_short_volume(raw_object_id: str) -> NormalizeResult: ... ``` 주의: - symbol mapping 실패 가능 - 원본 ticker_raw는 반드시 유지 ## 8. Event Parser 계약 입력: ```json { "document_id": "DOC::...", "source_name": "sec", "form_type": "8-K", "issuer_id": "ISSUER::...", "filing_date": "2026-01-29", "accepted_at_utc": "2026-01-29T21:05:00Z", "text": "...document text...", "metadata": { "item_numbers": ["2.02", "7.01"], "exhibits": ["EX-99.1"] } } ``` 출력: - `parser_event.schema.json`을 만족하는 JSON 실패: - invalid schema → `validation_status=invalid` - parser exception → error object 반환, event 생성 금지 ## 9. Feature Builder 계약 입력: - event row - related market data - optional macro/short volume rows 출력 예시: ```json { "event_id": "EVT::...", "snapshot_name": "event_v1", "snapshot_version": "1.0.0", "feature_json": { "guidance_direction": "raised", "oneoff_penalty": 0, "reaction_day_return": 0.042, "volume_ratio_20d": 2.3 } } ``` ## 10. Job Runner 계약 입력: - job_name - source_name - run_date - mode 출력: - `job_runs` 테이블 row 업데이트 - 로그 스트림 상태 전이: - pending → running → succeeded - pending → running → partial - pending → running → failed ## 11. 에러 객체 표준 ```json { "error_class": "RateLimitError", "message": "HTTP 429 from source", "retryable": true, "source_name": "sec", "entity_key": "0000789019", "context": {"url": "..."} } ``` ## 12. 로그 포맷 표준 ```json { "timestamp": "2026-03-12T10:00:00Z", "level": "INFO", "service": "sec_collector", "job_run_id": "...", "message": "fetched submissions", "source": "sec", "entity_id": "0000789019", "records_seen": 1, "records_written": 1 } ``` ## 13. 재시도 계약 - source adapter의 네트워크 실패는 최대 3회 지수 백오프 - validation 실패는 재시도하지 않음 - DB deadlock/connection issue는 retryable - schema mismatch는 raw 보존 후 failed 처리 ## 14. 금지 규칙 - adapter가 임의로 심볼명을 정정하지 말 것 - parser가 raw text를 수정 저장하지 말 것 - feature builder가 원문 source를 재호출하지 말 것 - job runner가 실패를 성공으로 덮어쓰지 말 것