← 文档库
📄 文档learn/2026-10-12-国债专属择时策略研究/README.md
🌱 创建于2026-10-12 10:57
✏️ 修改于2026-10-15 01:37
👁️ 浏览量—
🔗 短链接/~learn/s/183038da
🔐 可见性

国债专属择时策略研究(30年 / 10年国债)

> 日期:2026-10-12 > 背景:为择时策略池新增 30年国债ETF(511090.SH) 与 10年国债ETF(511260.SH) 两个品种。 > 直接沿用「现存策略 + GA 参数空间」回测时,曲线异常、多数策略失效,故本专项调研国债该类「稳定向上、低波动」品种的专属择时策略、更长历史数据锚定方案与参数空间扩展。

---

0. 一句话结论

国债是低波动慢牛品种,普通权益类择时策略(含成交量类)在这里几乎必然失效:短回看期策略无险可避、时间加权年化在短序列上过拟合、成交量在债券上是纯噪声。有效方向是拉长回看期的趋势跟踪族 + 波动率目标化,并用中债长序列财富指数替代只有 3 年的 ETF 数据做锚定。

---

1. 品种语义确认(先对齐标的)

品种 代码 追踪指数 备注
---------------------------
30年国债ETF 511090.SH CSI009036(中债-30年国债指数) 上市仅约 3 年,数据过短
十年国债ETF 511260.SH CSI930675(中债-10年国债指数) 数据约 8 年
国债ETF 511010.SH SSE000012(上证5年期国债指数) 参考对照

> 结论:511090 只有 ~3 年数据,直接 GA 必然过拟合 → 必须寻找更长历史的锚定序列。

---

2. 「策略失效」根因分析(回答"曲线特别奇怪、很多策略没用")

---

3. 长序列锚定方案(★ 已落地)

3.1 数据源

用 akshare 中债官方总收益指数(ak.bond_treasury_index_cbond(indicator='财富', period=...))作为锚定序列:

锚定代码 中债指数 起始 行数 对应 ETF
---------------------------------------
CBOND30Y 中债-30年国债财富指数 2011-01-04 ~3939 511090.SH
CBOND10Y 中债-10年国债财富指数 2008-01-02 ~4690 511260.SH

3.2 相关性验证

CBOND30Y 与 511090 日收益:相关 0.887、β 1.03 → 锚定序列能高度代表 ETF 走势,可用于长历史回测。

3.3 落地方式

在 df_loader.py fetch_data 增加分支:

CBOND_TREASURY_PERIODS = {
    'CBOND30Y': '30Y', 'CBOND10Y': '10Y', 'CBOND7-10Y': '7-10Y',
    'CBOND5Y': '5Y', 'CBOND0-1Y': '0-1Y', 'CBOND0-3Y': '0-3Y', 'CBOND1-5Y': '1-5Y',
}
# fetch_data('CBOND30Y') → 直接走 akshare,open=high=low=close、volume=amount=0

调用方式与普通 code 完全一致:fetch_data('CBOND30Y', start_date, end_date),后处理(缺失补充、新鲜度检查)对 CBOND 友好(末日期 2026-10-10,不触发滞后告警)。

---

4. 策略族调研(含论文依据)

4.1 论文依据

4.2 五大策略族

# 策略族 核心逻辑 期次 实现状态
----------------------------------
1 长周期时序动量(TSMOM) close.pct_change(lookback) > threshold 进场,短周期转负出场 Phase1 ✅ 已实现
2 唐奇安通道突破(Donchian) 突破 N 日新高进场、跌破 M 日新低出场 Phase1 ✅ 已实现
3 波动率目标化(VolTarget) 按已实现波动率倒数缩放仓位(小数仓位) Phase1 ✅ 已实现
4 绝对动量 + 长均线过滤 TSMOM 叠加长均线方向过滤 Phase1 ✅ 已并入策略 1/2 的 ma_filter_window
5 Carry / 期限利差 用中债收益率曲线斜率/期限利差择时 Phase2 ✅ 已实现 CarryTermSpreadStrategy(30Y 入池;10Y 未过门控,见 §7.3)

---

5. Phase1 已实现策略

三个纯价格策略,均照 VolatilityChannelStrategy.py 的无未来函数范式:所有指标输入 .shift(1),末尾 position.shift(signal_offset)。

策略类 文件 参数
-------------------
LongTermMomentumStrategy LongTermMomentumStrategy.py lookback(120~750) / threshold(0~0.05) / exit_lookback(20~250) / ma_filter_window[0,120,250,500]
DonchianBreakoutStrategy DonchianBreakoutStrategy.py entry_window(60~750) / exit_window(20~400) / ma_filter_window[0,120,250,500]
VolTargetStrategy VolTargetStrategy.py vol_window(20~250) / target_vol[0.03~0.08] / trend_window[0,60,120,250,500] / max_position(0.3~1.0)

