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
12 changes: 12 additions & 0 deletions docs/crypto_cross_platform_strategy_spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ A crypto runtime adapter must declare at least:
- `available_inputs`
- `available_capabilities`
- `portfolio_input_name` when the strategy needs `ctx.portfolio`
- `artifact_contract` when the strategy consumes upstream artifacts

`crypto_leader_rotation` currently declares an explicit artifact contract:

- `requires_snapshot_artifacts = true`
- `requires_snapshot_manifest_path = true`
- `snapshot_contract_version = crypto_leader_rotation.live_pool.v1`
- `config_source_policy = none`

The strategy package owns this declaration. Downstream platforms may decide how
to fetch the artifact, but they should not infer artifact requirements from
profile-name branches.

## Allowed and forbidden boundaries

Expand Down
10 changes: 10 additions & 0 deletions docs/crypto_cross_platform_strategy_spec.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@
- `available_inputs`
- `available_capabilities`
- 当策略需要 `ctx.portfolio` 时的 `portfolio_input_name`
- 当策略依赖上游 artifact 时的 `artifact_contract`

`crypto_leader_rotation` 当前显式声明的 artifact contract 是:

- `requires_snapshot_artifacts = true`
- `requires_snapshot_manifest_path = true`
- `snapshot_contract_version = crypto_leader_rotation.live_pool.v1`
- `config_source_policy = none`

策略包负责声明这些需求。下游平台可以决定从 Firestore、GCS、本地文件或状态里取 artifact,但不能再靠 profile 名称分支来猜这条策略需要什么 artifact。

## 允许和禁止

Expand Down
1 change: 1 addition & 0 deletions docs/crypto_portability_checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Use this before enabling a crypto profile on any downstream platform.
- [ ] `required_inputs` only use canonical crypto input names
- [ ] `target_mode` is explicitly declared
- [ ] every compatible platform has a runtime adapter
- [ ] upstream artifact needs are declared through `artifact_contract`, not platform profile branches
- [ ] strategy code does not branch on platform names
- [ ] strategy code does not read exchange env vars
- [ ] downstream runtime builds canonical inputs, not exchange-shaped names
Expand Down
1 change: 1 addition & 0 deletions docs/crypto_portability_checklist.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- [ ] `required_inputs` 只使用 canonical 加密输入名
- [ ] `target_mode` 已显式声明
- [ ] 每个兼容平台都有对应 runtime adapter
- [ ] 上游 artifact 需求通过 `artifact_contract` 声明,而不是下游平台 profile 分支
- [ ] 策略代码没有平台分支
- [ ] 策略代码没有直接读取交易所环境变量
- [ ] 下游 runtime 传给策略的是 canonical 输入,而不是交易所专属名字
Expand Down
8 changes: 7 additions & 1 deletion docs/crypto_strategy_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ At minimum, a new profile should touch these places:
3. add a unified entrypoint in `src/crypto_strategies/entrypoints/__init__.py`
4. add a runtime adapter in `src/crypto_strategies/runtime_adapters.py`
5. add catalog and entrypoint tests
6. if the profile becomes live, add platform status and portability checks downstream
6. if the profile consumes upstream artifacts, add an explicit `StrategyArtifactContract`
7. if the profile becomes live, add platform status and portability checks downstream

## StrategyDefinition

Expand Down Expand Up @@ -59,9 +60,13 @@ A new runtime adapter must declare at least:

- `available_inputs`
- `portfolio_input_name` when the strategy reads `ctx.portfolio`
- `artifact_contract` when the strategy needs upstream artifacts

If a platform does not support the profile yet, do not add it to `supported_platforms`.

The artifact contract belongs in this strategy package. Platform repositories
should consume it and map it to their own env/filesystem/runtime source rules.

## Forbidden shortcuts

Do not:
Expand All @@ -84,6 +89,7 @@ Do not:
- [ ] `StrategyManifest` matches the catalog definition
- [ ] entrypoint reads only canonical inputs
- [ ] runtime adapter added for every compatible platform
- [ ] artifact contract added when upstream artifacts are required
- [ ] strategy code has no platform branch or env reads
- [ ] catalog, entrypoint, and governance tests were updated
- [ ] downstream platform status script or adapter smoke was updated when rollout changed
7 changes: 6 additions & 1 deletion docs/crypto_strategy_template.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
3. 在 `src/crypto_strategies/entrypoints/__init__.py` 里加统一 entrypoint
4. 在 `src/crypto_strategies/runtime_adapters.py` 里加 runtime adapter
5. 补 catalog 和 entrypoint 测试
6. 如果要变成 live,再去下游平台补状态和 portability 检查
6. 如果策略消费上游 artifact,补显式 `StrategyArtifactContract`
7. 如果要变成 live,再去下游平台补状态和 portability 检查

