Skip to content

C/C++ API

OpenPine provides C ABI bindings with a C++ wrapper, allowing you to embed the Pine Script engine in C and C++ applications.

Requirements

  • C++17 compiler
  • CMake 3.11+
  • Rust toolchain (for building the native library)

Setup with CMake

The C++ API uses Corrosion to build the Rust library from CMake:

cmake
cmake_minimum_required(VERSION 3.11)
project(my_project)

add_subdirectory(path/to/openpine)

add_executable(my_app main.cpp)
target_link_libraries(my_app PRIVATE openpine_capi)

Basic Usage

cpp
#include <openpine.hpp>
#include <iostream>

int main() {
    const char* script = R"(
//@version=6
indicator("SMA")
plot(ta.sma(close, 5))
)";

    // Create instance
    openpine::CreateInstanceOptions opts(script, "1D", "NASDAQ:AAPL");

    try {
        openpine::MyProvider provider(candlesticks);
        opts.setDataProvider(&provider);
        auto instance = openpine::Instance::create(opts);

        // Run asynchronously; callback fires on the execution thread
        std::promise<void> done;
        instance.run("NASDAQ:AAPL", "1D", 0, [&done](const openpine::Result& r) {
            if (!r.isSuccess())
                std::cerr << "Error: " << r.errorMessage() << std::endl;
            done.set_value();
        });
        done.get_future().wait();

        // Read chart outputs
        auto chart = instance.chart();
        auto iter = chart.seriesGraphIterator(
            openpine::SeriesGraphType::Plot
        );

        while (iter.next()) {
            const auto plot = iter.seriesGraph().asPlot().value();
            std::cout << "Plot: " << plot.title() << std::endl;

            for (size_t i = 0; i < chart.seriesLength(); i++) {
                auto val = plot.value(i);
                if (val.has_value()) {
                    std::cout << "  bar " << i << ": " << val.value()
                              << std::endl;
                }
            }
        }
    } catch (const openpine::Error& e) {
        std::cerr << "Error: " << e.errorMessage() << std::endl;
        return 1;
    }

    return 0;
}

Key C++ Types

TypeDescription
CreateInstanceOptionsConfiguration: source, timeframe, symbol, optional DataProvider and OutputMode
InstanceCompiled, executable script
DataProviderAbstract base: implement symbolInfo + candlesticksOpen
CandlestickStreamRAII push stream; create in candlesticksOpen, push from any thread
CandlestickItemA bar plus confirmed flag (true = closed, false = forming)
ChartVisual outputs collection
SeriesGraphIteratorIterates series graphs with optional type filter
GraphIteratorIterates point-in-time graphs
SeriesGraphTagged union (Plot, PlotCandle, PlotShape, etc.)
GraphTagged union (Label, Line, Box, Table, etc.)
ErrorException with error message and optional span

Output Mode

Set the output mode on CreateInstanceOptions to control how plot() data is produced:

cpp
openpine::CreateInstanceOptions opts(script, "1D", "NASDAQ:AAPL");
opts.setOutputMode(ffi::OutputMode::Stream);
ModeBehavior
OutputMode::Chart (default)Plot data is written to instance.chart()
OutputMode::StreamPlot data is emitted as events via the event callback

Event Callback

Pass an optional event callback to Instance::run() to receive events as the script executes:

cpp
instance.run("NASDAQ:AAPL", "1D", 0,
    // Completion callback
    [&done](const openpine::Result& r) {
        done.set_value();
    },
    // Event callback (optional)
    [](const ffi::Event& event) {
        switch (event.kind) {
            case ffi::EventKind::BarStart:
                std::cout << "bar " << event.bar_start.bar_index << std::endl;
                break;
            case ffi::EventKind::HistoryEnd:
                std::cout << "history complete" << std::endl;
                break;
            case ffi::EventKind::DrawUpdatePlot:
                std::cout << "plot " << event.draw_update_plot.id
                          << " = " << event.draw_update_plot.value << std::endl;
                break;
            default:
                break;
        }
    }
);

Without an event callback (or passing nullptr), run() executes to completion without per-event notifications — equivalent to the previous behavior.

Feeding Data

