#!/usr/bin/env python3
"""Final cross-verification for 2026-10-30 briefing.
- 6669: Yahoo split-adjusted Close (phantom volume=0 bar filtered) vs TWSE-official-close series.
- RSI method comparison (simple rolling vs Wilder ewm) to explain prior-report discrepancy.
- Trigger evaluation.
"""
import json, re
from datetime import date
import numpy as np
import pandas as pd
import yfinance as yf

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

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

def rsi_wilder(c, n=14):
    d = c.diff(); up = d.clip(lower=0); dn = -d.clip(upper=0)
    au = up.ewm(alpha=1/n, adjust=False).mean(); ad = dn.ewm(alpha=1/n, adjust=False).mean()
    rs = (au / ad).replace([np.inf, -np.inf], np.nan).fillna(50)
    return 100 - 100 / (1 + rs)

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

fr = [parse(f) for f in ["tw_6669_202604.json", "tw_6669_202605.json", "tw_6669_202606.json",
                         "tw_6669_202607.json", "tw_6669_202608.json", "tw_6669_sep.json", "tw_6669_oct.json"]]
tw = pd.concat([f for f in fr if f is not None])
tw = tw[~tw.index.duplicated(keep="last")].sort_index()
tw = tw[tw["vol"] > 0]
tw_adj = tw.copy()
for col in ["open", "high", "low", "close"]:
    tw_adj[col] = np.where(tw_adj.index < date(2026, 9, 2), tw_adj[col] / 3.0, tw_adj[col])

# ---------- Yahoo split-adjusted ----------
yh = yf.Ticker("6669.TW").history(period="6mo", auto_adjust=False)
yc = yh["Close"].dropna()
y_clean = yh[yh["Volume"].fillna(0) > 0]["Close"].dropna()

def indicators(c):
    out = {}
    out["ma20"] = float(c.rolling(20).mean().iloc[-1])
    out["ma60"] = float(c.rolling(60).mean().iloc[-1])
    out["rsi_roll"] = float(rsi_rolling(c).iloc[-1])
    out["rsi_wild"] = float(rsi_wilder(c).iloc[-1])
    m = ema(c, 12) - ema(c, 26); s = ema(m, 9)
    out["macd"] = float(m.iloc[-1]); out["signal"] = float(s.iloc[-1]); out["hist"] = float((m - s).iloc[-1])
    l9 = c.rolling(9).min(); h9 = c.rolling(9).max()
    return out, m, s

tw_out, tw_macd, tw_sig = indicators(tw_adj["close"])
y_out, y_macd, y_sig = indicators(y_clean)
ya_out, ya_macd, ya_sig = indicators(yc)   # Yahoo WITH phantom bar

print("=== 6669 indicator comparison (2026-10-30) ===")
for k in ["ma20", "ma60", "rsi_roll", "rsi_wild", "macd", "signal", "hist"]:
    print(f"{k:10s} TWSE-split-adj={tw_out[k]:10.4f}   Yahoo-clean={y_out[k]:10.4f}   Yahoo-withPhantom={ya_out[k]:10.4f}")

print("\n=== RSI on 2026-10-29 (explain prior report's 38.88) ===")
def at(series, dt, f):
    idx = series.index
    ts = pd.Timestamp(dt)
    ts = ts.tz_localize(idx.tz) if idx.tz else ts
    return float(f(series).loc[ts])
for label, s in [("TWSE-adj", tw_adj["close"]), ("Yahoo-clean", y_clean), ("Yahoo-phantom", yc)]:
    print(f"  {label:14s} RSI10/29 roll={at(s,'2026-10-29',lambda x: rsi_rolling(x)):6.2f} "
          f"wild={at(s,'2026-10-29',lambda x: rsi_wilder(x)):6.2f} | "
          f"RSI10/30 roll={at(s,'2026-10-30',lambda x: rsi_rolling(x)):6.2f} wild={at(s,'2026-10-30',lambda x: rsi_wilder(x)):6.2f}")

# ---------- KDJ (EMA smoothing, on TWSE-split-adj) ----------
c = tw_adj["close"]
l9 = tw_adj["low"].rolling(9).min(); h9 = tw_adj["high"].rolling(9).max()
rsv = ((c - l9) / (h9 - l9) * 100).fillna(50)
k_e = rsv.ewm(com=2, adjust=False).mean(); d_e = k_e.ewm(com=2, adjust=False).mean()
k_s = rsv.rolling(3).mean(); d_s = k_s.rolling(3).mean()
print("\n=== KDJ 2026-10-30 ===")
print(f"  EMA: K={k_e.iloc[-1]:.2f} D={d_e.iloc[-1]:.2f} (prev K={k_e.iloc[-2]:.2f} D={d_e.iloc[-2]:.2f}) "
      f"-> fresh golden cross? {'YES' if (k_e.iloc[-2] <= d_e.iloc[-2] and k_e.iloc[-1] > d_e.iloc[-1]) else 'NO'}")
