Skip to content

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

toml
[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

TypeDescription
InstanceThe compiled, executable script instance
InstanceBuilderBuilder for configuring and compiling a script
CandlestickOHLCV bar data yielded by DataProvider and consumed by the VM
TimeFrameChart timeframe (1D, 5m, 1W, etc.)
SymbolInfoSymbol metadata — provided automatically by DataProvider; no manual construction needed
DataProviderAsync trait supplying candlesticks and symbol info for request.security()
CandlestickItemAn enum yielded by DataProvider::candlesticks: Confirmed(candle), Realtime(candle), or HistoryEnd
ChartCollection of visual outputs (plots, labels, lines, etc.)
EventLog and alert events emitted during execution
ScriptInfoMetadata about the compiled script (type, inputs, alerts, declared series outputs, optional bar_color declaration)
ErrorCompile or runtime errors

Minimal Example

rust
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 yielded

instance.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

Released under the MIT License.