Skip to content

feat: Rust+PyO3 migration#308

Merged
zengbin93 merged 23 commits intomasterfrom
feat/rust-czsc-migration
May 8, 2026
Merged

feat: Rust+PyO3 migration#308
zengbin93 merged 23 commits intomasterfrom
feat/rust-czsc-migration

Conversation

@0xcjun
Copy link
Copy Markdown
Collaborator

@0xcjun 0xcjun commented May 7, 2026

Summary

将 czsc 核心算法从 Python 迁移到 Rust workspace(9 个 crate,详见 docs/MIGRATION_NOTES.md §1),通过 PyO3 以 czsc._native 模块暴露,Python 端保留薄包装与适配层。

变更体量:14 commits,268 files changed,+61.5K / -9.1K LoC。

主要工作分组

  • 核心迁移czsc/core.pyczsc/py/ 完全删除,CZSC_USE_PYTHON 环境变量退役(spec §3.4);缠论核心类型 CZSC / FX / BI / ZS / RawBar / NewBar / Freq / Mark / Direction / Operate / Signal / Event / Position / BarGenerator 全部由 crates/czsc-core 通过 PyO3 提供
  • 类型 stubczsc/_native.pyipyo3-stub-gen 自动生成,CI 加 drift check(.github/workflows/code-quality.yml
  • API 入口czsc/__init__.py 移除 PEP 562 lazy loading 改为静态 import(-54% LoC,spec §3.1)
  • 环境变量层czsc.envs 由 117 行精简到 49 行(-58%)
  • 传感器czsc/sensors/ 部分恢复(utils 三件套),CTAResearch 留 NotImplementedError 占位(恢复计划见 MIGRATION_NOTES §10.2)
  • 新增czsc/utils/trade.stoploss_by_direction(czsc-only,TDD 实现)
  • 性能基准:CZSC::new(P1,96.585ms / 200ms);222-signal dispatch(P2,1.1µs/signal、4.7ms/10K)
  • 可视化czsc/utils/st_components.py 拆分到 czsc/svc/,svc 改为静态加载

Parity 验证

  • pytest test/parity 25/25 全绿(rs_czsc vs czsc 输出位级一致)
  • compare_optimize_full.py:222 信号下 OpensOptimize / ExitsOptimize 全量产物逐字节对比,1786 个 parquet + 2 个 xlsx 内容完全一致
  • cargo test --workspace:通过

0xcjun added 23 commits May 7, 2026 13:13
将缠论核心算法(CZSC、BarGenerator、信号、交易器等)迁移到 Rust workspace
(czsc-core / czsc-signals / czsc-trader / czsc-utils / czsc-ta 等 9 个 crate),
通过 maturin + PyO3 暴露为 czsc._native 扩展模块;Python 端裁剪为薄封装层,
统一以 czsc.* 暴露公共 API。

回测/绩效/mock 数据切换到外部 wbt 包;新增 parity / compat / smoke / integration
测试套件保证 Rust 与原 Python 实现行为一致;为全部 Python 与测试代码补充
详细的中文 docstring 与行内注释。
… imports

按飞书 spec wiki 子文档审计的 P0 清单做小补丁:
- czsc/__init__.py: __version__ 0.10.12 -> 1.0.0; __date__ 20260308 -> 20260507(与 Cargo.toml/pyproject.toml 一致)
- czsc/__init__.pyi: 整文件删除(残留 from rs_czsc / from .core 引用,basedpyright standard 模式报错;py.typed 保留,类型回退到内联注解)
- czsc/eda.py: mark_v_reversal 的 rs 双分支折叠为单一 from czsc import …,移除 rs_czsc 与 czsc.utils.bar_generator 死路径
- czsc/utils/sig.py / czsc/utils/analysis/stats.py: from rs_czsc import {Signal,daily_performance} 改走 czsc / wbt(等价符号已验证)
- czsc/utils/sig.pyi / czsc/utils/plotting/kline.pyi: 修 5 处 from czsc.core import … as …(已删模块)
- docs/MIGRATION_NOTES.md: 新增 §10 Phase Q 章节,登记上述修补清单
…._native

Phase F 已经把 derive_signals_config / derive_signals_freqs / list_all_signals
全量迁到 czsc._native(246 个信号模板可拉),sig_parse.py 中的 _lazy_rs_czsc
工厂 + 三个永真分支 (if list_all_signals is not None / if derive_signals_config
is not None) 已无意义。删掉工厂 + wrappers,顶层直接 from czsc._native import …,
同步消去 docstring/注释中"rs_czsc 不可用时"的陈述;.pyi 同步补 derive_*/list_all_signals
顶层签名与 sig_k3_map 字段。Spec §3.3 "待评估 Rust 是否已等价实现"标记可摘除。

LoC: czsc/traders/sig_parse.py 387 -> 326(-61 行)。
按 spec §3.1,所有公共 API 在导入期一次性 import;不再使用 PEP 562 lazy loading:
- 删除 _LAZY_MODULES / _LAZY_ATTRS / __getattr__ 三件套
- svc/fsa/aphorism/mock 改为顶层 from . import …
- 7 个 lazy 属性(capture_warnings / plot_czsc_chart / KlineChart / …)改为
  from czsc.utils.* import …
- 删除 if TYPE_CHECKING 守卫;welcome() 内的 lazy import 提到顶层

同时把注释/文档密度大幅收缩:
- 17 处冗长的"逐符号说明"注释块全删(信息已在符号自身 docstring)
- __all__ 字面表改为按主题分组的紧凑横排(仍保留全部 129 个公共名称)
- welcome() docstring 折成单行;模块 docstring 22 行 -> 11 行

LoC: 507 -> 235(-54%)。

循环 import 防坑:svc/fsa/aphorism/mock 中含 from czsc import top_drawdowns 等
回环 import,必须放到所有顶层符号绑定后再加载(第二批 from . import ...),
第一次误把它们提到顶部触发 ImportError partially-initialized module,调整顺序后
通过;文件中以"第一批 / 第二批"分组注释固化此约束。

测试更新:test/test_import_performance.py 中两个基于"streamlit 不应被
import czsc 拉起"的旧测试与新方向冲突,已删除;保留 import-time 兜底与
svc 可访问性测试。
调研发现 stoploss_by_direction 既不在当前安装的 rs_czsc 也不在 wbt 也不在
/Users/jun/Documents/vscodePro/rs_czsc git 历史 —— czsc/svc/backtest.py:261 的
from rs_czsc import stoploss_by_direction 一直是死调用,运行 Streamlit dashboard
时会 ImportError。

按 superpowers TDD 范式:
- RED: 新增 test/test_stoploss_by_direction.py 的 6 个失败测试(多/空头止损、
  order_id 切分、列契约、入参不可变性)
- GREEN: 在 czsc/utils/trade.py 用纯 Python 写最小实现(按方向连续段切
  order_id;向量化 hold_returns / min_hold_returns / returns / is_stop;
  浮点边界 1e-9 容差处理 92/100 - 1 = -0.07999... 这类问题)
- 切 czsc/svc/backtest.py:261 -> from czsc.utils.trade import stoploss_by_direction

效果:grep -r 'from rs_czsc\|import rs_czsc' czsc/ --include='*.py' 现在零结果,
spec C1 全量达成,czsc 内部彻底无 rs_czsc 依赖。
该函数与 is_trading_time 并列为 czsc-only 新增能力,登记于 MIGRATION_NOTES §2.2。
…ation log

补充 §10.2 "故意保留 / 暂缓的项" 表格(czsc/sensors/ 部分恢复、
czsc/traders/optimize.py 保留为薄外观层、czsc/utils/ta.py MACD ×2 约定保留、
_native.pyi 与 envs 精简归 P1)以及 §10.3 一段命令行验证日志,作为本轮
4 个代码 commit 的合并尾单。
按 spec §3.4 收缩 czsc/envs.py 到目标行数级别(Rust 端 set_envs 入口仍归 P1,
本轮先把 Python 侧 docstring 密度降到位):

- 模块级 20 行 docstring -> 11 行简洁说明
- 5 个函数(get_verbose / get_min_bi_len / get_max_bi_num + 内部 _env / _to_bool)
  各 8-15 行 verbose docstring -> 1-2 行单行说明
- 行为完全保留:环境变量大小写降级、参数显式优先、bool 宽松解析、int(float)
  容错都不变

同步清理 czsc/envs.pyi:删除已废弃的 valid_true / use_python / get_welcome
三个公共符号声明(test/test_envs.py::TestRetiredHelpers 已经在断言它们在
.py 中不存在,但 .pyi 之前未跟进,basedpyright 仍把它们误识为存在)。

测试:test/test_envs.py 16 项全部通过;全量 sweep 124 项 GREEN。
CLAUDE.md 仍记着已退役的 czsc/core.py / czsc/py/ / CZSC_USE_PYTHON 与已删除的
czsc/utils/{st_components,bar_generator,...}.py 路径。本次按当前仓库现状刷新:

- 核心组件 (§):1) czsc/core.py -> czsc._native(PyO3 扩展模块);2) czsc/py/ ->
  crates/(9 个 Rust crate);明确"不存在 Python 回退、CZSC_USE_PYTHON 已退役"
