"""
최고의 날 10일을 놓치면 — 코스피 일별 수익률에서 극단적인 날을 빼 보기 (짧은 실험 스크립트)

사용법:
    pip install pandas numpy matplotlib finance-datareader
    python best_worst_days.py
    python best_worst_days.py --synthetic     # 난수 데이터로 동작 확인

데이터: FinanceDataReader 의 코스피(KS11) 일별 종가 (1995-05 ~ 2026-10-14, 글과 같은 기간), 가격 지수(배당 제외)
"놓친 날"은 그날 하루 수익률을 0으로 바꿔 계산한다(현금 이자 없음).
결과:  output/best_worst_results.txt 와 차트 PNG
"""
import argparse
import os

import numpy as np
import pandas as pd

HERE = os.path.dirname(os.path.abspath(__file__))
OUT = os.path.join(HERE, "output")
END = "2026-10-14"                                # 글의 표와 같은 기간(2026년 10월 14일까지)
os.makedirs(OUT, exist_ok=True)


def load(synthetic=False):
    if synthetic:
        rng = np.random.default_rng(5)
        idx = pd.bdate_range("1995-05-02", "2026-09-17")
        return pd.Series(1000 * np.exp(np.cumsum(rng.standard_t(4, len(idx)) * 0.012 + 0.0002)), index=idx)
    import FinanceDataReader as fdr
    return fdr.DataReader("KS11", "1995-01-01", END)["Close"].dropna()


def cagr(r):
    years = (r.index[-1] - r.index[0]).days / 365.25
    return (1 + r).prod() ** (1 / years) - 1


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--synthetic", action="store_true")
    a = ap.parse_args()
    close = load(a.synthetic)
    r = close.pct_change().dropna()
    best, worst = r.nlargest(10), r.nsmallest(10)
    lines = [f"기간 {r.index[0].date()} ~ {r.index[-1].date()}, {len(r)}거래일"]

    rows = [("그대로 보유", r)]
    for n in [10, 20, 30]:
        rows.append((f"최고의 날 {n}일 놓침", r.where(~r.index.isin(r.nlargest(n).index), 0.0)))
    rows.append(("최악의 날 10일 피함", r.where(~r.index.isin(worst.index), 0.0)))
    both = best.index.union(worst.index)
    rows.append(("최고·최악 10일 모두 빠짐", r.where(~r.index.isin(both), 0.0)))
    lines.append("")
    for name, rr in rows:
        lines.append(f"{name:<16} 연복리 {cagr(rr):6.2%}  누적 {(1 + rr).prod():6.2f}배")

    # 붙어 다니는지: 최고의 날 각각에서 가장 가까운 최악의 날까지의 거래일 수
    pos = pd.Series(np.arange(len(r)), index=r.index)
    gaps = [int(min(abs(pos[b] - pos[w]) for w in worst.index)) for b in best.index]
    lines.append("\n최고의 날 10일 (날짜, 수익률, 가장 가까운 최악의 날까지 거래일 수)")
    for (d, v), g in sorted(zip(best.items(), gaps)):
        lines.append(f"  {d.date()}  {v:+.2%}  {g}")
    lines.append("최악의 날 10일")
    for d, v in sorted(worst.items()):
        lines.append(f"  {d.date()}  {v:+.2%}")
    lines.append(f"최고의 날 중 최악의 날과 20거래일 안에 있는 날: {sum(g <= 20 for g in gaps)}/10")

    # 변동성이 높은 시기에 몰리는지: 직전 60거래일 변동성 상위 10% 구간에 속한 비율
    vol = r.rolling(60).std().shift(1)
    hi = vol > vol.quantile(0.9)
    lines.append(f"직전 60일 변동성 상위 10% 구간에 속한 날: 최고 {hi[best.index].sum()}/10, 최악 {hi[worst.index].sum()}/10")
    text = "\n".join(lines)
    print(text)
    with open(os.path.join(OUT, "best_worst_results.txt"), "w", encoding="utf-8") as fh:
        fh.write(text + "\n")

    try:
        import matplotlib
        matplotlib.use("Agg")
        import matplotlib.pyplot as plt
        fig, ax = plt.subplots(figsize=(10, 4.5))
        ax.plot(close.loc[r.index], color="#555555", linewidth=0.9, label="KOSPI")
        ax.scatter(best.index, close.loc[best.index], color="#1f5fbf", s=40, zorder=3, label="10 best days")
        ax.scatter(worst.index, close.loc[worst.index], color="#b22222", s=40, marker="v", zorder=3, label="10 worst days")
        ax.set_yscale("log")
        ax.set_title("KOSPI: the 10 best and 10 worst days cluster in the same turbulent periods")
        ax.grid(alpha=0.3); ax.legend()
        fig.tight_layout(); fig.savefig(os.path.join(OUT, "52_best_worst_days.png"), dpi=120); plt.close(fig)
    except ImportError:
        pass


if __name__ == "__main__":
    main()
