"""数据探索：让数据自己说话
把所有基础K线特征列出来，看哪个真正预测方向"""
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]

# 1H
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)
idx = d.index

# ── 目标：未来4小时和8小时的收益 ──
fwd_ret_4 = np.roll(C, -4) / C - 1
fwd_ret_8 = np.roll(C, -8) / C - 1

# ── 特征工程：所有能想到的基础特征 ──
features = {}

# ── 1. K线本体 ──
features["body"] = abs(C - O)  # 实体大小
features["range"] = H - L  # 振幅
features["body_ratio"] = features["body"] / (features["range"] + 1e-9)  # 实体占比
features["upper_shadow"] = H - np.maximum(O, C)  # 上影线
features["lower_shadow"] = np.minimum(O, C) - L  # 下影线
features["shadow_ratio"] = features["upper_shadow"] / (features["lower_shadow"] + 1e-9)  # 影线比
features["is_bull"] = (C > O).astype(int)  # 阳线
features["is_bear"] = (C < O).astype(int)  # 阴线
features["is_doji"] = (features["body_ratio"] < 0.1).astype(int)  # 十字星

# ── 2. 成交量 ──
vol_ma20 = pd.Series(V).rolling(20).mean().values
features["vol_ratio"] = V / (vol_ma20 + 1e-9)  # 量比
features["vol_change"] = V / (np.roll(V, 1) + 1e-9)  # 量变化

# ── 3. 相对位置 ──
prev_h20 = pd.Series(H).shift(1).rolling(20).max().values
prev_l20 = pd.Series(L).shift(1).rolling(20).min().values
prev_h50 = pd.Series(H).shift(1).rolling(50).max().values
prev_l50 = pd.Series(L).shift(1).rolling(50).min().values
features["pos_in_range"] = (C - prev_l20) / (prev_h20 - prev_l20 + 1e-9)  # 在20根区间内的位置
features["near_high"] = (H > prev_h20 * 0.995).astype(int)
features["near_low"] = (L < prev_l20 * 1.005).astype(int)
features["breakout_high"] = (H > prev_h20).astype(int)  # 突破前高
features["breakout_low"] = (L < prev_l20).astype(int)  # 跌破前低

# ── 4. 收益/动量 ──
features["ret_1"] = C / C1 - 1  # 本根收益
features["ret_2"] = C / np.roll(C, 2) - 1
features["ret_3"] = C / np.roll(C, 3) - 1
features["ret_5"] = C / np.roll(C, 5) - 1
features["ret_10"] = C / np.roll(C, 10) - 1
features["ret_20"] = C / np.roll(C, 20) - 1

# ── 5. 波动率 ──
tr = np.maximum(H-L, np.maximum(abs(H-C1), abs(L-C1)))
features["atr_14"] = pd.Series(tr).rolling(14).mean().values / C  # 相对ATR
features["volatility_5"] = pd.Series(features["ret_1"]).rolling(5).std().values  # 5期波动率

# ── 6. 前K线特征 ──
features["prev_body"] = abs(C1 - O1)
features["prev_range"] = H1 - L1
features["prev_ret"] = C1 / np.roll(C, 2) - 1
features["prev_vol_ratio"] = np.roll(V, 1) / (np.roll(vol_ma20, 1) + 1e-9)
features["prev_is_bull"] = (C1 > O1).astype(int)

# ── 7. 双K线形态 ──
features["engulf_bull"] = ((C1 < O1) & (C > O) & (O <= C1) & (C >= O1)).astype(int)
features["engulf_bear"] = ((C1 > O1) & (C < O) & (O >= C1) & (C <= O1)).astype(int)
features["piercing"] = ((C1 < O1) & (C > O) & (O < L1) & (C > (O1+C1)/2) & (C < O1)).astype(int)
features["dark_cloud"] = ((C1 > O1) & (C < O) & (O > H1) & (C < (O1+C1)/2) & (C > O1)).astype(int)
features["two_bull"] = ((C1 > O1) & (C > O)).astype(int)  # 连阳
features["two_bear"] = ((C1 < O1) & (C < O)).astype(int)  # 连阴
features["bull_follow_bear"] = ((C1 < O1) & (C > O)).astype(int)  # 阴后阳
features["bear_follow_bull"] = ((C1 > O1) & (C < O)).astype(int)  # 阳后阴

