Skip to content

Commit 1d8254a

Browse files
Pigbibicursoragent
andcommitted
Add optional aggressive industry ETF QMT target and pin cn-equity-strategies.
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 8e2aec9 commit 1d8254a

5 files changed

Lines changed: 92 additions & 2 deletions

File tree

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Current scope is **dry-run first**: evaluate strategy targets and preview orders
99
| Profile | Input mode |
1010
|---|---|
1111
| `cn_industry_etf_rotation` | `market_history` (**主轨,runtime_enabled**) |
12+
| `cn_industry_etf_rotation_aggressive` | `market_history` (**optional target,vol25%**) |
1213
| `cn_dividend_quality_snapshot` | `feature_snapshot` (requires snapshot path) |
1314
| `cn_index_etf_tactical_rotation` | `market_history` (legacy / research_backtest_only) |
1415

@@ -29,6 +30,19 @@ curl http://127.0.0.1:8080/probe
2930
curl http://127.0.0.1:8080/dry-run
3031
```
3132

33+
### Industry ETF rotation aggressive (optional second target, vol25%)
34+
35+
```bash
36+
export STRATEGY_PROFILE=cn_industry_etf_rotation_aggressive
37+
export QMT_DRY_RUN_ONLY=true
38+
export QMT_MARKET_HISTORY_PATH=data/fixtures/market_history.sample.csv
39+
40+
python3 main.py
41+
curl http://127.0.0.1:8080/dry-run
42+
```
43+
44+
Runtime target example: `QuantRuntimeSettings/examples/targets/qmt/industry_etf_aggressive_dry_run.example.json`
45+
3246
### Legacy index ETF tactical rotation (research only)
3347

3448
```bash
@@ -59,6 +73,7 @@ End-to-end smoke (stage/build/run):
5973
```bash
6074
python3 scripts/smoke_cn_dividend_quality_dry_run_e2e.py
6175
python3 scripts/smoke_cn_industry_etf_rotation_dry_run_e2e.py
76+
python3 scripts/smoke_cn_industry_etf_rotation_aggressive_dry_run_e2e.py
6277
```
6378

6479
## Environment variables

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ dependencies = [
1414
"flask>=3.0",
1515
"pandas>=2.0",
1616
"quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@d18fe32b57a0a8bb746bebf6f11465dd68107eae",
17-
"cn-equity-strategies @ git+https://github.com/QuantStrategyLab/CnEquityStrategies.git@32956606a4925d018873e979866559168493d01b",
17+
"cn-equity-strategies @ git+https://github.com/QuantStrategyLab/CnEquityStrategies.git@de6c76033e9d8a83fd6a8a31a51ce98690bb4262",
1818
]
1919

2020
[project.optional-dependencies]
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#!/usr/bin/env python3
2+
"""End-to-end smoke: QMT dry-run for cn_industry_etf_rotation_aggressive (vol25%)."""
3+
4+
from __future__ import annotations
5+
6+
import argparse
7+
import json
8+
import os
9+
import sys
10+
from pathlib import Path
11+
12+
ROOT = Path(__file__).resolve().parents[1]
13+
FIXTURE = ROOT / "data" / "fixtures" / "market_history.sample.csv"
14+
15+
16+
def main(argv: list[str] | None = None) -> int:
17+
parser = argparse.ArgumentParser()
18+
parser.add_argument("--market-history", default=str(FIXTURE))
19+
args = parser.parse_args(argv)
20+
21+
if not Path(args.market_history).exists():
22+
raise SystemExit(f"missing market history fixture: {args.market_history}")
23+
24+
os.environ["STRATEGY_PROFILE"] = "cn_industry_etf_rotation_aggressive"
25+
os.environ["QMT_DRY_RUN_ONLY"] = "true"
26+
os.environ["QMT_MARKET_HISTORY_PATH"] = str(args.market_history)
27+
28+
if str(ROOT) not in sys.path:
29+
sys.path.insert(0, str(ROOT))
30+
from application.dry_run_service import run_dry_run_cycle
31+
from runtime_config_support import load_platform_runtime_settings
32+
33+
report = run_dry_run_cycle(runtime_settings=load_platform_runtime_settings())
34+
print(json.dumps(report, ensure_ascii=False, indent=2, default=str))
35+
if report.get("status") != "ok":
36+
return 1
37+
if not report.get("target_weights"):
38+
return 2
39+
return 0
40+
41+
42+
if __name__ == "__main__":
43+
raise SystemExit(main())

