diff --git a/.env.example b/.env.example index bca1dab..65008a5 100644 --- a/.env.example +++ b/.env.example @@ -2,7 +2,8 @@ QMT_DRY_RUN_ONLY=true QMT_EXECUTION_MODE=dry_run # Required only by: python3 scripts/preflight_qmt_runtime.py --paper-admission # QMT_PAPER_ADMISSION_INPUT_SHA256= -STRATEGY_PROFILE=cn_industry_etf_rotation +# No profiles are currently selectable; retained historical example only. +# STRATEGY_PROFILE=cn_industry_etf_rotation ACCOUNT_PREFIX=QMT ACCOUNT_REGION=CN QMT_MARKET_HISTORY_PATH=data/fixtures/market_history.sample.csv diff --git a/README.md b/README.md index 54b634c..02db668 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A-share quant platform layer for miniQMT / QMT, built on `QuantPlatformKit` and ## Status -Experimental. The current implementation evaluates strategies offline and previews orders. This project is not financial advice and does not guarantee any trading result. +Experimental. The pinned CN policy has no selectable runtime profiles. Retired profiles are rejected before offline evaluation or order preview; historical catalog metadata and adapters remain available. This project is not financial advice and does not guarantee any trading result. ## What this is @@ -24,30 +24,10 @@ Requirements: Python 3.11 or later and Git. Run these commands from the reposito python3 -m pip install -e '.[test]' python3 -m pytest -q tests -unset RUNTIME_TARGET_JSON -export STRATEGY_PROFILE=cn_industry_etf_rotation -export QMT_DRY_RUN_ONLY=true -export QMT_EXECUTION_MODE=dry_run -export QMT_MARKET_HISTORY_PATH=data/fixtures/market_history.sample.csv - -python3 scripts/preflight_qmt_runtime.py python3 main.py ``` -In another terminal, query the local service: - -```bash -curl http://127.0.0.1:8080/probe -curl http://127.0.0.1:8080/profiles -curl http://127.0.0.1:8080/dry-run -``` - -The fixture-backed smoke scripts run a local strategy cycle without starting the HTTP service: - -```bash -python3 scripts/smoke_cn_industry_etf_rotation_dry_run_e2e.py -python3 scripts/smoke_cn_industry_etf_rotation_aggressive_dry_run_e2e.py -``` +Query `GET /profiles` to inspect the disabled historical profiles. No profile can currently pass preflight or run through `/dry-run`. The retained industry ETF smoke scripts reject their retired profiles under this policy. ## Configuration @@ -63,20 +43,13 @@ Use [`.env.example`](.env.example) as the dry-run configuration reference. Expor | `RUNTIME_TARGET_JSON` | No | none | JSON runtime target from `QuantRuntimeSettings`; takes precedence over `STRATEGY_PROFILE`. | | `PORT` | No | `8080` | HTTP port; the Flask entrypoint binds to `0.0.0.0`. | -## Supported dry-run profiles - -| Profile | Input mode | Role | -|---|---|---| -| `cn_industry_etf_rotation` | `market_history` | Primary profile. | -| `cn_industry_etf_rotation_aggressive` | `market_history` | Optional profile with a 25% volatility target. | - -Set `STRATEGY_PROFILE=cn_industry_etf_rotation_aggressive` to select the optional profile when `RUNTIME_TARGET_JSON` is unset. The QMT target layout is documented in [QuantRuntimeSettings](https://github.com/QuantStrategyLab/QuantRuntimeSettings/blob/main/examples/targets/qmt/README.md). +## Retired profiles -Profile availability follows the `CnEquityStrategies` catalog and QMT rollout allowlist. Research-only profiles such as `cn_dividend_quality_snapshot` and `cn_index_etf_tactical_rotation` are rejected by the QMT runtime policy. +`cn_industry_etf_rotation` and `cn_industry_etf_rotation_aggressive` are no longer selectable, including through aliases, environment configuration or `RUNTIME_TARGET_JSON`. The QMT rollout allowlist is empty. Historical definitions and adapters remain available for reading and isolated contract tests; they do not grant runtime selection. ## Offline paper admission -`--paper-admission` checks a local configuration and fixed input file. It requires `QMT_EXECUTION_MODE=paper`, `QMT_DRY_RUN_ONLY=true`, and a pre-recorded SHA-256 digest matching `QMT_MARKET_HISTORY_PATH`. +`--paper-admission` currently rejects retired profiles at configuration validation. The retained checks below describe the offline contract for an eligible profile. The command checks a local configuration and fixed input file. It requires `QMT_EXECUTION_MODE=paper`, `QMT_DRY_RUN_ONLY=true`, and a pre-recorded SHA-256 digest matching `QMT_MARKET_HISTORY_PATH`. After configuring the profile and input path, replace `` with that recorded digest: @@ -91,7 +64,7 @@ The command loads the strategy contract and hashes the local input. It does not ## Disabled-target lifecycle validation -The weekday `Runtime Target Lifecycle` workflow runs deterministic preflight and a fixture-backed dry-run for a fixed disabled target. It publishes `disabled` / `dry_run` status. A completed cycle whose target weights are empty because of the risk gate is a successful no-order check; a preflight or dry-run failure is reported as `attention`. These reports do not change target settings, establish a broker connection, or submit orders. +With the current empty allowlist, the retained industry ETF target fails preflight and is reported as `attention`. The weekday `Runtime Target Lifecycle` workflow runs deterministic preflight and a fixture-backed dry-run for a fixed disabled target. It publishes `disabled` / `dry_run` status. A completed cycle whose target weights are empty because of the risk gate is a successful no-order check; a preflight or dry-run failure is reported as `attention`. These reports do not change target settings, establish a broker connection, or submit orders. ## HTTP endpoints diff --git a/README.zh-CN.md b/README.zh-CN.md index bc87517..efa824d 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -6,7 +6,7 @@ ## 状态 -实验性项目。当前实现离线评估策略并预览订单。本项目不构成投资建议,也不保证任何交易结果。 +实验性项目。固定依赖中的 A 股政策已无可选运行策略;退役策略会在离线评估或订单预览之前被拒绝,历史策略元数据和适配器仍保留。本项目不构成投资建议,也不保证任何交易结果。 ## 简介 @@ -24,30 +24,10 @@ QmtPlatform 提供 QMT 平台集成层。它从 `CnEquityStrategies` 加载策 python3 -m pip install -e '.[test]' python3 -m pytest -q tests -unset RUNTIME_TARGET_JSON -export STRATEGY_PROFILE=cn_industry_etf_rotation -export QMT_DRY_RUN_ONLY=true -export QMT_EXECUTION_MODE=dry_run -export QMT_MARKET_HISTORY_PATH=data/fixtures/market_history.sample.csv - -python3 scripts/preflight_qmt_runtime.py python3 main.py ``` -在另一个终端查询本地服务: - -```bash -curl http://127.0.0.1:8080/probe -curl http://127.0.0.1:8080/profiles -curl http://127.0.0.1:8080/dry-run -``` - -以下 smoke 脚本使用固定样本运行本地策略周期,无需启动 HTTP 服务: - -```bash -python3 scripts/smoke_cn_industry_etf_rotation_dry_run_e2e.py -python3 scripts/smoke_cn_industry_etf_rotation_aggressive_dry_run_e2e.py -``` +通过 `GET /profiles` 查看已停用的历史策略。目前没有策略能通过预检或运行 `/dry-run`;保留的行业 ETF smoke 脚本也会因策略退役而拒绝运行。 ## 配置 @@ -63,20 +43,13 @@ python3 scripts/smoke_cn_industry_etf_rotation_aggressive_dry_run_e2e.py | `RUNTIME_TARGET_JSON` | 否 | 无 | 来自 `QuantRuntimeSettings` 的 JSON 运行目标;优先于 `STRATEGY_PROFILE`。 | | `PORT` | 否 | `8080` | HTTP 端口;Flask 入口绑定 `0.0.0.0`。 | -## 支持的 dry-run 策略 - -| 策略配置 | 输入模式 | 用途 | -|---|---|---| -| `cn_industry_etf_rotation` | `market_history` | 主要策略。 | -| `cn_industry_etf_rotation_aggressive` | `market_history` | 可选策略,目标波动率为 25%。 | - -未设置 `RUNTIME_TARGET_JSON` 时,可通过 `STRATEGY_PROFILE=cn_industry_etf_rotation_aggressive` 选择可选策略。QMT 运行目标结构见 [QuantRuntimeSettings](https://github.com/QuantStrategyLab/QuantRuntimeSettings/blob/main/examples/targets/qmt/README.md)。 +## 已退役策略 -可用策略由 `CnEquityStrategies` 的策略目录和 QMT 启用清单决定。QMT 运行配置不接受 `cn_dividend_quality_snapshot`、`cn_index_etf_tactical_rotation` 等仅供研究的策略配置。 +`cn_industry_etf_rotation` 和 `cn_industry_etf_rotation_aggressive` 均不可再选择,包括别名、环境变量及 `RUNTIME_TARGET_JSON` 入口。QMT 运行允许清单为空。历史定义和适配器供读取及隔离的契约测试使用,不赋予运行选择资格。 ## 离线 paper-admission 检查 -`--paper-admission` 检查本地配置和固定输入文件。它要求 `QMT_EXECUTION_MODE=paper`、`QMT_DRY_RUN_ONLY=true`,且预先记录的 SHA-256 摘要与 `QMT_MARKET_HISTORY_PATH` 一致。 +`--paper-admission` 目前会在配置检查时拒绝退役策略。下面保留的是具备选择资格时的离线检查契约:检查本地配置和固定输入文件。它要求 `QMT_EXECUTION_MODE=paper`、`QMT_DRY_RUN_ONLY=true`,且预先记录的 SHA-256 摘要与 `QMT_MARKET_HISTORY_PATH` 一致。 配置策略和输入路径后,将 `` 替换为预先记录的摘要: @@ -91,7 +64,7 @@ python3 scripts/preflight_qmt_runtime.py --paper-admission ## 已停用目标的生命周期验证 -工作日运行的 `Runtime Target Lifecycle` 工作流为固定的已停用目标执行确定性预检和基于样本的 dry-run,并发布 `disabled` / `dry_run` 状态。若周期已完成,但风控检查使目标权重为空,视为成功完成无订单检查;预检或 dry-run 失败则报告为 `attention`。这些报告不会修改目标设置、建立券商连接或提交订单。 +当前允许清单为空,保留的行业 ETF 目标会在预检失败并报告为 `attention`。工作日运行的 `Runtime Target Lifecycle` 工作流为固定的已停用目标执行确定性预检和基于样本的 dry-run,并发布 `disabled` / `dry_run` 状态。若周期已完成,但风控检查使目标权重为空,视为成功完成无订单检查;预检或 dry-run 失败则报告为 `attention`。这些报告不会修改目标设置、建立券商连接或提交订单。 ## HTTP 接口 diff --git a/pyproject.toml b/pyproject.toml index b87d83c..ebc50e1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,8 +13,8 @@ authors = [{ name = "QuantStrategyLab" }] dependencies = [ "flask>=3.0", "pandas>=2.0", - "quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@7f140f07ac89f0b4b88347a903906825dde11c39", - "cn-equity-strategies @ git+https://github.com/QuantStrategyLab/CnEquityStrategies.git@3ed1bfb5c2d481f96b941c3d5299d477ccded734", + "quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@f6f2079f6b53d3cbb3b72bdcc5c2476ed8f170f1", + "cn-equity-strategies @ git+https://github.com/QuantStrategyLab/CnEquityStrategies.git@29b6cb74464ddb01874a9b3390dd56d19d5860af", ] [project.optional-dependencies] diff --git a/tests/conftest.py b/tests/conftest.py index 2879811..0ed6f6d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -36,3 +36,22 @@ def market_history_csv(tmp_path) -> str: path = tmp_path / "market_history.csv" frame.to_csv(path, index=False) return str(path) + + +@pytest.fixture +def historical_strategy_policy(monkeypatch): + """Exercise retained offline contracts under an explicit historical policy. + + Opt in only for tests of risk/preflight behavior below profile selection. + Retirement entrypoint tests use the real empty policy. + """ + from dataclasses import replace + import strategy_registry + + monkeypatch.setattr( + strategy_registry, + "PLATFORM_POLICY", + replace(strategy_registry.PLATFORM_POLICY, enabled_profiles=frozenset({ + "cn_industry_etf_rotation", "cn_industry_etf_rotation_aggressive", + })), + ) diff --git a/tests/test_dry_run_cycle.py b/tests/test_dry_run_cycle.py index 6e5297a..d373344 100644 --- a/tests/test_dry_run_cycle.py +++ b/tests/test_dry_run_cycle.py @@ -10,28 +10,21 @@ from strategy_registry import QMT_PLATFORM, PLATFORM_POLICY, get_enabled_profiles_for_platform -def test_qmt_enabled_profiles_include_primary_runtime_strategies(): - profiles = get_enabled_profiles_for_platform(QMT_PLATFORM, policy=PLATFORM_POLICY) - assert profiles == frozenset( - { - CN_INDUSTRY_ETF_ROTATION_PROFILE, - CN_INDUSTRY_ETF_ROTATION_AGGRESSIVE_PROFILE, - } - ) +def test_qmt_enabled_profiles_are_empty_after_retirement(): + assert get_enabled_profiles_for_platform(QMT_PLATFORM, policy=PLATFORM_POLICY) == frozenset() -def test_load_platform_runtime_settings_from_env(monkeypatch: pytest.MonkeyPatch, market_history_csv: str): - monkeypatch.setenv("STRATEGY_PROFILE", CN_INDUSTRY_ETF_ROTATION_PROFILE) +@pytest.mark.parametrize("profile", [CN_INDUSTRY_ETF_ROTATION_PROFILE, CN_INDUSTRY_ETF_ROTATION_AGGRESSIVE_PROFILE]) +def test_load_platform_runtime_settings_rejects_retired_profiles(monkeypatch, market_history_csv, profile): + monkeypatch.setenv("STRATEGY_PROFILE", profile) monkeypatch.setenv("QMT_DRY_RUN_ONLY", "true") monkeypatch.setenv("QMT_MARKET_HISTORY_PATH", market_history_csv) monkeypatch.delenv("RUNTIME_TARGET_JSON", raising=False) - - settings = load_platform_runtime_settings() - assert settings.strategy_profile == CN_INDUSTRY_ETF_ROTATION_PROFILE - assert settings.dry_run_only is True - assert settings.market_history_path == market_history_csv + with pytest.raises(ValueError, match="Unsupported STRATEGY_PROFILE"): + load_platform_runtime_settings() +@pytest.mark.usefixtures("historical_strategy_policy") def test_dry_run_cycle_fails_closed_on_concentrated_target( monkeypatch: pytest.MonkeyPatch, market_history_csv: str, @@ -65,6 +58,7 @@ def test_dry_run_cycle_fails_closed_on_concentrated_target( assert report["dry_run_only"] is True +@pytest.mark.usefixtures("historical_strategy_policy") def test_aggressive_industry_profile_fails_closed_on_concentration( monkeypatch: pytest.MonkeyPatch, market_history_csv: str, diff --git a/tests/test_main.py b/tests/test_main.py index 2e18bb7..a2ba56d 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -14,7 +14,7 @@ def client(monkeypatch: pytest.MonkeyPatch, market_history_csv: str): return app.test_client() -def test_probe_endpoint(client): +def test_probe_endpoint(client, historical_strategy_policy): response = client.get("/probe") assert response.status_code == 200 payload = response.get_json() @@ -23,7 +23,7 @@ def test_probe_endpoint(client): assert payload["strategy_profile"] == "cn_industry_etf_rotation" -def test_dry_run_endpoint(client): +def test_dry_run_endpoint(client, historical_strategy_policy): response = client.get("/dry-run") assert response.status_code == 200 payload = response.get_json() @@ -32,3 +32,19 @@ def test_dry_run_endpoint(client): assert payload["order_previews"] == [] assert payload["risk_flags"] == ["rejected:concentration"] assert payload["diagnostics"]["risk_gate"] == "REJECT" + + +def test_retired_profile_cannot_run_through_http(client, monkeypatch): + import main + + def unexpected_cycle(**kwargs): + pytest.fail("Retired profile reached the strategy cycle") + + monkeypatch.setattr(main, "run_dry_run_cycle", unexpected_cycle) + assert client.get("/dry-run").status_code == 500 + + +def test_profiles_endpoint_has_no_enabled_profiles(client): + response = client.get("/profiles") + assert response.status_code == 200 + assert not any(row["enabled"] for row in response.get_json()["profiles"]) diff --git a/tests/test_runtime_preflight.py b/tests/test_runtime_preflight.py index 5ced255..da4dc94 100644 --- a/tests/test_runtime_preflight.py +++ b/tests/test_runtime_preflight.py @@ -42,6 +42,7 @@ def _valid_e3_receipt() -> dict[str, object]: } +@pytest.mark.usefixtures("historical_strategy_policy") def test_preflight_accepts_primary_dry_run_config(monkeypatch, market_history_csv: str): monkeypatch.setenv("STRATEGY_PROFILE", "cn_industry_etf_rotation") monkeypatch.setenv("QMT_DRY_RUN_ONLY", "true") @@ -57,6 +58,7 @@ def test_preflight_accepts_primary_dry_run_config(monkeypatch, market_history_cs assert report.issues == () +@pytest.mark.usefixtures("historical_strategy_policy") def test_preflight_requires_market_history_for_direct_profile(monkeypatch): monkeypatch.setenv("STRATEGY_PROFILE", "cn_industry_etf_rotation") monkeypatch.setenv("QMT_DRY_RUN_ONLY", "true") @@ -69,6 +71,7 @@ def test_preflight_requires_market_history_for_direct_profile(monkeypatch): assert _issue_codes(report) == {"missing_market_history"} +@pytest.mark.usefixtures("historical_strategy_policy") def test_preflight_blocks_non_dry_run_config(monkeypatch, market_history_csv: str): monkeypatch.setenv("STRATEGY_PROFILE", "cn_industry_etf_rotation") monkeypatch.setenv("QMT_DRY_RUN_ONLY", "false") @@ -81,6 +84,7 @@ def test_preflight_blocks_non_dry_run_config(monkeypatch, market_history_csv: st assert _issue_codes(report) == {"non_dry_run_blocked"} +@pytest.mark.usefixtures("historical_strategy_policy") def test_paper_admission_requires_paper_mode_and_a_frozen_input_digest(monkeypatch, market_history_csv: str): monkeypatch.setenv("STRATEGY_PROFILE", "cn_industry_etf_rotation") monkeypatch.setenv("QMT_DRY_RUN_ONLY", "true") @@ -101,6 +105,7 @@ def test_paper_admission_requires_paper_mode_and_a_frozen_input_digest(monkeypat assert report.to_payload() == run_preflight(paper_admission=True).to_payload() +@pytest.mark.usefixtures("historical_strategy_policy") def test_paper_admission_rejects_live_mode_and_non_dry_run(monkeypatch, market_history_csv: str): monkeypatch.setenv("STRATEGY_PROFILE", "cn_industry_etf_rotation") monkeypatch.setenv("QMT_DRY_RUN_ONLY", "false") @@ -120,6 +125,7 @@ def test_paper_admission_rejects_live_mode_and_non_dry_run(monkeypatch, market_h } +@pytest.mark.usefixtures("historical_strategy_policy") def test_paper_admission_rejects_an_unfrozen_input(monkeypatch, market_history_csv: str): monkeypatch.setenv("STRATEGY_PROFILE", "cn_industry_etf_rotation") monkeypatch.setenv("QMT_DRY_RUN_ONLY", "true") @@ -141,6 +147,7 @@ def test_preflight_cli_has_no_non_dry_run_bypass(): assert exc_info.value.code == 2 +@pytest.mark.usefixtures("historical_strategy_policy") def test_preflight_cli_runs_offline_paper_admission(monkeypatch, market_history_csv: str): monkeypatch.setenv("STRATEGY_PROFILE", "cn_industry_etf_rotation") monkeypatch.setenv("QMT_DRY_RUN_ONLY", "true") @@ -224,6 +231,7 @@ def test_e3_receipt_validator_fails_closed_for_missing_required_fields(): assert _issue_codes(report) == {"missing_receipt_fields", "verify_only_required"} +@pytest.mark.usefixtures("historical_strategy_policy") def test_preflight_cli_validates_e3_receipt_offline(monkeypatch, market_history_csv: str, tmp_path, capsys): monkeypatch.setenv("STRATEGY_PROFILE", "cn_industry_etf_rotation") monkeypatch.setenv("QMT_DRY_RUN_ONLY", "true") diff --git a/tests/test_strategy_retirement_policy.py b/tests/test_strategy_retirement_policy.py new file mode 100644 index 0000000..63c5495 --- /dev/null +++ b/tests/test_strategy_retirement_policy.py @@ -0,0 +1,49 @@ +"""Retired profiles remain readable but cannot be selected by the platform.""" + +import pytest + +from cn_equity_strategies import get_platform_runtime_adapter, get_strategy_catalog +from strategy_loader import load_strategy_definition +from strategy_registry import get_supported_profiles_for_platform, resolve_strategy_definition + + +RETIRED_PROFILES = ('cn_industry_etf_rotation', 'cn_industry_etf_rotation_aggressive') + + +@pytest.mark.parametrize("profile", RETIRED_PROFILES) +def test_retired_profiles_are_not_selectable(profile): + assert profile not in get_supported_profiles_for_platform("qmt") + with pytest.raises(ValueError, match="Unsupported STRATEGY_PROFILE"): + resolve_strategy_definition(profile, platform_id="qmt") + with pytest.raises(ValueError, match="Unsupported STRATEGY_PROFILE"): + load_strategy_definition(profile) + + +@pytest.mark.parametrize("profile", RETIRED_PROFILES) +def test_retired_profile_aliases_are_not_selectable(profile): + catalog = get_strategy_catalog() + for alias, canonical in catalog.profile_aliases.items(): + if canonical == profile: + with pytest.raises(ValueError, match="Unsupported STRATEGY_PROFILE"): + resolve_strategy_definition(alias, platform_id="qmt") + + +@pytest.mark.parametrize("profile", RETIRED_PROFILES) +def test_retired_profile_history_and_adapters_remain_available(profile): + catalog = get_strategy_catalog() + assert profile in catalog.definitions + assert profile in catalog.metadata + assert get_platform_runtime_adapter(profile, platform_id="qmt") is not None + + +@pytest.mark.parametrize("profile", RETIRED_PROFILES) +def test_preflight_rejects_retired_profiles(monkeypatch, profile): + from runtime_preflight import run_preflight + + monkeypatch.setenv("STRATEGY_PROFILE", profile) + monkeypatch.setenv("QMT_DRY_RUN_ONLY", "true") + monkeypatch.delenv("RUNTIME_TARGET_JSON", raising=False) + report = run_preflight() + assert report.status == "error" + assert {issue.code for issue in report.issues} == {"runtime_config_error"} + assert "Unsupported STRATEGY_PROFILE" in report.issues[0].message diff --git a/uv.lock b/uv.lock index da541d8..e1a511e 100644 --- a/uv.lock +++ b/uv.lock @@ -37,7 +37,7 @@ wheels = [ [[package]] name = "cn-equity-strategies" version = "0.1.0" -source = { git = "https://github.com/QuantStrategyLab/CnEquityStrategies.git?rev=3ed1bfb5c2d481f96b941c3d5299d477ccded734#3ed1bfb5c2d481f96b941c3d5299d477ccded734" } +source = { git = "https://github.com/QuantStrategyLab/CnEquityStrategies.git?rev=29b6cb74464ddb01874a9b3390dd56d19d5860af#29b6cb74464ddb01874a9b3390dd56d19d5860af" } dependencies = [ { name = "pandas" }, { name = "quant-platform-kit" }, @@ -450,18 +450,18 @@ test = [ [package.metadata] requires-dist = [ - { name = "cn-equity-strategies", git = "https://github.com/QuantStrategyLab/CnEquityStrategies.git?rev=3ed1bfb5c2d481f96b941c3d5299d477ccded734" }, + { name = "cn-equity-strategies", git = "https://github.com/QuantStrategyLab/CnEquityStrategies.git?rev=29b6cb74464ddb01874a9b3390dd56d19d5860af" }, { name = "flask", specifier = ">=3.0" }, { name = "pandas", specifier = ">=2.0" }, { name = "pytest", marker = "extra == 'test'", specifier = ">=8" }, - { name = "quant-platform-kit", git = "https://github.com/QuantStrategyLab/QuantPlatformKit.git?rev=7f140f07ac89f0b4b88347a903906825dde11c39" }, + { name = "quant-platform-kit", git = "https://github.com/QuantStrategyLab/QuantPlatformKit.git?rev=f6f2079f6b53d3cbb3b72bdcc5c2476ed8f170f1" }, ] provides-extras = ["test"] [[package]] name = "quant-platform-kit" version = "1.0.0" -source = { git = "https://github.com/QuantStrategyLab/QuantPlatformKit.git?rev=7f140f07ac89f0b4b88347a903906825dde11c39#7f140f07ac89f0b4b88347a903906825dde11c39" } +source = { git = "https://github.com/QuantStrategyLab/QuantPlatformKit.git?rev=f6f2079f6b53d3cbb3b72bdcc5c2476ed8f170f1#f6f2079f6b53d3cbb3b72bdcc5c2476ed8f170f1" } [[package]] name = "six"