-
Notifications
You must be signed in to change notification settings - Fork 0
feat(risk): stamp consecutive_losses before strategy evaluate #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7d3b758
67007f9
3fbdd24
364ae68
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,18 +47,28 @@ def evaluate(self, *, available_inputs: Mapping[str, Any]) -> StrategyEvaluation | |
| runtime_config = dict(self.runtime_config) | ||
| apply_runtime_policy_to_runtime_config(runtime_config, self.runtime_adapter) | ||
|
|
||
| stamped_inputs = dict(available_inputs) | ||
| snapshot = stamped_inputs.get("portfolio_snapshot") | ||
| if snapshot is not None: | ||
| from quant_platform_kit.strategy_lifecycle.live_equity import stamp_consecutive_losses_on_snapshot | ||
|
|
||
| stamped_inputs["portfolio_snapshot"] = stamp_consecutive_losses_on_snapshot( | ||
| snapshot, | ||
| strategy_profile=self.profile, | ||
| ) | ||
|
Comment on lines
+55
to
+58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For the QMT profiles currently loaded through Useful? React with 👍 / 👎. |
||
|
|
||
| if _FEATURE_SNAPSHOT_INPUT in frozenset(self.entrypoint.manifest.required_inputs): | ||
| return self._evaluate_feature_snapshot_strategy( | ||
| runtime_config=runtime_config, | ||
| available_inputs=available_inputs, | ||
| available_inputs=stamped_inputs, | ||
| ) | ||
|
|
||
| as_of = datetime.now(timezone.utc) | ||
| ctx = build_strategy_context_from_available_inputs( | ||
| entrypoint=self.entrypoint, | ||
| runtime_adapter=self.runtime_adapter, | ||
| as_of=as_of, | ||
| available_inputs=dict(available_inputs), | ||
| available_inputs=stamped_inputs, | ||
| runtime_config=runtime_config, | ||
| ) | ||
| decision = self.entrypoint.evaluate(ctx) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from datetime import datetime, timezone | ||
| from unittest.mock import patch | ||
|
|
||
| from quant_platform_kit.common.models import PortfolioSnapshot | ||
| from quant_platform_kit.strategy_contracts import ( | ||
| StrategyDecision, | ||
| StrategyManifest, | ||
| StrategyRuntimeAdapter, | ||
| ) | ||
| from runtime_config_support import PlatformRuntimeSettings | ||
| import strategy_runtime as strategy_runtime_module | ||
|
|
||
|
|
||
| def _runtime_settings() -> PlatformRuntimeSettings: | ||
| return PlatformRuntimeSettings( | ||
| strategy_profile="cn_industry_etf_rotation", | ||
| strategy_display_name="CN Industry ETF Rotation", | ||
| strategy_domain="cn_equity", | ||
| dry_run_only=True, | ||
| market_history_path=None, | ||
| feature_snapshot_path=None, | ||
| feature_snapshot_manifest_path=None, | ||
| ) | ||
|
|
||
|
|
||
| def test_evaluate_stamps_consecutive_losses_on_portfolio_snapshot(): | ||
| class _Entrypoint: | ||
| manifest = StrategyManifest( | ||
| profile="cn_industry_etf_rotation", | ||
| domain="cn_equity", | ||
| display_name="CN Industry ETF Rotation", | ||
| description="test", | ||
| required_inputs=frozenset({"portfolio_snapshot"}), | ||
| ) | ||
|
|
||
| def evaluate(self, ctx): | ||
| self.ctx = ctx | ||
| return StrategyDecision() | ||
|
|
||
| entrypoint = _Entrypoint() | ||
| runtime = strategy_runtime_module.LoadedStrategyRuntime( | ||
| entrypoint=entrypoint, | ||
| runtime_adapter=StrategyRuntimeAdapter(portfolio_input_name="portfolio_snapshot"), | ||
| runtime_settings=_runtime_settings(), | ||
| ) | ||
| snapshot = PortfolioSnapshot( | ||
| as_of=datetime.now(timezone.utc), | ||
| total_equity=100_000.0, | ||
| positions=(), | ||
| metadata={}, | ||
| ) | ||
| stamped = PortfolioSnapshot( | ||
| as_of=snapshot.as_of, | ||
| total_equity=snapshot.total_equity, | ||
| positions=(), | ||
| metadata={"consecutive_losses": 3}, | ||
| ) | ||
|
|
||
| with patch( | ||
| "quant_platform_kit.strategy_lifecycle.live_equity.stamp_consecutive_losses_on_snapshot", | ||
| return_value=stamped, | ||
| ) as stamp: | ||
| result = runtime.evaluate(available_inputs={"portfolio_snapshot": snapshot}) | ||
|
|
||
| stamp.assert_called_once() | ||
| assert entrypoint.ctx.portfolio is stamped | ||
| assert entrypoint.ctx.portfolio.metadata["consecutive_losses"] == 3 | ||
| assert result.metadata["strategy_profile"] == "cn_industry_etf_rotation" |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This pin change leaves
tests/test_qsl_metadata.py:18still asserting the previous QuantPlatformKit hash (37c81901160c5b31127a27dba1c63944933fb6bf), so the QSL metadata test will fail as soon as the test environment has its normal dependencies installed. Please update the expected value alongside this metadata bump so CI reflects the new required bundle revision.Useful? React with 👍 / 👎.