# ── 8. 三K线形态 ──
features["three_bull"] = ((np.roll(C,2) > np.roll(O,2)) & (C1 > O1) & (C > O)).astype(int)  # 三连阳
features["three_bear"] = ((np.roll(C,2) < np.roll(O,2)) & (C1 < O1) & (C < O)).astype(int)  # 三连阴
features["morning_star"] = ((np.roll(C,2) < np.roll(O,2)) & (abs(np.roll(C,2)-np.roll(O,2))/(np.roll(H,2)-np.roll(L,2)+1e-9) < 0.3) & (C > O) & (C > (np.roll(O,2)+np.roll(C,2))/2)).astype(int)
features["evening_star"] = ((np.roll(C,2) > np.roll(O,2)) & (abs(np.roll(C,2)-np.roll(O,2))/(np.roll(H,2)-np.roll(L,2)+1e-9) < 0.3) & (C < O) & (C < (np.roll(O,2)+np.roll(C,2))/2)).astype(int)

# ── 9. 组合特征 ──
features["big_bull"] = ((C > O) & (features["body"] > features["range"] * 0.7) & (V > vol_ma20 * 1.5)).astype(int)
features["big_bear"] = ((C < O) & (features["body"] > features["range"] * 0.7) & (V > vol_ma20 * 1.5)).astype(int)
features["sweep_up"] = ((H > prev_h20) & (C < prev_h20)).astype(int)
features["sweep_down"] = ((L < prev_l20) & (C > prev_l20)).astype(int)

# 转换成DataFrame
df_feat = pd.DataFrame(features)

# ── 统计每个特征的预测能力 ──
print("=" * 80)
print("🔬 烛龙数据探索：哪个特征真正预测价格？")
print("=" * 80)
print(f"数据: BTC 1H, {n}根K线 ({idx[0].date()} ~ {idx[-1].date()})")
print(f"目标: 未来4小时收益 > 0.3%（正向预测能力）")
print()

# 目标1：未来4小时涨>0.3%
target = (fwd_ret_4 > 0.003).astype(int)
target_desc = "未来4h涨>0.3%"

# 只使用前80%数据做训练，后20%是样本外
train_end = int(n * 0.8)
results = []

for col in df_feat.columns:
    col_data = df_feat[col].values
    
    # 筛选非NaN且非0/1之外的数值特征用分位数
    # 对二进制特征直接统计
    unique_vals = np.unique(col_data[~np.isnan(col_data)])
    
    if len(unique_vals) <= 2 and 0 in unique_vals and 1 in unique_vals:
        # 二进制特征：直接看条件概率
        cond_true = (col_data[:train_end] == 1) & (~np.isnan(target[:train_end]))
        cond_false = (col_data[:train_end] == 0) & (~np.isnan(target[:train_end]))
        
        if cond_true.sum() >= 10 and cond_false.sum() >= 10:
            wr_true = target[:train_end][cond_true].mean()
            wr_false = target[:train_end][cond_false].mean()
            lift = wr_true - wr_false  # 提升
            n_true = cond_true.sum()
            
            results.append((col, lift, wr_true, wr_false, n_true, "binary"))
    else:
        # 数值特征：用中位数分高低两组
        med = np.nanmedian(col_data[:train_end])
        cond_high = (col_data[:train_end] > med) & (~np.isnan(target[:train_end]))
        cond_low = (col_data[:train_end] <= med) & (~np.isnan(target[:train_end]))
        
        if cond_high.sum() >= 10 and cond_low.sum() >= 10:
            wr_high = target[:train_end][cond_high].mean()
            wr_low = target[:train_end][cond_low].mean()
            lift = wr_high - wr_low
            n_high = cond_high.sum()
            
            results.append((col, lift, wr_high, wr_low, n_high, "numeric"))

# 排序：按区分能力排列
results.sort(key=lambda x: abs(x[1]), reverse=True)

