Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion .github/workflows/drift-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
timeout-minutes: 15
outputs:
snapshot_repository_ref: ${{ steps.snapshot-input.outputs.snapshot_repository_ref }}
lifecycle_profiles: ${{ steps.build-lifecycle.outputs.lifecycle_profiles }}
steps:
- name: Checkout
uses: actions/checkout@v6
Expand Down Expand Up @@ -139,6 +140,7 @@ jobs:
PY

- name: Build lifecycle preflight bundle
id: build-lifecycle
env:
LIFECYCLE_PREFLIGHT_BUNDLE_ROOT: ${{ runner.temp }}/lifecycle-preflight-bundle
MARKET_HISTORY_PATH: ${{ runner.temp }}/snapshot-input/downloaded_price_history.csv
Expand Down Expand Up @@ -172,6 +174,13 @@ jobs:
text=True,
)
)["profiles"]
if not profiles:
raise SystemExit("lifecycle preflight profile list is empty")
# Doctor/monitor/drift must only cover profiles that this preflight
# actually materializes. TQQQ stays on its dedicated P1/P3 lane.
output_file = Path(os.environ["GITHUB_OUTPUT"])
with output_file.open("a", encoding="utf-8") as handle:
handle.write(f"lifecycle_profiles={json.dumps(profiles, separators=(',', ':'))}\n")
bundle_root = Path(os.environ["LIFECYCLE_PREFLIGHT_BUNDLE_ROOT"])
market_history = os.environ["MARKET_HISTORY_PATH"]
store_root = bundle_root / "data" / "lifecycle_store"
Expand Down Expand Up @@ -215,6 +224,12 @@ jobs:
contents: read
issues: write
id-token: write
strategy:
fail-fast: false
matrix:
# Scope reusable doctor/monitor/drift to preflight-materialized profiles only.
# Keeps tqqq_growth_income on its dedicated research lane.
profile: ${{ fromJSON(needs.preflight_backtests.outputs.lifecycle_profiles) }}
uses: QuantStrategyLab/QuantPlatformKit/.github/workflows/reusable-drift-check.yml@5d6c6bcd970b4a179ccbd21a195e273b17fb59ef
with:
strategy_domain: us_equity
Expand All @@ -227,7 +242,7 @@ jobs:
lifecycle_performance_bucket: ${{ vars.LIFECYCLE_PERFORMANCE_BUCKET }}
quant_platform_kit_ref: 5d6c6bcd970b4a179ccbd21a195e273b17fb59ef
lifecycle_preflight_artifact: lifecycle-preflight-${{ github.run_id }}-${{ github.run_attempt }}
strategy_profile: ${{ inputs.strategy_profile || '' }}
strategy_profile: ${{ matrix.profile }}
gcp_workload_identity_provider: ${{ vars.GCP_WORKLOAD_IDENTITY_PROVIDER }}
gcp_service_account: ${{ vars.GCP_WORKLOAD_IDENTITY_SERVICE_ACCOUNT }}
gcp_project_id: ${{ vars.GCP_PROJECT_ID }}
Expand Down
25 changes: 21 additions & 4 deletions docs/research/global_etf_absolute_volatility.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,24 @@
在既有 history 回测入口 `UsEtfRotationBacktestRunner.run` 的 params 中显式设置
`research_absolute_volatility=True` 才启用;默认及 False 完全沿用原策略。
字符串、数字不能替代布尔开关。生产 snapshot、manifest、交易入口不消费此模块。
本批实现固定 126 个交易日收益、15% 年化目标,不搜索参数;这不构成 OOS 冻结、晋级或交易授权。
本批实现固定 126 个交易日收益、15% 年化目标,不搜索参数;人工候选工程实现不构成晋级或交易授权。

## 未来独立 OOS 合同(2026-09-18 冻结)

在启动日前锁定以下合同;到期后不得事后改参、换基准或放宽通过规则。

