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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ For the stricter end-state rules for new US equity strategies, see:
- [`docs/us_equity_release_cutover_plan.zh-CN.md`](./docs/us_equity_release_cutover_plan.zh-CN.md)
- [`docs/us_equity_live_switch_runbook.md`](./docs/us_equity_live_switch_runbook.md)
- [`docs/us_equity_live_switch_runbook.zh-CN.md`](./docs/us_equity_live_switch_runbook.zh-CN.md)
- [`docs/strategy_plugin_runtime_contract.md`](./docs/strategy_plugin_runtime_contract.md)

## Package layout

Expand All @@ -61,6 +62,7 @@ src/quant_platform_kit/
models.py
ports.py
strategies.py
strategy_plugins.py
ibkr/
connection.py
market_data.py
Expand Down Expand Up @@ -156,6 +158,7 @@ Cloud Run and self-hosted runner deployments should continue to deploy the strat
- [`docs/us_equity_cross_platform_strategy_spec.zh-CN.md`](./docs/us_equity_cross_platform_strategy_spec.zh-CN.md)
- [`docs/us_equity_live_switch_runbook.md`](./docs/us_equity_live_switch_runbook.md)
- [`docs/us_equity_live_switch_runbook.zh-CN.md`](./docs/us_equity_live_switch_runbook.zh-CN.md)
- [`docs/strategy_plugin_runtime_contract.md`](./docs/strategy_plugin_runtime_contract.md)

### 目录结构

Expand All @@ -165,6 +168,7 @@ src/quant_platform_kit/
models.py
ports.py
strategies.py
strategy_plugins.py
ibkr/
connection.py
market_data.py
Expand Down
101 changes: 101 additions & 0 deletions docs/strategy_plugin_runtime_contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Strategy Plugin Runtime Contract

This document describes how platform runtimes consume sidecar strategy plugin
artifacts, such as the Crisis Response plugin produced by
`UsEquitySnapshotPipelines`.

## Ownership

- Strategy plugin artifacts are produced upstream by snapshot / research
pipelines.
- Platform runtimes consume the latest plugin artifact and attach it to logs,
runtime reports, and notifications.
- Broker order placement remains in platform repositories.
- Strategy formulas remain in strategy repositories.

## Platform Mount Config

Platform config should only decide which plugin artifacts are mounted for a
strategy. It must not select the plugin mode. The mode lives inside the plugin
artifact and is the single behavior contract.

Suggested environment variable name: `STRATEGY_PLUGIN_MOUNTS_JSON`.

Recommended value:

```json
{
"strategy_plugins": [
{
"strategy": "tqqq_growth_income",
"plugin": "crisis_response_shadow",
"signal_path": "gs://qsl-runtime-logs-interactivebrokersquant/strategy-artifacts/us_equity/tqqq_growth_income/plugins/crisis_response_shadow/latest_signal.json",
"enabled": true
}
]
}
```

Use `expected_mode` only as a fail-closed deployment guard. It does not select
or reinterpret the mode:

```json
{
"strategy_plugins": [
{
"strategy": "tqqq_growth_income",
"plugin": "crisis_response_shadow",
"signal_path": "/var/strategy-artifacts/tqqq_growth_income/plugins/crisis_response_shadow/latest_signal.json",
"enabled": true,
"expected_mode": "shadow"
}
]
}
```

Do not put `mode` in the platform mount config. If the artifact says `paper`,
the platform implements `paper`; if it says `live`, the platform implements
`live` subject to risk checks and kill switches.

## Runtime Loader

Use `quant_platform_kit.common.strategy_plugins`:

```python
from quant_platform_kit.common.strategy_plugins import (
build_strategy_plugin_report_payload,
load_configured_strategy_plugin_signals,
parse_strategy_plugin_mounts,
)

mounts = parse_strategy_plugin_mounts(raw_json_config)
signals = load_configured_strategy_plugin_signals(
mounts,
strategy_profile=current_strategy_profile,
)
report_section = build_strategy_plugin_report_payload(signals)
```

The loader validates:

- the artifact is a JSON object
- `strategy` and `plugin` match the configured mount
- `mode`, `configured_mode`, and `effective_mode` are one of `shadow`, `paper`,
`advisory`, or `live`
- optional `expected_mode` matches `effective_mode`
- duplicate platform mounts are rejected
- platform mount config does not set `mode`

## Behavior Boundary

For `shadow`, platform runtimes should only add logs, runtime report fields, and
notification context.

For `paper`, platform runtimes may maintain a simulated plugin ledger, but must
not mutate real allocations.

