"""赌注对冲 v2：按形态历史胜率动态分配仓位"""
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]
d = df_raw.resample("1h").agg({"open":"first","high":"max","low":"min","close":"last","volume":"sum"}).dropna()

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)
n = len(d)

dd = df_raw.resample("1D").agg({"close":"last"}).dropna()
dd["ma20"] = dd["close"].rolling(20).mean()
dd["trend_up"] = dd["close"] > dd["ma20"]
d_ts = d.index
trend_1h = np.array([dd["trend_up"].reindex([ts], method="ffill").values[0] if ts >= dd.index[0] else True for ts in d_ts])

prev_l20 = pd.Series(L).shift(1).rolling(20).min().values
prev_h20 = pd.Series(H).shift(1).rolling(20).max().values
near_s = abs(L - prev_l20) / (prev_l20 + 1e-9) < 0.008
near_r = abs(H - prev_h20) / (prev_h20 + 1e-9) < 0.008

body = abs(C-O); body1 = abs(C1-O1); body2 = abs(C2-O2)
rk = H-L
body_r = body / (rk + 1e-9)

nbull = np.roll(C,-1) > np.roll(O,-1)
nbear = np.roll(C,-1) < np.roll(O,-1)

# 所有看涨形态
bull_engulf = (C1<O1) & (C>O) & (O<=C1) & (C>=O1) & near_s
piercing = (C1<O1) & (C>O) & (O<L1) & (C>(O1+C1)/2) & (C<O1) & near_s
hammer = (C>O) & ((O-L) >= body*2) & ((H-C) <= body*0.3) & (body_r < 0.4) & near_s
morning_star = (C1<O1) & (body1/rk < 0.3) & (C2<O2) & (C>(O1+C1)/2) & near_s
bull_doji = (C1<O1) & (body_r < 0.15) & (C>(H+L)/2) & near_s

# 所有看跌形态
bear_engulf = (C1>O1) & (C<O) & (O>=C1) & (C<=O1) & near_r
dark_cloud = (C1>O1) & (C<O) & (O>H1) & (C<(O1+C1)/2) & (C>O1) & near_r
shooting = (C<O) & ((H-C) >= body*2) & ((O-L) <= body*0.3) & (body_r < 0.4) & near_r
evening_star = (C1>O1) & (body1/rk < 0.3) & (C2>O2) & (C<(O1+C1)/2) & near_r
bear_doji = (C1>O1) & (body_r < 0.15) & (C<(H+L)/2) & near_r

bull_patterns = [
    ("吞没+阳线", bull_engulf & nbull),
    ("刺透+阳线", piercing & nbull),
    ("锤子+阳线", hammer & nbull),
    ("晨星+阳线", morning_star & nbull),
    ("十字+阳线", bull_doji & nbull),
]
bear_patterns = [
    ("吞没+阴线", bear_engulf & nbear),
    ("乌云+阴线", dark_cloud & nbear),
    ("流星+阴线", shooting & nbear),
    ("暮星+阴线", evening_star & nbear),
    ("十字+阴线", bear_doji & nbear),
]

SL, TP, MB = 0.015, 0.045, 48
FEE = 0.001

def calc_winrate(mask, dirc):
    wins, losses = 0, 0
    for i in range(100, n-MB):
        if not mask[i]: continue
        if dirc == 1 and not trend_1h[i]: continue
        if dirc == -1 and trend_1h[i]: continue
        for j in range(1, MB+1):
            if i+j >= n: break
            ret = (C[i+j]/C[i]-1)*dirc
            if ret >= TP: wins+=1; break
            if ret <= -SL: losses+=1; break
    t = wins+losses
    return (wins/t, t) if t >= 5 else (None, t)

# 计算每个形态的胜率（样本内）
split = int(n*0.67)
print("形态各形态胜率（样本内）:")
print(f"{'形态':16s} {'胜率':>6s} {'笔数':>6s} {'仓位权重':>8s}")
print("-"*40)

weights = {}
for label, mask in bull_patterns:
    wr, t = calc_winrate(mask, 1)
    if wr is None: continue
    w = max(0.5, min(1.5, wr/0.5))
    weights[label] = (w, 1, mask)
    print(f"{label:16s} {wr:>5.1%} {t:>5d} {w:>7.2f}x")

for label, mask in bear_patterns:
    wr, t = calc_winrate(mask, -1)
    if wr is None: continue
    w = max(0.5, min(1.5, wr/0.5))
    weights[label] = (w, -1, mask)
    print(f"{label:16s} {wr:>5.1%} {t:>5d} {w:>7.2f}x")

print(f"\n共{len(weights)}个有效形态")

# ── 回测 ──
def backtest(fixed_weight=False):
    results = {"in": [], "out": []}
    
    for label, (w, dirc, mask) in weights.items():
        wgt = 1.0 if fixed_weight else w
        
        for period, st, en in [("in",0,split), ("out",split,n)]:
            for i in range(st, min(en,n)):
                if not mask[i]: continue
                if i+MB >= n: continue
                entry = C[i]
                closed = False
                for j in range(1, MB+1):
                    if i+j >= n: break
                    ret = (C[i+j]/entry-1)*dirc
                    # 实际盈亏 = 原始收益 × 权重
                    effective_ret = ret * wgt
                    if effective_ret >= TP:
                        results[period].append(TP - FEE)
                        closed=True; break
                    if effective_ret <= -SL:
                        results[period].append(-SL - FEE)
                        closed=True; break
                if not closed:
                    fr = (C[min(i+MB,n-1)]/entry-1)*dirc * wgt
                    results[period].append(fr - FEE)
    return results

def ps(results, label):
    print(f"\n▶ {label}")
    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(f"\n{'='*60}")
print("赌注对冲回测")
print(f"{'='*60}")
print(f"SL={SL*100:.1f}% TP={TP*100:.1f}%")

r0 = backtest(fixed_weight=True)
ps(r0, "固定仓位（所有形态=1x）")

r1 = backtest(fixed_weight=False)
ps(r1, "赌注对冲（按胜率分配0.5x~1.5x）")