feat(stock): news_issues (감성 기반 악재 flag)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -172,6 +172,43 @@ def market_events(ticker: str, ticker_prices: "pd.DataFrame",
|
||||
return events
|
||||
|
||||
|
||||
# ---- Task 3.2: news_issues ----
|
||||
|
||||
NEG_SENTIMENT = -0.3 # 이하면 악재 후보
|
||||
|
||||
|
||||
def _news_sentiment_map(date: str) -> dict:
|
||||
"""date 기준 news_sentiment 테이블에서 ticker → {score_raw, news_count} 맵 반환."""
|
||||
with db._conn() as conn:
|
||||
try:
|
||||
rows = conn.execute(
|
||||
"SELECT ticker, score_raw, news_count FROM news_sentiment WHERE date=?",
|
||||
(date,),
|
||||
).fetchall()
|
||||
except Exception:
|
||||
return {}
|
||||
return {r["ticker"]: {"score_raw": r["score_raw"], "news_count": r["news_count"]}
|
||||
for r in rows}
|
||||
|
||||
|
||||
def news_issues(tickers: list[str], date: str, use_llm: bool = True) -> dict[str, list]:
|
||||
"""news_sentiment 음수 → 악재 flag. (LLM 요약은 best-effort; 단위 테스트는 use_llm=False로.)"""
|
||||
senti = _news_sentiment_map(date)
|
||||
out: dict[str, list] = {}
|
||||
for t in tickers:
|
||||
s = senti.get(t)
|
||||
if not s or s["score_raw"] is None:
|
||||
continue
|
||||
if s["score_raw"] <= NEG_SENTIMENT:
|
||||
sev = "high" if s["score_raw"] <= NEG_SENTIMENT * 2 else "med"
|
||||
out.setdefault(t, []).append({
|
||||
"type": "news",
|
||||
"severity": sev,
|
||||
"summary": f"부정 뉴스 감성({s['score_raw']:+.2f}, {s.get('news_count', 0)}건)",
|
||||
})
|
||||
return out
|
||||
|
||||
|
||||
def decide_action(tech_score: float, exit_flags: dict, pnl: float | None,
|
||||
add_score: float = ADD_SCORE) -> tuple[str, str]:
|
||||
"""액션 결정 매트릭스: sell > trim > add > hold (우선순위 순).
|
||||
|
||||
Reference in New Issue
Block a user