"""
📈 Grace Momentum v2 — C入场+G出场合并版
=====================================
入场（来自C）：RSI>75 + 远高于MA50(>8%) + 放量(>1.5x)，三条件必须全满足
出场（来自G）：固定止盈(4xATR) + 固定止损(1.5xATR) + 强制出场(RSI<40)，无尾随
"""

import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')

d = pd.read_parquet("/root/quant_pipeline/data/btc_daily.parquet")
o = d['o'].values.astype(float)
h = d['h'].values.astype(float)
l = d['l'].values.astype(float)
c = d['c'].values.astype(float)
v = d['v'].values.astype(float)
N = len(d)

print(f"📊 数据: {N}根日线  {d.index[0].date()} ~ {d.index[-1].date()}")

def roll_mean(arr, n): return pd.Series(arr).rolling(n).mean().values
def rsi(arr, period=14):
    delta = pd.Series(arr).diff()
    gain = delta.clip(lower=0).rolling(period).mean()
    loss = (-delta.clip(upper=0)).rolling(period).mean()
    return (100 - 100/(1+gain/(loss+1e-9))).values

ma20 = roll_mean(c, 20)
ma50 = roll_mean(c, 50)
dist_ma50 = (c / ma50 - 1) * 100
rsi14 = rsi(c, 14)
vol_ma20 = roll_mean(v, 20)
vol_ratio = v / np.maximum(vol_ma20, 0.01)
tr = np.maximum(h - l, np.maximum(np.abs(h - np.roll(c, 1)), np.abs(l - np.roll(c, 1))))
atr14 = roll_mean(tr, 14)


def backtest(name, leverage=3,
             entry_rsi=75, entry_dist=8.0, entry_vol=1.5,
             sl_atr=2.0, tp_atr=4.0, max_hold=30,
             use_trailing=False, force_exit_rsi=40,
             require_all=True):  # True=全部满足, False=三选二
    trades = []
    
    for i in range(100, N - 5):
        cond = 0
        if rsi14[i] > entry_rsi: cond += 1
        if dist_ma50[i] > entry_dist: cond += 1
        if vol_ratio[i] > entry_vol: cond += 1
        
        if require_all and cond < 3:
            continue
        if not require_all and cond < 2:
            continue
        
        entry_price = c[i]
        current_atr = atr14[i]
        
        sl_price = entry_price - current_atr * sl_atr
        tp_price = entry_price + current_atr * tp_atr
        
        highest = entry_price
        hit_sl = hit_tp = force_exit = False
        exit_price = entry_price
        exit_idx = i
        
        for j in range(i + 1, min(i + max_hold + 1, N)):
            dh, dl, dc = h[j], l[j], c[j]
            highest = max(highest, dh)
            
            if use_trailing:
                trail_sl = highest - current_atr * sl_atr
                sl_price = max(sl_price, trail_sl)
            
            # 强制出场
            if rsi14[j] < force_exit_rsi and j > i + 3:
                exit_price = dc; force_exit = True; exit_idx = j; break
            
            if dh >= tp_price:
                exit_price = tp_price; hit_tp = True; exit_idx = j; break
            if dl <= sl_price:
                exit_price = sl_price; hit_sl = True; exit_idx = j; break
            if (j - i) >= max_hold:
                exit_price = dc; exit_idx = j; break
        
        else:
            exit_price = c[min(i + max_hold, N - 1)]
            exit_idx = min(i + max_hold, N - 1)
        
        ret = (exit_price / entry_price - 1) * leverage
        fee = (entry_price + exit_price) / entry_price * 0.0005 * leverage
        net = round((ret - fee) * 100, 2)
        
        trades.append({
            "entry": d.index[i].date(),
            "exit": d.index[exit_idx].date(),
            "entry_px": entry_price,
            "exit_px": exit_price,
            "rsi": rsi14[i],
            "dist_ma50": dist_ma50[i],
            "bars": exit_idx - i,
            "hit_sl": hit_sl, "hit_tp": hit_tp, "force_exit": force_exit,
            "net": net,
        })
    
    if not trades:
        return {"name": name, "trades": 0}
    
    df = pd.DataFrame(trades)
    wins = (df["net"] > 0).sum()
    total = len(df)
    avg_ret = df["net"].mean()
    pnl = df["net"].values / 100
    cum = (1 + pnl).prod()
    
    eq = [1.0]
    for r in pnl:
        eq.append(eq[-1] * (1 + r))
    peak = np.maximum.accumulate(eq)
    dd = (np.array(eq) - peak) / peak * 100
    max_dd = np.min(dd)
    
    avg_pnl = np.mean(pnl)
    std_pnl = np.std(pnl) if np.std(pnl) > 0 else 1
    sharpe = avg_pnl / std_pnl * np.sqrt(252)
    median_ret = np.median(pnl) * 100
    
    df["year"] = df["entry"].astype(str).str[:4]
    yearly = {}
    for yr, grp in df.groupby("year"):
        yw = (grp["net"] > 0).sum()
        yt = len(grp)
        y_avg = grp["net"].mean()
        y_cum = (1 + grp["net"].values / 100).prod()
        yearly[yr] = {"trades": yt, "wins": yw, "avg": y_avg, "cum": y_cum}
    
    return {
        "name": name, "trades": total, "wins": wins,
        "win_rate": wins/total*100, "avg_ret": avg_ret,
        "median_ret": median_ret, "cum": cum, "max_dd": max_dd,
        "best": df["net"].max(), "worst": df["net"].min(),
        "sharpe": round(sharpe, 2),
        "avg_bars": df["bars"].mean(),
        "sl_hit": df["hit_sl"].sum(), "tp_hit": df["hit_tp"].sum(),
        "force_exit_count": df["force_exit"].sum(),
        "yearly": yearly, "df": df,
    }


