#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 섹터 대비 이상반응 탐지: SOXX 대비 -5%p 이상 언더퍼폼한 종목
import json, os, re, sys, datetime, urllib.request, urllib.parse
import yfinance as yf

BASE = os.path.expanduser("~/Claude/Projects/에이전트구축")
MEDIA = os.path.join(BASE, "media_feed.jsonl")
XFEED = os.path.join(BASE, "x_feed.jsonl")
NQLEV = os.path.join(BASE, "nq_levels.py")
LOG = os.path.join(BASE, "reaction_log.jsonl")
DRY = "--dry" in sys.argv
SPREAD = 5.0

UNIV = ["MU","SNDK","NBIS","AAOI","SOXL","META","NVDA","LITE","TER","ALAB",
        "COHR","MRVL","CRDO","GLW","AVGO","AEHR","ASML","LRCX","AMD","CRWV","IREN"]

def send(msg):
    try:
        s = open(NQLEV, encoding="utf-8").read()
        tok = re.search(r'BOT_TOKEN\s*=\s*["\']([^"\']+)', s).group(1)
        cid = re.search(r'CHAT_ID\s*=\s*["\']?([^"\'\s]+)', s).group(1)
    except Exception:
        print("[!] 토큰 없음"); return
    d = urllib.parse.urlencode({"chat_id": cid, "text": msg}).encode()
    urllib.request.urlopen(f"https://api.telegram.org/bot{tok}/sendMessage", d, timeout=15)

px = yf.download(UNIV + ["SOXX"], period="5d", interval="1d",
                 progress=False, auto_adjust=False)["Close"]
px = px.dropna(how="all")

def chg(tk):
    try:
        s = px[tk].dropna()
        if len(s) < 2: return None
        return float((s.iloc[-1] / s.iloc[-2] - 1) * 100)
    except Exception:
        return None

base = chg("SOXX")
if base is None:
    print("SOXX 데이터 없음"); sys.exit()

now = datetime.datetime.now(datetime.timezone.utc)
buf = []
for p in (MEDIA, XFEED):
    if not os.path.exists(p): continue
    for line in open(p, "rb"):
        try: d = json.loads(line.decode("utf-8", "replace"))
        except Exception: continue
        try:
            t = datetime.datetime.fromisoformat(str(d.get("ts","")).replace("Z","+00:00"))
        except Exception: continue
        if (now - t).total_seconds() <= 172800:
            buf.append(d.get("text",""))

POS = re.compile(r"(계약|수주|deal|contract|상회|beat|상향|raise|확대|expansion|"
                 r"record|사상 *최|백로그|backlog|증설)", re.I)

lines = []
for tk in UNIV:
    c = chg(tk)
    if c is None: continue
    sp = c - base
    if sp <= -SPREAD:
        pat = re.compile(r"(?<![A-Za-z])\$?" + tk + r"(?![A-Za-z])")
        n = len([t for t in buf if pat.search(t) and POS.search(t)])
        tail = f" · 호재 {n}건 있었음 → 분배 의심" if n else ""
        lines.append(f"{tk} {c:+.2f}% (SOXX {base:+.2f}%, 괴리 {sp:+.1f}%p){tail}")

last = str(px.index[-1].date())
msg = f"[섹터 대비 이상반응 {last} 종가]\n" + ("\n".join(lines) if lines else "해당 없음")
print(msg)
if lines and not DRY:
    send(msg)
    with open(LOG, "a", encoding="utf-8") as f:
        for l in lines:
            f.write(json.dumps({"date": last, "line": l}, ensure_ascii=False) + "\n")
