Skip to content

Instance Builder

InstanceBuilder compiles an Navi source string and produces an executable Instance.

Basic Usage

rust
use navi_vm::{Instance, TimeFrame};

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

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

Data Providers

The first argument to Instance::builder() is a DataProvider. It serves two roles:

  1. Main chart K-lines — consumed by Instance::run() to drive bar-by-bar execution
  2. request.security() data — MTF and cross-symbol candlestick streams

Vec<Candlestick> implements DataProvider directly, so you can pass bars without any wrapper:

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

For multi-symbol or live data, implement DataProvider:

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

Inspecting Script Info

Use the standalone script_info() function to inspect a script's metadata without creating an executable instance. It requires only the source code:

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());

This is useful for discovering input IDs before building an instance (see Input Configuration).

Builder Methods

InstanceBuilder (from Instance::builder) compiles source on build(). InstanceBuilderFromProgram (from Instance::builder_from_program) skips compilation. Compile-time options are only available on InstanceBuilder.

MethodDescriptionbuilderbuilder_from_program
with_localeLocale for error messages and number/date formatting
with_input_valueOverride a script input by ID
with_input_sessionsFilter which trade sessions are processed
with_strategy_config_overrideOverride strategy() parameters at instance level
with_background_colorCustom chart background color
with_last_infoLast bar index and timestamp
with_execution_limitsLoop iteration and security call limits
with_mtf_waitMax wait time for request.security() MTF data in realtime
with_output_modeSeries graph output mode (Chart or Stream)
with_jitEnable Cranelift JIT compilation (jit feature required)
with_library_loaderRegister a custom library loader with fallback chaining

Script Metadata

with_locale

Set the locale for error messages and number/date formatting:

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

Input Configuration

with_input_value

Override default input values by ID. Input IDs are assigned sequentially starting from 0, in the order input.*() calls appear in the script.

To discover input IDs, use script_info() (see Inspecting Script Info): inputs is a Vec<Input> in declaration order, so the index equals the ID.

rust
// Navi script:
// 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)       // Override RSI Length to 21
    .build().await?;

with_input_sessions

Control which trade sessions are processed:

rust
use navi_vm::InputSessions;

let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .with_input_sessions(InputSessions::REGULAR | InputSessions::EXTENDED)
    .build().await?;
FlagDescription
InputSessions::REGULARRegular trading hours
InputSessions::EXTENDEDExtended/pre-market/after-hours
InputSessions::OVERNIGHTOvernight sessions
InputSessions::ALLAll sessions

Strategy Configuration

with_strategy_config_override

Override individual strategy() parameters without modifying the script source. Fields set to Some(…) take precedence over the script-declared values; fields left as None fall back to the script defaults. Has no effect on non-strategy scripts.

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 fields (all Option<T>, default None):

FieldDescriptionDefault
initial_capitalInitial account equity1_000_000
commission_typeCommission mode: percent, cash_per_contract, or cash_per_orderpercent
commission_valueCommission amount. When percent: percentage points (1 = 1%). When cash_per_contract or cash_per_order: absolute currency amount per contract or order.0
margin_longMargin requirement for long positions, as a percentage100
margin_shortMargin requirement for short positions, as a percentage100
slippageSlippage per fill, in ticks0
pyramidingMaximum number of entries allowed in the same direction0
risk_free_rateAnnual risk-free rate (%) used for Sharpe and Sortino ratios2
process_orders_on_closeFill orders at bar close instead of next bar openfalse
calc_on_order_fillsRe-execute the script after each order fillfalse
backtest_fill_limits_assumptionExtra ticks beyond limit price required before a limit order fills0
close_entries_ruleWhich open entry to close first: FIFO or ANYFIFO

The override is stored on the instance and reapplied automatically on each reset(), so the same parameter values persist across resets.

Display & Environment

with_background_color

Set a custom 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

Provide information about the last available bar (useful for 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?;

Execution Limits

with_execution_limits

Configure runtime limits to protect against runaway scripts:

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?;

When not called, the default limit of 500,000 loop iterations per bar is used.

with_mtf_wait

Set a fixed sleep duration applied before executing each realtime bar when the script contains request.security() calls:

rust
use std::time::Duration;

let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .with_mtf_wait(Duration::from_secs(3))
    .build().await?;
ValueBehaviour
Duration::ZERO (default)No sleep — non-blocking behaviour
Duration::from_secs(3)Sleep 3 s before each realtime bar

When the script contains no request.security() calls, the function returns immediately without sleeping — zero overhead.

Output Mode

with_output_mode

Controls how plot() data is produced during execution.

ModeBehavior
OutputMode::Chart (default)Series graph data is written to instance.chart()
OutputMode::StreamSeries graph data is emitted as DrawEvent in the event stream
rust
use navi_vm::{Instance, OutputMode, TimeFrame};

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

See Executing Scripts for usage examples.

Custom Library Loader

with_library_loader

Register a custom LibraryLoader for resolving use imports. The new loader is prepended to the resolution chain — it is tried first, falling back to the previously registered loader, with the built-in prelude/stdlib loader always at the end.

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> {
        // return (source_code, module_name) or Err
        todo!()
    }
}

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

Call with_library_loader multiple times to build a priority stack. Each new loader is tried before all previously registered ones:

rust
// Resolution order: loader_b → loader_a → built-in prelude/stdlib
let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
    .with_library_loader(loader_a)
    .with_library_loader(loader_b)
    .build().await?;

Advanced: Reuse a Compiled Program

Instance::builder() compiles the source on each build() call. If you need to compile once and reuse the Program across multiple instances — e.g. different providers, different input values, or just to avoid repeated compilation — use Instance::builder_from_program:

rust
use navi_vm::{Instance, TimeFrame};

// Compile once.
let program = Instance::builder(provider.clone(), source, TimeFrame::days(1), "NASDAQ:AAPL")
    .build().await?.into_program(); // or use navi_vm::compile() directly

// Reuse across many instances — no recompilation.
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 is reference-counted internally, so cloning is cheap. Note that builder_from_program does not expose with_library_loader — that is a compile-time option that does not apply when a program is already compiled.

Complete Example

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

let instance = Instance::builder(bars, source, TimeFrame::minutes(5), "NASDAQ:AAPL")
    .with_locale("en")
    .with_input_value(0, 20)        // Override first input
    .with_input_value(1, 2.5)       // Override second input
    .with_input_sessions(InputSessions::REGULAR)
    .with_last_info(LastInfo::new(999, last_time))
    .with_output_mode(OutputMode::Stream)
    // Strategy scripts only: override execution parameters
    .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?;

Next Steps

Released under the MIT License.