"""SMC/ICT 策略量化回测
核心概念：流动性扫荡 + FVG + Order Block 三重共震"""
import pandas as pd, numpy as np

df_raw = pd.read_parquet("data/btc_multidim.parquet")
df_raw.columns = [c.lower() for c in df_raw.columns]

# ── 用原始数据（15min）做SMC，因为SMC需要看到流动性扫荡的细节 ──
# 但最终在1H级别开仓
d = df_raw.resample("1h").agg({"open":"first","high":"max","low":"min","close":"last","volume":"sum"}).dropna()

# 日线
dd = df_raw.resample("1D").agg({"close":"last"}).dropna()
dd["ma20"] = dd["close"].rolling(20).mean()

O,H,L,C,V = d["open"].values,d["high"].values,d["low"].values,d["close"].values,d["volume"].values
O1,H1,L1,C1 = np.roll(O,1),np.roll(H,1),np.roll(L,1),np.roll(C,1)
O2,H2,L2,C2 = np.roll(O,2),np.roll(H,2),np.roll(L,2),np.roll(C,2)
O3,H3,L3,C3 = np.roll(O,3),np.roll(H,3),np.roll(L,3),np.roll(C,3)
n = len(d)

split = int(n * 0.67)
SL, TP, MB = 0.015, 0.045, 48
FEE = 0.001

d_ts = d.index

# ══════════════════════════════════════════
# SMC 核心概念定义（1H级别）
# ══════════════════════════════════════════

# 1. 流动性扫荡 (Liquidity Sweep)
# 突破前20根高点/低点后迅速收回
lookback = 20
prev_h = pd.Series(H).shift(1).rolling(lookback).max().values
prev_l = pd.Series(L).shift(1).rolling(lookback).min().values

sweep_up = (H > prev_h) & (C < prev_h)  # 突破前高→收回来 = 扫止损
sweep_down = (L < prev_l) & (C > prev_l)  # 跌破前低→收回来

# 2. FVG - 公允价值缺口（三K线模型）
# 看涨FVG: K1的高 < K3的低 → 中间有未被填充的价格真空
fvg_bull = H2 < L  # 两根前的最高 < 当前最低
fvg_bear = L2 > H  # 两根前的最低 > 当前最高

# 3. Order Block (OB)
# 强趋势前最后一根反向K线
# 简单OB定义即可，去掉未用的numpy pct_change
bull_ob_simple = (C1 < O1) & (C > O) & (C > H1) & (C > O1 * 1.005)
bear_ob_simple = (C1 > O1) & (C < O) & (C < L1) & (C < O1 * 0.995)

# 4. 边际价格 (Premium/Discount)
# 用最近日线高低点作为公平价值区
# 价格 < 日线ma20 = discount(折扣区→找做多)
# 价格 > 日线ma20 = premium(溢价区→找做空)
daily_trend = np.array([
    dd["ma20"].reindex([ts], method="ffill").values[0] if ts >= dd.index[0] else C[0]
    for ts in d_ts
])
in_discount = C < daily_trend * 0.99  # 价格低于均线→折扣区
in_premium = C > daily_trend * 1.01  # 价格高于均线→溢价区

# ══════════════════════════════════════════
# SMC 策略1: 流动性扫荡+FVG
# 价格扫掉流动性→留下FVG→价格回补FVG时进场
# ══════════════════════════════════════════
sigs_sweep_fvg = []
for i in range(5, n-5):
    # 做多：先扫荡向下(跌破前低)，然后出现FVG，价格回到FVG
    if sweep_down[i]:
        # 找后面有没有FVG
        for offset in range(1, 4):
            if i+offset >= n: break
            if fvg_bull[i+offset]:
                sigs_sweep_fvg.append({"i": i+offset+1, "dir": 1, "entry": C[i+offset+1], "type": "扫荡+FVG", "entry_idx": i+offset+1})
                break
    # 做空
    if sweep_up[i]:
        for offset in range(1, 4):
            if i+offset >= n: break
            if fvg_bear[i+offset]:
                sigs_sweep_fvg.append({"i": i+offset+1, "dir": -1, "entry": C[i+offset+1], "type": "扫荡+FVG", "entry_idx": i+offset+1})
                break

# ══════════════════════════════════════════
# SMC 策略2: Order Block + 流动性
# 价格从OB启动→扫流动性→回OB→进场
# ══════════════════════════════════════════
sigs_ob = []
for i in range(5, n-5):
    # 多头OB + 之后有扫荡 + 回到OB附近
    if bull_ob_simple[i]:
        entry = C[i]
        sigs_ob.append({"i": i, "dir": 1, "entry": entry, "type": "OB", "entry_idx": i})
    if bear_ob_simple[i]:
        sigs_ob.append({"i": i, "dir": -1, "entry": C[i], "type": "OB", "entry_idx": i})

