Instance Builder
InstanceBuilder compiles an Navi source string and produces an executable Instance.
Basic Usage
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:
- Main chart K-lines — consumed by
Instance::run()to drive bar-by-bar execution request.security()data — MTF and cross-symbol candlestick streams
Vec<Candlestick> implements DataProvider directly, so you can pass bars without any wrapper:
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:
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:
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.
| Method | Description | builder | builder_from_program |
|---|---|---|---|
with_locale | Locale for error messages and number/date formatting | ✓ | ✓ |
with_input_value | Override a script input by ID | ✓ | ✓ |
with_input_sessions | Filter which trade sessions are processed | ✓ | ✓ |
with_strategy_config_override | Override strategy() parameters at instance level | ✓ | ✓ |
with_background_color | Custom chart background color | ✓ | ✓ |
with_last_info | Last bar index and timestamp | ✓ | ✓ |
with_execution_limits | Loop iteration and security call limits | ✓ | ✓ |
with_mtf_wait | Max wait time for request.security() MTF data in realtime | ✓ | ✓ |
with_output_mode | Series graph output mode (Chart or Stream) | ✓ | ✓ |
with_jit | Enable Cranelift JIT compilation (jit feature required) | ✓ | ✓ |
with_library_loader | Register a custom library loader with fallback chaining | ✓ | — |
Script Metadata
with_locale
Set the locale for error messages and number/date formatting:
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.
// 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:
use navi_vm::InputSessions;
let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
.with_input_sessions(InputSessions::REGULAR | InputSessions::EXTENDED)
.build().await?;| Flag | Description |
|---|---|
InputSessions::REGULAR | Regular trading hours |
InputSessions::EXTENDED | Extended/pre-market/after-hours |
InputSessions::OVERNIGHT | Overnight sessions |
InputSessions::ALL | All 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.
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):
| Field | Description | Default |
|---|---|---|
initial_capital | Initial account equity | 1_000_000 |
commission_type | Commission mode: percent, cash_per_contract, or cash_per_order | percent |
commission_value | Commission amount. When percent: percentage points (1 = 1%). When cash_per_contract or cash_per_order: absolute currency amount per contract or order. | 0 |
margin_long | Margin requirement for long positions, as a percentage | 100 |
margin_short | Margin requirement for short positions, as a percentage | 100 |
slippage | Slippage per fill, in ticks | 0 |
pyramiding | Maximum number of entries allowed in the same direction | 0 |
risk_free_rate | Annual risk-free rate (%) used for Sharpe and Sortino ratios | 2 |
process_orders_on_close | Fill orders at bar close instead of next bar open | false |
calc_on_order_fills | Re-execute the script after each order fill | false |
backtest_fill_limits_assumption | Extra ticks beyond limit price required before a limit order fills | 0 |
close_entries_rule | Which open entry to close first: FIFO or ANY | FIFO |
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:
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):
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:
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:
use std::time::Duration;
let instance = Instance::builder(provider, source, timeframe, "NASDAQ:AAPL")
.with_mtf_wait(Duration::from_secs(3))
.build().await?;| Value | Behaviour |
|---|---|
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.
| Mode | Behavior |
|---|---|
OutputMode::Chart (default) | Series graph data is written to instance.chart() |
OutputMode::Stream | Series graph data is emitted as DrawEvent in the event stream |
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.
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:
// 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:
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
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
- Executing Scripts — feed data and run
- Reading Outputs — inspect results