# -*- coding: utf-8 -*-
"""코스닥 레버리지, 계속 들까 밤에만 들까 — 알고랩 재현 코드 (2026-10-21 영상).

계좌  : 2015-12-30 종가에 1,000만원으로 산다 → 2026-10-20 까지.
  계속 들고 있기 = 종가 → 종가 그대로
  밤에만         = 장 마감 동시호가(종가)에 사서 다음 날 장 시작 동시호가(시가)에 판다 · 판 돈은 낮 동안 현금
  낮에만         = 시가에 사서 그날 종가에 판다
  수수료         = 살 때·팔 때 각각 편도 FEE(기본 0.015%) — 밤에만·낮에만은 날마다 두 번 낸다
자료  : data_kosdaqnight/*.csv — 네이버 증권 일봉 원시 시세(거래소 체결가 · 분배금 미보정) · 2026-10-20 까지 얼림.
        233740 KODEX 코스닥150레버리지 · 229200 KODEX 코스닥150 · 122630 KODEX 레버리지 · 069500 KODEX 200
돌리는 법: python run_kosdaqnight.py   (표준 라이브러리만 씁니다)
"""
import csv
import datetime
import math
import os

HERE = os.path.dirname(os.path.abspath(__file__))
D = os.path.join(HERE, "data_kosdaqnight")
START, ASOF = "2015-12-30", "2026-10-20"   # 기준일 고정 — 영상과 같은 숫자가 나온다
FEE = 0.00015
WON = 1000  # 만원
NAME = {"233740": "KODEX 코스닥150레버리지", "229200": "KODEX 코스닥150", "122630": "KODEX 레버리지(코스피200 2배)", "069500": "KODEX 200"}


def load(code, start=START, end=ASOF):
    with open(os.path.join(D, code + ".csv"), encoding="utf-8") as f:
        rows = [r for r in csv.DictReader(f) if start <= r["date"] <= end and float(r["open"]) > 0]
    o = [float(r["open"]) for r in rows]; c = [float(r["close"]) for r in rows]
    ds = [r["date"] for r in rows]
    # i 번째 날(1부터): 밤 = c[i-1]→o[i] · 낮 = o[i]→c[i]
    night = [o[i] / c[i - 1] - 1 for i in range(1, len(rows))]
    day = [c[i] / o[i] - 1 for i in range(1, len(rows))]
    hold = [c[i] / c[i - 1] - 1 for i in range(1, len(rows))]
    return ds[1:], ds[0], hold, night, day


def grow(rets, fee=0.0, w=1.0):
    eq, out = 1.0, []
    for r in rets:
        eq *= (1 + w * r) * (1 - fee) ** 2 if fee else (1 + w * r)
        out.append(eq)
    return out


def summary(eq, ds, d0):
    yrs = (datetime.date.fromisoformat(ds[-1]) - datetime.date.fromisoformat(d0)).days / 365.25
    peak, mdd, pk_d, lo_d, cur_pk = 1.0, 0.0, d0, d0, d0
    for e, d in zip(eq, ds):
        if e > peak:
            peak, cur_pk = e, d
        if e / peak - 1 < mdd:
            mdd, pk_d, lo_d = e / peak - 1, cur_pk, d
    return WON * eq[-1], (eq[-1] ** (1 / yrs) - 1) * 100, mdd * 100, pk_d, lo_d


def won(x):
    return f"{x:,.0f}만원"


def line(label, eq, ds, d0):
    end, cagr, mdd, a, b = summary(eq, ds, d0)
    print(f"  {label:<10} 1,000만원 → {won(end)}  (연 {cagr:.2f}% · 가장 크게 떨어졌을 때 {mdd:.2f}% {a}→{b})")
    return end, mdd


def breakeven(hold, night):
    target = grow(hold)[-1]
    lo, hi = 0.0, 0.002
    for _ in range(60):
        f = (lo + hi) / 2
        lo, hi = (f, hi) if grow(night, f)[-1] > target else (lo, f)
    return (lo + hi) / 2 * 100