strategy_registry.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from cn_equity_strategies import (
1717
CN_EQUITY_DOMAIN,
1818
get_platform_runtime_adapter,
19+
get_qmt_rollout_allowlist,
1920
get_runtime_enabled_profiles,
2021
get_strategy_catalog,
2122
)
@@ -27,7 +28,7 @@
2728
}
2829

2930
STRATEGY_CATALOG = get_strategy_catalog()
30-
QMT_ROLLOUT_ALLOWLIST = get_runtime_enabled_profiles()
31+
QMT_ROLLOUT_ALLOWLIST = get_qmt_rollout_allowlist()
3132
PLATFORM_CAPABILITY_MATRIX = PlatformCapabilityMatrix(
3233
platform_id=QMT_PLATFORM,
3334
supported_domains=PLATFORM_SUPPORTED_DOMAINS[QMT_PLATFORM],

tests/test_dry_run_cycle.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from cn_equity_strategies.catalog import (
66
CN_DIVIDEND_QUALITY_SNAPSHOT_PROFILE,
7+
CN_INDUSTRY_ETF_ROTATION_AGGRESSIVE_PROFILE,
78
CN_INDUSTRY_ETF_ROTATION_PROFILE,
89
)
910
from runtime_config_support import PlatformRuntimeSettings, load_platform_runtime_settings
@@ -13,6 +14,7 @@
1314
def test_qmt_enabled_profiles_include_primary_runtime_strategies():
1415
profiles = get_enabled_profiles_for_platform(QMT_PLATFORM, policy=PLATFORM_POLICY)
1516
assert CN_INDUSTRY_ETF_ROTATION_PROFILE in profiles
17+
assert CN_INDUSTRY_ETF_ROTATION_AGGRESSIVE_PROFILE in profiles
1618
assert CN_DIVIDEND_QUALITY_SNAPSHOT_PROFILE in profiles
1719

1820

@@ -54,3 +56,32 @@ def test_dry_run_cycle_returns_target_weights(monkeypatch: pytest.MonkeyPatch, m
5456
assert report["target_weights"]
5557
assert report["order_previews"]
5658
assert report["dry_run_only"] is True
59+
60+
61+
def test_dry_run_cycle_supports_aggressive_industry_profile(
62+
monkeypatch: pytest.MonkeyPatch,
63+
market_history_csv: str,
64+
):
65+
monkeypatch.setenv("STRATEGY_PROFILE", CN_INDUSTRY_ETF_ROTATION_AGGRESSIVE_PROFILE)
66+
monkeypatch.setenv("QMT_DRY_RUN_ONLY", "true")
67+
monkeypatch.setenv("QMT_MARKET_HISTORY_PATH", market_history_csv)
68+
69+
from application.dry_run_service import run_dry_run_cycle
70+
from application.qmt_client import QmtBrokerClient
71+
72+
settings = PlatformRuntimeSettings(
73+
strategy_profile=CN_INDUSTRY_ETF_ROTATION_AGGRESSIVE_PROFILE,
74+
strategy_display_name="CN Industry ETF Rotation Aggressive",
75+
strategy_domain="cn_equity",
76+
dry_run_only=True,
77+
market_history_path=market_history_csv,
78+
feature_snapshot_path=None,
79+
feature_snapshot_manifest_path=None,
80+
)
81+
report = run_dry_run_cycle(
82+
runtime_settings=settings,
83+
client=QmtBrokerClient(market_history_path=market_history_csv),
84+
)
85+
assert report["status"] == "ok"
86+
assert report["strategy_profile"] == CN_INDUSTRY_ETF_ROTATION_AGGRESSIVE_PROFILE
87+
assert report["target_weights"]

0 commit comments

Comments
 (0)