[STK2-06] 시그널 조합 + 서비스

작업 내용 (설계 의도)

변경 사항

  • signal.py: combine()(0.6×감성 + 0.4×모멘텀, [-1,1] 클램핑)·classify()(5단계 라벨).
  • signal_service.py: build_signal() 파이프라인 오케스트레이션, Signal/Stock dataclass.
  • STK2-02·04·05를 결합하는 통합 티켓.

의존

  • 선행: STK2-02, STK2-04, STK2-05
  • 후행: STK2-07, STK2-08

다이어그램

처리 흐름

sequenceDiagram
    participant API as main.py
    participant SVC as signal_service.py
    participant TC as toss_client
    participant NW as news / headlines
    participant SE as sentiment
    participant MO as momentum
    participant SG as signal.py
    API->>SVC: build_signal(symbol)
    SVC->>TC: get_stock(symbol)
    SVC->>TC: get_closes(symbol)
    SVC->>NW: fetch + dedup(headlines)
    SVC->>SE: score(headlines)
    SVC->>MO: score(closes)
    SE-->>SVC: sentiment_score
    MO-->>SVC: momentum_score
    SVC->>SG: combine(sentiment_score, momentum_score)
    SG->>SG: clamp(0.6×s + 0.4×m, -1, 1)
    SG->>SG: classify(composite)
    SG-->>SVC: Signal dataclass
    SVC-->>API: Signal

클래스 의존

flowchart LR
    subgraph Application["Application"]
        signal_service["signal_service.py (build_signal)"]
        signal["signal.py (combine, classify)"]
    end
    subgraph Infra["Infrastructure"]
        toss_client["toss_client.py"]
    end
    subgraph Domain["Domain"]
        sentiment["sentiment.py"]
        momentum["momentum.py"]
        news["news.py / headlines.py"]
    end
    signal_service --> signal
    signal_service --> toss_client
    signal_service --> sentiment
    signal_service --> momentum
    signal_service --> news

테스트 케이스

  • 종합 점수가 0.6×감성 + 0.4×모멘텀 공식으로 계산된다.
  • 감성 0.8, 모멘텀 0.5일 때 composite는 0.68이고 라벨은 “강한 긍정”이다.
  • composite ≥ 0.5이면 “강한 긍정”, 0.15≤x<0.5이면 “긍정”으로 분류된다.
  • composite가 -1.5가 되도록 입력해도 clamp 후 -1.0이 반환된다.
  • 감성·모멘텀 모두 0이면 composite 0.0, 라벨 “중립”이다.
  • build_signalSignal dataclass의 모든 필드가 채워진 객체를 반환한다.