# -*- coding: utf-8 -*-
"""ISA 만기 날 계좌가 마이너스면 — 빼서 예금에 둘까, 1년 더 둘까 (AlgoLab 재현 코드 · 표준 라이브러리만)

판
  · 매달 첫 거래일 100만원 × 36번(넣은 돈 3,600만원) · 사고팔 때 편도 0.1%
  · 만기 = 첫 납입 36개월 뒤 첫 거래일 · 만기 평가액 = 그날 다 팔았을 때(수수료 뺌)
  · 빼서 예금 = 만기 평가액을 12개월 예금(한국 3개월 금리 IR3TIB01KRM156N 월별 · 이자소득세 15.4%)
  · 1년 더   = 같은 ETF 를 12개월 더 들고 그날 판다(추가 납입 없음)
자료(data_isa/ · 2026-10-02 까지 얼림)
  · kodex200_splice.csv : KODEX200(069500) — 2009-04-17 전 네이버 종가(분배금 미반영) · 이후 야후 수정종가
  · SPY.csv(야후 adjclose) · DEXKOUS.csv(원/달러) · IR3TIB01KRM156N.csv(FRED)
  · N225.csv(야후 닛케이 가격지수 · 배당 미반영) · DEXJPUS.csv(엔/달러 · FRED)
돌리는 법: python run_isa.py
"""
import csv
import os
import statistics
import sys

sys.stdout.reconfigure(encoding="utf-8", errors="replace")

HERE = os.path.dirname(os.path.abspath(__file__))
D = os.path.join(HERE, "data_isa")
COST, TAX, MON = 0.001, 0.154, 100.0


def read(name, col):
    out = []
    with open(os.path.join(D, name), encoding="utf-8") as f:
        for r in csv.DictReader(f):
            v = float(r[col])
            if v > 0:
                out.append((r["date"], v))
    return out


def monthly_first(rows):
    m = {}
    for d, v in rows:
        k = d[:7]
        if k not in m:
            m[k] = (d, v)
    keys = sorted(m)
    for a, b in zip(keys, keys[1:]):  # 달이 비면 안 된다
        ya, ma = int(a[:4]), int(a[5:])
        assert (ya * 12 + ma) + 1 == int(b[:4]) * 12 + int(b[5:]), ("월 구멍", a, b)
    return keys, [m[k][0] for k in keys], [m[k][1] for k in keys]


RATE = {d[:7]: v for d, v in read("IR3TIB01KRM156N.csv", "IR3TIB01KRM156N")}


def rate_of(ym):
    ks = [k for k in RATE if k <= ym]
    return RATE[max(ks)] if ks else None  # 금리 자료 앞의 만기는 뺀다


def run(rows):
    keys, days, px = monthly_first(rows)
    out = []
    for i0 in range(0, len(px) - 48):
        sh = sum(MON * (1 - COST) / px[i0 + k] for k in range(36))
        paid = MON * 36
        im = i0 + 36
        vm = sh * px[im] * (1 - COST)
        w = vm
        rs = [rate_of(keys[im + j]) for j in range(12)]
        if None in rs:
            continue
        for rr in rs:
            w *= 1 + (rr / 100 / 12) * (1 - TAX)
        h = sh * px[im + 12] * (1 - COST)
        out.append(dict(start=keys[i0], mat=keys[im], matday=days[im], paid=paid, vm=vm, w=w, h=h))
    return out


def man(x):
    return f"{round(x):,}만원"


