Earnings week from the filings, not the headlines: one script on a free API key
헤드라인이 아닌 공시 원문으로 보는 어닝 시즌 — 무료 API 키와 스크립트 하나로
금융 데이터 API 서비스 AlphAI를 운영하는 개발자가 신규 사용자처럼 무료 키를 발급받아 자사 API를 도그푸딩한 기록입니다. 관심 종목의 실적 발표일·공시 분석·매크로 일정을 하나의 스크립트로 모으는 과정에서 메트릭 단위 누락, SDK의 204 처리 오류, Retry-After 계산 버그 등 다섯 가지 결함을 찾아 당일 수정했고, 슬라이딩 윈도우 레이트 리밋의 실제 동작을 측정해 공개합니다.
- 주제
본문
실적 시즌마다 반복되는 세 가지 질문 — 이번 주에 누가 발표하는가, 이미 발표한 기업이 실제로 뭐라고 했는가, 같은 기간에 어떤 매크로 지표가 나오는가 — 에 답하기 위해, 저자는 자신이 운영하는 AlphAI의 무료 티어(분당 20회, 일 100회, 카드 불필요)로 새 계정을 만들어 스크립트를 작성했습니다. 내부 도움 없이 신규 사용자와 동일한 조건에서 9월 12일과 Fed 금리 결정 다음 날인 17일, 두 차례 실행한 결과를 있는 그대로 공개합니다.
스크립트 구조와 API 설계
핵심은 GET /api/symbols/{ticker}/earnings/ 엔드포인트입니다. 한 번의 호출로 기업이 직접 확정한 다음 실적 발표일과, 과거 공시에 대한 'read'(8-K·6-K 공시 원문을 구조화 분석한 결과 — verdict, 공시에 인쇄된 그대로의 핵심 지표, 가이던스, 그리고 공시가 빠뜨린 항목 목록)를 함께 반환합니다. 덕분에 25개 종목 워치리스트가 요청 25회, 매크로 캘린더 1회, Fed 이후 매크로 뉴스 1회로 총 27회 — 일일 한도 100회 안에 들어갑니다. SDK가 캘린더를 아직 감싸지 않아 그 부분은 httpx로 직접 호출했는데, 저자는 'SDK가 API를 따라가지 못하는 상황은 어디서나 만난다'며 그대로 뒀다고 밝힙니다.
실제 실행 결과 25개 종목 중 12개만 확정일이 있었고(Micron 9월 30일, Costco 24일, Nvidia 11월 17일 등), Oracle은 매출 전년 30%·클라우드 인프라 121% 성장으로 'strong' read를 받았습니다. FOMC 당일 공시한 Lennar의 read 예시가 인상적인데, verdict 'weak'와 함께 순이익 2.84억 달러(전년 5.91억), 신규 주문 9% 감소, 연간 인도 목표 하향 같은 근거 수치와 '영업현금흐름 미공시, 4분기 매출 가이던스 미공시' 같은 missing_items가 붙어 있습니다 — 저자는 '이런 분기에는 기업이 말하지 않은 것이 절반의 이야기'라고 적습니다.
실행하면서 발견한 버그 다섯 개
① 메트릭 값이 문자열이라 단위(scale)가 없었습니다. Oracle 매출이 '$19,345'로 도착했는데 공시 표가 백만 달러 단위라 실제로는 193억 달러이고, Adobe는 '$6.76 billion', Microsoft는 '$90.0 billion' 대 전년 '$76,441'처럼 단위 표기가 제각각이었습니다. 파서로 추측하면 'Oracle이 Adobe보다 천 배 작은' 결과가 나오므로, 공시 원문에 적힌 경우에만 numeric·unit·scale을 채우고 없으면 null로 두는 수정이 당일 배포됐습니다. ② 최신 read가 없을 때 204 No Content를 반환하는데 Python SDK 0.6.0이 InvalidResponseError를 던졌고, TypeScript SDK는 객체를 약속한 타입 아래 undefined를 반환했습니다. ③ BRK.B가 아니라 BRK-B가 정식 티커인데 SDK가 검색 파라미터를 보낼 수 없었습니다. ④ 캘린더의 to_date는 배타적(exclusive)인데 뉴스 엔드포인트는 포함적(inclusive)인, 같은 호스트 위의 두 가지 윈도우 규약. ⑤ Retry-After 계산 오류 — 아래가 핵심입니다.
레이트 리밋 실측: 버스트 vs 페이싱
같은 26개 요청을 두 방식으로 실행했습니다. 호출 사이에 3.2초 간격을 두면 80초에 429 없이 완료됐고, 전속력으로 돌리면 22번 거절당하며 96초가 걸렸습니다. 원인은 슬라이딩 윈도우 알고리즘입니다. 직전 분의 카운트가 현재 분에 걸쳐 선형 감쇠하는데, Retry-After는 초과분 전체가 감쇠한다고 가정해 계산되므로 버스트 직후에는 실제 대기 시간보다 훨씬 짧게 약속했습니다(2초를 약속하고 실제로는 약 30초 후에야 윈도우가 열림). 이를 믿고 두 번 재시도한 SDK는 벽에 부딪혔고, 수정 후에는 21번째 요청에 Retry-After: 60이 정확히 명시됐습니다. 실용적 교훈은 단순합니다 — 첫 호출부터 한도가 의미하는 속도(약 3.2초 간격)로 페이싱하는 것이 버스트보다 빠르고 재시도 경로를 아예 타지 않습니다. 거절된 요청은 일일 한도를 소비하지 않는 것도 확인했습니다.
아직 해결되지 않은 것들
read 커버리지가 2026년 7월 28일 이후 공시 약 1,800건으로 제한적이며, 확정일은 기업이 보통 2~3주 전에 발표하므로 워치리스트 대부분이 공백입니다. Berkshire는 '구조적 한계'가 아니라 수집 버그 — 본문 없는 헤더만 파이프라인에 도착해 read가 생성되지 않았다고 정정합니다. SDK 재시도 기본값(2회, 최대 60초)도 얇다고 인정합니다. 자사 제품의 흠결을 숨기지 않고 공개한, 드문 형태의 도그푸딩 보고서입니다.
원문: dev.to / 번역·요약: Trawling