"""leverage_compare.py — bandingin 1x / 3x / 5x / dinamis(<=5x).

Patokan: leverage dipilih biar liquidation price SELALU di ATAS SL.
Kalau SL lebar (naik >20%), leverage dikurangi biar liq > SL.

Rumus liq (short, leverage L, maintenance mm):
  liq = entry * (1 + (1-mm)/L)
Cari L max (<=Lcap) s.t. liq > SL:
  L < (1-mm)/(SL/entry - 1)

Position size (notional) = equity * risk_pct * L / sl_dist
PnL per trade = R * (equity * risk_pct)   # R sudah vs sl_dist, jadi leverage
                                          # mengalikan exposure, bukan R
Actually: dgn leverage L, notional = equity*risk_pct*L/sl_dist.
PnL_usd = (entry-exit)/entry * notional = R * (equity*risk_pct*L)
Cost = notional * (fee*2+slip*2) ≈ equity*risk_pct*L*(fee*2+sl*2)

Liquidation check: kalau harga naik >= liq_price -> posisi liquidated
(loss = full notional * (1-mm) ≈ -equity*risk_pct*L, i.e. -L R_max)
"""
import sys, os, glob
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
import pandas as pd
import numpy as np
from config.settings_v2 import default_params, V2Params
from strategy.detector_v2 import detect_v2
from strategy.simulator_v2 import simulate_v2


def load_csv(path):
    df = pd.read_csv(path).sort_values("time").reset_index(drop=True)
    return [{"time": int(r.time), "open": r.open, "high": r.high,
             "low": r.low, "close": r.close, "volume": r.volume,
             "quote_volume": r.quote_volume, "taker_buy_quote": r.taker_buy_quote}
            for r in df.itertuples()]


def get_trades(files):
    out = []
    for f15p in files:
        sym = os.path.basename(f15p).replace("_15m.csv", "")
        d = os.path.dirname(f15p)
        f4p = os.path.join(d, f"{sym}_4h.csv")
        if not os.path.exists(f4p):
            continue
        k4 = load_csv(f4p)
        k15 = load_csv(f15p)
        p = default_params()
        p.pump_pct = 0.30
        for s in detect_v2(k4, k15, p):
            if s.get("squeeze_score", 0) >= 3:
                t = simulate_v2(sym, k15, s, p, equity=100.0)
                if t and t.r_multiple is not None:
                    out.append({
                        "symbol": sym, "entry_time": t.entry_time,
                        "score": s.get("squeeze_score", 0),
                        "r": t.r_multiple, "entry": t.entry_price,
                        "sl": t.sl, "exit_reason": t.exit_reason,
                        "exit_bar": t.exit_bar,
                    })
    return out


def leverage_for_sl(entry, sl, Lcap=5.0, mm=0.005):
    """Cari L max (<=Lcap) biar liq_price > SL (dengan margin 1%).
    Return (L, liq_price)."""
    sl_ratio = sl / entry - 1.0  # frac naik ke SL
    if sl_ratio <= 0:
        return Lcap, entry * (1 + (1 - mm) / Lcap)
    # L_max = (1-mm)/sl_ratio, tapi butuh liq > sl -> L < (1-mm)/sl_ratio
    # pakai 0.95 margin biar liq strictly di atas sl
    L_max = (1 - mm) / sl_ratio * 0.95
    L = min(L_max, Lcap)
    if L < 1.0:
        L = 1.0
    liq = entry * (1 + (1 - mm) / L)
    return L, liq


def simulate_mode(trades, mode, Lcap=5.0, mm=0.005):
    """mode: '1x','3x','5x','dyn'. Return (equity, max_dd, liq_count)."""
    equity = 100.0
    peak = 100.0
    max_dd = 0.0
    liq_count = 0
    for t in trades:
        risk_pct = 0.01 if t["score"] >= 4 else 0.005
        risk_usd = equity * risk_pct
        if mode == "1x":
            L = 1.0
        elif mode == "3x":
            L = 3.0
        elif mode == "5x":
            L = 5.0
        else:  # dyn
            L, _ = leverage_for_sl(t["entry"], t["sl"], Lcap, mm)
        # liquidation price
        liq = t["entry"] * (1 + (1 - mm) / L)
        # liquidation cuma kalau SL strictly di atas liq (harga naik tembus liq duluan)
        if t["sl"] > liq * 1.001:
            pnl = -risk_usd * L * (1 - mm)
            cost = risk_usd * L * (0.003)
            liq_count += 1
        else:
            pnl = t["r"] * risk_usd * L
            cost = risk_usd * L * (0.003)
        net = pnl - cost
        equity += net
        if equity > peak:
            peak = equity
        dd = (peak - equity) / peak
        if dd > max_dd:
            max_dd = dd
    return equity, max_dd, liq_count


def main():
    base = "data/real"
    groups = [
        ("2025-07", f"{base}/OOS/2025-07"),
        ("2025-08", f"{base}/OOS/2025-08"),
        ("2025-09", f"{base}/OOS/2025-09"),
        ("2025-10", f"{base}/OOS/2025-10"),
        ("2025-11", f"{base}/OOS/2025-11"),
        ("2025-12", f"{base}/OOS/2025-12"),
        ("2026-01", f"{base}/IS"),
        ("2026-03", f"{base}/OOS/2026-03"),
        ("2026-04", f"{base}/OOS/2026-04"),
        ("2026-05", f"{base}/OOS/2026-05"),
        ("2026-06", f"{base}/OOS/2026-06"),
    ]
    all_t = []
    for label, folder in groups:
        files = sorted(glob.glob(os.path.join(folder, "**", "*_15m.csv"), recursive=True))
        ts = get_trades(files)
        for t in ts:
            t["month"] = label
        all_t += ts
    all_t.sort(key=lambda x: x["entry_time"])
    print(f"Total trades: {len(all_t)}")

    print(f"\n{'Mode':8} {'Equity$':>10} {'Ret%':>7} {'MaxDD':>7} {'Liquid':>7}")
    for mode in ["1x", "3x", "5x", "dyn"]:
        eq, dd, liq = simulate_mode(all_t, mode)
        print(f"{mode:8} {eq:10.2f} {(eq/100-1)*100:6.1f}% {dd*100:6.1f}% {liq:7d}")


if __name__ == "__main__":
    main()