5.1 参数空间扩展(关键)

现存权益类参数空间窗口多在 20~200 日;国债需整体拉长到 60~750 日才覆盖论文建议的 >3 年回看期。已在 params_grid.py 末尾新增三个专属参数 dict。

> ⚠️ GA 构造契约:ParamsOptGeneticDevelop.evaluate() 里 strategy_class(strategy_df, params),params 的 key 直接来自参数 dict → 策略 __init__ 形参名必须与参数 dict 的 key 完全一致**,否则 TypeError。

5.2 Smoke 测试结果(CBOND30Y)

策略配置 trades 胜率 累计收益
--------------------------------
LongTermMomentum(lookback=252, ma=250) 12 0.50 +119.0%
LongTermMomentum(lookback=500, exit=120) 6 0.50 +98.4%
Donchian(252/126, ma=250) 7 0.57 +111.9%
VolTarget(60, 0.05, ma=250) 319 0.55 +117.2%
VolTarget(20, 0.04) 493 0.53 +124.6%
buy&hold 基准 — — ~+155%

> 单策略均低于 buy&hold 属正常——择时牺牲部分收益换取暴露/回撤下降。真实价值由 GA 多目标(年化/时间衰减年化/凯利/回撤/资金占用)择优。

---

6. GA 回测执行

6.1 已完成的接线改动

文件 params_deep_seek.py:

6.2 运行命令

# 沙箱内运行 venv 必须剥离 PYTHONHOME/PYTHONPATH
/usr/bin/env -u PYTHONHOME -u PYTHONPATH .venv/bin/python \
  -m unittest adata.aohan.quant.backtest.deep_learning.params_deep_seek.ParamsDeepSeek.test_opt_mult_market_params

# 按标的分进程并行
GA_CODES=CBOND30Y /usr/bin/env -u PYTHONHOME -u PYTHONPATH .venv/bin/python -m unittest ...
GA_CODES=CBOND10Y /usr/bin/env -u PYTHONHOME -u PYTHONPATH .venv/bin/python -m unittest ...

结果输出目录:adata/aohan/quant/backtest/deep_learning/genetic_opt_params/

---

7. 过程问题与决策记录(本轮完成度)

7.1 已完成 ✅

# 事项 结果
---------------
1 Carry / 期限利差策略 ✅ 已实现 CarryTermSpreadStrategy(接中债收益率曲线 ak.bond_china_yield,用长端-短端利差 generating 仓位),并入 params_grid;30Y 体入池(tenor=30, signal_mode=1, window=221, threshold=0.0551)
2 未来函数复检 ✅ 全部入池策略过 shift(1) 关卡:位置序列 pos.shift(1) 对比 baseline 保留率 ≈ 100%,无未来函数(复用 workspace/_check_future_func.py)
3 入池 ✅ 已写 timing_config.py:511090(30Y)9 条、511260(10Y)7 条,weight 口径 = 估分/100,形式对齐既有择时品种(详见 §7.3)
4 ETF 实盘映射 ✅ 映射关系确立:511090 ↔ CBOND30Y(相关 0.887 / β 1.03)、511260 ↔ CBOND10Y;实盘卡片用真实 ETF 展示,GA 用长序列优化参数
5 成交量类最终处置 ✅ 国债回测中剔除;本期保留 params_deep_seek.short_strategies 中的引用(不影响国债),归为待清理低优先项(见 §7.2-④)

7.2 仍存问题 / 未决项 ⏳

