# 缠论信号研究 → 实盘落地:交接文档 > 最后更新: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.41(30m/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.44,t 仍 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% 最大回撤是在真实时间序列上按实际发生顺序算的, > **亏损扎堆已经体现在里面**,不是用独立假设推出来的。相关性没被漏掉, > 只是不能再指望「加币能降回撤」。 **11 币全样本复核(step48,2026-08-28)**:1161 笔 / 208 天 / 实盘口径。 相邻两笔的间隔:中位 **122 分钟**,62.8% 超过 1 小时,组合平均每天仅 5.58 笔。 同时持仓数:**90.9% 的时间空仓**,7.5% 只有 1 仓,≥2 仓合计 1.7%,峰值 8。 所以「11 个币会不会同时开」的答案是:**绝大多数时候不会,但左尾是硬的**—— 7.4% 的信号与前一笔同分钟、20.7% 在 5 分钟内。而这批的方向: > 同一分钟出现多笔的时刻共 72 个,**方向完全一致的占 100.0%**(不是 92.7%)。 **这些同时开的仓不是分散,是同一笔押注被拆到几个币上做。** 名义 3 个仓位,实质是 3 倍单向敞口。两条推论: 1. **保证金不是约束,闲置才是。** 91% 时间空仓,峰值并发只占 0.1% 的时间。 「币多了保证金不够」基本不成立,真问题是资金绝大部分时间在睡觉 2. **并发上限要按「同向净敞口」设,不能按「仓位个数」设。** 同时开的必然同向,限「最多 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 倍。** **簇内顺序:看着是圣杯,其实大半是未来信息(step48)** ⚠️ 用户提出研究簇内的币种先后顺序。事后按位次切,落差很大: | 分组 | 笔数 | 胜率 | 毛R | PF | 余量bp | |---|---|---|---|---|---| | (对照)孤立单笔 | 763 | 65.5% | 0.832 | 2.83 | 11.92 | | 簇内第 1 笔 | 158 | 81.6% | **1.441** | 5.58 | 25.63 | | 簇内第 2 笔 | 158 | 72.8% | 1.085 | 3.93 | 20.09 | | 簇内第 3 笔 | 53 | 62.3% | 0.733 | 2.19 | 11.41 | **但「我是首发」= 「接下来 5 分钟没有别的币再发」,这是未来信息。** 首发赢面大恰恰因为后面真跟出来了别的币(那波行情是真的), 而"会不会跟出来"在下单那一刻不可知。 换成开仓时真正可知的信息(**过去** 5 分钟有无别的币先发): | 分组 | 笔数 | 占比 | 胜率 | 毛R | PF | 余量bp | |---|---|---|---|---|---|---| | 无前序 | 979 | 84% | 68.4% | 0.938 | 3.19 | 14.13 | | 有前序 ≥1 个币先发 | 182 | 16% | 69.8% | **1.021** | 3.71 | **21.88** | **43% 的落差塌成 8.8%,方向还反过来(跟随略优于首发)。** 但残留不是零,且有一处比毛R 更要紧:**滑点余量 14.13 → 21.88bp,高 55%**。 对 1m 这是实打实的——1m 的生死线就在执行成本上。 「谁在领跑」无稳定结构:首发率 BTC 8.6% ~ DOGE 18.4%,158 次首发摊到 10 个 币平均 15.8 次,这个离散度基本是抽样噪声。 > ⛔ **这个方向的纪律(组合空间大,样本只有 158 个簇,不设约束必然批量产出 > 假发现)**:每一个切法都必须能写成「开仓那一刻我已经知道的信息」。 > 凡用到「这一簇共几个币」「我是第几个」「簇跨度多长」的,都含未来信息, > 回测漂亮但实盘拿不到。 **✅ 唯一可交易的那条已通过样本外验证(step49)**:10 币 × 80 万根, 2025-02 ~ 2026-08 共 3941 笔,其中发现期(step48 用过的最近 208 天)1161 笔、 样本外 2780 笔。 | | 无前序 | 有前序 | 差 | |---|---|---|---| | 样本外 毛R | 1.086 | **1.275** | +0.189 | | 样本外 余量 | 18.83bp | **24.89bp** | +6.06bp | | 发现期 余量 | 14.14bp | 21.96bp | +7.82bp | **逐时段 6/6 全部同向**(余量差 12.52 / 9.10 / 3.82 / 3.89 / 6.95 / 9.25bp, 中位 8.03)。发现期毛R 差只有 0.085,**样本外反而放大到 0.189——不是过拟合 衰减,是发现期恰好偏保守**。占比很稳,各时段 13~19%。 ATR 混淆已排除:`有前序` 的 ATR 确实略高(中位 15.04 vs 13.13bp),但按 ATR 四分位分层后 **4/4 层同向**,层内余量差 3.29 / 8.39 / 5.16 / 4.14bp,与不分层 的 +6.06 同量级。且毛R 本身是 ATR 归一化指标,其 +0.19 不可能是 ATR 假象。 **可用方式**:这 16% 的信号能多容忍约 6bp 执行成本。 > ⛔ **「加仓位权重」这条已被 step51 否掉,见 §3.39。** +6bp 余量在 5bp > 滑点假设下只值约 0.011R,而这批信号 100% 发生在别的币已有仓位时—— > 加权后峰值并发 7.00 → 8.46、回撤 17.3 → 19.2,按峰值保证金归一是净负的。 > 边际收益买不起它带来的集中度。 > > 仍未测的出路是**放宽 ATR 门控**(多做几笔,而非每笔做大):ATR 最低层里 > `有前序` 余量仍有 14.92bp(对照 11.63),提示门控对它们可以更松。 > **尚未回测,先别改。** > ⚠️ **「同分钟 vs 错开」是看过数据之后才划的切法,不是事先定的。** > 208 天、158 个簇,post-hoc 切分容易切出噪声。**拿它当仓位规则前必须换一段 > 时间验证。** 目前可确定的只有「扎堆整体更好」(簇级也成立); > 「好处全集中在错开那一档」尚待样本外确认。 **❌ 信号根成交量:假设方向是反的,高量入场明显更差(step50)** 用户假设「有资金的趋势才是好趋势」,预期开仓根成交量越大越好。 成交量在信号根收盘时可知,符合上面那条纪律,是合法的可交易切法。 同一批 3941 笔,按信号根相对成交量四分位: | vr60 分位(当根量 / 前 60 根均量) | 样本外毛R | 样本外余量 | 发现期毛R | 发现期余量 | |---|---|---|---|---| | 量最低(中位 0.63) | **1.421** | **27.18bp** | 1.192 | 20.91bp | | 量中低 | 1.187 | 20.37bp | 1.133 | 19.80bp | | 量中高 | 1.067 | 18.69bp | 0.841 | 13.30bp | | 量最高(中位 5.34) | 0.794 | 13.47bp | 0.634 | 7.94bp | **单调递减,且样本内外、两套量比基准(前 10 根 / 前 60 根)全部同向。** 稳健性达到了 step49 那条的标准:**ATR 四分位 4/4 同向**(层内毛R 差 −0.327 / −0.238 / −0.347 / −0.592),**逐时段 7/7 同向**。不是 ATR 换脸。 机制在出场结构里,**伤害集中在止损命中率**: | vr60 分位 | 止盈% | 止损% | 超时% | 到 3ATR 减仓率 | 赢时均R | 亏时均R | |---|---|---|---|---|---|---| | 量最低 | 33.5 | 22.5 | 44.0 | 77.6% | 1.830 | −1.098 | | 量最高 | 26.1 | **45.3** | 28.6 | 60.9% | 1.651 | −1.106 | 亏损幅度四档全是 −1.10(止损就是止损),赢时均R 只降 10%。**止损率翻倍** 是全部损失来源。这里有个判别点:若只是「2 ATR 止损相对突然放大的波动太窄」 的尺度错配,超时单应按原比例分流进止盈和止损两侧;实际是超时(−15.4pp) **和止盈(−7.4pp)一起**流进止损(+22.8pp)。**方向本身在变差,不只是止损太窄**, 所以对策是回避而非放宽止损。 为什么直觉会反:B4/S4 是**在突破根上进场**的。大量根意味着这一冲已经由别人 的资金完成了,你在它的收盘价接手——高量不是「资金在推你」,是「你在给推 完的人接盘」。用户的直觉在中长周期趋势跟随上成立,但这个入场点站在它的 对面。**「有资金」要能获利,必须在资金到达之前进场,不是同时。** 与 `有前序` 是两件独立的事,不是同一效应换个说法(相关性很弱:有前序组 vr10 中位 1.76 vs 无前序 1.46)。2×2 交叉,最好的一档是两者叠加: | | 笔数 | 占比 | 毛R | 余量bp | |---|---|---|---|---| | 低量 × 有前序 | 253 | 6% | **1.398** | **31.00bp** | | 低量 × 无前序 | 1717 | 44% | 1.092 | 18.35bp | | 高量 × 有前序 | 382 | 10% | 1.074 | 19.62bp | | 高量 × 无前序 | 1589 | 40% | 0.988 | 16.39bp | > 尚未做的:把量比做成硬过滤(如砍掉最高四分位)要付 25% 的笔数, > 而 1m 本来就稀疏(10 币约 2.5 笔/天)。**先当仓位权重用,别当开关。** > (已由 step51 §3.39 定论:该做成开关,不是权重。) **❌ 但持仓中放量**不是**该平仓的信号——恰恰相反(step52)** 用户接着问:既然入场撞上放量是接盘,那持仓中出现放量根是不是也说明这一波 走完了、该直接平掉?测下来是反的,而且反得比入场那条还干净。 规则:持仓期间任一根 `vr60 ≥ 阈值` 就收盘市价平掉(taker)。同根内优先级 止损(盘中)> 目标(盘中)> 放量平仓(收盘)。10 币 3941 笔: | 全样本 | 毛R | 净均R | R夏普 | PF | 余量bp | 胜率 | 均持仓 | |---|---|---|---|---|---|---|---| | **基线(不看量)** | **1.068** | **0.940** | **0.627** | **3.80** | **18.45** | 71.7% | 30.0 | | vr60≥3 就平 | 0.617 | 0.475 | 0.459 | 2.81 | 7.81 | 69.7% | 11.1 | | vr60≥5 就平 | 0.858 | 0.723 | 0.568 | 3.37 | 12.85 | 71.7% | 19.8 | | vr60≥8 就平 | 1.009 | 0.878 | 0.617 | 3.69 | 16.68 | 71.9% | 26.3 | **阈值越高(触发越少)越接近基线,单调——这条曲线的最优点是「永不触发」。** 样本内外一致。「浮盈才平」的变体把胜率抬到 74.4%(基线 71.7%)但毛R 掉到 0.655,是过早止盈的教科书特征:**胜率上升、期望下降**。 根因在这里。vr60≥5 触发的 1769 笔(45%),若不平,最终: | | 占比 | 净均R | 对照:全体基线 | |---|---|---|---| | 走到 8ATR 止盈 | **45.7%** | +2.640 | 30.1% | | 最终止损 | **16.6%** | −0.422 | 33.1% | | 48 根超时 | 37.8% | +1.283 | 36.8% | **持仓中的放量根标记的是最好的那批单子,不是最差的。** 在那里平掉每笔让出 +0.484R。幸存者偏差已控(触发组平均已活 9.6 根):按基线持仓 ≥K 根分层后 **5/5 档全部同向**,放量组止盈率约为无量组两倍—— | 存活≥K根 | 放量组止盈率 | 无量组止盈率 | 放量组净均R | 无量组净均R | R差 | |---|---|---|---|---|---| | 5 | 41.2% | 19.8% | 1.443 | 0.658 | +0.785 | | 10 | 42.7% | 21.4% | 1.599 | 0.898 | +0.700 | | 20 | 34.4% | 20.3% | 1.558 | 1.106 | +0.452 | | 30 | 24.7% | 15.5% | 1.469 | 1.188 | +0.281 | **同一个事件,入场时是负的、持仓时是正的,区别只在你站在它的哪一边。** 入场那根放量你是买方,付的是资金已经推到的价;持仓中那根放量是资金来接你的 货,那是兑现不是离场。用户「有资金的趋势才是好趋势」的直觉**成立, 但作用点在持仓期而非入场点**。 > 未测的建设性方向:既然持仓中放量预示止盈率翻倍,或许该**延长 runner 目标 / > 加仓**而不是平仓。注意基线已经隐含吃到了这部分(它就是拿着不动), > 所以要多拿必须主动改目标位,**这是新参数,要重新回测**。 > R差 随 K 递减(0.785 → 0.281)说明越晚出现的放量根剩余空间越小, > 若要做,触发窗口应偏前段。 **TRX 应从币池剔除**:实盘口径 208 天只有 **5 笔**,ATR 门控几乎全刷掉 (与 §5.3「2026 只剩 4.2%」一致)。实际可用是 10 个币,不是 11 个。 逐币每天笔数:DOGE/AVAX/SOL 约 0.66,BTC/LTC 0.45,BNB 0.31,TRX 0.02。 #### 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. **流动性 / 盘口价差**——滑点能否压进预算 **两条都是及格线,不是「越高越好」的排序依据。** ⚠️ **「那就做 ATR 最大的」是错的**,两个方向都不支持: **币内**(每个币按自己的 ATR 分位)——严格单调,高 ATR 时刻更差: | 币内 ATR 分位 | 净均R | R夏普 | |---|---|---| | 最低 20% | **1.040** | **0.690** | | 40-60% | 0.982 | 0.655 | | 最高 20% | **0.878** | 0.599 | ATR 飙升往往意味着行情已走过头,噪声大、容易反抽。 (但不必加上限门控:最高分位的 0.878 仍然是好交易,砍掉只是白丢机会。) **跨币**——看似正相关(ATR vs 净均R r=+0.684),但**剔除 BTC 后掉到 +0.469**, 且其余 9 币的净均R 区间只有 0.935~1.024(极差 0.089)。BTC 自己 0.787, 明显低于所有其他币。BTC 差更可能因为它是最大最有效的市场、结构性机会少, 把它的低 ATR 与低 R 读成因果是过度拟合一个点。 **ATR 唯一真正提供的是 bp 余量**(r=+0.85,剔 BTC 后仍 +0.85)。 但这是**机械关系**——余量按定义随 ATR 缩放。它有用(滑点大致固定在 bp, 余量高就能扛更多滑点),但不代表 ATR 高的币「更赚」。 ⚠️ **追高 ATR 还会撞上这份数据看不见的流动性墙。** 现有 11 币全是头部流动性; 高 ATR 通常意味着小市值、盘口更薄,多出来的余量很可能被滑点吃回去。 把在头部币上成立的关系外推到另一个流动性区间,是最容易翻车的那类外推。 现有 11 币的实际情况:BTC ATR 中位 2026 仅 6.5bp、门控刷掉 58.5%、 预算 8.58bp 垫底;TRX 2026 门控后只剩 4.2% 的信号,当前环境基本不能做。 筛选很便宜:拉候选币 1m 数据,只算 ATR 分位数与盘口价差,**不必跑策略** 就能排掉大半。 ⚠️ **但样本外的说服力会被稀释**:当前最硬的证据是「8 个币完全没参与调参、 全部为正」。若新币是在同一份历史上筛出来的,它们就不再是样本外。 要保持这个性质,得留一批筛完不看、直接进实盘验证。 #### 3.34 组合最优解:选币的杠杆很小,币数的杠杆在前 6 个 穷举每个 N 的最优子集(1m,门控后,固定日历,只扣手续费不扣滑点): | 币数 | 最优子集夏普 | 增量 | 年化R | 若信号独立应有 | 捕获比例 | |---|---|---|---|---|---| | 1 | 9.94 | — | 323 | 9.94 | 100% | | 2 | 13.15 | +3.21 | 611 | 14.06 | 94% | | 4 | 16.93 | +1.61 | 1174 | 19.88 | 85% | | 6 | 18.87 | +0.78 | 1672 | 24.35 | 77% | | **8** | **19.87** | +0.38 | 2188 | 28.11 | 71% | | 11 | 20.22 | +0.02 | 2690 | 32.97 | **61%** | **三条结论:** 1. **边际收益在 6~8 币后归零。** 8→11 只换来 +0.35 夏普(+1.8%), 但年笔数多 500+,成本与运维复杂度是实打实的。 2. ~~**具体选哪几个几乎不重要。**~~ 165 个 8 币组合:最好 19.87(ADA/AVAX/ DOGE/ETH/LINK/LTC/SOL/XRP)、最差 17.61、中位 18.66,极差 2.26(12%)。 ⚠️ **这一条已被 §3.37 推翻**——它忽略了盘口冲击。计入后币间差距是 5 倍, 且排序近乎翻转(AVAX 从必选变成必剔,BTC 反而可用)。**选币很重要。** 3. **相关性吃掉近 40% 的分散化收益**——捕获比例从 100% 单调降到 61%。 与 3.31 的「92.7% 同向」一致:加币是加机会,不是加分散。 ⚠️ **并发保证金不是瓶颈,此前的担心是错的。**(早前按事件平均算出 0.92, 高估了——事件在繁忙时段聚集。时间加权才是对的口径。) | | 时间加权均并发 | 有仓时间占比 | 有仓时均并发 | p99 | 最大 | |---|---|---|---|---|---| | 全样本 11 币 | 0.15 | 12.3% | 1.24 | 2 | 9 | | 2026 11 币 | 0.11 | 9.1% | 1.26 | 2 | 8 | **资金 88% 的时间闲置**,扩币在保证金维度有大把余地。峰值 9 仍要按 3.31 的同向假设做限额,但那是尾部风控,不是容量约束。 ⚠️ **上表夏普只扣手续费、未扣滑点,绝对值虚高(年化 20 不可信), 只能用于相对比较。** 真正的约束是实测滑点 vs 预算——见 5.3 与 3.35。 按杠杆排序:**滑点是生死(100%)> 币数 1→6(+90%)> 选哪几个(±12%,基本是噪声)**。 #### 3.36 成本模型的已知缺口:滑点被当成了常数,但它是仓位的函数 ⚠️ **全文所有预算数字都假设滑点是固定 bp,与下单规模无关。这是错的** (用户 2026-08-28 指出)。且这个错误不是随机的——它系统性偏袒低 ATR 端。 固定分数下单意味着 **名义额 = 风险额 / (2·ATR),即仓位 ∝ 1/ATR**, 而预算(bp)∝ ATR。**两者方向相反:** | ATR 桶 bp | 中位杠杆 | 中位名义额(10万本金、每笔冒1%) | 预算 bp | |---|---|---|---| | 8-12 | 5.0x | **$502,029** | **13.7** | | 12-16 | 3.6x | $362,362 | 19.5 | | 16-22 | 2.7x | $269,999 | 23.1 | | 22-30 | 2.0x | $198,172 | 29.3 | | >30 | 1.3x | **$129,065** | **52.1** | **低 ATR = 单子最大 + 预算最小 + 滑点随单子变大 = 三重挨打。** 所以按固定 bp 算出的逐币/分桶预算,**系统性低估了低 ATR 端的劣势**。 ✅ **ATR 门控因此比原论证更有价值**:被刷掉的信号中位杠杆 **8.9x、p90 17.6x** (保留的是中位 3.2x、p90 5.3x)——门控砍掉的正是最大的单子。 这比「固定成本占比高」是更强的理由。 **三条待办(都属于「数据回来前不做、回来后会后悔」):** 1. **影子交易要记录信号时刻的盘口快照(≥10 档),不只是成交价。** 有深度就能离线算任意仓位的冲击成本——**一次测量回答所有资金量级**, 否则换个规模就得重测。若用极小量或 paper 测,得到的只是 `价差/2 + 漂移` 这个下界,必然乐观。 2. **资金容量要算出来,不要等实盘暴露。** 预算 20bp 蕴含一个资金上限。 中位名义额 $32 万、p90 $53 万,在 ADA/LINK/AVAX 的 1m 永续盘口上不是零头。 3. **maker 腿的仓位问题是成交率,不是冲击。** 回测假设 3 ATR 与 8 ATR 的限价单全额成交。$30 万挂单可能只部分成交,**而部分成交会改变分批出场 的收益结构**(§3.5 的结论建立在全额成交上)。需在影子数据里验证。 #### 3.37 计入盘口冲击后,币种排序几乎翻转 —— §3.34「选哪几个不重要」作废 ⚠️ Bitget USDT 永续实时盘口(2026-08-28 01:30 HKT,5 次快照取中位)。 **净预算 = 回测预算 − $300k 单边吃单冲击**($300k ≈ 10万本金 × 中位杠杆 3.2x): | 币 | 价差bp | ask深度 | $300k冲击 | 回测预算 | **净预算** | 冲击占预算 | |---|---|---|---|---|---|---| | SOL | 0.09 | $3.3M | 2.82 | 28.3 | **25.5** | 10% | | DOGE | 1.12 | $13.5M | 4.35 | 28.5 | **24.1** | 15% | | ETH | 0.04 | $19.4M | 0.27 | 20.8 | **20.5** | 1% | | XRP | 0.68 | $14.9M | 4.82 | 24.5 | **19.7** | 20% | | BTC | 0.01 | $4.5M | 0.26 | 14.6 | **14.3** | 2% | | LINK | 0.84 | $2.8M | 12.20 | 26.2 | 14.0 | 47% | | ADA | 4.62 | $6.3M | 9.70 | 23.5 | 13.8 | 41% | | LTC | 1.99 | $3.2M | 11.13 | 23.6 | 12.5 | 47% | | AVAX | 1.33 | $3.0M | 23.65 | 29.0 | **5.3** | **82%** | **§3.34 的「165 个 8 币组合极差仅 12%、选哪几个不重要」是错的**——那个结论 完全建立在忽略流动性上。计入冲击后币间差距是 **5 倍**(25.5 vs 5.3)。 **排序近乎翻转,根因是 ATR 与流动性负相关**,所以按预算(∝ATR)排出的序 恰好是按流动性排的逆序: - **AVAX 原始预算最高(29.0)、净预算最低(5.3)**。它在最优 8 币里入选频率 95%,实际最该剔除。5 次快照 22.3~24.7bp,不是抽样噪声 - LINK / ADA / LTC(入选频率 95%/95%/70%)净预算全掉到 12~14 - **BTC 此前判为「中性」,实际净预算 14.3,反而优于 LINK/ADA/LTC**—— 原始预算最低但冲击几乎为零 这就是 §3.33 定性警告过的「流动性墙」,现已量化,且比预想严重。 ✅ **真正的结论:可用币种是资金量的函数,不存在固定的最优币表。** 按 $100k 名义额(≈$32k 本金)重算,净预算回到 ADA 18.0 / LINK 17.2 / LTC 16.5 / AVAX 11.6——除 AVAX 外全部健康。**扩币前先定本金。** **当前运行点:本金 < $2k(用户 2026-08-28 确认,目的是先跑通流程)。** 在这个规模上 **§3.37 的重排序不适用**——单笔仅 $6.4k,冲击占预算 1~8% (连 AVAX 也只有 8%),**9 个币全部可用,§3.34 的「选哪几个不重要」重新成立**。 分水岭在 **$20k~$50k 本金**:以上才需要收缩到 SOL/DOGE/ETH/XRP/BTC (这 5 个在 $2k~$200k 全区间冲击 ≤21%,是与资金量无关的安全核心)。 冲击占预算比例(按本金,杠杆 3.2x): | 币 | $2k | $10k | $20k | $50k | $100k | |---|---|---|---|---|---| | BTC / ETH | 0% | 0% | 0% | 0% | 0~2% | | SOL | 1% | 3% | 4% | 5% | 6% | | XRP / DOGE | 5~6% | 9~10% | 12% | 15% | 16~17% | | LINK | 4% | 11% | 16% | **31%** | **46%** | | LTC | 12% | 22% | **30%** | **41%** | **57%** | | ADA | 20% | **28%** | **29%** | **38%** | **51%** | | AVAX | 8% | **28%** | **43%** | **70%** | **91%** | ✅ **小资金无操作性障碍**:Bitget 最小下单量在 $300 本金都不卡, 半仓取整误差最差 2.4%(ETH @ $500),其余 <1%。 ⚠️ **但 $2k 跑通 ≠ 验证了策略容量。** 小资金下有两处系统性偏乐观、且不可外推: (a) 冲击≈0,测出的滑点只剩 `价差/2 + 延迟漂移`,几乎必然通过 15.19bp, 但不能推出 $50k 也通过;(b) **maker 腿成交率也是假的好**——$3.2k 挂单几乎 必然全成,$150k 不一定,而 §3.5 分批出场的收益结构全建立在全额成交上。 **→ 这正是「记录盘口快照」的价值:它是唯一能让小资金数据回答大资金问题的东西。** 只记成交价的话,每上一个资金台阶都得重测一遍。 $2k 阶段真正该盯的只有两件:**延迟与漂移**(与规模无关,结论长期有效)、 **中枢阶梯 + ATR 门控必须在线上生效**(否则测的是另一批信号,与回测不可比)。 ⚠️ **三条保留**:(1) 单一时段快照,盘口有日内周期,需跨时段采样; (2) **信号恰在波动放大时触发,那时盘口更薄——上表是乐观下界**; (3) 只算了入场 taker 腿,止损/超时同为 taker,maker 腿则是成交率问题。 第 (2) 条意味着这批静态数字**不能替代影子数据**,只是把「该测什么、该剔谁」 提前定下来了。 ### 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.38 浅色信号(未过双过滤)在 1m 上不能做——差在毛质量,不在成本(step44) 用户观察「图上很多浅色 b4/s4 的入场价看着也很有优势」。step40 做过同类消融但 只覆盖 15m/30m 且用旧口径(5bp 平摊、1.5/3.0/48),1m 从未验证。 `research/step44_filter_ablation_1m.py`,8 币 × 30 万根,当前最优 1m 出场 (SL2.0 / 3ATR 减半 / runner 8ATR / rstop 留原位 / 48 根),真实费率, ATR ≥ 8bp 门控后共 2302 笔: | 口径 | 笔数 | 胜率 | 毛R | 净均R | 剔10%PF | 滑点余量bp | |---|---|---|---|---|---|---| | **深色**(同向 ∧ 阶梯) | 955 | 68.3% | **0.933** | 0.798 | 2.35 | **15.07** | | **浅色**(任一不过) | 1347 | 44.3% | **0.155** | 0.009 | 0.74 | **0.10** | | — 只过阶梯 | 445 | 52.1% | 0.419 | 0.279 | 1.03 | 4.18 | | — 只过同向 | 265 | 38.1% | **−0.029** | −0.176 | 0.59 | **−2.15** | | — 两个都不过 | 637 | 41.4% | 0.046 | −0.103 | 0.62 | −1.64 | **关键是「毛R」列——那是扣任何成本之前的数字。** 浅色 0.155 vs 深色 0.933, **差 6 倍**。所以不存在「信号本来不错、被手续费吃掉」这回事:浅色的跟随性 本来就差。浅色扣完手续费只剩 **0.10bp**,任何滑点都会让它亏;逐币 8 个里 4 个已为负(ETH −1.98 / LTC −2.93 / DOGE −1.05 / XRP −0.14),最好的 ADA 也才 2.41bp。 **为什么肉眼看着有优势**:(1) 入场价确实是好的,信号标的是真实局部结构; (2) 但价格不跟随,毛R 0.155 覆盖不了 2 ATR 的止损——**入场点好 ≠ 价格继续 朝你走**;(3) 44.3% 的胜率意味着近一半确实走对,扫图时看到的就是这些, 走错的当时形态一样好看。 ⚠️ **新发现:1m 上阶梯过滤远比同向过滤重要,与 30m 相反。** 只过阶梯 +4.18bp,只过同向 **−2.15bp 且毛R 为负**;而 30m 上同向单独就能把 PF 从 1.59 提到 1.85(§4 step30)。**两个过滤器的相对价值随级别翻转, 不要跨级别套用滤网结论。** ### 3.39 因子化仓位:量因子该做成**开关**不是权重,前序因子根本不该进仓位(step51) 用户提出把 §3.31 的两个分层结论(`有前序` +6bp 余量、低量根 +14bp 余量) 做成仓位/杠杆因子。做了组合层面的评估,结论与分层表给人的印象**不一致**, 两个因子的命运完全相反。 **方法上刻意设的三道约束**(分层结论转仓位规则最容易翻车的地方): 1. **阈值取整数、权重取整数比,一个参数都不搜。** vr60 切在 1 / 2 / 4, 权重 1.5 / 1.25 / 0.75 / 0.5。观测到的四分位边界在不同时段并不一致 (样本外 0.63/1.26/2.28/5.34,发现期 0.62/1.48/3.11/7.97), 用样本分位数等于在同一批数据上既发现又调参。 2. **权重归一化到均值 1**,各方案投出去的平均资金相同,均R 才可比。 3. **判据是四项一起看**:均R / R夏普 / 最大回撤 / **峰值加权并发**。 只看均值必然误判——加权一定抬方差,且 `有前序` 按定义就发生在别的币 已有仓位时,给它加权就是在敞口最集中的时刻加杠杆。 全样本 3941 笔,假定滑点 5bp/taker 腿: | 方案 | 盈亏平衡滑点 | R夏普 | 最大回撤R | 峰值并发 | 总R/峰值并发 | |---|---|---|---|---|---| | 等权(现状) | 16.7bp | 0.420 | 17.3 | 7.00 | 371.4 | | 仅量因子(加权) | 18.2bp | 0.442 | 12.7 | 7.98 | 367.4 | | **仅前序因子** | 17.0bp | 0.421 | **19.2** | **8.46** | **312.6** | | 两因子相乘 | 18.6bp | 0.446 | 14.3 | 9.48 | 314.6 | | **✅ 硬砍高量档(vr60 ≥ 4 不做)** | **18.6bp** | **0.496** | **11.2** | **6.00** | **389.3** | | 硬砍 + 前序权重 | 19.0bp | 0.500 | 11.0 | 7.19 | 332.1 | **① `有前序` 不能做仓位因子——它的边际收益买不起它带来的集中度。** 盈亏平衡滑点只 +0.3bp,但峰值并发 7.00 → 8.46、回撤 17.3 → 19.2, **按峰值保证金归一后是净负的(371 → 313)**。原因是量纲:+6bp 余量在 5bp 滑点假设下只值约 0.011R,而它 100% 发生在已有并发仓位的时刻。 §3.31 说的「可用于加仓位权重」**到此作废**——那条余量优势是真的, 但兑现方式不是仓位。(可能的出路是对这批放宽 ATR 门控,即多做几笔而非 每笔做大,那条仍未测。) > ⚠️ **本节推荐的开关(`vr60 ≥ 4 不做`)已被 §3.391 取代。** > `mom60 ≥ 7 不做` 在盈亏平衡滑点、R夏普、回撤、总R、保留笔数五项上全面更优。 > 本节关于**「开关优于权重」「前序不该进仓位」「优势形态是削尾」**三条方法论 > 结论仍然成立,只是最佳的那个开关换了个因子。 **② 量因子「不做」优于「少做」。** 加权版 R夏普 0.442、硬砍版 0.496; 回撤 12.7 vs 11.2;峰值并发 7.98 vs **6.00**。硬砍付出笔数 −22% (3941 → 3061)、总R −10%,换来回撤 −35% 和并发 −14%。 **并发下降这一点单独就值**:§3.31 已把峰值敞口列为扩币的前置约束, 砍掉高量档等于同时松开这个约束。 **③ 优势的形态是「削尾」不是「抬均值」。** 均R 差在各滑点档上几乎恒定 (0.085 → 0.082),但**基数在塌**,所以相对优势随成本上升而放大: 15bp 处总R/回撤 2.57 → 11.86(4.6 倍),因为回撤从 150.5 掉到 60.3。 **这两个因子买的是尾部风险,不是收益。** ⚠️ **可操作口径要用发现期的数,不是全样本。** 盈亏平衡滑点样本外 18.2bp、 发现期只有 **13.5bp**(硬砍后 15.6bp)——差距就是 2026 的 ATR 压缩(§5.3)。 发现期才代表当前波动环境。13.5bp 与 `shadow_budget.BUDGET_PORTFOLIO_2026` 的 15.19bp 同量级,互为印证。**影子测量要对标的是 13.5 / 15.6,不是 18.6。** ### 3.391 开仓前那段:量要小、势要有但不能过头(step53)⭐ 用户提出看开仓**之前**的量与趋势方向。这是第三个位置——step50 管信号根、 step52 管持仓中,这里管入场前。 **先纠正一个错的心智模型(我和用户都以为的)**:B4/S4 不是回抽后进场。 `mom10` 中位 **+3.47 ATR**,为负的只占 **6%**。也就是说信号触发时,价格在前 10 根里已经顺着你的方向走了三个多 ATR,**你的 2 ATR 止损是架在一段已经走完 的行情后面**。所谓"回抽后转强"里的回抽,幅度远小于它之前那一冲。 这一条也解释了 step50:信号根是突破根,放量 = 追在最后一棒上。 **① 入场前的量:单调,越小越好,且独立于信号根的量** | vpre10(前 10 根量均值,已 shift(1) 不含信号根) | 样本外毛R | 样本外余量 | 发现期毛R | 发现期余量 | |---|---|---|---|---| | Q1 最低(中位 0.61) | **1.474** | **27.25bp** | 1.074 | 17.85bp | | Q2 | 1.266 | 23.45bp | 1.184 | 19.76bp | | Q3 | 1.156 | 21.37bp | 1.033 | 18.61bp | | Q4 最高(中位 2.21) | 0.573 | 8.51bp | 0.510 | 6.04bp | vpre30 同形状。**控 `vr60` 后仍成立**:信号根低量层里毛R差 −0.284、 高量层 −0.541,与 `vr60` 相关只有 **+0.301**,不是同一件事换个说法。 **② 入场前的动量:驼峰形,不是单调** | mom60(前 60 根顺向动量,ATR 单位) | 样本外毛R | 样本外余量 | 发现期毛R | 发现期余量 | |---|---|---|---|---| | Q1 最低(中位 0.33) | 1.252 | 22.60bp | 1.180 | 19.47bp | | **Q2(中位 3.14)** | **1.473** | **28.68bp** | **1.355** | **26.26bp** | | Q3 | 1.141 | 20.49bp | 0.981 | 16.23bp | | Q4 最高(中位 8.33) | 0.603 | 8.65bp | **0.284** | **1.44bp** | mom10 同形状(Q2 最好、Q4 最差),样本内外一致。**势要有——完全没动过的 Q1 也不如 Q2;但不能过头——Q4 在发现期余量只剩 1.44bp,等于不能做。** ⚠️ 驼峰形意味着**中位数二分法会把它测没**:控制表里 mom10 在"信号根低量"层 的毛R差是 +0.044(看着无效),那是因为二分把 Q1+Q2 和 Q3+Q4 各自平均了。 **对非单调因子不要用中位数分层做检验。** **③ 逆向进场(真回抽)很差,但只占 6%** | | 笔数 | 占比 | 毛R | 余量bp | |---|---|---|---|---| | 顺向 × 缩量 | 1851 | 47% | **1.350** | **24.63** | | 顺向 × 放量 | 1872 | 48% | 0.855 | 14.35 | | 逆向 × 缩量 | 119 | 3% | 0.615 | 7.81 | | 逆向 × 放量 | 99 | 3% | 0.362 | 3.61 | **④ `mom60≥7 不做` 全面优于 §3.39 定的 `vr60≥4 不做` —— 换开关** 发现期(当前波动环境),阈值取整数不搜参: | 方案 | 保留 | 盈亏平衡滑点 | R夏普 | 回撤R | 总R | |---|---|---|---|---|---| | 等权(现状) | 100% | 13.5bp | 0.325 | 16.8 | 596.6 | | 砍 vr60≥4(§3.39 的建议) | 69% | 15.6bp | 0.411 | 10.8 | 514.9 | | **✅ 砍 mom60≥7** | **78%** | **17.5bp** | **0.476** | **7.7** | **657.7** | | 砍 vr60≥4 + mom60≥7 | 58% | 18.3bp | 0.515 | 8.0 | 527.8 | | 三个都砍(+vpre10≥1.5) | 42% | 20.0bp | 0.593 | 6.5 | 430.4 | **`砍 mom60≥7` 在每一项上都赢过 `砍 vr60≥4`,还多留 9 个点的笔数。** 更要紧的是它**总R 比不砍还高**(657.7 vs 596.6):被砍掉那 22% 的单子期望为负, 砍掉不是花钱买稳健,是纯赚。样本外同向(21.4bp / 0.588 / 1923.6, 对照砍 vr60 的 19.8 / 0.529 / 1820.8)。 **选哪个方案取决于真实滑点,这是决策表**(发现期总R): | 真实滑点 | 0bp | 5bp | 8bp | 10bp | 12bp | 15bp | 18bp | 20bp | |---|---|---|---|---|---|---|---|---| | 等权 | **946** | 597 | 387 | 247 | 107 | −102 | −312 | −452 | | 砍 mom60≥7 | 921 | **658** | **500** | **394** | **289** | 131 | −28 | −133 | | 三个都砍 | 574 | 430 | 344 | 287 | 229 | **143** | **57** | −0 | **5~12bp 区间 `砍 mom60≥7` 通吃**,那正是最可能的落点。只有真实滑点 ≥15bp 才该上"三个都砍",而那时策略本身已经在生死线上。**影子测量出数之前不用选, 但要知道选项长这样。** > 阈值 `mom60 ≥ 7`、`vpre10 ≥ 1.5` 是贴着 Q4 边界取的整数,不是搜出来的, > 但也**不是完全无关于数据**(我看过分位表才取的整)。真上线前应在更早的 > 时段上确认阈值附近没有断崖式敏感。 ### 3.392 时段(亚/欧/美)没用,周日有效但已被 mom60 吃掉(step54) 用户问哪个时段更容易盈利。答案分两半:**时段这个切法本身无效,但换成星期几 就有东西——只是那东西不该做成新过滤。** **① 亚/欧/美三段:差异在噪声量级,且美盘两个时期反号** | UTC 0/8/16 三等分 | 笔数 | 毛R | 余量bp | 中位ATRbp | 样本外毛R | 发现期毛R | |---|---|---|---|---|---|---| | 亚盘 | 1249 | 1.127 | 18.18 | 12.5 | 1.153 | 1.057 | | 欧盘 | 1414 | 1.006 | 18.43 | 14.1 | 1.044 | 0.926 | | 美盘 | 1278 | 1.079 | 18.73 | 13.6 | **1.158** | **0.885** | **美盘在样本外是最好的一段、在发现期是最差的一段**,这是噪声的典型指纹。 段间毛R极差 0.122,**置换检验 p = 0.0895**——随便把 24 小时切成三份, 有 9% 的概率切出这么大的差。按真实开盘时刻切五段(含欧美重叠单列)结论一样。 ⚠️ 这里**不是**输给了 ATR 混淆。控 ATR 后段间极差 0.101 / 0.108,和无条件的 0.122 几乎一样——时段效应不是 ATR 的代理,它只是本来就小。逐小时表里最差的 20 UTC(0.802)与最好的 22 UTC(1.262)差得很大,但每小时只有 108~275 笔, 是噪声,别照着挑时辰。 **② 星期几才有信号,集中在周日** | | 笔数 | 胜率 | 毛R | PF | 余量bp | 样本外毛R | 发现期毛R | |---|---|---|---|---|---|---|---| | 周五 | 601 | 74.7% | **1.228** | 4.78 | 23.44 | 1.359 | 0.862 | | 周四 | 623 | 73.5% | 1.150 | 4.56 | 20.64 | 1.104 | 1.252 | | 周日 | 470 | 66.6% | **0.893** | **2.76** | **13.08** | 0.955 | 0.772 | | 工作日 | 3133 | 72.6% | 1.095 | 4.01 | 19.38 | 1.151 | 0.959 | | 周末 | 808 | 67.9% | 0.963 | 3.08 | 14.86 | 0.983 | 0.920 | 置换检验:周末 vs 工作日 **p = 0.0246**,周日 vs 其余 **p = 0.0079**。 方向在两个时期一致,10 个币里 7 个周末更差(BTC 最甚 −0.336)。 ⚠️ 但**幅度在发现期塌了大半**(工作日−周末:样本外 −0.168 → 发现期 −0.039)。 > ⛔ **这条在长样本上没复现,而且符号翻转——见 ⑤,按噪声处理。** **③ 关键:周末的劣势有一半住在 `mom60≥7` 那批单子里,step53 已经砍掉了** | | 周末 vs 工作日 | 周日 vs 其余 | |---|---|---| | 过滤前 | −0.132 | −0.199 | | **施加 `mom60<7` 后** | **−0.058** | **−0.104** | 注意重叠**不在笔数上**:`mom60≥7` 在周末占 22.9%、工作日 21.3%,几乎一样。 是**伤害**重叠——周末真正亏钱的是那些追已走完行情的单子。周末流动性薄, 追高的代价被放大,这跟 §3.391「势不能过头」是同一件事在另一个维度上的投影。 **④ 所以不要加这条过滤。决策表(发现期总R):** | 方案 | 保留 | 0bp | 5bp | 8bp | 10bp | 12bp | 15bp | |---|---|---|---|---|---|---|---| | 等权 | 100% | 946 | 597 | 387 | 247 | 107 | −102 | | 砍周日 | 86% | 844 | 543 | 362 | 241 | 120 | −60 | | 砍周末 | 78% | 747 | 473 | 309 | 200 | 90 | −74 | | 砍欧盘 | 60% | 579 | 363 | 234 | 148 | 61 | −68 | | **砍 mom60≥7(§3.391)** | 78% | **921** | **658** | **500** | **394** | **289** | **131** | | 砍 mom60≥7 + 周日 | 68% | 804 | 574 | 435 | 343 | 251 | 113 | **`砍 mom60≥7 + 周日` 在 0~20bp 每一档都输给 `砍 mom60≥7` 单用**——叠加只是 白丢 10% 的笔数。单用「砍周日」也要到 12bp 以上才赢过等权。 **⑤ 长样本复核(用户追问"你用的是长时间周期分析的?"):时段坐实无效, 周末效应没复现且符号翻转** 1m 只能追到 18.5 个月 / 3941 笔,而上面的结论是"没效果"——**没效果最怕样本 不够**。改用 step41 那份 5m/15m/30m:**2019-09 → 2026-08,2525 天,8168 笔**, 过滤口径相同(`h1_agree==1 & push` 就是深色),出场配置 `s2_so8_k2_m48` 正是实盘那套 2/3/8/2/48。 时段:**三个周期全部远离显著**—— | | 笔数 | 段间毛R极差 | 置换 p | |---|---|---|---| | 5m | 5434 | 0.046 | **0.6185** | | 15m | 1821 | 0.072 | **0.6687** | | 30m | 913 | 0.176 | **0.3105** | | 合并 | 8168 | 0.036 | **0.6445** | 1m 那个 p=0.0895 现在看清楚了:**它就是零分布里运气略好的一条尾巴。** 周末:**沿相反方向翻转**—— | | 周末−工作日 毛R | 置换 p | |---|---|---| | 1m(18.5 个月) | **−0.132** | 0.0246 | | 5m(7 年) | **+0.014** | 0.7616 | | 15m(7 年) | **+0.142** | 0.0710 | | 30m(7 年) | −0.006 | 0.9546 | | 合并(7 年) | **+0.041** | 0.2784 | 周日同样翻转:1m 上 −0.199(p=0.0079),合并长样本 **+0.033**(p=0.4774)。 ⚠️ 这不是严格的样本外复现——1m 与 5m 的信号是不同的总体,不是同一批交易换个 时间窗。但**若"周末流动性薄所以吃亏"是真的市场结构效应,它没有理由只在 1m 上 出现、在 5m 上还反号**。结合本步一共跑了 ~35 个分组比较(24 小时 + 两套时段 定义 + 7 个星期 + 周末/周日),冒出一个 p=0.008 恰是多重比较的期望产物。 > **这一步的价值是排除,而且排除得比第一轮更彻底。** 用户问时段,直觉上 > 「美盘流动性好该更赚」很难自证伪,跑完才知道它连随机切分都跑不赢。 > 更要紧的是**第一轮我把周日效应写成了"统计上真实"**——长样本一上就翻了符号。 > 教训有两条:**① 报告"无效"之前先确认样本量撑得起这个"无"**; > **② 在几十个分组里挑出的最显著那个,默认它是噪声,除非它能在另一个总体上 > 复现。** p 值不做多重比较校正就等于没做检验。 ### 3.393 一类/二类买卖点:引擎实现下全是负的,一类尤其糟(step55) 用户提出把 B4 那套研究思路搬到一二类上,并指定用 5m/15m 测。 **先量滞后,不先跑收益**——`Chan_BSP_TYPE` 的枚举注释里写着 B4 存在的全部意义 就是「几何位置同 B3/S3,但不等笔确认」,说明这个项目早就付过等笔确认的学费。 **① 滞后:三类完全一样(用户的判断,实测确认)** | 类型 | 5m 滞后中位 | 15m 滞后中位 | 5m 漂移中位 | 占 2ATR 止损 | |---|---|---|---|---| | B1 | 8 根 | 9 根 | 1.68 ATR | 84% | | B2 | 8 根 | 8 根 | 2.03 ATR | 101% | | B3 | 8 根 | 9 根 | 1.95 ATR | 98% | 三类都卡在同一处:`中枢 is_sure` + `笔 is_sure` + `sure_time`。 **所以滞后不是区分一二三类的变量**,它是 `find_all_bsp` 这个实现的共同属性。 ⚠️ 「漂移为正的占比 100%」是**定义决定的,不是发现**:笔之所以在那里结束, 正是因为价格从极值反向走了,`bi.end_klc` 就是极值点。有信息量的是幅度不是符号。 **② 收益:全负,一类是最差的一档**(5m / 15m,5 币,出场用实盘的 2/3/8/2/48) | 类型 | 5m 笔数 | 5m 胜率 | 5m 毛R | 5m PF | 5m t值 | 15m PF | 15m t值 | |---|---|---|---|---|---|---|---| | **B1** | 623 | **18.6%** | −0.592 | **0.24** | **−17.3** | 0.21 | −18.9 | | **S1** | 644 | **14.8%** | −0.675 | **0.20** | **−22.3** | 0.23 | −15.9 | | B2 | 293 | 35.8% | −0.104 | 0.73 | −1.4 | 0.85 | −0.8 | | S2 | 288 | 36.8% | −0.115 | 0.82 | −1.6 | 0.75 | −2.4 | | B3 | 2806 | 33.3% | −0.187 | 0.64 | −8.0 | 0.76 | −5.3 | | S3 | 2685 | 35.4% | −0.135 | 0.70 | −5.6 | 0.73 | −4.8 | **口径校验通过**:B3 跑出 PF 0.64/0.76,§4 记的引擎 B3 是 0.66 —— 接线是对的, 所以 B1/B2 的数可以信。 **③ 一类为什么烂得这么彻底:它的结构自带反向杠杆** B1 买点的理想入场是那一笔的低点。等到 `sure_time` 可以动手时,价格已经**朝上 跑了 1.68 ATR**。于是新入场价往下 2 ATR 的止损,落在**比原始低点还低 0.3 ATR** 的位置——几乎贴着极值,任何一次回踩都打掉;而 3 ATR 的减半目标从低点算起变成 了 4.7 ATR。胜率 15~21% 完美对应这个几何:**它低于随机入场**(SL2/TP3 下随机 约 40%)。 **这和三类的滞后代价不是一回事。** 三类是顺势突破,等 8 根是「追高一点」; 一类是逆势抄底,等 8 根是「在反弹了 1.7 ATR 之后才去接刀」——**同样的滞后, 在反转型信号上是加倍惩罚**。 **④ 结论与下一步** 引擎现成的 B1/B2 **不可用**,且原因是结构性的不是参数性的,调参救不回来。 B3 当年的解法不是调参,是**重新定义成实时判据**(B4:收盘突破 zg → 收盘未跌回 → 重新上行,全部当根可判),选样口径随之改变(627 个中枢只认 212 个), 统计性质才翻正。 **但这条路未必能照搬到一类**:B4 能实时判,是因为「突破 + 回踩不破 + 重新上行」 每一步都能用收盘价当根判定。而一类的核心是**背驰**,它要求「知道这一笔已经 结束」——那本身就是事后信息。要做实时版,得先找到一个不依赖笔端点的力竭代理。 > 另有一个来自 §3.391 的先验值得先算:**一类按定义就发生在一段已走完的行情 > 末端,也就是 `mom60` 最高的那个区间——而那正是我们实测最差的四分位** > (Q4 发现期余量只剩 1.44bp)。真要做实时一类,先验就不利。 ### 3.394 实时版一类(fast B1/S1):滞后不是病根,一类没有边(step56) §3.393 判定引擎 B1/S1 不可用后,用户要求照 B3→B4 的路子做实时版。做了, **没救回来**,但这次实验把病因锁死了。 `research/lib/fast_bsp1.py` 把引擎 B1 的三个条件全换成当根可判的代理: | 引擎条件 | 滞后源 | 实时代理 | |---|---|---| | 中枢已成 `zs.is_sure` | 无 | `available_ts`(与 B4 同口径) | | 一笔向下离开中枢 | 笔端点 | **收盘** < zd | | 背驰 `check_bi_div` | 笔端点 | 进入段面积在中枢确认时已是历史;离开段面积从突破根逐根累加 | | 触发 `leave_bi.sure_time` | **主要滞后源** | 收盘反向越过前一根极值(沿用 B4 的转强判据) | 注意一类和三类**方向相反**:中枢向下突破后 B4 做空,fast B1 是在同一段下跌里 找力竭然后做多。 **结果(5 币,5m/15m,出场 2/3/8/2/48)** | 信号 | 5m 笔数 | 5m 胜率 | 5m PF | 5m t值 | 15m PF | 15m t值 | |---|---|---|---|---|---|---| | fastB1 一买 | 482 | 22.4% | 0.38 | −9.4 | 0.37 | −10.6 | | fastB1 一卖 | 528 | 20.3% | 0.36 | −12.5 | 0.41 | −9.6 | | **fastB3 三买**(同管线对照) | 972 | **55.0%** | **1.83** | **+10.6** | 1.84 | +8.5 | | **fastB3 三卖**(同管线对照) | 927 | **54.6%** | **1.93** | **+10.5** | 2.06 | +10.7 | **对照组是这次实验的全部价值所在**:同一份数据、同一套出场、同一条管线, B4 跑出 PF 1.83~2.06 / t +8.5~+10.7。所以 fast B1 的负数不是管线接错了。 **病根锁定:滞后不是主因。** 引擎版 PF 0.22 → 实时版 0.38,确实有改善, 但离 1.0 还差得远。更关键的是滞后分档: | lag(入场根 − 极值根) | 5m 笔数 | 5m PF | |---|---|---| | ≤1 根(≈买在极值上) | 472 | 0.40 | | 2-3 根 | 390 | 0.36 | | 4-6 根 | 125 | 0.29 | **即使入场就在极值那一根,PF 仍只有 0.40。** §3.393 里「等 8 根导致几何劣势」 的解释因此只对了一半:滞后确实在扣分,但**把滞后清零也救不活**——一类在这个 市场/周期/出场结构下本身就是负边缘的。 背驰强度有单调性但不够:15m 上最强四分位 PF 0.99、余量 −0.24bp、t −2.0, 毛收益刚够打平、净收益仍为负;且这是四选一的样本内挑选,不构成可交易的结论。 **判定:一类到此为止,不再投入。** 这与 §3.4 消融、§3.391 的 `mom60` 结论 互相印证——这一族信号的 alpha 在**顺势延续**上,不在反转上。fast_bsp 的文档 头早就写了「B4 不只是 B3 提前几根,两者统计性质符号相反」,一类的实验说明 反过来也成立:**把反转信号提前,它还是反转信号。** ### 3.395 ⛔ 一类反手:**已被 step59 推翻,不要用本节的 PF**(step55/57) > **本节结论作废。** 下面的 PF 2.3~3.0 用 `sure_time+1` 当入场时点,而 §3.396 > 的逐根回放证明实盘要等中位 **+15 根**才知道信号存在(0% 能准时拿到), > 按真实首现根入场后 PF 塌到 0.92 / 1.30、t 0.20 / 0.64。 > 保留本节是为了记录推理链和那个「胜率低于随机 = 信号指反了」的判据, > 它本身是对的、也可复用;错的是把 `sure_time` 当成了可执行时刻。 用户问「是不是一类二类的识别错了」。查下来结论是:**代码没写错,但信号该反着用。** **先排除掉一个我自己的错误猜测。** `check_bi_div` 只比 MACD 面积、不检查创新极值, 这是缠论背驰定义(创新极值 **且** 力度减弱)丢了一半。看着像致命缺陷,但实测 **93.2% 的 B1 / 93.4% 的 S1 离开笔本来就创了新极值**,缺口只影响约 7%, 解释不了 PF 0.2。**先量再断,别拿理论缺陷当结论。** **真正的线索是胜率的方向**:B1 胜率 18.6%,而 SL2/TP3 下随机入场约 40%。 **远低于随机说明样本带信息,只是指反了**——真正没用的信号会落在 40% 附近。 于是拿相反方向重跑出场(路径依赖,不能把 g 取负号了事): | 反手 | 5m 胜率 | 5m PF | 5m t值 | 15m PF | 15m t值 | |---|---|---|---|---|---| | **B1(反手做空)** | 64.4% | **2.35** | +10.4 | **3.03** | +12.6 | | **S1(反手做多)** | 67.1% | **2.75** | +12.8 | 2.63 | +9.6 | | B3/S3(反手) | 45~49% | 1.14~1.24 | +4 | 1.15~1.20 | +4 | **稳健性(step57)——这是本项目做过的最稳的结果** | 切法 | 结论 | |---|---| | 逐币 | 5 个币全正。15m PF 2.25~4.12,5m 2.31~2.70 | | 前后半段 | 15m 2.81 / 2.81,5m 2.55 / 2.51,几乎不变 | | 逐年 | 2021~2026 **连续六年全正**,15m PF 2.57~2.99 | | 多空 | 两边都正,排除单边行情 | | 余量 | 15m 33~76bp、5m 18~28bp。对比 B4 只有约 3bp | **为什么不是 bug 而要反着用** 代码是照缠论写的:向下离开中枢 + 背驰 → B1 买。**教科书没错,错的是它发信号的 时刻。** 信号在 `leave_bi.sure_time` 发出,中位滞后 8~9 根,此时价格已从低点 **反弹了 1.68 ATR**(§3.393 表)。引擎想说「抄底」,但它给出的时间戳对应的是 「反弹已走完」。在下跌趋势里于此处做空 —— 那是标准的**顺势回调入场**。 所以这一族信号的几何识别是对的,方向标注按理论也是对的,**是滞后把它变成了 相反的交易**。它是一个穿着反转外衣的顺势回调信号。 **它与 B4 不是一回事**:B4 是突破延续(§3.391 实测 mom10 中位 +3.47 ATR, 根本不是回调入场),这个是回调延续。两种入场原型不同,**可能互补**。 **止损位置:真问题,但不是主因(step58)** 用户提出:B1 做多的止损可能挂在了 B2 低点上方,于是在那次「回踩不破前低」的 正常回抽处被打掉,而不是等趋势真正走完。查下来**观察成立且比预估更严重**: | 量 | 5m 一类 | |---|---| | 入场价到结构极值(ATR,用入场根 ATR 归一) | 中位 **2.90** | | 2ATR 止损在结构位外侧留的余地 | 中位 **−0.90**(在结构**以内**) | | **止损落在结构位以内的占比** | **88.6%** | 即近九成的一类做多,价格连回踩到前低都不需要就已出局。 修掉之后确实有改善,但救不活。⚠️ 做这个对照时目标必须**随止损等比放大** (恒定 1.5R 减半 / 4R 收尾);放宽止损却不放大目标会把盈亏比压到 1 以下, 测出来的「放宽无效」是自证的: | 方向 | 止损 | 中位宽度 | 胜率 | PF | t值 | |---|---|---|---|---|---| | 原方向做多 | 固定 2ATR | 2.00 | 16.7% | 0.22 | −31.0 | | 原方向做多 | 结构+1.5 | 4.40 | 33.1% | **0.36** | −16.6 | | 反手做空 | 固定 2ATR | 2.00 | 65.7% | **2.53** | +14.4 | | 反手做空 | 结构+1.5 | 4.40 | 58.2% | 2.15 | +10.7 | **结论:趋势确实越过第二类买卖点继续走原方向。** 最有说服力的读法是 「结构+1.5」那一档——止损宽达 4.4 ATR,仍有 **67% 被打掉**,即三分之二的一类 信号会在 48 根内跌破结构低点再多走 1.5 ATR。缠论里「B2 回踩不破前低」这条 在本市场多数时候不成立。 佐证:反手做空在**最窄的 2ATR 止损上最好**(2.53),放宽反而降到 2.15。 这与顺势回调入场自洽——价格若重新涨过反弹高点,论点当场失效,不该留余地。 **仍未验证,落地前必须做完(否则不许上实盘)** 1. **因果性**。`zs.is_sure` / `bi.sure_time` 在全量数据上算出,§5.41 已知中枢 右边缘会重画。必须用增量/流式回放确认信号在当时真的发得出来。 *反证据(不能替代验证)*:同一套机器算出的 B3 是 PF 0.64 的烂数, 若存在通用的未来函数,B3 不该这么难看。 2. **消融:是不是「反弹就做空」都赚**。照 §3.4 对 B4 做过的那样,把背驰条件 拿掉、把中枢换成伪阻力位,看 PF 掉不掉。掉不下来就说明 alpha 在几何和 时点上,与背驰无关,那这个信号就该重新命名和重新设计。 3. **与 B4 的重叠度与相关性**。若只是换名字的 B4,就没有增量价值。 ### 3.396 ⭐ 因果回放:信号存在性是真的,时点是假的(step59) §3.395 的 PF 太好,先查因果性——增量模块文件头自己写着「最后一笔 is_sure **允许收回**」,§5.41 也记了中枢右边缘会重画。 **做法**:`init_stream` 预热 2 万根,随后逐根 `append_bar`,每根之后重算 `cal_bi_zs_list_pure` + `find_all_bsp`,记录每个信号**第一次出现**在哪一根; 入场用首现根,而不是事后的 `sure_time`。信号身份用「类型 + 极值 KLC 时刻」, **不能用 `sure_time` 当键**——它正是会被重画的字段。 必须逐根。分段重建(每 S 根算一次)等于在第 t 根多给了 S 根的信息, 测出来的因果性是假的。 | 检查 | 结果 | |---|---| | 召回(全量信号在回放中出现过的比例) | **100%**(B1 50/50,S1 65/65) | | 幻影(回放发过、全量没有的) | **0** | | 首现根 − 全量 `sure_time` | 中位 **+15 根**,P25 +11,P75 +22,P90 +31 | | **准时拿到的比例** | **0%** | | 反手 · 5m | 全量口径 | **回放首现口径** | |---|---|---| | B1 | PF 2.35 | **0.92**(t 0.20,余量 −1.37bp) | | S1 | PF 2.75 | **1.30**(t 0.64,余量 +4.21bp) | **结论**:信号的**存在性**是因果的(不重画、不消失),但**时点**不是。 实盘要晚中位 15 根、5m 上 75 分钟才知道它存在,而那笔交易吃的正是 「反弹已走完」这个窗口,晚 75 分钟就没了。t 值 0.20/0.64 完全不显著。 ⚠️ **可复用的教训**:`sure_time` 是引擎事后标注的确认时刻,**不等于可执行时刻**。 任何用它当 entry 的回测都要先过逐根回放。样本 115 笔偏薄,但机制清楚、 点估计从 2.35 塌到 0.92,方向不存在疑义。 ### 3.397 线段顶点当标准答案:识别是对的,但精度和时点都不够(step60) 用户提出用线段终点当趋势反转的标准答案——是未来函数,但用作**标签**验证 检测器而非用于交易,能把「检测器对不对」和「能不能交易」分开。 **对照组是关键**:B1 按构造就长在笔的低点上,而笔低点本来就有概率撞上线段底。 所以问的是「在所有同向笔端点里,B1 这个标签把命中率提高了多少倍」。 | 5m | 样本 | 命中线段顶点 | 相对笔端点的提升 | |---|---|---|---| | 笔端点(基准) | 48229 | 15.0% | 1.00 | | **B1** | 623 | **30.3%** | **2.02×** | | **S1** | 644 | 27.2% | 1.81× | | B3 / S3 | 2806 | **0.0%** | 0.00 | 15m 同向:B1 28.1%(1.81×)、S1 27.3%(1.76×)。 各容差档(±0~±5 根)数字完全相同,不是 bug:线段终点**本身就是某根笔的终点**, 两者天然按笔端点对齐,放宽容差不会多命中。B3/S3 恰好 0% 也是对的—— 三类长在回抽笔的端点上,按定义就在趋势中段,不该命中反转点。这两条互为 标签有效性的旁证。 **所以检测器是有信息量的:它把「这个笔低点是真反转」的概率翻了一倍。** 但还有两道坎: | 5m · 按原方向抄底 | 笔数 | 胜率 | PF | t值 | |---|---|---|---|---| | **未命中**线段顶点 | 903 | 8.7% | **0.08** | −42.0 | | **命中**线段顶点 | 364 | 36.3% | **0.70** | −3.2 | 15m:未命中 PF 0.08、命中 PF 0.83(t −0.36)。 **精度只有 30%,而那 70% 的噪声是灾难性的(PF 0.08)。更要命的是:即使用 未来函数把精度提到 100%,也只到 PF 0.70~0.83,仍不赚钱。** 原因是 §3.398 的几何:入场价已在结构底上方 **2.90 ATR**(step58)。 即使你真站在线段底上,从底部上方 2.9 ATR 处用 2 ATR 止损做多,结构本身就是 负期望的。§3.396 的回放还要在此之上再晚 15 根。 **一类的完整诊断(三层,逐层否定)** 1. 识别对不对 —— **对**,2× 提升(step60) 2. 精度够不够 —— **不够**,30%;且用未来函数选到 100% 也只到 PF 0.83 3. 时点来不来得及 —— **来不及**,全量口径已晚到入场价高出结构底 2.9 ATR, 实盘回放再晚 15 根 **判定:一类线到此为止。** 病不在识别,在于「等笔确认」这个机制天然把信号 推到了结构失效之后,而这是 `find_all_bsp` 的固有属性,不是可调的参数。 ### 3.398 极值点的波动率:观察成立,但解释力有限(step61) 用户提出:一二类长在极值点上,那个区域波动天然大,这可能就是确认慢的原因。 拆成三条可证伪的子命题分别测,结论是**一条成立、一条只沾边、一条不成立**。 **① 一类确实长在高波动区 —— 成立,且很干净** `atr_z` = 该点 ATR / 其**之前** 200 根的中位 ATR(含当根会把要检验的插针算进去)。 | 5m | 样本 | atr_z 中位 | 高于基准占比 | |---|---|---|---| | 笔端点(基准) | 48219 | 1.003 | **50%** | | **B1** | 623 | **1.224** | **74%** | | **S1** | 644 | 1.173 | 68% | | B3 / S3 | 2806 | 1.03~1.06 | 55~56% | | **B2 / S2** | 293 | **0.94~0.97** | **39~44%** | 基准恰好落在 50%,说明构造是对的。一类比随机笔端点显著偏高。 ⚠️ **但二类是反过来的**:B2/S2 的 atr_z 中位 < 1,只有约四成高于基准 —— **二类长在低波动区**。这符合结构:二类是首轮反转冲动之后的回抽,那时波动已 平复。所以「一二类都在极值点」这个说法对一类成立、对二类不成立, 两者是镜像而非同类。 **② 波动大 → 确认慢 —— 只沾边,不是主因** | 5m · atr_z 四分位 | atr_z 中位 | 滞后中位 | 滞后均值 | |---|---|---|---| | Q1 低 | 0.85 | 8.0 | 9.3 | | Q2 | 1.07 | **7.0** | 8.8 | | Q3 | 1.34 | 8.0 | 9.6 | | Q4 高 | 1.80 | 9.0 | 11.3 | `corr(atr_z, lag_bars) = +0.10`(15m 亦为 +0.107)。方向对,但只解释约 1% 的 方差,滞后中位几乎不随波动变化。**滞后是结构性的**——笔要等分型形成再确认, 这个 8~9 根跟波动率基本无关(§3.393 已测得三类买卖点滞后完全一致)。 **③ 入场后波动率回落 → 目标够不着 —— 回落属实,但不是亏损的机制** `atr_fwd` = 入场后 48 根平均 ATR / 入场时 ATR:一类 5m 中位 **0.896**(69% 回落), 三类 0.945~0.957。即一类的 3/8 ATR 目标按一个虚高的 ATR 定,绝对价格上约高估 10%。回落是真的。 **但它不是死因**: | 5m | 走到 3ATR 减仓 | 止损 | 超时 | 止盈 | |---|---|---|---|---| | **一类** | **9.4%** | **81.3%** | 17.9% | 0.8% | | 三类 | 30.7% | — | — | — | **九成的一类根本走不到第一个目标,八成直接止损出场。** 若死因是「目标够不着」, 超时占比该很高;实际超时只有 17.9%。所以一类不是「走不动」,是**入场后立刻 朝反方向走**。 按 atr_z 分档看表现也印证:PF 在 0.17~0.25 之间基本持平,高波动组并不更差 也不更好 —— 波动率**不是**区分变量。 **小结**:极值点高波动这个观察是对的,但它既没造成滞后(结构性的),也没造成 亏损(死于立即反向而非目标够不着)。它与 §3.397 的结论一致:一类的入场价已在 结构底上方 2.9 ATR,那个位置本身就是负期望的。 ### 3.399 ⛔ 一类线收尾:过滤器符号随入场时点翻转,判定为拟合(step62/56) 用户指出:很多一二类实际在**趋势中途**被识别而非末期;若真在末期,即使有延迟 也该走出行情。step60 的数据支持这个判断——命中线段顶点的 30% 是 PF 0.70~0.83, **没命中的 70% 是 PF 0.08**,亏损几乎全部来自误识别在趋势中途的那批。 **step62:找到了区分变量,识别精度接近翻倍** `ext_run`(极值越过中枢边界几个 ATR)单调有效,5m: | ext_run 档 | 命中线段顶点 | PF | |---|---|---| | Q1 最短 | **12.0%** | 0.12 | | Q4 最延伸 | **43.8%** | 0.46 | 最佳组合 `ext_run≥P75 且 div≥中位`:命中率 47.9%、PF 0.54 (基准 28.7% / 0.22)。**识别确实可以优化,且幅度不小。** 三个附带结论: - **背驰越强反而越差**:div Q1(最强)命中 17.0% / PF 0.13,Q4(最弱) 37.5% / 0.28。这是对 `check_bi_div` 这个 MACD 面积判据的直接证伪。 - **趋势级数无区分力**:连续同向中枢数 ≤1/2/3/≥4 的命中率 28.5/30.6/28.6/25.5%,基本持平。 - **「趋势 vs 盘整」前提引擎已隐含强制**(纠正我先前的猜测):B1 要求 `enter_bi.dir == leave_bi.dir == DOWN`,中枢向下进、向下出本身就定义了它嵌在 下跌趋势里,实测该条件在 2504 笔上**恒为 True**,过滤器无从添加。 `zs_count` 也不可用——它是全局中枢序号(310、585)而非趋势内序号。 **step56 复测:把过滤器接到低滞后版上,符号翻转** step56 的实时版把滞后压到 ≤1 根但无质量过滤(PF 0.40),step62 有过滤但滞后 未解(PF 0.54)。两者各解决一半,合起来是唯一同时处理两个约束的路径。结果: | 5m · 延伸度分档 | 滞后版(step62) | 低滞后版(step56) | |---|---|---| | Q1 最短 | 0.12 | **0.58** | | Q4 最延伸 | **0.46** | 0.33 | 叠加后 PF 从 0.37 掉到 0.30~0.33,**比不过滤更差**。`div` 同样翻转:滞后版里 背驰最弱的最好,低滞后版里背驰最强的最好。 **这个符号翻转是判定依据,不是需要再调的参数。** 同一批信号、同一个特征, 换个入场时点最优方向就反过来,说明这些「过滤效果」是入场时点的交互产物, 不是信号的稳定属性。继续在上面挑阈值就是拟合噪声。 **一类线全部尝试汇总(六次独立进攻,全部止步于 1.0 以下)** | 尝试 | 最好 PF | 出处 | |---|---|---| | 引擎原生 | 0.20~0.24 | step55 | | 反手 | 2.35~3.03 → **0.92/1.30** 因果回放后 | step57 / **step59** | | 实时重写(fast_bsp1) | 0.36~0.41 | step56 | | 结构止损(目标等比放大) | 0.36 | step58 | | 线段顶点选样(**用未来函数**) | 0.70~0.83 | step60 | | 质量过滤(ext_run + div) | 0.54 | step62 | | **低滞后 + 质量过滤** | **0.33** | step56 | 注意第五行:**即使用未来函数把选样做到完美,也只到 0.83。** **判定:一类线关闭。** 病根是 §3.397 的几何——入场价已在结构底上方 2.9 ATR, 实盘回放还要再晚 15 根(§3.396),而这来自「等笔确认」这个机制本身, 不是可调参数。识别可以优化,但识别从来不是瓶颈。 ### 3.3991 ⛔ 二类:不是继承了一类的错,是信息为零(step64) 用户提的悖论:二类按定义是「一买后回调、再继续趋势」,一类既已证否,二类凭什么好? 代码上悖论成立——`find_all_bsp` 里 B2 被 B1 门控(`if first_bsp_bi_div`)。但当时 有个反驳值得测:B2 比 B1 多要求价格**确实反弹了**、且回踩**守住了** B1 低点, 这两条是「那个底是真的」的事后确认,而 §3.397 的诊断恰恰是一类缺这个确认。 若成立,B2 就是 B1 里被验证过的子集,是好事而非坏事。 **测下来是坏消息,且机制与用户说的不同。** ⚠️ 先记一个**我用错的口径**:报表里 B2/S2「命中线段顶点 0.0%」(276~293 笔一个 没中)**是定义决定的,不是发现**。B2 按定义是反弹后的更高低点,线段顶点在 B1 那个极值上,两者不可能重合。这个指标对自称在极值的一类有效,对二类无效。 若日后要给二类找标准答案,该问的是「B2 之后 B1 低点是否再未被破」,不是顶点重合。 两组有效结论: **一、二类的几何是所有信号里最差的** | | 入场到结构止损位 | 2ATR 止损落在结构内侧 | |---|---|---| | B1 | 2.90 ATR | 88.6% | | **B2** | **4.87 ATR** | **99~100%** | 二类的天然止损位是 B1 的低点(回踩守住的就是那点)。但反弹 + 回踩之后,入场价 离那个低点已有 4.87 ATR。**二类没有可用的止损位**:挂 2 ATR 是纯任意止损, 挂到结构位则单笔风险近 5 ATR。step58 在一类上试过放宽到结构位,PF 仅 0.22→0.36; 二类的缺口比一类大 68%,没有理由更好。 **二、二类两个方向都没有边缘——这是它和一类的本质区别** | 5m/15m | 原方向 PF / t | 反手 PF / t | |---|---|---| | B1 | 0.21~0.24 / **−17 ~ −19** | 2.35~3.03 / **+10 ~ +13** | | S1 | 0.20~0.23 / **−16 ~ −22** | 2.63~2.75 / **+9.6 ~ +12.8** | | B2 | 0.73~0.85 / −0.79 ~ −1.42 | 1.09~1.10 / +1.67 ~ +1.70 | | S2 | 0.75~0.82 / −1.55 ~ −2.40 | 0.85~1.13 / +0.19 ~ +1.36 | **一类 t = −17~−22 是强烈指反**:它有信息,只是滞后把「反转信号」变成了 「反弹追单」(反手曾看似 PF 3.0,但 §3.396 已证那是 `sure_time` 后视产物)。 **二类两边 t 都贴着零**,原方向 −0.8、反手 +1.7,5m 反手 S2 甚至 0.85。 没有可提取的东西,连反手都没有。 其余画像与 §3.393 一致:二类 atr_z 中位 0.94~0.97(低波动,与一类的 1.22 相反), 滞后 8 根(与一类持平,§3.393 已记「三类滞后无差别」)。 **判定:二类关闭,一二类线整体收尾。** 一类是**信号指反**(有信息、方向错、 错在滞后),二类是**信号消失**——在一类之上又等两笔,把仅剩的方向性磨掉, 同时把止损位推远到 4.87 ATR。用户的悖论方向对,但不是「继承错误」, 是「多等的那两笔把信息也等没了」。 ### 3.3992 ⭐ 一类线重开:病根是选样×止损的交互,不是没机会(step65/58/66) 用户问「那如果修改 B1/B2 的识别规则呢」。这个问题**推翻了 §3.399 的关闭结论**, 下面三步是重开的依据。 **step65:先量机会本身装不装得下确认成本(不经过检测器,直接取 `seg_list`)** | 级别 | 线段中位幅度 | 扣 2.9 ATR 确认成本后 | 剩余 > 2ATR 止损 | 剩余/止损 | |---|---|---|---|---| | 5m | 11.4 ATR | 8.5 | **93%** | **4.26x** | | 15m | 10.9 | 8.0 | 93% | 3.99 | | 1h | 11.5 | 8.6 | 95% | 4.30 | | 4h | 12.4 | 9.5 | 96% | 4.74 | 五币四级别一致(10.5~13.7)。**滞后不是瓶颈**——这与我原先的预期相反。 但它与 §3.397 直接矛盾(空间在,却拿不到),矛盾的解答就是下面这条。 **step58 复测:先排除一个伪解释。** 首轮结构止损用了 `r_scale=True`, 3.9 ATR 的止损把 runner 目标推到 15.6 ATR,比 11.4 ATR 的整段行情还长。 改成绝对 3/8 ATR 目标重跑,PF 仍是 0.36(与 r_scale 版**完全相同**)。 **我预判的这个修正不成立**,首轮结论正确只是理由错了。 **step66:把两个杠杆同时拧(三者首次组合),天花板一次性打开** | 5m | 固定 2ATR | 结构+1.0 | |---|---|---| | 落在真线段端点(28.7%) | PF **0.70** / 胜率 36% | PF **2.29** / 胜率 63% | | 没落在(71.3%) | 0.08 | 0.14 | 15m:真底那批 **0.83 → 4.11**,胜率 44% → 70%。 **这是交互效应,不是叠加**——前三次进攻各自只动了一半,所以全部失败: | 只做完美选样(step60) | 只放宽止损(step58) | **两个一起(step66)** | |---|---|---| | 0.70 / 0.83 | 0.36 | **2.29 / 4.11** | MFE/MAE 给出机制:真底那批**逆向行程中位 2.24 ATR(5m)/ 2.08(15m)**, 即 2 ATR 止损**打掉了 51~58% 的好单**;而非真底那批逆向行程中位 4.76/4.93, 放宽只是亏更多。**固定 2 ATR 同时做错两件事:杀掉对的单,又留不住错的单。** ⚠️ **这个 2.29/4.11 里有两个未来函数,不是策略,是上界**: (a) 选样用了线段端点;(b) 入场仍在 `sure_time`,而 §3.396 证明实盘还要再晚 15 根。 它证明的是**失败原因可修(选样精度 + 止损位),而非这个级别没得做**。 **由此产生的两个待跑实验:** 1. ✅ **实时过滤器 + 结构止损** → 见 §3.3993,兑现不了,但换来一个确切门槛 2. **超时从 48 根放宽**(仍未跑)。线段中位时长 **113 根**,48 根超时在行情走完 42% 时就平仓;真底那批 MFE 中位仅 2.37 ATR,可能是被截断的(而非行情本身小) ### 3.3993 ⭐ 一类线的门槛量化:全局只有一个变量,精度要 67~73%(step67) 把 §3.3992 的天花板换成**实时可算**的 `ext_run` 选样,配结构止损: | | 5m | 15m | |---|---|---| | 全体无过滤 | 0.34 | 0.32 | | ext_run ≥ P75(实时) | 0.51 | 0.48 | | ext_run≥P75 且 div≥中位 | 0.60 | 0.46 | | ★ 真线段端点(未来函数) | **2.29** | **4.11** | **兑现不了。** 但数字对不上——Q4 的真端点浓度已达 44%(基线 28.7%), 若真端点值 2.29,44% 浓度不该只有 0.51。拆开看: | ext_run 档 | 真端点组 PF (5m / 15m) | 非真端点组 PF (5m / 15m) | |---|---|---| | Q1 最短 | 2.53 / 2.61 | 0.18 / 0.11 | | Q4 最延伸 | 2.22 / **6.10** | 0.16 / 0.12 | **这张表是本节的结论**:`ext_run` 在真端点**内部**毫无反向选样(5m 持平、 15m 单调递增到 6.10),在非真端点内部也毫无区分力(恒在 0.11~0.18)。 **全局只有「是不是真端点」这一个变量在起作用**,其余特征都是它的噪声代理。 这同时**修正了 §3.399 的判定**:当时把 `ext_run` 的符号翻转判为拟合。 现在看它不是拟合——它确实提纯(28.7% → 44%),只是提纯幅度远远不够。 于是 PF 退化成两组按精度 p 的混合,可解出门槛: | 实时精度 | 28.7%(当前) | 44%(ext_run) | 60% | 70% | 100%(上界) | |---|---|---|---|---|---| | PF (5m) | 0.34 | 0.50 | 0.73 | 0.93 | 2.29 | | PF (15m) | 0.33 | 0.51 | 0.81 | 1.10 | 4.11 | **盈亏平衡精度:5m 需 72.6%,15m 需 67.1%。** 当前 28.7%,`ext_run` 做到 44%。 **判定:一类线的问题已完全定性,但门槛极高。** 要把「这是不是真线段底」的实时 判准精度做到 ~70%(现在 28.7%,最好的特征到 44%),本质上是要求实时抄底摸顶的 准确率达到七成。这是可以尝试的 ML 问题(标签、特征、评估口径本节都已给全), 但**先验上很难**,且 70% 只是毛平衡,还要再扣 §3.396 的 15 根因果滞后。 > **这解释了为什么 B4 能做而一类不能**:B4 是突破后的**延续**信号, > 不需要判断反转;一类的全部难度集中在「这是不是那个底」这一个二分类上。 ### 3.3994 🚨 B4 因果回放:时点干净,但实盘做的不是回测那批单(step63/68) **这条影响正在跑的实盘,优先级高于本文档其余全部内容。** **好消息:B4 的时点完全干净,一类那个坑它没有** | | B1(§3.396) | **B4** | |---|---|---| | 召回 | 100% | 100% | | **准时率(首现==entry_idx)** | **0%** | **100%** | | 迟到中位 | +15 根 | **0 根** | **坏消息:实时回放会多产出一批全量口径里不存在的信号,且它们显著亏钱** 按**实盘口径**(2001 根滚动窗口、每 500 根 `init_stream` 重建、只做当根收盘, 逐行对齐 `shadow_signal.py`)重放 5 币 × 20000 根: | 过滤 | 留下 | **额外占比** | 全部 PF | 额外 PF | 回测口径 PF | |---|---|---|---|---|---| | 无过滤 | 789 | 75% | 0.64 | 0.46 | 1.37 | | 仅同向 | 402 | 77% | 0.65 | 0.35 | 2.65 | | 仅阶梯 | 341 | 68% | 0.71 | 0.31 | 2.14 | | 仅 ATR 门控 | 721 | 76% | 0.64 | 0.46 | 1.35 | | **三道全开** | **195** | **74%** | **0.73** | **0.26** | **4.21** | **三道滤网挡不住这批信号。** 它们把成交量砍掉 75%(789 → 195),但额外信号占比 从 75% 只降到 74%——按同样比例刷掉好的和坏的。实盘口径拆开: | 分组 | 笔数 | 胜率 | PF | 余量bp | t值 | |---|---|---|---|---|---| | 回测里也有的 | 51 | 74.5% | **4.21** | +30.15 | +5.90 | | **回测里没有的** | **144** | 22.9% | **0.26** | −14.25 | **−6.21** | | **实盘实际会做的** | 195 | 36.4% | **0.73** | −4.65 | −0.59 | **结论:回测报的 4.21 是拿不到的。** 那 51 笔只有事后用全量历史重算才能识别; 实盘在当下拿到 195 个信号,**没有任何办法分辨哪 51 个是真的**。机制是重画—— 实时算出的中枢,数据变多后被修正掉,信号随之消失。 这与 §3.396 是同一类错误的两种表现:一类错在**时点**(信号存在但晚 15 根), B4 错在**存在性**(信号当根就有,但其中 74% 事后会消失)。 **顺带闭掉一条挂了很久的待办:窗口长度不是问题。** 2001 根滚动窗口与 2 万→4 万根增长窗口跑出**完全相同**的 789/197/592,中枢是局部结构,截断无影响。 §5.41 那条「9.0% 中枢消失是不是窗口左边界效应」可以判为**否**。 ⚠️ **两点限定**: - 本轮测的是 **5m/30m**,实盘跑的是 **1m**。机制相同但数字必须单独验, 这是下一步第一件事 - 同向过滤器被我**开了未来函数的后门**(大级别分型时间线用全量历史算), 真实盘的 HTF 同样会重画,只会**更差**不会更好 **这不否定 B4 的 alpha**——回测口径那 51 笔 PF 4.21、t 5.90 是真实的,说明 「真中枢上的 B4」确实赚钱。问题是实时分不出真假中枢。可能的方向(均未测): 要求中枢的构成笔 `is_sure`(用滞后换稳定性)、或等 N 根看信号是否仍然存在。 ### 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.94(TP=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.381(1.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 | | **按 ATR 从大到小选币 / 追高 ATR 时刻** | 门控之上 ATR 高不加分。**币内严格单调递减**:ATR 最低 20% 分位净均R 1.040,最高 20% 只有 0.878。跨币看似正相关(r=+0.684)但**剔除 BTC 后掉到 +0.469**,其余 9 币极差仅 0.089。ATR 唯一真正给的是 bp 余量(机械关系)。且追高 ATR 会撞上流动性墙——现有 11 币全是头部流动性,外推到小市值币时多出的余量会被滑点吃回去。**ATR 是及格线不是排序键** | §3.33 | | **`available_ts` 改取 `bis[2]`(中枢成立即可用)以消除右边缘重画** | 重画确实从 6.5% 降到 1.2%、滞后 2.16→2.01,**但 alpha 被打成零**:实盘口径毛 R 0.933 → **−0.000**(扣费前就没了 edge),PF 3.22→0.80,余量 15.07→−2.20bp,8/8 币全变差。中枢没发育完就下注,支撑/压力还没立住。**「等中枢最后一笔」就是 alpha 本身,重画是必付代价。**推论:任何以「让信号更早确定」为目标的改动,先测毛 R,不要只看重画率和滞后 | step47 §5.42 | | **挑「信号根成交量大」的信号(有资金推的趋势)** | **方向完全相反**:vr60 四分位上毛R 单调 1.421→0.794、余量 27.18→13.47bp,样本内外 / 两套量比基准 / ATR 4 分位 4/4 / 逐时段 7/7 全部同向。止损率 22.5%→45.3% 翻倍,且**止盈率同时下降**(说明是方向变差,不是止损太窄)。根因:B4/S4 在突破根上进场,大量根 = 这一冲已经由别人的资金走完,你在收盘价接盘。**低量那一档才是好的**,可反向用作仓位权重 | step50 §3.31 | | **持仓中放量就平仓(把成交量当出场信号)** | **方向也是反的,且比入场那条更干净**:毛R 1.068 → 0.617(vr60≥3)/ 0.858(≥5)/ 1.009(≥8),**阈值越高越接近基线,最优点是「永不触发」**。触发的那批若不平,止盈率 45.7%、止损率 16.6%(全体基线 30.1% / 33.1%),**放量根标记的是最好的单子不是最差的**,平掉每笔让出 +0.484R。幸存者偏差已控(按持仓 ≥K 根分层 5/5 同向)。同一事件入场为负、持仓为正——入场时你是买方,持仓时资金是来接你货的 | step52 §3.31 | | **按时段交易(亚/欧/美)** | **噪声,已在 7 年长样本上坐实**:1m 段间毛R极差 0.122、p=0.0895 且美盘两期反号;5m/15m/30m(2019-09→2026-08,8168 笔)p = **0.62 / 0.67 / 0.31**,合并 **0.64**。不是 ATR 混淆——控 ATR 后极差 0.101/0.108,与无条件的 0.122 一样,它本来就小 | step54 §3.392 | | **砍周日/周末** | **1m 上显著但没复现,符号还翻了**:1m 周末 −0.132(p=0.0246)、周日 −0.199(p=0.0079);7 年长样本 5m **+0.014**、15m **+0.142**、30m −0.006、合并 **+0.041**(周日 +0.033)。本步共跑 ~35 个分组比较,冒出一个 p=0.008 恰是多重比较的期望产物。**即便按 1m 那版当真也不可加**:伤害的一半住在 `mom60≥7` 里,`mom60≥7 + 周日` 在 0~20bp 每一档都输给 `mom60≥7` 单用 | step54 §3.392 | | 线段(`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.01bp,100 档深度**。即时价差成本可忽略。 **但代理会把延迟从几百毫秒放大到秒级,而 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 全网格重测(step42,11 币 × 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 唯一沿用原参数的地方 - 纯整仓高 TP(TP8/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/根),所以没有任何理由带更长的历史。 ⚠️ **「假阳性 0%」是 n=180 的结果,不足以排除 0.5% 量级的重画** (2026-08-28 更正)。若真实率为 0.5%,180 笔里期望仅出现 0.9 次—— **0% 与 0.5% 在该样本量下无法区分**。 实测反例:web 端 BTC 1m(10 万根窗口),同一份数据分别截到本地 22:25 与 02:08 两个视角,212 个信号中 **1 个消失**(08-27 22:19 的 B4,约 0.47%), 与 15m/30m 的 0.7% 同量级。用户在界面上先看到、后消失,即此现象。 **缓解事实:消失的那个 `htf_agree=False, ladder_ok=False`,是浅色 b4, 本就不交易。** 真正要回答的是「**双过滤通过的深色信号会不会重画**」—— step39 报的「同向过滤也成立 100%」同样基于 n=180,同样不足以排除低频事件。 ⚠️ **step39 的「假阳性」测法本身是无效的**:它从全序列随机抽非信号点,看时点 重建会不会凭空冒信号。1m 信号密度约 474 根 1 个,180 个随机点期望只撞上 0.38 个——**测出 0% 几乎不含信息量**。重画只发生在「差一点就成型」的结构附近, 不在随机点上。 #### 5.41 重画的根因:`available_ts` 取的是中枢**结束**而非**形成** ⚠️⚠️ 代码审计(比逐根跑数据快得多):`find_fast_bsp3` 给定 `zones` 后严格因果 (只用 `close[j]` / `high[j-1]`),加未来K线动不了已成型的入场点。 **重画只可能来自 `zones` 表被修订。** `zones_from_zs_list` 里: ```python last_bi = bis[-1] # ← 中枢的最后一笔 avail = ts_of.get(sure_key) or ... # available_ts = 它的确认时刻 ``` **用户 2026-08-28 指出的缠论定义问题**:中枢**形成**只需三笔有重叠区间; **结束**才需要「离开且不返回的笔」确认。判信号只需形成,不必等结束。 而 `bis[-1]` 只有中枢走完才知道是哪一笔——**取的是结束时刻**。 实测(BTC 1m,30 万根,1651 个中枢): | 中枢笔数 | 占比 | |---|---| | 恰好 3 笔 | 25.5% | | **>3 笔** | **74.5%**(中位 5、均值 8.1、最大 89) | **「最后一笔确认」比「第三笔确认」晚:中位 62 分钟、p75 142、p90 247、最大 1739。** 1m 上 62 分钟就是 62 根 K 线。 **三个后果:** 1. **这就是重画源。** 中枢每吸收一笔,`bis[-1]` 就后移一次。300 时点抽样、 3284 个「当时已生效」的中枢:完全一致 84.4%,**确认时刻被改 6.5% (推后 198 个 vs 提前 17 个,中位 +100 分钟、p90 +413)**,中枢消失 9.0%, 阶梯翻转 2.6%。用户看到的 08-27 22:19 B4 消失即此。 2. **它解释了 docstring 里那个 34%。** 「627 个中枢只认 212 个(34%),被拒的 多数是『中枢确认时价格早已离开、此后再没回来』」——**「价格早已离开」不是 市场现象,是这个定义造出来的**:中枢正是被离开笔终结的,所以扫描起点天然 落在离开之后;代码又要求 `was_inside`,于是只能等价格**再次回到中枢再突破 一次**。那 66% 多半不是没机会,是第一次离开时还没开始看。 3. **方向对回测有利、对实盘不利。** 全量的 `available_ts` 系统性更晚 → 回测扫描起点比实盘晚 → **回测偏保守,不是被高估**;但实盘会产出更多、更早 的信号,这部分质量不在回测统计里。 **关键性质:`bis[2]` 是不变量,`bis[-1]` 不是。** 用户 2026-08-28 补充的 判断——「只有没确认的才会重画,确认的都不会」——与实测一致:6.5% 的改动 **不是已确认的笔被推翻**,而是中枢又吸收了新笔、`bis[-1]` 变成了另一根笔。 原来那根笔本身没变。而中枢一旦由三笔构成,第三笔就固定,不随延伸改变。 **A/B 实测(BTC 1m,300 时点,3284 个已生效中枢):** | 口径 | 完全一致 | 确认时刻被改 | 中枢消失 | 改动中位 | |---|---|---|---|---| | 现行 `bis[-1]`(中枢结束) | 84.4% | **6.5%** | 9.0% | +100 分 | | 改用 `bis[2]`(中枢形成) | 89.7% | **1.2%** | 9.0% | +16 分 | **`bis[2]` 把该重画源压到 1/5。** 两种口径全量中枢数都是 1651——纯粹改变 「何时可用」,不增删中枢。 **改成「第三笔确认」的预期收益**:消除该重画源(第三笔确认后不再变)、 符合缠论定义、滞后大幅下降(§3.2 已证滞后是收益第一驱动)。 ⚠️ **剩下 9.0% 的「中枢消失」两种口径完全相同,是另一个源,与笔确认无关**, 且在改用 `bis[2]` 后成为主要重画来源。最可能是**窗口左边界效应**:2000 根窗口 截断笔结构,造出全量视角里不存在的中枢。 ⚠️⚠️ **由此牵出一个此前未记录的口径差:回测用全量历史建中枢,实盘用 2000 根 窗口。** 若那 9% 确由左边界造成,则它不是重画,而是**回测与实盘看到的中枢集合 本就不同**——比重画更根本。**待查。** ⚠️ **但不要直接改。** 这会换掉整个信号总体,本文档所有数字都绑定当前实现。 反向风险明确:中枢形成即可用意味着会去做「仍在形成中」的中枢的突破, 价格可能再回来——**信号变多但质量可能变差**。必须 A/B 实测。 > ⛔ **A/B 已跑完(step47,2026-08-28):`bis[2]` 作废,不要再试。** > 详见 §5.42。它把重画压到 1/5 的同时,把毛 R 从 0.933 打到 −0.000—— > **alpha 归零,不是成本问题。** 下面这段「待办」保留仅作推理记录。 **(已完成)`available_ts = bis[2].sure_time` vs `bis[-1].sure_time` 的完整 A/B** (信号数、滞后、PF、剔10%PF、滑点预算),以及深色信号的 大样本重画审计(脚本已写:`research/step45_repaint.py`)。 对实盘的影响有限但真实:实盘会比回测多开极少量的仓,这部分质量不在回测统计里。 **但出场(止损/止盈/超时)不依赖信号是否仍在图上,不会出现「信号消失、仓位卡住」。** **这一步顺带解决了一个更重要的问题**:1m 的回测口径本身是可信的, `is_sure` 回撤对 1m 同样没有影响。**1m 现在唯一的风险就剩执行成本。** ⚠️ **延迟预算的分解**(这直接决定 1m 生死): | 环节 | 实测 | 说明 | |---|---|---| | 信号计算 | **0.20s** | 2000 根窗口,可控 | | 走代理访问 Bitget | **约 2.4s** | 单次 REST 往返(含建连),**这才是瓶颈** | 计算只占延迟的一小部分,**网络是主要来源**。所以影子交易器必须: 用 **WebSocket 订阅**而非轮询 REST 拉 K 线,并尽快把实盘迁到境外 VPS。 否则测到的滑点全是代理的账。 #### 5.42 A/B 判决:`bis[2]` 把 alpha 打成零,重画是必付代价(step47)⛔ 8 个样本外币 × 30 万根 1m,两组共用同一个 TF_DF,只切 `available_ts` 的取法。 实盘口径(深色 ∧ ATR≥8bp): | | `bis[-1]` 现行 | `bis[2]` 中枢成立 | |---|---|---| | 笔数 | 955 | 989 | | 滞后 | 2.16 | 2.01 | | 胜率 | 68.3% | 40.2% | | **毛 R** | **0.933** | **−0.000** | | 净均 R | 0.798 | −0.147 | | R 夏普 | 0.532 | −0.107 | | PF | 3.22 | 0.80 | | 剔10% PF | 2.35 | 0.55 | | 滑点余量 | **15.07bp** | **−2.20bp** | **判决依据是「毛 R 0.933 → −0.000」这一行**:扣任何费用之前 edge 就没了, 所以不是成本、不是门控、不是出场参数的问题,是信号本身不再有预测力。 逐币 8/8 全部变差,只有 BTC 勉强为正(0.617 → 0.116)。 **为什么**:`bis[2]` 只要三笔重叠就认为中枢可用,此时中枢尚未发育, 支撑/压力还没立住。`bis[-1]` 那段「等待」不是可以优化掉的延迟——**它就是 alpha 本身**。 由此得到一条一般性结论,值得推广到别的"降延迟"想法上: > 右边缘重画不是 bug,是这个信号质量的**必付代价**。任何以「让信号更早确定」 > 为目标的改动,都要先测毛 R,**不能只测重画率或滞后**。 > §5.41 只测了稳定性就给出「唯一能同时改善收益与稳定性」的判断,是过早的。 顺带交叉验证:现行口径本次算出滑点余量 **15.07bp**,与 §5.3 里 2026 组合口径的 15.19bp 基本吻合——两条独立路径得到同一个数,该预算值可信。 脚本 `research/step47_avail_bi_ab.py`;开关在 `chanlun/analysis/fast_bsp.py` 的 `AVAIL_BI_INDEX`(环境变量 `CHAN_AVAIL_BI`),**默认 −1,保持现行口径**。 ### 5.5 增量更新已验证:一致且快 12 倍(2026-08-28) `pipeline/builders/incremental.py` 此前一直标着「需重新验证」。已验完。 **一致性:400 根、每 20 根与全量重算对拍一次(共 20 次), 笔数 / 中枢 zg·zd / 信号 entry_idx 全部相同,无漂移。** **耗时(本机 arm64 单线程,1m 2000 根窗口):** | 环节 | 增量 | 全量重算 | | |---|---|---|---| | TF_DF / `append_bar` | 14.0ms | 192.5ms | | | 中枢 + `find_fast_bsp3` | 7.6ms | 10.1ms | | | **1m 侧合计** | **21.7ms** | 202.6ms | 9.3x | | 5m 侧(`append_bar` 7.1 + 分型线 2.9) | 10.0ms/次 | 84.4ms | 每 5 根一次 → 摊 **2.0ms** | | **完整一根** | **≈24ms** | **286ms** | **≈12x** | ⚠️ **必须定期重建窗口,否则增量的优势会被自己吃掉。** `append_bar` 不是 O(1)——`pd.concat`、`add_indicators`、`cal_bi_list` 三处 仍是全量 O(n),而 dataframe 只增不减: | | 1200 根后行数 | 中位 | 前100根 → 后100根 | |---|---|---|---| | 不裁剪 | 3200 | 16.3ms | 13.9 → **19.4(+40%)** | | **每 500 根重建** | 2199 | 14.4ms | 14.2 → **14.0(持平)** | `IncrementalBuilderMixin` 没有裁剪接口,做法是**每约 500 根用最近 2000 根 重新 `init_stream`**。单次约 192ms,摊到每根 0.4ms,可忽略。 **下一个数量级的线索(用户 2026-08-28 指出)**:`add_indicators` 算了很多本策略用不到的指标。它在 `append_bar` 里是**每根全表重算**的, 所以浪费不是一次性的,而是每分钟一次、且随窗口长度线性放大。 改它会动到 `dataframe` 的列集合,web 与回测都依赖,需整体评估。 > 后续实测(见 §5.6)**推翻了这条线索的份量**:`add_indicators` 只占全量 > 构建的 1.3%,talib 是向量化 C,便宜。真正的浪费在 `ChanKLU.set_indicators` > 和 `ChanKLC.cal_all_ema_status`。方向对(确实有大量无用计算),位置错了。 **这是延迟这块唯一有数量级收益的方向**:`find_fast_bsp3` 本身只占 0.75ms (全链路的 0.3%),优化信号代码毫无意义;95% 的时间花在 TF_DF 从头重建 2000 根上,而实盘每分钟只新增 1 根——**重算了 1999 根没变的东西**。 > 服务器侧实测 1m 那条腿 695ms(本机同口径 202.6ms,慢 3.4x)。若两条腿 > 同比例,那边完整链路约 980ms,**已超 §6 定的 800ms lag 告警线,且尚未计 > 任何网络耗时**。改用增量后应降至约 82ms。⚠️ 该外推假设两条腿慢的比例一致, > 服务器侧应直接实测一次完整链路。 > > **§5.6 优化后本机复测(同口径 2001 根、单线程、从头算一遍)**: > > | 口径 | 优化前 | 现在 full | 现在 lean | > |---|---|---|---| > | 1m 腿 | 202.6ms | **71.8ms** | **50.0ms** | > | 完整链路(两腿+信号+过滤) | 286ms | **178.4ms** | **120.5ms** | > | 增量 `append_bar` | 24ms/根 | — | **13.6ms/根** | > > 按 3.4x 换算到服务器:完整链路 980ms → 约 **611ms(full) / 413ms(lean)**, > **即使完全不上增量也已落回 800ms 告警线以内**;上增量约 46ms。 > ⚠️ 但 3.4x 是在**优化前的代码**上量的。优化把工作从逐行 Python 对象操作挪到 > numpy 批量操作,若服务器慢在 CPU 主频则比值成立,若慢在内存带宽则未必。 > **服务器侧必须直接实测,不要只信这个换算。** > 为什么这一步必要:step38 只验证了 15m/30m,从没验证 1m。而窗口 4000 根 > 对 15m 是 41 天、对 1m 只有 2.8 天,中枢的左边界效应完全不是一个量级。 > 窗口同时牵动两头:太短则中枢被截断信号不符,太长则算得慢延迟大滑点高。 ### 5.6 引擎提速 2.6~3.6x,逐位一致(2026-08-28) 先建了 `step46_engine_parity.py` 作为安全网:对 5 个用例(BTC 1m/5m、ETH 5m、 SOL 15m、XRP 30m,各 2 万根)固化 klc/笔/中枢/信号/被消费列的哈希。 **没有它不要碰引擎**——行为变化是静默的,不报错、不崩,只是信号悄悄换一批, 而 HANDOFF 全部数字都绑在当前实现上。 三处改动,每处都过了对拍: | 改动 | 原因 | 效果 | |---|---|---| | `cal_kl_data` 预取 ndarray,不再 `df.iloc[i]` | 每根新建 40 列 Series + 在其上做几十次逐键查找 | 这一处就占构建的 **96%**,2 万根 1946ms → 824ms | | `ChanKLC.cal_all_ema_status` 改惰性 | 每 KLC 立即重算,占 25%,而**全仓无任何读取方** | 调用 14322 → 2248 次 | | 删 `get_klc_list` 里的 `ema_up_list/ema_down_list` | 累加一整轮后直接丢弃,纯死代码 | — | 另外加了 `TF_DF(..., lean=True)`:只构建到中枢,跳过线段/走势中枢/整套 MACD 状态机(这些只服务 `bsp_list` 和 web 展示,笔与中枢不依赖)。 **已验证 lean 与 full 的笔/中枢/信号在 5 个用例上完全一致。** 结果(2 万根 5m):**full 1946 → 754ms(2.6x),lean → 543ms(3.6x)**。 增量路径在两种模式下都仍与全量逐位一致;web API 全字段正常(full 模式一字未动)。 验证做了四层,每改一处都重跑: 1. **对拍基线**(上表所列全部哈希项),full 模式三处改动后全部一致 2. **lean ≡ full**:笔/中枢/信号在 5 个用例上完全相同 3. **增量路径**:`init_stream` + `append_bar` 追加 150~200 根 vs 全量重建, lean/full 两种模式都逐位一致 4. **web 冒烟**:真实 HTTP 请求,`bsp_list` 168 / `seg_list` 177 / `zs_list` 17 / `klc_trend` 齐全 **大样本对拍(20 万根 × 5 用例)**:用 `git stash` 切回改动前的原始代码存基线, 再切回优化版对拍,**逐位一致**。这是真正的改前/改后比对,不是自己跟自己比。 同一批工作量 **99.9s → 43.3s(full 模式 2.3x)**,与 2 万根时的 2.6x 一致, 说明提速不随规模衰减。复现: ```bash git stash push -- chanlun/ python step46_engine_parity.py --save --rows 200000 --out step46_baseline_big.json git stash pop python step46_engine_parity.py --check --rows 200000 --out step46_baseline_big.json ``` 关键认识订正: - **缩放是线性的,不是 O(n²)**。中途一度量到"数据 6 倍、耗时 44 倍",是我自己 开的 `tracemalloc` 污染了计时。干净重测每翻倍 ×2.06~2.20,16 万根 lean 4.2s。 - **`add_indicators` 只占 1.3%**,删无用指标在这里几乎省不到时间。 它有 11 个列(`bbup365`/`bblow30`/`bbp302` 等)Python 与前端都无人读取, 但它们不进 KLU,唯一成本是 web 响应体积(约占 45 列中的 11 列)。 - 实盘延迟已不是瓶颈:`append_bar` 约 32ms/根。 **继续优化只对研究吞吐有意义。** > 「对 800ms 告警线有 25 倍余量」这个说法要小心,两处口径不对:单币的 > `append_bar` 不是信号总延迟(`compute()` 全程 108ms,chan 构建只占约 > 22ms),而多币是同一秒一起收盘、要串行清空的(十币 247ms)。另外 > 800ms 哨兵测的是**数据到达**(本地接收 − K线收盘,实测 268~898ms), > 计算是叠加在它之上,不共享那条预算。见 §5.6。 剩余热点(lean、8 万根口径):`cal_trend` 与 `set_indicators_from` 各约 0.36~0.59s。 它们带序列状态(`last_trend` + 近 5 根窗口),向量化风险高,收益约 20%,暂不做。 > **更正(2026-08-28)**:原先此处写「`cal_trend` 不能跳过——`bi.py:221` 读 > `klc.trend`,笔的计算依赖它」。**这条不成立。** `bi.py:221` 在 `cal_trend` > **自己**的循环里,读的是它自身的序列状态(前几根已处理 KLC 的 trend), > 不是 `cal_bi_list` 的依赖。 > > 实测定论:`init_stream/append_bar` 从不调 `cal_trend`(它只在 > `get_klc_list` 里),所以增量追加出来的 klc 其 `trend` 恒为 `UNKNOWN`, > 而批量构建的有值。`verify_incr_parity.py` 三币 1,800 根对拍两者**逐字段 > 相同**。所以 `cal_bi_list` 不依赖 `klc.trend`,`cal_trend` 对「bi → 中枢 > → fast_bsp」这条链是可跳的。 > > 这处偏差值得记:它是读代码读出来的(把一个函数的内部自引用误当成外部 > 依赖),而对拍一次就定论了。同类判断优先用对拍。 ### 5.7 增量在影子路径上的落地(2026-08-28) `shadow_signal.compute` 已改为按 `(symbol, timeframe)` 缓存流式对象。 **前提必须先验。** `init_stream/append_bar` 没有 trim,`dataframe` 靠 `pd.concat` 无界增长,所以增量必然让窗口每根 +1,只能周期性重建拉回,两次 重建之间窗口是 `[W, W+500]` 而非恒定 `W`。若 `compute()` 输出随窗口长度变, 增量就等于静默换掉一批信号。`verify_window_sens.py` 三币 75 个信号窗口、 `+200/+500/+1000` 三档逐字段一致,前提成立。注意 §5.4 说的是「命中率在 2000 根**饱和**」——饱和不等于不变,那是两回事。 **重建不要用 `init_stream`。** 它逐行 `dataframe.iloc[idx]`,正是提速刚修掉 的反模式:2001 根要 238.5ms,批量 `TF_DF(df, lean=True)` 只 74.3ms,慢 3.2 倍。第一版用它重建,十币启动时各来一次,清空反而从 560ms 涨到 1686ms。 `append_bar` 能接在批量构建的对象上,`_ensure_stream_state` 会补出 `_klc_feed_last_klu`。 **实测(2001 根窗口):** `append_bar` 21.8ms vs 批量 lean 重建 77.7ms = 3.56x。拆解 = `add_indicators` 全表 7.5ms(34%,为加一根重算 2001 行)+ `cal_bi_list` 整表重扫 11.1ms(51%)+ `concat` 1.4ms。这两项都在引擎侧,是 下一步提速的着力点。 **十币 / 2 核的效果:** 清空 560→247ms,排队 92→10ms,纯计算 219→108ms, 内存 597→598MiB(20 条缓存流的开销在噪声里)。 ~~**瓶颈已经换位置了:** `inner_ms` 108ms 里 chan 构建只占约 22ms,其余 86ms 是 `build_htf_zones` / `find_fast_bsp3` / `attach_htf_context`。~~ ⚠️ **上面这段是错的,2026-08-28 撤回,见 §5.72。** 那个 86ms 不是测出来的, 是减出来的:22ms 是孤立环境下 1m 腿的 `append_bar`,108ms 是跑着的十币采集器 里的 `inner_ms`。一个无争抢一个有争抢,还漏了 5m 腿,这个减法不成立。 实测信号链合计只有 16~21ms,`build_htf_zones` 是其中最大的一项(13ms), `find_fast_bsp3` / attach 合计不到 2%。 #### 5.71 第二轮引擎优化(2026-08-28,服务端反馈驱动) 承上——§5.7 末尾点的那两个引擎侧热点(`add_indicators` 全表重算 34%、 `cal_bi_list` 整表重扫 51%)。逐个查下来,**四处都是"算了没人要的东西", 不是算法本身慢**: | 改动 | 性质 | |---|---| | `cal_trend` 挂到 lean 下 | 见上面那条更正——它不是笔的依赖。web 走非 lean,`klc_trend` 图层不受影响 | | `check_fx_pattern` 删掉拼完就丢的字符串 | 它把 `klu.to_string()` 拼成 `p` 只为一行注释掉的 print。2000 根上近 3 万次 f-string + 6 万次 enum 格式化,在 `cal_bi_list` 内层。`klu.pattern` 只被 `cal_klu_pattern` 自己的双K/三K 判定读,不出模块、不进 web,故整个调用 lean 下也跳 | | `ChanBI.add_klc` 去二次方 | 去重原本线性扫 `klc_list`,且**每加一根就把整笔所有 KLU 的 macdhist 重累一遍** → 往一笔加 k 根是 O(k²)。改下标集合 + `macd_hist`/`macd_div` 惰性求值。这两个值只有背驰判定(`bsp.py`)读,lean 下 bsp 根本不算 | | `add_indicators` 批量挂列 | 2001 行上 TA 计算合计只有 2.5ms,而 30 多次 `df['x']=` 要 3.6ms——**开销大头是 BlockManager 逐列插入,不是计算**。改为一次 concat。`cal_volume_ratio` 里为算一列 rolling 而 `dataframe.copy()` 整张 40 列表,一并去掉 | 实测(本机,2001 根窗口)。**注意这组数和 §5.7 的 21.8ms 不同机**——本机基线 就是 13.9ms,服务端约慢 1.4~1.6 倍,别把两边的绝对值直接比: | | 改前 | 改后 | |---|---|---| | `append_bar` | 13.9ms | **6.6ms** | | └ `rebuild_bi_zs` | 8.7ms | 2.8ms | | └ `add_indicators` | 4.4ms | 3.5ms | | `TF_DF` lean | 49.8ms | **32.9ms** | | `TF_DF` full | 72.7ms | 64.9ms | **对拍**(`git worktree` 检出改动前的提交,同一份 BTC 1m 4000 根, lean/full 两模式 × klc/klu/bi/zs/seg/bsp/指标列/列序,38 项指纹): - **full 模式 19 项全部一致** → web 那条路一个字节没动。 - **lean 模式差 2 项,且正是设计要它差的两项**:`klc.trend`(跳了 `cal_trend`,现恒为 `UNKNOWN`)与 `klu.pattern`(跳了 `cal_klu_pattern`)。 - **lean 下的 `bi`/`zs`/`seg`/`bsp`/`dataframe` 全部一致** —— 这就是"这两个字段没人读"那句断言的实测证据:把它们打空,下游一位不变。 ⚠️ 别把这写成"完全一致"。有两项按设计就该变,写成全等会掩盖掉真正要担保的 那件事:**变的只有这两个已确认无人消费的字段。** full 模式的 `bsp_list` 是 `macd_hist` 的唯一消费者,它没变才说明惰性求值是对的。 **剩下没做的:`add_indicators` 仍是全表重算**(为加一根算 2001 行)。 EMA/MACD/ATR 是递推的、BB/SMA/量比是窗口的,理论上都能 O(1) 更新到精确值, 但 Wilder RSI 需要额外维护 `avg_gain`/`avg_loss` 状态(从输出反推不出来)。 做完 `append_bar` 可到 3~4ms。**风险在于一处不精确就静默换掉一批信号, 要做必须先扩对拍。** **瓶颈已经换位置了。** 本机分档(`research/live/probe_inner.py`,与服务端 同口径):`TF_DF` 两条腿占 70%、`build_htf_zones` 13%、`htf_fx_timeline` 6%、 `find_fast_bsp3`+ladder+attach 合计不到 2%。但服务端报的是 chan 构建 22ms / 信号链 86ms,按机器差(×1.36)解释不了四倍差距。 **曾怀疑是 payload 反序列化,实测 `_rebuild` 只有 1.0ms,假设不成立。** 两边跑同一个探针对分档表,才能定位那 86ms。 **→ 已定位,见 §5.72:那 86ms 不存在,是服务端的减法错了。分档两边一致。** #### 5.72 那 86ms 不存在:`inner_ms` 是地板 30ms + 缓存浪费 37ms + 争抢 1.5x 服务端跑了同一个 `probe_inner.py`(2 核,采集器十币在跑)。**分档占比两边 对得上,四倍差距是伪命题:** | | 本机 | 服务端 | |---|---|---| | `TF_DF` 两条腿 | 70% | **68~74%** | | `build_htf_zones` | 13% | 10~12% | | `htf_fx_timeline` | 6% | 5% | | `find_fast_bsp3`+ladder+attach | <2% | **<2%** | | 信号链合计 | — | **16~21ms**(不是 86ms) | 服务端那个 22/86 是无效减法(见 §5.7 的撤回批注)。 **但对完表冒出一个真问题:** 探针里全量 `TF_DF` 合计 106~126ms,而采集器走 增量报的 `inner_ms` 也是 100ms 中位——增量该快得多,两个数却一样。差额去哪了? 排查顺序与结论: | 假设 | 实测 | 判定 | |---|---|---| | `_rebuild` 反序列化贵 | 两腿合计 **1.6ms** | ✗ | | 某些币结构复杂 | 十币中位 92~105ms,几乎一致 | ✗ | | 周期性全量重建拉高中位 | 占 21.5%,且只贵 1.5x(133 vs 91ms) | 部分 | | 批内后段被拖慢 | `inner_ms` 对批内次序相关 **−0.083**,是平的 | ✗ | | **CPU 争抢** | 见下 | **✓ 主因** | 「批内平坦」一度被我读成争抢的反证,其实反了:**两个 worker 在整段突发期都 满载时,批内每个币被拖慢的程度一样,平坦正是均匀争抢的表现。** 用外生指标(到达密集度,仅由 `t_data_ms` 决定,不含 `inner_ms`): | ±250ms 内到达币数 | 1 | 2 | 4 | 6 | 8 | |---|---|---|---|---|---| | `inner_ms` 中位 | 67ms | 80ms | 94ms | 105ms | 117ms | ⚠️ **第一版用 `beg = t_signal_ms − inner_ms` 反推并发区间,得到相关 0.533, 这个数是废的**——`inner_ms` 越大区间越宽、重叠自然越多,相关性有一部分是构造 出来的。换外生指标后相关降到 0.271。**凡是用被解释变量去构造解释变量的,先 怀疑循环。** 注入合成负载复现(离线,2 核): | 背景 | `compute` | 放大 | |---|---|---| | 空闲 | 29.9ms | 1.00x | | 1 个满载进程 | 41.7ms | 1.39x | | 2 个满载进程 | 80.2ms | 2.68x | | 3 个满载进程 | 85.4ms | 2.86x | ⚠️ **别拿这张表算争抢倍数——那个 29.9ms 基线是废的。** 它每次喂同一个窗口, `append_bar` 发现没有新根、一根没追(实测 6 次调用 stream 长度 2001→2001 零 增长),测到的只是信号链本身。拿它去比在场的 `inner_ms` 得出「3.3 倍争抢」, **又是一次口径不对齐——和被撤回的 22/86 同一类错,隔了一小时又犯一次。** 口径对齐后:**在场每次调用平均追加 2.81 根**,因为 2 个 worker 各存一份缓存, 各自漏掉对方处理过的根(`stream_bars` 逐根 delta 在 ±1~±4 对称分布,正是两个 独立计数器的指纹;**真重建只占 0.31%**,此前算的 21.5% 是把换 worker 的小幅 回退误判成重建)。而 `add_indicators` 每次追加都全表重算,所以追加成本随根数 近线性涨: | 离线每次追加 | 1 根 | 2 根 | 3 根 | 4 根 | |---|---|---|---|---| | `compute` | **45.0ms** | 55.7ms | 70.1ms | 85.9ms | 在场平均 2.81 根 → 离线插值 **67.3ms**;在场 `inner_ms` 中位 **100ms**。 **结论(已按口径修正):** `inner_ms` 100ms 拆成 | 档 | 耗时 | 性质 | |---|---|---| | 信号链 + `_rebuild` | ~30ms | 真实计算,追 0 根时的地板 | | 追加 2.81 根(含 5m 腿) | ~37ms | **其中约 22ms 是缓存重复的浪费** | | 2 核争抢 | ~33ms | **1.49x**,不是 3.3x | ⚠️ 争抢是 1.5 倍而非 3.3 倍,所以「加核」的收益比上一版写的小得多。 **新发现的便宜杠杆:按币绑定 worker。** 现在 symbol 落到哪个 worker 是随机的, 每个 worker 的缓存都只见到自己处理过的根,于是人人都要追 2.81 根。若把每个 symbol 固定到同一个 worker(symbol hash → worker),每次只追 1 根:离线 67.3 → 45.0ms,在场约省 22ms × 1.49 ≈ **33ms/币,占 inner_ms 三分之一**。 代价是失去负载均衡,但十币两 worker 本来就是静态五五分。 `add_indicators` 增量化(§5.71 末尾那条未做项)会同时压掉追加档的大头, 两件事叠加。 ⚠️ 这和 §5.7 心跳里「币数 > 核数时加 worker 无用」不矛盾但要分清:**加 worker 确实无用(worker 已等于核数);加核有用但只值 1.5 倍那部分。** 原判定把两者 混成一句了。 --- ## 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~1w,2172~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` 反而不用改 | | 44 | **1m 过滤消融**:浅色信号毛R 只有深色的 1/6,差在质量不在成本,不能做 | | 45 | 重画审计脚本(时点重建,深色口径)——待跑 | | 46 | **引擎逐位对拍基线**(klu/klc/bi/seg/zs/信号/消费列哈希),重构安全网 | | 47 | **`available_ts` bis[2] A/B** → 作废,毛R 被打成零(§5.42) | | 48 | 多币开仓时间分布、并发、簇内顺序(首发是未来信息) | | 49 | **「有前序」样本外验证**(10 币 80 万根)→ 通过,余量 +6bp,6/6 时段同向 | | 50 | **信号根成交量**(用户假设「有资金的趋势才是好趋势」)→ **方向相反**,高量毛R 0.794 vs 低量 1.421,ATR 4/4 + 时段 7/7 同向;根因是在突破根上给别人接盘 | | 51 | **因子化仓位**(把 49/50 做成权重)→ 量因子该做成**开关**不是权重(硬砍 vs 加权:R夏普 0.496/0.442、并发 6.00/7.98);**前序因子不该进仓位**,边际收益买不起集中度。优势形态是削尾不是抬均值 | | 52 | **持仓中放量出场**(用户提议)→ 反的,最优点是永不触发;放量根标记的是止盈率翻倍的好单。自带模拟器与 `walk_exits` 逐笔对拍,10/10 币通过 | | 53 | **开仓前的量与趋势方向**(用户提议)⭐ → 前段量单调(越小越好,独立于信号根量);前段动量**驼峰形**(Q2 最好、Q4 最差)。`砍 mom60≥7` 全面优于 §3.39 的 `砍 vr60≥4`,且总R 比不砍还高。附滑点决策表 | | 54 | **开仓时段(亚/欧/美)**(用户提议)→ **无效**。1m 上 p=0.09、美盘两期反号;**7 年 × 5m/15m/30m 复核 p=0.62/0.67/0.31**,坐实。周日效应在 1m 上 p=0.008 但长样本**符号翻转**(+0.033),按多重比较的噪声处理。**排除型结论,含一次自我更正** | 输出都在 `research/out/`。**2026-08-27 清理过一轮**:step1~20 的输出(早期方法论 已被推翻,含未来函数偏差)与旧口径备份一并删除,只留支撑当前结论的证据。 脚本都还在,需要时可重跑。step31 及之后的 `.py` 都在 `research/` 下。 --- ## 9. 与用户协作的注意事项 - **用中文回复**(用户规则) - 用户技术判断力强,会主动纠正方向(他纠正过我对区间套的理解、 指出内存回收、指出结论印证缠论定义)。**他的领域直觉值得认真对待** - 他反复强调**不要再调参**,要落地验证 - 他讨厌长时间等待,早期多次问「还在跑吗」「好了吗」。 长任务要后台跑并主动汇报进度,不要让他干等 - 报数字时**必须交代口径**(复利/杠杆/费率),否则会误导 --- ## 10. 待办清单 - [ ] 🚨 **B4 实盘口径 PF 是 0.73,回测报 4.21 —— 最高优先级**(§3.3994)。 时点干净(100% 准时,一类那个坑没有),但实时会多产出 74% 的信号, 它们 PF 0.26 / t −6.21,**三道滤网按同比例刷除、完全挡不住**。 回测那 51 笔要事后全量重算才能识别,实盘当下分不出来。**要做三件事**: - [ ] **① 在 1m 上复验**(本轮测的是 5m/30m,实盘跑 1m)。机制相同但数字要单独出, `step68_live_window.py --ltf 1m --htf 5m`,这是决定是否要动实盘的依据 - [ ] **② 试「中枢构成笔 is_sure」与「等 N 根看信号是否仍在」**(均未测)。 前者用滞后换稳定性,后者直接过滤会消失的信号。目标是把那 74% 压下去 - [ ] **③ 通知服务器侧**:当前实盘在做一批回测里不存在的单。在 ① 出结果前 不建议改参数,但应知晓风险 - [x] **窗口左边界效应判为否**(§3.3994)。2001 根滚动窗口与 2万→4万根增长窗口 跑出完全相同的 789/197/592,中枢是局部结构,截断无影响 - [ ] **一类线:问题已完全定性,等一个决策**(§3.3992 / §3.3993)。 全局只有一个变量——**「是不是真线段端点」**:是则 PF 2.29(5m)/4.11(15m), 否则恒为 0.13,其余特征都只是它的噪声代理。**盈亏平衡精度 5m 72.6%、 15m 67.1%**,当前 28.7%,最好的实时特征 `ext_run` 到 44%。 要继续就是做一个「实时判真底」的分类器打到 ~70%,标签/特征/评估口径 §3.3993 已给全,但先验很难,且 70% 只是毛平衡,还要再扣 15 根因果滞后。 **这条不阻塞任何实盘工作** - [ ] **一类超时从 48 根放宽**(§3.3992,仍未跑,代价很低)。线段中位时长 113 根, 48 根在行情走完 42% 时平仓,真底那批 MFE 中位仅 2.37 ATR 疑似被截断 - [x] **二类关闭**(step64,§3.3991)。与一类不同,二类是**信号消失**而非信号指反: 两个方向 t 都贴零,且入场离结构止损位 4.87 ATR(一类 2.90), §3.3992 的结构止损解法对它不适用 - [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 步 - [x] **实盘信号计算改用增量** → ✅ 已上线(§5.7):清空十币 560 → 247ms。 配套的窗口裁剪也做了,但**重建不要用 `init_stream`**(它逐行 `iloc`, 比批量 `TF_DF(lean=True)` 慢 3.2 倍,第一版用它反而涨到 1686ms) - [ ] **按币绑定 worker(`symbol hash → worker`)** ⭐ 当前最便宜的一刀。 现在 symbol 随机落 worker,每个 worker 的缓存只见过自己处理的根, 于是人人都要追 2.81 根,其中约 22ms 是纯重复。绑定后每次只追 1 根: 离线 67.3 → 45.0ms,在场约省 **33ms/币(inner_ms 的三分之一)**。 代价是失去负载均衡,但十币两 worker 本来就是静态五五分。见 §5.72 - [ ] **`add_indicators` 增量化**(§5.71 末尾)。它是上面那个「追加档」的大头 ——每追一根都全表重算 2001 行,所以追加成本随根数近线性涨。 与绑定 worker 是叠加关系,不是二选一。 **拦路的是 Wilder RSI**:EMA/MACD/ATR 递推、BB/SMA/量比是窗口,都能 O(1) 更新到精确值,但 RSI 要额外维护 `avg_gain`/`avg_loss`(从输出反推 不出来)。**一处不精确就静默换掉一批信号,要做必须先扩对拍** - [ ] **`available_ts` 改用 `bis[2]`(中枢形成)而非 `bis[-1]`(中枢结束)的 完整 A/B** —— ✅ **已完成,结论是不换**(step47,见 §5.42)。 当时预判的「反向风险:会去做仍在形成中的中枢的突破,质量可能变差」 **正是实际发生的事**,且比预期严重:毛 R 0.933 → −0.000,8/8 币全变差。 **这条已关闭,不要重开。** - [ ] **查那 9.0% 的「中枢消失」是不是窗口左边界效应**(§5.41)。若是,则 「回测用全量历史建中枢、实盘用 2000 根窗口」是比重画更根本的口径差。 注意这条与 `bis[2]` 无关,**没有被 step47 否掉**,仍然要查 - [ ] **深色信号大样本重画审计**,脚本已写:`research/step45_repaint.py` (逐根时点重建,替换掉 step39 那个随机抽点的无效测法)。 §5.42 之后这条的意义变了:不再是为了「修掉重画」,而是为了**量化实盘 会多开多少仓、那部分的质量如何**——重画本身已确认是必付代价 - [ ] **写 V2 controller + 盘口缓冲,跑影子测量**(当前第一件事)。 字段与统计口径见 §6 第 3 步,**三条硬要求别漏**:只统计过滤后的信号根、 条件漂移与无条件漂移分开报、出场腿按 maker/taker 分开统计 - [x] **改掉 `shadow_report.py` 的 `BUDGET_BP`** → ✅ 已改为从 `lib/shadow_budget.py` import。原先硬编码的 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。 **这条当时优先级最高**:报表里 `drift > b` 与 `drift > b*0.6` 是自动 停机开关,用错的预算会让它比真实情况早约五倍触发,误杀一个可行的策略 - [x] **live 信号路径补上中枢阶梯过滤与 ATR 门控** → ✅ 已补 (`shadow_signal.py:11-18`:同向 + 阶梯 + `atr_pct ≥ ATR_GATE_BP`)。 原先只有 `h1_agree == 1`,测的不是我们要交易的那批信号 - [ ] **子类覆盖加启动断言** + **运行时 lag 探针(>800ms 停开仓)**,见 §6 第 2 步 - [ ] 补测 ADA / DOGE / AVAX 的漂移——真正要交易的是这批(预算 15~22bp)。 BTC/ETH/SOL 流动性最好,测滑点偏乐观是安全方向,但判可行性不能只看它们。 **BTC 大概率不该做 1m**:ATR 中位 2026 仅 6.5bp(11 币最低), 门控刷掉 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 对门控 + 流动性), 并留一批筛完不看、直接进实盘验证,以保住样本外的说服力 - [x] **因子化仓位已评估完(step51,§3.39)**,结论与分层表给的印象相反: 量因子做成**开关**(vr60 ≥ 4 不做),前序因子**不进仓位**。 剩下两件事: - [ ] **上「mom60 ≥ 7 不做」这个开关**(§3.391 已取代原来的 `vr60 ≥ 4`)。 与其他开关不同,**这个不需要等滑点数据**:它在 0bp 到 20bp 的每一档上 都不输给等权,5~12bp 区间还大幅领先,且总R 比不砍更高(657.7 vs 596.6) ——被砍的那 22% 期望为负。**唯一的前置动作是确认阈值附近没有断崖敏感** (阈值 7 是贴着 Q4 边界取的整数,看过分位表才取的)。 ⚠️ **live 路径目前没有这一层**:`shadow_signal.py` 的三道过滤是 同向 + 阶梯 + ATR 门控,`mom60` 连算都没算。要上得先在信号侧补出这个字段 - [ ] 若真实滑点实测 ≥15bp,再考虑叠加 `vpre10 ≥ 1.5` 与 `vr60 ≥ 4` ("三个都砍",保留 42%)。滑点 <12bp 时这个叠加是亏的,别提前上 - [ ] `有前序` 的余量优势改用**放宽 ATR 门控**兑现(多做几笔而非每笔做大)。 §3.31 已记,未回测 - [ ] **持仓中放量 → 延长 runner 目标 / 加仓**(step52 的建设性反面)。 已知:触发后止盈率 45.7% vs 全体 30.1%,控存活期后 5/5 档同向。 但基线已隐含吃到这部分(拿着不动就是),要多拿必须主动改目标位, **那是新参数,要重新回测**。R差 随触发时点递减(0.785 → 0.281), 窗口应偏前段