# ══════════════════════════════════════════
# SMC 策略3: 三重共震（最强的）
# OB + 扫荡 + FVG 同时出现
# ══════════════════════════════════════════
sigs_triple = []
for i in range(10, n-5):
    # 多头三重共震
    if sweep_down[i] and fvg_bull[i]:
        # 加点日线趋势确认
        sigs_triple.append({"i": i+1, "dir": 1, "entry": C[i+1], "type": "扫荡+FVG", "entry_idx": i+1})
    if sweep_up[i] and fvg_bear[i]:
        sigs_triple.append({"i": i+1, "dir": -1, "entry": C[i+1], "type": "扫荡+FVG", "entry_idx": i+1})

# ══════════════════════════════════════════
# 回测
# ══════════════════════════════════════════
def backtest(sigs, use_discount_filter=False):
    results = {"in": [], "out": []}
    stats = {"total": 0}
    for s in sigs:
        i = s["entry_idx"]
        if i+MB >= n: continue
        period = "in" if i < split else "out"
        entry = s["entry"]; dirc = s["dir"]
        
        # 折扣区过滤
        if use_discount_filter:
            if dirc == 1 and not in_discount[i]: continue
            if dirc == -1 and not in_premium[i]: continue
        
        stats["total"] += 1
        closed = False
        for j in range(1, MB+1):
            if i+j >= n: break
            ret = (C[i+j]/entry-1)*dirc
            if ret >= TP: results[period].append(TP-FEE); closed=True; break
            if ret <= -SL: results[period].append(-SL-FEE); closed=True; break
        if not closed:
            fr = (C[min(i+MB,n-1)]/entry-1)*dirc
            results[period].append(fr-FEE)
    return results, stats

def ps(results, stats, label):
    print(f"\n▶ {label}")
    print(f"  [统计] 共{stats['total']}笔")
    for nm,key in [("样本内","in"),("样本外","out")]:
        tr=results[key]
        if len(tr)<5: print(f"  {nm}: 仅{len(tr)}笔"); continue
        wr=sum(1 for r in tr if r>0)/len(tr)
        cum=np.prod([1+r for r in tr])
        ch=[tr[i:i+5] for i in range(0,len(tr),5)]
        pw=sum(1 for c in ch if sum(c)>0)/len(ch) if ch else 0
        avg=np.mean(tr)*100
        running=1.0;peak=1.0;mdd=0
        for r in tr:
            running*=(1+r);peak=max(peak,running)
            mdd=min(mdd,(running-peak)/peak)
        sp=np.mean(tr)/(np.std(tr)+1e-9)*np.sqrt(365*24/MB) if np.std(tr)>0 else 0
        print(f"  {nm}: {len(tr)}笔 wr={wr:.1%} cum={cum:.3f} avg={avg:+.2f}% 周盈≈{pw:.1%} MDD={mdd:.1%} Sharpe={sp:.2f}")

print("="*70)
print("🔬 SMC/ICT 策略量化回测")
print("="*70)
print(f"SL={SL*100:.1f}% TP={TP*100:.1f}% 数据: BTC 1H {n}根")
print(f"\n{'─'*70}")
print("概念验证——看看每个逻辑单独能打出多少信号、胜率如何")

# 先看各基础概念的数量和基本分布
print(f"\n基础概念信号量:")
print(f"  流动性扫荡(上): {sweep_up.sum()}  流动性扫荡(下): {sweep_down.sum()}")
print(f"  FVG(多): {fvg_bull.sum()}  FVG(空): {fvg_bear.sum()}")
print(f"  OB(多): {bull_ob_simple.sum()}  OB(空): {bear_ob_simple.sum()}")

# 跑各策略
ps(*backtest(sigs_sweep_fvg), "策略1: 流动性扫荡+FVG")
ps(*backtest(sigs_ob), "策略2: Order Block")
ps(*backtest(sigs_triple), "策略3: 扫荡+FVG(双重)")

# 折扣区过滤版
ps(*backtest(sigs_sweep_fvg, True), "策略1+折扣过滤")
ps(*backtest(sigs_ob, True), "策略2+折扣过滤")

print(f"\n{'='*70}")
print("📊 与烛龙v1.7对比：")
print("  v1.7: 65笔 wr=56.9% cum=2.518 周盈≈69% MDD=-16.7% Sharpe=5.18")

# 分析：为什么SMC信号质量如何
print(f"\n{'─'*70}")
print("📋 FVG填充率分析（FVG出现后是否被回补）")
filled_count = 0
total_fvg = 0
for i in range(5, n-24):
    if fvg_bull[i]:
        total_fvg += 1
        # 检查后面24小时内价格是否回到FVG内
        for j in range(1, 25):
            if i+j >= n: break
            if L[i+j] <= H[i-2]:  # 价格跌回FVG
                filled_count += 1
                break
    elif fvg_bear[i]:
        total_fvg += 1
        for j in range(1, 25):
            if i+j >= n: break
            if H[i+j] >= L[i-2]:
                filled_count += 1
                break

if total_fvg > 0:
    print(f"  FVG总数: {total_fvg}")
    print(f"  24h内回补: {filled_count} ({filled_count/total_fvg:.1%})")
    print(f"  搜索说的70%填充率 {'✅ 接近' if abs(filled_count/total_fvg-0.7)<0.15 else '❌ 不符'}")