- 结论口径:单看 100 代结果与旧池互有高低(GA 随机性所致),故采用 「32 代 ∪ 100 代」合并集:按 (code, strategy, individual) 去重取 fitness 最大,逐策略在门控内重取估分最高解 → 合并集严格不劣于旧池。解析脚本 workspace/_score_treasury_ga.py(merged 模式)。 - 净收益:6 条策略估分提升并更新入池 —— 511090 的 RSIAndMA(52.7→60.6) / CarryTermSpread(49.6→54.4) / VolTarget(17.0→21.6);511260 的 DonchianBreakout(20.2→29.7) / LongTermMomentum(18.3→23.2) / VolTarget(13.9→17.8)。其余策略维持原解(不劣化)。 - ⚠️ 解析脚本 BUG(本轮已修):合并模式曾误把 passed_sorted 写成对未排序 rows 过滤,导致取到「插入顺序第一个门控通过者」而非估分最高者(曾误报 CBOND30Y/LongTermMomentum 最优 37.4,实为 49.83)。已改为 pv = [r for r in rows_sorted if gate(r)]。 - ⚠️ 入口纠错:params_deep_seek.py 没有 main() 也没有 if __name__=='__main__',唯一可执行入口是 unittest 方法 ParamsDeepSeek.test_opt_mult_market_params。之前文档里写的 -c "exec(open(...).read()); main()" 会 NameError,不可用。正确命令: ``bash # 沙箱内运行 venv 必须剥离 PYTHONHOME/PYTHONPATH;单进程按标的分拆 GA_CODES=CBOND30Y GA_GENERATIONS=100 GA_POP_SIZE=100 \ /usr/bin/env -u PYTHONHOME -u PYTHONPATH .venv/bin/python \ -m unittest adata.aohan.quant.backtest.deep_learning.params_deep_seek.ParamsDeepSeek.test_opt_mult_market_params ` (GA_CODES/GA_STRATEGIES/GA_GENERATIONS/GA_POP_SIZE 支持按标的/策略/代数/种群并行拆分;结果 CSV 位于 .../genetic_opt_params/,文件名 {code}_{Strategy}_params_opt_0_{MMDDYY}.csv,每代追加一行且多次运行会追加表头行,分析时需过滤 individual=='individual' 并去重。)

- 策略数学:夹角由原始价差 MA.diff() 经 arctan 得到(calculate_angle),是尺度相关量,不随价格归一化。 - 尺度效应:债券价格 ~100、日均线变动仅 ~0.01–0.1 → 夹角只有 1–6°;而权益指数(如 HSI 数万点)日均线变动数十点、arctan 饱和接近 ±90°,夹角差常达数十度。 - 实证(workspace/_probe_angle_trades.py,复刻 GA 评估契约): - CBOND30Y / CBOND10Y:随机 60 组 GA 参数 → 成交率 0%(近全部 No trades occurred)。 - 手工把 angle_buy=angle_sell=1 并让买/卖窗口配对、穿透方向取 more/less 时:CBOND30Y 出 91 笔、CBOND10Y 出 81 笔 → 说明不是信号算不出来,而是随机参数命中不了。 - 对照组 HSI:随机 60 组 成交率 50% → 证明该失效是低波动/低价格品种特有。 - 结论:非国债数据缺陷,而是「夹角尺度相关」+「11 维搜索空间可行域过窄」的叠加,在低波动品种上被放大到成交率≈0。 - 处置:不改动该共享策略(对 arctan 前做归一化会改变其在所有现存品种上的行为,回归风险高,且非本专项目标);维持不补入池。若日后要救活,方向是「斜率归一化(除以价格或滚动 std)后再取角度」。

- params_deep_seek.strategy_configs(GA 长侧共享清单)中,成交量类(VolumePrice* / VolumeFour* / VolumeRSI*)与 DateBasedStrategy 已在 commit 2198a82 被整批移出,换入 4 个国债策略主体 —— 即此清单现已不含成交量类策略。⚠️ 注意:该清单为全 code 共享,故该移除对权益品种 GA 也生效;如后续要重跑权益 GA 需按需补回。 - short_strategies(仅 enable_short=True 时生效)仍引用 2 个成交量策略,国债回测 enable_short=False 不受影响。 - 结论:国债侧「剔除成交量类」目标已达成;是否对 short_strategies 做全局物理删除维持待定(会波及短侧其它品种,非本专项必需,风险>收益)。

7.3 入池明细(估分口径)

- ⚠️ 口径纠偏(2026-10-13 校准):第 4 项权重乘的是 avg_average_holding_drawdown 的绝对值,不是 avg_max_drawdown(见 ParamsOptGeneticDevelop.py L366-381 的 WEIGHT_NAMES 顺序:annual → exp_decay → kelly → average_holding_drawdown → funds)。用旧池 261012 CSV 反推已逐条复现注释估分(如 FourMovingAverages 81.6 / Donchian 80.5 / DualGroup 78.1 …),校准脚本 workspace/_score_treasury_ga.py。

  • 择优规则:门控通过的解里取估分最高一条入池("估分最高"常为小样本僵尸解,须先过门控再排序)。
  • 门控:资金占用 ≥20% / 年交易 ≥1 / 年化 ≥4(国债绝对收益低,故年化门槛从通用的 8 降至 4);剔除 n_pass=0 僵尸解。
  • 511090(30年国债ETF)9 条(括号为 weight,加粗为本轮 100 代加深后提升):FourMovingAverages(0.82) / DonchianBreakout(0.80) / DualGroupFourMA(0.78) / RSIAndMA(0.61,↑52.7→60.6) / CarryTermSpread(0.54,↑49.6→54.4) / LongTermMomentum(0.50) / MVAndRSI(0.45) / MomentumBounce(0.43) / VolTarget(0.22,↑17.0→21.6)。
  • 511260(10年国债ETF)7 条:RSIAndMA(0.60) / FourMovingAverages(0.54) / DualGroupFourMA(0.31) / DonchianBreakout(0.30,↑20.2→29.7) / LongTermMomentum(0.23,↑18.3→23.2) / MVAndRSI(0.16) / VolTarget(0.18,↑13.9→17.8)。

