Files
web-page-backend/docs/superpowers/specs/2026-07-02-realtime-trade-alerts-design.md
gahusb 4cb9dc6a7c docs(stock): 실시간 매매 알람 설계 스펙 (watchlist∪screener buy + exit+trailing sell, 1분 Windows 워커, NAS edge dedup)
브레인스토밍 확정 요구사항 6종 + 아키텍처 A(신규 Windows docker 워커). TA/조건판정은
Windows, edge 중복판정 상태는 NAS 영속(재시작 스팸 방지). cross-repo 계약(webai
monitor-set/report, agent-office notify, watchlist CRUD, heartbeat) 정의.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EqCYBhvTcdeCTUDX3RhWx9
2026-07-02 15:19:00 +09:00

11 KiB
Raw Blame History

실시간 매매 알람 (Real-time Trade Alerts) — 설계 스펙

  • 작성일: 2026-07-02
  • 상태: 설계 승인됨 (사용자 리뷰 대기)
  • 관련 세션: BE(web-backend, 본 스펙 주도) · AI(web-ai 워커) · FE(web-ui 탭)

1. 목표

장이 열려 있는 동안(시간외 포함) 실시간으로 주가 기준치를 분석해, 조건 충족 시 매수/매도 알람을 텔레그램으로 사용자 + 아내 둘 다에게 전송한다. 기술적 분석(TA) 계산은 Windows PC의 docker 워커에서 수행한다.

기존에는 이 판단들이 EOD(하루 1회)로만 돌았다:

  • 매수 후보 = 스크리너(평일 16:30) · 매도/보유 advisory = holdings_intel(08:30/16:50).

이번 작업의 핵심 = 동일 판단을 장중(+시간외) 1분 주기 실시간으로 전환 + 조건 충족 즉시 알람.

2. 확정된 요구사항 (사용자 결정)

항목 결정
매수 유니버스 watchlist(사용자 관리) 당일 스크리너 후보
매수 트리거 TA 자동 시그널(수동 목표가 없음)
매도 트리거 기존 exit 룰 + 트레일링 스톱
감시 주기/세션 1분 폴링 · 장전 시간외 08:3009:00 · 정규장 09:0015:30 · 시간외 단일가 16:0018:00
중복 방지 상태 전이(edge-triggered) — 거짓→참 전이 시만 알림, 참 유지 중 무알림, 재무장
watchlist 관리 텔레그램 봇 명령 + web-ui 탭 둘 다
수신자 사용자 + 아내 둘 다(매수·매도 모두)
TA 연산 위치 Windows WSL2 docker 신규 워커
트레일링 스톱 기본값 보유기간 고점 대비 10%(파라미터화)
매수 신호 지지선 되돌림(MA20/50) · 돌파(전고점/52주) · RSI 과매도 반등

3. 아키텍처

[Windows WSL2 docker] trade-monitor 워커  (web-ai · AI세션)
  1분 루프 (KST 세션 게이팅)
   ① GET  NAS  /api/webai/trade-alert/monitor-set   (X-WebAI-Key)
   ② KIS 실시간/시간외 시세 + 분봉/일봉 → TA 계산
   ③ 조건 평가 → 현재 발화집합 F = {(ticker, kind, condition)}
   ④ POST NAS  /api/webai/trade-alert/report {firing: F}   (X-WebAI-Key)
   ⑤ heartbeat: worker:trade-monitor:heartbeat (EX45, 관측 편입)
        │
        ▼
[NAS] stock (:18500 · web-backend · BE)
   • watchlist·alert_state(edge dedup, 영속)·alert_history·holding high-water
   • monitor-set 조립(watchlist  screener 후보  보유) + 세션/휴장 게이팅
   • report 수신 → edge diff(F vs 직전 발화) → 신규 edge를 agent-office로 push
        │ (텔레그램 전송 성공 시에만 alert_state 갱신)
        ▼
[NAS] agent-office (:18900 · web-backend · BE)
   • POST /api/agent-office/stock/trade-alert → 텔레그램(너+아내)
   • 봇 명령 /watch /unwatch /watchlist → stock watchlist CRUD
   • 알람 activity feed 편입

[web-ui] 관심종목 탭 (FE세션) — watchlist CRUD + 알람 이력 뷰

