# -*- coding: utf-8 -*-
"""주식 계좌에 섞을 금속 — 은 ETF 냐 금 ETF 냐 (AlgoLab · 2026-10-12)

세 계좌를 같은 날 같은 돈으로 시작한다.
  ① KODEX 200 만
  ② KODEX 200 7 : KODEX 은선물(H) 3
  ③ KODEX 200 7 : KODEX 골드선물(H) 3
매달 첫 거래일 종가에 비율을 다시 7:3 으로 맞춘다(사고판 금액에 편도 0.1%).
가격은 야후 수정종가(분배금 반영) — data_silver/ 에 얼려 두었다(2026-10-08 까지).
교차 판: 미국 SPY + SLV / GLD (달러, 2006-05~).

돌리는 법:  pip install numpy pandas  →  python run_silver.py
"""
import os
import numpy as np
import pandas as pd

HERE = os.path.dirname(os.path.abspath(__file__))
D = os.path.join(HERE, "data_silver")
FEE = 0.001        # 편도 0.1%
원금 = 1000         # 만원
WS = [0, 5, 10, 15, 20, 25, 30, 40, 50]
ASOF = {"K": "2026-10-08", "U": "2026-10-09"}
PANELS = {
    "K": dict(이름="국내 상장(원화)", eq="069500_KS", ag="144600_KS", au="132030_KS", start="2011-07-18",
              주식="KODEX 200", 은="KODEX 은선물(H)", 금="KODEX 골드선물(H)"),
    "U": dict(이름="미국 상장(달러)", eq="SPY", ag="SLV", au="GLD", start="2006-05-01",
              주식="SPY", 은="SLV", 금="GLD"),
}


def load(sym):
    df = pd.read_csv(os.path.join(D, sym + "_1d.csv"), parse_dates=["date"])
    return df.set_index("date")["close"].rename(sym)


def panel(pn):
    P = PANELS[pn]
    px = pd.concat([load(P["eq"]), load(P["ag"]), load(P["au"])], axis=1, join="inner").dropna()
    return px.loc[P["start"]:ASOF[pn]], P


def sim(px, w):
    """두 열(주식, 금속) 가격 → 계좌 가치(첫날 1 - 비용). 월이 바뀐 첫 거래일 종가에 되돌림."""
    r = px.pct_change().fillna(0.0).values
    idx = px.index
    tgt = np.array([1 - w, w])
    hold = tgt * (1 - FEE)
    vals = np.empty(len(px)); vals[0] = hold.sum()
    for i in range(1, len(px)):
        hold = hold * (1 + r[i])
        v = hold.sum()
        if w > 0 and idx[i].month != idx[i - 1].month:
            t = np.abs(tgt - hold / v).sum() / 2
            v *= 1 - FEE * 2 * t
            hold = tgt * v
        vals[i] = v
    return pd.Series(vals, idx)


def 낙폭(s):
    return float((s / s.cummax() - 1).min() * 100)


def 골짜기(s):
    dd = s / s.cummax() - 1
    t = dd.idxmin()
    return s.loc[:t].idxmax().date(), t.date()


def 시작달바꾸기(px, P, years):
    starts = px.index.to_series().groupby([px.index.year, px.index.month]).first().values
    n = 금얕음 = 금더벌음 = 0
    for st in map(pd.Timestamp, starts):
        en = st + pd.DateOffset(years=years) if years else px.index[-1]
        if en > px.index[-1]:
            break
        sub = px.loc[st:en]
        if len(sub) < 200:
            continue
        sa = sim(sub[[P["eq"], P["ag"]]], 0.30)
        sg = sim(sub[[P["eq"], P["au"]]], 0.30)
        n += 1
        금얕음 += 낙폭(sg) > 낙폭(sa)
        금더벌음 += sg.iloc[-1] > sa.iloc[-1]
    return n, 금얕음, 금더벌음