## StrategyDefinition 必填项

Expand Down Expand Up @@ -59,9 +60,12 @@ entrypoint 必须做到:

- `available_inputs`
- 如果策略会读 `ctx.portfolio`,则声明 `portfolio_input_name`
- 如果策略依赖上游 artifact,则声明 `artifact_contract`

如果某个平台还不支持这条策略,就不要先把它写进 `supported_platforms`。

artifact contract 属于策略包。平台仓库只负责把它映射到自己的环境变量、文件、Firestore/GCS 或 runtime 状态来源,不能靠 profile 名称分支来猜策略需要什么 artifact。

## 禁止的偷懒方式

不要:
Expand All @@ -84,6 +88,7 @@ entrypoint 必须做到:
- [ ] `StrategyManifest` 和 catalog 定义一致
- [ ] entrypoint 只读取 canonical 输入
- [ ] 每个兼容平台都补了 runtime adapter
- [ ] 需要上游 artifact 时已新增 artifact contract
- [ ] 策略代码里没有平台分支和环境变量读取
- [ ] catalog、entrypoint、governance 测试已更新
- [ ] 如果 rollout 变了,下游平台的状态脚本或 adapter smoke 也已更新
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "crypto-strategies"
version = "0.4.3"
version = "0.4.4"
description = "Shared crypto strategy catalog and implementations"
readme = "README.md"
requires-python = ">=3.11"
Expand Down
12 changes: 12 additions & 0 deletions src/crypto_strategies/runtime_adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from quant_platform_kit.common.strategies import resolve_catalog_profile
from quant_platform_kit.strategy_contracts import (
StrategyArtifactContract,
StrategyRuntimeAdapter,
validate_strategy_runtime_adapter,
)
Expand All @@ -23,12 +24,22 @@
)


CRYPTO_LEADER_ROTATION_ARTIFACT_CONTRACT = StrategyArtifactContract(
requires_snapshot_artifacts=True,
requires_snapshot_manifest_path=True,
requires_strategy_config_path=False,
snapshot_contract_version="crypto_leader_rotation.live_pool.v1",
config_source_policy="none",
)


PLATFORM_RUNTIME_ADAPTERS: dict[str, dict[str, StrategyRuntimeAdapter]] = {
BINANCE_PLATFORM: {
"crypto_leader_rotation": StrategyRuntimeAdapter(
status_icon="🪙",
available_inputs=CRYPTO_CANONICAL_REQUIRED_INPUTS,
portfolio_input_name="portfolio_snapshot",
artifact_contract=CRYPTO_LEADER_ROTATION_ARTIFACT_CONTRACT,
),
}
}
Expand All @@ -55,6 +66,7 @@ def get_platform_runtime_adapter(profile: str | None, *, platform_id: str) -> St
__all__ = [
"BINANCE_PLATFORM",
"CRYPTO_CANONICAL_REQUIRED_INPUTS",
"CRYPTO_LEADER_ROTATION_ARTIFACT_CONTRACT",
"PLATFORM_RUNTIME_ADAPTERS",
"get_platform_runtime_adapter",
"resolve_canonical_profile",
Expand Down
12 changes: 12 additions & 0 deletions tests/test_contract_governance.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
from crypto_strategies.runtime_adapters import (
BINANCE_PLATFORM,
CRYPTO_CANONICAL_REQUIRED_INPUTS,
CRYPTO_LEADER_ROTATION_ARTIFACT_CONTRACT,
PLATFORM_RUNTIME_ADAPTERS,
get_platform_runtime_adapter,
)
from quant_platform_kit.strategy_contracts import resolve_strategy_artifact_contract


ALLOWED_TARGET_MODES = frozenset({"weight", "value"})
Expand Down Expand Up @@ -56,6 +58,16 @@ def test_every_compatible_platform_has_runtime_adapter_coverage(self) -> None:
elif definition.target_mode != PLATFORM_NATIVE_TARGET_MODES[platform_id]:
self.assertTrue(adapter.portfolio_input_name)

def test_live_profiles_declare_explicit_artifact_contract(self) -> None:
adapter = get_platform_runtime_adapter("crypto_leader_rotation", platform_id=BINANCE_PLATFORM)
contract = resolve_strategy_artifact_contract(adapter)

self.assertEqual(contract, CRYPTO_LEADER_ROTATION_ARTIFACT_CONTRACT)
self.assertTrue(contract.requires_snapshot_artifacts)
self.assertTrue(contract.requires_snapshot_manifest_path)
self.assertEqual(contract.snapshot_contract_version, "crypto_leader_rotation.live_pool.v1")
self.assertEqual(contract.config_source_policy, "none")

def test_runtime_adapter_map_matches_catalog_compatibility(self) -> None:
compatibility_map = {
profile: frozenset(definition.supported_platforms)
Expand Down