Skip to content

ChartView

ChartView renders charts by consuming a unified stream of K-line bars and script execution events from a ChartProvider. It handles layout, rendering, and user interaction.

For a comparison with data provider approaches, see Chart View Overview.

Architecture

The client-side ChartView connects to your data and execution source through the ChartProvider trait. The provider streams a single unified ChartStreamEvent sequence — candlestick bars and per-script output events — to the chart renderer.

When to Use

Use ChartView with a remote provider when:

  • Script compilation and execution happen on a server
  • You want a thin client that only renders — no local VM dependency
  • You're building a web-based charting platform with server-side execution

Use ChartView with LocalChartProvider (requires the local feature) when:

  • You want fully in-process VM execution
  • You're prototyping or building a desktop/CLI application

Creating a ChartView

rust
use navi_chart::{ChartView, ChartEvent, Size, Theme};

// Create an empty chart — no symbol or timeframe required at construction.
let cv = ChartView::new(
    provider,                        // impl ChartProvider
    my_platform,                     // impl Platform
    Size { width: 800.0, height: 600.0 },
    my_canvas_ctx,                   // impl DrawingContext
);

cv.set_theme(Theme::dark());
cv.set_locale("en");

// Set symbol and timeframe when ready — this starts the data stream.
cv.set_symbol("NASDAQ:AAPL");
cv.set_timeframe(TimeFrame::days(1));

Generic Parameters

ChartView<P, T> is generic over:

ParameterDescription
P: ChartProviderYour data and script event source
T: Clone + PartialEq + DisplayA user-defined "tag" type attached to each script. Its Display output is used as the label title for compile-error slots.

Implementing ChartProvider

The ChartProvider trait has one associated type and one method that returns a unified stream:

rust
use futures_util::Stream;
use navi_chart::ChartProvider;
use navi_chart_wire::{ChartStreamEvent, ChartStreamRequest};
use navi_types::TimeFrame;

pub trait ChartProvider {
    /// Script handle understood by this provider.
    ///
    /// The provider chooses what shape this takes — Navi source,
    /// a hash, a cache key, a UUID, a pre-compiled `Program`, …
    type Script: Clone + 'static;

    /// Stream unified K-line + script events for the given symbol, timeframe,
    /// and scripts. Events are tagged with `script_id` values `0..n`
    /// corresponding to the index in `request.scripts`.
    fn chart_stream(
        &self,
        symbol: String,
        timeframe: TimeFrame,
        request: ChartStreamRequest<Self::Script>,
    ) -> impl Stream<Item = ChartStreamEvent> + 'static;
}

Choosing the Script handle type

type Script lets the provider decide how a script is identified. Common choices:

type Script = …When to use it
StringThe handle is the Navi source code. The backend compiles on every chart_stream call (or caches by hash internally).
u64 / a content hashThe client uploads source once; only the hash is passed in ChartStreamRequest.
UuidThe backend maintains a session-scoped registry.
navi_chart::ProgramUse LocalChartProvider — the client compiles locally and ships the bytecode.

ChartStreamRequest

ChartStreamRequest<S> carries the script list and global configuration:

rust
pub struct ChartStreamRequest<S> {
    /// Scripts to execute, in order. Events in the stream are tagged
    /// with their 0-based index in this list.
    pub scripts: Vec<ScriptDescriptor<S>>,
    /// Locale for diagnostic messages (e.g. `"en"`, `"zh-CN"`).
    pub locale: String,
    /// Which trading sessions to accept as input bars.
    pub input_sessions: InputSessions,
}

Each ScriptDescriptor<S> carries the script handle and user-overridden input values:

rust
pub struct ScriptDescriptor<S> {
    pub script: S,
    pub input_values: InputValues,  // HashMap<usize, serde_json::Value>
}

ChartStreamEvent

ChartStreamEvent is the item type yielded by chart_stream. Your provider must produce these events in the correct order:

rust
pub enum ChartStreamEvent {
    /// A candlestick bar. Historical bars come before `HistoryEnd`;
    /// realtime bars (which may update in place) come after.
    Bar(Candlestick),
    /// Boundary between historical and realtime data. Emitted exactly once.
    /// Applies globally to all scripts in the session.
    HistoryEnd,
    /// An event produced by a specific script.
    Script(TaggedScriptEvent),
    /// A session-level unrecoverable error. The stream terminates after this.
    Error(Error),
}

pub struct TaggedScriptEvent {
    /// Zero-based index into `ChartStreamRequest::scripts`.
    pub script_id: u64,
    pub event: ScriptEventPayload,
}

pub enum ScriptEventPayload {
    /// Script metadata. Emit once per script before the first bar.
    SessionInfo { script_info: Arc<ScriptInfo>, symbol_info: Box<SymbolInfo> },
    /// Visual output event (plots, labels, lines, etc.).
    Draw(DrawEvent),
    /// Strategy order/fill event.
    Strategy(StrategyEvent),
    /// A per-script error. Does not affect other scripts in the session.
    Error(Error),
}

Stream Protocol

SessionInfo{id=0}  SessionInfo{id=1}   ← per-script, before first bar
Bar(t0)  Script{id=0, Draw}  Script{id=1, Draw}
Bar(t1)  Script{id=0, Draw}  Script{id=1, Draw}

HistoryEnd                               ← global, exactly once
Bar(realtime)  Script{id=0, Draw}  …

LocalChartProvider

The LocalChartProvider (requires the local feature) runs scripts in-process using the Navi VM. It wraps any DataProvider:

rust
use navi_chart::LocalChartProvider;

let provider = LocalChartProvider::new(data_provider);
// Optionally configure:
// .with_execution_limits(limits)
// .with_mtf_wait(Duration::from_millis(200))

let cv = ChartView::new(provider, platform, size, ctx);
cv.set_symbol(symbol);
cv.set_timeframe(tf);

The DataProvider supplies K-line data for both the main series and any request.security() MTF calls.

Adding Scripts

ChartView::add_script accepts a value of type P::Script:

rust
// With a remote string-handle provider:
let id = cv.add_script(
    "//@version=6\nindicator(\"RSI\")\nplot(ta.rsi(close, 14))".to_string(),
    "rsi".to_string(),
);

// With LocalChartProvider (Script = Program):
use navi_chart::Program;
let program: Program = compile(source).await?;
let id = cv.add_script(program, "rsi".to_string());

When any script is added, removed, or has its configuration changed, ChartView restarts the session — calling chart_stream() again with the updated script list. All scripts replay from bar 0.

Persistence

ChartView exposes save() / load() for snapshot persistence:

rust
let snapshot = cv.save();  // ChartSnapshot<T, P::Script>
let json = serde_json::to_string(&snapshot).unwrap();

let snapshot: ChartSnapshot<String, MyScript> = serde_json::from_str(&json).unwrap();
cv.load(snapshot);

Shared API

ChartView implements six traits. Full documentation for each:

Weak References

Use ChartView::downgrade() to obtain a WeakChartView — a non-owning reference that does not prevent the chart from being dropped:

rust
let weak: WeakChartView<_> = cv.downgrade();

let callback = move || {
    if let Some(cv) = weak.upgrade() {
        cv.on_mouse_up(pos);
    }
};

WeakChartView implements Clone, so the same handle can be shared across multiple closures.

Released under the MIT License.