"""
레버리지 ETF를 오래 들고 있으면 생기는 일 (실험 스크립트)

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

데이터: FinanceDataReader 의 코스피(KS11) 일별 종가 (1995-05 ~ 2026-09-17, 글과 같은 기간)
결과:  output/leveraged_results.txt 와 차트 PNG

매일 장 마감에 지수 하루 수익률의 L배를 주도록 다시 맞추는 상품을 흉내 낸다.
실제 상품의 보수, 선물 롤오버·금리 비용, 추적 오차, 배당은 넣지 않았다(가격 지수 기준).
"""
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-09-17"                                # 글의 표와 같은 기간(2026년 9월 17일까지)
os.makedirs(OUT, exist_ok=True)

PERIODS = {
    "2003-03~2007-10 상승": ("2003-03-17", "2007-10-31"),
    "2007-11~2008-10 하락": ("2007-10-31", "2008-10-24"),
    "2011-05~2016-12 박스권": ("2011-05-02", "2016-12-29"),
    "2020-03~2021-06 상승": ("2020-03-19", "2021-06-30"),
    "2021-07~2022-09 하락": ("2021-06-30", "2022-09-30"),
}


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


def years(r):
    return (r.index[-1] - r.index[0]).days / 365.25


def summary(r):
    w = (1 + r).cumprod()
    y = years(r)
    return {
        "누적(%)": (w.iloc[-1] - 1) * 100,
        "연복리(%)": (w.iloc[-1] ** (1 / y) - 1) * 100,
        "연변동성(%)": r.std() * np.sqrt(len(r) / y) * 100,
        "MDD(%)": (w / w.cummax() - 1).min() * 100,
    }


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()
    y = years(r)
    n = len(r) / y                                   # 1년 거래일 수(실제 데이터 기준)
    lines = [f"기간 {r.index[0].date()} ~ {r.index[-1].date()}, {len(r)}거래일"]

    # 1) 전체 기간: 1배, 2배, -1배, 3배
    tab = pd.DataFrame({f"{L}배": summary(L * r) for L in [1, 2, -1, 3]}).T
    lines.append("\n[전체 기간, 비용 없음]\n" + tab.round(1).to_string())
    cost = 0.01                                      # 가정: 연 1% 비용을 매일 나눠 뺀다
    lines.append(f"2배, 연 {cost:.0%} 비용 가정 시 연복리: {summary(2 * r - cost / n)['연복리(%)']:.1f}%")

    # 2) 변동성 끌림 근사식: 연 로그 성장률 ≈ L·μ − L²·σ²/2
    mu, s2 = r.mean() * n, r.var() * n
    rows = []
    for L in [1, 2, -1, 3]:
        rows.append({"배수": L, "L·μ(%)": L * mu * 100, "끌림 L²σ²/2(%)": L * L * s2 / 2 * 100,
                     "근사 로그성장(%)": (L * mu - L * L * s2 / 2) * 100,
                     "실제 로그성장(%)": np.log1p(L * r).sum() / y * 100})
    lines.append("\n[근사식 vs 실제, 연율]\n" + pd.DataFrame(rows).set_index("배수").round(1).to_string())

    # 3) 구간별
    rows = []
    for k, (s, e) in PERIODS.items():
        rr = r.loc[s:e].iloc[1:]
        if len(rr) < 20:
            continue
        rows.append({"구간": k, "지수(%)": ((1 + rr).prod() - 1) * 100,
                     "지수×2(%)": ((1 + rr).prod() - 1) * 200,
                     "2배 상품(%)": ((1 + 2 * rr).prod() - 1) * 100,
                     "-1배 상품(%)": ((1 - rr).prod() - 1) * 100,
                     "연변동성(%)": rr.std() * np.sqrt(n) * 100})
    lines.append("\n[구간별, 비용 없음]\n" + pd.DataFrame(rows).set_index("구간").round(1).to_string())

    # 4) 1년(250거래일) 보유를 모든 시작일에 대해
    lr1 = np.log1p(r).rolling(250).sum()
    lr2 = np.log1p(2 * r).rolling(250).sum()
    df = pd.DataFrame({"R1": np.exp(lr1) - 1, "R2": np.exp(lr2) - 1}).dropna()
    gap = df.R2 - 2 * df.R1
    flat = df[df.R1.abs() < 0.05]
    lines.append(f"\n[1년 보유 {len(df)}개 구간]")
    lines.append(f"2배 상품이 '지수 수익률×2'에 못 미친 비율: {(gap < 0).mean():.1%}, 차이 중앙값 {gap.median():.1%}p")
    lines.append(f"지수가 1년간 ±5% 안에서 끝난 {len(flat)}개 구간: 2배 상품 수익률 중앙값 {flat.R2.median():.1%}, "
                 f"손실 비율 {(flat.R2 < 0).mean():.1%}")

    text = "\n".join(lines)
    print(text)
    with open(os.path.join(OUT, "leveraged_results.txt"), "w", encoding="utf-8") as fh:
        fh.write(text + "\n")

    try:
        import matplotlib
        matplotlib.use("Agg")
        import matplotlib.pyplot as plt
        colors = {1: "#333333", 2: "#1f5fbf", -1: "#b22222"}
        fig, ax = plt.subplots(figsize=(10, 4.5))
        for L, lab in [(1, "KOSPI (1x)"), (2, "2x daily"), (-1, "-1x daily")]:
            ax.plot((1 + L * r).cumprod(), label=lab, color=colors[L], linewidth=1.1)
        ax.set_yscale("log")
        ax.set_title("KOSPI vs simulated daily-rebalanced 2x and -1x (no costs, log scale)")
        ax.set_ylabel("growth of 1")
        ax.grid(alpha=0.3); ax.legend()
        fig.tight_layout(); fig.savefig(os.path.join(OUT, "49_leverage_full.png"), dpi=120); plt.close(fig)

        s, e = PERIODS["2011-05~2016-12 박스권"]
        rr = r.loc[s:e].iloc[1:]
        fig, ax = plt.subplots(figsize=(10, 4.5))
        for L, lab in [(1, "KOSPI (1x)"), (2, "2x daily"), (-1, "-1x daily")]:
            ax.plot((1 + L * rr).cumprod(), label=lab, color=colors[L], linewidth=1.1)
        ax.axhline(1, color="black", linewidth=0.8)
        ax.set_title("Range-bound market, 2011-05 to 2016-12: both 2x and -1x lose")
        ax.set_ylabel("growth of 1")
        ax.grid(alpha=0.3); ax.legend()
        fig.tight_layout(); fig.savefig(os.path.join(OUT, "50_leverage_range.png"), dpi=120); plt.close(fig)
    except ImportError:
        pass


if __name__ == "__main__":
    main()