- 数据格式转换示例:from czsc.core import CZSC ... -> from czsc import CZSC ...
- 关键环境变量:CZSC_USE_PYTHON 标为废弃;CZSC_VERBOSE / MIN_BI_LEN / MAX_BI_NUM
  补充大小写约定与构造器参数优先级
- 缓存管理:czsc.utils.cache.home_path -> czsc.home_path(顶层别名)/
  czsc.utils.data.cache.home_path(实际定义处)
- Streamlit 集成:把 czsc/utils/st_components.py 改为 czsc/svc/,
  并标注 lazy loading 已按 spec §3.1 移除
- Rust/Python 混合架构 (§):删掉"版本控制 / 回退机制"两条,改为说明
  Rust 是唯一实现 + maturin 构建 + czsc._native 扩展模块名 + pyo3-stub-gen
  自动 stub(计划中)+ 一次性 fork rs-czsc 后不再同步
- czsc/utils/ 列表:refresh 到 Phase J 后的目录结构(data/ / analysis/ /
  crypto/ / plotting/ 子目录 + trade.py 含 stoploss_by_direction)
…2.4 / Q4)

各业务 crate 的 #[gen_stub_pyclass] / #[gen_stub_pyfunction] / #[gen_stub_pymethods]
装饰器早就布到位了,但缺收集器 + 生成 binary。本次补齐:

