navi-vm Overview
navi-vm is the Rust crate that compiles and executes Navi programs. It provides a high-level API centered around the Instance type.
Adding the Dependency
[dependencies]
navi-vm = { git = "https://github.com/longbridge/navi.git" }Core Concepts
Execution Model
Navi uses a bar-by-bar execution model. The VM actively pulls candlestick data from a DataProvider and executes the entire script for each bar.
DataProvider::candlesticks()
→ Candlestick 1 → Execute → Outputs updated
→ Candlestick 2 → Execute → Outputs updated
→ Candlestick 3 → Execute → Outputs updated
→ ...Key Types
| Type | Description |
|---|---|
Instance | The compiled, executable script instance |
InstanceBuilder | Builder for configuring and compiling a script |
Candlestick | OHLCV bar data yielded by DataProvider and consumed by the VM |
TimeFrame | Chart timeframe (1D, 5m, 1W, etc.) |
SymbolInfo | Symbol metadata — provided automatically by DataProvider; no manual construction needed |
DataProvider | Async trait supplying candlesticks and symbol info for request.security() |
CandlestickItem | An enum yielded by DataProvider::candlesticks: Confirmed(candle), Realtime(candle), or HistoryEnd |
Chart | Collection of visual outputs (plots, labels, lines, etc.) |
Event | Log and alert events emitted during execution |
ScriptInfo | Metadata about the compiled script (type, inputs, alerts, declared series outputs, optional bar_color declaration) |
Error | Compile or runtime errors |
Minimal Example
use navi_vm::{Instance, TimeFrame};
#[tokio::main]
async fn main() -> Result<(), navi_vm::Error> {
// 1. Define the Navi script
let source = r#"
indicator("SMA Crossover")
fast = ta.sma(close, 5)
slow = ta.sma(close, 20)
plot(fast, "Fast SMA", color.BLUE)
plot(slow, "Slow SMA", color.RED)
plot_shape(ta.crossover(fast, slow), style=Shape.TriangleUp)
"#;
// 2. Build the instance with bar data
let timeframe = TimeFrame::days(1);
let symbol = "NASDAQ:AAPL";
let bars = load_candles(); // Your data source — Vec<Candlestick>
let instance = Instance::builder(bars, source, timeframe, symbol)
.build().await?;
// 3. Check for compilation warnings
let warnings = instance.warnings();
if !warnings.is_empty() {
eprintln!("{}", warnings.display());
}
// 4. Run the script against the provider's bars
let instance = instance.run_to_end(symbol, timeframe).await?;
// 5. Read outputs
for (_id, graph) in instance.chart().series_graphs() {
if let Some(plot) = graph.as_plot() {
let title = plot.title.as_deref().unwrap_or("unnamed");
println!("Plot '{}': {} bars", title, plot.series.len());
}
}
Ok(())
}Execution Model
Instance::run() drives the full execution loop consuming the DataProvider stream:
DataProvider::candlesticks() stream
Confirmed(_) -> Closed bar (bar_index advances, barstate.is_confirmed = true)
Realtime(_) -> Forming bar (rollback-capable; confirmed on rollover or stream end)
HistoryEnd -> Signals that all historical bars have been yieldedinstance.run() returns a RunHandle for iterating events via handle.next_event().await. The handle owns the Instance while execution is in progress, so call handle.into_instance() if you need the instance back afterwards. Use run_to_end() to consume all events and get the finished instance back in one step, or use run() directly for per-bar event processing (the handle also exposes instance methods like save_state() while iterating).
Next Steps
- Instance Builder — configure scripts with inputs, locale, sessions
- Executing Scripts — TimeFrame, Candlestick, SymbolInfo details
- Reading Outputs — plots, labels, lines, strategy orders
- Error Handling — compile and runtime errors
- Security & Limits — loop iteration limits and untrusted script safety