설계 원칙

  • TA/조건판정 = Windows(요구사항). edge 중복판정 상태 = NAS 영속 → 워커 재시작해도 재알림 스팸 없음(youtube_publisher 교훈 재적용).
  • 워커는 dedup 상태를 안 가진다. 매 사이클 "현재 발화집합 전체"만 보고 → NAS가 diff(단일 진실원천).
  • 워커의 대외 채널은 NAS stock 한 곳(기존 ai_trade↔stock의 X-WebAI-Key 재사용). 텔레그램 발송은 stock→agent-office push(기존 realestate→agent-office/notify 패턴).

4. DB 스키마 (stock.db)

-- 매수 감시 관심종목 (사용자 관리)
CREATE TABLE IF NOT EXISTS watchlist (
  ticker      TEXT PRIMARY KEY,
  name        TEXT,
  note        TEXT,
  params_json TEXT NOT NULL DEFAULT '{}',   -- 종목별 조건 오버라이드(선택)
  added_at    TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
);

-- edge 중복판정 상태 (영속 — 재시작 스팸 방지의 핵심)
CREATE TABLE IF NOT EXISTS trade_alert_state (
  ticker           TEXT NOT NULL,
  kind             TEXT NOT NULL,   -- 'buy' | 'sell'
  condition        TEXT NOT NULL,   -- ex) buy_ma20_pullback, sell_trailing_stop
  currently_firing INTEGER NOT NULL DEFAULT 0,
  first_fired_at   TEXT,
  last_fired_at    TEXT,
  last_seen_at     TEXT,
  PRIMARY KEY (ticker, kind, condition)
);

-- 알람 이력
CREATE TABLE IF NOT EXISTS trade_alert_history (
  id          INTEGER PRIMARY KEY AUTOINCREMENT,
  ticker      TEXT NOT NULL,
  name        TEXT,
  kind        TEXT NOT NULL,
  condition   TEXT NOT NULL,
  price       REAL,
  detail_json TEXT,
  fired_at    TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
);
CREATE INDEX IF NOT EXISTS idx_tah_fired ON trade_alert_history(fired_at DESC);

보유기간 고점(트레일링 스톱용) high-water는 krx_daily_prices(기존)에서 lookback max로 계산하거나 별도 컬럼으로 관리 — 구현 계획에서 확정(v1: 포지션 최초 관측 이후 일봉 고가 max, 없으면 최근 N일).

5. 계약 (Contracts) — cross-repo 잠금 대상

5.1 NAS stock ↔ Windows 워커 (X-WebAI-Key)

GET /api/webai/trade-alert/monitor-set

{
  "session": "pre | regular | after | closed",
  "as_of": "2026-07-02T09:01:00+09:00",
  "buy_targets":  [{"ticker":"005930","name":"삼성전자","source":"watch|screener","params":{}}],
  "sell_targets": [{"ticker":"000660","name":"SK하이닉스","avg_price":180000,"qty":10,
                    "holding_high":210000,"params":{}}],
  "buy_params":  {"rsi_oversold":30,"breakout_vol_mult":1.5,"pullback_pct":0.02},
  "exit_params": {"stop_pct":0.08,"take_pct":0.25,"trailing_pct":0.10}
}
  • session=closed면 워커는 KIS 호출 없이 sleep.

POST /api/webai/trade-alert/report

{ "as_of":"2026-07-02T09:01:00+09:00",
  "firing":[ {"ticker":"005930","kind":"buy","condition":"buy_ma20_pullback",
              "price":71500,"detail":{"ma20":71200,"rsi":34}} ] }

응답: { "new_alerts": <int>, "cleared": <int> }

  • NAS가 firing vs trade_alert_state[firing=1] diff → 신규 edge만 텔레그램.

5.2 stock → agent-office (내부)

POST /api/agent-office/stock/trade-alert

{ "alerts":[ {"ticker":"005930","name":"삼성전자","kind":"buy",
              "condition":"buy_ma20_pullback","price":71500,
              "detail":{...},"fired_at":"..."} ] }

→ agent-office가 너+아내에게 텔레그램. (realestate/notify 패턴)

5.3 stock watchlist CRUD (web-ui + agent-office 봇)

  • GET /api/stock/watchlist
  • POST /api/stock/watchlist {ticker, note?}
  • DELETE /api/stock/watchlist/{ticker}
  • GET /api/stock/trade-alerts?days=N (이력, web-ui용)

5.4 워커 heartbeat (관측 편입)

worker:trade-monitor:heartbeat EX45, 값 JSON {name:"trade-monitor",kind:"trader",state:"market_open|market_closed|idle",ts,last_alert_at,...}. /api/agent-office/nodes workers[]에 추가.

6. 알람 조건 (Windows 워커가 계산)

