Options
Market, display, drawings, settings, and per-indicator options for Vela™ and the workspace.
The second argument to new Vela(container, options, deps?) configures market data, display, and behavior. Everything here is optional — new Vela('#chart', { data: myBars }) is enough to render candles.
This vocabulary is shared with the workspace, which accepts every
option below except height (plus its shell options) — there, each one is the DEFAULT
of every cell, overridable per cell through cells.
Per-indicator options (the third argument to addIndicator) are covered at the end.
Market options
How the chart obtains its candles.
| Option | Type | Meaning |
|---|---|---|
symbol | string | Symbol to load — the string is the WHOLE market identity. A bare ticker ('BTCUSDT') resolves against the registered providers in declaration order (first one whose index lists it); an EXCHANGE: prefix ('coinbase:BTC-USD', case-insensitive) pins the venue — a registered provider name, or a listing prefix a provider's index declares ('NASDAQ:AAPL', strict: a wrong venue resolves to nothing). |
timeframe | string | Bar interval, e.g. '1h'. |
session | 'regular' | 'extended' | Trading session to show, on markets that have one (regular = RTH, the default; extended = pre/post-market included). The flag rides every data request — providers without a session concept ignore it. Switch at runtime with chart.setMarket({ session }) or the bottombar's RTH/ETH toggle. |
bars | number | How many bars of history to load. Depths beyond one ~10k-bar chunk paint the recent window first, then backfill older bars in the background — watch history:progress / await chart.historyComplete() for the full depth. |
visibleRange | VisibleRangePreset | {from,to} | — |
data | OHLCV[] | Offline bars. When set, no network fetch happens. |
dataand provider fetch are mutually exclusive. Supplydatato run fully offline (recommended for first runs and tests), or setsymbol/timeframe/barsto fetch.The fetch path needs a registered provider. No provider is bundled — register one with
chart.data.registerProvider(...); registering it fires the chart's parked initial load. Each bar is{ time, open, high, low, close, volume? }withtimein epoch milliseconds.With offline
data,timeframeis still honored — it sets bar spacing and axis labels — whilesymbolandbarsare ignored.
A fetching chart pairs these market options with a registered provider — the display flags ride along in the same object, and registering the provider fires the parked initial load.
import { Vela } from '@luxalgo/vela';
import { BinanceProvider } from '@luxalgo/vela/providers/binance';
const chart = new Vela('#chart', {
symbol: 'BTCUSDT', // bare = first registered provider that lists it; 'binance:BTCUSDT' pins
timeframe: '1h',
bars: 500, // how many bars of history to load
live: true, // history + a forming candle on each tick
theme: 'dark',
logScale: true, // logarithmic price scale
currentPriceLine: true, // dashed line + axis label at the last price
upColor: '#26a69a', // recolor the default cyan/white candles
downColor: '#ef5350',
});
// registering the provider resolves the symbol and fires the fetch
chart.data.registerProvider('binance', new BinanceProvider());Display & behavior options
| Option | Type | Default | Notes |
|---|---|---|---|
live | boolean | false | true adds a forming candle + live ticks on top of history. |
theme | 'dark' | 'light' or a theme object | dark | Pass an object to fully customize colors/fonts. |
renderer | renderer class | native | A renderer class to instantiate; omit for the built-in native renderer (default). The multi-renderer port (IChartRenderer) stays open — pass any class implementing it. |
defaultLanguage | string | first registered engine* | Scripting language used when addIndicator doesn't name one. Falls back to the first engine registered at construction, then to 'pine'. |
currentPriceLine | boolean | true | Dashed line + axis label at the latest price. |
logScale | boolean | false | Logarithmic price scale. |
nativeBackend | 'auto' | 'canvas2d' | 'webgl2' | auto | Native geometry backend. auto = WebGL2 if available, else canvas2d. Only applies to the native renderer. |
animations | boolean or { zoom?, pan?, scroll?, autoscale?, liveBar?, intro? } | on | true/false toggles every motion; an object configures each on its own (see The animations option below). Defaults: eased zoom on, pan momentum on (short snappy glide), autoscale glide on, first-load reveal on, live-bar glide off. |
glow | number | 0 | Neon glow/bloom for line series (~0.6 = strong). WebGL2 only — ignored on canvas2d. |
upColor | string | #089981 (green) | Bullish candle color (native renderer). |
downColor | string | #f23645 (red) | Bearish candle color (native renderer). |
priceStyle | 'candles' | 'bars' | 'line' | 'area' | 'baseline' | 'candles' | How the base price series is drawn (native renderer). |
drawings | boolean | { toolbar?, tools?, groups? } | toolbar shown | Interactive drawing tools. true/omitted ⇒ toolbar visible; false ⇒ toolbar hidden (the chart.drawings API still works headlessly); object customizes it (see below). Capability-gated (native renderer only). |
settings | { hidden?: string[] } | all visible | Chart-settings dialog visibility policy: setting ids to hide — a whole tab, a group, or a single row (see below). |
priceAxis | { ticks?: (ctx) => PriceAxisTick[] | null } | built-in ticks | Your own price-axis ladder (native renderer): ticks returns each pane's ticks, and the axis labels and horizontal gridlines both follow them (see below). |
* defaultLanguage falls back to the first injected engine's language if you don't set it.
Leave renderer off for the built-in native backend; a custom renderer class (implementing IChartRenderer) can be passed to swap the whole rendering backend.
The drawings option
By default the drawing toolbar is shown (on a renderer that supports it). Pass false to hide
the bar while still driving drawings from code, or an object to customize which tools appear:
| Field | Type | Effect |
|---|---|---|
toolbar | boolean | Show/hide the bar (default true). |
tools | DrawingTypeKey[] | Allow-list of tools; each is bucketed into its own group. |
groups | { id, label, tools }[] | Explicit, custom-labelled groups (unregistered/empty groups are dropped). |
// hide the bar but keep the programmatic API:
new Vela('#chart', { data: bars, drawings: false });
// only a few tools:
new Vela('#chart', { data: bars, drawings: { tools: ['trendline', 'hline', 'box'] } });See Drawing tools for the full catalogue and the chart.drawings API.
The animations option
Every eased motion of the native renderer is configured on its own. Each field takes
true (the built-in feel), false (instant — the motion is off), or a duration in
milliseconds: the ease time-constant, after which the motion has covered about 63% of
the remaining distance; it looks settled after roughly three of them. Durations are
capped at 1000 ms.
| Field | Default | What it eases |
|---|---|---|
zoom | true (70 ms) | The wheel zoom: bar spacing glides toward each notch's target instead of jumping. Off, the keyboard zoom keys of the widget jump too. |
pan | true (110 ms) | Pan momentum on touch: the velocity a finger flick releases with decays over this time. false = the chart stops dead on release. A mouse release always stops dead. |
scroll | follows pan (130 ms) | The programmatic scroll glide — the scroll-to-latest button, chart.panBy, the keyboard pan keys — easing the view to its target at constant zoom. Left unset it is on whenever pan is on, so { pan: false } still means an instant pan everywhere. |
autoscale | true (80 ms) | The price scale's glide toward its new range while a zoom or fling is in flight (off = it snaps every frame). |
liveBar | false | The forming candle (and the current-price line and label) slide toward each live tick instead of snapping: true = a 90 ms ease. A new bar always snaps; the crosshair, legend and data window always show the real values. |
intro | true ('settle', 650 ms) | The first-load reveal: candles draw themselves in left to right. 'settle' overshoots and eases back, 'grow' eases out; false skips it; { style?, duration? } sets the style and/or the sweep duration in ms (capped at 5000). |
new Vela('#chart', {
animations: {
zoom: 150, // a slower, softer zoom glide
pan: false, // …but a drag release stops dead (and panBy is instant)
liveBar: true, // the forming candle glides to each tick
intro: { style: 'grow', duration: 400 },
},
});Each motion is also a live renderer feature — animZoom, animPan, animScroll,
animAutoscale, animLiveBar, intro — so chart.renderer.set('animZoom', 0) switches
it off at runtime (see Renderer features). The
settings dialog's Symbol → Animation group carries on/off switches for zoom, pan
momentum (inertia and scroll glide together), the price scale, price changes (the live
bar) and the reveal; switching one back on restores the duration configured here. In the
rich config they are animations.{zoom, pan, autoscale, intro} plus
priceScale.animateLastPrice.
The settings option — hiding settings-dialog entries
By default the chart-settings dialog shows everything. settings.hidden lists setting
ids to hide; an id hides its whole subtree, and a tab with nothing left disappears
from the rail:
new VelaWorkspace('#chart', {
layout: false,
bars: 1000, // force the fetch depth…
settings: { hidden: ['advanced'] }, // …and remove the tab that would change it
});
// or a finer cut: one group, one row
new Vela('#chart', { data: bars, settings: { hidden: ['canvas.grid', 'scales.price-scale.countdown'] } });Hiding is display-only: hidden values keep being stored, delivered, and applied —
which is exactly what makes "force an option, hide its control" work. The policy is
instance state, not chart config: it never rides getConfig()/applyConfig() into
exported templates. It can also be set at runtime with
chart.renderer.setSettingsVisibility({ hidden: [...] }).
Ids are dot-separated paths: <tab>, <tab>.<group>, <tab>.<group>.<row>. Enumerate
every addressable id of a live chart with chart.renderer.listSettingsIds() (plugin
chart types and host-app sections included).
The complete catalog — every id, at every depth. Any subset works; a parent id makes its children redundant:
new VelaWorkspace('#chart', {
settings: {
hidden: [
// ══ Symbol tab ════════════════════════════════════════════════
'symbol', // the whole tab
'symbol.type', // the Type select (chart style)
'symbol.style.candles', // Candles group (also styles Heikin Ashi)
'symbol.style.candles.body', // Body toggle + up/down colors
'symbol.style.candles.borders', // Borders toggle + colors
'symbol.style.candles.wick', // Wick toggle + colors
'symbol.style.candles.spacing', // Spacing
'symbol.style.bars', // Bars group
'symbol.style.bars.up-color', // Color Up
'symbol.style.bars.down-color', // Color Down
'symbol.style.bars.spacing', // Spacing
'symbol.style.line', // Line group
'symbol.style.line.color', // Color
'symbol.style.line.width', // Width
'symbol.style.area', // Area group
'symbol.style.area.line-color', // Line color
'symbol.style.area.width', // Width
'symbol.style.area.top-fill', // Top fill
'symbol.style.area.bottom-fill', // Bottom fill
'symbol.style.baseline', // Baseline group
'symbol.style.baseline.top-line', // Top line
'symbol.style.baseline.bottom-line', // Bottom line
'symbol.style.baseline.fill-top', // Fill top area
'symbol.style.baseline.fill-bottom', // Fill bottom area
'symbol.style.baseline.base-level', // Base level %
'symbol.style.baseline.width', // Width
'symbol.animation', // Animation group
'symbol.animation.zoom', // Animate zoom (the wheel-zoom glide)
'symbol.animation.pan', // Pan momentum (drag-release inertia + scroll glide)
'symbol.animation.autoscale', // Animate price scale (the autoscale glide)
'symbol.animation.price-changes', // Animate price changes (the live-bar glide)
'symbol.animation.intro', // Reveal on load (the first-paint candle reveal)
'symbol.timezone', // Time zone group
// ══ Scales and lines tab ══════════════════════════════════════
'scales', // the whole tab
'scales.price-scale', // Price scale group
'scales.price-scale.mode', // Regular/Percent/Indexed/Logarithmic
'scales.price-scale.invert', // Invert scale
'scales.price-scale.last-price-line', // Last Price Line
'scales.price-scale.last-price-label', // Last price label
'scales.price-scale.countdown', // Countdown to bar close
'scales.price-scale.axis-labels', // Axis labels
'scales.price-scale.border-color', // Scale border color
'scales.crosshair', // Crosshair group
'scales.crosshair.color', // Color
'scales.crosshair.width', // Width
'scales.crosshair.style', // Solid/Dashed/Dotted
// ══ Canvas tab ════════════════════════════════════════════════
'canvas', // the whole tab
'canvas.background', // Background & text group
'canvas.background.color', // Background
'canvas.background.text-color', // Text color
'canvas.background.text-size', // Text size
'canvas.background.pane-separator', // Pane separator color
'canvas.grid', // Grid group
'canvas.grid.vertical', // Vertical lines toggle + color
'canvas.grid.horizontal', // Horizontal lines toggle + color
'canvas.margins', // Margins group
'canvas.margins.top', // Top (% of pane height)
'canvas.margins.bottom', // Bottom (% of pane height)
'canvas.margins.right', // Right (bars after the newest bar)
'canvas.theme', // Theme group (Dark/Light)
// ══ Events tab (timeline marks — present once marks name groups) ═
'events', // the whole tab
'events.groups', // Visible events (one checkbox per mark group)
'events.groups.<group-id>', // a group's checkbox (the group id, kebab-cased)
// ══ Widget & workspace tabs (shell-contributed) ═══════════════
'status-line', // the whole Status line tab
'status-line.parts', // Status line group (the five rows below)
'status-line.logo', // Symbol logo
'status-line.name', // Symbol name
'status-line.market', // Market status
'status-line.ohlc', // OHLC values
'status-line.change', // Bar change values
'status-line.indicators', // Indicators group (the two rows below)
'status-line.indicator-titles', // Titles
'status-line.indicator-values', // Values
'advanced', // the whole Advanced tab
'advanced.bars', // Bars to fetch
'time-zone', // the workspace Time zone group in the Symbol tab
'time-zone.zone', // (same row — UTC, Exchange, then the catalog;
// it replaces the renderer's `symbol.timezone`)
'trading-session', // the RTH/ETH group in the Symbol tab
'trading-session.session', // Session select
'trading-session.premarket-color', // Pre-market shading color (day-split markets)
'trading-session.postmarket-color', // Post-market shading color (day-split markets)
'trading-session.extended-color', // Extended-hours shading color (overnight markets)
'watermark', // the Watermark group (the two rows below)
'watermark.visible', // Symbol watermark
'watermark.replay', // Replay watermark (the line under it while replaying)
// ══ Plugin chart types (registerChartType) ════════════════════
'type:<chartTypeId>', // the type's settings tab (+ its subsections)
'type:<chartTypeId>.<key>', // a value row, by its settings bag key
'type:<chartTypeId>.<heading-slug>', // a group, by its heading label's slug
'type:<chartTypeId>.<subsection-slug>',// a subsection, by its title's slug
'symbol.style.<chartTypeId>', // a candle-drawn plugin style's Candles group
// (rows: .body, .borders, .wick, .spacing)
// ══ Host sections (setSettingsSections) ═══════════════════════
'<sectionId>', // the section's `id` field, else its title's slug
'<sectionId>.<rowId>', // a row's `id` field, else its label's slug
],
},
});Conditional entries hide-and-stay-hidden: trading-session only appears on symbols
that have sessions, status-line only when the shell's status line is on,
canvas.theme only when the host wires the theme switch — hiding them is safe either
way. Host sections contributed through setSettingsSections need nothing from the
contributor: the id fields are optional stability aids, label slugs are the
fallback.
The priceAxis option — custom price-axis ticks
priceAxis.ticks replaces the built-in tick ladder of the native renderer's price axes.
It is called for each pane — the price pane and every study pane — and returns the ticks
to draw. The axis labels and the horizontal gridlines both follow the same ticks, so they
always line up.
new Vela('#chart', {
data: bars,
priceAxis: {
// Emphasize the built-in levels and add a muted level between two of them
// wherever there is room.
ticks: ({ mode, defaults, priceToY }) => {
if (mode !== 'price') return null; // keep the built-in percent/indexed ticks
const out = [];
defaults.forEach((tick, i) => {
out.push({ ...tick, major: true });
const next = defaults[i + 1];
if (next && Math.abs(priceToY(next.price) - priceToY(tick.price)) > 40) {
const mid = (tick.price + next.price) / 2;
out.push({ price: mid, label: mid.toFixed(2), major: false });
}
});
return out;
},
},
});Each tick is { price, label, major? }: price places the gridline and the label, label
is drawn as given, and major: true draws the label semibold, major: false draws it
muted, while leaving major out draws the regular label. Gridlines look the same either
way. A label too close to the top or bottom edge of its pane is skipped, as with the
built-in ticks.
The function receives one context object per pane:
| Field | Meaning |
|---|---|
pane | { id, kind } — kind is 'price' or 'study'. |
min, max | The pane's visible range, in prices (also in percent and indexed mode). |
log | Whether the pane's scale is logarithmic. |
height | The pane height in CSS pixels. |
mode | 'price', 'percent' or 'indexed' — how the axis labels read this frame. |
baseline | The percent/indexed baseline price (undefined in price mode). |
mintick | The symbol's tick size, when known. |
fontSize | The axis font size in pixels. |
priceToY(price) | A price's y within the pane, in CSS pixels (log and inverted scales included). |
defaults | The built-in ticks for this frame — return them, or a filtered or extended copy, to build on them. |
- Return
nullorundefinedto keep the built-in ticks, or an empty array to draw none. - Entries with a non-finite
priceor a non-stringlabelare dropped. - If the function throws, the pane keeps its built-in ticks and the console shows one warning for the chart.
- A pane whose indicator declares its own axis (a blank or categorical axis) draws no price ticks and never calls the function. The extra scale column of an indicator merged onto its own scale keeps its built-in ticks.
- The function runs again only when a pane's range, height, mode, baseline, tick size or font size changes. If your ticks depend on your own state, set the function again to recompute them.
The option seeds the native renderer's runtime priceAxisTicks feature: change or clear
it later with chart.renderer.set('priceAxisTicks', fn) or null (see
Renderer features). A function is never part of
getConfig(). On a workspace, the top-level priceAxis is the default
of every cell, including cells a later layout change creates.
Non-obvious defaults, called out
- Animations are on by default (eased zoom, snappy inertial pan).
- The current-price line is on by default.
- The price scale is linear by default (
logScale: false).
Instead of 'dark'/'light', pass a full theme object to control every color and the font — all seven fields are required:
const midnight = {
background: '#0b0e14',
textColor: '#c9d1d9',
gridColor: '#1c2230',
borderColor: '#30363d',
upColor: '#3fb950',
downColor: '#f85149',
fontFamily: 'Inter, system-ui, sans-serif',
};
new Vela('#chart', { data: bars, theme: midnight });The theme can also be swapped at runtime — chart.setTheme('light') (or a full theme
object) re-skins the chart live and emits theme:changed so your surrounding UI can
follow; users reach the same switch in chart settings → Canvas → Theme. The built-in
dark and light themes share the same candle colors, so switching never recolors the
series. Setting only a background color through the settings dialog (or applyConfig)
keeps the app theme: when that background lands in the other luminance class (a white
plot on the dark theme), the derived inks — text, grid, axis border — re-base
automatically so legends and axis labels stay readable, while any explicitly chosen
text color wins.
Capability-gated options
Some options only take effect when the active backend supports them. glow is WebGL2-only — it is silently ignored on the canvas2d backend. If you force nativeBackend: 'canvas2d', glow has no effect.
A native-renderer styling combo: draw price as a glowing line on the GPU backend and make panning instant while keeping the eased zoom.
new Vela('#chart', {
data: bars,
priceStyle: 'line', // candles | bars | line | area | baseline
nativeBackend: 'webgl2', // force the GPU backend
glow: 0.6, // neon bloom on line series (WebGL2 only)
animations: { zoom: true, pan: false }, // eased zoom, no pan momentum
});Per-indicator options
The optional second argument to addIndicator(source, options) — runIndicator and
runScript take the same object:
| Option | Type | Meaning |
|---|---|---|
id | string | The indicator's id on the chart — an opaque, non-empty string you own (a document key, an editor tab, a UUID). It is what handle.id, chart.indicators(), the indicator:* events, script:run and the legend/pane surfaces carry, so you can address the indicator without keeping a side map. Omit it and the chart mints one — stable while the indicator stays on the chart, not across reloads. An id already live on the chart is rejected, never renamed: addIndicator throws, runIndicator/runScript resolve { ok: false, error }. Removing the indicator frees its id. |
language | string | Which registered engine runs this script. Defaults to the chart's defaultLanguage. |
inputs | Record<string, InputValue> | Input overrides, keyed by input title or key. |
props | Record<string, InputValue> | Declaration-property overrides (a strategy's initial_capital, an indicator's precision, …), keyed like the engine's props schema. Ignored by engines without props support. |
overlay | boolean | Force overlay vs. separate pane. Default: read from indicator(overlay=…). |
pane | 'price' | 'new' | Explicit pane placement. |
title | string | Display title override. |
See API reference for the IndicatorHandle you get back, and Quickstart for the end-to-end flow.