print(f"  SMA: K={k_s.iloc[-1]:.2f} D={d_s.iloc[-1]:.2f} (prev K={k_s.iloc[-2]:.2f} D={d_s.iloc[-2]:.2f}) "
      f"-> fresh golden cross? {'YES' if (k_s.iloc[-2] <= d_s.iloc[-2] and k_s.iloc[-1] > d_s.iloc[-1]) else 'NO'}")

# ---------- Volume: TWSE official 20-day ----------
v = tw["vol"]
v20_incl = float(v.tail(20).mean()); v20_excl = float(v.tail(21).head(20).mean())
vt = float(v.iloc[-1])
print("\n=== Volume (TWSE official) ===")
print(f"  today={vt:,.0f}  20d avg incl today={v20_incl:,.0f} -> ratio {vt/v20_incl:.4f}")
print(f"  20d avg excl today={v20_excl:,.0f} -> ratio {vt/v20_excl:.4f}")

# ---------- MA60 streak + volume condition (skill pitfall #18 convention) ----------
ma60 = c.rolling(60).mean()
below = (c < ma60).values
streak = 0
for b in reversed(below):
    if b:
        streak += 1
    else:
        break
streak_dates = list(tw.index[-streak:])
pre3 = [d for d in tw.index if d < streak_dates[0]][-3:]
print("\n=== Full-exit #1 evaluation ===")
print(f"  streak below MA60 = {streak} days, from {streak_dates[0]} to {streak_dates[-1]}")
print(f"  MA60 now={float(ma60.iloc[-1]):.2f}  (skill fixed 2142)")
print(f"  pre-streak 3 days {['%s'%d for d in pre3]} avg = {float(v.loc[pre3].mean()):,.0f}")
print(f"  near 3 days avg = {float(v.tail(3).mean()):,.0f}  -> chg {float((v.tail(3).mean()-v.loc[pre3].mean())/v.loc[pre3].mean()*100):+.1f}%")
seg = v.iloc[-streak:]
print(f"  [alt reading] within-streak first3 avg={float(seg.iloc[:3].mean()):,.0f} vs near3={float(seg.tail(3).mean()):,.0f} "
      f"-> chg {float((seg.tail(3).mean()-seg.iloc[:3].mean())/seg.iloc[:3].mean()*100):+.1f}%")

# ---------- 00412A ----------
f412 = parse("tw_00412a_oct.json")
f412 = f412[f412["vol"] > 0]
print("\n=== 00412A 主動安聯亞半導體 (TWSE official) ===")
print(f412[["open", "high", "low", "close", "vol"]].to_string())
print(f"  close={f412['close'].iloc[-1]} chg={(f412['close'].iloc[-1]/f412['close'].iloc[-2]-1)*100:+.2f}%")
print(f"  listing low close={f412['close'].min()} high close={f412['close'].max()} (bars={len(f412)})")
print(f"  10d avg vol={f412['vol'].tail(10).mean():,.0f} today={f412['vol'].iloc[-1]:,.0f}")

# ---------- P&L ----------
print("\n=== P&L ===")
for name, px, n, cost in [("6669", 1810.0, 258, 1737.0), ("00412A", 8.62, 15000, 9.43)]:
    mv, basis = px * n, cost * n
    print(f"  {name:8s} mv={mv:>10,.0f} basis={basis:>10,.0f} pnl={mv-basis:+9,.0f} ({(mv/basis-1)*100:+.2f}%)")
mvT = 1810.0 * 258 + 8.62 * 15000; bT = 1737.0 * 258 + 9.43 * 15000
print(f"  TOTAL    mv={mvT:>10,.0f} basis={bT:>10,.0f} pnl={mvT-bT:+9,.0f} ({(mvT/bT-1)*100:+.2f}%)")
print(f"  6669 vs trim1810: {(1810.0-1810.0):+.2f} (at line) | vs trim1720: {(1720/1810-1)*100:+.2f}% | vs stop1640: {(1640/1810-1)*100:+.2f}%")
print(f"  6669 vs add-buy 1720: {(1720/1810-1)*100:+.2f}%")
print(f"  00412A vs stop 8.00: {(8.00/8.62-1)*100:+.2f}% | vs hold-lower 8.50: {(8.50/8.62-1)*100:+.2f}% | vs TP12: {(12/8.62-1)*100:+.1f}%")
