扎堆开仓反而更赚,但好处全在「错开几分钟」那一档,同分钟的最差
承接上一条:既然同时开的必然同向,那关键是这批交易比孤立的好还是坏。 11 币 / 1161 笔 / 实盘口径: 真孤立(±5 分钟内无同伴) 763 笔 胜率 65.5% 毛R 0.832 PF 2.83 错开:5 分钟内但不同分钟 240 笔 胜率 82.1% 毛R 1.443 PF 7.31 同一分钟撞在一起 158 笔 胜率 63.3% 毛R 0.777 PF 2.20 必须把两者分开——结论相反,混在一起会得出错误判断。错开的是全样本最好的 一档,同分钟的反而略差于孤立组。机制上:错开 = 行情从某个币扩散开,后发是 对先发的确认;同分钟 = 全市场同时被一个冲击打中,即追高。 簇级复核(±5 分钟合一簇,排除重复计数):多笔簇簇均毛R 1.180 vs 单笔簇 0.832,簇级 R 夏普 0.848 vs 0.459,结论不是重复计数撑起来的。多笔簇内 全赢 59.5%、全输 10.1%,簇内风险不可分散但偏度有利。 集中度上两类没差别(整簇同向 99.4%),差别纯在收益。所以「限制最多 N 个 并发仓位」把两类一视同仁是错的,它们期望收益差 1.9 倍。 注意:同分钟 vs 错开是看过数据后才划的切法,不是事先定的,208 天 158 个簇 容易切出噪声。当仓位规则用之前必须换一段时间验证。目前只有「扎堆整体更好」 是稳的(簇级也成立)。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -259,6 +259,33 @@ push = z_above if direction == 1 else z_below
|
|||||||
2. **并发上限要按「同向净敞口」设,不能按「仓位个数」设。**
|
2. **并发上限要按「同向净敞口」设,不能按「仓位个数」设。**
|
||||||
同时开的必然同向,限「最多 3 仓」等于允许 3 倍单向敞口
|
同时开的必然同向,限「最多 3 仓」等于允许 3 倍单向敞口
|
||||||
|
|
||||||
|
**扎堆的交易赚不赚钱(step48)**:必须把「同一分钟」和「错开几分钟」分开,
|
||||||
|
**两者结论相反**,混在一起会得出错误判断。
|
||||||
|
|
||||||
|
| 分组 | 笔数 | 胜率 | 毛R | PF | 余量bp |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| 真孤立(±5 分钟内无同伴) | 763 (66%) | 65.5% | 0.832 | 2.83 | 11.92 |
|
||||||
|
| **错开:5 分钟内但不同分钟** | 240 (21%) | **82.1%** | **1.443** | **7.31** | **29.59** |
|
||||||
|
| 同一分钟撞在一起 | 158 (14%) | 63.3% | 0.777 | 2.20 | 12.35 |
|
||||||
|
|
||||||
|
**错开的那批是全样本最好的一档**(毛R 是孤立组的 1.7 倍),而严格同分钟的
|
||||||
|
反而略差于孤立组。机制上说得通:错开 = 一波行情从某个币扩散开,后发信号是
|
||||||
|
对先发的确认;同分钟 = 全市场同时被一个冲击打中,那就是追高。
|
||||||
|
|
||||||
|
簇级复核(±5 分钟合一簇,每簇算一个观测,排除重复计数):
|
||||||
|
多笔簇簇均毛R **1.180** vs 单笔簇 0.832,簇级 R 夏普 **0.848** vs 0.459。
|
||||||
|
结论在簇级依然成立。多笔簇内全赢 59.5%、全输 10.1%、有赢有输 30.4%
|
||||||
|
—— 簇内风险不可分散,但偏度有利(赢的簇是输的簇的 6 倍)。
|
||||||
|
|
||||||
|
集中度上两类没差别:整簇方向一致 **99.4%**(同分钟 100%、错开 99.2%)。
|
||||||
|
簇内中位 2 个币、最大 8,跨度中位 2 分钟。**所以「限制最多 N 个并发仓位」
|
||||||
|
把两类一视同仁是错的,它们期望收益差 1.9 倍。**
|
||||||
|
|
||||||
|
> ⚠️ **「同分钟 vs 错开」是看过数据之后才划的切法,不是事先定的。**
|
||||||
|
> 208 天、158 个簇,post-hoc 切分容易切出噪声。**拿它当仓位规则前必须换一段
|
||||||
|
> 时间验证。** 目前可确定的只有「扎堆整体更好」(簇级也成立);
|
||||||
|
> 「好处全集中在错开那一档」尚待样本外确认。
|
||||||
|
|
||||||
**TRX 应从币池剔除**:实盘口径 208 天只有 **5 笔**,ATR 门控几乎全刷掉
|
**TRX 应从币池剔除**:实盘口径 208 天只有 **5 笔**,ATR 门控几乎全刷掉
|
||||||
(与 §5.3「2026 只剩 4.2%」一致)。实际可用是 10 个币,不是 11 个。
|
(与 §5.3「2026 只剩 4.2%」一致)。实际可用是 10 个币,不是 11 个。
|
||||||
逐币每天笔数:DOGE/AVAX/SOL 约 0.66,BTC/LTC 0.45,BNB 0.31,TRX 0.02。
|
逐币每天笔数:DOGE/AVAX/SOL 约 0.66,BTC/LTC 0.45,BNB 0.31,TRX 0.02。
|
||||||
|
|||||||
@@ -83,6 +83,11 @@ def collect(sym: str, rows: int) -> pd.DataFrame | None:
|
|||||||
"htf": sig["htf_agree"].to_numpy(),
|
"htf": sig["htf_agree"].to_numpy(),
|
||||||
"lad": sig["ladder_ok"].to_numpy(),
|
"lad": sig["ladder_ok"].to_numpy(),
|
||||||
"hold": res[f"{cfg}_b"].to_numpy(),
|
"hold": res[f"{cfg}_b"].to_numpy(),
|
||||||
|
# 毛利与出场原因:扎堆的交易赚不赚钱要靠这几列,别只存时刻
|
||||||
|
"g": res[f"{cfg}_g"].to_numpy(),
|
||||||
|
"r": res[f"{cfg}_r"].to_numpy(),
|
||||||
|
"c": res[f"{cfg}_c"].to_numpy(),
|
||||||
|
"atr_pct": atr / close,
|
||||||
})
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f" {sym} 失败: {e!r}", flush=True)
|
print(f" {sym} 失败: {e!r}", flush=True)
|
||||||
@@ -131,6 +136,66 @@ def analyse(d: pd.DataFrame, label: str) -> None:
|
|||||||
print(f"\n同一分钟出现多笔的时刻 {len(multi)} 个,其中方向完全一致的占 "
|
print(f"\n同一分钟出现多笔的时刻 {len(multi)} 个,其中方向完全一致的占 "
|
||||||
f"{agree*100:.1f}% —— 这部分不是分散,是同一笔押注被拆成几个币")
|
f"{agree*100:.1f}% —— 这部分不是分散,是同一笔押注被拆成几个币")
|
||||||
|
|
||||||
|
if "g" in d.columns:
|
||||||
|
profit_by_cluster(d)
|
||||||
|
|
||||||
|
|
||||||
|
def profit_by_cluster(d: pd.DataFrame) -> None:
|
||||||
|
"""扎堆的交易赚不赚钱。
|
||||||
|
|
||||||
|
必须把「同一分钟」和「错开几分钟」分开看——8 币预试中两者结论相反:
|
||||||
|
错开的是全样本最好的一档,同分钟的反而略差于孤立。混在一起会得出错误结论。
|
||||||
|
"""
|
||||||
|
from lib.exit_model import fee_of, taker_notional
|
||||||
|
|
||||||
|
d = d.sort_values("date").reset_index(drop=True)
|
||||||
|
t = d.date.values.astype("datetime64[m]").astype(np.int64)
|
||||||
|
n0 = np.searchsorted(t, t, "right") - np.searchsorted(t, t, "left")
|
||||||
|
n5 = np.searchsorted(t, t + 5, "right") - np.searchsorted(t, t - 5, "left")
|
||||||
|
net = d.g.values - fee_of(d.r.values, d.c.values)
|
||||||
|
R = net / (SL * d.atr_pct.values)
|
||||||
|
gR = d.g.values / (SL * d.atr_pct.values)
|
||||||
|
tn = taker_notional(d.r.values, d.c.values)
|
||||||
|
|
||||||
|
def row(m, lab):
|
||||||
|
if m.sum() < 20:
|
||||||
|
return {"分组": lab, "笔数": int(m.sum()), "备注": "样本不足"}
|
||||||
|
nn, rr, gg, tt = net[m], R[m], gR[m], tn[m]
|
||||||
|
w, o = nn[nn > 0].sum(), -nn[nn <= 0].sum()
|
||||||
|
return {"分组": lab, "笔数": int(m.sum()), "占比": f"{m.mean()*100:.0f}%",
|
||||||
|
"胜率": f"{(nn > 0).mean()*100:.1f}%", "毛R": round(gg.mean(), 3),
|
||||||
|
"净均R": round(rr.mean(), 3),
|
||||||
|
"R夏普": round(rr.mean() / rr.std(ddof=1), 3),
|
||||||
|
"PF": round(w / o, 2) if o > 0 else np.inf,
|
||||||
|
"余量bp": round(nn.mean() / tt.mean() * 1e4, 2)}
|
||||||
|
|
||||||
|
print("\n扎堆的交易赚不赚钱")
|
||||||
|
print(pd.DataFrame([
|
||||||
|
row((n0 == 1) & (n5 == 1), "真孤立(±5 分钟内无同伴)"),
|
||||||
|
row((n0 == 1) & (n5 > 1), "错开:5 分钟内有同伴但不同分钟"),
|
||||||
|
row(n0 > 1, "同一分钟撞在一起"),
|
||||||
|
]).to_string(index=False))
|
||||||
|
|
||||||
|
# 簇级:同一波行情里的几笔高度相关,逐笔统计会把有效样本算多
|
||||||
|
clu = (d.date.diff().dt.total_seconds().fillna(9e9) > 300).cumsum()
|
||||||
|
c = pd.DataFrame({"clu": clu, "R": R, "gR": gR, "net": net}).groupby("clu")
|
||||||
|
agg = c.agg(n=("R", "size"), R=("R", "mean"), gR=("gR", "mean"))
|
||||||
|
print(f"\n簇级(±5 分钟合为一簇,避免重复计数):单笔簇 {(agg.n==1).sum()}、"
|
||||||
|
f"多笔簇 {(agg.n>1).sum()}")
|
||||||
|
for lab, m in (("单笔簇", agg.n == 1), ("多笔簇", agg.n > 1)):
|
||||||
|
g = agg[m]
|
||||||
|
if len(g) < 10:
|
||||||
|
continue
|
||||||
|
print(f" {lab:<5} {len(g):>4} 簇 簇均毛R {g.gR.mean():.3f} "
|
||||||
|
f"簇均净R {g.R.mean():.3f} 簇级R夏普 {g.R.mean()/g.R.std(ddof=1):.3f}")
|
||||||
|
win = c["net"].apply(lambda s: (s > 0).all())
|
||||||
|
lose = c["net"].apply(lambda s: (s <= 0).all())
|
||||||
|
multi_idx = agg.index[agg.n > 1]
|
||||||
|
if len(multi_idx):
|
||||||
|
aw, al = win[multi_idx].mean(), lose[multi_idx].mean()
|
||||||
|
print(f" 多笔簇内:全赢 {aw*100:.1f}%、全输 {al*100:.1f}%、"
|
||||||
|
f"有赢有输 {(1-aw-al)*100:.1f}% —— 簇内风险不可分散,但偏度有利")
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
ap = argparse.ArgumentParser()
|
ap = argparse.ArgumentParser()
|
||||||
|
|||||||
Reference in New Issue
Block a user