diff --git a/pyproject.toml b/pyproject.toml index 3271f75..982bf7e 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@37c81901160c5b31127a27dba1c63944933fb6bf", + "quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@0c69df08144872ccd1d8bf523738e80748d8d664", ] [project.optional-dependencies] diff --git a/src/hk_equity_strategies/entrypoints/__init__.py b/src/hk_equity_strategies/entrypoints/__init__.py index 43167ea..8aab40b 100644 --- a/src/hk_equity_strategies/entrypoints/__init__.py +++ b/src/hk_equity_strategies/entrypoints/__init__.py @@ -9,7 +9,14 @@ from hk_equity_strategies.strategies import hk_global_etf_tactical_rotation as global_etf_strategy from hk_equity_strategies.strategies import hk_low_vol_dividend_quality_snapshot as low_vol_dividend_strategy -from ._common import apply_risk_gate, get_current_holdings, merge_runtime_config, require_market_data, weights_to_positions +from ._common import ( + apply_risk_gate, + get_current_holdings, + merge_runtime_config, + record_strategy_decision, + require_market_data, + weights_to_positions, +) def evaluate_hk_global_etf_tactical_rotation(ctx: StrategyContext) -> StrategyDecision: @@ -36,7 +43,14 @@ def evaluate_hk_global_etf_tactical_rotation(ctx: StrategyContext) -> StrategyDe risk_flags=risk_flags, diagnostics=diagnostics, ) - return apply_risk_gate(decision) + decision = apply_risk_gate(decision) + record_strategy_decision( + ctx, + decision, + profile_id=hk_global_etf_tactical_rotation_manifest.profile, + domain=hk_global_etf_tactical_rotation_manifest.domain, + ) + return decision hk_global_etf_tactical_rotation_entrypoint = CallableStrategyEntrypoint( @@ -69,7 +83,14 @@ def evaluate_hk_low_vol_dividend_quality_snapshot(ctx: StrategyContext) -> Strat risk_flags=risk_flags, diagnostics=diagnostics, ) - return apply_risk_gate(decision) + decision = apply_risk_gate(decision) + record_strategy_decision( + ctx, + decision, + profile_id=hk_low_vol_dividend_quality_snapshot_manifest.profile, + domain=hk_low_vol_dividend_quality_snapshot_manifest.domain, + ) + return decision hk_low_vol_dividend_quality_snapshot_entrypoint = CallableStrategyEntrypoint( @@ -85,7 +106,14 @@ def evaluate_hk_low_vol_dividend_quality_snapshot(ctx: StrategyContext) -> Strat def evaluate_hk_equity_combo(ctx: StrategyContext) -> StrategyDecision: from hk_equity_strategies.combo_entrypoints import evaluate_hk_equity_combo as _eval - return apply_risk_gate(_eval(ctx)) + decision = apply_risk_gate(_eval(ctx)) + record_strategy_decision( + ctx, + decision, + profile_id=hk_equity_combo_manifest.profile, + domain=hk_equity_combo_manifest.domain, + ) + return decision from hk_equity_strategies.combo_manifests import hk_equity_combo_manifest # noqa: E402 — intentional late import diff --git a/src/hk_equity_strategies/entrypoints/_common.py b/src/hk_equity_strategies/entrypoints/_common.py index 47b84e4..a8e20b2 100644 --- a/src/hk_equity_strategies/entrypoints/_common.py +++ b/src/hk_equity_strategies/entrypoints/_common.py @@ -4,6 +4,7 @@ from typing import Any from quant_platform_kit.strategy_contracts import PositionTarget, StrategyContext, StrategyDecision +from quant_platform_kit.strategy_lifecycle.performance_monitor import PerformanceMonitor logger = logging.getLogger(__name__) @@ -11,6 +12,30 @@ # 风控硬门 — 每个 entrypoint 返回 StrategyDecision 前必须调用 # --------------------------------------------------------------------------- +_performance_monitor: PerformanceMonitor | None = None + + +def record_strategy_decision( + ctx: StrategyContext, + decision: StrategyDecision, + *, + profile_id: str, + domain: str, +) -> None: + """Record per-run decision for live monitoring (roadmap 5a).""" + global _performance_monitor + try: + if _performance_monitor is None: + _performance_monitor = PerformanceMonitor() + _performance_monitor.record( + profile_id, + decision, + execution_result={}, + domain=domain, + ) + except Exception as exc: # pragma: no cover + logger.warning("PerformanceMonitor.record failed: %s", exc) + def apply_risk_gate( decision: StrategyDecision,