"""
15分钟高频确认策略 — 不等人跌，只等人买
核心：只要4层确认信号出现就入场（不论之前有没有大跌）
目标是：一天3-5笔，胜率>60%，平均单笔>0.3%
"""

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

df = pd.read_parquet("/root/quant_pipeline/data/btc_15m_binance.parquet")
df['time'] = pd.to_datetime(df['ts'])
df = df.set_index('time').sort_index()

o = df['open'].values.astype(float)
h = df['high'].values.astype(float)
l = df['low'].values.astype(float)
c = df['close'].values.astype(float)
v = df['volume'].values.astype(float)
N = len(df)

def roll_max(arr, n): return pd.Series(arr).rolling(n).max().values
def roll_min(arr, n): return pd.Series(arr).rolling(n).min().values
def roll_mean(arr, n): return pd.Series(arr).rolling(n).mean().values
def ewm(arr, span): return pd.Series(arr).ewm(span=span, adjust=False).mean().values

atr14 = roll_mean(np.maximum(h - l, np.maximum(np.abs(h - np.roll(c, 1)), np.abs(l - np.roll(c, 1)))), 14)
vol_ma20 = roll_mean(v, 20)
ema9 = ewm(c, 9)
ema21 = ewm(c, 21)
ema50 = ewm(c, 50)

print(f"📊 数据: {N:,}根15分K线 ≈ {N//96}个交易日")
print("="*100)

# ── 在每根K线上检测信号（不是等大跌，是只要有确认就做） ──
def detect_multiconfirm_now(idx):
    """在当前K线检测多层确认信号（不看大跌条件）"""
    if idx < 5 or idx >= N - 5:
        return None
    
    confirms = 0
    details = []
    
    # 1. 阳线
    if c[idx] > o[idx]:
        confirms += 1
        details.append("bull")
    
    # 2. 收盘 > ema9
    if c[idx] > ema9[idx]:
        confirms += 1
        details.append("ema9")
    
    # 3. 收盘突破前一根高点
    if c[idx] > h[idx-1]:
        confirms += 1
        details.append("break")
    
    # 4. 成交量配合（不低于均量）
    if v[idx] >= vol_ma20[idx] * 0.9:
        confirms += 1
        details.append("vol")
    
    # 5. 阳线实体 >= 前一根的50%
    if c[idx] > o[idx]:
        body = c[idx] - o[idx]
        prev_body = abs(c[idx-1] - o[idx-1])
        if body >= prev_body * 0.4:
            confirms += 1
            details.append("body")
    
    # 6. 低点上移
    if l[idx] > l[idx-1] * 0.999:
        confirms += 1
        details.append("lowup")
    
    # 7. 收盘 > ema21（中长趋势确认）
    if c[idx] > ema21[idx]:
        confirms += 1
        details.append("ema21")
    
    return confirms, details, idx