Implement DataProvider and pass it to CreateInstanceOptions::setDataProvider(). In candlesticksOpen, create a CandlestickStream, push bars (synchronously or from another thread), and return the raw handle:

cpp
class MyProvider : public openpine::DataProvider {
 public:
  explicit MyProvider(const std::vector<openpine::Candlestick>& bars)
      : bars_(bars) {}

  openpine::PartialSymbolInfo symbolInfo(std::string_view) override {
    return {};  // use defaults
  }

  ffi::CandlestickStream* candlesticksOpen(
      std::string_view symbol, std::string_view timeframe,
      int64_t from_time) override {
    auto stream = std::make_unique<openpine::CandlestickStream>(
        symbol, timeframe, from_time);
    for (const auto& bar : bars_)
      stream->push(openpine::CandlestickItem{bar, true});
    stream->finish();
    return stream->release();
  }

 private:
  std::vector<openpine::Candlestick> bars_;
};

// Usage
MyProvider provider(candlesticks);
opts.setDataProvider(&provider);
auto instance = openpine::Instance::create(opts);

std::promise<void> done;
instance.run("NASDAQ:AAPL", "1D", 0, [&done](const openpine::Result& r) {
    done.set_value();
});
done.get_future().wait();

For live streaming, push bars from a separate thread asynchronously:

cpp
ffi::CandlestickStream* candlesticksOpen(...) override {
    auto* stream = new openpine::CandlestickStream(symbol, timeframe, from_time);
    std::thread([stream, this]() {
        for (auto& bar : fetch_bars())
            stream->push(openpine::CandlestickItem{bar, true});
        stream->finish();
        delete stream;
    }).detach();
    return stream->handle();  // hand raw handle to VM, thread owns the wrapper
}

Accessing Graphs

After executing bars, call instance.chart() to access all visual outputs. The chart provides two iterator types:

MethodFilterContents
chart.seriesGraphIterator(type?)SeriesGraphTypePer-bar data: plots, backgrounds, fills, candles, shapes, arrows
chart.graphIterator(type?)GraphTypePoint-in-time objects: labels, lines, boxes, tables, hlines, polylines

Both iterators accept an optional type filter. Pass std::nullopt to iterate all types.

cpp
auto chart = instance.chart();
auto length = chart.seriesLength();

// Series graphs: one value per bar
auto iter = chart.seriesGraphIterator(std::nullopt);
while (iter.next()) {
    auto sg = iter.seriesGraph();

    if (sg.type() == openpine::SeriesGraphType::Plot) {
        auto plot = sg.asPlot().value();
        // plot.value(i), plot.color(i), plot.title(), ...
    }
    if (sg.type() == openpine::SeriesGraphType::PlotCandle) {
        auto candle = sg.asPlotCandle().value();
        // candle.value(i) -> std::optional<Bar>
    }
    // PlotShape, PlotArrow, PlotBar, PlotChar, BackgroundColors, Fill
}

// Point-in-time graphs
auto giter = chart.graphIterator(std::nullopt);
while (giter.next()) {
    auto g = giter.graph();

    if (g.type() == openpine::GraphType::Label) {
        auto label = g.asLabel().value();
        // label.x(), label.y(), label.text(), ...
    }
    if (g.type() == openpine::GraphType::Line) {
        auto line = g.asLine().value();
        // line.x1(), line.y1(), line.x2(), line.y2(), ...
    }
    // Box, Table, Hline, LineFill, Polyline
}

For the complete data model — every field, enum, and rendering algorithm — see Chart Rendering.

Error Handling

All errors are thrown as openpine::Error exceptions:

cpp
try {
    opts.setDataProvider(&provider);
    auto instance = openpine::Instance::create(opts);
    std::promise<void> done;
    instance.run("NASDAQ:AAPL", "1D", 0, [&](const openpine::Result& r) {
        done.set_value();
    });
    done.get_future().wait();
} catch (const openpine::Error& e) {
    std::cerr << "Error: " << e.errorMessage() << std::endl;

    // Optional: get error source location
    auto span = e.errorSpan();
    if (span.has_value()) {
        std::cerr << "  at line " << span.value().startLine << std::endl;
    }
}

Next Steps

Released under the MIT License.