- 未标注的策略沿用 32 代解(合并集下未找到更优解,估分与旧池持平),确保「严格不劣于旧池」。

7.4 前端接入(与既有择时品种同构)

7.5 完整回测效果(策略池 vs 买入持有,2026-10-13)

用生产回测引擎 run_baseline 对现状策略池跑完整回测(脚本 workspace/_run_treasury_backtest.py),四个口径全部跑通:

标的 区间 策略池 年化 / 最大回撤 / Calmar / 夏普 买入持有 年化 / 最大回撤 / Calmar / 夏普
------------------------
511090 真实ETF 2023-06 ~ 2026-10(3.3y) 5.99% / -4.29% / 1.40 / 1.03 7.92% / -9.30% / 0.85 / 0.99
511090 代理CBOND30Y 2011-01 ~ 2026-10(16.2y) 5.56% / -5.09% / 1.09 / 1.11 5.94% / -16.37% / 0.36 / 0.85
511260 真实ETF 2017-08 ~ 2026-10(9.1y) 2.54% / -2.51% / 1.02 / 0.59 3.37% / -4.62% / 0.73 / 0.70
511260 代理CBOND10Y 2008-01 ~ 2026-10(19.3y) 4.05% / -3.76% / 1.08 / 1.43 4.32% / -6.82% / 0.63 / 1.12

结论(诚实口径):

---

8. 窗口口径统一:1 年窗口暖机不足陷阱(2026-10-15 复盘)

> 背景:用户发现界面「策略的昨/今/明仓位」在不同数据路径下用了不同窗口(部分仍是最新 1 年),长窗口策略(尤其国债)在 1 年窗口下暖机不足会算错。

8.1 三条独立数据路径必须同口径(= DEFAULT_STRATEGY_WINDOW_YEARS,默认 10 年)

路径 位置 修复前 修复后
----------------------------
A. DB 昨/今/明仓位(界面展示数据源) show_marker.py L2384-2399 bt_data 预取 非国债已用 self.strat_date 保持(国债走 TREASURY_TIMING_ANCHOR 长历史锚定)
B. timing 图表 HTML(charts/timing/*.html) show_marker.py L3309 full_start datetime.now() - 365天 self.strat_date(默认 10 年)
C. 截面选基引擎 show_marker.py L2296 years_back=1 years_back=DEFAULT_STRATEGY_WINDOW_YEARS

8.2 数据源合并:长历史源被短源压过(已修)

8.3 国债除息陷阱(回顾 §3)

ETF 场内价会因分红除息产生向下缺口,直接拿 ETF 价做长窗口趋势信号会误判为「下跌」。故国债择时锚定中债官方总收益财富指数(含息、无除权缺口,见 §3.1),与 GA 优化同口径。

---

9. 相关文档

▶ 📜 变更历史(7 次提交)

2bf8fcc2026-10-15 01:37refactor: rename DEFAULT_BACKTEST_YEARS -> DEFAULT_STRATEGY_WINDOW_YEARS
38749ce2026-10-15 01:26fix(timing): 统一昨今明仓位计算窗口为长窗口 + 修复长历史源被短源压过
798ad4d2026-10-13 14:59feat(择时): 国债策略池GA加深至100代并合并择优 + 完整回测效果记录
f76c21c2026-10-12 11:58docs(择时): 修正国债专项 §4.2 Carry 策略状态(已实现,非待实现)
bbc81642026-10-12 11:57docs(择时): 国债专项 §7.2 未决项逐条闭环
e82abd72026-10-12 11:49feat(择时): 国债ETF(511090/511260)择时策略池入池 + 前端卡片接入
2198a822026-10-12 10:57feat(择时): 新增国债专属择时策略族 + 中债长序列数据源
🍃quant 知识库
by 伊甸园和枫

🔊 播放设置

勾选音源后调节配比,总和自动归一化
已选 0 个 · 合计:0%
合成中...
00:00/00:00
1.0x

分享图片

生成中...