def backtest_frequent(name, min_confirms=4, 
                      trend_filter=None,
                      sl_mult=1.0, tp_mult=2.0, 
                      max_hold=12,
                      cooldown=2):  # 同方向最少间隔几根K线
    """
    高频确认策略
    每根K线都检查是否出现足够的确认信号，出现了就入场
    """
    trades = []
    last_long_idx = -cooldown
    
    for i in range(60, N - 5):
        # 检查信号
        result = detect_multiconfirm_now(i)
        if result is None:
            continue
        
        confirms, details, idx = result
        
        if confirms < min_confirms:
            continue
        
        # 趋势过滤
        if trend_filter == "uptrend" and ema21[i] < ema50[i]:
            continue
        if trend_filter == "downtrend" and ema21[i] > ema50[i]:
            continue
        
        # 冷却期（避免连续重复入场）
        if i - last_long_idx < cooldown:
            continue
        
        last_long_idx = i
        
        # 入场
        entry = c[i]
        cur_atr = atr14[i]
        sl_price = entry - cur_atr * sl_mult
        tp_price = entry + cur_atr * tp_mult
        
        # 额外用结构止损：最近5根最低点
        recent_low = np.min(l[max(0, i-5):i+1])
        sl_use = min(sl_price, recent_low * 0.997)
        
        hit_sl = hit_tp = False
        exit_price = entry
        exit_idx = i
        
        for j in range(i + 1, min(i + max_hold + 1, N)):
            dh, dl = h[j], l[j]
            if dh >= tp_price:
                exit_price = tp_price; hit_tp = True; exit_idx = j; break
            if dl <= sl_use:
                exit_price = sl_use; hit_sl = True; exit_idx = j; break
            if (j - i) >= max_hold:
                exit_price = c[j]; exit_idx = j; break
        
        net = round((exit_price / entry - 1) * 100 - 0.06, 2)
        
        trades.append({
            "time": df.index[i],
            "entry": entry,
            "exit": exit_price,
            "confirms": confirms,
            "details": "|".join(details),
            "bars": exit_idx - i,
            "hit_sl": hit_sl, "hit_tp": hit_tp,
            "net": net,
        })
    
    if not trades or len(trades) < 10:
        return {"name": name, "trades": 0}
    
    df_t = pd.DataFrame(trades)
    wins = sum(1 for t in trades if t['net'] > 0)
    total = len(trades)
    avg_ret = np.mean([t['net'] for t in trades])
    
    pnl = np.array([t['net'] for t in trades]) / 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(96 * 365)
    
    total_ret = sum(t['net'] for t in trades)
    
    # 日均交易数
    total_days = N / 96
    trades_per_day = total / total_days
    
    # 最大连续亏损
    losses = [1 for t in trades if t['net'] <= 0]
    max_consec = 0
    cur = 0
    for t in trades:
        if t['net'] <= 0:
            cur += 1
            max_consec = max(max_consec, cur)
        else:
            cur = 0
    
    return {
        "name": name, "trades": total, "wins": wins,
        "win_rate": wins/total*100, "avg_ret": avg_ret,
        "total_ret": total_ret, "cum": cum, "max_dd": max_dd,
        "best": max(t['net'] for t in trades),
        "worst": min(t['net'] for t in trades),
        "sharpe": round(sharpe, 2),
        "trades_per_day": round(trades_per_day, 1),
        "max_consec_losses": max_consec,
        "df": df_t,
    }


# ═══ 方案对比 ═══
scenarios = [
    # (名称, 最少确认层数, 趋势过滤, SL多倍, TP多倍, 持仓上限, 冷却期)
    ("A 4层确认", 4, None, 1.0, 2.0, 12, 2),
    ("B 4层+1H向上", 4, "uptrend", 1.0, 2.0, 12, 2),
    ("C 5层确认", 5, None, 1.0, 2.0, 12, 2),
    ("D 5层+1H向上", 5, "uptrend", 1.0, 2.0, 12, 2),
    ("E 4层+1H向上+宽盈", 4, "uptrend", 1.2, 3.0, 16, 2),
    ("F 3层确认", 3, None, 1.0, 2.0, 12, 1),
    ("G 3层+1H向上", 3, "uptrend", 1.0, 2.0, 12, 1),
    ("H 4层+窄损窄盈", 4, "uptrend", 0.8, 1.5, 8, 2),
    ("I 4层+短持", 4, "uptrend", 1.0, 1.5, 6, 2),
    ("J 4层+1H向上+超短", 4, "uptrend", 1.0, 2.0, 6, 2),
]

results = []
for name, conf, trend, sl, tp, hold, cool in scenarios:
    r = backtest_frequent(name, conf, trend, sl, tp, hold, cool)
    results.append(r)
    if r["trades"] > 0:
        flag = "✅" if r["sharpe"] > 2.0 else ("⚠️" if r["sharpe"] > 0 else "❌")
        print(f"  {flag} {name}: {r['trades']}笔 {r['trades_per_day']:.1f}笔/天 {r['win_rate']:.0f}%胜率 平均{r['avg_ret']:+.2f}% Sharpe={r['sharpe']:.1f} 复利{r['cum']:.2f}x")
    else:
        print(f"  ⏭️ {name}: 0笔")

# ── 主表 ──
print(f"\n{'='*120}")
print(f"{'📊 高频确认策略对比':^120}")
print(f"{'='*120}")
print(f"{'方案':<22} {'笔数':>6} {'笔/天':>7} {'胜率':>7} {'平均%':>8} {'合计%':>9} {'复利':>10} {'回撤':>8} {'最好':>7} {'最差':>7} {'Sharpe':>7}")
print(f"{'-'*120}")

for r in results:
    if r.get("trades", 0) > 0:
        wc = "🟢" if r["sharpe"] > 2.0 else ("🟡" if r["sharpe"] > 1.0 else "🔴")
        print(f"{wc} {r['name']:<20} {r['trades']:>6d} {r['trades_per_day']:>6.1f} {r['win_rate']:>5.0f}% {r['avg_ret']:>+7.2f}% {r['total_ret']:>+8.0f}% {r['cum']:>8.2f}x {r['max_dd']:>+7.1f}% {r['best']:>+6.2f}% {r['worst']:>+6.2f}% {r['sharpe']:>6.1f}")