- crates/czsc-python/Cargo.toml
  - 把 pyo3 的 extension-module feature 拆成本地可选 default feature
    (default = ["extension-module"]),方便 binary 用 --no-default-features
    跳过它,让 pyo3 自动链接 libpython(否则 macOS 链接器找不到
    _PyExc_* 等符号)
  - 新增 [[bin]] stub_gen,path = src/bin/stub_gen.rs
- crates/czsc-python/src/lib.rs
  - 写自定义 stub_info() 函数(替代 define_stub_info_gatherer! 宏):
    把 from_pyproject_toml 路径显式指向 workspace 根
    (默认宏假设 pyproject.toml 与 Cargo.toml 同目录,但 czsc 仓库的
    pyproject 在 workspace 根、Cargo.toml 在 crates/czsc-python/)
- crates/czsc-python/src/bin/stub_gen.rs
  - 最小入口:调用 stub_info()?.generate()?

触发命令:
  PYO3_PYTHON=$(uv run python -c 'import sys; print(sys.executable)') \
    cargo run --bin stub_gen -p czsc-python --no-default-features

产物:czsc/_native.pyi(1 235 行),覆盖 BI / CZSC / FX / ZS / BarGenerator /
Position / Signal / Event / RawBar / NewBar / FakeBI / Direction / Mark /
Operate / Freq / ParsedSignalDoc 等核心类,以及 30+ TA 算子、信号函数与
顶层 chip_distribution_triangle / parse_signal_doc 等。
pyproject.toml::tool.maturin.include = ["czsc/**/*.pyi", ...] 已经覆盖此文件,
wheel 打包时自动携带,不需要追加配置。

