Skip to content

实例构建器

InstanceBuilder 配置并将 Navi 脚本编译为可执行的 Instance

基本用法

rust
use navi_vm::{Instance, TimeFrame};

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

instance.run_to_end("NASDAQ:AAPL", TimeFrame::days(1)).await?;

数据提供者

Instance::builder() 的第一个参数是 DataProvider,服务于两个目的:

  1. 主 chart K 线Instance::run() 从中消费数据驱动逐 bar 执行
  2. request.security() 数据 — 多周期和跨标的 K 线流

Vec<Candlestick> 直接实现了 DataProvider,无需任何包装即可传入:

rust
let mut instance = Instance::builder(bars, source, timeframe, "NASDAQ:AAPL")
    .build().await?;
instance.run_to_end("NASDAQ:AAPL", timeframe).await?;

多标的或实时数据,实现 DataProvider

rust
let mut instance = Instance::builder(MyProvider::new(), source, timeframe, "NASDAQ:AAPL")
    .build().await?;
instance.run_to_end("NASDAQ:AAPL", timeframe).await?;

标的字符串作为最后一个参数传入;标的元数据由 provider 自动解析。

检查脚本信息

使用独立函数 script_info() 检查脚本的元数据,无需创建可执行实例,也不需要数据提供者、品种或周期:

rust
use navi_vm::script_info;

let info = script_info(source)?;

println!("Script type: {:?}", info.script_type);
println!("Is overlay: {}", info.overlay());
println!("Inputs: {} total", info.inputs.len());

for input in &info.inputs {
    // 检查输入定义
}

在构建实例之前可用此函数查询输入 ID(详见输入配置)。

构建器方法

InstanceBuilder(来自 Instance::builder)在 build() 时编译源码。 InstanceBuilderFromProgram(来自 Instance::builder_from_program)跳过编译。 编译期选项仅在 InstanceBuilder 上可用。

