# -*- coding: utf-8 -*-
# =============================================================================
# 커버드콜 ETF 12년 실측 — AlgoLab (algolab.co.kr/coveredcall)
#
# 영상 "커버드콜 ETF 12년, 원금이 730만원 남았습니다"의 재현 코드입니다.
# 표준 라이브러리만 씁니다(pip 설치 불필요). 파이썬 3.8+ 에서:
#
#   python covered_call_backtest.py
#
# 하는 일:
#   1) 야후 파이낸스에서 QYLD·QQQ·XYLD·SPY·SCHD 일봉과 분배금 내역을 받는다
#   2) 1000만원을 넣고 세 가지로 계산한다
#      A. 분배금 전액 재투자(총수익)  B. 분배금 현금 수령  C. 분배금에 15% 원천징수 후 재투자
#   3) 연도별 총수익, 주당 분배금 성장까지 영상의 모든 수치를 다시 만든다
#
# ⚠ ASOF 를 영상 기준일로 고정했습니다. 오늘까지 데이터로 보려면 None 으로 바꾸세요.
#   (고정하지 않으면 시세가 움직여 영상과 숫자가 달라집니다 — 오류가 아닙니다)
# =============================================================================
import datetime
import json
import urllib.request

ASOF = datetime.date(2026, 8, 27)   # 영상 기준일. 최신으로 보려면 None
INIT = 1000.0                       # 만원


def fetch(sym):
    u = (f"https://query1.finance.yahoo.com/v8/finance/chart/{sym}"
         f"?range=20y&interval=1d&events=div%2Csplit")
    req = urllib.request.Request(u, headers={"User-Agent": "Mozilla/5.0"})
    j = json.loads(urllib.request.urlopen(req, timeout=40).read())
    r = j["chart"]["result"][0]
    ts = r["timestamp"]
    close = r["indicators"]["quote"][0]["close"]
    adj = r["indicators"]["adjclose"][0]["adjclose"]
    rows = []
    for t, c, a in zip(ts, close, adj):
        if c is None or a is None:
            continue
        d = datetime.date.fromtimestamp(t)
        if ASOF and d > ASOF:
            continue
        rows.append((d, c, a))
    divs = []
    for v in (r.get("events", {}).get("dividends", {}) or {}).values():
        d = datetime.date.fromtimestamp(v["date"])
        if ASOF and d > ASOF:
            continue
        divs.append((d, v["amount"]))
    divs.sort()
    return rows, divs


def reinvest(rows, d0):
    """A. 분배금 전액 재투자 = adjclose 총수익"""
    seg = [r for r in rows if r[0] >= d0]
    return INIT * seg[-1][2] / seg[0][2]


def take_cash(rows, divs, d0, tax=0.0):
    """B. 분배금 현금 수령 — 수량 고정. (남은 평가액, 받은 분배금 합계)"""
    seg = [r for r in rows if r[0] >= d0]
    units = INIT / seg[0][1]
    cash = sum(units * amt * (1 - tax) for d, amt in divs if seg[0][0] <= d <= seg[-1][0])
    return units * seg[-1][1], cash


def reinvest_after_tax(rows, divs, d0, tax=0.15):
    """C. 분배금에 세금 떼고 그날 종가로 재투자"""
    seg = [r for r in rows if r[0] >= d0]
    di = dict(divs)
    units = INIT / seg[0][1]
    for d, c, a in seg:
        if d in di:
            units += units * di[d] * (1 - tax) / c
    return units * seg[-1][1]


def yearly(rows, d0):
    seg = [r for r in rows if r[0] >= d0]
    out = {}
    for y in range(seg[0][0].year, seg[-1][0].year + 1):
        m = [i for i, r in enumerate(seg) if r[0].year == y]
        if len(m) < 10:
            continue
        prev = seg[m[0] - 1][2] if m[0] > 0 else seg[m[0]][2]
        out[y] = seg[m[-1]][2] / prev - 1
    return out


def year_div(divs, y):
    return sum(a for d, a in divs if d.year == y)


def main():
    data = {s: fetch(s) for s in ("QYLD", "QQQ", "XYLD", "SPY", "SCHD")}
    d0 = data["QYLD"][0][0][0]          # QYLD 상장일 = 2013-12-12
    print(f"구간: {d0} ~ {data['QYLD'][0][-1][0]}  (1000만원 기준, 만원 단위)\n")

    print("[나스닥100] 같은 지수, 두 ETF")
    for sym in ("QYLD", "QQQ"):
        rows, divs = data[sym]
        a = reinvest(rows, d0)
        b_val, b_cash = take_cash(rows, divs, d0)
        c = reinvest_after_tax(rows, divs, d0)
        print(f"  {sym:5s} 재투자 {a:7.0f}만 | 현금수령: 남은 돈 {b_val:6.0f}만"
              f" + 받은 배당 {b_cash:6.0f}만 = {b_val + b_cash:7.0f}만 | 세후재투자 {c:7.0f}만")

    print("\n[S&P500] 같은 비교 — 부호가 유지되는가")
    d0x = data["XYLD"][0][0][0]
    for sym in ("XYLD", "SPY"):
        print(f"  {sym:5s} 재투자 {reinvest(data[sym][0], d0x):7.0f}만  (시작 {d0x})")

    print("\n[연도별 총수익] QYLD vs QQQ — 커버드콜이 이긴 해")
    yq = yearly(data["QYLD"][0], d0)
    yb = yearly(data["QQQ"][0], d0)
    wins = [y for y in yq if y in yb and yq[y] > yb[y]]
    for y in sorted(yq):
        if y in yb:
            mark = " ← QYLD 승" if y in wins else ""
            print(f"  {y}  QYLD {yq[y]*100:+6.1f}%   QQQ {yb[y]*100:+6.1f}%{mark}")
    print(f"  → QYLD가 이긴 해 {len(wins)}번")

    print("\n[주당 연 분배금] 배당이 자라는가 (2014 → 2025)")
    for sym in ("SCHD", "QYLD"):
        v14 = year_div(data[sym][1], 2014)
        v25 = year_div(data[sym][1], 2025)
        print(f"  {sym:5s} ${v14:.2f} → ${v25:.2f}  ({v25 / v14:.2f}배)")

    rows, divs = data["SCHD"]
    seg0 = [r for r in rows if r[0] >= d0][0]
    print(f"\n[SCHD] 2013-12 매수가 기준 지금 시가배당률(YoC): "
          f"{year_div(divs, 2025) / seg0[1] * 100:.1f}%")
    print(f"  SCHD 재투자 {reinvest(rows, d0):.0f}만 · "
          f"같은 기간 SPY {reinvest(data['SPY'][0], d0):.0f}만")

    print("\n[민감도] 시작일을 바꿔도 순위가 유지되는가 (재투자)")
    for dd in (datetime.date(2018, 1, 2), datetime.date(2021, 11, 19),
               datetime.date(2022, 12, 28)):
        a = reinvest(data["QYLD"][0], dd)
        b = reinvest(data["QQQ"][0], dd)
        print(f"  {dd} 시작  QYLD {a:6.0f}만  QQQ {b:6.0f}만")

    print("\n한계: 미국 상장 ETF 세전 기준, 환율·국내 세금 미반영. 과거 실측이지 예측이 아닙니다.")


if __name__ == "__main__":
    main()
