# -*- coding: utf-8 -*-
"""대형주가 크게 빠진 날 — 다음 날 바로 살까, 반등을 보고 살까 (AlgoLab · 2026-10-24)

돌리는 법: 이 파일 옆에 data_bigdrop/ 폴더를 두고  python run_bigdrop.py
표준 라이브러리만 씁니다. 가격 자료는 2026-10-23 까지 얼려 둔 야후 일봉 수정종가(날짜·종가)입니다.

빠진 날  : 종가가 직전 20거래일 최고 종가보다 20% 이상 낮아진 첫 날 t
           (같은 종목은 60거래일이 지나고, 다시 20% 안쪽으로 회복했다가 빠질 때만 다시 센다)
바로     : t 다음 거래일 종가에 산다
반등 보고: t 이후 가장 낮았던 종가에서 10% 오른 종가가 나온 날의 다음 거래일 종가에 산다
           (끝 날까지 안 나오면 현금으로 둔다)
평가     : 둘 다 '바로 산 날 + 250거래일'(약 1년) 종가로 같은 날 평가 · 사는 비용 편도 0.1% · 현금 이자 0
국내 종목은 하루 ±31% 넘는 등락을 액면분할 오류로 보고 0 으로 지운다.
"""
import csv
import math
import os

HERE = os.path.dirname(os.path.abspath(__file__))
D = os.path.join(HERE, "data_bigdrop")
H, GAP, COST, BUY0 = 250, 60, 0.001, "2005-01-01"
US = "XOM GE MSFT C BAC WMT JNJ PFE AIG MO PG JPM IBM INTC CVX WFC KO CSCO GOOG VZ AMGN PEP HD COP HPQ UPS T ORCL QCOM MRK".split()


def load(path, clean):
    d, c = [], []
    for r in csv.reader(open(path, encoding="utf-8")):
        if r and r[0][:1].isdigit() and float(r[1]) > 0:
            d.append(r[0]); c.append(float(r[1]))
    if clean:
        x = [c[0]]
        for i in range(1, len(c)):
            g = c[i] / c[i - 1] - 1
            x.append(x[-1] * (1 + (0.0 if abs(g) > 0.31 else g)))
        c = x
    return d, c


def events(d, c, X):
    out, armed, last = [], True, -10**9
    for i in range(20, len(c)):
        hi = max(c[i - 20:i])
        if d[i] >= BUY0 and armed and i - last >= GAP and c[i] <= (1 - X) * hi:
            out.append(i); last = i; armed = False
        elif not armed and c[i] > (1 - X) * hi:
            armed = True
    return out


def knee(c, t, end, Y):
    lo = c[t]
    for s in range(t, end):
        lo = min(lo, c[s])
        if c[s] >= (1 + Y) * lo:
            return s
    return None


def ma_day(c, t, end):
    for s in range(max(t, 20), end):
        if c[s] > sum(c[s - 19:s + 1]) / 20:
            return s
    return None


def ret(c, buy, end):
    if buy is None or buy >= end:
        return 1.0
    return c[end] / c[buy] * (1 - COST)


def run(sub, clean, names=None, X=0.20, Y=0.10):
    names = names or sorted(f[:-4] for f in os.listdir(os.path.join(D, sub)) if f.endswith(".csv"))
    ev = []
    for nm in names:
        d, c = load(os.path.join(D, sub, nm + ".csv"), clean)
        for t in events(d, c, X):
            end = t + 1 + H
            if end < len(c):
                s = knee(c, t, end, Y)
                wb = None if s is None else s + 1
                m = ma_day(c, t, end)
                ev.append(dict(nm=nm, day=d[t], kd=d[t + 1], wd=None if wb is None or wb >= end else d[wb],
                               wait=None if s is None else s - t, end=d[end],
                               K=ret(c, t + 1, end), W=ret(c, wb, end), M=ret(c, None if m is None else m + 1, end)))
    return ev


def won(ev):
    return sum(e["K"] > e["W"] for e in ev)


def gmean(xs):
    return math.exp(sum(math.log(x) for x in xs) / len(xs))


