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 @@ -9,7 +9,7 @@ description = "Shared crypto strategy catalog and implementations"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@37c81901160c5b31127a27dba1c63944933fb6bf",
"quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@0c69df08144872ccd1d8bf523738e80748d8d664",
]

[tool.setuptools]
Expand Down
38 changes: 33 additions & 5 deletions src/crypto_strategies/entrypoints/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
crypto_trend_rotation_manifest,
)

from ._common import apply_risk_gate
from ._common import apply_risk_gate, record_strategy_decision


"""Unified crypto strategy entrypoints built on top of legacy core/rotation modules."""
Expand Down Expand Up @@ -261,7 +261,14 @@ def evaluate_crypto_live_pool_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=crypto_live_pool_rotation_manifest.profile,
domain=crypto_live_pool_rotation_manifest.domain,
)
return decision


crypto_live_pool_rotation_entrypoint = CallableStrategyEntrypoint(
Expand Down Expand Up @@ -343,7 +350,14 @@ def evaluate_crypto_btc_dca(ctx: StrategyContext) -> StrategyDecision:
risk_flags=risk_flags,
diagnostics=diagnostics,
)
return apply_risk_gate(decision, max_single_weight=0.50)
decision = apply_risk_gate(decision, max_single_weight=0.50)
record_strategy_decision(
ctx,
decision,
profile_id=crypto_btc_dca_manifest.profile,
domain=crypto_btc_dca_manifest.domain,
)
return decision


crypto_btc_dca_entrypoint = CallableStrategyEntrypoint(
Expand Down Expand Up @@ -426,7 +440,14 @@ def evaluate_crypto_trend_rotation(ctx: StrategyContext) -> StrategyDecision:
risk_flags=risk_flags,
diagnostics=diagnostics,
)
return apply_risk_gate(decision, max_single_weight=0.30)
decision = apply_risk_gate(decision, max_single_weight=0.30)
record_strategy_decision(
ctx,
decision,
profile_id=crypto_trend_rotation_manifest.profile,
domain=crypto_trend_rotation_manifest.domain,
)
return decision


crypto_trend_rotation_entrypoint = CallableStrategyEntrypoint(
Expand Down Expand Up @@ -615,7 +636,14 @@ def evaluate_crypto_equity_combo(ctx: StrategyContext) -> StrategyDecision:
risk_flags=risk_flags,
diagnostics=diagnostics,
)
return apply_risk_gate(decision, max_single_weight=0.30)
decision = apply_risk_gate(decision, max_single_weight=0.30)
record_strategy_decision(
ctx,
decision,
profile_id=crypto_equity_combo_manifest.profile,
domain=crypto_equity_combo_manifest.domain,
)
return decision


crypto_equity_combo_entrypoint = CallableStrategyEntrypoint(
Expand Down
29 changes: 29 additions & 0 deletions src/crypto_strategies/entrypoints/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,42 @@
import logging

from quant_platform_kit.strategy_contracts import PositionTarget, 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: object,
decision: StrategyDecision,
*,
profile_id: str,
domain: str,
) -> None:
"""Record per-run decision for live monitoring (roadmap 5a).

Crypto entrypoints don't consistently thread `StrategyContext` typing, so we
accept `ctx` as `object` and only use it for future extension.
"""
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 _position_weight(position: PositionTarget) -> float | None:
if position.target_weight is not None:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_contract_governance.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"binance",
"BINANCE_",
)
QPK_HEALTH_COMMIT = "37c81901160c5b31127a27dba1c63944933fb6bf"
QPK_HEALTH_COMMIT = "0c69df08144872ccd1d8bf523738e80748d8d664"
QPK_OLD_HEALTHLESS_COMMIT = "86f03fb8e83c0d372f4e1c64cccf3e6da50b8dd4"


Expand Down
Loading