为 PyO3 项目自动生成 Python 类型存根:nautilus_trader 中的 pyo3-stub-gen 实战指南
【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_trader
导读
patches/pyo3-stub-gen/README.md记录了 Jij Inc. 开源的pyo3-stub-gencrate——一个面向 PyO3 与 maturin 项目的 Python 类型存根(*.pyi)生成器。它通过半自动化的方式,把 Rust 侧的 PyO3 绑定自动翻译成 Python 类型存根,并在默认翻译器无法覆盖的边缘场景提供手工覆写能力。在 nautilus_trader 仓库中,这套机制被完整落地:nautilus-pyo3crate 把全工作区各 crate 的 Python 绑定聚合到nautilus_trader._libnautilus单一扩展模块,再由pyo3-stub-gen生成整套python/nautilus_trader/下的.pyi存根。读完本文,你将掌握如何为 PyO3 项目接入 stub 生成、如何使用#[gen_stub_*]系列过程宏、如何通过python参数 /override_type/submit!手工修正复杂类型、如何用type_alias!定义语义化类型别名,以及如何集成mypy stubtest校验与 Sphinx 文档生成,最后还能看到 nautilus_trader 在生产规模下的工程化实践。
设计动机:为什么需要"半自动化"翻译
Rust 与 Python 的类型系统存在根本差异,加之过程宏的能力边界,从 Rust 代码自动、完整地翻译出 Python 存根在原理上并不可行。因此pyo3-stub-gen采用半自动化策略(对应 README Design 一节):
- 提供一套默认翻译器,覆盖"大多数"(
most)而非"全部"(all)场景; - 同时提供手工指定翻译的途径,让用户只对默认翻译不生效的边缘情况做局部修正;
- 手工翻译可以与默认翻译器的产物合并集成,从而最大化默认翻译器的复用率。
整个 crate 家族分为两个部分:
pyo3-stub-gen:提供手工指定翻译的运行时机制(StubInfo、inventory注册、type_alias!等);pyo3-stub-gen-derive:基于pyo3-stub-gen的机制实现默认翻译器——即#[gen_stub_pyfunction]、#[gen_stub_pyclass]、#[gen_stub_pymethods]等过程宏。
从仓库内的 Cargo.toml 可以看到,pyo3-stub-gen0.20.0 的默认 feature 为either、infer_signature、numpy、ordered-float,另有可选的rust_decimal;依赖上通过pyo3 >= 0.26、inventory 0.3、numpy、serde等支撑类型翻译与信息收集,并用toml读取pyproject.toml配置。
[!NOTE] 最低支持 Python 版本为3.10,不要在 PyO3 配置中启用 3.9 或更早版本。
版本 0.15.0–0.17.1 曾无意中包含一个 LGPL 依赖,该依赖在 0.17.2 中移除,受影响版本已被 yank。
快速上手:给 PyO3 函数生成第一个.pyi
用过程宏注解 Rust 代码
pyo3-stub-gen提供#[gen_stub_pyfunction]等过程宏,与 PyO3 的#[pyfunction]配合使用。一个最简单的 PyO3 项目原本如下:
use pyo3::prelude::*; #[pyfunction] fn sum_as_string(a: usize, b: usize) -> PyResult<String> { Ok((a + b).to_string()) } #[pymodule] fn your_module_name(m: &Bound<PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(sum_as_string, m)?)?; Ok(()) }接入 stub 生成只需三处修改(对应 README Usage):
use pyo3::prelude::*; use pyo3_stub_gen::{derive::gen_stub_pyfunction, define_stub_info_gatherer}; #[gen_stub_pyfunction] // Proc-macro attribute to register a function to stub file generator. #[pyfunction] fn sum_as_string(a: usize, b: usize) -> PyResult<String> { Ok((a + b).to_string()) } #[pymodule] fn your_module_name(m: &Bound<PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(sum_as_string, m)?)?; Ok(()) } // Define a function to gather stub information. define_stub_info_gatherer!(stub_info);其中define_stub_info_gatherer!(stub_info)会生成一个名为stub_info的函数,用于在运行时收集所有被注册的 stub 信息。
[!NOTE]
#[gen_stub_pyfunction]宏必须放在#[pyfunction]宏之前。
nautilus_trader 仓库里,nautilus-pyo3crate(crates/pyo3/Cargo.toml)正是这种模式的产品级实例:它以lib.name = "nautilus_pyo3"、crate-type = ["rlib", "cdylib"]聚合全工作区各 crate 的pythonfeature 绑定,再通过#[pymodule] fn _libnautilus(...)(见 crates/pyo3/src/lib.rs#L79-L307)把 analysis、core、common、model、trading、backtest 以及 binance、bybit、okx 等 20 余个适配器子模块逐一wrap_pymodule!后挂载到nautilus_trader._libnautilus命名空间,并写入sys.modules使它们可作为子模块导入。
生成存根文件的可执行目标
接着创建一个可执行目标src/bin/stub_gen.rs,调用stub_info()并触发生成(对应 README 对应小节):
use pyo3_stub_gen::Result; fn main() -> Result<()> { // `stub_info` is a function defined by `define_stub_info_gatherer!` macro. let stub = pure::stub_info()?; stub.generate()?; Ok(()) }在Cargo.toml的[lib]段同时声明rlib与cdylib:
[lib] crate-type = ["cdylib", "rlib"]然后运行:
cargo run --bin stub_gen该命令会在项目根生成存根文件(如pure.pyi)。maturin 会自动发现该存根并打包进 wheel——对应 maturin 的 "adding python type information" 项目布局约定。
nautilus_trader 中的对应实现位于 crates/pyo3/bin/stub_gen.rs,且[bin] name = "python-stub-gen"在 crates/pyo3/Cargo.toml#L176-L181 中声明。nautilus_pyo3::stub_info()(见 crates/pyo3/src/lib.rs#L326-L335)与文档示例略有不同:它不依赖 crate 内部的 pyproject,而是向上定位工作区根目录并显式加载python/pyproject.toml,再调用pyo3_stub_gen::StubInfo::from_pyproject_toml(&pyproject_path)完成初始化——这正是多 crate 工作区聚合绑定的必然形态。
#[gen_stub(skip)]:从存根中排除目标
对不想出现在.pyi文件中的函数或方法,使用#[gen_stub(skip)]:
use pyo3::prelude::*; use pyo3_stub_gen::derive::*; #[gen_stub_pyclass] #[pyclass] struct MyClass; #[gen_stub_pymethods] #[pymethods] impl MyClass { #[gen_stub(skip)] fn internal_method(&self) { // This method will not appear in the .pyi file } }#[gen_stub(default = xx)]:为 getter/setter/类属性指定默认值
对 getter、setter 与类属性,可以用default参数指定会出现在存根中的默认值:
use pyo3::prelude::*; use pyo3_stub_gen::derive::*; #[gen_stub_pyclass] #[pyclass] struct Config { #[pyo3(get, set)] #[gen_stub(default = Config::default().timeout)] timeout: usize, } impl Default for Config { fn default() -> Self { Config { timeout: 30 } } } #[gen_stub_pymethods] #[pymethods] impl Config { #[getter] #[gen_stub(default = Config::default().timeout)] fn get_timeout(&self) -> usize { self.timeout } }手工覆写:默认翻译器不够用时
当自动类型翻译结果不理想时,可用 Python 存根语法手工指定类型,共有两条主线、三种方法(对应 README Manual Overriding):
方法一:python参数整体覆写签名
当需要完整替换函数签名的 Python 表达时,用#[gen_stub_pyfunction(python = "...")]:
use pyo3::prelude::*; use pyo3_stub_gen::derive::*; #[gen_stub_pyfunction(python = r#" import collections.abc import typing def fn_with_callback(callback: collections.abc.Callable[[str], typing.Any]) -> collections.abc.Callable[[str], typing.Any]: """Example using python parameter for complete override.""" "#)] #[pyfunction] pub fn fn_with_callback<'a>(callback: Bound<'a, PyAny>) -> PyResult<Bound<'a, PyAny>> { callback.call1(("Hello!",))?; Ok(callback) }该方式的特点:
- ✅ 对生成的存根拥有完整控制权;
- ✅ 支持
collections.abc.Callable等复杂类型; - ✅ 允许附带自定义 docstring;
- ✅ import 语句会被自动抽取。
方法二:#[gen_stub(override_type(...))]局部覆写
当大多数类型翻译正确、仅个别参数需要调整时,用override_type覆写参数、用override_return_type覆写返回值:
use pyo3::prelude::*; use pyo3_stub_gen::derive::*; #[gen_stub_pyfunction] #[pyfunction] #[gen_stub(override_return_type(type_repr="collections.abc.Callable[[str], typing.Any]", imports=("collections.abc", "typing")))] pub fn get_callback<'a>( #[gen_stub(override_type(type_repr="collections.abc.Callable[[str], typing.Any]", imports=("collections.abc", "typing")))] cb: Bound<'a, PyAny>, ) -> PyResult<Bound<'a, PyAny>> { Ok(cb) }该方式的特点:
- ✅ 对单个类型进行细粒度控制;
- ✅ 其余参数仍保持自动生成;
- ✅ 明确标注哪些类型需要手工指定。
方法三:submit!宏独立提交定义
#[gen_stub_pyfunction]与#[gen_stub_pyclass]内部会自动生成submit!块来注册类型信息;你也可以手工追加submit!块补充或覆写自动注册。当同一函数/方法存在多个类型签名时,生成器会自动在.pyi中输出@overload装饰器,让类型检查器正确识别多签名函数。
实现函数重载有两种途径:
python_overload参数:随函数内联定义重载;submit!块:独立维护 stub 定义——尤其适合过程宏/代码生成场景。
函数重载(内联方式):
use pyo3::prelude::*; use pyo3_stub_gen::derive::*; // Define overloads inline with python_overload parameter #[gen_stub_pyfunction( python_overload = r#" @overload def process(x: int) -> int: """Process integer input""" "# )] #[pyfunction] pub fn process(x: f64) -> f64 { x + 1.0 }生成的存根:
@overload def process(x: int) -> int: """Process integer input""" @overload def process(x: float) -> float: ... # Auto-generated from Rust用no_default_overload = true抑制自动生成(当运行时做动态类型分发、Rust 签名不具代表性时):
use pyo3::prelude::*; use pyo3_stub_gen::derive::*; #[gen_stub_pyfunction( python_overload = r#" @overload def func(x: int) -> int: ... @overload def func(x: str) -> str: ... "#, no_default_overload = true // Don't generate from Rust signature )] #[pyfunction] pub fn func(ob: Bound<PyAny>) -> PyResult<PyObject> { // Runtime type checking todo!() }类方法重载(submit!方式):
use pyo3::prelude::*; use pyo3_stub_gen::{derive::*, inventory::submit}; #[gen_stub_pyclass] #[pyclass] pub struct Calculator {} #[gen_stub_pymethods] #[pymethods] impl Calculator { fn add(&self, x: f64) -> f64 { x + 1.0 } } // Alternative: Use submit! for method overloads (useful for proc-macro/code generation) submit! { gen_methods_from_python! { r#" class Calculator: @overload def add(self, x: int) -> int: """Add integer (overload)""" "# } }submit!体系的核心收益:
- ✅ 用
python_overload参数内联定义重载; - ✅ 自动生成
@overload装饰器; - ✅ 基于索引排序保证确定性顺序;
- ✅ 为过程宏/代码生成场景提供
submit!语法。
更进阶的类方法模式(完全手工提交方法签名、以及用#[gen_stub(skip)]混合过程宏与手工提交)可参考 README 指向的examples/pure/src/manual_submit.rs与 "Python Stub Syntax Support" 文档。
进阶:RustType标记在 Python 语法中引用 Rust 类型
在 Python stub 语法内部,可以直接用pyo3_stub_gen.RustType["TypeName"]引用 Rust 类型,其展开依赖该 Rust 类型对PyStubTypetrait 的实现:
use pyo3::prelude::*; use pyo3_stub_gen::{derive::*, inventory::submit}; #[pyfunction] pub fn sum_list(values: Vec<i32>) -> i32 { values.iter().sum() } submit! { gen_function_from_python! { r#" def sum_list(values: pyo3_stub_gen.RustType["Vec<i32>"]) -> pyo3_stub_gen.RustType["i32"]: """Sum a list of integers""" "# } }RustType标记会自动展开为合适的 Python 类型:
RustType["Vec<i32>"]→typing.Sequence[int](用于参数);RustType["i32"]→int(用于返回值)。
它尤其适用于:Vec<T>、HashMap<K, V>等泛型;实现了PyStubType的自定义类型;以及需要保证 Rust 与 Python 类型映射一致性的场景。
何时选用哪种方法
| 场景 | 推荐方法 |
|---|---|
复杂类型(如Callable、Protocol) | 方法一:python = "..."参数 |
| 覆写一两个参数 | 方法二:#[gen_stub(override_type(...))] |
函数重载(@overload) | python_overload = "..."参数 |
| 在 Python 语法中引用 Rust 类型 | RustType["..."]标记 |
| 完整替换函数签名 | 方法一:python = "..."参数 |
类型别名:type_alias!与gen_type_alias_from_python!
类型别名可以为复杂或频繁使用的类型定义语义化名称,提升存根可读性与可维护性(对应 README Type Aliases)。
基础用法
use pyo3_stub_gen::type_alias; use std::collections::HashMap; // Simple type alias type_alias!("your_module", SimpleAlias = Option<usize>); // Collection types type_alias!("your_module", StrIntMap = HashMap<String, i32>); // Nested option types type_alias!("your_module", MaybeString = Option<Option<String>>);直接联合类型语法
type_alias!支持直接书写联合类型,大多数情况下无需单独的impl_stub_type!声明:
use pyo3_stub_gen::type_alias; // Simple union types type_alias!("your_module", NumberOrStringAlias = i32 | String); // Multiple types in a union type_alias!("your_module", TripleUnion = i32 | String | bool); // Unions of generic types type_alias!("your_module", GenericUnion = Option<i32> | Vec<String>); // Complex nested unions type_alias!("your_module", ComplexUnion = Option<Vec<i32>> | Option<Vec<String>>);备选:与impl_stub_type!组合复用
若同一联合类型需要在多处引用,仍可采用两步式impl_stub_type!+type_alias!模式:
use pyo3_stub_gen::{impl_stub_type, type_alias}; // Define a reusable union type struct NumberOrString; impl_stub_type!(NumberOrString = i32 | String); // Use it in multiple type aliases type_alias!("your_module", NumberOrStringAlias = NumberOrString); type_alias!("your_module", AnotherAlias = NumberOrString);生成输出与语法配置
类型别名在 Python 存根中以TypeAlias注解渲染(兼容 Python 3.11+):
from typing import TypeAlias __all__ = [ "MaybeDecimal", "NumberOrStringAlias", "SimpleAlias", "StrIntMap", "StructUnion", ] MaybeDecimal: TypeAlias = typing.Optional[DecimalHolder] NumberOrStringAlias: TypeAlias = builtins.int | builtins.str SimpleAlias: TypeAlias = typing.Optional[builtins.int] StrIntMap: TypeAlias = builtins.dict[builtins.str, builtins.int] StructUnion: TypeAlias = ComparableStruct | HashableStruct默认生成的是 Python 3.12 之前的TypeAlias语法;若项目面向 Python 3.12+,可在pyproject.toml中开启type语句语法:
[tool.pyo3-stub-gen] use-type-statement = true输出将变为:
type MyAlias = int | str[!NOTE] 使用
use-type-statement = true时,请确保项目最低 Python 版本为 3.12 或更高——type语句在更早版本中不可用。
用 Python 语法定义复杂别名
需要 Python 特定语法(如特定 import、复杂泛型、难以用PyStubType表达的类型)时,可用gen_type_alias_from_python!:
use pyo3_stub_gen::derive::gen_type_alias_from_python; gen_type_alias_from_python!( "your_module", r#" import collections.abc from typing import TypeAlias CallbackType: TypeAlias = collections.abc.Callable[[str], None] "# );解析器同时接受 3.12 前后两种语法,输出格式统一由use-type-statement配置控制。
类型别名是"仅存根"(stub-only)构造,运行时并不存在,纯粹服务于静态类型检查与 IDE 支持;同时它天然兼容自动__all__生成以及 mypy、pyright、ruff 等类型检查器。
质量保障:mypy stubtest 集成
mypy stubtest):
uv run stubtest your_module_name --ignore-missing-stub --ignore-disjoint-basesPyO3/maturin 项目必需的标志
--ignore-missing-stub:maturin 会创建内部原生模块(.so文件)并向__init__.py再导出;stubtest 会为这些内部模块寻找存根,但实际并不存在(所有类型都在__init__.pyi中),该标志用于消除此类误报。--ignore-disjoint-bases:PyO3 类在运行时属于 disjoint bases,而 pyo3-stub-gen 不会生成@typing.disjoint_base装饰器。
已知限制:嵌套子模块
stubtest 不适用于 PyO3 嵌套子模块。嵌套的#[pymodule]在运行时创建的是属性(attribute)而非可导入模块,而存根文件使用目录结构组织。对于含嵌套子模块的项目,需要对这些包禁用 stubtest。
nautilus_trader 的 python/generate_stubs.py 在生成完成后执行了一整套与之配套的存根后处理(post_process_stubs):修正文件头(# This file is automatically generated by pyo3_stub_gen)、按rename_all = "SCREAMING_SNAKE_CASE"重命名枚举变体、py_new -> __init__等方法重命名、从#[pyo3(signature = (...))]属性还原真实默认值(并安全地抑制"必选参数出现在带默认参数之后"的非法签名)、为缺失默认值的Optional参数补= None、对后向引用局部类的默认值替换为...以避免类体内非法运行时表达式、注入手写 Python 符号的再导出(如ReportProvider、TearsheetChart等)、以及剥离 docstring(运行时__doc__才是唯一事实来源)。这些正是 stubtest 能在 nautilus_trader 数千个绑定上通过的必要工程支撑。
进阶:用同一份元数据生成 Sphinx API 文档
除了存根,pyo3-stub-gen 还能用同一份 Rust 类型元数据生成 Sphinx)。
配置
在pyproject.toml中添加[tool.pyo3-stub-gen.doc-gen]段:
[tool.pyo3-stub-gen.doc-gen] output-dir = "docs/api" json-output = "api_reference.json" index-title = "API Reference"src/bin/stub_gen.rs无需任何改动——只要存在该配置段,stub.generate()就会自动生成文档。可用选项:
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
output-dir | Path | "docs/api" | 生成文件输出目录(相对pyproject.toml) |
json-output | String | "api_reference.json" | JSON 数据文件名 |
separate-pages | Boolean | true | 每个模块生成独立.rst页面 |
index-title | String | "{package} API Reference" | index.rst标题 |
intro-message | String | (默认简介文本) | index.rst的简介文字(空字符串则省略) |
contents-table | Boolean | false | 显示模块内容汇总表 |
Sphinx 搭建
在开发依赖中加入 Sphinx 相关包:
[dependency-groups] dev = ["myst-parser", "sphinx", "sphinx-rtd-theme"]创建docs/conf.py加载生成的扩展:
import sys from pathlib import Path # Add the API docs directory so Sphinx can find the generated extension sys.path.insert(0, str(Path(__file__).parent / "api")) project = "your_project" extensions = [ "pyo3_stub_gen_ext", # Generated extension — reads api_reference.json "sphinx.ext.intersphinx", # Enables cross-references to external projects ] intersphinx_mapping = { "python": ("https://docs.python.org/3", None), } html_theme = "sphinx_rtd_theme"生成文件与构建
运行cargo run --bin stub_gen会在output-dir产出:
api_reference.json—— 结构化 API 数据(JSON 中间表示);pyo3_stub_gen_ext.py—— 把 JSON 渲染为文档的 Sphinx 扩展;index.rst—— 含 toctree 的目录页(separate-pages = true时);<module>.rst—— 每个模块一页(separate-pages = true时)。
每个模块.rst只含一个由扩展展开的指令:
pure ==== .. pyo3-api:: pure构建命令:
cargo run --bin stub_gen # Generate API data + Sphinx files uv run --with sphinx sphinx-build -W -b html docs docs/_build # Build HTML生成的扩展提供两个 RST 指令:
.. pyo3-api:: module_name—— 渲染单个模块的 API 参考;.. pyo3-api-package:: package_name—— 渲染包内所有模块。
Rust 文档注释(/// ...)会渲染为 MyST Markdown,支持交叉引用(:class:`ClassName`)、代码块、admonition 等 Sphinx/MyST 特性,因此myst-parser是必需的。README 还提示了单模块项目(examples/pure/docs/)与含子模块的多模块项目(examples/mixed/docs/)两套完整参考实现。
生产级实践:nautilus_trader 如何落地 stub 生成
nautilus_trader 将上述机制规模化,形成了一条可复现的存根生产线:
- 聚合绑定:
nautilus-pyo3(crates/pyo3/src/lib.rs)把所有 crate 的 Python 绑定收拢到单一_libnautilus扩展模块,并在 crates/pyo3/Cargo.toml#L20-L21 声明crate-type = ["rlib", "cdylib"],同时满足 stub 生成(rlib)与 wheel 构建(cdylib)两种用途。 - 生成入口:
cargo run --bin python-stub-gen(crates/pyo3/bin/stub_gen.rs)调用nautilus_pyo3::stub_info(),后者显式读取 python/pyproject.toml 并构造StubInfo(crates/pyo3/src/lib.rs#L326-L335)。 - 构建集成:
python/pyproject.toml的[tool.maturin]指向../crates/pyo3/Cargo.toml、module-name = "nautilus_trader._libnautilus",并通过include = ["**/*.pyi"]把生成的存根打进 wheel;[build-system]使用maturin==1.15.0。 - 脚本驱动与后处理:python/generate_stubs.py 既可作为独立脚本(
python generate_stubs.py),也可作为自定义构建脚本(python generate_stubs.py build);它解析[tool.maturin]的 feature 列表(剔除extension-module并补入nautilus-interactive-brokers/gateway)后调用 stub 生成器,再执行类重定位(如把_libnautilus中的AmmType、Dex、Chain等迁回model模块)、__all__同步、配置门面存根生成、格式化与陈旧存根清理。 - 兼容性补丁:由于上游
pyo3-stub-gen 0.20.0之后版本拒绝pymodule根之外的模块路径,而本仓库的gen_stub_*注解指向nautilus_trader包路径(位于nautilus_trader._libnautilus根之外),patches/README.md 明确说明该 crate 被固定在 0.20.0,并通过本地补丁适配pyo3 0.29.0:src/util.rs中用cast::<T>()替换三处已移除的Bound<PyAny>::downcast::<T>()调用,src/exception.rs移除PyEnvironmentError/PyIOError的 stub 类型实现(PyO3 0.29 将两者别名到PyOSError,保留会造成重复 trait impl)。补丁刻意不改变存根布局、类重定位、模块命名与签名规范化等行为——这些仍由python/generate_stubs.py与固定的 0.20.0 代码控制。
这套链路回答了"一个工作区、几十个 crate、上千个绑定类如何产出可信的.pyi"这一问题:过程宏负责自动化翻译,python/override_type/submit!/type_alias!负责边缘修正,stubtest 负责校验,Sphinx doc-gen 负责文档化,而generate_stubs.py负责把这一切编排进构建管线。
结语与进一步阅读
pyo3-stub-gen的价值在于把"Python 类型存根"从手写维护的负担转化为"从 Rust 绑定半自动生成 + 边缘手工修正"的工程流程。其设计(默认翻译器 + 手工覆写 + 元数据合并)与 nautilus_trader 的规模化实践(聚合 crate、脚本化后处理、版本锁定与兼容性补丁)共同证明:即便面对 Rust/Python 类型系统差异与过程宏局限,仍然可以获得类型完整、IDE 友好、经 stubtest 校验的 Python API 层。
可以继续深入阅读的仓库文件:
- patches/pyo3-stub-gen/README.md —— 本文的直接来源(用法、重载、类型别名、stubtest、Sphinx 文档生成);
- patches/README.md —— 本仓库为何固定 0.20.0 并打补丁的决策记录;
- patches/pyo3-stub-gen/src/lib.rs 与 patches/pyo3-stub-gen/src/stub_type.rs —— 核心
StubInfo与类型翻译实现; - crates/pyo3/src/lib.rs 与 crates/pyo3/bin/stub_gen.rs —— 聚合绑定与生成入口;
- python/generate_stubs.py —— 存根后处理与构建编排;
- python/pyproject.toml —— maturin 与 stub 打包配置。
【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_trader
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考