From 62ec00df9d6c0f3b2004546b94faa67ca38d08b8 Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Mon, 6 Apr 2026 07:11:13 +0800 Subject: [PATCH 1/3] rename tech pullback cash buffer profile --- .github/workflows/sync-cloud-run-env.yml | 4 +-- README.md | 22 +++++++-------- ...aper_cash_buffer_branch.dryrun.env.example | 11 -------- configs/paper_cash_buffer_branch.env.example | 11 -------- ...paper_cash_buffer_branch.paper.env.example | 11 -------- ...r_global_etf_rotation.rollback.env.example | 2 +- ...ch_pullback_cash_buffer.dryrun.env.example | 11 ++++++++ ...aper_tech_pullback_cash_buffer.env.example | 11 ++++++++ ...ech_pullback_cash_buffer.paper.env.example | 11 ++++++++ ...th_pullback_v1_4_cash_buffer_refinement.py | 4 +-- ...test_growth_pullback_v1_5_freeze_review.py | 20 +++++++------- ...ech_pullback_cash_buffer_daily_overlay.py} | 8 +++--- ...h_pullback_tech_pullback_cash_buffer.json} | 4 +-- .../growth_pullback_branch_manifest.json | 4 +-- ...th_pullback_v1_4_cash_buffer_refinement.md | 2 +- .../growth_pullback_v1_4_recommendation.json | 2 +- ...rowth_pullback_v1_5_consistency_checks.csv | 4 +-- .../growth_pullback_v1_5_freeze_review.csv | 2 +- .../growth_pullback_v1_5_freeze_review.md | 14 +++++----- .../growth_pullback_v1_5_recommendation.json | 4 +-- ..._cash_buffer_daily_overlay_comparison.csv} | 24 ++++++++--------- ..._buffer_daily_overlay_recommendation.json} | 2 +- ...back_cash_buffer_daily_overlay_summary.md} | 8 +++--- runtime_config_support.py | 4 +-- strategy_registry.py | 2 +- tests/test_execution_service.py | 12 ++++----- tests/test_feature_snapshot_service.py | 24 ++++++++--------- ...th_pullback_v1_4_cash_buffer_refinement.py | 4 +-- ...test_growth_pullback_v1_5_freeze_review.py | 22 +++++++-------- tests/test_rebalance_service.py | 4 +-- tests/test_runtime_config_support.py | 27 ++++++++++++------- tests/test_snapshot_strategy_runtime.py | 26 +++++++++--------- tests/test_strategy_loader.py | 20 +++++++------- ...ech_pullback_cash_buffer_daily_overlay.py} | 6 ++--- 34 files changed, 176 insertions(+), 171 deletions(-) delete mode 100644 configs/paper_cash_buffer_branch.dryrun.env.example delete mode 100644 configs/paper_cash_buffer_branch.env.example delete mode 100644 configs/paper_cash_buffer_branch.paper.env.example create mode 100644 configs/paper_tech_pullback_cash_buffer.dryrun.env.example create mode 100644 configs/paper_tech_pullback_cash_buffer.env.example create mode 100644 configs/paper_tech_pullback_cash_buffer.paper.env.example rename research/{backtest_cash_buffer_branch_daily_overlay.py => backtest_tech_pullback_cash_buffer_daily_overlay.py} (99%) rename research/configs/{growth_pullback_cash_buffer_branch_default.json => growth_pullback_tech_pullback_cash_buffer.json} (93%) rename research/results/{cash_buffer_branch_daily_overlay_comparison.csv => tech_pullback_cash_buffer_daily_overlay_comparison.csv} (88%) rename research/results/{cash_buffer_branch_daily_overlay_recommendation.json => tech_pullback_cash_buffer_daily_overlay_recommendation.json} (94%) rename research/results/{cash_buffer_branch_daily_overlay_summary.md => tech_pullback_cash_buffer_daily_overlay_summary.md} (93%) rename tests/{test_cash_buffer_branch_daily_overlay.py => test_tech_pullback_cash_buffer_daily_overlay.py} (96%) diff --git a/.github/workflows/sync-cloud-run-env.yml b/.github/workflows/sync-cloud-run-env.yml index 2db75f7..d12e683 100644 --- a/.github/workflows/sync-cloud-run-env.yml +++ b/.github/workflows/sync-cloud-run-env.yml @@ -56,11 +56,11 @@ jobs: NOTIFY_LANG ) - if [ "${STRATEGY_PROFILE:-}" = "russell_1000_multi_factor_defensive" ] || [ "${STRATEGY_PROFILE:-}" = "cash_buffer_branch_default" ]; then + if [ "${STRATEGY_PROFILE:-}" = "russell_1000_multi_factor_defensive" ] || [ "${STRATEGY_PROFILE:-}" = "tech_pullback_cash_buffer" ]; then required_vars+=(IBKR_FEATURE_SNAPSHOT_PATH) fi - if [ "${STRATEGY_PROFILE:-}" = "cash_buffer_branch_default" ]; then + if [ "${STRATEGY_PROFILE:-}" = "tech_pullback_cash_buffer" ]; then required_vars+=(IBKR_FEATURE_SNAPSHOT_MANIFEST_PATH IBKR_STRATEGY_CONFIG_PATH IBKR_RECONCILIATION_OUTPUT_PATH) fi diff --git a/README.md b/README.md index c20aa5d..82ef9da 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ IBKR runtime for shared `us_equity` strategy profiles from `UsEquityStrategies`. - `global_etf_rotation` (`Global ETF Rotation Defense`): quarterly ETF momentum rotation with daily canary defense - `russell_1000_multi_factor_defensive` (`Russell 1000 Multi-Factor Defensive`): monthly stock-selection strategy that consumes a precomputed feature snapshot -- `cash_buffer_branch_default` (`Tech Pullback Cash Buffer`, alias `tech_pullback_cash_buffer`): monthly tech-heavy stock-selection branch with explicit BOXX cash buffer +- `tech_pullback_cash_buffer` (`Tech Pullback Cash Buffer`): monthly tech-heavy stock-selection branch with explicit BOXX cash buffer Current strategy implementations are sourced from `UsEquityStrategies`. @@ -29,11 +29,7 @@ This runtime matrix is the authoritative enablement source for IBKR. `UsEquitySt - `global_etf_rotation` - `russell_1000_multi_factor_defensive` -- `cash_buffer_branch_default` - -Human-readable alias is available for config review and future migration: - -- `tech_pullback_cash_buffer` -> `cash_buffer_branch_default` +- `tech_pullback_cash_buffer` **IBKR profile matrix** @@ -42,7 +38,7 @@ Human-readable alias is available for config review and future migration: | --- | --- | --- | --- | --- | --- | --- | --- | | `global_etf_rotation` | Global ETF Rotation Defense | `global_macro_etf_rotation` | Yes | Yes | Yes | `us_equity` | current rollback line | | `russell_1000_multi_factor_defensive` | Russell 1000 Multi-Factor Defensive | `r1000_multifactor_defensive` | Yes | No | No | `us_equity` | defensive stock baseline | -| `cash_buffer_branch_default` | Tech Pullback Cash Buffer | `tech_pullback_cash_buffer` | Yes | No | No | `us_equity` | current IBKR paper dry-run candidate | +| `tech_pullback_cash_buffer` | Tech Pullback Cash Buffer | `—` | Yes | No | No | `us_equity` | current IBKR paper dry-run candidate | **Pool (22 ETFs + 1 safe haven):** @@ -158,7 +154,7 @@ The selected `ACCOUNT_GROUP` is now the runtime identity. Keep broker-specific i |----------|----------|-------------| | `IB_GATEWAY_ZONE` | Optional fallback | GCE zone (for example `us-central1-a`). Recommended to keep in the selected account-group entry; this env var is only a transition fallback. | | `IB_GATEWAY_IP_MODE` | Optional fallback | `internal` (default) or `external`. Recommended to keep in the selected account-group entry; this env var is only a transition fallback. | -| `STRATEGY_PROFILE` | Yes | Strategy profile selector. Supported `us_equity` values: `global_etf_rotation`, `russell_1000_multi_factor_defensive`, `cash_buffer_branch_default` (alias: `tech_pullback_cash_buffer`) | +| `STRATEGY_PROFILE` | Yes | Strategy profile selector. Supported `us_equity` values: `global_etf_rotation`, `russell_1000_multi_factor_defensive`, `tech_pullback_cash_buffer` | | `ACCOUNT_GROUP` | Yes | Account-group selector. No default fallback. | | `IBKR_FEATURE_SNAPSHOT_PATH` | Conditionally required | Required when `STRATEGY_PROFILE=russell_1000_multi_factor_defensive`. Path to the latest feature snapshot file (`.csv`, `.json`, `.jsonl`, `.parquet`). | | `IB_ACCOUNT_GROUP_CONFIG_SECRET_NAME` | Yes for Cloud Run | Secret Manager secret name for account-group config JSON. Recommended production source. | @@ -208,11 +204,11 @@ NOTIFY_LANG=zh ``` ```bash -STRATEGY_PROFILE=cash_buffer_branch_default +STRATEGY_PROFILE=tech_pullback_cash_buffer ACCOUNT_GROUP=default IB_ACCOUNT_GROUP_CONFIG_SECRET_NAME=ibkr-account-groups -IBKR_FEATURE_SNAPSHOT_PATH=/var/data/cash_buffer_branch_feature_snapshot_latest.csv -IBKR_FEATURE_SNAPSHOT_MANIFEST_PATH=/var/data/cash_buffer_branch_feature_snapshot_latest.csv.manifest.json +IBKR_FEATURE_SNAPSHOT_PATH=/var/data/tech_pullback_cash_buffer_feature_snapshot_latest.csv +IBKR_FEATURE_SNAPSHOT_MANIFEST_PATH=/var/manifests/tech_pullback_cash_buffer_feature_snapshot_latest.csv.manifest.json IBKR_DRY_RUN_ONLY=true GLOBAL_TELEGRAM_CHAT_ID= NOTIFY_LANG=zh @@ -337,7 +333,7 @@ gcloud run services update ibkr-quant \ 基于 IBKR 的全球 ETF 季度轮动策略(国际市场、商品、美股行业、美股宽基、科技和半导体),含每日金丝雀应急机制。定位上比 `TQQQ`、`SOXL` 这类高弹性科技策略更稳健,但不再把科技完全排除在外。部署在 GCP Cloud Run,连接 GCE 上的 IB Gateway。 -当前 `global_etf_rotation`、`russell_1000_multi_factor_defensive` 和 `cash_buffer_branch_default` 的策略实现都来自 `UsEquityStrategies`。 +当前 `global_etf_rotation`、`russell_1000_multi_factor_defensive` 和 `tech_pullback_cash_buffer` 的策略实现都来自 `UsEquityStrategies`。 完整策略说明现在放在 [`UsEquityStrategies`](https://github.com/QuantStrategyLab/UsEquityStrategies#global_etf_rotation)。下面的策略章节主要保留执行侧摘要。 @@ -396,7 +392,7 @@ IBKR 账户 |------|------|------| | `IB_GATEWAY_ZONE` | 可选过渡项 | GCE zone(如 `us-central1-a`)。推荐直接放进选中的账号组配置里;这里只保留过渡 fallback。 | | `IB_GATEWAY_IP_MODE` | 可选过渡项 | `internal`(默认)或 `external`。推荐直接放进选中的账号组配置里;这里只保留过渡 fallback。 | -| `STRATEGY_PROFILE` | 是 | 策略档位选择。当前可用的 `us_equity` 值:`global_etf_rotation`、`russell_1000_multi_factor_defensive`、`cash_buffer_branch_default`(alias:`tech_pullback_cash_buffer`) | +| `STRATEGY_PROFILE` | 是 | 策略档位选择。当前可用的 `us_equity` 值:`global_etf_rotation`、`russell_1000_multi_factor_defensive`、`tech_pullback_cash_buffer` | | `ACCOUNT_GROUP` | 是 | 账号组选择器,不再提供默认回退。 | | `IB_ACCOUNT_GROUP_CONFIG_SECRET_NAME` | Cloud Run 建议必填 | 账号组配置 JSON 在 Secret Manager 里的密钥名。生产环境推荐使用。 | | `IB_ACCOUNT_GROUP_CONFIG_JSON` | 否 | 本地开发用的账号组配置 JSON fallback。不建议在生产 Cloud Run 直接使用。 | diff --git a/configs/paper_cash_buffer_branch.dryrun.env.example b/configs/paper_cash_buffer_branch.dryrun.env.example deleted file mode 100644 index 7a3f15e..0000000 --- a/configs/paper_cash_buffer_branch.dryrun.env.example +++ /dev/null @@ -1,11 +0,0 @@ -# IBKR paper runtime switch: cash_buffer_branch_default (dry-run) -STRATEGY_PROFILE=cash_buffer_branch_default -ACCOUNT_GROUP=default -IB_ACCOUNT_GROUP_CONFIG_SECRET_NAME=ibkr-account-groups -IBKR_FEATURE_SNAPSHOT_PATH=/var/data/cash_buffer_branch_feature_snapshot_latest.csv -IBKR_FEATURE_SNAPSHOT_MANIFEST_PATH=/var/data/cash_buffer_branch_feature_snapshot_latest.csv.manifest.json -IBKR_STRATEGY_CONFIG_PATH=/app/research/configs/growth_pullback_cash_buffer_branch_default.json -IBKR_RECONCILIATION_OUTPUT_PATH=/var/log/ibkr_cash_buffer_branch_reconciliation.json -IBKR_DRY_RUN_ONLY=true -GLOBAL_TELEGRAM_CHAT_ID= -NOTIFY_LANG=zh diff --git a/configs/paper_cash_buffer_branch.env.example b/configs/paper_cash_buffer_branch.env.example deleted file mode 100644 index f66cfe5..0000000 --- a/configs/paper_cash_buffer_branch.env.example +++ /dev/null @@ -1,11 +0,0 @@ -# IBKR paper runtime switch: cash_buffer_branch_default -STRATEGY_PROFILE=cash_buffer_branch_default -ACCOUNT_GROUP=default -IB_ACCOUNT_GROUP_CONFIG_SECRET_NAME=ibkr-account-groups -IBKR_FEATURE_SNAPSHOT_PATH=/var/data/cash_buffer_branch_feature_snapshot_latest.csv -IBKR_FEATURE_SNAPSHOT_MANIFEST_PATH=/var/data/cash_buffer_branch_feature_snapshot_latest.csv.manifest.json -IBKR_STRATEGY_CONFIG_PATH=/app/research/configs/growth_pullback_cash_buffer_branch_default.json -IBKR_RECONCILIATION_OUTPUT_PATH=/var/log/ibkr_cash_buffer_branch_reconciliation.json -IBKR_DRY_RUN_ONLY=true -GLOBAL_TELEGRAM_CHAT_ID= -NOTIFY_LANG=zh diff --git a/configs/paper_cash_buffer_branch.paper.env.example b/configs/paper_cash_buffer_branch.paper.env.example deleted file mode 100644 index 3cf14a0..0000000 --- a/configs/paper_cash_buffer_branch.paper.env.example +++ /dev/null @@ -1,11 +0,0 @@ -# IBKR paper runtime switch: cash_buffer_branch_default (paper ordering) -STRATEGY_PROFILE=cash_buffer_branch_default -ACCOUNT_GROUP=default -IB_ACCOUNT_GROUP_CONFIG_SECRET_NAME=ibkr-account-groups -IBKR_FEATURE_SNAPSHOT_PATH=/var/data/cash_buffer_branch_feature_snapshot_latest.csv -IBKR_FEATURE_SNAPSHOT_MANIFEST_PATH=/var/data/cash_buffer_branch_feature_snapshot_latest.csv.manifest.json -IBKR_STRATEGY_CONFIG_PATH=/app/research/configs/growth_pullback_cash_buffer_branch_default.json -IBKR_RECONCILIATION_OUTPUT_PATH=/var/log/ibkr_cash_buffer_branch_reconciliation.json -IBKR_DRY_RUN_ONLY=false -GLOBAL_TELEGRAM_CHAT_ID= -NOTIFY_LANG=zh diff --git a/configs/paper_global_etf_rotation.rollback.env.example b/configs/paper_global_etf_rotation.rollback.env.example index 7daa578..b043561 100644 --- a/configs/paper_global_etf_rotation.rollback.env.example +++ b/configs/paper_global_etf_rotation.rollback.env.example @@ -4,7 +4,7 @@ ACCOUNT_GROUP=default IB_ACCOUNT_GROUP_CONFIG_SECRET_NAME=ibkr-account-groups GLOBAL_TELEGRAM_CHAT_ID= NOTIFY_LANG=zh -# remove if previously set for cash_buffer_branch_default: +# remove if previously set for tech_pullback_cash_buffer: # IBKR_FEATURE_SNAPSHOT_PATH # IBKR_FEATURE_SNAPSHOT_MANIFEST_PATH # IBKR_STRATEGY_CONFIG_PATH diff --git a/configs/paper_tech_pullback_cash_buffer.dryrun.env.example b/configs/paper_tech_pullback_cash_buffer.dryrun.env.example new file mode 100644 index 0000000..f956856 --- /dev/null +++ b/configs/paper_tech_pullback_cash_buffer.dryrun.env.example @@ -0,0 +1,11 @@ +# IBKR paper runtime switch: tech_pullback_cash_buffer (dry-run) +STRATEGY_PROFILE=tech_pullback_cash_buffer +ACCOUNT_GROUP=default +IB_ACCOUNT_GROUP_CONFIG_SECRET_NAME=ibkr-account-groups +IBKR_FEATURE_SNAPSHOT_PATH=/var/data/tech_pullback_cash_buffer_feature_snapshot_latest.csv +IBKR_FEATURE_SNAPSHOT_MANIFEST_PATH=/var/manifests/tech_pullback_cash_buffer_feature_snapshot_latest.csv.manifest.json +IBKR_STRATEGY_CONFIG_PATH=/workspace/research/configs/growth_pullback_tech_pullback_cash_buffer.json +IBKR_RECONCILIATION_OUTPUT_PATH=/var/log/ibkr_tech_pullback_cash_buffer_reconciliation.json +IBKR_DRY_RUN_ONLY=true +GLOBAL_TELEGRAM_CHAT_ID= +NOTIFY_LANG=zh diff --git a/configs/paper_tech_pullback_cash_buffer.env.example b/configs/paper_tech_pullback_cash_buffer.env.example new file mode 100644 index 0000000..1f99467 --- /dev/null +++ b/configs/paper_tech_pullback_cash_buffer.env.example @@ -0,0 +1,11 @@ +# IBKR paper runtime switch: tech_pullback_cash_buffer +STRATEGY_PROFILE=tech_pullback_cash_buffer +ACCOUNT_GROUP=default +IB_ACCOUNT_GROUP_CONFIG_SECRET_NAME=ibkr-account-groups +IBKR_FEATURE_SNAPSHOT_PATH=/var/data/tech_pullback_cash_buffer_feature_snapshot_latest.csv +IBKR_FEATURE_SNAPSHOT_MANIFEST_PATH=/var/manifests/tech_pullback_cash_buffer_feature_snapshot_latest.csv.manifest.json +IBKR_STRATEGY_CONFIG_PATH=/workspace/research/configs/growth_pullback_tech_pullback_cash_buffer.json +IBKR_RECONCILIATION_OUTPUT_PATH=/var/log/ibkr_tech_pullback_cash_buffer_reconciliation.json +IBKR_DRY_RUN_ONLY=true +GLOBAL_TELEGRAM_CHAT_ID= +NOTIFY_LANG=zh diff --git a/configs/paper_tech_pullback_cash_buffer.paper.env.example b/configs/paper_tech_pullback_cash_buffer.paper.env.example new file mode 100644 index 0000000..32aa895 --- /dev/null +++ b/configs/paper_tech_pullback_cash_buffer.paper.env.example @@ -0,0 +1,11 @@ +# IBKR paper runtime switch: tech_pullback_cash_buffer (paper ordering) +STRATEGY_PROFILE=tech_pullback_cash_buffer +ACCOUNT_GROUP=default +IB_ACCOUNT_GROUP_CONFIG_SECRET_NAME=ibkr-account-groups +IBKR_FEATURE_SNAPSHOT_PATH=/var/data/tech_pullback_cash_buffer_feature_snapshot_latest.csv +IBKR_FEATURE_SNAPSHOT_MANIFEST_PATH=/var/manifests/tech_pullback_cash_buffer_feature_snapshot_latest.csv.manifest.json +IBKR_STRATEGY_CONFIG_PATH=/workspace/research/configs/growth_pullback_tech_pullback_cash_buffer.json +IBKR_RECONCILIATION_OUTPUT_PATH=/var/log/ibkr_tech_pullback_cash_buffer_reconciliation.json +IBKR_DRY_RUN_ONLY=false +GLOBAL_TELEGRAM_CHAT_ID= +NOTIFY_LANG=zh diff --git a/research/backtest_growth_pullback_v1_4_cash_buffer_refinement.py b/research/backtest_growth_pullback_v1_4_cash_buffer_refinement.py index 20e76a8..f978669 100644 --- a/research/backtest_growth_pullback_v1_4_cash_buffer_refinement.py +++ b/research/backtest_growth_pullback_v1_4_cash_buffer_refinement.py @@ -394,10 +394,10 @@ def build_recommendation( level = "cash_buffer_branch_candidate" reason = "cash-buffer branch direction still exists, but the refined candidates do not keep enough relative-to-QQQ edge" elif honest_ok and occupancy_ok and concentration_ok and best_is_current_default and (stable_margin or no_material_gain_over_current): - level = "cash_buffer_branch_default_frozen" + level = "tech_pullback_cash_buffer_frozen" reason = "current honest cash-buffer branch is already good enough; small refinements do not justify further spec churn" elif honest_ok and performance_ok: - level = "cash_buffer_branch_default" + level = "tech_pullback_cash_buffer" reason = "a refined honest cash-buffer candidate is strong enough to act as the branch default, but the spec is not fully frozen yet" else: level = "cash_buffer_branch_candidate" diff --git a/research/backtest_growth_pullback_v1_5_freeze_review.py b/research/backtest_growth_pullback_v1_5_freeze_review.py index f41fff7..d12847b 100644 --- a/research/backtest_growth_pullback_v1_5_freeze_review.py +++ b/research/backtest_growth_pullback_v1_5_freeze_review.py @@ -26,7 +26,7 @@ DEFAULT_RESULTS_DIR = SCRIPT_DIR / "results" DEFAULT_CONFIGS_DIR = SCRIPT_DIR / "configs" MAIN_COST_BPS = 5.0 -CANONICAL_CONFIG_FILENAME = "growth_pullback_cash_buffer_branch_default.json" +CANONICAL_CONFIG_FILENAME = "growth_pullback_tech_pullback_cash_buffer.json" V14_SUMMARY_FILENAME = "growth_pullback_v1_4_cash_buffer_refinement.csv" V14_RECOMMENDATION_FILENAME = "growth_pullback_v1_4_recommendation.json" @@ -72,7 +72,7 @@ def resolve_paths(configs_dir: Path, results_dir: Path) -> dict[str, Path]: def build_canonical_spec(center_cfg: gp.GrowthPullbackConfig, previous_candidate_name: str) -> CanonicalBranchSpec: config = replace( center_cfg, - name="cash_buffer_branch_default", + name="tech_pullback_cash_buffer", holdings_count=8, single_name_cap=0.10, sector_cap=0.40, @@ -80,7 +80,7 @@ def build_canonical_spec(center_cfg: gp.GrowthPullbackConfig, previous_candidate exposures=gp.suite.ExposureConfig("80_60_0", 0.60, 0.00), ) return CanonicalBranchSpec( - name="cash_buffer_branch_default", + name="tech_pullback_cash_buffer", previous_candidate_name=previous_candidate_name, config=config, risk_on_exposure=0.80, @@ -95,7 +95,7 @@ def build_canonical_spec(center_cfg: gp.GrowthPullbackConfig, previous_candidate def canonical_spec_to_dict(spec: CanonicalBranchSpec) -> dict[str, object]: return { - "role": "cash_buffer_branch_default", + "role": "tech_pullback_cash_buffer", "status": "research_only", "strategy": "growth_pullback_systematic_v1", "branch_name": "cash_buffer_branch", @@ -295,7 +295,7 @@ def build_consistency_checks( }, { "check": "recommendation_matches_branch_default_role", - "passed": recommendation_level == "cash_buffer_branch_default", + "passed": recommendation_level == "tech_pullback_cash_buffer", "detail": f"recommendation={recommendation_level}", }, { @@ -390,7 +390,7 @@ def build_role_table( qqq_full = v12.extract_period_row(qqq_reference_rows, "QQQ", MAIN_COST_BPS, "Full Sample") rows.extend([ { - "strategy": "cash_buffer_branch_default", + "strategy": "tech_pullback_cash_buffer", "role": "cash-buffered parallel branch", "full_cagr": canonical_summary["full_cagr"], "oos_cagr": canonical_summary["oos_cagr"], @@ -486,15 +486,15 @@ def build_freeze_recommendation( level = "candidate" reason = "branch role is valid, but consistency or performance checks are not strong enough for default status" elif frozen_blockers: - level = "cash_buffer_branch_default" + level = "tech_pullback_cash_buffer" reason = "canonical naming and branch role are now clear, but avg names / 2022 profile / recent micro-adjustment mean it should stop at default, not frozen" else: - level = "cash_buffer_branch_default_frozen" + level = "tech_pullback_cash_buffer_frozen" reason = "branch semantics, deployment consistency, and behavior are all stable enough to freeze" return { "research_recommendation": level, - "branch_name": "cash_buffer_branch_default", + "branch_name": "tech_pullback_cash_buffer", "previous_candidate_name": "cash_buffer_a__hb10__base__adv50", "reason": reason, "frozen_blockers": frozen_blockers, @@ -638,7 +638,7 @@ def main() -> None: "information_ratio_vs_qqq_oos": float(v12.extract_period_row(full_rows_df, "coherent_full_deployment_branch", MAIN_COST_BPS, "OOS Sample")["Information Ratio vs QQQ"]), }) - preliminary_recommendation_level = "cash_buffer_branch_default" + preliminary_recommendation_level = "tech_pullback_cash_buffer" manifest = build_manifest( spec=spec, canonical_summary=canonical_summary, diff --git a/research/backtest_cash_buffer_branch_daily_overlay.py b/research/backtest_tech_pullback_cash_buffer_daily_overlay.py similarity index 99% rename from research/backtest_cash_buffer_branch_daily_overlay.py rename to research/backtest_tech_pullback_cash_buffer_daily_overlay.py index f3580a7..cee7136 100644 --- a/research/backtest_cash_buffer_branch_daily_overlay.py +++ b/research/backtest_tech_pullback_cash_buffer_daily_overlay.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Research-only daily overlay backtest for cash_buffer_branch_default.""" +"""Research-only daily overlay backtest for tech_pullback_cash_buffer.""" from __future__ import annotations @@ -25,7 +25,7 @@ DEFAULT_RESULTS_DIR = SCRIPT_DIR / "results" DEFAULT_CONFIGS_DIR = SCRIPT_DIR / "configs" -DEFAULT_BASELINE_CONFIG_PATH = DEFAULT_CONFIGS_DIR / "growth_pullback_cash_buffer_branch_default.json" +DEFAULT_BASELINE_CONFIG_PATH = DEFAULT_CONFIGS_DIR / "growth_pullback_tech_pullback_cash_buffer.json" DEFAULT_QQQ_PLUS_RESULTS_PATH = DEFAULT_RESULTS_DIR / "stock_alpha_v1_1_spec_lock.csv" QQQ_PLUS_CURRENT_DEFAULT_SCENARIO = "v11_liquid_50m_norm_universe_cross_sectional_h12_cap8_sector30_hold10" MAIN_COST_BPS = 5.0 @@ -107,7 +107,7 @@ def load_baseline_spec(path: Path) -> BaselineSpec: return BaselineSpec( config=config, risk_on_exposure=float(payload["exposures"]["risk_on"]), - name="cash_buffer_branch_default_monthly_baseline", + name="tech_pullback_cash_buffer_monthly_baseline", config_payload=payload, ) @@ -117,7 +117,7 @@ def build_baseline_candidate(spec: BaselineSpec) -> v12.GeometryCandidate: spec.name, spec.config, risk_on_exposure=spec.risk_on_exposure, - note="Monthly baseline from canonical cash_buffer_branch_default; month-start chooses names, non-rebalance days are no-op.", + note="Monthly baseline from canonical tech_pullback_cash_buffer; month-start chooses names, non-rebalance days are no-op.", ) diff --git a/research/configs/growth_pullback_cash_buffer_branch_default.json b/research/configs/growth_pullback_tech_pullback_cash_buffer.json similarity index 93% rename from research/configs/growth_pullback_cash_buffer_branch_default.json rename to research/configs/growth_pullback_tech_pullback_cash_buffer.json index 324cb22..f287b72 100644 --- a/research/configs/growth_pullback_cash_buffer_branch_default.json +++ b/research/configs/growth_pullback_tech_pullback_cash_buffer.json @@ -1,9 +1,9 @@ { - "role": "cash_buffer_branch_default", + "role": "tech_pullback_cash_buffer", "status": "research_only", "strategy": "growth_pullback_systematic_v1", "branch_name": "cash_buffer_branch", - "name": "cash_buffer_branch_default", + "name": "tech_pullback_cash_buffer", "previous_candidate_name": "cash_buffer_a__hb10__base__adv50", "family": "tech_heavy_pullback", "universe": "tech_heavy", diff --git a/research/results/growth_pullback_branch_manifest.json b/research/results/growth_pullback_branch_manifest.json index 7caa248..c10981d 100644 --- a/research/results/growth_pullback_branch_manifest.json +++ b/research/results/growth_pullback_branch_manifest.json @@ -1,5 +1,5 @@ { - "branch_name": "cash_buffer_branch_default", + "branch_name": "tech_pullback_cash_buffer", "previous_candidate_name": "cash_buffer_a__hb10__base__adv50", "role": "cash-buffered parallel branch", "strategy_family": "tech_heavy_pullback", @@ -50,7 +50,7 @@ "OOS CAGR and IR improved with a simple hold_bonus increase from 0.05 to 0.10.", "Deployment semantics remain perfectly aligned with the written spec." ], - "recommendation": "cash_buffer_branch_default", + "recommendation": "tech_pullback_cash_buffer", "oos_cagr": 0.34068001857447316, "oos_cagr_minus_qqq": 0.23873167970694698, "oos_max_drawdown": -0.19176289938838953, diff --git a/research/results/growth_pullback_v1_4_cash_buffer_refinement.md b/research/results/growth_pullback_v1_4_cash_buffer_refinement.md index 6d2f9c3..7ba471d 100644 --- a/research/results/growth_pullback_v1_4_cash_buffer_refinement.md +++ b/research/results/growth_pullback_v1_4_cash_buffer_refinement.md @@ -85,7 +85,7 @@ | QQQ | 2023+ | 0.282076 | -0.227683 | 0.000000 | 1.000000 | 1.000000 | 1.000000 | -0.000000 | | 1.000000 | 1.000000 | ## Recommendation -- research_recommendation=cash_buffer_branch_default +- research_recommendation=tech_pullback_cash_buffer - selected_refined_default=cash_buffer_a__hb10__base__adv50 - keep_parallel_branch=True - role_vs_qqq_plus_current_default=并行分支 diff --git a/research/results/growth_pullback_v1_4_recommendation.json b/research/results/growth_pullback_v1_4_recommendation.json index 91b9e60..14e224c 100644 --- a/research/results/growth_pullback_v1_4_recommendation.json +++ b/research/results/growth_pullback_v1_4_recommendation.json @@ -2,7 +2,7 @@ "current_default": "coherent_cash_buffer_branch", "selected_refined_default": "cash_buffer_a__hb10__base__adv50", "full_deployment_reference": "coherent_full_deployment_branch", - "research_recommendation": "cash_buffer_branch_default", + "research_recommendation": "tech_pullback_cash_buffer", "keep_parallel_branch": true, "role_vs_qqq_plus_current_default": "并行分支", "reason": "a refined honest cash-buffer candidate is strong enough to act as the branch default, but the spec is not fully frozen yet", diff --git a/research/results/growth_pullback_v1_5_consistency_checks.csv b/research/results/growth_pullback_v1_5_consistency_checks.csv index f33d4e8..1a178af 100644 --- a/research/results/growth_pullback_v1_5_consistency_checks.csv +++ b/research/results/growth_pullback_v1_5_consistency_checks.csv @@ -3,8 +3,8 @@ geometry_capacity_matches_target,True,"theoretical_capacity=0.8000, risk_on_targ risk_on_realized_matches_target,True,"realized=0.8000, target=0.8000" soft_defense_realized_matches_target,True,"realized=0.6000, target=0.6000" hard_defense_realized_zero,True,"realized=0.0000, target=0.0000" -config_name_matches_canonical_name,True,"config_name=cash_buffer_branch_default, canonical_name=cash_buffer_branch_default" +config_name_matches_canonical_name,True,"config_name=tech_pullback_cash_buffer, canonical_name=tech_pullback_cash_buffer" previous_candidate_mapping_preserved,True,previous_candidate_name=cash_buffer_a__hb10__base__adv50 canonical_metrics_match_previous_candidate,True,"canonical_oos=0.340680, prev_oos=0.340680; canonical_2022=-0.120121, prev_2022=-0.120121" -recommendation_matches_branch_default_role,True,recommendation=cash_buffer_branch_default +recommendation_matches_branch_default_role,True,recommendation=tech_pullback_cash_buffer manifest_role_matches_default_branch,True,manifest_role=cash-buffered parallel branch diff --git a/research/results/growth_pullback_v1_5_freeze_review.csv b/research/results/growth_pullback_v1_5_freeze_review.csv index ef67d2f..2f91e60 100644 --- a/research/results/growth_pullback_v1_5_freeze_review.csv +++ b/research/results/growth_pullback_v1_5_freeze_review.csv @@ -1,5 +1,5 @@ strategy,role,full_cagr,oos_cagr,oos_cagr_minus_qqq,oos_max_drawdown,return_2022,annual_turnover,avg_names_held,risk_on_realized_stock_weight,beta_vs_qqq,alpha_ann_vs_qqq,information_ratio_vs_qqq -cash_buffer_branch_default,cash-buffered parallel branch,0.2348114426770309,0.34068001857447316,0.23873167970694698,-0.19176289938838953,-0.12012100392137592,2.6466073207171315,5.898989898989899,0.7999999999999998,0.5027805307503466,0.25647649401833045,0.8743531809818416 +tech_pullback_cash_buffer,cash-buffered parallel branch,0.2348114426770309,0.34068001857447316,0.23873167970694698,-0.19176289938838953,-0.12012100392137592,2.6466073207171315,5.898989898989899,0.7999999999999998,0.5027805307503466,0.25647649401833045,0.8743531809818416 qqq_plus_current_default,main offensive default,0.2416506149571933,0.3340103912486361,0.23206205238110988,-0.3237786750219295,-0.015312392803116026,3.4402863545816738,9.465766634522662,0.96,0.7447972976980726,0.2412113087334444,0.8364950943114928 coherent_full_deployment_branch,full-deployment tech pullback reference,0.2441213592871787,0.3153714302805344,0.2134230914130082,-0.2106193998208613,-0.15963157059653155,3.6427988047808766,7.373737373737374,1.0,0.576097006927484,0.23489758747442563,0.7777996924145466 russell_1000_multi_factor_defensive,defensive base,0.16331062031314847,0.18446943665750348,0.0825210977899773,-0.2543933164893194,-0.13965933707208145,3.9264577108233722,20.644165863066537,1.0,0.5689185619374639,0.12298815318475183,0.3444248571875299 diff --git a/research/results/growth_pullback_v1_5_freeze_review.md b/research/results/growth_pullback_v1_5_freeze_review.md index 2a4834a..ba13ceb 100644 --- a/research/results/growth_pullback_v1_5_freeze_review.md +++ b/research/results/growth_pullback_v1_5_freeze_review.md @@ -3,12 +3,12 @@ ## Canonical branch default | role | status | strategy | branch_name | name | previous_candidate_name | family | universe | normalization | min_adv20_usd | sector_whitelist | symbol_whitelist | notes | score_template | holdings_count | single_name_cap | sector_cap | hold_bonus | regime | benchmark_symbol | breadth_mode | breadth_symbols | breadth_thresholds | exposures | residual_proxy | cost_assumption_bps_one_way | branch_role | canonicalized_from | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| cash_buffer_branch_default | research_only | growth_pullback_systematic_v1 | cash_buffer_branch | cash_buffer_branch_default | cash_buffer_a__hb10__base__adv50 | tech_heavy_pullback | tech_heavy | universe_cross_sectional | 50000000.000000 | ["Information Technology", "Communication"] | [] | Sector proxy only: Information Technology + Communication; no sub-industry data available | balanced_pullback | 8 | 0.100000 | 0.400000 | 0.100000 | qqq_breadth | QQQ | broad | [] | {"hard": 0.35, "soft": 0.55} | {"hard_defense": 0.0, "risk_on": 0.8, "soft_defense": 0.6} | simple_excess_return_vs_QQQ | 5.000000 | cash-buffered parallel branch | growth_pullback_systematic_v1.4 cash_buffer_refinement | +| tech_pullback_cash_buffer | research_only | growth_pullback_systematic_v1 | cash_buffer_branch | tech_pullback_cash_buffer | cash_buffer_a__hb10__base__adv50 | tech_heavy_pullback | tech_heavy | universe_cross_sectional | 50000000.000000 | ["Information Technology", "Communication"] | [] | Sector proxy only: Information Technology + Communication; no sub-industry data available | balanced_pullback | 8 | 0.100000 | 0.400000 | 0.100000 | qqq_breadth | QQQ | broad | [] | {"hard": 0.35, "soft": 0.55} | {"hard_defense": 0.0, "risk_on": 0.8, "soft_defense": 0.6} | simple_excess_return_vs_QQQ | 5.000000 | cash-buffered parallel branch | growth_pullback_systematic_v1.4 cash_buffer_refinement | ## Final role table | strategy | role | full_cagr | oos_cagr | oos_cagr_minus_qqq | oos_max_drawdown | return_2022 | annual_turnover | avg_names_held | risk_on_realized_stock_weight | beta_vs_qqq | alpha_ann_vs_qqq | information_ratio_vs_qqq | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| cash_buffer_branch_default | cash-buffered parallel branch | 0.234811 | 0.340680 | 0.238732 | -0.191763 | -0.120121 | 2.646607 | 5.898990 | 0.800000 | 0.502781 | 0.256476 | 0.874353 | +| tech_pullback_cash_buffer | cash-buffered parallel branch | 0.234811 | 0.340680 | 0.238732 | -0.191763 | -0.120121 | 2.646607 | 5.898990 | 0.800000 | 0.502781 | 0.256476 | 0.874353 | | qqq_plus_current_default | main offensive default | 0.241651 | 0.334010 | 0.232062 | -0.323779 | -0.015312 | 3.440286 | 9.465767 | 0.960000 | 0.744797 | 0.241211 | 0.836495 | | coherent_full_deployment_branch | full-deployment tech pullback reference | 0.244121 | 0.315371 | 0.213423 | -0.210619 | -0.159632 | 3.642799 | 7.373737 | 1.000000 | 0.576097 | 0.234898 | 0.777800 | | russell_1000_multi_factor_defensive | defensive base | 0.163311 | 0.184469 | 0.082521 | -0.254393 | -0.139659 | 3.926458 | 20.644166 | 1.000000 | 0.568919 | 0.122988 | 0.344425 | @@ -21,16 +21,16 @@ | risk_on_realized_matches_target | True | realized=0.8000, target=0.8000 | | soft_defense_realized_matches_target | True | realized=0.6000, target=0.6000 | | hard_defense_realized_zero | True | realized=0.0000, target=0.0000 | -| config_name_matches_canonical_name | True | config_name=cash_buffer_branch_default, canonical_name=cash_buffer_branch_default | +| config_name_matches_canonical_name | True | config_name=tech_pullback_cash_buffer, canonical_name=tech_pullback_cash_buffer | | previous_candidate_mapping_preserved | True | previous_candidate_name=cash_buffer_a__hb10__base__adv50 | | canonical_metrics_match_previous_candidate | True | canonical_oos=0.340680, prev_oos=0.340680; canonical_2022=-0.120121, prev_2022=-0.120121 | -| recommendation_matches_branch_default_role | True | recommendation=cash_buffer_branch_default | +| recommendation_matches_branch_default_role | True | recommendation=tech_pullback_cash_buffer | | manifest_role_matches_default_branch | True | manifest_role=cash-buffered parallel branch | ## Freeze review -- recommendation=cash_buffer_branch_default +- recommendation=tech_pullback_cash_buffer - previous_candidate_name=cash_buffer_a__hb10__base__adv50 -- branch_name=cash_buffer_branch_default +- branch_name=tech_pullback_cash_buffer - role_vs_qqq_plus_current_default=并行分支 - keep_parallel_branch=True - reason=canonical naming and branch role are now clear, but avg names / 2022 profile / recent micro-adjustment mean it should stop at default, not frozen @@ -39,4 +39,4 @@ ## Manifest summary | branch_name | role | previous_candidate_name | benchmark | intended_use | | --- | --- | --- | --- | --- | -| cash_buffer_branch_default | cash-buffered parallel branch | cash_buffer_a__hb10__base__adv50 | QQQ | Research-only parallel stock branch for concentrated tech leaders bought on controlled pullbacks with an explicit 20% cash/BOXX buffer in risk-on. | \ No newline at end of file +| tech_pullback_cash_buffer | cash-buffered parallel branch | cash_buffer_a__hb10__base__adv50 | QQQ | Research-only parallel stock branch for concentrated tech leaders bought on controlled pullbacks with an explicit 20% cash/BOXX buffer in risk-on. | \ No newline at end of file diff --git a/research/results/growth_pullback_v1_5_recommendation.json b/research/results/growth_pullback_v1_5_recommendation.json index 0daf6f8..f5bdeeb 100644 --- a/research/results/growth_pullback_v1_5_recommendation.json +++ b/research/results/growth_pullback_v1_5_recommendation.json @@ -1,6 +1,6 @@ { - "research_recommendation": "cash_buffer_branch_default", - "branch_name": "cash_buffer_branch_default", + "research_recommendation": "tech_pullback_cash_buffer", + "branch_name": "tech_pullback_cash_buffer", "previous_candidate_name": "cash_buffer_a__hb10__base__adv50", "reason": "canonical naming and branch role are now clear, but avg names / 2022 profile / recent micro-adjustment mean it should stop at default, not frozen", "frozen_blockers": [ diff --git a/research/results/cash_buffer_branch_daily_overlay_comparison.csv b/research/results/tech_pullback_cash_buffer_daily_overlay_comparison.csv similarity index 88% rename from research/results/cash_buffer_branch_daily_overlay_comparison.csv rename to research/results/tech_pullback_cash_buffer_daily_overlay_comparison.csv index fbe9b05..d8187d5 100644 --- a/research/results/cash_buffer_branch_daily_overlay_comparison.csv +++ b/research/results/tech_pullback_cash_buffer_daily_overlay_comparison.csv @@ -1,16 +1,16 @@ strategy,family,cost_bps_one_way,period,Start,End,Total Return,CAGR,Max Drawdown,Volatility,Sharpe,Sortino,Calmar,Turnover/Year,Rebalances/Year,Average Names Held,Beta vs QQQ,Information Ratio vs QQQ,Up Capture vs QQQ,Down Capture vs QQQ,2022 Return,2023+ CAGR,beta_vs_qqq,alpha_ann_vs_qqq,tracking_error_vs_qqq,information_ratio_vs_qqq,up_capture_vs_qqq,down_capture_vs_qqq,rolling_36m_alpha_mean,rolling_36m_alpha_median,rolling_36m_alpha_last,rolling_36m_alpha_positive_ratio,Average Stock Weight,Average BOXX Weight,Overlay Trigger Frequency,Average Days In Throttle,Average Days Trimmed,Days In Throttle Share,Days Trimmed Share,Degraded Signal Share,overlay_trigger_frequency,average_days_in_throttle,average_days_trimmed,days_in_throttle_share,days_trimmed_share,degraded_signal_share -cash_buffer_branch_default_monthly_baseline,monthly_baseline,0.0,Full Sample,2018-01-02,2026-04-02,4.755674795852736,0.23643994190919604,-0.3073638175128768,0.2306005629024522,1.037996928794197,1.252890217872731,0.7692510583139492,2.646607320717131,8.973605577689243,5.824493731918997,0.5432317915777534,0.20737436395782305,0.6852339287997956,0.12991408219883313,-0.1192167647234279,0.5287244523691539,0.5432317915777534,0.13407682197513507,0.21964532355387098,0.20737436395782305,0.6852339287997956,0.12991408219883313,0.07107281567945753,0.04720718337757644,0.2808467118290693,0.8089461713419257,0.5537867824409068,0.44621321755909316,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -cash_buffer_branch_default_monthly_baseline,monthly_baseline,0.0,OOS Sample,2022-01-03,2026-04-02,2.490584101723596,0.34255260573229096,-0.1916510378578209,0.22312707697546538,1.4366767481429314,1.90186419207682,1.787376732008196,2.7982862903225807,9.661451612903226,6.101313320825516,0.5028003776079697,0.8806498475119708,1.0127533901187837,0.13569639149750395,-0.1192167647234279,0.5287244523691539,0.5028003776079697,0.2578776643495561,0.22244038155082352,0.8806498475119708,1.0127533901187837,0.13569639149750395,0.20246670398962638,0.1975763853448783,0.28084671182907006,1.0,0.5663849765258215,0.43361502347417846,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -cash_buffer_branch_default_monthly_baseline,monthly_baseline,0.0,2022,2022-01-03,2022-12-30,-0.1192167647234279,-0.1205321032161123,-0.1473480842578787,0.11661193440173574,-1.0343300085975302,-0.8867766335682251,-0.8180092997012782,2.0741343490304707,4.0470914127423825,1.8804780876494025,0.14083841225936303,0.7547581257138813,0.0,0.19484548607001775,-0.1192167647234279,0.5287244523691539,0.14083841225936303,-0.07217515601224586,0.2958901730049101,0.7547581257138813,0.0,0.19484548607001775,,,,,0.15617529880478087,0.8438247011952191,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -cash_buffer_branch_default_monthly_baseline,monthly_baseline,0.0,2023+,2023-01-03,2026-04-02,2.9630455734406924,0.5287244523691539,-0.1916510378578209,0.2462100305377828,1.853836261461959,2.664054448256608,2.7587873161500736,3.028338607594937,11.404430379746836,7.401226993865031,0.7901980198293913,0.9646976412312489,1.23700641086397,0.03271628506705888,-0.1192167647234279,0.5287244523691539,0.7901980198293913,0.24387786264820507,0.19430260696204096,0.9646976412312489,1.23700641086397,0.03271628506705888,0.241942228237925,0.24999814279451354,0.2808467118290707,1.0,0.6928746928746928,0.30712530712530717,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -cash_buffer_branch_default_monthly_baseline,monthly_baseline,5.0,Full Sample,2018-01-02,2026-04-02,4.693458553595624,0.2348114426770309,-0.3083922411959321,0.2305812884853688,1.0323333759231557,1.2458270577949806,0.7614051565189903,2.646607320717131,8.973605577689243,5.824493731918997,0.5432164893760933,0.2013526473993671,0.6826953316939703,0.13191061485454292,-0.12012100392137592,0.5264192069255103,0.5432164893760933,0.13275387164986283,0.21962906242019783,0.2013526473993671,0.6826953316939703,0.13191061485454292,0.06958660268945491,0.04568150302746318,0.27949926080829407,0.8021228203184231,0.5537867824409068,0.44621321755909316,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -cash_buffer_branch_default_monthly_baseline,monthly_baseline,5.0,OOS Sample,2022-01-03,2026-04-02,2.4699698396640235,0.34068001857447316,-0.19176289938838953,0.22311874719721844,1.430439511465031,1.891864480185319,1.7765689800323283,2.7982862903225807,9.661451612903226,6.101313320825516,0.502780394656072,0.8743533417402809,1.0100573409414484,0.13783008037623837,-0.12012100392137592,0.5264192069255103,0.502780394656072,0.25647654397981495,0.2224369338760282,0.8743533417402809,1.0100573409414484,0.13783008037623837,0.20103876919333322,0.1963456327131466,0.27949926080829485,1.0,0.5663849765258215,0.43361502347417846,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -cash_buffer_branch_default_monthly_baseline,monthly_baseline,5.0,2022,2022-01-03,2022-12-30,-0.12012100392137592,-0.12144561610226978,-0.1473480842578786,0.11662608635640283,-1.0430282844776455,-0.8809537193472543,-0.8242089926987033,2.0741343490304707,4.0470914127423825,1.8804780876494025,0.14083089480158717,0.7512593988800813,-0.0011209256904424034,0.19596225734213568,-0.12012100392137592,0.5264192069255103,0.14083089480158717,-0.07320682523754109,0.2958983662106398,0.7512593988800813,-0.0011209256904424034,0.19596225734213568,,,,,0.15617529880478087,0.8438247011952191,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -cash_buffer_branch_default_monthly_baseline,monthly_baseline,5.0,2023+,2023-01-03,2026-04-02,2.9436898199965196,0.5264192069255103,-0.1917628993883893,0.2461991646932531,1.8477484521857512,2.6536571286687494,2.7451566940449768,3.028338607594937,11.404430379746836,7.401226993865031,0.7901749477428144,0.9569249291851327,1.2339615835897864,0.036620453851144534,-0.12012100392137592,0.5264192069255103,0.7901749477428144,0.24236511174931172,0.1942935165504279,0.9569249291851327,1.2339615835897864,0.036620453851144534,0.2405474889681383,0.24865365123435113,0.2794992608082955,1.0,0.6928746928746928,0.30712530712530717,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -cash_buffer_branch_default_monthly_baseline,monthly_baseline,10.0,Full Sample,2018-01-02,2026-04-02,4.631902755489005,0.23318476742367977,-0.3094193013398555,0.23056312173396298,1.0266639438365857,1.2391118995393355,0.753620625519924,2.646607320717131,8.973605577689243,5.824493731918997,0.5432011871744332,0.1953290043535231,0.6801567345881429,0.13390714751025362,-0.12102455617753993,0.5241170176641934,0.5432011871744332,0.1314309213245906,0.21961396466426708,0.1953290043535231,0.6801567345881429,0.13390714751025362,0.06810038969945227,0.044155822677349896,0.27815180978751886,0.7983320697498104,0.5537867824409068,0.44621321755909316,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -cash_buffer_branch_default_monthly_baseline,monthly_baseline,10.0,OOS Sample,2022-01-03,2026-04-02,2.4494732873705534,0.33880967458732414,-0.19187476091895794,0.22311162654435113,1.4241940907729618,1.8841206762875846,1.7657855205376753,2.7982862903225807,9.661451612903226,6.101313320825516,0.5027604117041741,0.868051906770272,1.0073612917641117,0.13996376925497514,-0.12102455617753993,0.5241170176641934,0.5027604117041741,0.2550754236100738,0.22243469926481768,0.868051906770272,1.0073612917641117,0.13996376925497514,0.19961083439704008,0.19511488008141484,0.27815180978751963,1.0,0.5663849765258215,0.43361502347417846,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -cash_buffer_branch_default_monthly_baseline,monthly_baseline,10.0,2022,2022-01-03,2022-12-30,-0.12102455617753993,-0.12235842396176622,-0.1473480842578787,0.11664255147301852,-1.0517035926754035,-0.8910594668568658,-0.8304039009263448,2.0741343490304707,4.0470914127423825,1.8804780876494025,0.14082337734381128,0.7477585605105919,-0.002241851380885222,0.19707902861425486,-0.12102455617753993,0.5241170176641934,0.14082337734381128,-0.07423849446283629,0.2959074716745159,0.7477585605105919,-0.002241851380885222,0.19707902861425486,,,,,0.15617529880478087,0.8438247011952191,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -cash_buffer_branch_default_monthly_baseline,monthly_baseline,10.0,2023+,2023-01-03,2026-04-02,2.924425092434444,0.5241170176641934,-0.19187476091895794,0.24618939330643555,1.8416519182523061,2.64517705000437,2.7315578930444344,3.028338607594937,11.404430379746836,7.401226993865031,0.7901518756562376,0.9491447136879954,1.230916756315601,0.04052462263523456,-0.12102455617753993,0.5241170176641934,0.7901518756562376,0.2408523608504183,0.19428581316584353,0.9491447136879954,1.230916756315601,0.04052462263523456,0.23915274969835165,0.24730915967418887,0.2781518097875204,1.0,0.6928746928746928,0.30712530712530717,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +tech_pullback_cash_buffer_monthly_baseline,monthly_baseline,0.0,Full Sample,2018-01-02,2026-04-02,4.755674795852736,0.23643994190919604,-0.3073638175128768,0.2306005629024522,1.037996928794197,1.252890217872731,0.7692510583139492,2.646607320717131,8.973605577689243,5.824493731918997,0.5432317915777534,0.20737436395782305,0.6852339287997956,0.12991408219883313,-0.1192167647234279,0.5287244523691539,0.5432317915777534,0.13407682197513507,0.21964532355387098,0.20737436395782305,0.6852339287997956,0.12991408219883313,0.07107281567945753,0.04720718337757644,0.2808467118290693,0.8089461713419257,0.5537867824409068,0.44621321755909316,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +tech_pullback_cash_buffer_monthly_baseline,monthly_baseline,0.0,OOS Sample,2022-01-03,2026-04-02,2.490584101723596,0.34255260573229096,-0.1916510378578209,0.22312707697546538,1.4366767481429314,1.90186419207682,1.787376732008196,2.7982862903225807,9.661451612903226,6.101313320825516,0.5028003776079697,0.8806498475119708,1.0127533901187837,0.13569639149750395,-0.1192167647234279,0.5287244523691539,0.5028003776079697,0.2578776643495561,0.22244038155082352,0.8806498475119708,1.0127533901187837,0.13569639149750395,0.20246670398962638,0.1975763853448783,0.28084671182907006,1.0,0.5663849765258215,0.43361502347417846,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +tech_pullback_cash_buffer_monthly_baseline,monthly_baseline,0.0,2022,2022-01-03,2022-12-30,-0.1192167647234279,-0.1205321032161123,-0.1473480842578787,0.11661193440173574,-1.0343300085975302,-0.8867766335682251,-0.8180092997012782,2.0741343490304707,4.0470914127423825,1.8804780876494025,0.14083841225936303,0.7547581257138813,0.0,0.19484548607001775,-0.1192167647234279,0.5287244523691539,0.14083841225936303,-0.07217515601224586,0.2958901730049101,0.7547581257138813,0.0,0.19484548607001775,,,,,0.15617529880478087,0.8438247011952191,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +tech_pullback_cash_buffer_monthly_baseline,monthly_baseline,0.0,2023+,2023-01-03,2026-04-02,2.9630455734406924,0.5287244523691539,-0.1916510378578209,0.2462100305377828,1.853836261461959,2.664054448256608,2.7587873161500736,3.028338607594937,11.404430379746836,7.401226993865031,0.7901980198293913,0.9646976412312489,1.23700641086397,0.03271628506705888,-0.1192167647234279,0.5287244523691539,0.7901980198293913,0.24387786264820507,0.19430260696204096,0.9646976412312489,1.23700641086397,0.03271628506705888,0.241942228237925,0.24999814279451354,0.2808467118290707,1.0,0.6928746928746928,0.30712530712530717,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +tech_pullback_cash_buffer_monthly_baseline,monthly_baseline,5.0,Full Sample,2018-01-02,2026-04-02,4.693458553595624,0.2348114426770309,-0.3083922411959321,0.2305812884853688,1.0323333759231557,1.2458270577949806,0.7614051565189903,2.646607320717131,8.973605577689243,5.824493731918997,0.5432164893760933,0.2013526473993671,0.6826953316939703,0.13191061485454292,-0.12012100392137592,0.5264192069255103,0.5432164893760933,0.13275387164986283,0.21962906242019783,0.2013526473993671,0.6826953316939703,0.13191061485454292,0.06958660268945491,0.04568150302746318,0.27949926080829407,0.8021228203184231,0.5537867824409068,0.44621321755909316,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +tech_pullback_cash_buffer_monthly_baseline,monthly_baseline,5.0,OOS Sample,2022-01-03,2026-04-02,2.4699698396640235,0.34068001857447316,-0.19176289938838953,0.22311874719721844,1.430439511465031,1.891864480185319,1.7765689800323283,2.7982862903225807,9.661451612903226,6.101313320825516,0.502780394656072,0.8743533417402809,1.0100573409414484,0.13783008037623837,-0.12012100392137592,0.5264192069255103,0.502780394656072,0.25647654397981495,0.2224369338760282,0.8743533417402809,1.0100573409414484,0.13783008037623837,0.20103876919333322,0.1963456327131466,0.27949926080829485,1.0,0.5663849765258215,0.43361502347417846,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +tech_pullback_cash_buffer_monthly_baseline,monthly_baseline,5.0,2022,2022-01-03,2022-12-30,-0.12012100392137592,-0.12144561610226978,-0.1473480842578786,0.11662608635640283,-1.0430282844776455,-0.8809537193472543,-0.8242089926987033,2.0741343490304707,4.0470914127423825,1.8804780876494025,0.14083089480158717,0.7512593988800813,-0.0011209256904424034,0.19596225734213568,-0.12012100392137592,0.5264192069255103,0.14083089480158717,-0.07320682523754109,0.2958983662106398,0.7512593988800813,-0.0011209256904424034,0.19596225734213568,,,,,0.15617529880478087,0.8438247011952191,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +tech_pullback_cash_buffer_monthly_baseline,monthly_baseline,5.0,2023+,2023-01-03,2026-04-02,2.9436898199965196,0.5264192069255103,-0.1917628993883893,0.2461991646932531,1.8477484521857512,2.6536571286687494,2.7451566940449768,3.028338607594937,11.404430379746836,7.401226993865031,0.7901749477428144,0.9569249291851327,1.2339615835897864,0.036620453851144534,-0.12012100392137592,0.5264192069255103,0.7901749477428144,0.24236511174931172,0.1942935165504279,0.9569249291851327,1.2339615835897864,0.036620453851144534,0.2405474889681383,0.24865365123435113,0.2794992608082955,1.0,0.6928746928746928,0.30712530712530717,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +tech_pullback_cash_buffer_monthly_baseline,monthly_baseline,10.0,Full Sample,2018-01-02,2026-04-02,4.631902755489005,0.23318476742367977,-0.3094193013398555,0.23056312173396298,1.0266639438365857,1.2391118995393355,0.753620625519924,2.646607320717131,8.973605577689243,5.824493731918997,0.5432011871744332,0.1953290043535231,0.6801567345881429,0.13390714751025362,-0.12102455617753993,0.5241170176641934,0.5432011871744332,0.1314309213245906,0.21961396466426708,0.1953290043535231,0.6801567345881429,0.13390714751025362,0.06810038969945227,0.044155822677349896,0.27815180978751886,0.7983320697498104,0.5537867824409068,0.44621321755909316,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +tech_pullback_cash_buffer_monthly_baseline,monthly_baseline,10.0,OOS Sample,2022-01-03,2026-04-02,2.4494732873705534,0.33880967458732414,-0.19187476091895794,0.22311162654435113,1.4241940907729618,1.8841206762875846,1.7657855205376753,2.7982862903225807,9.661451612903226,6.101313320825516,0.5027604117041741,0.868051906770272,1.0073612917641117,0.13996376925497514,-0.12102455617753993,0.5241170176641934,0.5027604117041741,0.2550754236100738,0.22243469926481768,0.868051906770272,1.0073612917641117,0.13996376925497514,0.19961083439704008,0.19511488008141484,0.27815180978751963,1.0,0.5663849765258215,0.43361502347417846,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +tech_pullback_cash_buffer_monthly_baseline,monthly_baseline,10.0,2022,2022-01-03,2022-12-30,-0.12102455617753993,-0.12235842396176622,-0.1473480842578787,0.11664255147301852,-1.0517035926754035,-0.8910594668568658,-0.8304039009263448,2.0741343490304707,4.0470914127423825,1.8804780876494025,0.14082337734381128,0.7477585605105919,-0.002241851380885222,0.19707902861425486,-0.12102455617753993,0.5241170176641934,0.14082337734381128,-0.07423849446283629,0.2959074716745159,0.7477585605105919,-0.002241851380885222,0.19707902861425486,,,,,0.15617529880478087,0.8438247011952191,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +tech_pullback_cash_buffer_monthly_baseline,monthly_baseline,10.0,2023+,2023-01-03,2026-04-02,2.924425092434444,0.5241170176641934,-0.19187476091895794,0.24618939330643555,1.8416519182523061,2.64517705000437,2.7315578930444344,3.028338607594937,11.404430379746836,7.401226993865031,0.7901518756562376,0.9491447136879954,1.230916756315601,0.04052462263523456,-0.12102455617753993,0.5241170176641934,0.7901518756562376,0.2408523608504183,0.19428581316584353,0.9491447136879954,1.230916756315601,0.04052462263523456,0.23915274969835165,0.24730915967418887,0.2781518097875204,1.0,0.6928746928746928,0.30712530712530717,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 daily_portfolio_throttle_overlay__held_breadth_5state,daily_portfolio_throttle_overlay,0.0,Full Sample,2018-01-02,2026-04-02,1.2372197926794044,0.1025731160156953,-0.31586363851445687,0.1412059434066132,0.7637098124808323,0.7922962415730113,0.32473860080289235,12.153778635458165,46.08067729083665,5.218900675024108,0.21177282920630977,-0.37406475405904693,0.34370354638811373,0.12559753096912685,-0.08519752265070979,0.26337152180621604,0.21177282920630977,0.06679585364802848,0.22983588084551132,-0.37406475405904693,0.34370354638811373,0.12559753096912685,0.025733130439692352,0.009080855443508505,0.16246074201042607,0.5504169825625473,0.3286058851905452,0.6713941148094549,0.5016883743367101,11.685393258426966,0.0,0.5016883743367101,0.0,0.0,0.5016883743367101,11.685393258426966,0.0,0.5016883743367101,0.0,0.0 daily_portfolio_throttle_overlay__held_breadth_5state,daily_portfolio_throttle_overlay,0.0,OOS Sample,2022-01-03,2026-04-02,0.9531249346162227,0.17087119932866623,-0.12581178992971032,0.13600537430036821,1.231992502109256,1.4172892664507384,1.3581493389779298,12.624689516129035,46.8933870967742,5.433395872420262,0.22861090260552594,0.19545299910070527,0.5894370398375502,0.15812540038769882,-0.08519752265070979,0.26337152180621604,0.22861090260552594,0.13905681840271084,0.2194297672762084,0.19545299910070527,0.5894370398375502,0.15812540038769882,0.118245667066601,0.11705701888283034,0.16246074201042646,1.0,0.3427230046948357,0.6572769953051644,0.5126760563380282,11.142857142857142,0.0,0.5126760563380282,0.0,0.0,0.5016883743367101,11.685393258426966,0.0,0.5016883743367101,0.0,0.0 daily_portfolio_throttle_overlay__held_breadth_5state,daily_portfolio_throttle_overlay,0.0,2022,2022-01-03,2022-12-30,-0.08519752265070979,-0.08615604282779388,-0.08275798164103698,0.03344726284676462,-2.655595246133043,-1.2744607604293277,-1.0410602230670147,3.3388504155124656,13.153047091412741,1.0199203187250996,0.024948957195034732,0.8110920959515007,0.0,0.1412107097081077,-0.08519752265070979,0.26337152180621604,0.024948957195034732,-0.08024142952424597,0.31453683813223643,0.8110920959515007,0.0,0.1412107097081077,,,,,0.039043824701195225,0.9609561752988047,0.2151394422310757,18.0,0.0,0.2151394422310757,0.0,0.0,0.5016883743367101,11.685393258426966,0.0,0.5016883743367101,0.0,0.0 diff --git a/research/results/cash_buffer_branch_daily_overlay_recommendation.json b/research/results/tech_pullback_cash_buffer_daily_overlay_recommendation.json similarity index 94% rename from research/results/cash_buffer_branch_daily_overlay_recommendation.json rename to research/results/tech_pullback_cash_buffer_daily_overlay_recommendation.json index e9748ad..f231e3b 100644 --- a/research/results/cash_buffer_branch_daily_overlay_recommendation.json +++ b/research/results/tech_pullback_cash_buffer_daily_overlay_recommendation.json @@ -1,5 +1,5 @@ { - "baseline_strategy": "cash_buffer_branch_default_monthly_baseline", + "baseline_strategy": "tech_pullback_cash_buffer_monthly_baseline", "overlay_has_incremental_value": false, "best_overlay_family": "daily_name_level_trim_overlay", "best_overlay_strategy": "daily_name_level_trim_overlay__20dma_half_confirm2", diff --git a/research/results/cash_buffer_branch_daily_overlay_summary.md b/research/results/tech_pullback_cash_buffer_daily_overlay_summary.md similarity index 93% rename from research/results/cash_buffer_branch_daily_overlay_summary.md rename to research/results/tech_pullback_cash_buffer_daily_overlay_summary.md index f4a648d..4ab64e5 100644 --- a/research/results/cash_buffer_branch_daily_overlay_summary.md +++ b/research/results/tech_pullback_cash_buffer_daily_overlay_summary.md @@ -1,7 +1,7 @@ # cash_buffer_branch daily overlay research ## Baseline -- strategy=cash_buffer_branch_default +- strategy=tech_pullback_cash_buffer - family=tech_heavy_pullback - monthly stock exposures={"risk_on": 0.8, "soft_defense": 0.6, "hard_defense": 0.0} - holdings=8 @@ -19,12 +19,12 @@ ## Baseline reference (5 bps, OOS) | strategy | CAGR | Max Drawdown | Information Ratio vs QQQ | alpha_ann_vs_qqq | return_2022 | cagr_2023_plus | Turnover/Year | Average Stock Weight | Average BOXX Weight | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| cash_buffer_branch_default_monthly_baseline | 0.340680 | -0.191763 | 0.874353 | 0.256477 | -0.120121 | 0.526419 | 2.798286 | 0.566385 | 0.433615 | +| tech_pullback_cash_buffer_monthly_baseline | 0.340680 | -0.191763 | 0.874353 | 0.256477 | -0.120121 | 0.526419 | 2.798286 | 0.566385 | 0.433615 | ## Family selection table (5 bps, OOS) | strategy | family | overlay_selection_score | OOS CAGR | OOS MaxDD | Information Ratio vs QQQ | alpha_ann_vs_qqq | 2022 Return | 2023+ CAGR | Turnover/Year | Average Stock Weight | Average BOXX Weight | Overlay Trigger Frequency | Average Days In Throttle | Average Days Trimmed | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| cash_buffer_branch_default_monthly_baseline | monthly_baseline | 0.800000 | 0.340680 | -0.191763 | 0.874353 | 0.256477 | -0.120121 | 0.526419 | 2.798286 | 0.566385 | 0.433615 | 0.000000 | 0.000000 | 0.000000 | +| tech_pullback_cash_buffer_monthly_baseline | monthly_baseline | 0.800000 | 0.340680 | -0.191763 | 0.874353 | 0.256477 | -0.120121 | 0.526419 | 2.798286 | 0.566385 | 0.433615 | 0.000000 | 0.000000 | 0.000000 | | daily_name_level_trim_overlay__20dma_half_confirm2 | daily_name_level_trim_overlay | 0.770000 | 0.254582 | -0.148093 | 0.562249 | 0.195607 | -0.112283 | 0.395667 | 8.218125 | 0.468099 | 0.531901 | 0.632864 | 0.000000 | 12.481481 | | daily_name_level_trim_overlay__50dma_zero_confirm2 | daily_name_level_trim_overlay | 0.550000 | 0.214020 | -0.156768 | 0.381910 | 0.169430 | -0.113352 | 0.337437 | 9.655560 | 0.421972 | 0.578028 | 0.581221 | 0.000000 | 12.380000 | | daily_portfolio_throttle_overlay__qqq_dma_mom_6state | daily_portfolio_throttle_overlay | 0.480000 | 0.206767 | -0.134282 | 0.344992 | 0.166449 | -0.113148 | 0.326901 | 12.115107 | 0.413333 | 0.586667 | 0.404695 | 8.450980 | 0.000000 | @@ -48,7 +48,7 @@ _skipped because A/B did not both clear the incremental-value filter_ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | daily_name_level_trim_overlay__20dma_half_confirm2 | daily_name_level_trim_overlay | 0.254582 | 0.152634 | -0.148093 | -0.112283 | 0.395667 | 8.218125 | 6.101313 | 0.468099 | 0.531901 | 0.389330 | 0.195607 | 0.562249 | 0.816321 | 0.167005 | 0.632864 | 0.000000 | 12.481481 | | daily_portfolio_throttle_overlay__qqq_dma_mom_6state | daily_portfolio_throttle_overlay | 0.206767 | 0.104818 | -0.134282 | -0.113148 | 0.326901 | 12.115107 | 5.448405 | 0.413333 | 0.586667 | 0.285801 | 0.166449 | 0.344992 | 0.688788 | 0.159194 | 0.404695 | 8.450980 | 0.000000 | -| cash_buffer_branch_default_monthly_baseline | monthly_baseline | 0.340680 | 0.238732 | -0.191763 | -0.120121 | 0.526419 | 2.798286 | 6.101313 | 0.566385 | 0.433615 | 0.502780 | 0.256477 | 0.874353 | 1.010057 | 0.137830 | 0.000000 | 0.000000 | 0.000000 | +| tech_pullback_cash_buffer_monthly_baseline | monthly_baseline | 0.340680 | 0.238732 | -0.191763 | -0.120121 | 0.526419 | 2.798286 | 6.101313 | 0.566385 | 0.433615 | 0.502780 | 0.256477 | 0.874353 | 1.010057 | 0.137830 | 0.000000 | 0.000000 | 0.000000 | | qqq_plus_current_default | reference | 0.334010 | 0.232062 | -0.323779 | -0.015312 | 0.464792 | 3.706698 | 10.570356 | 0.760188 | 0.239812 | 0.744797 | 0.241211 | 0.836495 | 1.200446 | 0.399330 | 0.000000 | 0.000000 | 0.000000 | | russell_1000_multi_factor_defensive | reference | 0.184469 | 0.082521 | -0.254393 | -0.139659 | 0.307094 | 4.530278 | 24.000000 | 0.759287 | 0.240713 | 0.568919 | 0.122988 | 0.344425 | 0.826915 | 0.413046 | 0.000000 | 0.000000 | 0.000000 | | QQQ | reference | 0.101948 | 0.000000 | -0.348280 | -0.325770 | 0.282076 | 0.000000 | 1.000000 | 1.000000 | 0.000000 | 1.000000 | 0.000000 | | 1.000000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 | diff --git a/runtime_config_support.py b/runtime_config_support.py index e6aad02..8248c04 100644 --- a/runtime_config_support.py +++ b/runtime_config_support.py @@ -157,12 +157,12 @@ def resolve_strategy_config_path( if path is not None: return path, "env" - if str(strategy_profile).strip().lower() == "cash_buffer_branch_default": + if str(strategy_profile).strip().lower() == "tech_pullback_cash_buffer": bundled = ( Path(__file__).resolve().parent / "research" / "configs" - / "growth_pullback_cash_buffer_branch_default.json" + / "growth_pullback_tech_pullback_cash_buffer.json" ) if bundled.exists(): return str(bundled), "bundled_canonical_default" diff --git a/strategy_registry.py b/strategy_registry.py index 7510c60..e71e53b 100644 --- a/strategy_registry.py +++ b/strategy_registry.py @@ -19,7 +19,7 @@ # 平台启用状态从策略定义层拆出来;当前只让 IBKR 明确启用这三条。 IBKR_ENABLED_PROFILES = frozenset( { - "cash_buffer_branch_default", + "tech_pullback_cash_buffer", "global_etf_rotation", "russell_1000_multi_factor_defensive", } diff --git a/tests/test_execution_service.py b/tests/test_execution_service.py index fb44180..6dfc347 100644 --- a/tests/test_execution_service.py +++ b/tests/test_execution_service.py @@ -51,7 +51,7 @@ def fake_fetch_quote_snapshots(_ib, symbols): order_intent_cls=OrderIntent, translator=translate, strategy_symbols=["VOO", "BIL"], - strategy_profile="cash_buffer_branch_default", + strategy_profile="tech_pullback_cash_buffer", signal_metadata={ "regime": "risk_on", "breadth_ratio": 0.6, @@ -98,7 +98,7 @@ def accountValues(self): order_intent_cls=OrderIntent, translator=translate, strategy_symbols=["VOO"], - strategy_profile="cash_buffer_branch_default", + strategy_profile="tech_pullback_cash_buffer", signal_metadata={}, dry_run_only=False, cash_reserve_ratio=0.03, @@ -107,7 +107,7 @@ def accountValues(self): sell_settle_delay_sec=0, ) - assert trade_logs == ["pending_orders_detected profile=cash_buffer_branch_default symbols=VOO"] + assert trade_logs == ["pending_orders_detected profile=tech_pullback_cash_buffer symbols=VOO"] def test_execute_rebalance_blocks_same_day_repeat_via_execution_lock(tmp_path, monkeypatch): @@ -132,7 +132,7 @@ def fake_fetch_quote_snapshots(_ib, symbols): order_intent_cls=OrderIntent, translator=translate, strategy_symbols=["VOO", "BOXX"], - strategy_profile="cash_buffer_branch_default", + strategy_profile="tech_pullback_cash_buffer", account_group="default", service_name="ibkr-paper", account_ids=("DU123",), @@ -207,7 +207,7 @@ def accountValues(self): order_intent_cls=OrderIntent, translator=translate, strategy_symbols=["VOO"], - strategy_profile="cash_buffer_branch_default", + strategy_profile="tech_pullback_cash_buffer", account_group="default", service_name="ibkr-paper", account_ids=("DU123",), @@ -248,7 +248,7 @@ def fake_fetch_quote_snapshots(_ib, symbols): order_intent_cls=OrderIntent, translator=translate, strategy_symbols=["VOO", "BOXX"], - strategy_profile="cash_buffer_branch_default", + strategy_profile="tech_pullback_cash_buffer", account_group="default", service_name="ibkr-paper", account_ids=("DU123",), diff --git a/tests/test_feature_snapshot_service.py b/tests/test_feature_snapshot_service.py index ba99a33..60add8e 100644 --- a/tests/test_feature_snapshot_service.py +++ b/tests/test_feature_snapshot_service.py @@ -94,9 +94,9 @@ def test_load_feature_snapshot_guarded_requires_manifest_when_requested(self): run_as_of="2026-04-01", required_columns=("as_of", "symbol", "sector", "mom_6_1"), require_manifest=True, - expected_strategy_profile="cash_buffer_branch_default", - expected_config_name="cash_buffer_branch_default", - expected_contract_version="cash_buffer_branch_default.feature_snapshot.v1", + expected_strategy_profile="tech_pullback_cash_buffer", + expected_config_name="tech_pullback_cash_buffer", + expected_contract_version="tech_pullback_cash_buffer.feature_snapshot.v1", ) self.assertIsNone(result.frame) @@ -111,14 +111,14 @@ def test_load_feature_snapshot_guarded_validates_manifest_checksums(self): snapshot_path = Path(tmp_dir) / "snapshot.csv" config_path = Path(tmp_dir) / "config.json" snapshot_path.write_text("as_of,symbol,sector,mom_6_1\n2026-03-31,AAA,tech,0.1\n", encoding="utf-8") - config_path.write_text(json.dumps({"name": "cash_buffer_branch_default"}), encoding="utf-8") + config_path.write_text(json.dumps({"name": "tech_pullback_cash_buffer"}), encoding="utf-8") manifest_path = Path(f"{snapshot_path}.manifest.json") manifest_path.write_text( json.dumps( { - "contract_version": "cash_buffer_branch_default.feature_snapshot.v1", - "strategy_profile": "cash_buffer_branch_default", - "config_name": "cash_buffer_branch_default", + "contract_version": "tech_pullback_cash_buffer.feature_snapshot.v1", + "strategy_profile": "tech_pullback_cash_buffer", + "config_name": "tech_pullback_cash_buffer", "config_path": str(config_path), "config_sha256": _sha256_file(config_path), "snapshot_path": str(snapshot_path), @@ -134,16 +134,16 @@ def test_load_feature_snapshot_guarded_validates_manifest_checksums(self): run_as_of="2026-04-01", required_columns=("as_of", "symbol", "sector", "mom_6_1"), require_manifest=True, - expected_strategy_profile="cash_buffer_branch_default", - expected_config_name="cash_buffer_branch_default", + expected_strategy_profile="tech_pullback_cash_buffer", + expected_config_name="tech_pullback_cash_buffer", expected_config_path=str(config_path), - expected_contract_version="cash_buffer_branch_default.feature_snapshot.v1", + expected_contract_version="tech_pullback_cash_buffer.feature_snapshot.v1", ) self.assertIsNotNone(result.frame) self.assertEqual(result.metadata["snapshot_guard_decision"], "proceed") - self.assertEqual(result.metadata["snapshot_manifest_strategy_profile"], "cash_buffer_branch_default") - self.assertEqual(result.metadata["snapshot_manifest_config_name"], "cash_buffer_branch_default") + self.assertEqual(result.metadata["snapshot_manifest_strategy_profile"], "tech_pullback_cash_buffer") + self.assertEqual(result.metadata["snapshot_manifest_config_name"], "tech_pullback_cash_buffer") if __name__ == "__main__": diff --git a/tests/test_growth_pullback_v1_4_cash_buffer_refinement.py b/tests/test_growth_pullback_v1_4_cash_buffer_refinement.py index 522a923..a03e607 100644 --- a/tests/test_growth_pullback_v1_4_cash_buffer_refinement.py +++ b/tests/test_growth_pullback_v1_4_cash_buffer_refinement.py @@ -115,7 +115,7 @@ def test_freeze_decision_logic_can_freeze_current_default(): all_candidates=candidate_table, ) - assert rec['research_recommendation'] == 'cash_buffer_branch_default_frozen' + assert rec['research_recommendation'] == 'tech_pullback_cash_buffer_frozen' assert rec['keep_parallel_branch'] is True assert rec['role_vs_qqq_plus_current_default'] == '并行分支' @@ -154,7 +154,7 @@ def test_result_export_not_empty(tmp_path): } ]), recommendation={ - 'research_recommendation': 'cash_buffer_branch_default', + 'research_recommendation': 'tech_pullback_cash_buffer', 'selected_refined_default': 'coherent_cash_buffer_branch', 'keep_parallel_branch': True, 'role_vs_qqq_plus_current_default': '并行分支', diff --git a/tests/test_growth_pullback_v1_5_freeze_review.py b/tests/test_growth_pullback_v1_5_freeze_review.py index 5c9dc09..b789242 100644 --- a/tests/test_growth_pullback_v1_5_freeze_review.py +++ b/tests/test_growth_pullback_v1_5_freeze_review.py @@ -25,11 +25,11 @@ def test_canonical_config_export(tmp_path): center = module.gp.load_spec_config(CONFIGS_DIR / module.v14.CENTER_CONFIG_FILENAME) spec = module.build_canonical_spec(center, 'cash_buffer_a__hb10__base__adv50') - out = tmp_path / 'growth_pullback_cash_buffer_branch_default.json' + out = tmp_path / 'growth_pullback_tech_pullback_cash_buffer.json' module.save_canonical_spec(out, spec) payload = json.loads(out.read_text(encoding='utf-8')) - assert payload['name'] == 'cash_buffer_branch_default' + assert payload['name'] == 'tech_pullback_cash_buffer' assert payload['previous_candidate_name'] == 'cash_buffer_a__hb10__base__adv50' assert payload['holdings_count'] == 8 assert payload['single_name_cap'] == 0.10 @@ -56,10 +56,10 @@ def test_manifest_fields_non_empty(): 'return_2022': -0.120121, }, qqq_plus_oos=pd.Series({'CAGR': 0.33401}), - recommendation_level='cash_buffer_branch_default', + recommendation_level='tech_pullback_cash_buffer', ) - assert manifest['branch_name'] == 'cash_buffer_branch_default' + assert manifest['branch_name'] == 'tech_pullback_cash_buffer' assert manifest['role'] assert manifest['intended_use'] assert manifest['why_this_is_a_cash_buffer_branch'] @@ -98,7 +98,7 @@ def test_geometry_and_deployment_consistency_checks(): v14_summary=v14_summary, previous_candidate_name='cash_buffer_a__hb10__base__adv50', canonical_summary=canonical_summary, - recommendation_level='cash_buffer_branch_default', + recommendation_level='tech_pullback_cash_buffer', manifest=manifest, ) @@ -123,7 +123,7 @@ def test_recommendation_logic_defaults_but_not_frozen(): ]), ) - assert recommendation['research_recommendation'] == 'cash_buffer_branch_default' + assert recommendation['research_recommendation'] == 'tech_pullback_cash_buffer' assert recommendation['keep_parallel_branch'] is True assert recommendation['role_vs_qqq_plus_current_default'] == '并行分支' assert 'average_names_held_still_low' in recommendation['frozen_blockers'] @@ -135,7 +135,7 @@ def test_result_export_not_empty(tmp_path): module.write_markdown_report( md_path, canonical_payload={ - 'name': 'cash_buffer_branch_default', + 'name': 'tech_pullback_cash_buffer', 'previous_candidate_name': 'cash_buffer_a__hb10__base__adv50', 'holdings_count': 8, 'single_name_cap': 0.10, @@ -143,7 +143,7 @@ def test_result_export_not_empty(tmp_path): }, comparison_df=pd.DataFrame([ { - 'strategy': 'cash_buffer_branch_default', + 'strategy': 'tech_pullback_cash_buffer', 'role': 'cash-buffered parallel branch', 'oos_cagr': 0.34068, } @@ -152,16 +152,16 @@ def test_result_export_not_empty(tmp_path): {'check': 'geometry_capacity_matches_target', 'passed': True, 'detail': 'ok'} ]), recommendation={ - 'research_recommendation': 'cash_buffer_branch_default', + 'research_recommendation': 'tech_pullback_cash_buffer', 'previous_candidate_name': 'cash_buffer_a__hb10__base__adv50', - 'branch_name': 'cash_buffer_branch_default', + 'branch_name': 'tech_pullback_cash_buffer', 'role_vs_qqq_plus_current_default': '并行分支', 'keep_parallel_branch': True, 'reason': 'test', 'frozen_blockers': ['average_names_held_still_low'], }, manifest={ - 'branch_name': 'cash_buffer_branch_default', + 'branch_name': 'tech_pullback_cash_buffer', 'role': 'cash-buffered parallel branch', 'previous_candidate_name': 'cash_buffer_a__hb10__base__adv50', 'benchmark': 'QQQ', diff --git a/tests/test_rebalance_service.py b/tests/test_rebalance_service.py index 75d1e90..06af56b 100644 --- a/tests/test_rebalance_service.py +++ b/tests/test_rebalance_service.py @@ -76,7 +76,7 @@ def disconnect(self): False, "breadth=41.0%", { - "strategy_profile": "cash_buffer_branch_default", + "strategy_profile": "tech_pullback_cash_buffer", "managed_symbols": ("AAA", "BOXX"), "status_icon": "🧲", "trade_date": "2026-04-01", @@ -120,6 +120,6 @@ def disconnect(self): assert result == "OK - executed" payload = json.loads(output_path.read_text(encoding="utf-8")) - assert payload["strategy_profile"] == "cash_buffer_branch_default" + assert payload["strategy_profile"] == "tech_pullback_cash_buffer" assert payload["snapshot_as_of"] == "2026-03-31" assert payload["orders_submitted"][0]["symbol"] == "AAA" diff --git a/tests/test_runtime_config_support.py b/tests/test_runtime_config_support.py index 5abf14e..f60a116 100644 --- a/tests/test_runtime_config_support.py +++ b/tests/test_runtime_config_support.py @@ -130,14 +130,14 @@ def test_load_platform_runtime_settings_rejects_unknown_strategy_profile(monkeyp def test_platform_supported_profiles_are_filtered_by_registry(): assert get_supported_profiles_for_platform(IBKR_PLATFORM) == frozenset( { - "cash_buffer_branch_default", + "tech_pullback_cash_buffer", "global_etf_rotation", "russell_1000_multi_factor_defensive", } ) -def test_load_platform_runtime_settings_accepts_cash_buffer_alias(monkeypatch): +def test_load_platform_runtime_settings_accepts_tech_pullback_cash_buffer(monkeypatch): monkeypatch.setenv("STRATEGY_PROFILE", "tech_pullback_cash_buffer") monkeypatch.setenv("ACCOUNT_GROUP", "default") monkeypatch.setenv("IB_ACCOUNT_GROUP_CONFIG_JSON", MINIMAL_GROUP_JSON) @@ -145,7 +145,7 @@ def test_load_platform_runtime_settings_accepts_cash_buffer_alias(monkeypatch): settings = load_platform_runtime_settings(project_id_resolver=lambda: "project-1") - assert settings.strategy_profile == "cash_buffer_branch_default" + assert settings.strategy_profile == "tech_pullback_cash_buffer" def test_platform_profile_matrix_marks_default_and_rollback(): @@ -153,7 +153,7 @@ def test_platform_profile_matrix_marks_default_and_rollback(): by_profile = {row["canonical_profile"]: row for row in rows} assert by_profile["global_etf_rotation"]["is_default"] is True assert by_profile["global_etf_rotation"]["is_rollback"] is True - assert by_profile["cash_buffer_branch_default"]["display_name"] == "Tech Pullback Cash Buffer" + assert by_profile["tech_pullback_cash_buffer"]["display_name"] == "Tech Pullback Cash Buffer" @@ -168,8 +168,8 @@ def test_load_platform_runtime_settings_reads_feature_snapshot_path(monkeypatch) assert settings.feature_snapshot_path == "/tmp/r1000-latest.csv" -def test_load_platform_runtime_settings_reads_cash_buffer_runtime_config(monkeypatch): - monkeypatch.setenv("STRATEGY_PROFILE", "cash_buffer_branch_default") +def test_load_platform_runtime_settings_reads_tech_pullback_runtime_config(monkeypatch): + monkeypatch.setenv("STRATEGY_PROFILE", "tech_pullback_cash_buffer") monkeypatch.setenv("ACCOUNT_GROUP", "default") monkeypatch.setenv("IB_ACCOUNT_GROUP_CONFIG_JSON", MINIMAL_GROUP_JSON) monkeypatch.setenv("IBKR_FEATURE_SNAPSHOT_PATH", "/tmp/cash-buffer.csv") @@ -188,8 +188,8 @@ def test_load_platform_runtime_settings_reads_cash_buffer_runtime_config(monkeyp assert settings.dry_run_only is True -def test_load_platform_runtime_settings_uses_bundled_cash_buffer_config_when_env_missing(monkeypatch): - monkeypatch.setenv("STRATEGY_PROFILE", "cash_buffer_branch_default") +def test_load_platform_runtime_settings_uses_bundled_tech_pullback_config_when_env_missing(monkeypatch): + monkeypatch.setenv("STRATEGY_PROFILE", "tech_pullback_cash_buffer") monkeypatch.setenv("ACCOUNT_GROUP", "default") monkeypatch.setenv("IB_ACCOUNT_GROUP_CONFIG_JSON", MINIMAL_GROUP_JSON) monkeypatch.setenv("IBKR_FEATURE_SNAPSHOT_PATH", "/tmp/cash-buffer.csv") @@ -199,7 +199,7 @@ def test_load_platform_runtime_settings_uses_bundled_cash_buffer_config_when_env settings = load_platform_runtime_settings(project_id_resolver=lambda: "project-1") assert settings.strategy_config_path is not None - assert settings.strategy_config_path.endswith("growth_pullback_cash_buffer_branch_default.json") + assert settings.strategy_config_path.endswith("growth_pullback_tech_pullback_cash_buffer.json") assert settings.strategy_config_source == "bundled_canonical_default" @@ -295,3 +295,12 @@ def test_parse_account_group_configs_supports_top_level_mapping(): assert configs["default"].ib_client_id == 4 assert configs["default"].account_ids == ("U1",) assert configs["default"].service_name == "svc" + + +def test_load_platform_runtime_settings_rejects_legacy_cash_buffer_profile(monkeypatch): + monkeypatch.setenv("STRATEGY_PROFILE", "cash_buffer_branch_default") + monkeypatch.setenv("ACCOUNT_GROUP", "default") + monkeypatch.setenv("IB_ACCOUNT_GROUP_CONFIG_JSON", MINIMAL_GROUP_JSON) + + with pytest.raises(ValueError, match="Unsupported STRATEGY_PROFILE"): + load_platform_runtime_settings(project_id_resolver=lambda: "project-1") diff --git a/tests/test_snapshot_strategy_runtime.py b/tests/test_snapshot_strategy_runtime.py index 584a777..9f5f692 100644 --- a/tests/test_snapshot_strategy_runtime.py +++ b/tests/test_snapshot_strategy_runtime.py @@ -22,9 +22,9 @@ def _write_cash_buffer_manifest(snapshot_path: Path, config_path: Path, *, snaps json.dumps( { "manifest_type": "feature_snapshot", - "contract_version": "cash_buffer_branch_default.feature_snapshot.v1", - "strategy_profile": "cash_buffer_branch_default", - "config_name": "cash_buffer_branch_default", + "contract_version": "tech_pullback_cash_buffer.feature_snapshot.v1", + "strategy_profile": "tech_pullback_cash_buffer", + "config_name": "tech_pullback_cash_buffer", "config_path": str(config_path), "config_sha256": _sha256_file(config_path), "snapshot_path": str(snapshot_path), @@ -92,11 +92,11 @@ def fake_load_feature_snapshot_guarded(path, **_kwargs): assert result[4]["snapshot_guard_decision"] == "proceed" -def test_compute_signals_loads_cash_buffer_branch_default_runtime(strategy_module_factory, monkeypatch, tmp_path): +def test_compute_signals_loads_tech_pullback_cash_buffer_runtime(strategy_module_factory, monkeypatch, tmp_path): pytest.importorskip("pandas") snapshot_path = tmp_path / "cash_buffer_snapshot.csv" - config_path = tmp_path / "cash_buffer_branch_default.json" + config_path = tmp_path / "tech_pullback_cash_buffer.json" snapshot_path.write_text( "\n".join( [ @@ -120,7 +120,7 @@ def test_compute_signals_loads_cash_buffer_branch_default_runtime(strategy_modul config_path.write_text( json.dumps( { - "name": "cash_buffer_branch_default", + "name": "tech_pullback_cash_buffer", "family": "tech_heavy_pullback", "branch_role": "cash-buffered parallel branch", "benchmark_symbol": "QQQ", @@ -143,7 +143,7 @@ def test_compute_signals_loads_cash_buffer_branch_default_runtime(strategy_modul _write_cash_buffer_manifest(snapshot_path, config_path, snapshot_as_of="2026-03-31") module = strategy_module_factory( - STRATEGY_PROFILE="cash_buffer_branch_default", + STRATEGY_PROFILE="tech_pullback_cash_buffer", IBKR_FEATURE_SNAPSHOT_PATH=str(snapshot_path), IBKR_FEATURE_SNAPSHOT_MANIFEST_PATH=str(Path(f"{snapshot_path}.manifest.json")), IBKR_STRATEGY_CONFIG_PATH=str(config_path), @@ -152,7 +152,7 @@ def test_compute_signals_loads_cash_buffer_branch_default_runtime(strategy_modul result = module.compute_signals(None, {"AAPL"}) assert result[0]["BOXX"] == pytest.approx(0.2) - assert result[4]["strategy_profile"] == "cash_buffer_branch_default" + assert result[4]["strategy_profile"] == "tech_pullback_cash_buffer" assert result[4]["strategy_config_source"] in {"env", "external_config"} assert result[4]["realized_stock_weight"] == pytest.approx(0.8) assert result[4]["snapshot_guard_decision"] == "proceed" @@ -161,7 +161,7 @@ def test_compute_signals_loads_cash_buffer_branch_default_runtime(strategy_modul def test_compute_signals_fail_closes_when_snapshot_missing(strategy_module_factory): module = strategy_module_factory( - STRATEGY_PROFILE="cash_buffer_branch_default", + STRATEGY_PROFILE="tech_pullback_cash_buffer", IBKR_FEATURE_SNAPSHOT_PATH="/tmp/definitely-missing-cash-buffer-snapshot.csv", IBKR_RUN_AS_OF_DATE="2026-04-01", ) @@ -188,7 +188,7 @@ def test_compute_signals_fail_closes_when_snapshot_is_stale(strategy_module_fact ) module = strategy_module_factory( - STRATEGY_PROFILE="cash_buffer_branch_default", + STRATEGY_PROFILE="tech_pullback_cash_buffer", IBKR_FEATURE_SNAPSHOT_PATH=str(snapshot_path), IBKR_RUN_AS_OF_DATE="2026-04-05", ) @@ -202,7 +202,7 @@ def test_compute_signals_fail_closes_when_snapshot_is_stale(strategy_module_fact def test_compute_signals_fail_closes_when_manifest_missing(strategy_module_factory, tmp_path): snapshot_path = tmp_path / "snapshot.csv" - config_path = tmp_path / "cash_buffer_branch_default.json" + config_path = tmp_path / "tech_pullback_cash_buffer.json" snapshot_path.write_text( "\n".join( [ @@ -217,7 +217,7 @@ def test_compute_signals_fail_closes_when_manifest_missing(strategy_module_facto config_path.write_text( json.dumps( { - "name": "cash_buffer_branch_default", + "name": "tech_pullback_cash_buffer", "family": "tech_heavy_pullback", "branch_role": "cash-buffered parallel branch", "benchmark_symbol": "QQQ", @@ -239,7 +239,7 @@ def test_compute_signals_fail_closes_when_manifest_missing(strategy_module_facto ) module = strategy_module_factory( - STRATEGY_PROFILE="cash_buffer_branch_default", + STRATEGY_PROFILE="tech_pullback_cash_buffer", IBKR_FEATURE_SNAPSHOT_PATH=str(snapshot_path), IBKR_STRATEGY_CONFIG_PATH=str(config_path), IBKR_RUN_AS_OF_DATE="2026-04-01", diff --git a/tests/test_strategy_loader.py b/tests/test_strategy_loader.py index 5213392..d907803 100644 --- a/tests/test_strategy_loader.py +++ b/tests/test_strategy_loader.py @@ -1,6 +1,8 @@ import sys import types +import pytest + from strategy_loader import load_signal_logic_module @@ -28,7 +30,7 @@ def test_load_signal_logic_module_resolves_russell_1000_multi_factor_defensive() assert module.SIGNAL_SOURCE == "feature_snapshot" -def test_load_signal_logic_module_resolves_cash_buffer_branch_default(monkeypatch): +def test_load_signal_logic_module_resolves_tech_pullback_cash_buffer(monkeypatch): try: import pandas # noqa: F401 except ModuleNotFoundError: @@ -37,15 +39,15 @@ def test_load_signal_logic_module_resolves_cash_buffer_branch_default(monkeypatc market_calendars_module = types.ModuleType("pandas_market_calendars") market_calendars_module.get_calendar = lambda name: None monkeypatch.setitem(sys.modules, "pandas_market_calendars", market_calendars_module) - sys.modules.pop("us_equity_strategies.strategies.cash_buffer_branch_default", None) + sys.modules.pop("us_equity_strategies.strategies.tech_pullback_cash_buffer", None) - module = load_signal_logic_module("cash_buffer_branch_default") + module = load_signal_logic_module("tech_pullback_cash_buffer") - assert module.__name__ == "us_equity_strategies.strategies.cash_buffer_branch_default" + assert module.__name__ == "us_equity_strategies.strategies.tech_pullback_cash_buffer" assert module.SIGNAL_SOURCE == "feature_snapshot" -def test_load_signal_logic_module_resolves_cash_buffer_alias(monkeypatch): +def test_load_signal_logic_module_rejects_legacy_cash_buffer_profile(monkeypatch): try: import pandas # noqa: F401 except ModuleNotFoundError: @@ -54,9 +56,7 @@ def test_load_signal_logic_module_resolves_cash_buffer_alias(monkeypatch): market_calendars_module = types.ModuleType("pandas_market_calendars") market_calendars_module.get_calendar = lambda name: None monkeypatch.setitem(sys.modules, "pandas_market_calendars", market_calendars_module) - sys.modules.pop("us_equity_strategies.strategies.cash_buffer_branch_default", None) + sys.modules.pop("us_equity_strategies.strategies.tech_pullback_cash_buffer", None) - module = load_signal_logic_module("tech_pullback_cash_buffer") - - assert module.__name__ == "us_equity_strategies.strategies.cash_buffer_branch_default" - assert module.SIGNAL_SOURCE == "feature_snapshot" + with pytest.raises(ValueError, match="Unsupported STRATEGY_PROFILE"): + load_signal_logic_module("cash_buffer_branch_default") diff --git a/tests/test_cash_buffer_branch_daily_overlay.py b/tests/test_tech_pullback_cash_buffer_daily_overlay.py similarity index 96% rename from tests/test_cash_buffer_branch_daily_overlay.py rename to tests/test_tech_pullback_cash_buffer_daily_overlay.py index c6e0b29..4681b2b 100644 --- a/tests/test_cash_buffer_branch_daily_overlay.py +++ b/tests/test_tech_pullback_cash_buffer_daily_overlay.py @@ -6,7 +6,7 @@ def load_module(): - path = Path(__file__).resolve().parents[1] / 'research' / 'backtest_cash_buffer_branch_daily_overlay.py' + path = Path(__file__).resolve().parents[1] / 'research' / 'backtest_tech_pullback_cash_buffer_daily_overlay.py' spec = importlib.util.spec_from_file_location('backtest_cash_buffer_branch_daily_overlay_test', path) module = importlib.util.module_from_spec(spec) sys.modules[spec.name] = module @@ -71,7 +71,7 @@ def test_result_export_not_empty(tmp_path): module = load_module() summary_path = tmp_path / 'summary.md' baseline_payload = { - 'name': 'cash_buffer_branch_default', + 'name': 'tech_pullback_cash_buffer', 'family': 'tech_heavy_pullback', 'exposures': {'risk_on': 0.8, 'soft_defense': 0.6, 'hard_defense': 0.0}, 'holdings_count': 8, @@ -81,7 +81,7 @@ def test_result_export_not_empty(tmp_path): 'benchmark_symbol': 'QQQ', } baseline_row = pd.Series({ - 'strategy': 'cash_buffer_branch_default_monthly_baseline', + 'strategy': 'tech_pullback_cash_buffer_monthly_baseline', 'CAGR': 0.25, 'Max Drawdown': -0.2, 'Information Ratio vs QQQ': 0.8, From bd454567601aacff4b8106ac9b36a8e9a5ae8485 Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Mon, 6 Apr 2026 07:13:31 +0800 Subject: [PATCH 2/3] update us equity strategies pin for rename --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 73a3f2d..664aae5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ flask gunicorn quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@6e8cc058b821aea8a54015d4b39e02fbdd3dc198 -us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@8b0c59f2ecd7c74cc2d350f1870f124a2560b205 +us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@53f996edec6af4a5eca592513ba835903ec7bf8d pandas numpy requests From 5fe54ae9ef5d72407abd1ea9d1bb3eb9fae52cb4 Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Mon, 6 Apr 2026 07:14:31 +0800 Subject: [PATCH 3/3] fix us equity strategies pin hash --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 664aae5..31c9f44 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ flask gunicorn quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@6e8cc058b821aea8a54015d4b39e02fbdd3dc198 -us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@53f996edec6af4a5eca592513ba835903ec7bf8d +us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@53f996ef489700f325a5b750355d644f4aed7270 pandas numpy requests