auto: 일일 백업 2026-08-13 02:00

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
hyowons
2026-08-13 02:00:00 +09:00
parent c93d1ac385
commit 6428846506
127 changed files with 1822 additions and 1688 deletions
+3 -3
View File
@@ -44,9 +44,9 @@
| `watchlist-monitor` | 평일 10·12·14시 (휴장일 self-skip) | `watchlist_monitor.py check` | buy/target/stop 감지 시 텔레그램 요약 + HTML 메일 1통. 2026-05-12 15분 간격 → 3회 축소 (관리자님 요청) |
| `surge-monitor` | 평일 09:0015:35 **매 1분** (`StartCalendarInterval` 396엔트리, 스크립트 self-skip) | `surge_monitor.py check` | **급등락 알림** (2026-08-04 도입). 자산웹 🔔 토글 켜진 종목만(`state/behive_surge_toggles.json`). 조회·알림 전용이라 `feedback_no_trade_auto_triggers` 예외 아님(주문 API 경로 없음). **세 기준 병행**: 거래소 **상·하한가 도달**(2026-08-06 추가, 그날 끝까지 간 것이라 무엇에도 가리지 않음) + 거래소 **VI 발동**(급등락 여부를 거래소가 판정 → 문턱 불필요) + **자기 이력 분위수**(장중 이탈폭 ≥ 그 종목 과거 이탈폭 `SURGE_PCTL` p90, 확대는 p98). ⚠️ **고정 %를 쓰자는 제안은 거절할 근거가 있다** — 실측(관심·감시 65종목 × 280거래일, 2026-08-04) 장중 이탈폭 중간값 3.8%라 ±3%가 평범한 날이고, 종목별 변동성이 **22배 차**(ATR14 1.0~22.3%)라 ±5% 문턱이면 KODEX 미국S&P500 발생 0회 / SK이터닉스 75회다. 같은 5%가 한쪽엔 도달 불가·한쪽엔 노이즈. ⚠️ **ATR 배수도 폐기됐다 — 2026-08-04 관리자님 지적**("하루 최대 폭이 30%~+30%인데"). 국내 가격제한폭이 ±30%인데 ATR 배수는 상한이 없어 문턱이 제한폭 밖으로 밀려난다: 1차(ATR×1.5) 5/65종목, 확대(ATR×3.0) **31/65(48%)**가 발동 불가였다. 분위수는 실제 관측값이라 구조적으로 제한폭을 못 넘고(p90~p99 전부 30% 초과 0종목) 알림량이 정의상 (1−p)로 확정된다(p90 = 종목당 연 25회). **ATR 배수 재제안 금지.** ⚠️ 평시 사이클 **키움 콜 0** — behive_web `/api/realtime/quotes` 1콜이 현재가와 VI를 함께 준다(VI 1h는 시장 전역 broadcast라 구독 무관). 구독 상한에 걸려 빠진 종목만 ka10095 배치 1콜 폴백. ⚠️ **문턱은 하루 1회만 계산**(`state/surge_thresholds.json`) — `daily_candles_cache.get_candles`가 stale 캐시에 ka10081을 때리므로 매분 × 종목수면 폭주. ⚠️ **ka10081 유량 초당 5건** — 70종목 일괄 산출 시 26종목이 429로 실패(실측). 사이클당 12개·0.35s 간격으로 나눠 계산하고(70종목 ≈ 6분) **조회 실패는 캐시하지 않는다**(이력부족과 같이 `{}`로 굳히면 429 한 번에 그 종목이 하루 내내 VI만 감시). ⚠️ **VI 레그는 behive_web 프로세스 의존** — 죽으면 VI 알림이 조용히 사라지고 stderr 경고만(백업 트리거는 `feedback_no_unsanctioned_failover`대로 의도적 부재). ⚠️ **상·하한가·VI 중 문턱을 넘으면 생략이 아니라 보류**(2026-08-06 관리자님 요청) — `surge_alerts.json``__pending__`에 이탈폭 최대 시점을 적어뒀다가 가림이 풀린 사이클에 방출한다. 그전엔 버려져서 **VI 중 돌파 후 해제 시점에 되밀리면 알림이 아예 없었다**. 가림 여부는 보류 목록이 비었는지가 아니라 `evaluate`**가림사유 반환값**으로 판단해야 한다(VI 중 되밀림도 목록이 비어 조기 방출됨). 방출분은 **발송 성공 시에만** 보류에서 제거. VI가 마감까지 안 풀리면 그날 보류분은 방출 안 됨(강제 flush 없음). ⚠️ **상·하한가는 ka10095 `upl_pric`/`lst_pric`(거래소 계산값)** — 제한폭은 기준가 호가단위로 절사 후 ±하는 규칙이라 직접 계산하면 ETF·가격대별 표를 재현해야 해 틀린다. 장중 불변이라 `state/surge_limits.json`에 하루 1콜 캐시, 일봉 이력 불필요라 **이력 부족 종목도 감시됨**. dedup `state/surge_alerts.json` = 분위수 방향별 1회 + 확대 1회 + 상·하한가 1회, VI는 `vi:<발동시각>`이라 발동 건마다 1회(하루 상한 없음). 로그 `logs/stock-surge-monitor.{log,err.log}` |
| `trailing-monitor` | 평일 09:0015:30 **매 1분** (`StartCalendarInterval` 391엔트리, 스크립트 self-skip) | `trailing_monitor.py check` | **트레일링 스톱 감시** (2026-07-30 도입). ⚠️ **매매 API를 자동 호출하는 유일한 트리거**`feedback_no_trade_auto_triggers` 예외로 관리자님이 명시 승인. 허용 근거: 호출하는 건 `kiwoom_order.modify_order` **하나뿐**이고 신규 발주·수량·방향 변경 경로가 없음. PIN 승인 시 계좌·종목·수량·트레일 폭이 확정되고, 감시는 이미 승인된 스톱주문의 조건단가를 **상향만** 정정. 예약 0건이면 즉시 종료(API 콜 0). 상태 `state/trailing_stops.json`, 로그 `logs/stock-trailing-monitor.{log,err.log}`. ⚠️ **cadence 이력 2분→30초→1분**(전부 2026-07-30 관리자님 지시). launchd 최소 단위가 1분이라 30초는 프로세스 내부 `--repeat 2 --gap 30`(1분 발화 × 30초 간격 2회)으로 구현했고 **CLI 옵션은 살아있다** — 다시 30초로 갈 땐 plist ProgramArguments에 인자만 추가. `StartInterval=30`은 GUI idle 시 발화 보류라 사용 금지(sim-scan이 같은 이유로 캘린더 전환). ⚠️ **스톱주문은 장 마감 후 소멸한다 — 2026-07-30 실증 완료**(가정이 틀렸음). 첫 실주문 TRL-A2Z4(제닉스로보틱스 1주, 14:35 등록, 손절선 4,370→4,455 3회 상향)가 **체결 없이** 사라졌다: 당일매매일지 매도 기록 0건 · 보유수량 3,338주 불변 · 미체결 0건. 감지 시각 **18:21**(15:30 장 마감 즉시가 아니라 몇 시간 뒤 — 감시는 그 사이 매분 정상 발화했고 로그가 조용했으니 주문은 18:20까지 조회에 살아있었다. NXT 시간대엔 손절선이 실제로 유효했던 셈. 1회 관측이라 매일 같은 시각인지는 미확인). ⚠️ **자동 재등록 안 한다**(관리자님 결정) — 알림만 보내고 재등록은 자산웹에서 수동. 소멸 알림에 사유 구분 있음(아래). ⚠️ **예약 종료 알림 3갈래**(`_fill_check`+`_notify_gone`): 미체결 조회에서 사라진 것만으로는 체결·취소·소멸을 구분할 수 없어(셋 다 목록에서 빠짐) 예약이 사라질 때만 ka10170 1콜 추가로 판정 — ✅체결(당일 매도기록 있음, 수량·평균가·실현손익 표시. 관리자님 수동 매도와는 구분 불가라 단정 안 함) / ⚠️소멸(매도기록 없음 = 체결 안 됨 확정 + 재등록 안내) / 🎯판정불가(일지 조회 실패). **조회 실패를 '체결 안 됨'으로 단정하면 안 된다**`_fill_check`가 (기록, 성공여부) 튜플을 반환하는 이유. ⚠️ **초기 고점 = 등록 시점 현재가 고정**, 이후 갱신도 **현재가로만**(당일고가를 쓰면 등록 전 고가 때문에 즉시 발동). ⚠️ **고점 기준 선택 옵션은 2026-07-30 제거 — 다시 넣지 말 것**(`peak_basis`/`52주 전고점`/`매수후 고점`). 과거 고점 기준은 손절선이 현재가 위로 올라가 `TRAIL_IMMEDIATE` 거부되는데, **손실 종목은 등록 자체가 불가**(실측 제닉스로보틱스 현재가 4,805 · 매수후 고점 6,890 → 필요 폭 30.3% / 52주 19,940 → 75.9%, 상한 30% 초과)고 **이익 종목은 과거 고점 ≈ 현재가라 결과가 동일**. 차이 날 때 못 쓰고 쓸 수 있을 때 차이 없어 폐기. ⚠️ **최저 매도가**(`min_sell_price`)로 손절선 하한 지정 가능 — `cond_uv = max(트레일, 최저가)`. 트레일 폭을 넓게 잡아도 이 가격 아래로 안 내려간다. 고점이 올라 트레일이 추월하면 그 뒤론 트레일 지배(전환점 = 최저가÷(1−pct)). 1분 간격이라 그 사이 스파이크는 놓친다(손절선이 덜 올라가는 방향 — 손실 위험 아님). ⚠️ **정밀도 보완 안 함으로 결론**(2026-07-30 관리자님): 최저 매도가로 하한이 통제되니 1분 해상도 누락은 감당 범위. `entry_day_high` 기준선 / `realtime_hub` WS 틱 추적 **둘 다 채택 안 함 — 재제안 금지** |
| `briefing` | 평일 20:10 (휴장일 self-skip) | `stock_portfolio_report.py send` | 메일 발송. 텔레그램 리포트 요약은 2026-05-14 관리자님 요청으로 비활성화. 실패 자가 알림 |
| `briefing-fallback-2030` | 평일 20:30 (휴장일 self-skip) | `briefing_fallback.py retry` | 스냅샷 있으면 skip(idempotent), 없으면 send 재실행. 알림 없음 |
| `briefing-fallback-2100` | 평일 21:00 (휴장일 self-skip) | `briefing_fallback.py final` | 마지막 폴백. 실패 시 레이 텔레그램 알림 |
| `briefing` | 평일 20:10 (휴장일 self-skip) | `stock_portfolio_report.py send --no-mail` | ⚠️ **메일 발송 중지 — 2026-08-12 관리자님 결정**("겹치는 부분 많고 메일 너무 많다"). 이제 이 트리거의 목적은 **오늘 스냅샷 확보뿐**이다(`state/portfolio_daily_snapshot.json` = 자산웹 '당일 평가손익'·`rebase_to_nxt_close` 전일 baseline). ⚠️ **`run` 모드로 바꾸지 말 것** — 휴장일 self-skip이 `send` 분기에만 있어 `run`이면 휴장일에도 스냅샷이 생겨 85일치 이력에 없던 휴장일 키가 섞이고, 평문 리포트 100여 줄을 로그에 쏟는다. `--no-mail`은 휴장일 가드·실패 자가알림을 유지하고 `send_mail` 호출만 건너뛴다(`MAIL_SKIPPED=no_mail_flag` 출력). 텔레그램 요약은 2026-05-14부터 이미 비활성 |
| `briefing-fallback-2030` | 평일 20:30 (휴장일 self-skip) | `briefing_fallback.py retry` | 스냅샷 있으면 skip(idempotent), 없으면 `send --no-mail` 재실행. 알림 없음. ⚠️ 최근 2주 실측 단 한 번도 실제 작업 안 함(20:10이 매일 성공) — 무해한 보험이라 유지 |
| `briefing-fallback-2100` | 평일 21:00 (휴장일 self-skip) | `briefing_fallback.py **force**` | 마지막 폴백 + 20:10 데이터 부정확 보완. 스냅샷 있으면 `run`으로 21:00 fresh 값 덮어쓰기, 없으면 `send --no-mail` + 실패 시 레이 텔레그램 알림. ⚠️ **plist 실제 인자는 `force`** — 이 표가 오래 `final`로 잘못 적혀 있었다(2026-08-12 수정) |
| `send-balance` | 매월 1일 04:30 | `send_balance_to_budget.py` | 본인 계좌(가희 제외) → 골디 inbox `securities_balance`. 골디 결산 30분 전 |
| `ipo-calendar-sync` | 매주 금요일 17:00 | `ipo_calendar_sync.py` | IPO 청약·상장 일정 → Google Calendar |
| `fomc-calendar-sync` | 매월 1일 09:10 | `fomc_calendar_sync.py` | 연준 FOMC 회의 일정 → Google Calendar `[FOMC] YYYY-MM 회의`(+` (SEP)`). 2026-07-29 신설. **캘린더엔 2026-03-23 관리자님이 2027-12까지 11건을 이미 손으로 등록해 두셨고 날짜도 정확** — 스크립트는 그 표기를 승계해 흡수했으므로 당분간 실제 신규 등록은 2028년분부터. **이벤트 기간 = 회의 시작일(ET) ~ 발표일(KST)** 3일 — 성명 발표가 KST로는 회의 종료 다음날 새벽(EDT 03:00·EST 04:00)이라, ET 날짜만 넣으면 한국 장이 결과를 반영하는 날에 캘린더가 비어 브리핑 '오늘 일정'에 안 뜬다(2026-07-29 관리자님 지적으로 수정). colorId 9 보존(`--event-color` 미지정). 삭제 루프가 `old_start <= today`를 건너뛰어 지난 회의는 안 지움. `--list`=파싱만(gog 미호출) |