def report(name, rows):
    loss = [r for r in rows if r["vm"] < r["paid"]]
    gain = [r for r in rows if r["vm"] >= r["paid"]]
    win = [r for r in loss if r["h"] > r["w"]]
    rec = [r for r in loss if r["h"] >= r["paid"]]
    print(f"\n== {name} · 만기 {len(rows)}번 ({rows[0]['mat']} ~ {rows[-1]['mat']})")
    print(f"  만기 날 마이너스 {len(loss)}번 → 1년 더가 빼서 예금보다 더 남은 것 {len(win)}번")
    print(f"  1년 뒤 넣은 돈 이상 {len(rec)}번 · 넣은 돈보다 적었던 것 {len(loss) - len(rec)}번")
    print(f"  (대조) 만기 날 플러스 {len(gain)}번 → 1년 더가 더 남은 것 {sum(r['h'] > r['w'] for r in gain)}번")
    for lim, lab in ((200, "일반형 200만원"), (400, "서민형 400만원")):
        k = sum((r["h"] - max(0.0, r["h"] - r["paid"] - lim) * 0.099) > r["w"] for r in loss)
        print(f"  세금(비과세 {lab} 넘는 이익에 9.9%)을 1년 더 쪽에 물려도: {k}번")
    if loss:
        print(f"  1년 더 ÷ 예금 가운데 값 {statistics.median(r['h'] / r['w'] for r in loss):.3f}")
        print("  1년 더가 진 만기: " + " · ".join(r["mat"] for r in loss if r["h"] <= r["w"]))
    eps, cur = [], []
    for r in rows:
        if r["vm"] < r["paid"]:
            cur.append(r)
        elif cur:
            eps.append(cur); cur = []
    if cur:
        eps.append(cur)
    maj = sum(1 for e in eps if 2 * sum(r["h"] > r["w"] for r in e) > len(e))
    print(f"  따로 떨어진 마이너스 사건 {len(eps)}번 · 그중 1년 더가 과반 이긴 사건 {maj}번")
    n10 = sum(1 for r in loss if r["mat"] >= "2010-01")
    print(f"  2010년 이후 만기 중 마이너스 {n10}번")
    return loss


def main():
    k = read("kodex200_splice.csv", "close")
    rows = run(k)
    loss = report("KODEX200", rows)
    print("\n  [마이너스 만기 전부]  시작 → 만기 날 · 만기 평가 · 빼서 예금 1년 · 1년 더")
    for r in loss:
        mark = "1년 더 승" if r["h"] > r["w"] else "예금 승"
        print(f"    {r['start']} → {r['matday']} · {man(r['vm'])} · {man(r['w'])} · {man(r['h'])} · {mark}")
    for s in ("2020-03", "2021-02"):
        r = next(x for x in rows if x["start"] == s)
        print(f"\n[사례] {s} 시작 · 만기 {r['matday']} · 넣은 돈 {man(r['paid'])} · 만기 날 {man(r['vm'])}"
              f" → 빼서 예금 1년 {man(r['w'])} / 1년 더 {man(r['h'])}")
    report("KODEX200 · 2009-05 이후 시작만", [r for r in rows if r["start"] >= "2009-05"])
    spy = read("SPY.csv", "adjclose")
    fx = read("DEXKOUS.csv", "DEXKOUS")
    fxd = dict(fx)
    fxs = sorted(fxd)
    j, last, sp = 0, None, []
    for d, v in spy:
        while j < len(fxs) and fxs[j] <= d:
            last = fxd[fxs[j]]; j += 1
        if last:
            sp.append((d, v * last))
    report("S&P500 원화(SPY 수정종가 × 원/달러)", run(sp))
    # 일본 닛케이(가격지수 · 원화) — 원/엔 = 원/달러 ÷ 엔/달러
    jp = dict(read("DEXJPUS.csv", "DEXJPUS"))
    jk = sorted((d, fxd[d] / jp[d]) for d in fxd if d in jp)
    nk = read("N225.csv", "close")
    j, last, nkk = 0, None, []
    for d, v in nk:
        while j < len(jk) and jk[j][0] <= d:
            last = jk[j][1]; j += 1
        if last:
            nkk.append((d, v * last))
    report("일본 닛케이 원화(가격지수 × 원/엔)", run(nkk))
    print("\n끝.")


if __name__ == "__main__":
    main()