方法说明builderbuilder_from_program
with_locale数字/日期格式化的区域设置
with_input_value按 ID 覆盖脚本输入
with_input_sessions过滤处理的交易时段
with_strategy_config_override在实例层面覆盖 strategy() 参数
with_background_color自定义图表背景颜色
with_last_info最后一根 K 线的索引和时间戳
with_execution_limits循环迭代次数和 security 调用次数限制
with_mtf_wait实时阶段等待 request.security() MTF 数据就绪的最大时长
with_output_mode系列图形输出模式(ChartStream
with_jit启用 JIT 编译(需要 jit feature)
with_library_loaderuse 语句的自定义加载器,支持链式 fallback

脚本元数据

with_path

为脚本设置文件路径(用于错误信息):

rust
let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .build().await?;

with_locale

设置数字和日期格式化的区域设置:

rust
let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .with_locale("zh")
    .build().await?;

输入配置

with_input_value

按 ID 覆盖默认输入值。输入 ID 从 0 开始按顺序分配,与脚本中 input.*() 调用的出现顺序一致。

如需查询各输入的 ID,可使用 script_info()(详见检查脚本信息):inputs 是按声明顺序排列的 Vec<Input>,索引即为 ID。

rust
// Navi 脚本:
// length = input.int(14, "RSI Length")    // input id 0
// src = input.source(close, "Source")     // input id 1

let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .with_input_value(0, 21)       // 将 RSI Length 覆盖为 21
    .build().await?;

with_input_sessions

控制处理哪些交易时段:

rust
use navi_vm::InputSessions;

let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .with_input_sessions(InputSessions::REGULAR | InputSessions::EXTENDED)
    .build().await?;
标志描述
InputSessions::REGULAR常规交易时段
InputSessions::EXTENDED延长/盘前/盘后交易
InputSessions::OVERNIGHT隔夜交易时段
InputSessions::ALL所有交易时段

策略配置

with_strategy_config_override

在不修改脚本源码的情况下,覆盖 strategy() 的部分参数。设为 Some(…) 的字段优先于脚本中声明的值;设为 None 的字段回退到脚本默认值。对非策略脚本无效。

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

let instance = Instance::builder(bars, source, TimeFrame::days(1), "NASDAQ:AAPL")
    .with_strategy_config_override(StrategyConfigOverride {
        initial_capital: Some(50_000.0),
        commission_value: Some(0.1),
        ..Default::default()
    })
    .build().await?;

StrategyConfigOverride 字段(均为 Option<T>,默认 None):

字段说明默认值
initial_capital初始账户资金1_000_000
commission_type佣金计算方式:percentcash_per_contractcash_per_orderpercent
commission_value佣金金额。percent 模式下为百分比数值(1 = 1%);cash_per_contractcash_per_order 模式下为每合约或每笔订单的绝对金额。0
margin_long多头保证金比率(%)100
margin_short空头保证金比率(%)100
slippage每笔成交滑点,单位:tick0
pyramiding同方向最大开仓次数0
risk_free_rate年化无风险利率(%),用于计算夏普/索提诺比率2
process_orders_on_close在 bar 收盘价成交,而非下一根 bar 开盘价false
calc_on_order_fills订单成交后重新执行脚本false
backtest_fill_limits_assumption限价单成交所需超出限价的额外 tick 数0
close_entries_rule优先平仓规则:FIFOANYFIFO

覆盖值存储在实例上,每次 reset() 时自动重新应用,不会因重置而丢失。

显示与环境

with_background_color

设置自定义背景颜色:

rust
use navi_vm::ast::Color;

let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .with_background_color(Color::new(255, 255, 255, 255))
    .build().await?;

with_last_info

提供最后一根可用 K 线的信息(用于 barstate.is_last):

rust
use navi_vm::LastInfo;

let last = LastInfo::new(499, 1700000000000);
let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .with_last_info(last)
    .build().await?;

执行限制

with_execution_limits

配置运行时限制,以防止脚本失控:

rust
use navi_vm::ExecutionLimits;

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

let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .with_execution_limits(limits)
    .build().await?;

若未调用此方法,默认每 Bar 最多 500,000 次循环迭代。

还可以通过 with_max_bars_back() 控制内存中保留多少根历史 Bar(默认:1000)。详情请参阅安全与执行限制

with_mtf_wait

设置每根实时 K 线执行前的固定等待时长(脚本含 request.security() 时生效):

rust
use std::time::Duration;

let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .with_mtf_wait(Duration::from_secs(3))
    .build().await?;

实时模式下,主 chart 流与 request.security() 子流由不同的异步任务驱动。新的 chart bar 到达时, 子流数据可能因网络延迟尚未就绪。若不等待,VM 会使用最后一个缓存值。

启用 with_mtf_wait 后,VM 在每根实时 bar 执行前先等待指定时长,让子流有时间推送最新数据,再执行脚本。

行为
Duration::ZERO(默认值)不等待——非阻塞行为
Duration::from_secs(3)每根实时 bar 前 sleep 3 秒——适合 3 秒内到达的数据源
其他时长固定 sleep,对所有 request.security() 子流统一生效

脚本中没有 request.security() 调用时,函数立即返回不执行 sleep,零性能开销。

库加载

with_library_loader

use 语句添加自定义库加载器。新 loader 被插入到解析链的最前面,失败后依次 回退到之前注册的 loader,内置 prelude/stdlib 始终作为最后的 fallback。

rust
use navi_loader::LibraryLoader;

struct MyLoader;

impl LibraryLoader for MyLoader {
    type Source = String;
    type Error = String;
    fn load(&self, path: &str) -> Result<(String, String), Self::Error> {
        // 返回 (源代码, 模块名) 或 Err
        todo!()
    }
}

let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .with_library_loader(MyLoader)
    .build().await?;

多次调用 with_library_loader 可构建优先级栈,每个新 loader 优先于之前注册的 loader:

rust
// 解析顺序:loader_b → loader_a → 内置 prelude/stdlib
let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .with_library_loader(loader_a)
    .with_library_loader(loader_b)
    .build().await?;

高级编译

编译一次,复用 Program

Instance::builder() 每次 build().await 都会重新编译源码。如果需要把同一个 Program 用于多个实例(例如不同的 provider 或不同的输入值),可先编译一次, 再用 Instance::builder_from_program 构造每个实例,避免重复编译:

rust
use navi_compiler::CompileOptions;
use navi_vm::{Instance, NativeFuncs, TimeFrame};

// 编译一次。
let program = CompileOptions::new(source)
    .with_library_loader(navi_builtins::builtins_loader())
    .with_load_options(navi_builtins::load_options())
    .with_native_func_registry(&NativeFuncs)
    .compile()?;

// 多个实例共享同一个 program,不再重新编译。
let instance_a = Instance::builder_from_program(
    program.clone(), provider_a, TimeFrame::days(1), "NASDAQ:AAPL",
)
.with_input_value(0, 14)
.build().await?;

let instance_b = Instance::builder_from_program(
    program.clone(), provider_b, TimeFrame::days(1), "NASDAQ:AAPL",
)
.with_input_value(0, 21)
.build().await?;

Program 内部用 Arc 持有,克隆开销很小。注意 builder_from_program 不暴露 with_library_loader——这是编译期选项,对已编译的 program 无效。

CompileOptions 的 setter

直接用 navi_compiler::CompileOptions 时可以使用下列 setter —— Instance::builder 没有暴露它们,因为它内部已经配好了合理的默认值:

Setter说明
with_load_options(opts)覆盖 prelude/stdlib 解析配置。一般以 navi_builtins::load_options() 为起点再做定制。
with_native_func_registry(&registry)替换 native 函数注册表。必须包含脚本用到的所有 native 函数;传 &navi_vm::NativeFuncs 即可使用 vm 自带的全套。
with_locale(locale)InstanceBuilder 上同名方法行为一致。
with_library_loader(loader)同上 —— 把额外的 loader 链到现有 loader 之前。
with_optimize(enable)切换优化开关(默认 true)。

输出模式

with_output_mode

控制 plot() 数据的产出方式。

模式行为
OutputMode::Chart(默认)系列图形数据写入 instance.chart()
OutputMode::Stream系列图形数据以 DrawEvent 形式在事件流中发出
rust
use navi_vm::{Instance, OutputMode, TimeFrame};

let mut instance = Instance::builder(provider, source, TimeFrame::days(1), "NASDAQ:AAPL")
    .with_output_mode(OutputMode::Stream)
    .build().await?;

详见 执行脚本

完整示例

rust
use navi_vm::{Instance, OutputMode, StrategyConfigOverride, TimeFrame};

let mut instance = Instance::builder(bars, source, TimeFrame::minutes(5), "NASDAQ:AAPL")
    .with_locale("en")
    .with_input_value(0, 20)        // 覆盖第一个输入
    .with_input_value(1, 2.5)       // 覆盖第二个输入
    .with_input_sessions(InputSessions::REGULAR)
    .with_last_info(LastInfo::new(999, last_time))
    .with_output_mode(OutputMode::Stream)
    // 仅策略脚本有效:覆盖执行参数
    .with_strategy_config_override(StrategyConfigOverride {
        initial_capital: Some(50_000.0),
        commission_value: Some(0.1),
        ..Default::default()
    })
    .build().await?;

instance.run_to_end("NASDAQ:AAPL", TimeFrame::minutes(5)).await?;

下一步

基于 MIT 许可证发布。