| 项 | 冻结值 |
|---|---|
| 候选 | UES `global_etf_absolute_volatility`(`research_absolute_volatility=True`) |
| 窗口 | **2026-10-01** 至 **2027-09-30**(含端点按既有 session/日历语义) |
| 费用 | 默认 **10 bps**(买卖全腿,复用既有模拟器) |
| 比较基准 | 原策略(开关关闭)、**VOO**、**BIL** |
| 记录指标 | 波动、最大回撤、收益代价;允许 `NO_IMPROVEMENT` / `INCOMPLETE` |
| 权限 | `research_only` / `no_order`;不授予 shadow、paper、live |
| `locked` | **true**(相对本未来窗口;2017–2024 learning 仍非本 OOS) |

未到窗口开始前:可只读核验输入/代码身份,不得用历史 learning 回填本窗口,不得为通过调参。
AI 真模型评审仍须另授权与额度条件;本冻结不触发 `resume_deferred`。

## 计算

Expand All @@ -26,7 +43,7 @@ BIL 仍有收益、波动和交易成本。窗口和目标记录在回测结果
[Moreira–Muir 原论文](https://amoreira2.github.io/alan-moreira.github.io/VolPortfolios_published.pdf)。
原论文采用上月方差倒数缩放,本候选不是其直接复现,也不能承接其收益结论。
[Cederburg 等的研究](https://www.lehigh.edu/~xuy219/research/COWY.pdf)提供样本外未系统胜出的反向证据。
作者主页已由 VPS 做过一次只读可用性核查,尚未接入 AAB 的固定来源契约;旧 NBER 案例仍停止。
固定作者摘要已接入 AAB;[run35136606479](https://github.com/QuantStrategyLab/AIAuditBridge/actions/runs/35136606479) 认证通过,因 quota deferred 未执行模型。本候选由人工实现;AI 评审与金融验收仍待完成;旧 NBER 案例仍停止。

## 验证与限制

Expand All @@ -35,5 +52,5 @@ BIL 仍有收益、波动和交易成本。窗口和目标记录在回测结果
- 复用既有模拟器的现金、份额漂移和买卖全腿费用;当前默认费用为 10 bps。
- runner 按月末检查,原策略在季度月末正常调仓,不宣称每日波动保护。
- close-only lag-one:以 d 收盘信号和 d 收盘价格模拟 d→d+1 持仓,属于研究近似,不证明实时可成交。
- 后续真实比较须使用已批准云端输入,比较原策略、VOO、BIL 的波动、回撤及收益代价;允许 NO_IMPROVEMENT 或 INCOMPLETE
- 本批没有真实行情回测、参数择优、独立样本外验收或模型生成代码试验。人工工程实现不等于系统 AI 联网设计已成功。
- 真实比较按上方已冻结未来 OOS 合同执行;2017–2024 learning / VOO·BIL 比较不得改称本窗口 OOS
- 本批没有真实行情回测、参数择优或模型生成代码试验。人工工程实现不等于系统 AI 联网设计已成功。
9 changes: 9 additions & 0 deletions tests/test_drift_workflow_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,14 @@ def test_drift_workflow_wires_real_snapshot_history_and_preflight_bundle() -> No
assert "REQUESTED_STRATEGY_PROFILE: ${{ inputs.strategy_profile || '' }}" in workflow
assert '"--list-profiles"' in workflow
assert "unsupported targeted lifecycle profile=" in workflow
assert "id: build-lifecycle" in workflow
assert "lifecycle_profiles: ${{ steps.build-lifecycle.outputs.lifecycle_profiles }}" in workflow
assert "lifecycle_profiles=${json.dumps(profiles, separators=(',', ':'))}" in workflow or (
'handle.write(f"lifecycle_profiles={json.dumps(profiles, separators=(\',\', \':\'))}\\n")' in workflow
)
assert "matrix:" in workflow
assert "profile: ${{ fromJSON(needs.preflight_backtests.outputs.lifecycle_profiles) }}" in workflow
assert "strategy_profile: ${{ matrix.profile }}" in workflow
assert "tqqq_growth_income" not in workflow.split("LIFECYCLE_PREFLIGHT_BUNDLE_ROOT", 1)[1].split("Upload lifecycle", 1)[0]
assert "codex_audit_service_url: ${{ secrets.CODEX_AUDIT_SERVICE_URL }}" in workflow
assert "secrets.SNAPSHOT_REPOSITORY_TOKEN || secrets.QSL_REPO_SYNC_TOKEN || github.token" in workflow