매수 (buy_targets):

  • buy_ma20_pullback — MA20>MA50>MA200 정렬 + 저가가 MA20/50에 pullback_pct 이내 접근 후 종가 반등
  • buy_breakout — 종가 > (전 N일 고점 또는 52주 신고가) + 거래량 > breakout_vol_mult×20일평균
  • buy_rsi_bounce — RSI(14)가 rsi_oversold 아래로 내려갔다가 봉 시리즈 내에서 다시 상향 돌파(최근 봉에서 30 상향 크로스). 워커는 무상태 — 매 사이클 봉 데이터로 크로스를 계산(cross-cycle 메모리 불필요)

매도 (sell_targets):

  • sell_stop_loss — (priceavg)/avg ≤ stop_pct
  • sell_ma_break — 종가 < MA50 (심각: < MA200)
  • sell_take_profit — (priceavg)/avg ≥ take_pct
  • sell_climax — 급등 소진(holdings_intel climax 로직 이식)
  • sell_trailing_stop — price ≤ holding_high × (1 trailing_pct)

7. 데이터 흐름 — edge dedup (NAS)

매 1분 report 수신 시:
  F        = report.firing 집합
  prev     = SELECT (ticker,kind,condition) FROM trade_alert_state WHERE currently_firing=1
  new_edge = F  prev
  cleared  = prev  F
  for e in new_edge:
      ok = agent_office.send_trade_alert(e)          # 텔레그램
      if ok:
          INSERT trade_alert_history(e)
          UPSERT trade_alert_state(e, firing=1, fired/last=now)
      # 실패 시 상태 미갱신 → 다음 사이클 재시도
  for c in cleared:
      UPDATE trade_alert_state SET firing=0 WHERE key=c   # 재무장
  UPDATE last_seen_at for all F
  • 영속 trade_alert_state → 워커·NAS 재시작에도 재알림 스팸 없음.
  • 텔레그램 실패 시 firing 미표시 → 재시도 보장(node_monitor "성공 시만 갱신" 관용).

8. 세션/휴장 게이팅

NAS monitor-set.session 필드가 KST 시각 + holidays.json(is_market_open)으로 판정:

  • pre 08:3009:00 / regular 09:0015:30 / after 16:0018:00 → 그 외/휴장 = closed.
  • 워커는 closed면 sleep. (불필요 KIS 호출·알람 차단)

9. 에러 처리

  • 워커: KIS 실패 → 해당 사이클 skip + 다음 분 재시도, 종목별 실패 격리. heartbeat로 생사 노출.
  • NAS: 워커 인증 X-WebAI-Key. 텔레그램 실패 → 상태 미갱신. report는 멱등(같은 F 재전송 무해).
  • 워커 다운 시 알람 정지 → node_monitor 경보(기존 관측)로 감지.

10. 테스트 전략 (BE, TDD)

  • watchlist CRUD (추가/중복/삭제/조회)
  • monitor-set 조립 (watchlist screener 보유, 세션 게이팅, 휴장)
  • edge diff 로직: 신규 edge만 알림 / 참 유지 무알림 / 해제 후 재발화 재알림 / 재시작 지속성(영속 상태)
  • 텔레그램 전송 실패 시 상태 미갱신(재시도)
  • alert_history 기록 / trade-alerts 조회
  • agent-office: /watch·/unwatch·/watchlist 봇 명령 → stock CRUD, trade-alert notify → 텔레그램 포맷(너+아내)
  • webai 계약 엔드포인트(monitor-set/report) 스키마·인증

11. 작업 분담

repo 세션 산출물 상태
web-backend (stock + agent-office) BE(본 세션) DB·watchlist·edge·webai 계약·텔레그램·봇 이번에 구현
web-ai (services/trade-monitor/ WSL2 docker) AI세션 1분 루프·KIS·TA·조건평가·report·heartbeat 계약 넘김
web-ui (관심종목 탭) FE세션 watchlist CRUD·조건·이력 뷰 계약 넘김
  • 계약(§5)은 co-gahusb로 잠근 뒤 3세션 병렬.
  • 워커 재빌드는 로컬 docker(사용자): wsl -d Ubuntu-24.04 -- docker compose up -d --build trade-monitor.

12. 범위 밖 (YAGNI / 후속)

  • 실주문 자동 집행(알람 전용, KIS 주문 X).
  • KIS 웹소켓 실시간 틱(1분 폴링으로 충분).
  • 종목별 수동 목표가(이번은 TA 자동만).
  • 백테스트/성과 추적(후속 슬라이스).