Initial commit
This commit is contained in:
+100
@@ -0,0 +1,100 @@
|
||||
"""Shared data models. Every module talks to the others through these types only."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date, datetime
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class Quote(BaseModel):
|
||||
symbol: str
|
||||
name: str
|
||||
group: str
|
||||
close: float | None = None
|
||||
prev_close: float | None = None
|
||||
change_pct: float | None = None # (close/prev_close - 1) * 100
|
||||
change_abs: float | None = None # close - prev_close (used for rates: yield points)
|
||||
day_high: float | None = None
|
||||
day_low: float | None = None
|
||||
volume: float | None = None
|
||||
avg_volume_10d: float | None = None
|
||||
after_hours_price: float | None = None
|
||||
after_hours_change_pct: float | None = None # vs regular close
|
||||
as_of: datetime | None = None
|
||||
source: str = "yfinance"
|
||||
|
||||
|
||||
class NewsItem(BaseModel):
|
||||
title: str
|
||||
summary: str = ""
|
||||
link: str
|
||||
source: str
|
||||
published: datetime
|
||||
|
||||
|
||||
class EconEvent(BaseModel):
|
||||
name: str
|
||||
time: datetime | None = None
|
||||
country: str = "US"
|
||||
actual: str | None = None
|
||||
forecast: str | None = None
|
||||
previous: str | None = None
|
||||
importance: Literal["high", "medium", "low"] = "medium"
|
||||
kind: Literal["released", "upcoming"] = "released"
|
||||
|
||||
|
||||
class EarningsItem(BaseModel):
|
||||
symbol: str
|
||||
name: str
|
||||
report_date: date
|
||||
timing: Literal["bmo", "amc", "unknown"] = "unknown"
|
||||
eps_actual: float | None = None
|
||||
eps_estimate: float | None = None
|
||||
revenue_actual: float | None = None
|
||||
revenue_estimate: float | None = None
|
||||
|
||||
|
||||
class MarketPayload(BaseModel):
|
||||
"""Everything collected for one briefing, before LLM summarisation."""
|
||||
|
||||
session_date: date # US trading date that just closed
|
||||
us_close_time: datetime # tz-aware (America/New_York)
|
||||
generated_at: datetime # tz-aware (Asia/Seoul)
|
||||
krx_date: date # the Korean trading date this briefing is for
|
||||
krx_open_today: bool
|
||||
quotes: list[Quote] = Field(default_factory=list)
|
||||
news: list[NewsItem] = Field(default_factory=list)
|
||||
events: list[EconEvent] = Field(default_factory=list)
|
||||
earnings: list[EarningsItem] = Field(default_factory=list)
|
||||
history_notes: list[str] = Field(default_factory=list) # e.g. "S&P 500 3일 연속 하락"
|
||||
data_gaps: list[str] = Field(default_factory=list) # collectors that failed / partial
|
||||
|
||||
def quotes_by_group(self) -> dict[str, list[Quote]]:
|
||||
out: dict[str, list[Quote]] = {}
|
||||
for q in self.quotes:
|
||||
out.setdefault(q.group, []).append(q)
|
||||
return out
|
||||
|
||||
|
||||
class Report(BaseModel):
|
||||
"""LLM output schema. Used verbatim as the JSON Schema for structured output."""
|
||||
|
||||
headline: str = Field(description="한 줄 총평 (40자 이내)")
|
||||
market_overview: str = Field(description="지수·섹터 흐름 3~5문장")
|
||||
key_drivers: list[str] = Field(description="상승/하락 요인 3~6개, 각 한 문장")
|
||||
rates_fx_commodities: str = Field(description="금리·달러·원화·유가·금 흐름 2~4문장")
|
||||
korea_implications: list[str] = Field(description="한국 장 시사점 3~6개 (반도체/SOX, 환율, ADR, EWY 등)")
|
||||
watchlist_today: list[str] = Field(description="오늘 한국 장에서 확인할 것 2~5개")
|
||||
risk_flags: list[str] = Field(description="주의사항 1~4개")
|
||||
data_gaps: list[str] = Field(default_factory=list, description="수집 실패로 반영하지 못한 항목")
|
||||
|
||||
|
||||
class BriefingResult(BaseModel):
|
||||
payload: MarketPayload
|
||||
report: Report | None
|
||||
markdown: str
|
||||
model_used: str | None = None
|
||||
degraded: bool = False # True when LLM failed and only the numeric table was produced
|
||||
error: str | None = None
|
||||
Reference in New Issue
Block a user