diff --git a/.github/workflows/drift-check.yml b/.github/workflows/drift-check.yml index c88048e..29c1924 100644 --- a/.github/workflows/drift-check.yml +++ b/.github/workflows/drift-check.yml @@ -26,7 +26,7 @@ jobs: uses: actions/checkout@v6 with: repository: QuantStrategyLab/QuantPlatformKit - ref: 5488048cc0fc8e818b8c4be7a9729f6f1b5fdfdd + ref: 4664c2b283e9409a7dae4febf8571260c9f72724 path: external/QuantPlatformKit - name: Set up Python @@ -194,7 +194,7 @@ jobs: contents: read issues: write id-token: write - uses: QuantStrategyLab/QuantPlatformKit/.github/workflows/reusable-drift-check.yml@5488048cc0fc8e818b8c4be7a9729f6f1b5fdfdd + uses: QuantStrategyLab/QuantPlatformKit/.github/workflows/reusable-drift-check.yml@4664c2b283e9409a7dae4febf8571260c9f72724 with: strategy_domain: cn_equity caller_event_name: ${{ github.event_name }} @@ -203,7 +203,7 @@ jobs: snapshot_checkout_path: external/CnEquitySnapshotPipelines snapshot_repository_ref: ${{ needs.preflight_backtests.outputs.snapshot_repository_ref }} ai_gateway_service_url: ${{ vars.AI_GATEWAY_SERVICE_URL }} - quant_platform_kit_ref: 5488048cc0fc8e818b8c4be7a9729f6f1b5fdfdd + quant_platform_kit_ref: 4664c2b283e9409a7dae4febf8571260c9f72724 lifecycle_preflight_artifact: lifecycle-preflight-${{ github.run_id }}-${{ github.run_attempt }} secrets: codex_audit_service_url: ${{ secrets.CODEX_AUDIT_SERVICE_URL }} diff --git a/pyproject.toml b/pyproject.toml index 431a686..facf5b8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ readme = "README.md" requires-python = ">=3.11" dependencies = [ "pandas>=2.0", - "quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@5488048cc0fc8e818b8c4be7a9729f6f1b5fdfdd", + "quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@4664c2b283e9409a7dae4febf8571260c9f72724", ] [project.optional-dependencies] diff --git a/qsl.toml b/qsl.toml index 7987062..6eca026 100644 --- a/qsl.toml +++ b/qsl.toml @@ -5,5 +5,5 @@ upgrade_ring = "ring_b" bundle = "2026.09.1" requires = [ "pandas>=2.0", - "quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@5488048cc0fc8e818b8c4be7a9729f6f1b5fdfdd", + "quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@4664c2b283e9409a7dae4febf8571260c9f72724", ] diff --git a/tests/test_drift_workflow_config.py b/tests/test_drift_workflow_config.py index da47c26..97c49ea 100644 --- a/tests/test_drift_workflow_config.py +++ b/tests/test_drift_workflow_config.py @@ -18,8 +18,8 @@ def test_drift_workflow_wires_real_snapshot_history_and_preflight_bundle() -> No assert '"conclusion": "success"' in workflow assert "cn_etf_market_history.csv" in workflow assert "repository: QuantStrategyLab/QuantPlatformKit" in workflow - assert "ref: 5488048cc0fc8e818b8c4be7a9729f6f1b5fdfdd" in workflow - assert "quant_platform_kit_ref: 5488048cc0fc8e818b8c4be7a9729f6f1b5fdfdd" in workflow + assert "ref: 4664c2b283e9409a7dae4febf8571260c9f72724" in workflow + assert "quant_platform_kit_ref: 4664c2b283e9409a7dae4febf8571260c9f72724" in workflow assert "python -m pip install --no-deps -e external/QuantPlatformKit" in workflow assert "scripts/run_walk_forward_backtest.py" in workflow assert '"--list-profiles"' in workflow @@ -30,7 +30,7 @@ def test_drift_workflow_wires_real_snapshot_history_and_preflight_bundle() -> No assert "Upload lifecycle preflight artifact" in workflow assert "actions/upload-artifact@v7" in workflow assert workflow.count("github.ref == format('refs/heads/{0}', github.event.repository.default_branch)") == 2 - assert "uses: QuantStrategyLab/QuantPlatformKit/.github/workflows/reusable-drift-check.yml@5488048cc0fc8e818b8c4be7a9729f6f1b5fdfdd" in workflow + assert "uses: QuantStrategyLab/QuantPlatformKit/.github/workflows/reusable-drift-check.yml@4664c2b283e9409a7dae4febf8571260c9f72724" in workflow assert "strategy_domain: cn_equity" in workflow assert "caller_event_name: ${{ github.event_name }}" in workflow assert "caller_pr_head_repository: ${{ github.event.pull_request.head.repo.full_name || '' }}" in workflow diff --git a/tests/test_proxy_backtest.py b/tests/test_proxy_backtest.py index c78828c..5404abd 100644 --- a/tests/test_proxy_backtest.py +++ b/tests/test_proxy_backtest.py @@ -173,6 +173,47 @@ def test_proxy_does_not_require_quotes_on_calendar_holidays(): assert result.final_holdings["510300"] > 0 +def test_proxy_does_not_insert_2023_market_holiday_gaps(): + dates = pd.bdate_range("2023-05-30", "2023-06-26") + holidays = {pd.Timestamp("2023-06-22"), pd.Timestamp("2023-06-23")} + rows = [ + {"date": day, "symbol": "159949", "close": 10.0} + for day in dates + if day not in holidays + ] + result = run_proxy_backtest( + pd.DataFrame(rows), + lambda history: ({"159949": 1.0}, {}), + universe_symbols=("159949",), + config=ProxyBacktestConfig( + min_history_days=1, + rebalance_frequency="monthly", + commission_rate=0, + min_commission=0, + cash_reserve_ratio=0, + ), + ) + assert list(result.equity_curve.index) == [day for day in dates if day not in holidays] + assert result.final_holdings["159949"] > 0 + + +def test_proxy_rejects_missing_price_on_2023_trading_day_while_holding(): + dates = pd.bdate_range("2023-05-30", "2023-06-26") + holidays = {pd.Timestamp("2023-06-22"), pd.Timestamp("2023-06-23")} + rows = [ + {"date": day, "symbol": "159949", "close": float("nan") if day == pd.Timestamp("2023-06-26") else 10.0} + for day in dates + if day not in holidays + ] + with pytest.raises(ValueError, match="finite positive price"): + run_proxy_backtest( + pd.DataFrame(rows), + lambda history: ({"159949": 1.0}, {}), + universe_symbols=("159949",), + config=ProxyBacktestConfig(min_history_days=1, rebalance_frequency="monthly"), + ) + + def test_proxy_all_cash_tolerates_missing_trading_day_without_inventing_holdings(): rows = [{"date": day, "symbol": "510300", "close": 10.0} for day in pd.bdate_range("2024-01-02", periods=5) diff --git a/tests/test_qsl_compat_metadata.py b/tests/test_qsl_compat_metadata.py index 79aad33..7a95491 100644 --- a/tests/test_qsl_compat_metadata.py +++ b/tests/test_qsl_compat_metadata.py @@ -3,7 +3,7 @@ ROOT = Path(__file__).resolve().parents[1] -QPK_REVISION = "5488048cc0fc8e818b8c4be7a9729f6f1b5fdfdd" +QPK_REVISION = "4664c2b283e9409a7dae4febf8571260c9f72724" QPK_URL = ( "quant-platform-kit @ git+https://github.com/QuantStrategyLab/" f"QuantPlatformKit.git@{QPK_REVISION}" diff --git a/uv.lock b/uv.lock index ab8d382..4aa1753 100644 --- a/uv.lock +++ b/uv.lock @@ -40,7 +40,7 @@ requires-dist = [ { name = "ai-gateway-client", marker = "extra == 'research'", git = "https://github.com/QuantStrategyLab/AIAuditBridge.git?rev=60bd64a2ae059a082614181eeb845b46df395523" }, { name = "pandas", specifier = ">=2.0" }, { name = "pytest", marker = "extra == 'test'", specifier = ">=8" }, - { name = "quant-platform-kit", git = "https://github.com/QuantStrategyLab/QuantPlatformKit.git?rev=5488048cc0fc8e818b8c4be7a9729f6f1b5fdfdd" }, + { name = "quant-platform-kit", git = "https://github.com/QuantStrategyLab/QuantPlatformKit.git?rev=4664c2b283e9409a7dae4febf8571260c9f72724" }, ] provides-extras = ["test", "research"] @@ -324,7 +324,7 @@ wheels = [ [[package]] name = "quant-platform-kit" version = "1.0.0" -source = { git = "https://github.com/QuantStrategyLab/QuantPlatformKit.git?rev=5488048cc0fc8e818b8c4be7a9729f6f1b5fdfdd#5488048cc0fc8e818b8c4be7a9729f6f1b5fdfdd" } +source = { git = "https://github.com/QuantStrategyLab/QuantPlatformKit.git?rev=4664c2b283e9409a7dae4febf8571260c9f72724#4664c2b283e9409a7dae4febf8571260c9f72724" } [[package]] name = "six"