print(f"{'排名':>4s} {'特征名':25s} {'类型':8s} {'提升':>8s} {'高组胜率':>9s} {'低组胜率':>9s} {'样本量':>8s}")
print("-" * 80)
for i, (col, lift, wr_high, wr_low, n, typ) in enumerate(results[:30]):
    print(f"{i+1:>4d} {col:25s} {typ:8s} {lift:>+7.2%} {wr_high:>8.1%} {wr_low:>8.1%} {n:>7d}")

# 样本外验证：取前5个最有区分度的特征
print(f"\n{'=' * 80}")
print(f"📊 样本外验证：Top 5 特征")
print(f"{'=' * 80}")

test_start = train_end
for col, lift, _, _, _, _ in results[:5]:
    col_data = df_feat[col].values
    unique_vals = np.unique(col_data[~np.isnan(col_data)])
    
    if len(unique_vals) <= 2 and 0 in unique_vals and 1 in unique_vals:
        cond_t = (col_data[test_start:] == 1) & (~np.isnan(target[test_start:]))
        cond_f = (col_data[test_start:] == 0) & (~np.isnan(target[test_start:]))
        if cond_t.sum() >= 5:
            wr_t = target[test_start:][cond_t].mean()
            wr_f = target[test_start:][cond_f].mean()
            print(f"  {col:25s}: 条件=1时胜率{wr_t:.1%}  条件=0时{wr_f:.1%}  提升{wr_t-wr_f:+.1%}  样本{cond_t.sum()}")
    else:
        med = np.nanmedian(col_data[:train_end])
        cond_h = (col_data[test_start:] > med) & (~np.isnan(target[test_start:]))
        cond_l = (col_data[test_start:] <= med) & (~np.isnan(target[test_start:]))
        if cond_h.sum() >= 5:
            wr_h = target[test_start:][cond_h].mean()
            wr_l = target[test_start:][cond_l].mean()
            print(f"  {col:25s}: 高组胜率{wr_h:.1%}  低组{wr_l:.1%}  提升{wr_h-wr_l:+.1%}  样本{cond_h.sum()}")

# 多条件组合分析：最有区分度的特征两两组合
print(f"\n{'=' * 80}")
print(f"🔗 双特征组合分析")
print(f"{'=' * 80}")

top_binary = [col for col, _, _, _, _, typ in results[:10] if typ == "binary"]
top_signals = [c for c in ["engulf_bull","engulf_bear","piercing","dark_cloud","sweep_up","sweep_down","morning_star","evening_star","big_bull","big_bear"] if c in df_feat.columns]

combos = []
for i, c1 in enumerate(top_signals):
    for c2 in top_signals[i+1:]:
        mask = (df_feat[c1].values[:train_end] == 1) & (df_feat[c2].values[:train_end] == 1) & (~np.isnan(target[:train_end]))
        if mask.sum() >= 5:
            wr = target[:train_end][mask].mean()
            combos.append((c1, c2, wr, mask.sum()))

combos.sort(key=lambda x: x[2], reverse=True)

print(f"\n{'组合':35s} {'胜率':>7s} {'样本量':>8s}")
print("-" * 55)
for c1, c2, wr, n in combos[:15]:
    print(f"  {c1}+{c2:20s} {wr:>6.1%} {n:>7d}")

# 最重要的发现：看烛龙核心信号的胜率分布
print(f"\n{'=' * 80}")
print(f"🎯 烛龙核心信号的多时间窗口胜率")
print(f"{'=' * 80}")

# 烛龙信号 = 吞没+支撑+确认
long_mask = features["engulf_bull"] & (features["near_low"] == 1)
for ahead in [2, 4, 8, 12, 24]:
    fwd = np.roll(C, -ahead) / C - 1
    for thresh in [0.001, 0.002, 0.003, 0.005]:
        win = (fwd > thresh) & (~np.isnan(fwd))
        train_win = win[:train_end] & long_mask[:train_end]
        test_win = win[test_start:] & long_mask[test_start:]
        if train_win.sum() >= 5 and test_win.sum() >= 3:
            train_wr = train_win.mean()
            test_wr = test_win.mean()
            print(f"  {ahead:2d}h涨>{thresh:.1%}: 训练{len(train_win):>3d}笔胜率{train_wr:.1%}  测试{len(test_win):>3d}笔胜率{test_wr:.1%}")