# -*- coding: utf-8 -*-
"""코인 자동매매 설정값 4개 — 영상의 모든 숫자를 이 파일 하나로 재현한다.

    python run_coin.py

설정 (영상에서 공개한 값 그대로)
    봉        4시간
    진입      20봉 이동평균이 200봉 이동평균을 위로 통과
    청산      20봉 이동평균이 200봉 이동평균을 아래로 통과
    필터      일봉 50일 이동평균 위에 있을 때만 진입
    투입 비중  0.6 (계좌의 60퍼센트)
    거래비용   편도 0.1퍼센트

정직 규칙
  · 업비트 원화와 바이낸스 USDT 현물을 교집합 타임스탬프로 맞춘 뒤 같은 창에서 잰다.
    (창이 다르면 "어느 시장이 좋다"가 아니라 "어느 기간이 좋았다"를 재게 된다)
  · 합격 잣대 = min(두 시장) 연 30퍼센트 이상 AND 두 시장 최대낙폭 -35퍼센트 이내.
  · 워밍업 1300봉을 버린 뒤부터 잰다 — 모든 코인이 같은 규칙으로 시작한다.
  · data_asof/ 는 2026-09-10 에 얼린 시세다. 영상과 같은 숫자가 나오게 하려고 고정했다.
"""
import datetime
import os
import sys

import numpy as np

sys.stdout.reconfigure(encoding="utf-8", errors="replace")
HERE = os.path.dirname(os.path.abspath(__file__))
DATA = os.path.join(HERE, "data_asof")
ASOF = "2026-09-10"

FAST, SLOW, FILT, W = 20, 200, 50, 0.6
COST = 0.001
WARM = 1300
BPD = 6                      # 4시간봉 = 하루 6개
VAL = "2024-01-01"
COINS = [
    ("비트코인", "KRW-BTC", "BTCUSDT"), ("이더리움", "KRW-ETH", "ETHUSDT"),
    ("리플", "KRW-XRP", "XRPUSDT"), ("에이다", "KRW-ADA", "ADAUSDT"),
    ("도지코인", "KRW-DOGE", "DOGEUSDT"), ("솔라나", "KRW-SOL", "SOLUSDT"),
    ("아발란체", "KRW-AVAX", "AVAXUSDT"), ("체인링크", "KRW-LINK", "LINKUSDT"),
    ("트론", "KRW-TRX", "TRXUSDT"), ("폴카닷", "KRW-DOT", "DOTUSDT"),
    ("비트코인캐시", "KRW-BCH", "BCHUSDT"), ("이더리움클래식", "KRW-ETC", "ETCUSDT"),
    ("코스모스", "KRW-ATOM", "ATOMUSDT"), ("스텔라", "KRW-XLM", "XLMUSDT"),
    ("비체인", "KRW-VET", "VETUSDT"), ("헤데라", "KRW-HBAR", "HBARUSDT"),
    ("알고랜드", "KRW-ALGO", "ALGOUSDT"), ("니어", "KRW-NEAR", "NEARUSDT"),
    ("샌드박스", "KRW-SAND", "SANDUSDT"), ("퀀텀", "KRW-QTUM", "QTUMUSDT"),
    ("질리카", "KRW-ZIL", "ZILUSDT"),
]


def read(path):
    d, c = [], []
    with open(path, encoding="utf-8") as f:
        next(f)
        for line in f:
            p = line.rstrip("\n").split(",")
            if len(p) < 2:
                continue
            d.append(p[0])
            c.append(float(p[1]))
    return d, np.asarray(c)


def sma(x, n):
    out = np.full(len(x), np.nan)
    if len(x) >= n:
        cs = np.cumsum(np.insert(x, 0, 0.0))
        out[n - 1:] = (cs[n:] - cs[:-n]) / n
    return out


def align(du, cu, db, cb):
    su = {t: i for i, t in enumerate(du)}
    sb = {t: i for i, t in enumerate(db)}
    keys = [t for t in db if t in su]
    if len(keys) < WARM + 500:
        return None
    iu = np.asarray([su[t] for t in keys])
    ib = np.asarray([sb[t] for t in keys])
    return keys, cu[iu], cb[ib]


