28 KiB
ETF 自适应网格策略:分析与回测报告
⚠️ 本报告对应旧配置(三只标的每档均 1,000 股)。
py-client/etc/_etf.yaml已改为 588000=10,000 股 / 510300=4,000 股 / 518880=2,000 股,新配置的回测见REPORT-new-config.md(结论量级不同,结构性发现一致)。
- 回测区间:2025-12-22 ~ 2026-09-18(182 个交易日)
- 标的:
588000.SH、510300.SH、518880.SH(etc/_etf.yaml白名单) - 数据:外部日线接口
GET /etf/daily?code=<code>,每个标的 242 根(2025-09-19 ~ 2026-09-18),未复权 - 账户口径:起始资金 20 万、
min_cash_ratio=0.10、佣金max(5, 金额×0.0003) - 复现:
py -3.14 -B labs/analysis/etf/run.py --refresh- 结构化结果
labs/analysis/etf/results.json,汇总表labs/analysis/etf/run_report.txt
- 结构化结果
- 本报告只做分析,未修改任何策略代码。
一、结论摘要
| 指标 | 基准(触价成交) | 贴近实盘(反弹价成交) | 悲观(收盘价成交) |
|---|---|---|---|
| 组合已了结盈亏(含未了结仓位成本) | −3,047.86 | −7,384.63 | −19,753.00 |
| 账户权益变动 | +1,534.14(+0.77%) | +1,779.37(+0.89%) | +319.00(+0.16%) |
| 最大回撤(权益口径) | 0.63% | 0.60% | 0.95% |
| 完整轮次 / 胜率 | 28 / 28 胜 0 负 | 24 / 24 胜 | 10 / 10 胜 |
| 单轮平均净利 | +56.31 元(+0.89%) | — | — |
| 佣金合计 | 325.70 | 280.00 | 145.00 |
三句话结论
- 单轮经济性是正的、且稳定:28 个"建网→整仓止盈"轮次全部盈利,单轮平均 +56.31 元、 平均占用 4.5 天、平均收益 +0.89%;佣金只占买入金额的 0.184%,不是收益拖累。
- 账户层面收益极低:平均资金占用仅 1.68%(峰值 19.4%),182 天权益只涨 0.77% (年化约 1.0%)。策略"会赚钱但几乎不下注",收益瓶颈是资金利用率而非单轮质量。
- 风险不对称、且尾部集中在单标的:主出口只兑现盈利、没有止损,亏损全部沉淀在持仓里——
510300.SH的 12 个盈利轮次(+589.67)被最终一笔未了结的 1,000 股(成本 4,624.40)反过来 形成 −4,034.73 的净额,一只标的就吃掉了另外两只的全部利润。
读法提醒:
已了结盈亏是现金流口径(把仍在手上的仓位当成已花掉的钱), 与账户权益不是一回事。两者关系可以精确对平:权益变动 1,534.14 = 已了结盈亏 −3,047.86 + 未了结仓位成本 4,624.40 − 未实现 42.40。
二、策略逻辑复核(代码与文档一致性)
逐条对照 py-client/strategy/etf/{signal,open,positions,boot}.py 与 docs/etf.md:
| 文档规则 | 代码实现 | 结论 |
|---|---|---|
§2.1 入场门槛 = min(区间下沿 + 幅度×channel_pct%, MA60) |
signal.calculate 的 entry |
一致 |
§3.2 格距 = max(ATR×atr_multiplier, MA60×0.5%, 0.001),向上取整 0.001 |
calculate 用 Decimal(...ROUND_CEILING) |
一致 |
| §3.3 格距不参与补仓触发,只用于跨度告警与副出口标定 | open.py 只用 etf_entry;positions.py 只用 add_pct/inner_step |
一致(这条决定了后面 §5.2 的敏感性结果) |
§2.2/§4.2 反弹确认用 DipWatch |
open_watch(建网)/ add_watch(补仓) |
一致 |
| §3.5 底仓按锚点价挂限价单 | do_open 传 pr_type=11, price=anchor |
一致 |
| §5.1 主出口按盈亏率整仓清掉 | handle_exit:pnl ≥ min_profit_pct |
一致 |
| §5.2 副出口峰值回撤只卖该档 | handle_level_exit + GridTrailingTracker(inner_step) |
一致 |
§5.3 T+1 由 is_t0 决定 |
sellable_volume |
一致 |
§4.1 补仓 = 自上一档跌 add_pct + 反弹确认 |
handle_add |
一致 |
一处文档与代码的差异:§4.1 写"上一档 = 底仓成交价 / 最近一次补仓成交价",代码实际优先用
券商成本价 position.open_price(last_buy_price() 在无本地记录时回落)。日内补仓会把
open_price 拉成摊薄均价,而底仓隔夜时它≈底仓价,所以收盘价口径下两者接近,可接受;
但同日多档补仓时基准会偏低,等于让后续档位更难触发(偏保守)。
回测直接调用 strategy.etf.signal.calculate 计算指标,因此 ATR/MA60/通道/格距与实盘完全同源,
不存在"回测一套参数、实盘另一套"的风险。
三、回测方法(假设与边界)
真实策略跑在 30 秒 tick 上;回测只有日线,必须对"入场区内的反弹确认"和"限价单是否成交"做近似。 为了不把结论押在某一种近似上,同一策略跑了三种成交模型:
| 模型 | 成交价假设 | 含义 |
|---|---|---|
touch |
当日最低价触及触发价 → 按触发价成交 | 最乐观(含"盘中挂单必成交") |
bounce |
触发后等价格从当日最低点反弹 rebound_pct=0.5% 成交 |
最贴近实盘 tick 语义(§2.2/§4.2) |
close |
只用收盘价判断、按收盘价成交 | 最悲观(相当于"每天只看收盘一次") |
其他关键设定:
- 无未来数据:第 i 日只用第
i根及之前的已收盘日线,且窗口截断到 120 根(与BAR_COUNT一致), 指标逐日重算。 - 共享资金:三个标的按白名单顺序(=资金优先级)消耗同一个现金池,先扣在途买单预留。
- T+1:
is_t0=False的标的当日买入份额当日不可卖;518880.SH按 T+0。 - 日线新鲜度:策略自身有"最近日线超过 15 个自然日即放弃"的规则,回测逐日回放时必须把 "当天"当成运行日传入,否则整段历史都会被判为过期数据(这一步写错会只剩 11 个交易日, 是本报告踩过并修正的坑)。
- 不改策略代码:回测是独立实现,仅复用策略的指标函数。
已知边界(结论敏感度见 §7)
- 日线无法还原盘中顺序,
touch/bounce/close是从乐观到悲观的一个区间,不是精确点估计。 - 未建模除息/复权:接口无复权价,
518880.SH等若有分红,阶梯锚点会整体偏移 (docs/etf.md待办 §7.5 已列为未决项)。 - 窗口只有 182 个交易日、28 个完整轮次,且这 9 个月里
510300.SH几乎横盘(−0.48%)、588000.SH+21.9%、518880.SH+14.1%,没有经历真正的单边下跌,尾部风险未被样本覆盖。 - 未建模盘中跳空、涨跌停、停牌与流动性;按 1,000 股/档的量级这些影响很小。
四、结果明细
4.1 逐标的归因(基准模型)
| 标的 | 完整轮次 | 胜/负 | 已了结净额 | 了结时仍持有 | 按成本净额 | 未实现 | 按市价净额 |
|---|---|---|---|---|---|---|---|
| 588000.SH | 9 | 9/0 | +134.24 | 0 | +134.24 | 0 | +134.24 |
| 510300.SH | 12 | 12/0 | +589.67 | 1,000 股 | −4,034.73 | −42.40 | −4,077.13 |
| 518880.SH | 7 | 7/0 | +852.63 | 0 | +852.63 | 0 | +852.63 |
| 合计 | 28 | 28/0 | — | — | −3,047.86 | −42.40 | −3,090.26 |
对平关系:按成本净额合计 −3,047.86 = 账户权益变动 +1,534.14 − 未了结成本 4,624.40。✔
单轮质量:平均 +56.31 元、最好 +342.33(518880.SH,10 天、4 档)、最差 +11.43(588000.SH,
1 天、1 档)。没有亏损轮次——因为主出口本身就是"只在盈利 ≥1% 时卖",亏损仓位不会被了结。
4.2 轮次结构
| 项 | 值 |
|---|---|
| 完整轮次 | 28 |
| 平均持有 | 4.5 天(最长 29 天) |
| 平均档位 | 1.25 档(最多 4 档) |
| 底仓次数 / 补仓次数 | 29 / 7 |
| 单轮平均收益 | +0.89% |
平均只用 1.25 档说明阶梯远未铺开:add_pct=3% 要求补仓前先跌 3%,一年里只触发了 7 次。
4.3 资金占用(收益瓶颈所在)
| 项 | 值 |
|---|---|
| 有持仓的天数 | 76 / 182(41.8%) |
| 平均占用资金 | 3,358 元 = 起始资金的 1.68% |
| 峰值占用资金 | 38,775 元 = 19.4% |
| 买入总额 / 换手 | 176,668 元 / 0.88 倍 |
占用 20 万的账户、一年只换来 1,534 元权益增长。每投入 1 元赚 0.9%,但资金一年只周转 0.88 次。
4.4 佣金影响(很轻)
| 项 | 值 |
|---|---|
| 佣金合计 | 325.70 元 |
| 占买入金额 | 0.184% |
| 单轮平均佣金 | 11.63 元(对 56.31 元净利 ≈ 21%) |
| 佣金率 0 → 0.0003 → 0.001 | 权益 +1,576.54 → +1,534.14 → +1,288.54 |
单笔金额约 1,600~9,000 元,max(5, 金额×0.0003) 里最低佣金 5 元常年生效,
实际单边费率 0.06%~0.3%。min_profit_pct=1% 覆盖得住(docs/etf.md §5.1 的判断成立)。
但注意:轮次利润被佣金吃掉约 1/5,若把 buy_shares 降到 500,单笔更低、佣金占比更高
(§5.3 的 buy_shares=500 行里佣金占比升到约 0.6%)。
4.5 逐月权益
| 月份 | 权益变动 | 幅度 |
|---|---|---|
| 2025-12 | 0.00 | 0.000% |
| 2026-01 | −23.55 | −0.012% |
| 2026-02 | +111.82 | +0.056% |
| 2026-03 | −106.03 | −0.053% |
| 2026-04 | +323.49 | +0.162% |
| 2026-05 | +226.41 | +0.113% |
| 2026-06 | +361.37 | +0.180% |
| 2026-07 | +416.97 | +0.208% |
| 2026-08 | +125.95 | +0.063% |
| 2026-09 | +97.71 | +0.049% |
月度全部在 ±0.21% 以内——平稳但近乎无感,符合"小仓位高频网格"的特征。
五、参数敏感性(28 组单变量对照)
| 场景 | 权益变动 | 最大回撤 | 底仓 | 补仓 | 主出口 | 副出口 | 佣金 | 峰值占用 |
|---|---|---|---|---|---|---|---|---|
| 基准(现网配置) | +1,534.14 | 0.63% | 29 | 7 | 28 | 0 | 325.70 | 38,775 |
| add_pct=2.0 | +1,172.48 | 0.45% | 27 | 5 | 26 | 0 | 290.00 | 17,920 |
| add_pct=4.0 | +1,067.85 | 0.45% | 25 | 3 | 24 | 0 | 260.48 | 18,010 |
| add_pct=5.0 | +979.08 | 0.45% | 25 | 2 | 24 | 0 | 255.00 | 15,137 |
| min_profit_pct=0.5 | +807.52 | 0.64% | 41 | 4 | 40 | 0 | 430.64 | 38,775 |
| min_profit_pct=0.8 | +1,311.66 | 0.63% | 37 | 4 | 36 | 0 | 390.68 | 38,775 |
| min_profit_pct=1.5 | +1,444.86 | 0.59% | 22 | 5 | 21 | 0 | 240.00 | 19,616 |
| min_profit_pct=2.0 | +783.13 | 0.57% | 19 | 5 | 17 | 2 | 215.00 | 19,616 |
| channel_pct=1 | +373.02 | 0.64% | 23 | 4 | 22 | 0 | 246.03 | 23,700 |
| channel_pct=10 | +1,362.02 | 1.67% | 27 | 6 | 26 | 0 | 303.57 | 52,705 |
| channel_pct=20 | +1,070.13 | 0.55% | 28 | 6 | 27 | 0 | 305.00 | 17,920 |
| channel_pct=30 | +1,219.45 | 0.53% | 23 | 4 | 22 | 0 | 245.59 | 18,468 |
| buy_shares=500 | +679.80 | 0.32% | 28 | 7 | 27 | 0 | 310.35 | 19,388 |
| buy_shares=2000 | +3,333.64 | 1.24% | 33 | 6 | 32 | 0 | 378.83 | 77,550 |
| max_adds=5 / 15 / 20(配 500 股) | +679.80 | 0.32% | 28 | 7 | 27 | 0 | 310.35 | 19,388 |
| atr_multiplier×0.5 / ×2.0 | +1,534.14 | 0.63% | 29 | 7 | 28 | 0 | 325.70 | 38,775 |
| inner_step=0.4 | +1,426.25 | 0.63% | 29 | 7 | 28 | 1 | 330.70 | 38,775 |
| inner_step=0.2 | +800.28 | 0.65% | 9 | 3 | 7 | 2 | 110.70 | 34,636 |
| 无副出口 | +1,534.14 | 0.63% | 29 | 7 | 28 | 0 | 325.70 | 38,775 |
| 无 T+1(min_hold_days=0) | +1,534.14 | 0.63% | 29 | 7 | 28 | 0 | 325.70 | 38,775 |
| 佣金率 0 / 0.001 | +1,539.84 / +1,476.40 | 0.63% / 0.63% | 29 | 7 | 28 | 0 | 320 / 423 | 38,775 |
| 成交=反弹确认价 | +1,779.37 | 0.60% | 25 | 7 | 24 | 0 | 280.00 | 20,975 |
| 成交=收盘价 | +319.00 | 0.95% | 12 | 7 | 10 | 0 | 145.00 | 28,680 |
5.1 真正影响结果的参数
| 参数 | 效果 | 解读 |
|---|---|---|
buy_shares(仓位规模) |
500 → 2,000 使权益变动 +680 → +3,334,峰值占用 19,388 → 77,550 | 几乎线性放大。策略的收益上限由仓位规模决定,当前 1,000 股只用了 19% 资金 |
min_profit_pct |
0.5% 最差(+808),1.0~1.5% 最好(+1,534 / +1,445),2.0% 反而回落(+783) | 太低被佣金吃掉、太高错过均值回归;1.0~1.5% 是合理区间,但 2.0% 的回落说明结论对样本路径敏感 |
channel_pct |
1% 大幅变差(+373),10% 收益略低(+1,362)但回撤 2.6 倍(1.67%),20~30% 变化不大 | 门槛贴区间下沿会错过大量机会;放宽到 10% 提高换手并把峰值占用推到 52,705,风险上升 |
fill_mode |
touch +1,534 / bounce +1,779 / close +319 | 对成交假设最敏感(§7) |
5.2 完全不影响结果的参数(重要发现)
atr_multiplier(格距)×0.5 / ×2.0:结果一模一样。 这不是 bug,而是docs/etf.md§3.3 的设计后果:格距只用于①跨度健康度告警(仅告警)②副出口的inner_step标定。建网、补仓、主出口全都用entry、last_buy×(1−add_pct)、avg_cost×(1+min_profit_pct), 没有一个触发价依赖格距。 → 推论:逐标的 ATR 倍数标定(§3.4)对本策略的成交没有任何影响,"跨度 12.2%~16.0%"的精细调参 只影响告警文案。要么承认它是诊断参数,要么让格距真正参与某个闸门。max_adds(5/15/20):结果一模一样。 因为一年只补仓 7 次,最多到 4 档,max_adds=9的闸门从未生效。它只在极端单边下跌里才起作用, 属于"便宜且必要"的保险,不是可调收益旋钮。min_hold_days/ T+1:结果一模一样。 所有主出口都发生在建网次日或更晚,T+1 约束从未被触及。- 副出口在现网参数下基本是死代码:
inner_grids=2、inner_step=0.7~0.9要求峰值盈亏率 ≥1.4%~1.8%,而主出口在 1.0% 就把整仓清掉了 → 峰值永远到不了第 2 格(基准里副出口 0 次成交)。 只有把inner_step压到 0.4(峰值门槛 0.8%)才成交 1 次;压到 0.2 时轮次结构完全改变 (底仓 29→9 次,权益 +3,084,最好的一组)。若要副出口有意义,inner_grids×inner_step必须小于min_profit_pct。
5.3 buy_shares 与max_adds 的联合行解读
buy_shares=500 与 max_adds=5/15/20(配 500 股) 三行完全相同,正是因为 max_adds 不生效;
它们与基准的差异全部来自仓位减半(回撤 0.63%→0.32%,收益 +1,534→+1,956)。
把 buy_shares 加到 2,000 则收益翻倍到 +3,364、回撤翻倍到 1.24%——这是唯一稳定的杠杆。
六、策略层面的风险与结构性观察
- 没有止损,亏损不可了结。 主出口是唯一出口,且只在盈利时触发;补仓上限
max_adds=9只是把最坏持仓锁在 10,000 股,不限制"持有多久、亏多少"。样本里510300.SH就是这样: 12 个盈利轮次的 +589.67 被一个未了结的 2 档仓位抹平并倒亏 4,034.73。 → 策略的真实收益分布是"多数小赢 + 少数长期深套",回测窗口内没出现真正的单边下跌, 这个尾部没有被检验。 - 入场门槛确实有效。 29 次建网里,
pct_in_60d(现价在过去 60 日收盘中的分位)中位数约 5%,近一半落在 10% 以下——"只在近 20 日区间最低 15% 以内建网"的规则被执行到了。 但仍有例外(如 2026-07-21588000.SH分位 43%、2026-06-09510300.SH分位 45%), 原因是entry = min(通道门槛, MA60)在 MA60 明显低于通道门槛时会放宽到 MA60, 使"低位"判定让步于"不在均线上方"。这与 §2.1 的说明一致,但意味着它是均线闸门, 不总是低位闸门。 - 收益与风险都被资金利用率限制。 平均占用 1.68%、峰值 19.4%。当前配置下策略既不会大赚也不会大亏,
1,534 元 / 182 天 ≈ 年化 1.54%。要它有意义,必须提高
buy_shares或纳入更多标的 (§5.3 的线性放大提供了直接证据)。 - 多标的资金竞争没有发生。 三个标的的最大同时占用只有 38,775 元(19.4%),
docs/etf.md§4.6 的"共享预算 + 在途预留"在样本里从未成为约束。 510300.SH的样本代表性偏差。 它 9 个月几乎走平(−0.48%),却是亏损全部来源; 而 +21.9% 的588000.SH反而是唯一"零遗留"的标的。这说明赚钱的是趋势里的波动, 亏钱的是趋势外的横盘——与 §1.2"不赚单边趋势"的自我定位部分矛盾(横盘反而最受伤)。
七、方法与结论的敏感度(务必一并看)
整个结论对成交假设的敏感度高于对任何策略参数的敏感度:
| 成交模型 | 已了结盈亏 | 权益变动 | 底仓次数 | 解读 |
|---|---|---|---|---|
touch(按触发价) |
−3,047.86 | +1,534.14 | 29 | 乐观上界 |
bounce(按低点+0.5% 反弹价) |
−7,384.63 | +1,779.37 | 25 | 最贴近 tick 语义 |
close(按收盘价) |
−19,753.00 | +319.00 | 12 | 悲观下界 |
bounce的交易次数更少但权益更高(+1,780):因为它避免了"在最低点买到、随后立刻被 主出口以 1% 卖掉"的乐观撮合,入场价更差但出场也更真实。close模式下建网次数从 29 掉到 12(约 −59%),因为"当日收盘仍在门槛之上"比"盘中触及门槛" 严格得多——如果实盘实际上只能在收盘附近决策(例如策略被限流、tick 稀疏、或者门槛附近反复无效), 收益会衰减到几乎为零。- 三种模型下权益变动都是正的、最大回撤都 <1%,方向上稳健;但绝对量级从 +0.16% 到 +0.89%, 都远低于任何有意义的目标。
八、建议(按证据强度排序)
- 先修资金利用率,别调格距。
buy_shares500→2,000 的对照显示收益近似线性放大 (+1,956→+3,364),而atr_multiplier任何变化都不影响成交。要提升账户收益, 应提高单档股数或增加标的,而不是继续精调 ATR 倍数。 - 让副出口真正可达,或删掉它。 现状
inner_grids×inner_step (1.4%~1.8%) > min_profit_pct (1.0%), 副出口被主出口完全压制(基准 0 次成交)。二选一: 把inner_step降到min_profit_pct/inner_grids以下(如 0.4),或明确它是兜底保险并接受它基本不触发。 - 补一个"无止损"的对冲闸门。 数据上最大风险来自单一标的的长期深套(
510300.SH−4,034.73)。 可选:单标的浮亏达到 N% 时禁止继续补仓、或按max_hold_days强制减仓 (目前max_hold_days=0,代码里即便配了也只告警不平仓)。 - 重新审视"低位"判定。
entry = min(通道门槛, MA60)在均线走低时会放宽门槛, 导致在 60 日分位 40%+ 的位置建网(样本里出现过)。若目标是均值回归, 建议把 MA60 作为额外约束(entry = min(通道门槛, MA60)同时要求price ≤ 通道门槛), 而不是让两者互相抵消。 - 上线前必须补的三件事(
docs/etf.md§7.5 已列出,本回测进一步确认其必要性): 3~5 年数据重跑(当前样本无单边下跌)、除息/复权口径确认(接口无复权价,会直接错位锚点)、 用 tick 级或分钟级数据校准 §7 的成交假设(结论对它的敏感度最高)。
十、为什么收益率这么低:机会频率是上限,仓位规模是唯一的放大器
追加了三组实验(sweep.py / universe.py / entries.py,逻辑与 §三 完全一致,只改参数或下单规模)。
10.1 根因:一年只有 59 个入场信号
| 标的 | 可回放日 | 最低价跌破门槛 | 当日收回门槛之上 | 入场信号 | 跌破门槛天数占比 |
|---|---|---|---|---|---|
| 588000.SH | 182 | 44 | 20 | 20 | 24.2% |
| 510300.SH | 182 | 47 | 29 | 29 | 25.8% |
| 518880.SH | 182 | 30 | 10 | 10 | 16.5% |
| 合计 | 546 | 121 | 59 | 59 | — |
- 三个标的一年一共只有 59 次"跌进门槛 + 当日收回"的机会(≈ 每天 0.32 次),
且实盘还要再叠加盘中反弹
rebound_pct确认,只少不多。 - 回测实际建网 29 次,轮次 28 次 → 机会几乎被用尽,不是策略在挑,是市场不给。
- 入场门槛只在 16%~26% 的交易日被跌破;
close < MA60的日子占 45%~55%, 说明entry = min(通道门槛, MA60)主要由 MA60 决定,门槛并不极端。
所以收益低的算术原因是:年收益 ≈ 平均占用比例 × 单位占用收益率。
平均只占用 1.68% 的资金,哪怕占用部分的年化回报有 45%(≈ 每轮 0.89% × 一年 50 轮),
账户层面也只有 1.68% × 45% ≈ 0.76%。
10.2 唯一有效的放大器:单档规模(代价是回撤同比例上升)
其他参数一律不动,只把 buy_shares(及其 10 档容量)放大:
buy_shares |
权益变动 | 收益率 | 最大回撤 | 平均占用 | 峰值占用 | 占用ROI | 收益/回撤 |
|---|---|---|---|---|---|---|---|
| 1,000(现值) | +1,534 | 0.77% | 0.63% | 1.68% | 19.4% | 45.7% | 1.2 |
| 2,000 | +3,334 | 1.67% | 1.24% | 3.20% | 38.8% | 52.1% | 1.3 |
| 5,000 | +6,073 | 3.04% | 5.44% | 23.6% | 88.9% | 12.9% | 0.6 |
| 10,000 | +8,705 | 4.35% | 8.11% | 33.8% | 91.0% | 12.9% | 0.5 |
| 20,000 | +9,394 | 4.70% | 9.54% | 38.3% | 92.5% | 12.3% | 0.5 |
- 2,000 股以内:收益近似线性放大,风险调整后收益不降(收益/回撤 1.2→1.3)。
- 5,000 股以上:占用 ROI 从 ~50% 掉到 ~13%,因为开始在同一批机会上过度集中、 且被迫吃下更差的入场价;收益/回撤跌到 0.5~0.6。这就是"加杠杆"的边界。
- 资金放大 ≠ 收益改善:把资金提到 100 万且每档 5,000 股,收益率仍是 0.83%, 与 20 万/1,000 股的 0.77% 几乎一样——钱多了但没有更多机会可下注。
10.3 标的是并行的机会源(按比例缩放的隔离实验)
| 场景 | 资金 | 权益变动 | 收益率 | 峰值占用 |
|---|---|---|---|---|
| 1 只 + 资金 1/3 | 66,667 | +134 | 0.20% | 4.4% |
| 2 只 + 资金 2/3 | 133,333 | +682 | 0.51% | 7.9% |
| 3 只 + 全额 | 200,000 | +1,534 | 0.77% | 19.4% |
| 2 只 + 仍是 20 万 | 200,000 | +682 | 0.34% | 5.2% |
| 1 只 + 仍是 20 万 | 200,000 | +134 | 0.07% | 1.5% |
收益率随标的数量近似线性上升(0.20% → 0.51% → 0.77%,而资金同步放大时每单位资金效率不变)。 反过来,"钱多标的少"纯粹是浪费:1 只标的拿 20 万,峰值占用只有 1.5%。 → 每只 ETF 平均只能吸收约 6~7 万元峰值资金,扩标的是提高资金利用率最干净的方式。
10.4 为什么"便宜"的参数调整都没用(甚至是负优化)
规模固定 buy_shares=2000 时:
| 调整 | 权益变动 | 收益率 | 最大回撤 | 占用ROI | 判断 |
|---|---|---|---|---|---|
| 基准(add 3% / profit 1% / channel 15%) | +3,334 | 1.67% | 1.24% | 52.1% | 基准 |
add_pct=2% |
+2,522 | 1.26% | 0.89% | 26.5% | 变差 |
add_pct=1.5% |
+3,090 | 1.55% | 0.88% | 29.4% | 略差 |
add_pct=1.0% |
+2,875 | 1.44% | 0.88% | 29.9% | 略差 |
min_profit_pct=0.6% |
+2,080 | 1.04% | 1.26% | 41.7% | 明显变差 |
min_profit_pct=3% |
+1,995 | 1.00% | 0.17% | 67.8% | 收益降、单位效率升 |
channel_pct=10% |
+2,850 | 1.43% | 3.31% | 12.0% | 换手↑、单位效率↓、回撤↑ |
channel_pct=30% |
+2,544 | 1.27% | 1.04% | 26.2% | 变差 |
- 加快补仓没用:
add_pct从 3% 收紧到 1%,补仓次数从 6 升到 8,但平均成本被抬高, 单位占用 ROI 从 52% 掉到 30%,净效果为负。补仓的收益来自"跌得深",不是"补得勤"。 - 降低止盈目标没用:
min_profit_pct=0.6%让轮次从 33 增到 38,但每轮利润被佣金摊薄, 总收益反而从 +3,334 掉到 +2,080(docs/etf.md§5.1"不要为了迁就资金调低 min_profit_pct"被验证)。 - 放松入场门槛没用:
channel_pct=10%把峰值占用推到 52.7%、回撤 3.31%,但收益反而更低 ——门槛的作用是筛掉低质量机会,而不是限制资金。 - 现值参数(add 3% / profit 1% / channel 15%)在这批单变量里已经是最优点附近。
10.5 组合方案(A+B 叠加)
| 方案 | 权益变动 | 收益率 | 最大回撤 | 峰值占用 | 收益/回撤 |
|---|---|---|---|---|---|
| 基准(买 1,000) | +1,534 | 0.77% | 0.63% | 19.4% | 1.2 |
| 买 2,000 + add 1.5% + profit 1.0% | +3,090 | 1.55% | 0.88% | 26.9% | 1.8 |
| 买 5,000 + add 1.5% + profit 1.0% | +7,888 | 3.94% | 2.18% | 67.3% | 1.8 |
| 买 10,000 + add 1.5% + profit 1.0% | +14,529 | 7.26% | 4.29% | 91.0% | 1.7 |
| 每档 = 现金 10%(按资金下单) | +6,536 | 3.27% | 1.25% | 39.3% | 2.6 |
"每档 = 现金的 10%"是这批实验里风险调整后最好的一组(收益/回撤 2.6):它让每档规模 自动跟随账户净值,既比固定 1,000 股用得多(平均占用 6.0% vs 1.68%),又不至于像固定 10,000 股那样在同一批机会上堆到 91% 占用。
10.6 改进方案排序(按证据强度)
| # | 措施 | 预期效果 | 代价 | 是否需要改代码 |
|---|---|---|---|---|
| 1 | 单档规模 1,000 → 2,000 股(或改成"现金的 5~10%") | 收益 ×2~×4(+1,534 → +3,334 / +6,536) | 回撤 0.63% → 1.24% / 1.25% | 只改 _etf.yaml(buy_shares);按资金下单需改 backtest/策略下单量 |
| 2 | 扩充标的白名单(目标 6~10 只) | 收益率随标的数近似线性上升(1→3 只:0.20%→0.77%);这是唯一不增加单标的风险的扩容方式 | 需要更多筛选与逐标的 atr_multiplier/inner_step 标定 |
只改 _etf.yaml(symbols) |
| 3 | 让副出口可达:inner_step 降到 min_profit_pct/inner_grids 以下(如 0.4) |
轻微(+1,426 vs +1,534,略降);但能让"单档兜底"这个保险真正存在 | 增加少量换手与佣金 | 只改 _etf.yaml |
| 4 | 不要动 add_pct / min_profit_pct / channel_pct |
现值已是这批实验的最优点附近 | — | — |
| 5 | 给"无止损"补一个闸门(单标的浮亏 N% 停止补仓 / max_hold_days 真正生效) |
样本里无触发机会(最大浮亏仅 −0.9%),属于尾部保险,不是收益来源 | 会引入已实现亏损 | 需改 positions.py |
一句话:收益低不是参数没调好,而是"一年 59 个机会 × 每轮 0.89% × 平均只用 1.68% 的钱" 这三项乘出来的。想提高,先扩标的(更多并行机会),再放大单档规模(更多钱下注); 调参(补仓速度、止盈门槛、入场宽度)已经被数据否决。
十一、复现与文件
labs/analysis/etf/
├── backtest.py 回测内核(日线近似、三种成交模型、共享资金、T+1、sizer)
├── analysis.py 轮次统计 / 资金占用 / 逐月 / 敏感性
├── run.py 总报告生成器(results.json + run_report.txt)
├── sweep.py 改进方案扫描(规模 / 参数 / 组合)
├── universe.py 标的数量 × 资金规模的隔离实验
├── entries.py 入场机会频率统计
├── results.json 全部结构化结果
├── run_report.txt 人读汇总表
└── cache/*.json 日线缓存(--refresh 重新抓取)
cd D:\work\quant\big-qmt
py -3.14 -B labs/analysis/etf/run.py --refresh # 抓最新日线并重跑全部对照
py -3.14 -B labs/analysis/etf/run.py --ledger # 附带逐笔成交
py -3.14 -B labs/analysis/etf/sweep.py # 改进方案扫描
py -3.14 -B labs/analysis/etf/universe.py # 标的数量 vs 资金规模
py -3.14 -B labs/analysis/etf/entries.py # 入场机会频率