diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 23d131c..1c4d47b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: - name: Check QPK pin consistency run: | set -euo pipefail - QPK_EXPECTED_PIN=a90937621a7a05c9f72d0ca8a29be3fcb18a327c uv run --no-sync python scripts/check_qpk_pin_consistency.py + QPK_EXPECTED_PIN=0f2fde65023906f9f813fd1149a8dd6fbadbdd7f uv run --no-sync python scripts/check_qpk_pin_consistency.py - name: Ensure uv.lock matches pyproject.toml run: uv lock --check diff --git a/pyproject.toml b/pyproject.toml index ef46b7c..cd75f59 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ dependencies = [ "google-cloud-secret-manager", "google-cloud-storage", "yfinance", - "quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@a90937621a7a05c9f72d0ca8a29be3fcb18a327c", + "quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@0f2fde65023906f9f813fd1149a8dd6fbadbdd7f", "us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@4a3943883cd6b5bbfe32a559e56a91b40a81b7ce", "hk-equity-strategies @ git+https://github.com/QuantStrategyLab/HkEquityStrategies.git@709e5e1cde7841aed538d94eb26b552b46cb7806", ] @@ -64,5 +64,5 @@ include = [ [tool.uv] override-dependencies = [ - "quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@a90937621a7a05c9f72d0ca8a29be3fcb18a327c", + "quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@0f2fde65023906f9f813fd1149a8dd6fbadbdd7f", ] diff --git a/qsl.toml b/qsl.toml index 2bcb6fe..df369c6 100644 --- a/qsl.toml +++ b/qsl.toml @@ -5,7 +5,7 @@ upgrade_ring = "ring_d" allow_legacy = false [qsl.requires] -quant_platform_kit = "a90937621a7a05c9f72d0ca8a29be3fcb18a327c" +quant_platform_kit = "0f2fde65023906f9f813fd1149a8dd6fbadbdd7f" us_equity_strategies = "4a3943883cd6b5bbfe32a559e56a91b40a81b7ce" hk_equity_strategies = "709e5e1cde7841aed538d94eb26b552b46cb7806" diff --git a/strategy_runtime.py b/strategy_runtime.py index ac2d43b..f7e1f4b 100644 --- a/strategy_runtime.py +++ b/strategy_runtime.py @@ -70,6 +70,45 @@ def _parse_small_account_hold_policy(raw: Any) -> SmallAccountRiskHoldPolicy | N return None +def _verified_nav_from_capabilities(capabilities: Mapping[str, Any]) -> float | None: + capital_base = capabilities.get("capital_base") + if capital_base is None: + return None + try: + nav = float(getattr(capital_base, "target_equity")) + except (TypeError, ValueError, AttributeError): + return None + if nav <= 0.0: + return None + return nav + + +def _portfolio_weight_map_from_snapshot( + portfolio_snapshot: Any, + *, + verified_nav: float | None, +) -> dict[str, float] | None: + if portfolio_snapshot is None or verified_nav is None or verified_nav <= 0.0: + return None + positions = getattr(portfolio_snapshot, "positions", None) + if positions is None: + return None + weights: dict[str, float] = {} + try: + for position in positions: + symbol = str(getattr(position, "symbol", "") or "").strip().upper() + try: + market_value = float(getattr(position, "market_value")) + except (TypeError, ValueError, AttributeError): + continue + if not symbol or market_value <= 0.0: + continue + weights[symbol] = weights.get(symbol, 0.0) + market_value / verified_nav + except TypeError: + return None + return weights or None + + def _installed_ues_revision() -> str | None: """Read the VCS revision of the installed UES distribution.""" candidates: list[str] = [] @@ -534,6 +573,13 @@ def _build_runtime_risk_capabilities( "runtime_risk_limits": object(), }, "unavailable:small_account_hold_cash_only" capability_payload["small_account_hold_policy"] = hold_policy + # Always expose book weights for default/opt-in hold non-worsening checks. + current_weights = _portfolio_weight_map_from_snapshot( + snapshot, + verified_nav=_verified_nav_from_capabilities(capabilities), + ) + if current_weights is not None: + capability_payload["current_portfolio_weights"] = current_weights return capability_payload, "verified:runtime_risk_limits" def _build_context_capabilities( diff --git a/uv.lock b/uv.lock index d2993bf..255bf8a 100644 --- a/uv.lock +++ b/uv.lock @@ -17,7 +17,7 @@ resolution-markers = [ ] [manifest] -overrides = [{ name = "quant-platform-kit", git = "https://github.com/QuantStrategyLab/QuantPlatformKit.git?rev=a90937621a7a05c9f72d0ca8a29be3fcb18a327c" }] +overrides = [{ name = "quant-platform-kit", git = "https://github.com/QuantStrategyLab/QuantPlatformKit.git?rev=0f2fde65023906f9f813fd1149a8dd6fbadbdd7f" }] [[package]] name = "beautifulsoup4" @@ -791,7 +791,7 @@ requires-dist = [ { name = "pytest", marker = "extra == 'test'" }, { name = "pytest-cov", marker = "extra == 'test'" }, { name = "pytz" }, - { name = "quant-platform-kit", git = "https://github.com/QuantStrategyLab/QuantPlatformKit.git?rev=a90937621a7a05c9f72d0ca8a29be3fcb18a327c" }, + { name = "quant-platform-kit", git = "https://github.com/QuantStrategyLab/QuantPlatformKit.git?rev=0f2fde65023906f9f813fd1149a8dd6fbadbdd7f" }, { name = "requests" }, { name = "ruff", marker = "extra == 'test'" }, { name = "us-equity-strategies", git = "https://github.com/QuantStrategyLab/UsEquityStrategies.git?rev=4a3943883cd6b5bbfe32a559e56a91b40a81b7ce" }, @@ -1327,7 +1327,7 @@ wheels = [ [[package]] name = "quant-platform-kit" version = "1.0.0" -source = { git = "https://github.com/QuantStrategyLab/QuantPlatformKit.git?rev=a90937621a7a05c9f72d0ca8a29be3fcb18a327c#a90937621a7a05c9f72d0ca8a29be3fcb18a327c" } +source = { git = "https://github.com/QuantStrategyLab/QuantPlatformKit.git?rev=0f2fde65023906f9f813fd1149a8dd6fbadbdd7f#0f2fde65023906f9f813fd1149a8dd6fbadbdd7f" } [[package]] name = "requests"