def stats(g, ppy):
    eq = np.cumprod(g)
    yrs = len(g) / ppy
    cagr = eq[-1] ** (1 / yrs) - 1
    dd = eq / np.maximum.accumulate(eq) - 1
    return float(cagr), float(dd.min()), float(eq[-1])


def signal(c):
    f, s = sma(c, FAST), sma(c, SLOW)
    out = np.zeros(len(c))
    m = ~(np.isnan(f) | np.isnan(s))
    out[m] = (f[m] > s[m]).astype(float)
    ds = sma(c, FILT * BPD)
    fl = np.zeros(len(c))
    k = ~np.isnan(ds)
    fl[k] = (c[k] > ds[k]).astype(float)
    return out * fl * W


def grow(c, sig):
    ret = np.zeros(len(c))
    ret[1:] = c[1:] / c[:-1] - 1
    pos = np.r_[0.0, sig[:-1]]
    g = (1 + pos * ret) * (1 - COST * np.abs(np.diff(np.r_[0.0, pos])))
    return pos, g, ret


def const_w(ret, w):
    """같은 평균 노출로 그냥 나눠 담은 기준선 — 하루 한 번 리밸런싱, 같은 비용."""
    n = len(ret)
    g = np.empty(n)
    g[0] = 1.0
    for i in range(1, n):
        g[i] = 1 + w * ret[i]
        if i % BPD == 0:
            g[i] *= (1 - COST * 0.02)
    return g


def trades(pos, g):
    out, i, n = [], 0, len(pos)
    while i < n:
        if pos[i] > 0 and (i == 0 or pos[i - 1] == 0):
            j = i
            while j < n and pos[j] > 0:
                j += 1
            out.append(float(np.prod(g[i:min(j + 1, n)]) - 1))
            i = j
        else:
            i += 1
    return out


def won(x):
    return format(int(round(x * 1000)), ",")


print("=" * 92)
print("코인 자동매매 설정값 4개 — 재현 (기준일 %s · 거래비용 편도 %.1f퍼센트)" % (ASOF, COST * 100))
print("  4시간봉 · 이동평균 %d/%d · 일봉 %d일선 위에서만 · 투입 비중 %.1f" % (FAST, SLOW, FILT, W))
print("=" * 92)
print("%-12s %6s %4s %9s %10s %13s %13s %7s %8s %11s"
      % ("코인", "기간(년)", "합격", "연(업비트)", "연(바이낸스)", "1,000만원->봇",
         "1,000만원->보유", "낙폭봇", "낙폭보유", "거래(승률)"))

rows, npass = [], 0
for name, us, bs in COINS:
    pu = os.path.join(DATA, us + "_4h.csv")
    pb = os.path.join(DATA, bs + "_4h.csv")
    if not (os.path.exists(pu) and os.path.exists(pb)):
        print("  ! %s 데이터 없음" % name)
        continue
    du, cu = read(pu)
    db, cb = read(pb)
    a = align(du, cu, db, cb)
    if a is None:
        print("  ! %s 교집합 부족" % name)
        continue
    keys, xu, xb = a
    yrs = (datetime.date.fromisoformat(keys[-1][:10])
           - datetime.date.fromisoformat(keys[0][:10])).days / 365.25
    ppy = len(keys) / yrs
    r = {}
    for mkt, x in (("업비트", xu), ("바이낸스", xb)):
        pos, g, ret = grow(x, signal(x))
        c_, m_, mult = stats(g[WARM:], ppy)
        hc, hm, hmult = stats((1 + ret)[WARM:], ppy)
        vi = int(np.searchsorted(np.asarray(keys), VAL))
        vc = stats(g[vi:], ppy)[0] if vi < len(keys) - 60 else float("nan")
        tr = trades(pos[WARM:], g[WARM:])
        w = float(pos[WARM:].mean())
        bc = stats(const_w(ret[WARM:], w), ppy)[0]
        r[mkt] = dict(cagr=c_, mdd=m_, mult=mult, hold_mult=hmult, hold_mdd=hm,
                      val=vc, n=len(tr),
                      win=float(np.mean(np.asarray(tr) > 0)) if tr else 0.0,
                      base_cagr=bc, w=w)
    mc = min(r["업비트"]["cagr"], r["바이낸스"]["cagr"])
    wm = min(r["업비트"]["mdd"], r["바이낸스"]["mdd"])
    ok = bool(mc >= 0.30 and wm >= -0.35)
    npass += ok
    rows.append((name, r, ok, (len(keys) - WARM) / ppy, keys))
    u = r["업비트"]
    print("%-12s %8.1f %4s %9.1f%% %10.1f%% %11s만원 %11s만원 %7.1f%% %8.1f%% %6d(%.0f%%)"
          % (name, (len(keys) - WARM) / ppy, "합격" if ok else "-",
             u["cagr"] * 100, r["바이낸스"]["cagr"] * 100,
             won(u["mult"]), won(u["hold_mult"]),
             u["mdd"] * 100, u["hold_mdd"] * 100, u["n"], u["win"] * 100))