def median(xs):
    xs = sorted(xs); n = len(xs)
    return xs[n // 2] if n % 2 else (xs[n // 2 - 1] + xs[n // 2]) / 2


def report(title, ev):
    n, k = len(ev), won(ev)
    print(f"== {title}")
    print(f"  빠진 날 {n}번 · 1년 뒤 바로 산 쪽이 더 남은 것 {k}번 · 반등을 보고 산 쪽 {n - k}번")
    print(f"  반등을 보고 산 값이 바로 산 값보다 비쌌던 것 {k}번(같은 날 평가라 위와 같은 수)")
    print(f"  반등을 기다린 기간 가운데 값 {median([e['wait'] for e in ev if e['wait'] is not None]):.0f}거래일")
    print(f"  1,000만원 기준 1년 뒤 평균(기하): 바로 {1000 * gmean([e['K'] for e in ev]):,.0f}만원 · 반등 보고 {1000 * gmean([e['W'] for e in ev]):,.0f}만원")
    print(f"  1,000만원 기준 1년 뒤 평균(단순): 바로 {1000 * sum(e['K'] for e in ev) / n:,.0f}만원 · 반등 보고 {1000 * sum(e['W'] for e in ev) / n:,.0f}만원")
    print(f"  바로 산 쪽도 1년 뒤 산 값보다 아래였던 것 {sum(e['K'] < 1 for e in ev)}번")
    per, mon, yr = {}, {}, {}
    for e in ev:
        g = math.log(e["K"]) - math.log(e["W"])
        per.setdefault(e["nm"], []).append(g); mon.setdefault(e["day"][:7], []).append(g); yr.setdefault(e["day"][:4], []).append(g)
    f = lambda m: sum(sum(v) > 0 for v in m.values())
    print(f"  종목별 평균으로 바로가 앞선 종목 {f(per)}/{len(per)} · 달로 묶으면 {f(mon)}/{len(mon)}달 · 해로 묶으면 {f(yr)}/{len(yr)}해")
    print(f"  반등을 본 쪽이 평균으로 앞선 해: {' · '.join(y for y, v in sorted(yr.items()) if sum(v) < 0)}")
    e20 = [e for e in ev if e["day"][:4] == "2020"]
    print(f"  2020년 빠진 날 {len(e20)}번 · 반등을 보고 산 쪽이 더 남은 것 {sum(e['W'] > e['K'] for e in e20)}번")
    lk = sum(math.log(e['K']) for e in ev) / n; lw = sum(math.log(e['W']) for e in ev) / n; lm = sum(math.log(e['M']) for e in ev) / n
    print(f"  20일선 위로 올라선 걸 보고 사기(평균 로그) {lm:+.4f} — 바로 {lk:+.4f} · 반등 보고 {lw:+.4f}")


if __name__ == "__main__":
    print("대형주가 20일 최고 종가보다 20% 넘게 빠진 날 — 바로 살까, 반등(바닥 +10%)을 보고 살까 · 1년 뒤 같은 날 평가\n")
    main = run("u2007", True)
    report("국내 · 2007년 말 시가총액 상위 40종 (메인)", main)
    dev = run("kr36", True)
    report("국내 · 대형주 36종 (개발 표본)", dev)
    us = run("us30", False, US)
    report("미국 · 대형주 30종", us)
    print("\n[기준선 — 빠진 날이 아니라 아무 달 첫 거래일에, 다음 날 산 것과 11거래일 늦게 산 것]")
    for title, sub in (("국내 2007 상위 40", "u2007"), ("국내 36", "kr36")):
        n = k = 0
        for nm in sorted(f[:-4] for f in os.listdir(os.path.join(D, sub)) if f.endswith(".csv")):
            d, c = load(os.path.join(D, sub, nm + ".csv"), True)
            seen = set()
            for t in range(20, len(c)):
                if d[t] >= BUY0 and d[t][:7] not in seen:
                    seen.add(d[t][:7])
                    if t + 1 + H < len(c):
                        n += 1
                        k += c[t + 1] < c[t + 12]
        print(f"  {title}: {n:,}번 중 {k:,}번 늦게 산 값이 더 비쌌다")
    for title, ev in (("국내 2007 상위 40", main), ("국내 36", dev)):
        e8 = [e for e in ev if e["day"] >= "2008-01-01"]
        print(f"  {title} · 2008년 이후 빠진 날만: {len(e8)}번 중 {won(e8)}번 바로가 더 남음")
    print("\n[삼성전자 005930 — 빠진 날 전부 · 1,000만원 기준 1년 뒤]")
    for e in main:
        if e["nm"] == "005930":
            print(f"  {e['day']} → 바로 {e['kd']} 매수 {1000 * e['K']:,.0f}만원 · 반등 보고 {e['wd']} 매수 {1000 * e['W']:,.0f}만원 · 평가일 {e['end']}")
    print("\n[하락 × 반등 아홉 가지 조합 — 평균 로그로 바로가 앞선 조합 수]")
    for title, sub, clean, nm in (("국내 2007 상위 40", "u2007", True, None), ("국내 36", "kr36", True, None), ("미국 30", "us30", False, US)):
        w = wa = 0
        for X in (0.15, 0.20, 0.30):
            for Y in (0.05, 0.10, 0.20):
                ev = run(sub, clean, nm, X, Y)
                w += sum(math.log(e["K"]) for e in ev) > sum(math.log(e["W"]) for e in ev)
                wa += sum(e["K"] for e in ev) > sum(e["W"] for e in ev)
        print(f"  {title}: 9개 중 {w}개 (단순 평균으로는 9개 중 {wa}개)")
    print("\n끝.")
