Files
Chan/research/HANDOFF.md
T
jackyu66gitandCursor 6259f7380b research: 重算新出场口径下的并发,并修正「各笔基本独立」的说法
分批出场把 1m 平均持仓从 9.9 根拉到 29 根,§3.3 的并发数字是旧口径的。
重算后 8 币组合平均并发 0.043→0.120、有仓位时间 4.0%→10.2%,峰值仍是 6。

更要紧的是顺带查出来的相关性:同一小时内有 ≥2 个币发信号的时段占 20.9%,
其中 92.7% 方向完全一致。所以「有仓位时间低 → 各笔基本独立」这个推理不成立
——时间上不重叠不等于统计上独立。t 值有一定虚高(不足以推翻,t 在 43 以上),
但更实际的后果是:加币不产生分散,仓位不能按「1% × N 个币」线性放。

用户问「整个市场都是正相关的,是不是很少有独立行情」。市场相关是真的,
但这不是伪装成策略的 beta:多空各占 50.7% / 49.3%,做空 PF 4.40 还略好于
做多 4.05,所有时段净方向合计仅 +185 笔。分年看,2021 大牛年做空的 PF 5.34
是整张表最高的一格,七年里没有一年、没有一个方向是亏的。空头占比随行情
切换(牛市 46.5% → 熊市 55.9%)。

所以 92.7% 同向该理解为「检测器正确识别到全市场级别的结构」——若 6 个币
同时发信号却方向随机,那才说明信号是噪声。

另补 3.33 扩币筛选:ATR 对门控阈值与流动性是两条方向相反的约束,最优区间
在中间。BTC 输在波动不够(ATR 中位 2026 仅 6.5bp,预算垫底),TRX 2026 门控
后只剩 4.2% 信号。保证金约束那条待办从「预计影响小」改为扩币前置条件。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 00:45:32 +08:00