残留:basedpyright 在 _native.pyi 上报 8 个 upstream pyo3-stub-gen 已知问题
(__eq__ 参数类型不兼容父类、__dict__ 与 dict[str, Any] 不兼容),属于工具
false-positive,不影响 IDE 提示与运行时行为。

测试:124 项全量 sweep 仍 GREEN;ast.parse 验证 _native.pyi 语法合法。
防回归:12cf082 落地 stub_gen 后,Rust 装饰器改动若忘记重跑 stub_gen,
czsc/_native.pyi 会与 Rust 源码漂移。CI 缺这个保护点。

新增 stub-drift job(needs: rust-tests):
- checkout + Rust toolchain + Python 3.11
- PYO3_PYTHON=$(which python3) cargo run --bin stub_gen -p czsc-python --no-default-features
- git diff --exit-code czsc/_native.pyi 断言无漂移,
  否则失败并把本地重新生成命令打到日志里

本地验证:连续跑两次 stub_gen 产物一致(idempotent)。

覆盖两个回归方向:
(a) 改了 #[gen_stub_pyclass] / pyfunction / pymethods 装饰器但忘重跑
    -> CI 红灯阻拦
(b) 手改了 stub 但 Rust 没改 -> 下次 CI 复跑时把手改盖掉、提示提交方处理
之前 czsc/sensors/ 仅有 15 行占位 __init__.py,与 spec §9 "完整保留 3 文件
301 行(含 CTAResearch)"差距明显。本次:

- 从 git 历史 79bdf5e:czsc/sensors/utils.py 恢复 utils.py(121 行,含
  holds_concepts_effect / turn_over_rate / max_draw_down 三个纯 numpy
  / pandas 工具;无内部 czsc 依赖,可直接复用)
- 同步恢复 utils.pyi
- 重写 __init__.py:
  * 暴露 3 个 utility 函数到 czsc.sensors.* 顶层
  * 添加 CTAResearch 占位类,__init__ 直接抛 NotImplementedError,
    明确告知历史实现依赖已删的 czsc.traders.dummy.DummyBacktest
    (spec §3.3 删除),引导用户改用 czsc.run_replay / wbt.WeightBacktest
    组合,并指向 MIGRATION_NOTES §10.2 中的恢复计划

公共 API 影响:from czsc.sensors import {CTAResearch, holds_concepts_effect,
turn_over_rate, max_draw_down} 全部可用;CTAResearch() 实例化即
fail-fast,不会让代码运行半截才报 ImportError。

spec §9 "完整保留" 项剩余的就只是 cta.py 真实迁移(需先在 Rust 端
czsc-trader 提供等价 dummy/replay)。

测试:124 项全量 sweep 仍 GREEN。
… / 200ms)

闭合 spec §6 P1 验收:10 万根 K 线做完整 CZSC 分析(分型/笔/中枢识别)≤ 200 ms。

新增 crates/czsc-core/benches/czsc_analyze_bench.rs:
- 用 criterion 0.7 做基准(lto=true / opt-level=3 / codegen-units=1 配置由
  workspace [profile.release] 提供)
- 慢周期正弦 + 快周期抖动 + 渐进漂移生成 10 万根 30 分钟模拟 K 线
  (避免单调推高/降低让缠论分析路径退化)
- iter_batched(BatchSize::LargeInput) 测 CZSC::new(bars, max_bi_num=50)
- 20 样本

实测(M2 Mac,release):
  czsc_analyze/CZSC::new(bars=100000, max_bi_num=50)
                          time:   [96.276 ms 96.585 ms 96.971 ms]

96.585 ms vs spec §6 P1 目标 200 ms,余量 52%,P1 达标 ✅。

Cargo.toml 加 criterion = "0.7" 到 [dev-dependencies] + [[bench]] name = "czsc_analyze_bench" harness = false。
触发命令:cargo bench -p czsc-core
…gnal, 4.7ms/10K)

