Files
US2KR/README.md
T
jsahnandClaude Fable 5 f5fa7ec70f Add a Monday-morning weekly recap briefing
No US session closes into Monday KST, so Mondays (default 07:00, cron
in the daemon with restart catch-up) now publish a weekly review built
entirely from stored data: the quotes table collapsed into week-over-
week changes and the week's daily briefing headlines as the narrative.
The recap is a BriefingResult kind="weekly" reusing MarketPayload, so
the store, verification pass, all delivery channels (Blogger title
"미국 주간 리뷰 <monday>") and the dashboard (주간 리뷰 badge) work
unchanged. Manual runs via `run-once --weekly [--date <monday>]`.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-12 23:52:31 +09:00

126 lines
7.4 KiB
Markdown

# US2KR — 미국 마감 → 한국 개장 전 브리핑 봇
미국 정규장이 끝난 뒤(16:00 ET), 한국 장이 열리기 전(09:00 KST) 하루 한 번
미국 시장 데이터를 수집하고 MiniMax LLM으로 한국어 브리핑을 만들어 발송하는 상주 프로그램입니다.
설계 문서는 [PLAN.md](PLAN.md)를 참고하세요.
```
수집(yfinance · RSS · SearXNG · FRED) → SQLite 저장 → MiniMax 요약(JSON 스키마)
→ LLM 교차 검증(수치·방향 팩트체크, major 이슈면 1회 자동 수정) → Markdown 렌더 → Telegram / 파일
```
## 실행 시각
| | ET | KST |
|---|---|---|
| 미국 정규장 마감 | 16:00 | 05:00 (3~11월 EDT) / 06:00 (11~3월 EST) |
| **브리핑 발송** (마감 +75분, 기본) | 17:15 | **06:15 / 07:15** |
| 한국 개장 | — | 09:00 |
- **월요일 아침(기본 07:00 KST)에는 주간 리뷰 발행** — 월요일 새벽엔 마감되는 미국 세션이 없으므로,
저장된 지난주 시세(주간 변동)와 일일 브리핑 헤드라인으로 주간 요약을 만들어 같은 채널로 발행
(`US2KR_WEEKLY_ENABLED` / `US2KR_WEEKLY_TIME_KST`, 수동: `run-once --weekly [--date 월요일]`)
- 시각은 하드코딩이 아니라 `exchange_calendars`(XNYS/XKRX)로 계산 → 미국 휴장·조기폐장·서머타임 자동 대응
- 미국 휴장일은 스킵, 한국 휴장일은 리포트에 "오늘 한국 휴장" 표기. `US2KR_SKIP_WHEN_KRX_CLOSED=true`면
한국 휴장 연휴 중 **다음 미국 세션이 먼저 마감되어 무의미해지는 브리핑만** 스킵 — 금요일 세션(토요일 도착)은
월요일 개장용으로 항상 발송
## 설치
```bash
# Python 3.12 + uv
uv venv --python 3.12 .venv
uv pip install --python .venv/bin/python -e ".[dev]"
cp .env.example .env # MINIMAX_API_KEY 필수, TELEGRAM_* 선택
```
## 사용
```bash
.venv/bin/python -m us2kr.cli show-config # 설정 확인(시크릿 마스킹)
.venv/bin/python -m us2kr.cli next-run # 다음 브리핑 세션/시각
.venv/bin/python -m us2kr.cli run-once --dry-run # 지금 즉시 생성해서 터미널에 출력(발송 X)
.venv/bin/python -m us2kr.cli run-once --dry-run --no-llm # LLM 없이 숫자표만(축소 모드)
.venv/bin/python -m us2kr.cli run-once --date 2026-08-24 # 특정 미국 세션 기준 생성·발송(마감된 세션만)
.venv/bin/python -m us2kr.cli run-once --force # 이미 발송된 날이어도 다시(재발송)
.venv/bin/python -m us2kr.cli test-notify # 발송 채널 점검
.venv/bin/python -m us2kr.cli blogger-auth # Blogger OAuth 1회 발급(.env에 넣을 값 출력)
.venv/bin/python -m us2kr.cli daemon # 상주 실행(스케줄러)
```
종료 코드: `0` 성공 · `1` 오류(발송 채널 실패 포함) · `2` 스킵(이미 발송 / 한국 휴장)
아직 마감되지 않은 세션을 `--date`로 지정하면 `--dry-run`이 아닌 한 거부합니다(장중 데이터가 저장되면
데몬의 정규 브리핑이 "이미 발송"으로 건너뛰기 때문). 발송 테스트는 `--date <직전 세션> --force`로 하세요.
## 상주 실행(배포)
프로세스 생존은 OS에 맡기고, 프로그램은 "다음 실행 시각 계산 → 대기 → 실행"만 반복합니다.
재시작 후 마감 3시간(`US2KR_CATCHUP_WINDOW_HOURS`) 이내면 놓친 브리핑을 즉시 따라잡고, SQLite 기록으로 중복 발송을 막습니다.
| 환경 | 방법 |
|---|---|
| macOS | `bash deploy/install-launchd.sh` (launchd, KeepAlive) |
| macOS (Apple container) | `bash deploy/install-apple-container.sh` — 데몬+대시보드 빌드·기동. `--boot`를 붙이면 로그인 시 자동 기동(LaunchAgent) 등록 |
| Linux | `deploy/us2kr.service` → `systemctl enable --now us2kr` |
| Docker | `docker compose -f deploy/compose.yaml up -d --build` |
Apple container(`brew install container`)는 restart 정책이 없어 재부팅 후 자동 기동은 `--boot`의
LaunchAgent(`container system start` + `container start us2kr`)가 담당합니다.
`data/` `reports/` `logs/`는 호스트 디렉터리를 그대로 마운트하므로 launchd 방식과 산출물 위치가 같습니다.
> macOS 노트북은 **잠자기 중에는 실행되지 않습니다.** 06~07시에 깨어 있도록
> `sudo pmset repeat wakeorpoweron MTWRFS 06:00:00` 로 예약 기동을 걸거나, 항상 켜진 머신/서버에서 돌리세요.
## 설정(.env)
| 변수 | 기본값 | 설명 |
|---|---|---|
| `MINIMAX_API_KEY` | — | 필수. 글로벌 엔드포인트(`api.minimax.io`) 키 |
| `MINIMAX_MODEL` | `MiniMax-M3` | 기본 모델 |
| `MINIMAX_FALLBACK_MODELS` | `MiniMax-M2.7,MiniMax-M2.5` | 실패 시 순차 폴백 |
| `TELEGRAM_BOT_TOKEN` / `TELEGRAM_CHAT_ID` | — | 둘 다 있으면 Telegram 발송 |
| `BLOGGER_CLIENT_ID` `BLOGGER_CLIENT_SECRET` `BLOGGER_REFRESH_TOKEN` `BLOGGER_BLOG_ID` | — | 4개 모두 있으면 Blogger에 자동 발행. Google Cloud에서 Blogger API v3 활성화 + OAuth 데스크톱 클라이언트 생성 후 `us2kr blogger-auth`로 발급 |
| `BLOGGER_LABELS` | `미국증시,마감브리핑` | Blogger 게시물 라벨 CSV |
| `FRED_API_KEY` | — | 있으면 경제지표 발표 일정 포함 |
| `US2KR_RUN_OFFSET_MINUTES` | `75` | 미국 마감 후 몇 분 뒤 실행 |
| `US2KR_CATCHUP_WINDOW_HOURS` | `3` | 재시작 시 따라잡기 허용 창 |
| `US2KR_SKIP_WHEN_KRX_CLOSED` | `false` | 한국 휴장일에 다음 미국 세션이 먼저 마감되어 브리핑이 무의미해질 때만 스킵(금요일 세션은 항상 발송) |
| `US2KR_NEWS_LOOKBACK_HOURS` / `US2KR_NEWS_MAX_ITEMS` | `18` / `40` | 뉴스 수집 범위 |
| `SEARXNG_BASE_URL` | — | 있으면 RSS 외에 SearXNG 뉴스 검색 결과도 병합 (인스턴스에 `json` 포맷 활성화 필요) |
| `US2KR_SEARXNG_QUERIES` | 기본 검색어 3개 | SearXNG 검색어 CSV (기본: `us2kr/collectors/searx.py`의 `DEFAULT_QUERIES`) |
| `US2KR_VERIFY_ENABLED` | `true` | 요약 후 LLM 팩트체크(수치·방향을 수집 데이터와 대조). major 이슈 발견 시 1회 자동 수정 후 재검증. 검증 실패는 발송을 막지 않고 결과는 JSON·대시보드에 기록 |
| `US2KR_KRX_EXTRA_HOLIDAYS` | — | 캘린더에 없는 한국 임시휴장일 CSV (예: `2026-06-03`) |
## 산출물
- `reports/YYYY-MM-DD.md`, `.json` — 한국 거래일 기준 아카이브
- `data/us2kr.sqlite3` — 시세 스냅샷·리포트·발송 기록(전일 대비 비교, 중복 방지)
- `logs/us2kr.log` — 일자별 회전, API 키 마스킹
## 대시보드 (dashboard/)
쌓인 브리핑을 보는 웹 대시보드(TypeScript, 런타임 의존성 없는 Node 24 서버 + 번들 클라이언트).
`reports/*.json`만 읽는 읽기 전용 뷰라 데몬의 SQLite(WAL)와 절대 경합하지 않습니다.
- 최신 세션 요약 타일(S&P·나스닥·SOX·VIX·달러/원·10년물), 종목별 종가 추이 차트(표 전환 가능)
- 브리핑 목록/본문(Markdown 렌더, sanitize), 데이터 결측·오류 로그 표시
- Apple container 배포 시 함께 기동: `http://localhost:8090` (포트: `US2KR_DASHBOARD_PORT`)
```bash
cd dashboard
npm install
npm run build # tsc 타입체크 + esbuild 번들
US2KR_REPORTS_DIR=../reports npm start # http://localhost:8090
```
## 개발
```bash
.venv/bin/python -m pytest -q
.venv/bin/ruff check us2kr tests
```
LLM은 `us2kr/llm/base.py`의 `LLMProvider` 인터페이스 뒤에 있으므로, 다른 모델로 바꾸려면 구현체만 추가하면 됩니다.