# ── 最佳方案深度 ═══
print(f"\n{'='*120}")
print(f"🏆 最佳方案分析")
print(f"{'='*120}")

best = max(results, key=lambda x: x.get("sharpe", 0) if x.get("trades", 0) > 50 else 0)

if best and best["trades"] > 0:
    print(f"\n🏆 {best['name']}")
    print(f"   总交易: {best['trades']}笔 ({best['trades_per_day']:.1f}笔/天)")
    print(f"   胜率: {best['wins']}/{best['trades']} = {best['win_rate']:.1f}%")
    print(f"   平均: {best['avg_ret']:+.2f}%  合计: {best['total_ret']:+.0f}%")
    print(f"   年复利: {best['cum']:.2f}x  回撤: {best['max_dd']:.1f}%")
    print(f"   Sharpe: {best['sharpe']}  最好: {best['best']:+.2f}%  最差: {best['worst']:+.2f}%")
    print(f"   最大连续亏损: {best['max_consec_losses']}笔")
    
    df_best = best['df']
    
    # 按天统计
    df_best['day'] = df_best['time'].dt.strftime('%Y-%m-%d')
    daily = df_best.groupby('day').agg(
        笔数=('net', 'count'),
        日收益=('net', 'sum'),
        胜率=('net', lambda x: (x > 0).mean() * 100)
    )
    
    print(f"\n   📅 日报表（前30天）:")
    print(f"   {'日期':<12} {'笔数':>4} {'日收益':>8} {'胜率':>6}")
    print(f"   {'-'*33}")
    
    for d, row in daily.head(30).iterrows():
        tag = "🟢" if row['日收益'] > 0 else "🔴"
        print(f"   {d:<12} {row['笔数']:>4.0f} {tag} {row['日收益']:>+6.2f}% {row['胜率']:>5.0f}%")
    
    # 统计：日盈利天数比例
    profit_days = (daily['日收益'] > 0).sum()
    total_days = len(daily)
    print(f"\n   日盈利比例: {profit_days}/{total_days} = {profit_days/total_days*100:.0f}%")
    
    # 平均每日收益
    avg_daily = daily['日收益'].mean()
    print(f"   平均日收益: {avg_daily:+.2f}%")
    
    # 如果100U起步
    initial = 100
    daily_cum = (1 + daily['日收益'].values / 100).cumprod()
    final = daily_cum[-1] * initial
    print(f"\n   模拟: $100起步 → ${final:.0f}")
    
    # 展示逐笔
    print(f"\n   📋 前20笔:")
    print(f"   {'时间':<16} {'入场':>8} {'出场':>8} {'确认':>4} {'收益':>7}")
    print(f"   {'-'*48}")
    for _, t in df_best.head(20).iterrows():
        tag = "🟢" if t['net'] > 0 else "🔴"
        print(f"   {t['time'].strftime('%m-%d %H:%M'):<16} {t['entry']:>8.0f} {t['exit']:>8.0f} {t['confirms']:>4}层 {tag} {t['net']:>+5.2f}%")

# ── 结论 ──
print(f"\n{'='*120}")
print(f"💡 结论")
print(f"{'='*120}")
print(f"""
日复利能不能做到？

从数据看：
  {best['name'] if best else '最佳方案'}: {best['trades_per_day']:.1f}笔/天, 平均{best['avg_ret']:+.2f}%/笔
  → 平均日收益: {best.get('avg_ret', 0)*max(1, best.get('trades_per_day',0)):.2f}%
  → 年复利: {best.get('cum', 0):.2f}x
  → 日盈利天数占比: 在日报统计中看

{"+" * 60}
关于日复利的现实：
  一天1笔赚0.3% → 日复利0.3% → 年化(1.003)^365 = 2.98x
  一天3笔赚0.3% → 日复利0.9% → 年化(1.009)^365 = 26x
  
所以想要接近\"日复利\":
  单笔收益不是关键，关键是每天稳定地做出正期望的交易
  
目前的策略 {best['win_rate']:.0f}%胜率, {best['trades_per_day']:.1f}笔/天
  → 已经接近\"每周稳定盈利\"的水平
  → 但离\"每天必赚\"还有差距（因为61%胜率意味着每3笔亏1笔）
  
如果要真正\"日复利\"：
  要么把胜率提到70%+（需要更严格筛选信号）
  要么把单笔收益提到1%+（需要更好的出场管理）
  要么做高频（秒级）→ 需要订单簿数据
{"+" * 60}
""")
