Skip to content

安全与执行限制

将 Navi 嵌入宿主应用程序时,用户提供的 Navi 脚本会在 VM 内执行。VM 提供可配置的限制,以保护宿主进程不受失控脚本的影响。

为何需要限制

Navi 脚本可以包含循环(forwhilefor..in),其迭代次数可能根据用户输入或数据任意增加。若无限制,恶意或有缺陷的脚本可能导致宿主进程无限期挂起。Navi 的执行限制按 Bar 检查,因此开销可预测。

配置限制

在调用 build() 前,将 ExecutionLimits 传入 InstanceBuilder::with_execution_limits

rust
use navi_vm::{ExecutionLimits, Instance, TimeFrame};

let limits = ExecutionLimits::default()
    .with_max_loop_iterations_per_bar(1_000_000);

let mut instance = Instance::builder(source, TimeFrame::days(1), "NASDAQ:AAPL")
    .with_execution_limits(limits)
    .build().await?;

超出限制时,VM 会抛出运行时错误(Error::Exception),处理方式与其他运行时异常相同。

可用限制

max_loop_iterations_per_bar

属性
默认值500,000
作用域所有 forwhilefor..in 循环在每个 Bar 内共享同一预算
重置时机每根 K 线执行开始时计数器重置
禁用方式设置为 u64::MAX 可关闭限制

同一 Bar 内所有循环类型共享同一预算。例如,脚本中两个嵌套 for 循环各迭代 1,000 次,则共消耗 1,000,000 次迭代——超过默认预算 500,000。

rust
// 为计算密集型脚本提高限制
let limits = ExecutionLimits::default()
    .with_max_loop_iterations_per_bar(2_000_000);

// 完全禁用限制(不建议用于不受信任的脚本)
let limits = ExecutionLimits::default()
    .with_max_loop_iterations_per_bar(u64::MAX);

max_security_depth

属性
默认值3
作用域request.security() 调用的最大嵌套深度
禁用方式设置为 0 可完全禁止 request.security()

控制 request.security() 的最大嵌套层数。深度为 1 表示 security 表达式内部不得再调用 request.security()

rust
// 仅允许一层 request.security()(不允许嵌套)
let limits = ExecutionLimits::default()
    .with_max_security_depth(1);

// 完全禁止 request.security()
let limits = ExecutionLimits::default()
    .with_max_security_depth(0);

max_security_calls

属性
默认值40
作用域所有 request.security() 调用点中,唯一请求流 key 的最大数量
禁用方式设置为 0 可完全禁止 request.security()

每个不同的 (symbol, timeframe, calc_bars_count) 组合都会创建一个独立的数据流。共享同一完整 key 的多个调用点只计一次。

rust
// 在资源受限的环境中收紧限制
let limits = ExecutionLimits::default()
    .with_max_security_calls(10);

max_bars_back

属性
默认值1000
作用内存中保留的序列变量和 K 线数据的最大历史 Bar 数(硬上限)
超限行为最早的 Bar 被丢弃;close[N]N >= max_bars_back 时返回 na
rust
// 减少长时间运行脚本的内存占用
let limits = ExecutionLimits::default()
    .with_max_bars_back(500);

// 为需要引用深层历史的脚本保留更多数据
let limits = ExecutionLimits::default()
    .with_max_bars_back(5000);

脚本可以在此硬上限范围内调整历史缓冲区:

  • indicator(max_bars_back=N) / strategy(max_bars_back=N) — 设置脚本级别的默认值。
  • max_bars_back(variable, N) — 设置特定变量或内置序列(closeopen 等)的缓冲区。

两者均受 ExecutionLimits.max_bars_back 约束,不能超过该上限。

处理超限错误

超限错误以 Error::Exception 的形式抛出,与 runtime.error() 及其他运行时错误类型相同:

rust
match instance.run_to_end("NASDAQ:AAPL", TimeFrame::days(1)).await {
    Err(navi_vm::Error::Exception(e)) => {
        eprintln!("脚本错误:{}", e.display());
    }
    _ => {}
}

有关处理运行时错误的完整说明,请参阅错误处理

建议

  • 不受信任的脚本:保持默认限制或将其调低。如无需使用 request.security(),可将 max_security_depth 设为 10
  • 已知脚本:仅在确认脚本确实需要更多资源时才提高限制。
  • 服务端执行:使用 tokio::timeout 包装执行调用作为二次保障,因为循环限制仅防止过多迭代。
rust
use std::time::Duration;
use tokio::time::timeout;

let result = timeout(
    Duration::from_secs(30),
    instance.run_to_end("NASDAQ:AAPL", TimeFrame::days(1)),
).await;

下一步

基于 MIT 许可证发布。