# T+1 口径核查报告：测试里到底有没有 T+1，A 股行业基算不算？

**核查时间** 2026-10-02 · 窗口 2020-01-02 ~ 2026-09-29（1635 个交易日）· zip 分钟数据 · 万 0.5 佣金

---

## 一、三个结论先讲

1. **是，所有测试都开了 T+1**（主池、A 股行业基、合池、松耦合——两个 sleeve 共用一份 T+1 账本）。
2. **A 股行业基那 30 只，现实中确实全部是 T+1**（A 股股票型 ETF），所以对它而言这个约束是**真实约束**，不是宽松假设 —— 但下面第 3 条说明引擎并没有真正把它执行到位。
3. **★引擎的实现有实质弱化**：每次调仓都会"重建"当日买入锁定，早期买入的锁定被丢掉 → **同一交易日的回转被大量放行**。
   A 股行业基单跑时有 **18,882 笔卖出超出了真实可卖量**（累计相当于它全部换手 4,604 的量）。
   把 T+1 改成严格后，**A 股行业基的"5 分钟参数"结论被彻底推翻**（Calmar 1.66 → 0.76）。

---

## 二、事实一：参数确实开着，两个 sleeve 共用一本账

| 位置 | 证据 |
|---|---|
| `mix_engine.py` | `run_combined(..., t1_rule=True, ...)` 默认开启；卖出可用量 = `hold - 当日锁定`，被挡下时计入 `t1_blocked` |
| `backtest_minute.py` | `run_grid_run(..., t1_rule=True)` 默认开启（所有离线结论都走这里） |
| 熔断分支 | `hold = {kk: w for kk, w in hold.items() if kk in bought_today}` —— 熔断清仓时也保留当日买入部分（T+1 约束下当日卖不掉） |

所以测试**不是**偷偷用了 T+0。

---

## 三、事实二：按真实交易规则给两个池子分类

| 池 | 真实 T+0 | 真实 T+1 |
|---|---|---|
| **主池 145** | **85 只（59%）**：81 只跨境（港股 57 / 美股 13 / 其他海外 7 / 日本 3）+ 4 只实物黄金（518880、159937、518680、159830）+ 1 只豆粕期货（159985） | 60 只（41%）：宽基 47 + 科创主题 8 + 贵金属股 3 + 562060（标普A股红利，实为A股股票）+ 159321（黄金产业，实为股票） |
| **A 股行业基 30** | **0 只** | **30 只（100%）** —— 全部是 A 股股票型 ETF（半导体、通信、券商、银行、有色、煤炭、红利、医药、军工、白酒、消费、地产、光伏、新能源车、AI…） |

**所以 A 股行业基这边不存在"用 T+0 冒充 T+1"的问题：它们本来就是 T+1。** 你的担心在这一侧不成立；真正的问题在下面。

（分类时注意两个易错点：`512400 有色金属ETF` 跟踪的是**有色金属股票指数**、不是商品期货；`515450 标普中国A股大盘红利低波50` 名字里有"标普"但是 **A 股**指数 —— 这两只都是 T+1，最初的关键词自动分类把它们误判成 T+0，已人工纠正。）

---

## 四、事实三：引擎的 T+1 只"锁一轮"，不是"锁一天"

`backtest_minute.py` 与 `mix_engine.py` 的写法是：

```python
# 每次成交后
bought_today = {kk: inc[kk] * scale for kk in inc if ...}   # ← 整体重建
```

**问题**：只有"本次新增买入"的标的留在锁定表里，之前几次调仓买入的标的**锁定被清空** → 下一根 bar 就能卖。
效果上等于"买入后只要过了下一次调仓，就可以当日卖出"，而不是"当日买入的份额当日一律不可卖"。

### 违规量实测（诊断计数器）

| 方案 | 卖出超出真实可卖量的笔数 | 超出量合计（NAV 口径） |
|---|---|---|
| A 股行业基单跑（e5/m10/K3/2%） | **18,882 笔** | **4,780** |
| 主池 145 单跑（e30/m15/K3/1%） | 3,578 笔 | 806 |
| 松耦合同账户 80/20 | 18,786 笔 | 1,434 |