for pn in ("K", "U"):
    px, P = panel(pn)
    print(f"[{P['이름']}] {P['주식']} · {P['은']} · {P['금']} · {px.index[0].date()} ~ {px.index[-1].date()} · 거래일 {len(px):,}")
    s0 = sim(px[[P["eq"], P["ag"]]], 0.0)
    sa = sim(px[[P["eq"], P["ag"]]], 0.30)
    sg = sim(px[[P["eq"], P["au"]]], 0.30)
    for 이름, s in (("주식만", s0), ("주식 7 : 은 3", sa), ("주식 7 : 금 3", sg)):
        a, b = 골짜기(s)
        print(f"  {이름:<10} 가장 크게 줄었을 때 {낙폭(s):.2f}% ({a} → {b}) · 원금 {원금:,}만원 → 끝 {s.iloc[-1] * 원금:,.0f}만원")
    # 금속 대신 현금 3(이자 0 — 현금 쪽에 불리)
    pxc = px.assign(현금=1.0)
    sc_ = sim(pxc[[P["eq"], "현금"]], 0.30)
    a, b = 골짜기(sc_)
    print(f"  주식 7 : 현금 3(이자 0) 가장 크게 줄었을 때 {낙폭(sc_):.2f}% ({a} → {b}) · 원금 {원금:,}만원 → 끝 {sc_.iloc[-1] * 원금:,.0f}만원")
    starts = px.index.to_series().groupby([px.index.year, px.index.month]).first().values
    n = 금얕 = 은얕 = 금더 = 0
    for st_ in map(pd.Timestamp, starts):
        en_ = st_ + pd.DateOffset(years=5)
        if en_ > px.index[-1]:
            break
        sub = pxc.loc[st_:en_]
        c_ = 낙폭(sim(sub[[P["eq"], "현금"]], 0.30)); g_s = sim(sub[[P["eq"], P["au"]]], 0.30)
        n += 1; 금얕 += 낙폭(g_s) > c_; 은얕 += 낙폭(sim(sub[[P["eq"], P["ag"]]], 0.30)) > c_
        금더 += g_s.iloc[-1] > sim(sub[[P["eq"], "현금"]], 0.30).iloc[-1]
    print(f"  5년 창 {n}번 대 현금 3: 금이 덜 줄어든 {금얕}번 · 은이 덜 줄어든 {은얕}번 · 금이 더 남은 {금더}번")
    print("  비율을 바꾸면(금속 몫 %) — 가장 크게 줄었을 때 은 / 금:")
    얕음 = 0
    for w in WS[1:]:
        a = 낙폭(sim(px[[P["eq"], P["ag"]]], w / 100)); g = 낙폭(sim(px[[P["eq"], P["au"]]], w / 100))
        얕음 += g > a
        print(f"    {w:>2}%  은 {a:7.2f}%  금 {g:7.2f}%")
    print(f"  금 쪽이 덜 줄어든 비율: {얕음}/{len(WS) - 1}")
    n, 얕, 벌 = 시작달바꾸기(px, P, 5)
    print(f"  시작 달을 매달 옮긴 5년 창 {n}번: 금 섞은 계좌가 덜 줄어든 {얕}번 · 더 번 쪽 금 {벌}번 · 은 {n - 벌}번")
    n2, 얕2, 벌2 = 시작달바꾸기(px, P, 0)
    print(f"  시작 달부터 끝까지 창 {n2}번: 금이 덜 줄어든 {얕2}번 · 더 번 쪽 금 {벌2}번 · 은 {n2 - 벌2}번")
    # 겹치지 않는 5년 토막
    st = px.index[0]; 토막 = []
    while st + pd.DateOffset(years=5) <= px.index[-1]:
        sub = px.loc[st:st + pd.DateOffset(years=5)]
        토막.append(f"{sub.index[0].date()}~{sub.index[-1].date()} 은 {낙폭(sim(sub[[P['eq'], P['ag']]], 0.30)):.2f}% / 금 {낙폭(sim(sub[[P['eq'], P['au']]], 0.30)):.2f}%")
        st = st + pd.DateOffset(years=5)
    print("  겹치지 않는 5년 토막(가장 크게 줄었을 때 은 / 금): " + " · ".join(토막))
    # 되맞출 때 사고판 양(계좌 대비 합) — 빠진 호가 차이가 어느 쪽에 유리한가
    def 매매량(col):
        r = px[[P["eq"], col]].pct_change().fillna(0).values; idx = px.index
        tgt = np.array([0.7, 0.3]); hold = tgt.copy(); t = 0.0
        for i in range(1, len(px)):
            hold = hold * (1 + r[i]); v = hold.sum()
            if idx[i].month != idx[i - 1].month:
                t += np.abs(tgt - hold / v).sum() / 2; hold = tgt * v
        return t
    print(f"  되맞출 때 사고판 양(계좌 대비 합): 은 {매매량(P['ag']):.2f} · 금 {매매량(P['au']):.2f} — 빠진 호가 차이는 많이 사고판 쪽에 유리")
    r = px.pct_change().dropna()
    vol = r.std() * np.sqrt(252) * 100
    big = r[r[P["eq"]] < -0.03]
    def etf낙폭(c):
        return float((px[c] / px[c].cummax() - 1).min() * 100)
    print(f"  왜: ETF 자체로 가장 크게 빠진 폭 — 은 {etf낙폭(P['ag']):.2f}% · 금 {etf낙폭(P['au']):.2f}% · 주식 {etf낙폭(P['eq']):.2f}%")
    print(f"  왜: 주식이 하루 3% 넘게 빠진 날 {len(big)}일 — 은 평균 {big[P['ag']].mean() * 100:.2f}% (같이 빠진 날 {(big[P['ag']] < 0).sum()}) · "
          f"금 평균 {big[P['au']].mean() * 100:.2f}% (같이 빠진 날 {(big[P['au']] < 0).sum()})")
    print(f"  왜: 1년 출렁임 — 주식 {vol[P['eq']]:.1f}% · 은 {vol[P['ag']]:.1f}% · 금 {vol[P['au']]:.1f}% · 하루 움직임이 같이 가는 정도 주식-은 {r.corr().iloc[0, 1]:.2f} · 주식-금 {r.corr().iloc[0, 2]:.2f}")
    a, b = 골짜기(s0)
    pk, bt = pd.Timestamp(a), pd.Timestamp(b)
    print(f"  주식만 계좌의 가장 큰 골짜기({a} → {b}) 동안: 주식만 {(s0[bt] / s0[pk] - 1) * 100:.2f}% · "
          f"주식 7 : 은 3 {(sa[bt] / sa.loc[pk:bt].max() - 1) * 100:.2f}% · 주식 7 : 금 3 {(sg[bt] / sg.loc[pk:bt].max() - 1) * 100:.2f}%")
    yr = pd.concat([px.iloc[[0]], px.resample("YE").last()]).pct_change().dropna() * 100
    print("  해마다 오른 폭(%) 주식 / 은 / 금:")
    for i, v in yr.iterrows():
        print(f"    {i.year}  {v[P['eq']]:7.1f}  {v[P['ag']]:7.1f}  {v[P['au']]:7.1f}")
    print()
print("끝.")