For `advisory`, platform runtimes may surface a recommendation that requires
human confirmation.

For `live`, platform runtimes may allow execution only through explicit platform
risk budgets, kill switches, and data-freshness checks.
28 changes: 28 additions & 0 deletions src/quant_platform_kit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,21 @@
build_portfolio_snapshot_from_account_state,
build_strategy_evaluation_inputs,
)
from .common.strategy_plugins import (
PLUGIN_MODE_ADVISORY,
PLUGIN_MODE_LIVE,
PLUGIN_MODE_PAPER,
PLUGIN_MODE_SHADOW,
SUPPORTED_STRATEGY_PLUGIN_MODES,
StrategyPluginMountConfig,
StrategyPluginSignal,
build_strategy_plugin_report_payload,
load_configured_strategy_plugin_signals,
load_strategy_plugin_signal,
normalize_strategy_plugin_mode,
parse_strategy_plugin_mounts,
validate_strategy_plugin_signal_payload,
)
from .common.strategies import (
CRYPTO_DOMAIN,
PlatformStrategyPolicy,
Expand Down Expand Up @@ -80,6 +95,10 @@
"CRYPTO_DOMAIN",
"ExecutionReport",
"PlatformStrategyPolicy",
"PLUGIN_MODE_ADVISORY",
"PLUGIN_MODE_LIVE",
"PLUGIN_MODE_PAPER",
"PLUGIN_MODE_SHADOW",
"OrderIntent",
"PortfolioSnapshot",
"Position",
Expand All @@ -98,8 +117,11 @@
"StrategyEntrypointDefinition",
"StrategyManifest",
"StrategyMetadata",
"StrategyPluginMountConfig",
"StrategyPluginSignal",
"StrategyRuntimeAdapter",
"StrategyRuntimePolicy",
"SUPPORTED_STRATEGY_PLUGIN_MODES",
"ValueTargetPortfolioInputs",
"US_EQUITY_DOMAIN",
"build_allocation_intent",
Expand All @@ -109,6 +131,7 @@
"build_strategy_evaluation_inputs",
"build_value_target_portfolio_inputs_from_account_state",
"build_value_target_portfolio_inputs_from_snapshot",
"build_strategy_plugin_report_payload",
"resolve_decision_target_mode",
"translate_decision_to_target_mode",
"translate_value_decision_to_weight_targets",
Expand All @@ -125,14 +148,19 @@
"get_catalog_strategy_definition",
"get_catalog_strategy_metadata",
"get_enabled_profiles_for_platform",
"load_configured_strategy_plugin_signals",
"load_strategy_entrypoint",
"load_strategy_plugin_signal",
"normalize_profile_name",
"normalize_strategy_plugin_mode",
"parse_strategy_plugin_mounts",
"resolve_catalog_profile",
"resolve_platform_strategy_definition",
"resolve_strategy_artifact_contract",
"validate_strategy_artifact_contract",
"validate_strategy_decision",
"validate_strategy_manifest",
"validate_strategy_plugin_signal_payload",
"validate_strategy_runtime_adapter",
"validate_strategy_runtime_policy",
]
34 changes: 33 additions & 1 deletion src/quant_platform_kit/common/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,33 @@
"""Shared domain models, ports, and strategy contracts."""
"""Shared domain models, ports, strategy contracts, and plugin helpers."""

from .strategy_plugins import (
PLUGIN_MODE_ADVISORY,
PLUGIN_MODE_LIVE,
PLUGIN_MODE_PAPER,
PLUGIN_MODE_SHADOW,
SUPPORTED_STRATEGY_PLUGIN_MODES,
StrategyPluginMountConfig,
StrategyPluginSignal,
build_strategy_plugin_report_payload,
load_configured_strategy_plugin_signals,
load_strategy_plugin_signal,
normalize_strategy_plugin_mode,
parse_strategy_plugin_mounts,
validate_strategy_plugin_signal_payload,
)

__all__ = [
"PLUGIN_MODE_ADVISORY",
"PLUGIN_MODE_LIVE",
"PLUGIN_MODE_PAPER",
"PLUGIN_MODE_SHADOW",
"SUPPORTED_STRATEGY_PLUGIN_MODES",
"StrategyPluginMountConfig",
"StrategyPluginSignal",
"build_strategy_plugin_report_payload",
"load_configured_strategy_plugin_signals",
"load_strategy_plugin_signal",
"normalize_strategy_plugin_mode",
"parse_strategy_plugin_mounts",
"validate_strategy_plugin_signal_payload",
]
Loading