闭合 spec §6 P2 验收:
- 30+ 信号函数批量执行单根 K 线 P50 ≤ 50 µs / 信号
- 批量 1 万根 ≤ 80 ms

新增 crates/czsc-signals/benches/signals_bench.rs:
- 复用 P1 同款慢周期正弦 + 快周期抖动 + 渐进漂移 K 线生成器(独立 copy 避免
  跨 crate dev-dep)
- 构造 100 / 10 000 两个 size 的 CZSC,循环调用 SIGNAL_REGISTRY 中全部 222
  个 K 线信号各一次(spec 说"30+"是当时的下限估计,实际仓库内已注册 222 个)
- 用 black_box 阻止死代码消除

实测(M2 Mac,release):
  signals_dispatch/dispatch_all(222 signals, bars=100)
                          time:   [242.86 µs 244.20 µs 245.52 µs]
  signals_dispatch/dispatch_all(222 signals, bars=10000)
                          time:   [4.6427 ms 4.6820 ms 4.7254 ms]

- 244.2 µs / 222 signals ≈ 1.1 µs/signal vs spec ≤ 50 µs ⇒ 余 45×, P2 (单根) ✅
- 4.682 ms vs spec ≤ 80 ms ⇒ 余 17×, P2 (批量 1 万根) ✅

Cargo.toml 加 criterion = "0.7" 到 [dev-dependencies] + [[bench]] name = "signals_bench"。
触发命令:cargo bench -p czsc-signals
- Simplified list and dictionary assertions in various test files for clarity.
- Removed unnecessary line breaks and whitespace to enhance code cleanliness.
- Consolidated multi-line assertions into single lines where appropriate.
- Ensured consistent formatting across test cases, including parameterized tests.
- Updated comments and docstrings for better understanding of test purposes.
- Reordered imports in `test_fx.rs` for consistency.
- Simplified cloning in `test_mark_direction.rs` by directly assigning values.
- Enhanced readability of assertions in `test_signal.rs` by formatting multi-line assertions.
- Improved formatting and readability in `test_zs.rs` by aligning parameters and breaking long lines.
- Cleaned up function signatures in `lib.rs` and `signals_dispatcher.rs` for better clarity.
- Removed unnecessary `into_iter()` calls in `signals_dispatcher.rs` and `trader/api.rs`.
- Streamlined parameter handling in `trader/czsc_signals.rs` for better readability.
- Enhanced readability in `trader/czsc_trader.rs` by breaking long lines and improving indentation.
- Improved error handling and readability in `czsc-signals/src` files by using more concise patterns.
- Cleaned up unnecessary braces and improved formatting in various signal functions.
- Enhanced readability in `czsc-ta/src/python.rs` by breaking long function signatures into multiple lines.
- Improved assertions in tests for better clarity and consistency.
- Refactored `bar_generator.rs` and `mod.rs` for better readability and consistency in function signatures.
- 翻译 ~294 行 Rust 英文注释为中文,覆盖 czsc-{core,python,utils,ta,signals,
  trader,signal-macros} 与 error-support;保留 API/类型名、设计文档锚点、
  注释掉的代码块、Python 行号引用、数学公式不变。
- czsc/_native.pyi 同步重生成(pyo3-stub-gen 把 __reduce__ 的中文 doc
  嵌入 stub 后产生 18 行漂移,与 Rust 源同步提交以满足 stub-drift CI)。
- .github/workflows/code-quality.yml 去掉 cargo clippy 的 `|| true`,
  让 -D warnings 真正阻塞 CI(之前任何 clippy 错误都被静默吞掉)。

回归验证:cargo fmt/clippy/test (per-crate, 122 passed) +
ruff check/format + maturin develop + pytest (212 passed, 1 skipped)
全部通过;业务代码零改动。
@zengbin93 zengbin93 merged commit 4009c27 into master May 8, 2026
10 checks passed
@zengbin93 zengbin93 deleted the feat/rust-czsc-migration branch May 8, 2026 09:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants