#!/usr/bin/env python3
"""ai-portfolio-evening-watch v2 : 2026-10-30 analysis. TWSE official OHLCV for 6669 & 00412A."""
import json, glob, re
from datetime import date
import numpy as np
import pandas as pd

OUT = "/root/.hermes/cron/output"

def roc(y, m, d):
    return f"115/{m:02d}/{d:02d}"

def parse_twse(path, code):
    d = json.load(open(path, encoding="utf-8"))
    if d.get("stat") != "OK":
        return None
    rows = []
    for r in d["data"]:
        # 115/10/30
        m = re.match(r"^(\d+)/(\d+)/(\d+)$", r[0])
        y = int(m.group(1)) + 1911
        mo, dy = int(m.group(2)), int(m.group(3))
        def num(x):
            return float(str(x).replace(",", "").replace("+", "").strip())
        rows.append(dict(date=date(y, mo, dy), vol=num(r[1]), open=num(r[3]),
                         high=num(r[4]), low=num(r[5]), close=num(r[6])))
    df = pd.DataFrame(rows).set_index("date").sort_index()
    return df

# ---- 6669 official history (Apr-Oct) ----
frames = []
for f in sorted(glob.glob(f"{OUT}/tw_6669_*.json")):
    df = parse_twse(f, "6669")
    if df is not None:
        frames.append(df)
h = pd.concat(frames)
h = h[~h.index.duplicated(keep="last")].sort_index()
# drop any zero-volume rows (phantom)
h = h[h["vol"] > 0]
c = h["close"]
v = h["vol"]

def ema(s, n):
    return s.ewm(span=n, adjust=False).mean()

ma20 = float(c.rolling(20).mean().iloc[-1])
ma60 = float(c.rolling(60).mean().iloc[-1])
ma20_series = c.rolling(20).mean()
ma60_series = c.rolling(60).mean()

d = c.diff()
up = d.clip(lower=0); dn = -d.clip(upper=0)
rs = up.rolling(14).mean() / dn.rolling(14).mean()
rs = rs.replace([np.inf, -np.inf], np.nan).fillna(50)
rsi = 100 - 100 / (1 + rs)

macd = ema(c, 12) - ema(c, 26)
sig = ema(macd, 9)
hist = macd - sig

low9 = h["low"].rolling(9).min(); high9 = h["high"].rolling(9).max()
rsv = (c - low9) / (high9 - low9) * 100
rsv_sma = rsv.fillna(50)
k_ema = rsv.ewm(com=2, adjust=False).mean(); d_ema = k_ema.ewm(com=2, adjust=False).mean()
k_sma = rsv_sma.rolling(3).mean(); d_sma = k_sma.rolling(3).mean()

v20 = float(v.rolling(20).mean().iloc[-1])
vol_today = float(v.iloc[-1])

# consecutive closes below MA60
below = (c < ma60_series).values
streak = 0
for b in reversed(below):
    if b:
        streak += 1
    else:
        break
# volume trend across the streak: first-3 avg vs last-3 avg
seg = v.iloc[-streak:]
prev3 = float(seg.iloc[:3].mean()) if len(seg) >= 3 else None
last3 = float(seg.tail(3).mean()) if len(seg) >= 3 else None
vol_chg = ((last3 - prev3) / prev3 * 100) if prev3 else None

res6669 = {
    "bars": int(len(h)), "first": str(h.index[0]), "last": str(h.index[-1]),
    "close": float(c.iloc[-1]), "prev_close": float(c.iloc[-2]),
    "chg_pct": float((c.iloc[-1] / c.iloc[-2] - 1) * 100),
    "ma20": ma20, "ma60": ma60,
    "rsi14": float(rsi.iloc[-1]), "rsi_prev": float(rsi.iloc[-2]),
    "macd": float(macd.iloc[-1]), "signal": float(sig.iloc[-1]),
    "hist": float(hist.iloc[-1]), "hist_prev": float(hist.iloc[-2]),
    "K_ema": float(k_ema.iloc[-1]), "D_ema": float(d_ema.iloc[-1]),
    "K_ema_prev": float(k_ema.iloc[-2]), "D_ema_prev": float(d_ema.iloc[-2]),
    "K_sma": float(k_sma.iloc[-1]), "D_sma": float(d_sma.iloc[-1]),
    "K_sma_prev": float(k_sma.iloc[-2]), "D_sma_prev": float(d_sma.iloc[-2]),
    "volume": vol_today, "vol20avg": v20, "vol_ratio": vol_today / v20,
    "low20": float(c.tail(20).min()), "high20": float(c.tail(20).max()),
    "high60": float(c.tail(60).max()),
    "streak_below_ma60": int(streak),
    "streak_prev3_avg_vol": prev3, "streak_last3_avg_vol": last3,
    "streak_vol_chg_pct": vol_chg,
    "closes_tail12": [float(x) for x in c.tail(12)],
    "vol_tail6": [float(x) for x in v.tail(6)],
    "ma60_tail6": [float(x) for x in ma60_series.tail(6)],
    "rsi_tail5": [float(x) for x in rsi.tail(5)],
    "hist_tail5": [float(x) for x in hist.tail(5)],
}

# ---- 00412A official ----
f = parse_twse(f"{OUT}/tw_00412a_oct.json", "00412A")
if f is not None:
    f = f[f["vol"] > 0]
    res412 = {
        "ok": True, "bars": int(len(f)), "first": str(f.index[0]), "last": str(f.index[-1]),
        "close": float(f["close"].iloc[-1]), "prev_close": float(f["close"].iloc[-2]),
        "chg_pct": float((f["close"].iloc[-1] / f["close"].iloc[-2] - 1) * 100),
        "low": float(f["low"].iloc[-1]), "high": float(f["high"].iloc[-1]),
        "max_close": float(f["close"].max()), "min_close": float(f["close"].min()),
        "vol10avg": float(f["vol"].tail(10).mean()), "vol_today": float(f["vol"].iloc[-1]),
        "closes_tail": [float(x) for x in f["close"].tail(8)],
        "rows": [[str(i), float(r["close"]), float(r["vol"])] for i, r in f.iterrows()],
    }
else:
    res412 = {"ok": False}

out = {"6669": res6669, "00412A": res412}
print("###JSON###")
print(json.dumps(out, ensure_ascii=False, indent=1))
