436 lines
28 KiB
Markdown
436 lines
28 KiB
Markdown
# ETF 自适应网格策略:分析与回测报告
|
||
|
||
> **⚠️ 本报告对应旧配置(三只标的每档均 1,000 股)。`py-client/etc/_etf.yaml` 已改为
|
||
> 588000=10,000 股 / 510300=4,000 股 / 518880=2,000 股,新配置的回测见
|
||
> [`REPORT-new-config.md`](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 |
|
||
|
||
**三句话结论**
|
||
|
||
1. **单轮经济性是正的、且稳定**:28 个"建网→整仓止盈"轮次全部盈利,单轮平均 +56.31 元、
|
||
平均占用 4.5 天、平均收益 **+0.89%**;佣金只占买入金额的 **0.184%**,不是收益拖累。
|
||
2. **账户层面收益极低**:平均资金占用仅 **1.68%**(峰值 19.4%),182 天权益只涨 **0.77%**
|
||
(年化约 1.0%)。策略"会赚钱但几乎不下注",收益瓶颈是**资金利用率**而非单轮质量。
|
||
3. **风险不对称、且尾部集中在单标的**:主出口只兑现盈利、**没有止损**,亏损全部沉淀在持仓里——
|
||
`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)**
|
||
|
||
1. 日线无法还原盘中顺序,`touch/bounce/close` 是从乐观到悲观的一个**区间**,不是精确点估计。
|
||
2. 未建模除息/复权:接口无复权价,`518880.SH` 等若有分红,阶梯锚点会整体偏移
|
||
(`docs/etf.md` 待办 §7.5 已列为未决项)。
|
||
3. 窗口只有 182 个交易日、28 个完整轮次,且这 9 个月里 `510300.SH` 几乎横盘(−0.48%)、
|
||
`588000.SH` +21.9%、`518880.SH` +14.1%,没有经历真正的单边下跌,**尾部风险未被样本覆盖**。
|
||
4. 未建模盘中跳空、涨跌停、停牌与流动性;按 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%——**这是唯一稳定的杠杆**。
|
||
|
||
---
|
||
|
||
## 六、策略层面的风险与结构性观察
|
||
|
||
1. **没有止损,亏损不可了结。** 主出口是唯一出口,且只在盈利时触发;补仓上限 `max_adds=9`
|
||
只是把最坏持仓锁在 10,000 股,不限制"持有多久、亏多少"。样本里 `510300.SH` 就是这样:
|
||
12 个盈利轮次的 +589.67 被一个未了结的 2 档仓位抹平并倒亏 4,034.73。
|
||
→ 策略的真实收益分布是"多数小赢 + 少数长期深套",**回测窗口内没出现真正的单边下跌,
|
||
这个尾部没有被检验**。
|
||
2. **入场门槛确实有效。** 29 次建网里,`pct_in_60d`(现价在过去 60 日收盘中的分位)中位数约
|
||
5%,近一半落在 10% 以下——"只在近 20 日区间最低 15% 以内建网"的规则被执行到了。
|
||
但仍有例外(如 2026-07-21 `588000.SH` 分位 43%、2026-06-09 `510300.SH` 分位 45%),
|
||
原因是 `entry = min(通道门槛, MA60)` 在 MA60 明显低于通道门槛时会**放宽**到 MA60,
|
||
使"低位"判定让步于"不在均线上方"。这与 §2.1 的说明一致,但意味着**它是均线闸门,
|
||
不总是低位闸门**。
|
||
3. **收益与风险都被资金利用率限制。** 平均占用 1.68%、峰值 19.4%。当前配置下策略既不会大赚也不会大亏,
|
||
1,534 元 / 182 天 ≈ **年化 1.54%**。**要它有意义,必须提高 `buy_shares` 或纳入更多标的**
|
||
(§5.3 的线性放大提供了直接证据)。
|
||
4. **多标的资金竞争没有发生。** 三个标的的最大同时占用只有 38,775 元(19.4%),
|
||
`docs/etf.md` §4.6 的"共享预算 + 在途预留"在样本里从未成为约束。
|
||
5. **`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%,
|
||
**都远低于任何有意义的目标**。
|
||
|
||
---
|
||
|
||
## 八、建议(按证据强度排序)
|
||
|
||
1. **先修资金利用率,别调格距。** `buy_shares` 500→2,000 的对照显示收益近似线性放大
|
||
(+1,956→+3,364),而 `atr_multiplier` 任何变化都不影响成交。要提升账户收益,
|
||
应提高单档股数或增加标的,而不是继续精调 ATR 倍数。
|
||
2. **让副出口真正可达,或删掉它。** 现状 `inner_grids×inner_step (1.4%~1.8%) > min_profit_pct (1.0%)`,
|
||
副出口被主出口完全压制(基准 0 次成交)。二选一:
|
||
把 `inner_step` 降到 `min_profit_pct/inner_grids` 以下(如 0.4),或明确它是兜底保险并接受它基本不触发。
|
||
3. **补一个"无止损"的对冲闸门。** 数据上最大风险来自单一标的的长期深套(`510300.SH` −4,034.73)。
|
||
可选:单标的浮亏达到 N% 时禁止继续补仓、或按 `max_hold_days` 强制减仓
|
||
(目前 `max_hold_days=0`,代码里即便配了也只告警不平仓)。
|
||
4. **重新审视"低位"判定。** `entry = min(通道门槛, MA60)` 在均线走低时会放宽门槛,
|
||
导致在 60 日分位 40%+ 的位置建网(样本里出现过)。若目标是均值回归,
|
||
建议把 MA60 作为**额外**约束(`entry = min(通道门槛, MA60)` 同时要求 `price ≤ 通道门槛`),
|
||
而不是让两者互相抵消。
|
||
5. **上线前必须补的三件事**(`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 重新抓取)
|
||
```
|
||
|
||
```powershell
|
||
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 # 入场机会频率
|
||
```
|
||
|