auto: 일일 백업 2026-07-04 02:00
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -97,7 +97,7 @@ Stock agent (`agents/stock/workspace/scripts/`), run with `python3`:
|
||||
- `holiday_sync.py` — investing.com에서 한국(KRX) 휴장일 fetch → `state/market_holidays.json`. behive_web.py 자동갱신 토글이 휴장일·평일·시간대로 비활성 판정. CLI: `python3 holiday_sync.py [--show]`
|
||||
- `behive_youtube_digest.py` — 비하이브 YouTube 종목분석 수집·요약·발송 + 수동 워치리스트 추가(`add`)/조회·삭제
|
||||
- `watchlist_monitor.py` — 워치리스트 종목 장중 15분 시세 감시, buy/target/stop 트리거 발생 시 레이 텔레그램 알림 (LLM 없이 동작). 미보유 종목은 ka10095 batch 1콜 + 단건 ka10001 fallback. 보유 종목은 kt00018 재활용
|
||||
- `behive_web.py` — 워치리스트 실시간 웹 뷰 + 매매 진입점. `serve`(launchd, Tailscale IP 100.75.148.12:18790 바인드, 페이지 GET마다 키움 ka10095 batch 1콜로 워치리스트 시세 + kt00018·kt00001·ka10170 병렬 호출 후 HTML 응답. RENDER 캐시 10s, 종목별 quote 캐시 30s) / `render`(디버깅용 1회 렌더). 외부 노출은 NAS Synology reverse proxy(`stock.hyowons.net` → mac:18790) 경유. 인증 없음 — Tailnet 내부망 한정 운영. 보유종목 day_change 보정은 brifing 과 동일 A-4 정책. KPI 순서·라벨도 stock_portfolio_report 와 통일. 보유종목 행마다 `📋 거래내역` + `💰 거래` + `📝 메모` 버튼. 자산정보 탭 sub-tab 3개 (자산보기·차트보기·시장정보) + 우측 별도 `[💰 거래]` 버튼 (본인 첫 보유종목 자동 선택). 관심·감시종목 행에도 `💰 거래` (보유 모드면 매도 default).
|
||||
- `behive_web.py` — 워치리스트 실시간 웹 뷰 + 매매 진입점. `serve`(launchd, Tailscale IP 100.75.148.12:18790 바인드, 페이지 GET마다 키움 ka10095 batch 1콜로 워치리스트 시세 + kt00018·kt00001·ka10170 병렬 호출 후 HTML 응답. RENDER 캐시 10s, 종목별 quote 캐시 30s) / `render`(디버깅용 1회 렌더). 외부 노출은 NAS Synology reverse proxy(`stock.hyowons.net` → mac:18790) 경유. 인증 없음 — Tailnet 내부망 한정 운영. **빌드 버전 자동 리로드**(2026-07-03): 이 앱은 `location.reload` 없이 fetch로만 갱신해 코드 수정+재시작 후에도 열려있던 페이지는 옛 인라인 JS/CSS를 계속 씀. `BUILD`(파일 mtime, import 시 1회) 상수를 shell `window.__build`와 `/api/panels` 응답 `build`에 실어, `apply()`가 불일치(옛 페이지=`__build` 없음 포함) 감지 시 `sessionStorage` 가드로 build당 1회 자동 리로드 → 최신 JS/CSS 반영(리로드로도 안 맞으면 루프 없이 포기). 보유종목 day_change 보정은 brifing 과 동일 A-4 정책. KPI 순서·라벨도 stock_portfolio_report 와 통일. 보유종목 행마다 `📋 거래내역` + `💰 거래` + `📝 메모` 버튼. 자산정보 탭 sub-tab 3개 (자산보기·차트보기·시장정보) + 우측 별도 `[💰 거래]` 버튼 (본인 첫 보유종목 자동 선택). 관심·감시종목 행에도 `💰 거래` (보유 모드면 매도 default).
|
||||
|
||||
**거래 모달 시스템 (`order-modal` + `pin-modal` + `open-orders-modal`)** — 매매 진입점. 흐름: 종목 select(상단, 보유/관심/감시 통합) → 매수·매도 토글 → 호가창(ka10004 10단계, 1초 polling, visibility 가드) + 입력(계좌·주문유형 LIMIT/MARKET·단가·금액(매수만 양방향)·수량) → 매수/매도 버튼 → propose → PIN 모달(modal-top z-index) 카드 요약 + PIN 입력(`autocomplete="one-time-code"`) + 만료 카운트다운 → verify → 결과 토스트 + 자동 닫기. `[📋 진행중]` 탭은 활성 PIN 카드 → PIN 모달, 미체결만 → open-orders 모달(4계좌 통합 + 행별 취소). 매수 시 금액↔수량 양방향 자동(programmatic .value, 무한루프 X). 매도 토글 시 수량 자동 100%(max_qty). 시장 phase 라벨 + NXT 시간대+`nxt_enable=false` 시 `📵 NXT 거래불가` + 매수/매도 버튼 disable. 우상단 X 없음 — 하단 [닫기]/[취소] + overlay 클릭. **2계좌 동시 매도**(2026-06-12): 매도 버튼 시 **전량매도(입력 수량=max_qty)** 이고 같은 소유자 그룹(본인 일반↔ISA / 가희끼리)의 다른 계좌에도 같은 종목 보유(`accStatus.trde_able_qty>0`)면 `sell-choice-modal` 팝업 — [두 계좌 모두 매도](두 계좌 모두 매도가능 전량, 단가 동일) / [선택 계좌만] / [취소]. 일부매도는 팝업 없이 단일 진행. 매도 정보영역은 그룹 내 다른 계좌도 보유 시 `보유: 141주 / 전체 280주` 병기. 모두 매도는 `POST /api/order/propose_multi` → `handler.propose_trade_multi`(레그별 독립 검증, SELL+LIMIT/MARKET 한정, 같은 그룹만) → **카드 1장+PIN 1개**(iMessage `매도(2계좌)`)가 두 레그 승인 → verify 시 `_submit_multi_legs`가 레그 독립 제출(idem_hash 계좌별 분리)·독립 fill_watcher, 한 레그 실패해도 나머지 시도(ok=하나라도 접수).
|
||||
|
||||
@@ -108,6 +108,8 @@ Stock agent (`agents/stock/workspace/scripts/`), run with `python3`:
|
||||
**기업정보 모달 (4탭)** — 종목 행 `기업정보` 버튼 → `/api/stock_info?code`(키움 ka10001 + 네이버 분기영업이익 + `fnguide_client` 펀더멘털·컨센서스 + `wisereport_client` 리비전·서프라이즈·최근리포트) fetch → 단일 종목은 4탭 렌더: **요약**(현재가·목표주가·리비전·증가율·서프라이즈 + 최신 리포트 1건) / **기업정보·가치**(PER·PBR·EPS·BPS·ROE·영업이익·52주·유통비율·외국인) / **성장성·컨센서스**(매출/EPS 증가율·목표주가·투자의견·추정PER〔현재PER 병기〕·목표가 리비전·서프라이즈) / **투자리포트**(최근 증권사 리포트 목록, 목표가 ▲상향/▼하향). 탭은 모달 전용 클래스(`.info-tabs/.info-tabbtn/.info-tabpanel` + `data-info-tab`/`data-info-panel`)와 독립 핸들러로 자산탭 `.sub-tab`과 격리(전역 restoreSubs 충돌 회피). 항목별 ⓘ 탭 팝업 설명(`INFO_SPECS`/`INFO_DESC`, document 위임 `.info-label`). 비교 모드(여러 종목)는 탭 없이 기존 비교표 유지. ETF 등 데이터 없으면 해당 탭 "데이터 없어요".
|
||||
|
||||
**메모 모달 (`note-modal`)**(2026-06-26) — 보유·관심·감시종목 행 모두에 `📝 메모` 버튼. 클릭 → 팝업 textarea(최대 1000자, 여러 줄)에 기존 메모 prefill → [저장]은 `/notes/set` POST(submit 핸들러가 `note-form`을 fetch로 가로채 **현재 탭 유지** — 네이티브 폼이면 303→`/` 리다이렉트로 자산정보 탭으로 튐) 후 패널 새로고침, [삭제]는 버튼 왼쪽 위 작은 **확인 팝업(예/아니오)** 후 빈 텍스트 submit으로 항목 제거(오삭제 방지, `note-confirm`). 메모 있으면 버튼이 `📝 메모 ●` 노란 강조(`has-note`)+hover 툴팁. 저장소 `state/behive_stock_notes.json`(`{by_code, by_name}`, code 우선→name fallback, `stock_tags`와 동일한 fcntl lock·atomic write). **종목 단위 공용**(계좌 분리 X). 백엔드 `_load/_get/_set_stock_note`·`_note_button_html`·`_render_note_modal`, 태그 모달 패턴 복제.
|
||||
|
||||
**관심종목 그룹 (`group-assign-modal` + `group-manage-modal`)**(2026-07-03) — 관심종목이 39개까지 쌓여 그룹 분류 도입. **관심종목 탭 전용**(감시종목 제외), `stock_tags`와 **독립**. 저장소 `state/behive_interest_groups.json` = `{"groups":[순서있는 이름], "members":{종목명:[그룹명]}}` — 멤버십 키는 **종목명**(interests가 종목명 dict 키라 안정적, rename 없음). **다중 그룹 소속**·빈 그룹 유지·표시순서·rename/delete 연쇄를 레지스트리 한 파일에서 표현. `stock_tags`/`stock_notes`와 동일한 fcntl lock·atomic write. 관심 삭제 시 `_apply_interests_action('delete')`가 `_interests_lock` **밖에서** `_set_interest_groups(stock,[])`로 멤버십 자동 정리(별도 파일 락, 데드락 회피). **패널**(`_render_interests_panel`): **그룹 풀다운**(`.igrp-select`/`.igrp-panel`, `전체`+`보유중`+멤버 있는 그룹+`미분류` 옵션, 다중소속은 각 패널 중복 노출). **보유중은 `mode=='held'`로 자동 채워짐**(레지스트리 미등록, 보유종목도 그룹 배정 시 해당 그룹에 함께 노출). 전체=보유+관심(보유 먼저), 미분류=미보유&미배정만. 보유·그룹 둘 다 없으면 단일 목록으로 폴백. 선택 상태는 자산탭 `.sub-tab`과 격리(`igrpState`+`restoreIgrp`, 3s swap 후 복원, 사라진 키는 전체로 폴백). 같은 종목이 여러 패널에 중복 → `details_mutex`는 같은 `data-row-key`끼리 안 닫음(펼침 유지), `apply()`는 `querySelectorAll`로 전 인스턴스 open 복원. 관심종목 행 **`+ 그룹` 버튼은 `+태그` 버튼과 같은 디자인(`btn-add-tag`)으로 그 옆(detail-name 줄)** → 체크박스 배정 모달(+새 그룹 인라인, `new_group`). **풀다운 오른쪽 `+` 버튼**(`.igrp-add-btn`, flat·empty 케이스에도 항상 노출)→ 작은 드롭다운 메뉴(`.igrp-menu`: `종목 추가`=interest-modal / `그룹 관리`=group-manage-modal). 하단 트리거(관심추가·그룹관리 버튼)는 이 메뉴로 대체·제거. 그룹 관리 모달(그룹당 2줄 카드: 이름 인풋+수 / ▲▼·이름변경·삭제, 하단 새 그룹 행). **관리 모달 open은 트리거 `data-group-all`+`data-group-counts`(JSON)로 서버 왕복 없이 즉시 렌더**, 이후 op는 keepOpen fetch 응답 `{groups,counts}`로 재렌더. 엔드포인트 `POST /interests/groups/set`(stock+groups 반복필드+new_group)·`POST /interests/groups`(op=create|rename|delete|reorder|list〔list는 조회 폴백용 유지〕, `in_actions` 검사보다 먼저 정확매칭). 백엔드 `_load/_save/_get_interest_group(s)`·CRUD(`_create/_rename/_delete/_reorder_interest_groups`)·`_set_interest_groups`·`_interest_group_button_html`·`_render_group_assign_modal`·`_render_group_manage_modal`, 태그/메모 모달 패턴 복제. 기존 39개는 미분류로 자동 편입(쓰기 마이그레이션 없음). 레거시 `에이피알.tag:"뷰티"` 인라인 필드는 미사용 방치.
|
||||
- `send_balance_to_budget.py` — 매월 1일 04:30 launchd. 본인+가희 계좌를 소유자별(self/gahee)로 잔액·예수금·평가액 집계해 `agents/budget/inbox/incoming/`에 envelope(`topic: securities_balance`, schema v2 `by_owner`)로 떨어뜨린다. 골디가 self→`증권(효원)`·gahee→`증권(가희)`로 reconcile 분개. 골디 월간 결산(05:00) 입력. LLM 미경유, 실패 시 레이 텔레그램으로 자가 알림.
|
||||
- `trade_journal.py` — 종목별 매매 기록 누적. 키움 REST에 기간 거래내역 API 부재 → 평일 21:00 launchd(NXT 야간 마감 후)로 ka10170 4계좌 호출해 `state/trade_journal.jsonl`에 적재. `(date, account)` 단위 idempotent, 휴장일/주말 self-skip(`--force`로 우회). 적재 시작일 2026-05-13 이전 보유분은 `seed` 명령(1회)으로 현재 평단가×(보유-당일매수+당일매도) 단일 행으로 압축 적재됨(`seed=true` 플래그·`*` 마커). CLI: `collect [--date YYYYMMDD]` / `seed` / `show <code|name>` / `query [--from --to --account --code]`
|
||||
- `market_indicators_sync.py` — 시장 단위 ADR·투자자별 매매 누적. 네이버 m.stock `/api/index/{KOSPI|KOSDAQ}/integration` 한 콜로 dealTrendInfo·upDownStockInfo 수집 → `state/market_indicators_history.jsonl`. `(date, market)` 단위 idempotent, 휴장/주말 self-skip(`--force`). 평일 21:00 stock.trade-journal launchd에 통합 발화 (별도 plist 없음). KRX 정보데이터시스템은 응답 패턴 변경으로 백필 보류 — 매일 누적만 시작. behive_web 자산정보 탭의 시장정보 sub-tab이 sparkline 데이터원으로 사용
|
||||
|
||||
Reference in New Issue
Block a user