# ═══ 方案 ── 围绕C的入场 × G的出场 ═══
scenarios = [
    # (名称, 杠杆, RSI, dist%, vol, SL_ATR, TP_ATR, 持仓, 尾随, 强出RSI, 全满足?)
    ("A C原版(对照)", 3, 75, 8.0, 1.5, 1.5, 3.0, 20, True, 45, True),
    ("B G原版(对照)", 3, 65, 5.0, 1.3, 1.5, 4.0, 30, False, 40, False),
    
    # 合并方案：C的入场 + G的出场
    ("C1 合并基础", 3, 75, 8.0, 1.5, 1.5, 4.0, 30, False, 40, True),
    ("C2 宽松入场", 3, 70, 5.0, 1.3, 1.5, 4.0, 30, False, 40, True),
    ("C3 宽止损", 3, 75, 8.0, 1.5, 2.0, 5.0, 30, False, 40, True),
    ("C4 高杠杆", 5, 75, 8.0, 1.5, 2.0, 5.0, 25, False, 45, True),
    ("C5 三选二", 3, 75, 8.0, 1.5, 1.5, 4.0, 30, False, 40, False),
    ("C6 三选二宽松", 3, 70, 5.0, 1.3, 1.5, 4.0, 30, False, 40, False),
    ("C7 止盈放大", 3, 75, 8.0, 1.5, 2.0, 6.0, 40, False, 35, True),
]

results = []
for name, lev, rsi_val, dist_val, vol_val, sl_a, tp_a, hold, trail, force_rsi, req_all in scenarios:
    r = backtest(name, lev, rsi_val, dist_val, vol_val, sl_a, tp_a, hold, trail, force_rsi, req_all)
    results.append(r)

print(f"\n{'='*115}")
print(f"{'📊 合并方案对比':^115}")
print(f"{'='*115}")
print(f"{'方案':<20} {'笔数':>5} {'胜率':>7} {'平均%':>8} {'中位%':>8} {'复利':>10} {'回撤':>10} {'最好':>8} {'最差':>8} {'Sharpe':>7} {'SL':>3} {'TP':>3}")
print(f"{'-'*115}")

for r in results:
    wc = "🟢" if r["sharpe"] > 2.0 else ("🟡" if r["sharpe"] > 1.0 else "🔴")
    print(f"{wc} {r['name']:<18} {r['trades']:>5d} {r['win_rate']:>5.0f}% {r['avg_ret']:>+7.2f}% {r['median_ret']:>+7.2f}% {r['cum']:>8.2f}x {r['max_dd']:>+8.1f}% {r['best']:>+7.2f}% {r['worst']:>+7.2f}% {r['sharpe']:>6.1f} {r['sl_hit']:>3} {r['tp_hit']:>3}")

# ── 最佳方案深度 ──
best = max(results, key=lambda x: x["cum"] if x["cum"] < 1000 else 0)  # 排除异常值
print(f"\n{'='*115}")
print(f"🏆 推荐方案: {best['name']}")
print(f"{'='*115}")
print(f"   总交易: {best['trades']}笔 ({best['trades']/7.5:.0f}笔/年)")
print(f"   胜率: {best['wins']}/{best['trades']} = {best['win_rate']:.1f}%")
print(f"   平均收益: {best['avg_ret']:+.2f}%  中位数: {best['median_ret']:+.2f}%")
print(f"   复利净值: {best['cum']:.2f}x")
print(f"   最大回撤: {best['max_dd']:.1f}%")
print(f"   Sharpe: {best['sharpe']}")
print(f"   最好: {best['best']:+.2f}%  最差: {best['worst']:+.2f}%")
print(f"   持仓: {best['avg_bars']:.0f}天")
print(f"   SL触发: {best['sl_hit']}次 | TP触发: {best['tp_hit']}次 | 强出: {best['force_exit_count']}次")

print(f"\n   📅 年度:")
print(f"   {'年份':<6} {'笔数':>4} {'胜率':>5} {'平均收益':>10} {'年复利':>10}")
print(f"   {'-'*37}")
for yr in sorted(best['yearly'].keys()):
    yd = best['yearly'][yr]
    print(f"   {yr:<6} {yd['trades']:>4d} {yd['wins']/yd['trades']*100:>4.0f}% {yd['avg']:>+9.2f}% {yd['cum']:>8.2f}x")

print(f"\n   📋 逐笔:")
df_best = best['df']
print(f"   {'入场':<12} {'出场':<12} {'入场价':>8} {'RSI':>4} {'持仓':>3} {'收益':>8} {'原因':>4}")
print(f"   {'-'*55}")
for _, t in df_best.iterrows():
    tag = "🟢" if t["net"] > 0 else "🔴"
    reason = "SL" if t["hit_sl"] else ("TP" if t["hit_tp"] else "EX")
    print(f"   {str(t['entry']):<12} {str(t['exit']):<12} {t['entry_px']:>8.0f} {t['rsi']:>4.0f} {t['bars']:>3d}d {tag} {t['net']:>+7.2f}% {reason:>4}")