print(f"재현 — 기준일 {ASOF} · 시작 {START} 종가 · 수수료 편도 {FEE*100:.3f}%")
for code in ("233740", "229200", "122630", "069500"):
    ds, d0, hold, night, day = load(code)
    print(f"\n[{NAME[code]} {code}] {ds[0]} ~ {ds[-1]} · {len(ds):,}거래일")
    he, _ = line("계속 들고 있기", grow(hold), ds, d0)
    ne, nm = line("밤에만", grow(night, FEE), ds, d0)
    line("낮에만", grow(day, FEE), ds, d0)
    yrs = (datetime.date.fromisoformat(ds[-1]) - datetime.date.fromisoformat(d0)).days / 365.25
    print(f"  밤 사이 로그 합 연 {sum(math.log1p(x) for x in night)/yrs*100:+.2f}% · 장중 로그 합 연 {sum(math.log1p(x) for x in day)/yrs*100:+.2f}%")
    print(f"  수수료 손익분기(밤에만 = 계속 들고 있기): 편도 {breakeven(hold, night):.4f}%")
    if code != "233740":
        continue
    # 같은 낙폭 고정 비중 — 늘 w 만 들고 나머지는 현금(이자 0)
    lo, hi = 0.0, 1.0
    for _ in range(60):
        w = (lo + hi) / 2
        m = summary(grow(hold, 0, w), ds, d0)[2]
        lo, hi = (w, hi) if m > nm else (lo, w)
    w = (lo + hi) / 2
    print(f"  같은 낙폭 고정: 늘 {w*100:.1f}% 만 들고 나머지 현금 → {won(summary(grow(hold, 0, w), ds, d0)[0])} (가장 크게 떨어졌을 때 {summary(grow(hold, 0, w), ds, d0)[2]:.2f}%)")
    print("  수수료별 밤에만: " + " · ".join(f"{f*100:.4f}% {won(grow(night, f)[-1]*WON)}" for f in (0.0, 0.000036, 0.00015, 0.0003, 0.0005, 0.001)))
    print("  해마다(계속 / 밤에만 / 낮에만):")
    wins = 0
    years = sorted(set(d[:4] for d in ds))
    for y in years:
        idx = [i for i, d in enumerate(ds) if d[:4] == y]
        h = 1; n = 1; dd = 1
        for i in idx:
            h *= 1 + hold[i]; n *= (1 + night[i]) * (1 - FEE) ** 2; dd *= 1 + day[i]
        wins += n > h
        print(f"    {y}  {(h-1)*100:+7.1f}%  {(n-1)*100:+7.1f}%  {(dd-1)*100:+7.1f}%")
    print(f"  밤에만이 이긴 해 {wins}/{len(years)}")
    for tag, a, b in (("앞 5년", "2016-01-01", "2020-12-31"), ("뒤 6년", "2021-01-01", ASOF)):
        idx = [i for i, d in enumerate(ds) if a <= d <= b]
        h = 1; n = 1
        for i in idx:
            h *= 1 + hold[i]; n *= (1 + night[i]) * (1 - FEE) ** 2
        print(f"  {tag}({a[:4]}~{b[:4]}): 계속 {won(h*WON)} · 밤에만 {won(n*WON)}")
    # 3년 구간 — 2016-01 부터 2023-10 까지 달마다 시작
    tot = win = 0
    y0, m0 = 2016, 1
    while (y0, m0) <= (2023, 10):
        s = f"{y0}-{m0:02d}-01"; e = f"{y0+3}-{m0:02d}-01"
        idx = [i for i, d in enumerate(ds) if s <= d < e]
        h = 1; n = 1
        for i in idx:
            h *= 1 + hold[i]; n *= (1 + night[i]) * (1 - FEE) ** 2
        tot += 1; win += n > h
        m0 += 1
        if m0 == 13:
            y0, m0 = y0 + 1, 1
    print(f"  달마다 시작한 3년 구간 {tot}개 중 밤에만이 더 남은 구간 {win}개")
    # 세금 — 이 ETF 는 매매차익 보유기간과세 15.4%(매매차익·과표기준가 증분 중 작은 값 · 손실 상계 없음) 대상.
    #   과표기준가 자료가 없어 정확히는 못 잰다. 최악 가정: 이긴 밤마다 순이익의 15.4% 를 낸다.
    eq = 1.0
    for x in night:
        gg = (1 + x) * (1 - FEE) ** 2 - 1
        eq *= 1 + (gg * (1 - 0.154) if gg > 0 else gg)
    print(f"  [세금 최악 가정] 이긴 밤마다 15.4% → 밤에만 {won(eq*WON)} (계속 들고 있기는 손실로 끝나 세금 0 → {won(grow(hold)[-1]*WON)})")
print("\n끝.")
