Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
90 changes: 80 additions & 10 deletions src/cn_equity_strategies/entrypoints/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,14 @@
from cn_equity_strategies.strategies import cn_industry_etf_rotation_aggressive as industry_etf_aggressive_strategy
from cn_equity_strategies.strategies import cn_star_growth_momentum_quality as star_growth_momentum_quality_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_cn_industry_etf_rotation(ctx: StrategyContext) -> StrategyDecision:
Expand Down Expand Up @@ -51,7 +58,14 @@ def evaluate_cn_industry_etf_rotation(ctx: StrategyContext) -> StrategyDecision:
risk_flags=risk_flags,
diagnostics=diagnostics,
)
return apply_risk_gate(decision)
decision = apply_risk_gate(decision)
record_strategy_decision(
ctx,
decision,
profile_id=cn_industry_etf_rotation_manifest.profile,
domain=cn_industry_etf_rotation_manifest.domain,
)
return decision


cn_industry_etf_rotation_entrypoint = CallableStrategyEntrypoint(
Expand Down Expand Up @@ -85,7 +99,14 @@ def evaluate_cn_industry_etf_rotation_aggressive(ctx: StrategyContext) -> Strate
risk_flags=risk_flags,
diagnostics=diagnostics,
)
return apply_risk_gate(decision)
decision = apply_risk_gate(decision)
record_strategy_decision(
ctx,
decision,
profile_id=cn_industry_etf_rotation_aggressive_manifest.profile,
domain=cn_industry_etf_rotation_aggressive_manifest.domain,
)
return decision


cn_industry_etf_rotation_aggressive_entrypoint = CallableStrategyEntrypoint(
Expand Down Expand Up @@ -121,7 +142,14 @@ def evaluate_cn_index_etf_tactical_rotation(ctx: StrategyContext) -> StrategyDec
risk_flags=risk_flags,
diagnostics=diagnostics,
)
return apply_risk_gate(decision)
decision = apply_risk_gate(decision)
record_strategy_decision(
ctx,
decision,
profile_id=cn_index_etf_tactical_rotation_manifest.profile,
domain=cn_index_etf_tactical_rotation_manifest.domain,
)
return decision


cn_index_etf_tactical_rotation_entrypoint = CallableStrategyEntrypoint(
Expand Down Expand Up @@ -157,7 +185,14 @@ def evaluate_cn_chinext_tactical_rotation(ctx: StrategyContext) -> StrategyDecis
risk_flags=risk_flags,
diagnostics=diagnostics,
)
return apply_risk_gate(decision)
decision = apply_risk_gate(decision)
record_strategy_decision(
ctx,
decision,
profile_id=cn_chinext_tactical_rotation_manifest.profile,
domain=cn_chinext_tactical_rotation_manifest.domain,
)
return decision


cn_chinext_tactical_rotation_entrypoint = CallableStrategyEntrypoint(
Expand Down Expand Up @@ -195,7 +230,14 @@ def evaluate_cn_chinext_growth_momentum_quality(ctx: StrategyContext) -> Strateg
risk_flags=risk_flags,
diagnostics=diagnostics,
)
return apply_risk_gate(decision)
decision = apply_risk_gate(decision)
record_strategy_decision(
ctx,
decision,
profile_id=cn_chinext_growth_momentum_quality_manifest.profile,
domain=cn_chinext_growth_momentum_quality_manifest.domain,
)
return decision


cn_chinext_growth_momentum_quality_entrypoint = CallableStrategyEntrypoint(
Expand Down Expand Up @@ -231,7 +273,14 @@ def evaluate_cn_chinext_growth_momentum_quality_snapshot(ctx: StrategyContext) -
risk_flags=risk_flags,
diagnostics=diagnostics,
)
return apply_risk_gate(decision)
decision = apply_risk_gate(decision)
record_strategy_decision(
ctx,
decision,
profile_id=cn_chinext_growth_momentum_quality_snapshot_manifest.profile,
domain=cn_chinext_growth_momentum_quality_snapshot_manifest.domain,
)
return decision


cn_chinext_growth_momentum_quality_snapshot_entrypoint = CallableStrategyEntrypoint(
Expand Down Expand Up @@ -269,7 +318,14 @@ def evaluate_cn_star_growth_momentum_quality(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=cn_star_growth_momentum_quality_manifest.profile,
domain=cn_star_growth_momentum_quality_manifest.domain,
)
return decision


cn_star_growth_momentum_quality_entrypoint = CallableStrategyEntrypoint(
Expand Down Expand Up @@ -303,7 +359,14 @@ def evaluate_cn_dividend_quality_snapshot(ctx: StrategyContext) -> StrategyDecis
risk_flags=risk_flags,
diagnostics=diagnostics,
)
return apply_risk_gate(decision)
decision = apply_risk_gate(decision)
record_strategy_decision(
ctx,
decision,
profile_id=cn_dividend_quality_snapshot_manifest.profile,
domain=cn_dividend_quality_snapshot_manifest.domain,
)
return decision


cn_dividend_quality_snapshot_entrypoint = CallableStrategyEntrypoint(
Expand All @@ -319,7 +382,14 @@ def evaluate_cn_dividend_quality_snapshot(ctx: StrategyContext) -> StrategyDecis

def evaluate_cn_equity_combo(ctx: StrategyContext) -> StrategyDecision:
from cn_equity_strategies.combo_entrypoints import evaluate_cn_equity_combo as _eval
return apply_risk_gate(_eval(ctx))
decision = apply_risk_gate(_eval(ctx))
record_strategy_decision(
ctx,
decision,
profile_id=cn_equity_combo_manifest.profile,
domain=cn_equity_combo_manifest.domain,
)
return decision


from cn_equity_strategies.combo_manifests import cn_equity_combo_manifest # noqa: E402
Expand Down
29 changes: 29 additions & 0 deletions src/cn_equity_strategies/entrypoints/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,42 @@
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__)

# ---------------------------------------------------------------------------
# 风控硬门 — 每个 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).

entrypoints/evaluate only has `StrategyDecision`; execution_result is
expected to be recorded by platform runtime later.
"""
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,
Expand Down
Loading