1110 lines
60 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 缠论信号研究 → 实盘落地:交接文档
> 最后更新:2026-08-27。写给接手这项工作的下一个 agent。
> 读完这份文档应该不需要再翻聊天记录。若确有需要,历史转录在
> `/Users/jack/.cursor/projects/Users-jack-Project-chan/agent-transcripts/`。
---
## 0. 一句话现状
策略在回测和多重偏差审计下都站得住(跨 11 个币、7 年、多空对称、无未来函数),
**研究阶段基本结束**;当前唯一的拦路问题是「实盘执行成本能否压进预算」,
下一步是写影子交易器实测滑点,而不是继续调参。
**用户明确要求:不要再跑新的参数优化。** 他的原话是「很多时候回测数据特别好,
一到 dry run 和实盘就歇菜了」。他要的是执行层的验证。
---
## 1. 策略的确切定义
这是全文最重要的一节。所有回测数字都基于以下这套口径,改任何一条数字就不可比。
### 1.1 信号生成(三层)
```
第一层 小级别(LTF) 自身的中枢 build_htf_zones(cdf, ltf, chan=chan_l)
第二层 中枢突破后回抽不回,收盘转强 → 入场 find_fast_bsp3(cdf, zones)
第三层 大级别(HTF) 分型方向必须同向 attach_htf_context(...) 后取 h1_agree == 1
可选 中枢阶梯方向过滤(顺向推进) z_above / z_below,见 1.4
```
用户对「区间套」的定义(务必按这个理解,早期我理解错过):
**大级别(1h/4h)分型定位转折点 + 小级别(15m/5m/1m)中枢突破形成第三类买卖点。**
不是大级别中枢突破。
### 1.2 `find_fast_bsp3` 的当前默认参数
`research/lib/fast_bsp3.py`
| 参数 | 默认值 | 含义与注意 |
|---|---|---|
| `scan` | 200 | 中枢成立后向后扫多少根找突破 |
| `pullback_win` | 30 | 回抽窗口 |
| `tol` | **-1.0** | 负值=禁用「回抽未触及边界就跳过」。**这是最关键的参数**,见 3.2 |
| `max_per_zone` | 1 | 同一中枢只做首次入场,见 4.2 |
| `require_touch` | **False** | 不要求回抽触及中枢边界,见 3.2 |
`tol=-1` + `require_touch=False` 是当前口径,**滞后 2.2 根**。
早期默认(`tol=0.003`, `require_touch=True`)滞后 5.8 根,年化从 906% 掉到 370%。
新旧差异已完整量化在 step34(tol 扫描)与 step33`require_touch`)里,
旧口径的原始输出已清理——**不需要它,也不要回到旧口径**。
### 1.3 交易执行口径
```python
SL, TP, MAX_BARS = 1.5, 3.0, 48 # ATR 倍数止损/止盈 + 超时根数
run_trades(cdf, entries, SL, TP, MAX_BARS, fee=..., entry_delay=1)
```
- **入场:信号次根开盘价**`entry_delay=1`),不是信号根收盘价
- 研究口径成本:**4bp 手续费 + 1bp 滑点 = 5bp**(所有带「已扣 5bp」的数字都是这个)
- ATR 取信号根的值(`atr[sig_idx]`),不是入场根
> ⚠️ **TP=3.0 已被证明偏小(step41/42,见 3.5 与 5.3)**,且出场参数要按级别分开定。
> 本文档所有历史数字仍是 TP=3.0 口径,**改口径前不要混用**。
#### 成本模型:不是一刀切的 5bp(用户 2026-08-27 指出)
用户指出「只有开仓才是 taker」。对了一半——**止盈挂限价确实是 maker,
但止损是 stop-market,成交时仍是 taker**(做成 stop-limit 会有急跌不成交的
风险,那种损失远大于省下的费)。所以按出场原因分别计费。
**实际费率档位(用户 2026-08-27 给的,单边):返佣前 taker 0.040% /
maker 0.016%API 返 50% → taker 2.0bp / maker 0.8bp。**
| 环节 | 性质 | 单边费率 |
|---|---|---|
| 入场(次根开盘市价) | taker + 滑点 | **2.0bp** |
| 止盈(限价被动成交) | **maker,无滑点** | **0.8bp** |
| 止损(stop-market | taker + 滑点 | 2.0bp |
| 超时(到点市价平) | taker + 滑点 | 2.0bp |
> ⚠️ **step41/42 首轮跑的时候这里写的是 taker 3.0 / maker 1.0bp**
> 隐含「原始 taker 6bp、返 50% 得 3bp」的错误前提。真实原始是 4bp。
> **方向是保守的**——taker 高估了 50%,所以那两步跑出来的净收益、
> 滑点余量、均R 全部偏低,不是偏高。`exit_model.py` 已改成实际值,
> 但**本文档里凡是标注「旧费率 3/1bp」的数字尚未逐条重算**。
`SL1.5/TP3/48根` 1m 样本外实测(止盈成交占 60%):
| 口径 | 平均成本 | 说明 |
|---|---|---|
| 全 taker 双边 | 8.00bp | 最早的口径,双重高估 |
| 混合费率 @ 3/1bp | 6.20bp | step41/42 首轮 |
| **混合费率 @ 实际 2/0.8bp** | **2.00bp** | 当前 |
| 研究口径 固定 5bp | 5.00bp | §2 那批老数字 |
**注意方向变了:研究口径的 5bp 现在是偏保守的。** 实际混合费率只有 2bp。
实现在 `research/lib/exit_model.py`step41/42 共用。
**分批离场不额外增加费用**(手续费按名义额收,出场 0.5+0.5 总额不变),
而且第一批必然是止盈成交,所以分批在成本上还占便宜。
### 1.4 阶梯过滤(顺向推进)
对应缠论「趋势 vs 盘整」:当前中枢相对前一个中枢是否同向推进。
```python
pg, pdn = z["zg"].shift(), z["zd"].shift()
z["z_above"], z["z_below"] = z["zd"] > pg, z["zg"] < pdn
push = z_above if direction == 1 else z_below
```
加上它 PF 从 2.72 → 3.4130m/2h)。**用户特别在意这一点**,因为它印证了
缠论对趋势的定义(一个分型后第一个中枢就反转的概率低于出现第二个中枢后反转)。
### 1.5 各级别的确切配对
| 小级别 | 大级别(h1,做同向过滤) | 备注 |
|---|---|---|
| 1m | **5m** | step23 的配置是 `1m:5m:15m`15m 只记录不过滤 |
| 5m | **30m** | 统计量最强 |
| 15m | **1h** | |
| 30m | **2h** | 单笔质量最高 |
---
## 2. 最优配置与核心数字
### 2.1 级别矩阵(step28,同向+阶梯,3 币)
| 组合 | 笔数 | 胜率 | 中位 | PF | t值 |
|---|---|---|---|---|---|
| **5m/30m** | 1321 | 53.1% | +0.31% | 1.98 | **10.16** |
| **30m/2h** | 258 | 65.1% | +1.65% | **3.41** | 8.51 |
| 5m/15m | 1292 | 51.7% | +0.24% | 1.82 | 8.84 |
| 15m/1h | 467 | 56.5% | +0.74% | 2.46 | 7.96 |
选择逻辑:**30m/2h 单笔质量最好,5m/30m 统计显著性最强**。1h 及以上小级别笔数不足。
### 2.2 跨品种样本外(step37)— 最有说服力的一项
参数一个没动,拿 8 个完全没参与调参的币验证:
| 分组 | 笔数 | 年笔数 | 胜率 | 中位 | PF | t值 | 剔10%PF |
|---|---|---|---|---|---|---|---|
| 样本内(BTC/ETH/SOL) | 2309 | 334 | 61.4% | +0.531% | 2.86 | +19.11 | 1.77 |
| **样本外(8 个新币)** | 5859 | 884 | 62.9% | +0.592% | **2.73** | **+28.50** | 1.66 |
逐币最弱的 XRP 仍是 PF 2.21 / t 7.64。**没有一个币是负的。**
### 2.3 收益量级——务必用正确口径(step35 第 5 节)
这一条最容易被误读,历史上我自己也一度报错过口径:
| 口径 | 年化 | 回撤 | Sharpe |
|---|---|---|---|
| A 1%风险+20倍上限+复利 | +1323.8% | 8.1% | 10.38 |
| B 1%风险+20倍上限+不复利 | +270.0% | 8.4% | 10.38 |
| **C 固定名义1倍+不复利** | **+194.7%** | 8.4% | 7.95 |
| D 固定名义3倍+不复利 | +584.1% | 25.2% | 7.95 |
**只有 C/D 是可以直接对照的量级。** 那些 906%/1323% 的数字来自复利+杠杆假设。
平均单笔杠杆 2.1x(中位 1.8x)。
**未含资金费率,未含同时持仓的保证金约束。**
---
## 3. 已验证成立的结论
### 3.1 没有未来函数(step38,最要紧的一项)
担心的是 `incremental.py` 里那句注释:「最后一笔 is_sure 允许收回」——
中枢生效时刻 `available_ts` 取自笔的 `sure_time`,而那是全量重算得出的。
做法:对每个信号只喂到该根为止的数据(窗口 4000 根),重跑
`TF_DF → 中枢 → fast_bsp3`,看信号是否真的在那一刻存在。
结果:**同根命中 100.0%(抽检 1217 个信号),完全消失 0.0%,假阳性 0.7%。**
偏移中位 +0.0 根。回测口径可信。
### 3.2 滞后是收益的第一驱动(step34)
| 口径 | 笔数 | 滞后(根) | 胜率 | PF | 年化 | Sharpe |
|---|---|---|---|---|---|---|
| **禁用跳过(现用)** | 2309 | **2.2** | 61.4% | 2.86 | +906.1% | 9.02 |
| tol 0 | 2214 | 2.7 | 60.4% | 2.74 | +731.3% | 8.43 |
| tol 0.1% | 2134 | 3.6 | 58.7% | 2.64 | +569.8% | 7.65 |
| tol 0.3%(旧默认) | 2045 | 5.8 | 55.4% | 2.37 | +370.4% | 6.30 |
| tol 1% | 1561 | 9.9 | 48.3% | 1.88 | +112.0% | 3.53 |
滞后与收益严格单调。**这也意味着实盘延迟会直接侵蚀收益**,是下一阶段的核心风险。
### 3.3 其余已排除的偏差
- **中枢生效时刻**step36 §1):0% 走 `end_time` 回退分支,全部用 `sure_time`
- **多空对称**step36 §3):做多 PF 3.12 / t 14.77,做空 PF 2.62 / t 12.27
→ 不是单纯吃加密货币的上涨 beta
- **时间样本外**step36 §4):2019~2022 挑参数,2023~2026 验证 →
PF 从 3.31 降到 2.44t 仍 12.29。有衰减但成立
- **滑点脆弱性**step35 §1):加到 +30bp 仍 PF 1.73 / t 10.15
- **入场后移**step35 §2):后移 1→4 根平滑衰减(PF 3.18→2.53),
未来函数会呈断崖,这里没有
- **持仓重叠**(step35 §3-4):单腿平均并发 0.01,组合 9 腿平均并发 0.1,
有仓位时间仅 8.6%
> ⚠️ **「各笔基本独立」这句原来说得太满,已修正。** 低并发确实成立,
> 但那只说明「时间上不重叠」,不等于「统计上独立」——见下方 3.31。
#### 3.31 新出场口径下的并发,以及信号之间的相关性(1m,8 币样本外,门控后)
分批出场把平均持仓从 9.9 根拉到 29 根,并发必须重算:
| 出场 | 范围 | 平均并发 | 有仓位时间 | 峰值并发 |
|---|---|---|---|---|
| 旧 SL1.5/整仓TP3/48 | 8 币组合 | 0.043 | 4.0% | 6 |
| **新 SL2/分批→8/留损2/48** | 8 币组合 | **0.120** | **10.2%** | **6** |
并发翻近三倍,绝对值仍低。**要紧的是峰值 6**——8 个币里 6 个同时在仓。
**信号在时间上扎堆,且方向高度一致:**
- ±5 分钟内有其他币信号的占 21.6%,±30 分钟 39.6%,±60 分钟 49.6%
- 同一小时内有 ≥2 个币发信号的时段占 20.9%(2262 个小时),
**其中 92.7% 方向完全一致**,完全对冲的只有 5.8%
**这不是缺陷,是信号在正常工作。** 市场同涨同跌时,同一个中枢突破形态在
各币上同时出现;若 6 个币同时发信号却方向随机,那才说明信号是噪声。
但它有两个后果:
1. **t 值有一定虚高**:有效样本量小于名义的 13929 笔。不足以推翻结论
(t 值 43 以上),但「各笔基本独立」不再是准确说法
2. **加币不产生分散**,只增加机会数量。组合波动不会随币数下降。
**仓位不能按「1% 风险 × N 个币」线性放**——峰值 6 仓 × 92.7% 同向,
8 个币就是 6% 风险压在一个移动上;平均杠杆 3.6x 时峰值 21.6 倍名义额。
要设并发上限,或按当前并发数缩放单仓风险。
§10 里「保证金约束未建模」那条在扩币前必须先做
> 好消息:§5.3 报的 12.6% 最大回撤是在真实时间序列上按实际发生顺序算的,
> **亏损扎堆已经体现在里面**,不是用独立假设推出来的。相关性没被漏掉,
> 只是不能再指望「加币能降回撤」。
#### 3.32 多空对称:这不是伪装成策略的 beta
用户问「整个市场都是正相关的,是不是很少有独立行情」。市场相关是真的,
但**这个策略不是市场敞口**(1m,8 币样本外,门控后):
| 方向 | 笔数 | 占比 | 胜率 | 净bp | PF | 均R | t值 |
|---|---|---|---|---|---|---|---|
| 做多 | 7057 | 50.7% | 71.2% | 35.4 | 4.05 | 0.968 | 43.0 |
| 做空 | 6872 | 49.3% | 71.9% | 40.5 | **4.40** | **1.006** | 43.4 |
做空还略好。所有时段净方向合计仅 +185 笔(总 13929),**长期无方向偏置**。
分年更决定性——若是 beta,2021 年做空该被打爆:
| 年 | 多 均R / PF | 空 均R / PF | 空头占比 |
|---|---|---|---|
| 2021 大牛 | 1.019 / 4.45 | **1.116 / 5.34** | 46.5% |
| 2022 熊 | 0.901 / 3.85 | 0.962 / 4.07 | 53.8% |
| 2026 熊 | 0.863 / 3.33 | 0.793 / 3.42 | 55.9% |
**2021 年做空的 PF 5.34 是整张表最高的一格**,七年里没有一年、
没有一个方向是亏的。空头占比随行情切换(牛市 46.5% → 熊市 55.9%),
说明结构识别在跟着市况调方向。与 §3.3 的 30m 结论(多 PF 3.12 / 空 2.62)一致。
#### 3.33 扩币的筛选标准(两条方向相反的约束)
1. **ATR 分布对门控阈值**(当前 8bp)——小市值币波动大,这条更容易过
2. **流动性 / 盘口价差**——决定滑点能否压进预算,小市值币更难过
**两条方向相反,所以最优区间在中间,不在两端。** 现有 11 币正好演示:
BTC ATR 中位 2026 仅 6.5bp、门控刷掉 58.5%、预算 8.58bp 垫底(**大市值输在
波动不够**);TRX 2026 门控后只剩 4.2% 的信号,当前环境基本不能做。
最好的是中间那批:ADA 22.28、DOGE 18.55、XRP 17.04。
筛选很便宜:拉候选币 1m 数据,只算 ATR 分位数与盘口价差,**不必跑策略**
就能排掉大半。
⚠️ **但样本外的说服力会被稀释**:当前最硬的证据是「8 个币完全没参与调参、
全部为正」。若新币是在同一份历史上筛出来的,它们就不再是样本外。
要保持这个性质,得留一批筛完不看、直接进实盘验证。
### 3.35 跨 venue:信号身份不迁移,但总体期望迁移(live 前置测量)
**回测全部跑在 Binance 数据上,实盘要在 Bitget 成交。** 服务器侧的
`research/live/venue_parity.py` 把这件事量了:
| 品种 | Bitget信号 | Binance信号 | 同根重合 |
|---|---|---|---|
| BTC | 47 | 38 | 42.6% |
| ETH | 39 | 35 | 28.2% |
| SOL | 48 | 29 | **14.6%** |
(5m 同向过滤后口径。SOL 差得最多是因为 tick 粗细不同:Bitget 0.001、
Binance 0.01,粗 10 倍会让 `close > high[j-1]` 大量平局不触发,
Bitget 上信号多出 74%。**这是系统性差异,不是随机扰动。**)
`signal_sensitivity.py` 进一步显示 **0.25bp 的数据扰动就能换掉一半信号**
`aggregate_robustness.py` 的结论是:**总体期望不塌**。
加 0.5 / 1 / 2bp 噪声重跑,毛均收益与 PF 都在原区间内摆动,没有单调恶化。
> **脆的是信号身份,不是 alpha。** 这决定了两件事:
> 一、滑点**不能逐笔对照 Binance 回测**,只能对照同一 venue 上的总体毛均收益;
> 二、影子测量必须跑在生产路径上(Bitget 原生数据 + Hummingbot),
> 换个运行时测出来的数就不作数。
同窗口 Bitget vs Binance 毛均收益(210 天、TP=3.0 口径):
BTC 5.87 vs 6.09、ETH 10.02 vs 12.06、SOL 8.92 vs 9.87bp。
**Bitget 一致略低 0.2~2bp,但没有量级差异,总体口径可迁移。**
⚠️ 上面那批数字用的是 `毛均收益 6.0bp`、只有同向过滤、`TP=3.0`
**不能直接当预算用**,正确的逐币预算见 `lib/shadow_budget.py` 与 5.3。
### 3.4 alpha 的来源(step32 消融)
逐条拆掉 `fast_bsp3` 的条件后发现:**alpha 完全来自缠论中枢的上下文定位,
不是「回抽结束、收盘转强」那个触发动作本身。** 触发条件只负责压低滞后。
这个结论回答了用户当时的问题(「如果有这个,那就可以去判断任何笔的端点了」):
**不能**。脱离中枢上下文,那个触发条件没有预测力。
### 3.5 出场:TP=3.0 把右尾截早了(step41
起因是用户看图指出「止盈没做好」。查下来确实如此。
先量 MFE(每笔在被止损打掉之前最远走到几个 ATR):
| 分组 | 级别 | 笔数 | 中位 MFE | ≥3ATR | ≥5ATR | ≥8ATR |
|---|---|---|---|---|---|---|
| 样本内 | 15m/30m/5m | 2309 | 3.66 / 4.30 / 4.27 | 55~62% | 41~45% | 23~28% |
| 样本外 | 15m/30m/5m | 5859 | 4.08 / 3.80 / 4.30 | 57~61% | 42~45% | 25~28% |
**中位数就已经高于 TP=3**,样本内外一致。抬高 TP 的样本外结果:
| 出场 | 胜率 | 均收益 | 中位 | PF | t值 | 剔10%PF | 均持仓 |
|---|---|---|---|---|---|---|---|
| tp3(现用) | 62.6% | +0.559% | +0.588% | 2.71 | +28.31 | 1.64 | 11.2 |
| tp4 | 58.7% | +0.692% | +0.579% | 2.95 | +29.24 | 1.71 | 15.3 |
| **tp5** | 56.5% | **+0.783%** | +0.485% | **3.12** | **+29.36** | **1.75** | 18.8 |
| tp6 | 55.3% | +0.860% | +0.369% | 3.28 | +29.17 | 1.77 | 21.7 |
| tp8 | 54.1% | +0.983% | +0.284% | 3.56 | +28.27 | 1.78 | 25.7 |
- **8 个样本外币逐个看,均收益与 PF 全部提升**;2020~2026 七年 PF 也全部提升
- 剔掉最好 10% 后 PF 仍从 1.64 升到 1.75 → 不是几笔暴利撑起来的
- t 值在 tp5 见顶(+29.36)后走平,tp6/tp8 靠的是越来越薄的右尾
- 基线复现校验:step41 的 tp3 = 5859 笔 / PF 2.71 / t +28.31 / 剔10% 1.64
对上 §2.2 的 2.73 / +28.50 / 1.66,差在千分位(同根内止损与新高并存时保守
假定止损先到),说明对比是在正确基线上做的
**代价不是零**:胜率 62.6%→56.5%,中位 +0.588%→+0.485%(过半单子变差,
输在「摸到 3ATR 没走到 5ATR 又反手止损」那批),**平均持仓 11.2→18.8 根**
§3.3 的并发数据需重算。心理上也更难拿。
#### 分批离场更优(用户提的方案,改一处后成立)
用户方案:到 3 ATR 平一半 + 止损移到开仓价,剩下跑到大目标。
**前两步对,「止损移到开仓价」是错的。** 用户随后要求把剩余半仓的止损位
也扫一遍(0 / 0.5 / 1 / 1.5 / 2 ATR),结果**严格单调,三组初始 SL 全一致**:
样本外 5859 笔,初始 SL1.5、分批→8 ATR、48 根:
| 剩余半仓止损位 | 净均收益 | 中位 | PF | t值 | 剔10%PF |
|---|---|---|---|---|---|
| 保本损(0 | +0.611% | +0.420% | 2.81 | +25.66 | 1.50 |
| 留损 0.5 ATR | +0.688% | +0.455% | 3.03 | +27.65 | 1.65 |
| 留损 1.0 ATR | +0.732% | +0.475% | 3.17 | +28.64 | 1.74 |
| **留损 1.5 ATR(不动)** | +0.754% | +0.498% | 3.23 | +29.25 | 1.79 |
| 留损 2.0 ATR(放宽) | +0.762% | +0.510% | 3.25 | +29.48 | 1.81 |
**没有中间最优点,越松越好,但增量在「不动」处饱和**
保本→0.5 加 0.077%0.5→1 加 0.044%1→1.5 加 0.022%1.5→2 只加 0.008%。
所以**止损保持原位即可,放得比原止损更宽是拿实打实的风险换千分之一的收益**。
机理与移动止损失败同源(§4):回抽到成本价是突破后的正常结构,不是信号失效。
信号已用「摸到 3 ATR」自证,此时收紧止损等于用最坏价格淘汰掉最好的单子。
**胜率与整仓 TP3 完全相同是结构决定的**:摸到 3 ATR 的单子在分批下必然是赢家
(半仓 +3 ATR,剩余半仓最坏 1.5 ATR,净 +0.75 ATR)。这正是分批相对整仓
高 TP 的价值——同一批单子在整仓 TP5 下会变成 −1.5 ATR 的亏损。
#### 初始 SL 扫描:必须用 R 倍数口径,否则会把仓位差异算成策略优势
用户要求扫 1 / 1.5 / 2 ATR 初始止损。**百分比收益会误导**:SL 越宽每笔风险越大,
固定风险仓位下仓位越小,raw % 里 SL2 看着比 SL1.5 好 68%,其中大半是仓位效应。
样本外,分批→8 ATR、留损=初始 SL、48 根,§2.3 的 1%风险 + 20倍上限 + 复利:
| 配置 | 均R | R夏普 | 最差1%R | 平均杠杆 | 最大回撤(样本外/内) | Sharpe(外/内) |
|---|---|---|---|---|---|---|
| 现用 SL1.5 整仓TP3 | +0.722 | 0.494 | 1.55 | 1.97x | 12.1% / 11.9% | 14.79 / 8.33 |
| SL1.0 分批→8 | +1.023 | 0.381 | 1.93 | 2.70x | 16.0% / 14.7% | 11.36 / 6.72 |
| **SL1.5 分批→8** | **+1.023** | 0.530 | 1.55 | 1.97x | 13.5% / 9.4% | 15.79 / 9.30 |
| **SL2.0 分批→8** | +0.943 | **0.638** | **1.37** | 1.53x | 13.1% / **6.4%** | **19.00 / 11.35** |
- **SL=1.0 明确最差**:均R 与 SL1.5 打平,但 R夏普 只有 0.381、回撤最大、
最差单笔 −1.93R。止损太紧被噪声打掉,和 §3.2「滞后是第一驱动」是一类问题
- **SL=1.5 期望值最高**(均R 1.023),**SL=2.0 风险调整最好**
R夏普 0.638、回撤 6.4%、Sharpe 11.35,样本内外一致)
- SL2 只让出 8% 的单位风险期望,换来 20% 更高的 R夏普和更好的最差单笔。
**纠正一次误判:修正仓位效应后 SL=2.0 仍然占优,不是纯粹的仓位错觉**
- 按剔10%PF 排全网格,前 14 名全是 SL=2 的分批配置,
第一名 `SL2 分批→8ATR 留损2 48根`:剔10%PF 2.26(现用 1.59)、
净 +0.920%、PF 3.84、滑点余量 61.79bp(现用 40.05bp
- 该配置样本外 **8/8 币、7/7 年**对现用全面提升,无例外
> 上表的年化数字是 §2.3 的口径 A(复利+杠杆),且把 8 个币当一个账户串起来算,
> 严重虚高,**故意不记录**。只有回撤、Sharpe、R 这些比值可以横向比。
#### 换成实际费率(2/0.8bp)后:几乎没变
step41 首轮跑的是错误的 3/1bp(见 §1.3),已用实际费率重算分析段
(feather 存的是毛收益+出场原因,不必重跑模拟):
| 组 | 口径 | 平均费 | 净均收益 | 余量bp | 剔10%PF | 均R |
|---|---|---|---|---|---|---|
| 样本外 | 现用 SL1.5/TP3/48 | 3.00→**2.00bp** | 0.579→**0.589%** | 41.34→**42.05** | 2.01→2.05 | 0.790→0.811 |
| 样本外 | 最优 SL2 分批→8 留损2/48 | 4.45→**3.01bp** | 0.941→**0.955%** | 62.17→**63.13** | 2.69→2.75 | 0.975→0.998 |
| 样本内 | 最优 SL2 分批→8 留损2/48 | 4.43→**2.99bp** | 0.866→**0.881%** | 57.15→**58.10** | 2.91→2.98 | 0.960→0.984 |
**结论一个都没变,量级也没变**(余量动了 2%)。原因还是那条:
成本固定在 bp、目标随 ATR 缩放,所以**费率只对 1m 有杠杆**——
同一次改动在 1m 上把均R 抬了 8%,在这里只有 2%。
#### ATR 门控在这三条腿上是惰性的
1m 上有效的 `ATR ≥ 8bp` 门控(见 5.3),搬到这里几乎不触发:
| 级别 | ATR中位 | <8bp 占比 | 门控后保留 | 均R 变化 |
|---|---|---|---|---|
| 5m | 33.6bp | 2.60% | 97.4% | 1.007 → 1.006 |
| 15m | 60.2bp | 0.16% | 99.8% | 0.995 → 0.992 |
| 30m | 82.5bp | 0.00% | 100% | 0.947 → 0.947 |
**无害也无用**,所以可以当全局规则统一写进执行层,不必按级别分叉:
`ATR ≥ 5 + 1.1 × taker费(bp)`,当前费率下即 ≥ 8bp。
它只在 1m 上真正起作用(那里 21.5% 的信号在阈值以下)。
---
## 4. 已验证无效 / 不要重做的方向
| 方向 | 结论 | 出处 |
|---|---|---|
| 动态止盈(小级别三买后等大级别三买再调止盈) | 无效。持仓期太短(8~13 根),大级别确认来得太罕见 | step27 |
| 移动止损(1.5 ATR 跟踪距离) | 明显劣于固定 TP:样本外 PF 2.32、剔10%PF 0.94TP=3 是 2.71/1.64)。突破后的正常回抽就能把它洗出去,和 §3.2「滞后是第一驱动」同源 | step41 |
| **分批后收紧剩余半仓的止损(保本损及一切更紧的位置)** | 直觉上该做,数据上是负的。止损位 0/0.5/1/1.5/2 ATR 严格单调,越紧越差,三组初始 SL 全一致:保本损比不动少赚 0.14%/笔,剔10%PF 从 1.79 塌到 1.50。与移动止损同源——回抽到成本价是正常结构,不是信号失效。**分批要做,止损保持原位** | step41 §3.5 |
| 初始止损收到 1.0 ATR | 5m/15m/30m 上均R 与 1.5 打平但 R夏普 只有 0.3811.5 是 0.530)、回撤 16.0%、最差单笔 1.93R。**1m 上更极端**:剔10%PF 0.78~0.99、中位收益 0.122%、回撤 25.5%。1 ATR 就是噪声宽度 | step41/42 |
| 同一中枢重复入场(二次、三次) | 质量骤降,**只做首次** | step25 |
| 缠论引擎原生 `find_all_bsp` 的 B3/S3 | 统计上呈逆势、显著亏损:胜率 27.4%(低于 SL1.5/TP3.0 的随机基准 33%)、PF 0.66、t 18.76。**不是滞后造成的**——同一组过滤器把 B4 从 1.59 提到 2.26,对它无效(0.66→0.71),且入场后移 1~4 根只是平滑衰减。它几乎不筛(627 个中枢发 625 个信号),把「价格早已离开、再没回来」的历史区间也当信号发出来。B4/S4 因此单独立类,不是它的提前版 | step30/31 |
| 刷交易额换 VIP 费率 | 成本收益不划算,见 5.3。**但前提已变**:该结论算于「原始 taker 6bp、全 taker、TP=3.0、无门控」,四个前提现在全不成立(实际 2/0.8bp、混合费率、分批出场、有 ATR 门控)。已在返 50% 的档位上,继续压缩的空间本就有限,但**若要重估需重跑,不要直接引用旧结论** | step23 |
| 线段(`Chan_XD`)做大级别 | 滞后太大,且中枢极少 | 早期,用户也这么说 |
---
## 5. 实盘落地的现状与关键约束
### 5.1 交易所:这是当前最大的未决项 ⚠️
用户实际用 **Bitget**`手续费.xlsx` 在项目根目录):
| VIP | maker | taker | 升级门槛(月交易额) |
|---|---|---|---|
| 1 | 0.020% | 0.060% | 0 |
| 2 | 0.016% | 0.040% | $3M |
| 3 | 0.014% | 0.0375% | $20M |
返还比例:**手动 60%、API 50%**。
**框架支持情况(已查证,2026-08):**
| 框架 | Bitget 永续 | 说明 |
|---|---|---|
| freqtrade(本地已装 2025.4-dev | ❌ | 源码里搜不到一处 `bitget``SUPPORTED_EXCHANGES` = binance/bingx/bitmart/bybit/gate/htx/hyperliquid/kraken/okx |
| NautilusTrader | ❌ | `nautilus-bitget` 是 0.0.0 空壳无 API;官方适配器列表(2026-06-30)无 Bitget。Bitget 只出现在第三方 Tardis 历史数据里 |
| **Hummingbot** | ✅ | `bitget_perpetual` v2.0 连接器,2026 年 Bitget 官方合作。支持单向/对冲、限价/市价,有 Perp Candles Feed |
**用户最后问的就是「还有没有其他框架支持 Bitget」,答案是 Hummingbot——
这个问题还没得到他的回应,需要先确认交易所再定框架。**
其他信息:Hummingbot V2 的 `PositionExecutor` 结构恰好匹配我们的
「入场 + 止损 + 止盈 + 超时」,架构上契合。用户的费率表里还有一列空着的 `HL`
Hyperliquid),而 Hyperliquid 在 freqtrade 和 Nautilus 都是原生支持的。
### 5.2 网络:Bitget 必须走代理 ⚠️
```
直连 失败 NetworkError: bitget GET https://api.bitget.com/api/v2/spot/public/coins
代理 OK 2.39s (http://127.0.0.1:7897)
```
盘口实测(BTC/USDT:USDT 永续):bid 79492.7 / ask 79492.8
**价差仅 0.01bp100 档深度**。即时价差成本可忽略。
**但代理会把延迟从几百毫秒放大到秒级,而 1m 那条腿的滑点预算只有 3.9bp。
实盘这条腿最终必须落到境外 VPS,否则测出来的滑点是代理的账,不是策略的账。**
### 5.3 1m 那条腿的经济性(用户的核心问题)
用户原话:「1m 测的是返佣后是否能够不亏钱」,配合「一边刷交易额,一边走大点周期」。
1m 同向信号实测:**3578 笔 / 2.28 年 / 3 币 = 524 笔/年/币,
毛均收益 +0.0991%,胜率 54.2%,平均持仓仅 8.3 分钟。**
按 Bitget 真实费率算滑点预算:
> ⚠️ **下表的费率档位是错的**:它假设原始 taker 0.12%、返 50% 得 0.060% 双边。
> 实际是原始 taker 0.040% / maker 0.016%,返 50% 后 taker 2.0bp / maker 0.8bp
> (见 §1.3)。整张表连同下面那张「四条腿对比」都**只保留跨级别的相对关系**,
> 绝对数字全部作废。
| 档位 | 双边费率 | PF=1 滑点上限 | t>2 实用上限 |
|---|---|---|---|
| VIP1 taker + API 返50% | 0.060% | **3.9bp** | ~2.5bp |
| VIP1 taker + 手动返60% | 0.048% | 5.1bp | ~3.8bp |
| VIP2 taker + API 返50% | 0.040% | 5.9bp | ~4.5bp |
| VIP1 maker + API 返50% | 0.020% | 7.9bp | ~6.5bp |
**四条腿的滑点余量对比**VIP1 taker + API 返50%,即双边费率 0.060%):
| 级别 | 笔数 | 毛均收益 | 平均持仓 | 滑点余量 |
|---|---|---|---|---|
| **1m** | 3578 | +0.0991% | 8.3 根 | **3.91bp** |
| 5m | 1984 | +0.3253% | 9.7 根 | 26.53bp |
| 15m | 689 | +0.4436% | 12.1 根 | 38.36bp |
| 30m | 340 | +0.9504% | 11.7 根 | **89.04bp** |
**1m 和 30m 的余量差 23 倍。**
> **重要判断:1m 若失败,绝不能拿它去否定 5m/30m。**
> 三条腿的成本敏感度不在一个量级上。
#### ⚠️ 上面的 3.91bp 已被 step42 推翻:那是 30m 的参数套在 1m 上量出来的
用户指出:`SL1.5/TP3.0/MAXB48` 是在 5m/15m/30m 上定的,1m 不该混用。
**成本固定 5bp,而目标随 ATR 缩放**1m 的 3 ATR 只有 0.39%、30m 是 1.87%
成本占比差 5 倍,最优 TP 不可能相同。查证后确实如此,而且原因比成本更根本:
| 级别 | MFE 中位 | ≥6 ATR | ≥10 ATR |
|---|---|---|---|
| **1m** | **5.16 ATR** | **46.1%** | **28.6%** |
| 5m | 4.29 | 37.8% | 18.6% |
| 15m | 3.98 | 34.7% | 17.0% |
| 30m | 3.94 | 36.6% | 18.7% |
**1m 以 ATR 计的右尾是其他级别的两倍。** 机理:1m 的 ATR 量的是分钟级噪声,
但信号一旦成立抓到的是几小时级别的趋势,所以「行程 / ATR」比值天然更大;
30m 的 ATR 本身已经吃进了一部分趋势。所以 TP=3 ATR 在 1m 上是个远比在 30m 上
更紧的目标——**这不是调参问题,是量纲问题**。
(MFE 本身也随初始止损变化,止损越松活得越久、右尾越长:
1m 上 SL1.0 中位 2.37 ATR、SL1.5 是 5.16、SL2.0 是 6.73。
所以 TP 和 SL 必须一起定,不能固定一个扫另一个。)
#### 1m 全网格重测(step4211 币 × 7 年,25476 笔)
> ⚠️ 本小节的表用的是**旧费率 3/1bp**(跑的时候费率档位写错了,见 §1.3)。
> 实际是 2/0.8bp,所以下面所有净收益、余量、均R **都偏保守**。
> 用实际费率重算的版本在再下一小节,**决策请用那一版**。
扫描空间:`SL ∈ {1.0, 1.5, 2.0}` × `TP ∈ {2,3,4,5,6,8,10,12}` ×
`MAXB ∈ {48, 96}` × 分批(3 ATR 减半 → 剩余目标 {5,6,8,不设} × 留损 {0,0.5,1,1.5,2})。
**结论与 5m/15m/30m 同向,但幅度更大,且 1m 上没有取舍——最优解在每个指标上都赢。**
样本外(8 个未调参币,17734 笔):
| 口径 | 胜率 | 毛bp | 净均收益 | 中位 | PF | 剔10%PF | **滑点余量** | 均R | R夏普 | 最大回撤 | 均持仓 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 现用 SL1.5 / 整仓TP3 / 48根 | 60.8% | 20.27 | +0.141% | +0.169% | 2.09 | 1.24 | **11.06bp** | +0.434 | 0.274 | 14.7% | 9.7 |
| SL1 分批→8ATR 留损1 / 48根 | 45.9% | 20.29 | +0.133% | 0.122% | 2.01 | 0.96 | 9.07bp | +0.649 | 0.225 | 25.5% | 17.5 |
| SL1.5 分批→8ATR 留损1.5 / 48根 | 58.9% | 28.60 | +0.219% | +0.132% | 2.70 | 1.49 | 15.12bp | +0.781 | 0.376 | 14.1% | 23.7 |
| **SL2 分批→8ATR 留损2 / 48根** | 67.6% | 34.90 | **+0.284%** | +0.233% | **3.41** | **2.01** | **20.12bp** | **+0.792** | **0.500** | **12.6%** | 28.4 |
(滑点余量口径已改:分子是毛收益减实际手续费,分母是 taker 名义额,
即**每条 taker 腿能承受多少滑点**。旧表按双边固定 6bp 扣、不除 taker 腿,
所以数字偏大——上一版写的 14.27bp 就是旧口径,与 11.06bp 是同一件事的两种算法。)
- **滑点余量 11.06 → 20.12bp,接近翻倍**;均 R +0.434 → +0.792+82%
- **8/8 样本外币、7/7 年份全部提升**,无例外。最弱的 2026 年:
现用余量 4.69bp / 剔10%PF 0.92(已跌破 1),最优 9.84bp / 1.45
- 样本内同向:净 +0.100% → +0.221%,剔10%PF 1.07 → 1.72,回撤 19.7% → 10.2%
- **`MAX_BARS=48` 不用改**:1m 上 96 根在同配置下一致略差(剔10%PF 2.01 → 1.95),
这点与 5m/15m/30m 不同,是 1m 唯一沿用原参数的地方
- 纯整仓高 TPTP8/TP10)毛收益更高(44~47bp)但胜率跌到 59~60%、
中位收益反而更低;分批版 PF 与剔10%PF 更好,是更稳的那一侧
**1m 上 SL=1.0 是废的**(比其他级别更明确):剔10%PF 只有 0.78~0.99
中位收益 −0.122%(一半以上的交易是亏的),最差 1% 到 −3.69R,回撤 25.5%。
1 ATR 在 1m 上就是噪声宽度,止损放在噪声里必然被反复扫。
**减仓后剩余半仓的止损位——三组初始 SL 下都是同一条单调曲线:**
| 初始SL | 保本损(0) | 留损0.5 | 留损1 | 留损1.5 | 留损2 |
|---|---|---|---|---|---|
| SL1.0 剔10%PF | 0.78 | 0.91 | 0.96 | 0.98 | **0.99** |
| SL1.5 剔10%PF | 1.23 | 1.38 | 1.45 | 1.49 | **1.50** |
| SL2.0 剔10%PF | 1.68 | 1.86 | 1.95 | 2.00 | **2.01** |
**越松越好,到「不动」(= 初始 SL)为止基本饱和**,再放宽只有 +0.01 的边际。
保本损在 SL2 上直接砍掉 0.33 的剔10%PF,是全表最差的一档。
结论与 5m/15m/30m 完全一致:**减仓后不要动止损**。
#### 逐年余量在衰减——那是波动率压缩,不是 alpha 衰减
用户看到余量逐年递减后提的假设:「是不是跟波动率有关,2026 是熊市、2025 是牛市」。
**查证成立,而且相关性高到接近恒等式。**
`余量bp``当年 ATR 中位数` 的跨年相关系数 **r = +0.989**n=7)。
| 年 | ATR中位 | 毛bp | **毛/ATR** | 余量bp(旧费率) | 均R |
|---|---|---|---|---|---|
| 2020 | 16.3 | 39.8 | 2.44 | 23.98 | 0.939 |
| 2021 牛 | **22.1** | 58.7 | 2.65 | **36.49** | 1.031 |
| 2022 | 15.4 | 36.5 | 2.37 | 21.30 | 0.876 |
| 2023 | 9.8 | 24.9 | 2.53 | 13.88 | 0.846 |
| 2024 | 11.7 | 31.3 | 2.67 | 18.07 | 0.877 |
| 2025 | 11.6 | 27.8 | 2.40 | 15.76 | 0.883 |
| 2026 熊 | **8.8** | 19.8 | 2.26 | **10.28** | 0.713 |
**关键是「毛/ATR」列:2.26~2.67,七年无趋势。** 信号质量以 ATR 计几乎没衰减。
衰减的是 ATR 本身——2021 的 22.1bp 压到 2026 的 8.8bp,掉了 60%。
余量是 bp 口径,自然跟着掉。这就是 §5.3 的论证从跨级别搬到跨时间:
**成本固定在 bp,目标随 ATR 缩放**。低波动年份等于「1m 变得更像 1m」。
保留意见:2026 的毛/ATR 2.26 与余量/ATR 1.17 都是七年最低,
**可能还有一点真衰减**,但 8 个月 1682 笔分不出是噪声还是趋势。
#### ATR 门控:低 ATR 的信号毛质量不差,是被固定成本吃掉的
按信号根 ATR 分桶看**毛** R(零成本口径),结论与直觉相反:
| ATR桶 | 毛R | 净R @2/0.8bp | 净R @6/2bp |
|---|---|---|---|
| <6bp | **1.156** | — | 0.025 |
| 6-8 | **1.160** | — | 0.527 |
| 8-10 | 1.227 | — | 0.736 |
| 12-15 | 1.203 | — | 0.870 |
| >30 | **0.940** | — | 0.829 |
**低 ATR 桶的毛 R 不但不差,还是最高的一批**1.16 vs 高 ATR 桶的 0.94)。
断崖只在扣费之后出现。所以「低波动时段信号变差」是错的说法,
正确说法是**固定成本在低波动时占比过大**。
**推论:门控阈值是费率的函数,不是市场常数。** 换 VIP 档 / 换返佣 /
换交易所都要重算,不能抄:
| 单边 taker | 最优 ATR 阈值 |
|---|---|
| 0(假想) | 5bp |
| 1.5bp | 8bp |
| 3bp | 9bp |
| 6bp | 12bp |
大致 `阈值 ≈ 5 + 1.1 × taker费(bp)`,亚线性。
#### 实际费率(2/0.8bp)下的最终口径——**决策用这一版**
| 口径 | 笔数 | 平均费 | 净bp | 余量bp | PF | 剔10%PF | 均R | R夏普 |
|---|---|---|---|---|---|---|---|---|
| 现用 SL1.5/TP3/48 | 17734 | 2.00bp | 18.3 | 13.06 | 2.73 | 1.96 | 0.702 | 0.482 |
| 最优 SL2 分批→8 留损2/48 | 17734 | 2.99bp | 31.9 | 21.45 | 4.18 | 2.85 | 0.958 | 0.639 |
| **最优 + ATR≥8bp 门控** | 13929 | 3.00bp | 36.5 | **25.39** | **4.23** | **2.98** | **0.987** | **0.662** |
逐年(最优 + 8bp 门控,样本外):
| 年 | ATR中位 | 无门控余量 | 门控余量 | 保留% | 笔/周 | 剔10%PF | 均R |
|---|---|---|---|---|---|---|---|
| 2021 牛 | 22.1 | 37.47 | 37.80 | 98.6% | 55.5 | 3.41 | 1.064 |
| 2023 | 9.8 | 14.84 | **19.45** | 63.5% | 28.4 | 2.84 | 0.982 |
| 2025 | 11.6 | 16.73 | 20.79 | 71.2% | 45.5 | 3.11 | 1.007 |
| 2026 熊 | 8.8 | 11.21 | **15.19** | 55.4% | 27.5 | 2.51 | 0.824 |
- **门控是自适应的**:高波动年份几乎不触发(2021 保留 98.6%),
低波动年份才发力(2026 保留 55.4%、余量 +36%)。不是拟合出来的常数
- **费率降低后门控的边际价值也降低**:旧费率下门控带来 +5.6% 均R,
实际费率下只有 +3.0%。阈值曲线在 5~12bp 之间很平(均R 0.972~0.987),
**8bp 是峰值但不敏感**,不必纠结精确值
- **影子交易判据:14~15bp**(2026 最差年份口径)。近两年合计是 19.17bp,
全样本外 25.39bp。**用 2026 那个数做及格线,别用平均值**
**与旧 3.91bp 的差距来源未完全对齐**:旧数字来自 step23(2.28 年数据、
3578 笔、毛均收益 0.0991%),本步是 7 年、25476 笔、毛均收益 0.2027%
且滤网口径可能不同(§5.3 原文写的是「1m 同向信号」,未提阶梯),
费率口径也已改成混合 maker/taker。**在重新对齐之前,两个数字不要混用。**
风险点:入场条件恰好是「收盘突破转强」,那一刻价格正朝我们的方向跑,
所以延迟造成的是**系统性的追高/追低,不会正负抵消**。这正是
「回测漂亮、实盘歇菜」最常见的具体机制。
### 5.4 实时计算窗口(step39,已跑完)
`research/step39_pit_1m.py`(新写,**已跑完**:3 币 × 每币 60 信号 = 180 笔抽检):
| 窗口(根) | 覆盖天数 | 同根命中 | ±3根内 | 完全消失 | 同向过滤也成立 | 假阳性 | 耗时中位 | 耗时P95 |
|---|---|---|---|---|---|---|---|---|
| **2000** | 1.4 | **100.0%** | 100.0% | 0.0% | 100.0% | **0.0%** | **0.20s** | 0.22s |
| 4000 | 2.8 | 100.0% | 100.0% | 0.0% | 100.0% | 0.0% | 0.40s | 0.42s |
| 8000 | 5.6 | 100.0% | 100.0% | 0.0% | 100.0% | 0.0% | 0.81s | 0.84s |
| 16000 | 11.1 | 100.0% | 100.0% | 0.0% | 100.0% | 0.0% | 1.68s | 1.81s |
分币种(最大窗口):BTC/ETH/SOL 各 60 笔全部 100% 命中,零偏移。
**结论:1m 在 2000 根窗口(1.4 天)就已完全饱和,单次计算 0.20s,假阳性 0%。**
比 15m/30m 的 step38 结果还干净(那里有 0.7% 假阳性)。耗时对窗口严格线性
(约 0.105ms/根),所以没有任何理由带更长的历史。
**这一步顺带解决了一个更重要的问题**1m 的回测口径本身是可信的,
`is_sure` 回撤对 1m 同样没有影响。**1m 现在唯一的风险就剩执行成本。**
⚠️ **延迟预算的分解**(这直接决定 1m 生死):
| 环节 | 实测 | 说明 |
|---|---|---|
| 信号计算 | **0.20s** | 2000 根窗口,可控 |
| 走代理访问 Bitget | **约 2.4s** | 单次 REST 往返(含建连),**这才是瓶颈** |
计算只占延迟的一小部分,**网络是主要来源**。所以影子交易器必须:
用 **WebSocket 订阅**而非轮询 REST 拉 K 线,并尽快把实盘迁到境外 VPS。
否则测到的滑点全是代理的账。
> 为什么这一步必要:step38 只验证了 15m/30m,从没验证 1m。而窗口 4000 根
> 对 15m 是 41 天、对 1m 只有 2.8 天,中枢的左边界效应完全不是一个量级。
> 窗口同时牵动两头:太短则中枢被截断信号不符,太长则算得慢延迟大滑点高。
---
## 6. 接下来要做的事(按优先级)
### ~~第 1 步:跑完 step39,确定实时窗口~~ ✅ 已完成
**结果见 5.4 节。结论:影子交易器用 2000 根 1m 窗口,单次计算 0.20s。**
完整输出在 `research/out/step39.log``step39_pit_1m.csv`,无需重跑。
(若日后要重跑:`python research/step39_pit_1m.py --symbols BTC,ETH,SOL
--probe 60 --workers 3`,约 4 分钟。瓶颈不在窗口重建,而在 `run_one` 开头
那次 120 万根 1m 的全量 `TF_DF`。)
### ~~第 2 步:向用户确认交易所~~ ✅ 已定:Bitget + Hummingbot
他已选定**先只上 1m**(在我问「先从哪条腿落地」时选的 `1m_first`)。
#### 延迟已实测完成(2026-08-27,服务器侧)
四路对照(宿主机 ccxt.pro / 宿主机原始 WS / 容器原始 WS / 容器 Hummingbot
同一批 K 线对齐),把最初测到的「Hummingbot 慢 1030ms」拆开了:
- 容器网络 2~18ms
- **Hummingbot 自身处理开销 10~30ms(不慢反快)**
- 剩下 1350~1480ms 全在上游连接器的一个 bug 上
**Bug**:Bitget 换根时推的是带两根 K 线的消息 `[上一根, 新一根]`
而上游解析写的是 `data["data"][0]`,拿到的是上一根;新一根要等下一条
单元素消息、约 1.1 秒后才进 deque。ccxt 遍历全部元素,所以没这问题。
**修法**:不能简单改成 `[-1]`——那样上一根的收盘价会永远停在换根前
约 1 秒的那次推送上,而我们的信号对 0.25bp 的扰动都会换掉一半结果,
收盘价偏一个 tick 不可接受。正确做法是处理消息里的**全部元素**:
前面的就地写回,最后一根交基类 append。
20 分钟 / 60 根配对验证:
| 币 | 上游 lag | 补丁后 lag | 上游漂移 | 补丁后漂移 |
|---|---|---|---|---|
| BTC | 1454ms | **547ms** | 0.81bp | **0.50bp** |
| ETH | 1683ms | **506ms** | 1.20bp | **0.66bp** |
| SOL | 1408ms | **642ms** | 1.29bp | **0.87bp** |
补丁后与原始 WS 只差 5~14ms,贴着理论下限;19 根已收盘 K 线 OHLCV 逐根相同。
**漂移 0.50~0.87bp 对 15.19bp 的预算只用掉 6%,延迟这一关过了。**
⚠️ 但这是**无条件漂移**(60 根里绝大多数是普通换根),而我们只在
「收盘突破转强」那一刻入场,那一刻价格正朝我们方向跑。§3.2 已量化
滞后与收益严格单调且是系统性追高。**信号根上的条件漂移预计显著更高,
影子测量必须按信号根条件化统计**,见第 3 步。
另外测的是 BTC/ETH/SOL——既是流动性最好的,又恰好是样本内那三个;
真正要跑的 8 个样本外币盘口更薄,至少补测 TRX 和 ADA。
#### 容器内可行性(同批确认)
Python 3.13.14 + pandas 3.0.5 + numpy 2.4.6,与本机 `.venv` 的 pandas 同版本。
同一份切片喂进去,三个币的**信号下标、中枢数、中枢 checksum 全部逐一相同**
容器内 0.26s/币 比本机 0.29s 还快。**chanlun 直接挂载进容器即可,
不需要另起信号服务。**(装进现有 `.venv` 走不通:Hummingbot 要
`numba>=0.61.2``aiohttp<3.14`,与本机 Python 3.14 冲突。)
2000 根 1m 够取:单次上限 1000、分两页,1m 限 30 天。
#### 补丁落地方式:子类覆盖(已定)
不改镜像,用子类覆盖连接器的解析方法,升级 Hummingbot 不冲突。
⚠️ **这个方案有一个缺口必须堵**:如果上游把被覆盖的方法改名,
子类覆盖会变成**死代码,静默退回 1.4s,不报任何错**。
所以启动时要断言基类上确实存在该方法名(有条件的话连签名一起校验),
把静默失效转成启动即失败。
⚠️ **无论用哪种补丁方式,都要有运行时 lag 探针**——补丁机制只防得住
「上游代码变了」,防不住 Bitget 再改一次消息格式:
- 每根 K 线收盘记 `本地接收时间 K线收盘时间`
- 最近 30 根取滚动中位数
- **中位数 > 800ms 告警并停止开新仓**(补丁后实测 506~642ms
理论下限约 500ms;800ms 留足抖动余量又能对 1.4s 的退化一刀切死)
要停开仓而不只是打日志,因为损失是**经济性且静默的**——
不会崩、不会报错,只会让收益慢慢变差,几周后才从统计里看出来。
### 第 3 步:写影子交易器测滑点(核心工作)
**关键判断:测滑点不需要任何交易框架。** 完整框架解决的是「如何可靠下单」,
而当前的未知量是「成交价会差多少」。所以先写一个不下单的影子跑法:
```
每根 1m 收盘 → 拉最新 K 线 → 算信号(窗口取第 1 步的结论)
→ 信号触发时抓真实盘口(bid/ask + 深度)
→ 按仓位吃单深度算出真实成交价
→ 等下一根 K 线出来,取其开盘价(回测假设的成交价)
→ 两者相减 = 真实滑点
```
**必须把时间戳拆开记录**,否则不知道该优化哪里:
| 时间戳 | 含义 |
|---|---|
| `t_close` | K 线收盘时刻 |
| `t_data` | 数据到手时刻 → 拉取延迟 |
| `t_signal` | 信号算完时刻 → 计算延迟 |
| `t_book` | 盘口快照时刻 → 下单时点 |
滑点应分解成三块:**延迟漂移**`t_book` 中间价 次根开盘价)、
**盘口价差**(ask − 中间价)、**深度冲击**(加权成交价 − ask)。
延迟漂移已单独测过(第 2 步,0.50~0.87bp 无条件),
但**信号根上的条件漂移才是要测的量**。
#### 形态已定:Hummingbot V2 controller(不再写独立 shadow.py
- 信号承载在 V2 controller 里,chanlun 挂载进容器直接用
- **chanlun 计算走 `ProcessPoolExecutor`**0.26s 是纯 CPU 且受 GIL 限制,
直接放进 async 会卡住事件循环
- 滑点测量**读连接器真实盘口**,不用 paper trade 的模拟成交
#### 必须记录的字段(否则数据回来无法对判据)
时间戳四件套,拆开才知道该优化哪里:
| 字段 | 含义 |
|---|---|
| `t_close` | K 线收盘时刻 |
| `t_data` | 数据到手时刻 → 拉取延迟 |
| `t_signal` | 信号算完时刻 → 计算延迟 |
| `t_book` | 盘口快照时刻 → 下单时点 |
**每笔还必须记 `atr_pct`**——判据是 bp 口径而 ATR 逐年变化(见 5.3),
不记 ATR 就无法把实测滑点换算成 R、也无法验证门控是否正确执行。
**统计口径三条硬要求:**
1. **只统计通过全部滤网的信号根**(同向 + 阶梯 + `ATR ≥ 8bp` 门控)。
在未过滤的根上测滑点会把判据算宽——那些单子我们根本不做
2. **条件漂移与无条件漂移分开报**。无条件值(0.5~0.87bp)已知,
要的是信号根上的值。两者的差就是「系统性追高」的大小
3. **出场腿单独统计**:止盈是 maker 不吃滑点,止损/超时是 taker 要吃。
§1.3 的费率模型按出场原因分别计费,滑点也必须这样分,
否则会把 60% 不吃滑点的止盈腿混进平均值里,低估真实成本
对判据时用**每条 taker 腿的滑点**(入场 + 止损/超时出场),
不是每笔的总滑点——余量 15.19bp 就是这个口径。
**记录与报表已在服务器侧实现**`research/live/shadow_hb.py`(采集)与
`shadow_report.py`(报表)。它的设计比研究侧曾经写过的那版好,不要重复造:
**每个信号在 0.5s / 1s / 2s / 5s / actual 五个延迟上各测一次**——同信号内的
受控对比,能直接读出「延迟压到 X 秒滑点是多少」,决策不被当前实现拖累;
另外还扫了仓位,滑点也已拆成漂移 / 价差 / 冲击。
**判据常数留在研究侧**`research/lib/shadow_budget.py`
(逐币 `BUDGET_BP``ATR_GATE_BP``LEG_IS_TAKER``lag_healthy()`
`verdict()`)。分工的理由是这些数会变——2026-08-27 一天之内预算就动了四次,
费率也改了一次,写死在 live 侧的任何一份都会静默过期。
其他实现要点:
- 数据格式必须与 `lib/data.py` 一致:`timestamp`(int64 ms), `date`(tz
Asia/Shanghai), `open/high/low/close/volume`
- 持仓也要影子管理:出场按 1m 自己的参数(3 ATR 减半 / 留损 2 / 目标 8 ATR
/ 48 根超时),出场同样记录真实盘口
- 状态要落盘,重启不能丢
### 第 4 步:根据滑点结果决策
~~旧阈值(2.5 / 3.9bp)已作废~~:那是 TP=3.0、全 taker、原始费率记错
三重错误叠加出来的,见 5.3。**新判据如下。**
前提:1m 用自己的出场参数(`SL2 / 3ATR减半 / 留损2 / 目标8ATR / 48根`
+ `ATR ≥ 8bp` 门控 + 实际费率 2/0.8bp。
**判据用 2026 那个最差年份的 15.19bp,不要用全样本的 25.39bp。**
低波动环境是当前状态,不是尾部情形。
| 实测滑点(每条 taker 腿) | 判断 |
|---|---|
| ≤ 5bp | 1m 可行,各年份都有厚余量,进真实 dry run |
| 5~10bp | 可行但只在中高波动期,门控阈值要上调(见 5.3 的费率-阈值关系) |
| 10~15bp | 仅 2021 型牛市能做,常态环境不要开 |
| > 15bp | 1m 放弃 |
**5m/30m 不受影响这一句仍成立**(余量 26bp / 89bp,且那是旧费率下的保守值)。
另外两点:
- 滑点必须用 **WebSocket** 测。代理下 REST 单次往返约 2.4s,而信号计算只要
0.20s(§5.4),REST 测出来的全是代理的账
- 入场条件是「收盘突破转强」,那一刻价格正朝我们方向跑,
**延迟造成系统性追高、不会正负抵消**(§3.2 已量化滞后与收益严格单调)
### 第 5 步(后续):1m 与 5m 分实例
用户问过「1m 和 5m 分成两个实例跑呢?」——**对,而且是硬要求**:
freqtrade 一实例只能一个 timeframe;另有资金池隔离、统计独立、
API 限流风险隔离三个好处。
⚠️ **有个坑要提前处理**:两个实例可能在同一个币上同时持反向仓位
(1m 出三卖做空、5m 出三买做多),单向持仓模式下会互相平掉或直接报错。
要么开对冲模式,要么两个实例分配不同币种(币种够多,分开还能看出品种间差异)。
---
## 7. 环境与数据
### 7.1 数据
- 位置:`data/binance/futures/{SYM}_USDT_USDT-{tf}-futures.feather`
- BTC/ETH/SOL:全周期 1m~1w2172~2543 天
- 另有 8 个样本外币种(BNB/XRP/DOGE/ADA/AVAX/LINK/LTC/TRX):
**48 个文件已全部校验,2020~2026-08-26,零缺口零重复**
- 读取统一走 `research/lib/data.py``fetch_ohlcv(symbol, tf, limit)`
本地优先,回落到 `https://provider.jackyu66.com`(该服务只有 BTC/ETH/SOL
且小周期仅 90 天,别指望它)
### 7.2 代理
本地代理 `127.0.0.1:7897`。Binance 和 Bitget 的 API 都需要它。
### 7.3 已修过的两个 bug(别再踩)
1. **时间戳精度**`lib/data.py`):不能用 `date.astype("int64")//10**6`
该值单位取决于列精度(`[ns]` 给纳秒、`[ms]` 给毫秒),对毫秒精度的文件
会把时间戳砸平,再被 `drop_duplicates` 删掉九成数据。
正确写法:`(date - pd.Timestamp("1970-01-01", tz="UTC")) // pd.Timedelta("1ms")`
2. **相邻中枢撞同一 entry_idx**:会让下游 `set_index("entry_idx")`
`InvalidIndexError`。已在 `find_fast_bsp3` 末尾按
`["entry_idx","occ","zone_i"]` 排序后 `drop_duplicates("entry_idx")` 去重。
### 7.4 跑长任务的注意事项
- 用 shell 工具的原生后台执行(`block_until_ms=0`),
**不要用 `nohup`**——早期用 `nohup` 的后台任务多次莫名消失
- 1m 全量数据(120 万根)的 `TF_DF` 约需 2 分钟,任何涉及 1m 全量的脚本
都要预留时间
- 多进程用 `ProcessPoolExecutor`,记得在子进程里重新
`sys.path.insert``warnings.filterwarnings("ignore")`
- 内存很大(用户说「随便用」),但 1m 全量 × 多进程仍可能吃掉几十 GB
---
## 8. 代码地图
### 8.1 研究库 `research/lib/`
| 文件 | 作用 |
|---|---|
| `data.py` | 数据层,本地 feather 优先 |
| `fast_bsp3.py` | **实现已移入引擎**`chanlun/analysis/fast_bsp.py`),这里只剩转发导入。所有 step 脚本的 `from lib.fast_bsp3 import find_fast_bsp3` 不用改 |
| `nested_level.py` | `build_htf_zones` 同样移入引擎并转发;`annotate_position` 仍在本地 |
| `nested_bsp.py` | `htf_fx_timeline` / `attach_htf_context`:区间套上下文。
`confirm_ts``+period` 修正 K 线收盘时刻 |
| `fx_signal.py` | 分型信号提取与背驰 |
| `breakout.py` | 事件驱动回测。`run_trades` 支持 `entry_delay`/`slippage`/
`boost_dir`(动态止盈,已验证无效) |
### 8.2 缠论引擎 `chanlun/`
- `TF_DF(df, 1, tf)` 是入口,`.dataframe` 拿到含缠论标注的 DataFrame
- `analysis/fast_bsp.py`:**核心**,自研第四类买卖点 B4/S4(滞后 2.2 根),
2026-08-27 从 `research/lib/` 搬来,回测与 web 图表现在共用同一份代码。
搬运时逐条比对过 BTC/ETH/SOL × 15m/30m 六组,中枢与信号完全一致。
**它不是「B3/S3 提前几根」**:判据(收盘价实时 vs 笔端点事后)、方向来源
(突破侧 vs 离开笔)、选样口径(627 个中枢里认 212 个 vs 引擎发 625 个)都不同。
step30 同条件对拍见 §4——同一组过滤器把 B4 从 PF 1.59 提到 2.26,对引擎
B3/S3 无效(0.66→0.71);引擎那批胜率 27.4% 低于 SL1.5/TP3.0 的随机基准 33%
是系统性反向样本,不是滞后造成的衰减
`find_fast_bsp3` / `build_htf_zones` 外,还有过滤用的 `add_zone_ladder`
(中枢阶梯,§1.4)与 `htf_fx_timeline` / `attach_htf_agree`(区间套同向,§1.1 第三层)
- `pipeline/builders/fast_bsp.py``TF_DF.cal_fast_bsp()`,把上面那套包成
`ChanFastBSP`(类型 `Chan_BSP_TYPE.B4/S4`)。大级别由同一份 df 重采样得到,
配对表 `FAST_BSP_HTF_PAIR` 就是 §1.5 那张表。
**不在 `init_TF_DF` 里默认计算**,要显式调用
- web 端:`/api/analyze` 返回 `fast_bsp_list` / `element_fast_bsp_list` /
`sub_sub_fast_bsp_list`,每条带 `htf_agree``ladder_ok` 两个过滤标志。
图上深色 B4/S4 = 两个标志都满足,浅色 = 未通过过滤
- `pipeline/builders/incremental.py`:增量更新(`init_stream` / `append_bar` /
`replace_last_bar` / `rebuild_bi_zs`)。**注释写明「最后一笔 is_sure 允许收回」**,
这是 step38 要验证的那个隐患(已证实无影响)。
实盘若要用增量而非全量重算,需重新验证
- `pipeline/orchestrator.py``TF_DF` 方法的门面
- `web/`:图表与分析服务,**无下单/订单管理功能**,实盘用不上
### 8.3 实验索引
| step | 一句话 |
|---|---|
| 1~14 | 早期探索:信号有效性、滞后测量、直接预测收益(后发现有未来函数偏差,已弃) |
| 15~18 | 区间套雏形;`fast_bsp3` 诞生(降滞后) |
| 19~20 | 级别配对批量扫描 + 并行化 |
| 21 | 最优配置的尽调(尾部依赖、成本敏感) |
| 22 | 执行压力:收盘价 vs 次根开盘、滑点 |
| 23 | **费率分档 + 刷量测算**1m 经济性的数据来源) |
| 24 | 信号聚簇:同一大级别分型下的多信号是否独立(是) |
| 25 | 同中枢重复入场(只做首次) |
| 26 | **中枢阶梯序列**(趋势 vs 盘整,印证缠论定义) |
| 27 | 动态止盈(无效) |
| 28 | **全级别矩阵**(最优配置来源) |
| 29 | 信号漏斗诊断 + 参数敏感性 |
| 30/31 | 引擎原生 B3/S3 对比 + 方向反转诊断(引擎信号因滞后而亏损) |
| 32 | **消融研究**(alpha 来自中枢上下文,非触发动作) |
| 33 | `require_touch=False` 的影响(显著提升) |
| 34 | **tol 扫描**(滞后与收益严格单调) |
| 35 | **高收益审计**(口径、滑点、并发) |
| 36 | 偏差审计(生效时刻、多空、时间样本外) |
| 37 | **跨品种样本外**8 个新币) |
| 38 | **时点重建**(无未来函数) |
| 39 | **1m 时点重建 + 窗口扫描**2000 根窗口饱和) |
| 40 | 第四类买卖点的过滤消融(web 端原始 vs 过滤) |
| 41 | **出场口径全扫**SL × TP × MAX_BARS × 分批 × 剩余半仓止损位 + 真实费率模型)。TP=3 截早了;分批要做但止损保持原位;SL=1.0 最差 |
| 42 | **1m 单独定出场**(同一张全网格,11 币 7 年 25476 笔)。1m 右尾是其他级别 2 倍,TP 不能混用;`SL2 分批→8ATR 留损2 48根` 在均R/R夏普/回撤/剔10%PF 四项全赢,滑点余量 11.06→20.12bp`MAX_BARS=48` 反而不用改 |
输出都在 `research/out/`。**2026-08-27 清理过一轮**step1~20 的输出(早期方法论
已被推翻,含未来函数偏差)与旧口径备份一并删除,只留支撑当前结论的证据。
脚本都还在,需要时可重跑。step31 及之后的 `.py` 都在 `research/` 下。
---
## 9. 与用户协作的注意事项
- **用中文回复**(用户规则)
- 用户技术判断力强,会主动纠正方向(他纠正过我对区间套的理解、
指出内存回收、指出结论印证缠论定义)。**他的领域直觉值得认真对待**
- 他反复强调**不要再调参**,要落地验证
- 他讨厌长时间等待,早期多次问「还在跑吗」「好了吗」。
长任务要后台跑并主动汇报进度,不要让他干等
- 报数字时**必须交代口径**(复利/杠杆/费率),否则会误导
---
## 10. 待办清单
- [x] **step39**:1m 时点重建 + 窗口扫描 → **2000 根窗口饱和,计算 0.20s
假阳性 0%,1m 回测口径可信**
- [ ] **对齐 step23 与 step42 的 1m 数字**3.91bp vs 11.06bp)。差距可能来自
数据年限(2.28 年 vs 7 年)或滤网(§5.3 原文只提「同向」,未提阶梯)。
§6 第 4 步的三条决策阈值全部依赖这个数字,对齐前不要据它做去留判断
- [ ] **决定是否改出场口径**(step41/42 样本外均已过,见 3.5 与 5.3)。
**出场参数要按级别分开定,不能一套走天下。** 5m/15m/30m 的首选是
「初始 SL 2.0 ATR、到 3 ATR 平一半、剩余半仓止损保持原位、跑到 8 ATR、
48 根超时」;若更看重单位风险期望而非回撤,初始 SL 用 1.5。
**1m 用同一套形状,但 SL=2.0 是无争议的最优**(均 R、R夏普、回撤、
剔10%PF 四项全赢,见 5.3),`MAX_BARS=48` 不变。
改的话 `run_trades` 要支持分批(现在只支持整仓单目标),
并重跑 step28/35/37 刷新本文档的年化/回撤数字——**不要新旧口径混用**。
1m 的并发已在 3.31 重算完(0.043→0.120,峰值 6),5m/15m/30m 尚未
- [x] **用实际费率(2/0.8bp)重算 step41/42 的分析段** → 1m 见 5.3 末尾
(均R +8%,判据 15.19bp);5m/15m/30m 见 3.5 末尾(余量只动 2%,结论不变)
- [x] **ATR 门控搬到 5m/15m/30m 验证** → 惰性(30m 触发 0%、15m 0.16%、5m 2.6%),
可作全局规则 `ATR ≥ 5 + 1.1×taker费(bp)`,当前即 ≥ 8bp
- [x] **确认交易所与框架** → Bitget + Hummingbot,先只上 1m
- [x] **测执行延迟** → 上游连接器 bug 已定位并修好,补丁后 lag 506~642ms、
无条件漂移 0.50~0.87bp,占预算 6%。见 §6 第 2 步
- [ ] **写 V2 controller + 盘口缓冲,跑影子测量**(当前第一件事)。
字段与统计口径见 §6 第 3 步,**三条硬要求别漏**:只统计过滤后的信号根、
条件漂移与无条件漂移分开报、出场腿按 maker/taker 分开统计
- [ ] **改掉 `shadow_report.py` 的 `BUDGET_BP`**(当前 BTC 0.13 / ETH 4.02 /
SOL 2.92)。那三个数由六处差异叠加而来:只有同向没有阶梯过滤、
费率按 6bp 双边 taker、`TP=3.0`、余量没除 taker 名义额、无 ATR 门控、
且 BTC/ETH/SOL 恰是 ATR 最低的三个币。正确值 8.58 / 20.64 / 16.83
直接从 `lib/shadow_budget.py` import。
**这条优先级最高**:报表里 `drift > b``drift > b*0.6` 是自动停机开关,
用错的预算会让它比真实情况早约五倍触发,误杀一个可行的策略
- [ ] **live 信号路径补上中枢阶梯过滤与 ATR 门控**。现在是
`fin = full[full["h1_agree"] == 1]`,只有同向。缺这两项测的就不是
我们要交易的那批信号,改常数解决不了
- [ ] **子类覆盖加启动断言** + **运行时 lag 探针(>800ms 停开仓)**,见 §6 第 2 步
- [ ] 补测 ADA / DOGE / AVAX 的漂移——真正要交易的是这批(预算 15~22bp)。
BTC/ETH/SOL 流动性最好,测滑点偏乐观是安全方向,但判可行性不能只看它们。
**BTC 大概率不该做 1m**ATR 中位 2026 仅 6.5bp11 币最低),
门控刷掉 58.5%,修正后预算 8.58bp 仍是最低。TRX 2026 门控后只剩 4.2%
- [ ] 收集 1~2 周影子数据,与回测口径逐笔对比
- [ ] 按 §6 第 4 步的四档判据决策 1m 是否可行
- [ ] 若可行:真实 dry run
- [ ] 补一项研究阶段的缺口:**资金费率**从未计入任何回测。
注:1m 新参数下平均持仓 28.4 根 = 28 分钟,很少跨 8 小时结算点,
这一项对 1m 影响小;30m 腿才是真问题。
30m 腿平均持仓 6.5 小时会跨结算点,需要评估影响
- [ ] **保证金约束仍未建模,且比原来估的要紧**。原注「有仓位时间仅 8.6%、
预计影响小」是旧出场口径下的;新口径是 10.2%,**峰值并发 6 且 92.7%
同向**,平均杠杆 3.6x 时峰值约 21.6 倍名义额。见 3.31。
**这条是扩币的前置条件**——加币不分散,只把峰值敞口推得更高
- [ ] 若要扩币:按 3.33 的两条标准筛(ATR 对门控 + 流动性),
并留一批筛完不看、直接进实盘验证,以保住样本外的说服力