A 侧的 4,780 ≈ 它的全部换手（4,604）—— 换句话说，**A 侧之前的成绩几乎完全建立在同日回转上**。

---

## 五、把 T+1 改成严格后重跑（同日买入份额当日绝不卖）

### 5.1 A 股行业基单跑：参数排序整个翻转

| A 侧参数 | 默认口径（弱 T+1） | **严格 T+1** |
|---|---|---|
| **e5 / m10 / K3 / 2%**（原"最优"） | 5.618 / 29.38% / -17.74% / **Calmar 1.66** | 3.346 / 19.75% / -26.02% / **0.76** |
| e15 / m10 | 3.996 / 22.96% / -20.57% / 1.12 | 3.334 / 19.69% / -26.18% / 0.75 |
| **e30 / m10** | 4.526 / 25.27% / -25.01% / 1.01 | 4.226 / 24.00% / **-24.11%** / **1.00** ← 严格下最好 |
| e30 / m15 | 3.577 / 20.95% / -25.14% / 0.83 | 2.485 / 14.55% / **-41.90%** / 0.35 |
| e60 / m10 | 4.760 / 26.22% / -19.14% / 1.37 | 3.814 / 22.11% / -28.32% / 0.78 |
| e60 / m15 | 4.217 / 23.96% / -24.57% / 0.98 | 3.750 / 21.81% / -30.36% / 0.72 |

**结论**：默认口径下"评估越快越好"（e5 最优），严格口径下"越快越差"（e5/e15 只剩 0.75~0.76，
反而是最慢的 e30/m10 最好）。
→ 上一轮那份「A 股行业基要 5~10 分钟评估」的结论**作废**，那是同日回转制造的假象。
→ 更重要的：**严格 T+1 下，A 股行业基任何参数都跑不赢主池单跑**（最好的才 Calmar 1.00 vs 主池 1.89）。

### 5.2 主池 145：严格反而略好

| 主池（e30/m15/K3/1%） | 净值 | 年化 | 波动 | 最大回撤 | Calmar | 调仓 | 换手 |
|---|---|---|---|---|---|---|---|
| 默认口径 | 5.726 | 29.75% | 22.15% | -15.89% | 1.87 | 4381 | 1967 |
| **严格 T+1** | **6.053** | **30.83%** | 22.62% | -16.34% | **1.89** | 3290 | 1497 |

主池在更严的约束下**反而更好**（净值 +5.7%、换手 -24%）—— 说明主池本来就不需要日内回转，
而"同日买回卖回"多半是噪声交易。这一条对主池是**好消息**：不必担心口径放宽虚增收益。

### 5.3 同账户合并：结论不变，而且优势更大

| 方案（严格 T+1） | 净值 | 年化 | 波动 | 最大回撤 | Calmar |
|---|---|---|---|---|---|
| 只跑主池 145 | 6.053 | 30.83% | 22.62% | -16.34% | 1.89 |
| 只跑 A 股行业基 30（e5/m10） | 3.346 | 19.75% | 25.46% | -26.02% | 0.76 |
| 两个独立账户 80/20 | 5.512 | 29.01% | 21.54% | -16.89% | 1.72 |
| **★松耦合同账户 80/20** | **5.818** | **30.06%** | 21.51% | **-15.68%** | **1.92** |

- 严格口径下，"同账户·松耦合"**依然优于只跑主池**（1.92 vs 1.89）和两个独立账户（1.72）；
- 而且优势比之前更大（对比：默认口径 2.02 vs 1.87 vs 1.97）；
- A 侧换 e30/m15（严格下最稳的那档）：合并 80/20 → 5.595 / 29.30% / -15.90% / Calmar 1.84，**低于只跑主池的 1.89**。

  → 所以严格口径下，"A 侧用什么参数"很关键：**e5/m10 那档虽然单跑很差（0.76），但作为 20% 的分散化配置反而最优（1.92）**；
  换成 e30/m15 就只有 1.84（低于只跑主池）。这一点值得再单独研究。

**⚠️ 但要给这个优势打个折**：严格口径下"同账户 vs 两个独立账户"的 +0.20 Calmar 主要来自 **2022 年一年**：

