Skip to content

openpine-vm Overview

openpine-vm is the Rust crate that compiles and executes Pine Script programs. It provides a high-level API centered around the Instance type.

Adding the Dependency

toml
[dependencies]
openpine-vm = { git = "https://github.com/longbridge/openpine.git" }

Core Concepts

Execution Model

OpenPine uses a bar-by-bar execution model. You feed candlestick data one bar at a time, and the VM executes the entire script for each bar. This mirrors how Pine Script works on TradingView.

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 fed into 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)
ErrorCompile or runtime errors

Minimal Example

rust
use openpine_vm::{Instance, TimeFrame};

#[tokio::main]
async fn main() -> Result<(), openpine_vm::Error> {
    // 1. Define the Pine script
    let source = r#"
//@version=6
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)
plotshape(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 mut instance = Instance::builder(bars, source, timeframe, symbol)
        .build().await?;

    // 3. Check for compilation warnings
    for warning in instance.warnings() {
        eprintln!("Warning: {}", warning.display_as_warning());
    }

    // 4. Run the script against the provider's bars
    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.isconfirmed = true)
  Realtime(_)   -> Forming bar  (rollback-capable; confirmed when stream ends)
  HistoryEnd    -> Signals that all historical bars have been yielded

instance.run() returns a Stream of events. Use run_to_end() to consume all events and access the chart afterwards, or consume the stream directly for per-bar event processing.

Next Steps

Released under the MIT License.