print("-" * 92)
print("코인 %d개 중 합격 %d개" % (len(rows), npass))
print()
print("[ 영상이 말한 숫자 ]")
for nm in ("이더리움", "비트코인", "솔라나"):
    name, r, ok, yy, keys = next(x for x in rows if x[0] == nm)
    u = r["업비트"]
    print("  %s  %s ~ %s (%.1f년)" % (nm, keys[WARM][:10], keys[-1][:10], yy))
    print("     1,000만원 -> 이 설정 %s만원 · 그냥 보유 %s만원" % (won(u["mult"]), won(u["hold_mult"])))
    print("     최대 낙폭   이 설정 %.1f퍼센트 · 그냥 보유 %.1f퍼센트" % (u["mdd"] * 100, u["hold_mdd"] * 100))
    print("     거래 %d회 · 이긴 거래 %.1f퍼센트" % (u["n"], u["win"] * 100))
    print("     2024년 이후만 잘라 보면 연 %.1f퍼센트 (전체 구간 연 %.1f퍼센트보다 낮다)"
          % (u["val"] * 100, u["cagr"] * 100))
    print("     같은 평균 노출(%.2f)로 그냥 나눠 담은 기준선 연 %.1f퍼센트"
          " — 남은 돈의 우위는 '덜 넣은 것'이 아니다" % (u["w"], u["base_cagr"] * 100))
    print("     (다만 이 기준선은 남은 돈만 답한다. 노출을 맞춘 계좌와의 낙폭 비교는 재보지 않았다)")

print()
print("[ 시작일을 바꾸면 — 재는 구간의 시작만 옮겨 다시 잰다(규칙·신호는 그대로) ]")
STARTS = ["2017-11-01", "2018-01-01", "2018-05-01", "2018-09-01", "2019-01-01", "2019-09-01",
          "2020-01-01", "2020-06-01", "2021-01-01", "2021-06-01", "2022-01-01", "2022-06-01"]
for nm in ("비트코인", "이더리움", "솔라나"):
    us, bs = next((u, b) for n, u, b in COINS if n == nm)
    du, cu = read(os.path.join(DATA, us + "_4h.csv"))
    db, cb = read(os.path.join(DATA, bs + "_4h.csv"))
    keys, xu, xb = align(du, cu, db, cb)
    k = np.asarray(keys)
    yrs = (datetime.date.fromisoformat(keys[-1][:10])
           - datetime.date.fromisoformat(keys[0][:10])).days / 365.25
    ppy = len(keys) / yrs
    gs = [grow(x, signal(x))[1] for x in (xu, xb)]
    hit = tried = 0
    for s in STARTS:
        i0 = max(int(np.searchsorted(k, s)), WARM)
        if i0 >= len(keys) - 500:
            continue
        tried += 1
        rr = [stats(g[i0:], ppy) for g in gs]
        if min(r[0] for r in rr) >= 0.30 and min(r[1] for r in rr) >= -0.35:
            hit += 1
    print("  %s  시작일 %d개 중 %d번 합격" % (nm, tried, hit))

print()
print("데이터: 업비트 원화 4시간봉 · 바이낸스 USDT 현물 4시간봉 (%s 에 얼림)" % ASOF)
print("체결은 봉 마감 종가, 호가 밀림은 넣지 않았다. 전부 현물 매수만이고 공매도는 없다.")
print("지금도 거래되는 코인만 봤다. 상장폐지된 코인은 이 표에 없다.")
print("과거 데이터 시뮬레이션이다. 미래 수익을 보장하지 않는다.")
