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
| Type | Description |
|---|---|
Instance | The compiled, executable script instance |
InstanceBuilder | Builder for configuring and compiling a script |
Candlestick | OHLCV bar data fed into 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) |
Error | Compile 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 yieldedinstance.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
- 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