| 年份 | 同账户 80/20 | 两个独立账户 80/20 | 差 |
|---|---|---|---|
| 2020 | 30.95% | 32.09% | -1.15% |
| 2021 | 7.84% | 7.72% | +0.12% |
| **2022** | **14.97%** | **7.71%** | **+7.27%** |
| 2023 | 9.07% | 8.44% | +0.63% |
| 2024 | 52.62% | 54.36% | -1.73% |
| 2025 | 48.78% | 47.54% | +1.24% |
| 2026 | 35.53% | 36.19% | -0.65% |

逐日差异也仍是噪声型（累计 +5.42%、绝对差合计 146.7%、单日最大 ±1%）。
A 股行业基在严格口径下的逐年收益：2020 **+70.7%**、2021 **+40.8%**、2022 **-18.5%**、2023 +17.5%、
2024 +9.2%、2025 +16.7%、2026 +14.1% —— 它的历史业绩高度集中在 2020-21 两年，之后明显钝化。

**结论**：A 侧作为分散化配置有微弱价值，但证据强度不足以支撑"必须上"。建议要么小仓位试，
要么放弃、把 20% 还给主池。

---

## 六、已实施的改动

| 文件 | 改动 |
|---|---|
| `mix_engine.py` | 新增 `--t1strict`（严格口径）；新增违规诊断计数（默认口径下统计"卖出超过真实可卖量"的笔数与量） |
| `backtest_minute.py` | 新增 `--t1-strict`（**默认关闭**，旧口径与已有结论完全不变） |
| `run_group_single.py` | 支持 `strict` 参数 |
| `t1_probe.py` | 严格 T+1 下的 A 侧参数重扫脚本 |

**交叉验证**：两个引擎在严格 T+1 下结果完全一致 ——
主池 6.053 / 30.83% / -16.3% / Calmar 1.89 / 调仓 3290；
A 侧 3.346 / 19.75% / -26.02% / Calmar 0.76 / 调仓 4011（逐项吻合）。

---

## 七、需要你决定的四件事

1. **官方口径要不要切到严格 T+1？**
   建议切。理由：① 与已部署的 QMT 版一致（见下）；② A 侧误差极大（1.66 → 0.76），不切会做出错误决策；
   ③ 主池在严格口径下**更好**，切换不会造成"收益缩水"的错觉。
2. **已部署的 QMT 版用的是严格口径**（`STATE['bought_today'][code] = ... + int(quantity)` 是**累计**写法，第 957 行），
   与真实规则一致 → **是离线回测比实盘松**，不是相反。这解释了为什么实盘/离线对不齐时，
   离线看起来更好。
3. **A 股行业基这条线要不要继续？** 严格口径下它单跑打不赢主池（1.00 vs 1.89）；
   作为 20% 的分散化配置还能带来 +0.03 Calmar（1.92 vs 1.89），但幅度很小，
   且对参数选择敏感。建议：先按 e5/m10 小仓位试，或者干脆放弃。
4. **主池 85 只 T+0 品种要不要放开日内回转？** 引擎现在一刀切 T+1 对它们偏保守，
   但 QMT 版代码里也是同样一刀切 → 回测与实盘**口径一致**，不会对不上。
   若想利用 T+0 的日内灵活性，需要按标的类型分别设规则，并重跑全部结论（离线对照显示"关掉 T+1 反而更差"，
   所以这条优先级不高）。

---

## 八、复现命令

```bash
cd 云飞复刻版
PYTHONHASHSEED=0 python mix_engine.py --w 0.0 --t1strict          # A 侧 严格 T+1
PYTHONHASHSEED=0 python mix_engine.py --w 1.0 --t1strict          # 主池 严格 T+1
PYTHONHASHSEED=0 python mix_engine.py --w 0.8 --loose --t1strict  # 松耦合同账户 严格 T+1
PYTHONHASHSEED=0 python t1_probe.py                               # 严格 T+1 下 A 侧参数重扫
PYTHONHASHSEED=0 python backtest_minute.py --eval 30 --mom 15 --ktop 3 --mintr 0.01 \
    --source zip --commission 0.00005 --t1-strict                 # 参考引擎交叉验证
```
