The library
Host the chart yourself
The chart as a software library rather than a frame: you host the files, you write the datafeed, and the whole API is in your own page with no boundary in the middle. It is built for trading platforms and exchanges. If what you want is a chart in an article, you want the embed instead, and this page will try to talk you into it.
Which one do you want
Both draw the same chart, from the same engine, with the same drawing tools and indicators. What differs is who holds the files and where your code runs.
| Embed | Library | |
|---|---|---|
| What you add to your page | A <div> and one <script> | A bundle you serve, plus the code that mounts it |
| Who hosts the chart files | We do | You do |
| Where your code runs | Your page talks to a frame across an origin boundary | In your page, calling the chart directly |
| Your market data | Optional. Public feeds by default, or your own via data-datafeed | Required. There is no default feed |
| Getting it | Nothing to ask for. Copy the snippet | On request, under an agreement |
| Cost | Free, no account, no key | Commercial |
| Our mark on the chart | Always. It is what makes the embed free | Yours to configure, including off |
| Upgrades | Automatic. We ship, your chart changes | You choose when, by replacing the files |
What you are taking on
Named before anything else, because these are the parts that are invisible from a feature list and are most of the work.
| Yours | What it means in practice |
|---|---|
| Serving the files | A bundle, a stylesheet and around ninety lazily-loaded chunks, served from your own origin with correct caching. The chunks are content-hashed, so they are safe to cache forever; the entry is not. |
| The datafeed | There is no default feed and no fallback. Until you implement 10 methods, two of them required, the chart draws nothing. See Methods you implement, which is the same contract in both products. |
| Upgrades | A new version is a new set of files you deploy. Nothing reaches your users until you do it, which is the point, and it also means a fix does not reach them until you do it. |
| Untrusted indicators, if you offer them | Running code your users wrote needs the sandbox guest page on a separate site. It ships in the package; deploying it is yours, and skipping it means a script can freeze the tab it runs in. |
| Support for your own integration | We answer questions about the library. The chart inside your product is your product. |
What you get
One framework-agnostic bundle with no runtime dependencies, and the full API rather than the subset a message channel can carry.
| Surface | Count | What it covers |
|---|---|---|
| Widget options | 63 | Everything settable at construction: the symbol, the interval, the theme, the locale, the feature set, the save-and-load adapter. |
| Widget methods | 81 | The chart as an object: symbol and interval, theme, layout, screenshots, save and load, subscriptions. |
| Chart methods | 78 | One chart within it: drawings, studies, trading lines, the price scale, coordinate conversion, marks. |
| Datafeed methods | 10 | The contract you implement. Two required, the rest optional and each one a feature that switches itself off when absent. |
exact, compatible or partial, and the package ships that table as compatibility-report.json alongside the code. Read it before promising a specific behaviour to your own users: partial means the method exists and answers, not that it does everything its name suggests. getCompatibilityManifest() returns the same object at runtime.Use your own pair selector
If your application already has a pair selector, include 'header_symbol_search' in the widget's disabled_features array. This removes the chart toolbar's symbol button while keeping its other controls. Your own selector can still switch the chart through widget.setSymbol(symbol, interval).
disabled_features: ['header_symbol_search']Chart settings close when you click outside the floating panel. Changes apply immediately; closing the panel keeps them. Its colour pickers and menus remain usable.
Trading line styles
Order and position adapters expose matching set…() and get…() methods for every style. Customize colors, fonts, line dashes, horizontal and vertical padding, left and right insets, outline width, independent body, quantity and button radii, button width and icon stroke. Position adapters also expose P&L colors and protection-label and drag-out button styles. All are included in the compiled and minified distributions. See the embed styling guide for the complete property table and defaults; turn a property such as bodyPaddingHorizontal into setBodyPaddingHorizontal() or getBodyPaddingHorizontal().
const position = widget.createPositionLine({ bodyAlignment: 'left', bodyPaddingLeft: 16 })
.setPrice(100).setQuantity('2').setText('Long')
.setBodyFont('600 14px system-ui').setQuantityFont('12px system-ui')
.setBodyPaddingHorizontal(10).setBodyPaddingVertical(6)
.setBodyPaddingRight(16).setBodyBorderWidth(2)
.setBodyBorderRadius(12).setQuantityBorderRadius(4).setButtonBorderRadius(100)
.setButtonWidth(28).setButtonIconWidth(2)
.setBodyBackgroundColor('#ffffff').setBodyTextColor('#166534')
.setPnlPositiveColor('#16a34a').setPnlNegativeColor('#dc2626')
.setProtectLabelFont('600 12px system-ui')
.setProtectLabelPaddingHorizontal(8).setProtectLabelPaddingVertical(4)
.setProtectLabelBorderRadius(8).setProtectLabelBorderWidth(1)
.setProtectLabelBackgroundColor('#ffffff').setProtectLabelTextColor('#334155');
position.setBodyBorderRadius(null); // inherit the shared borderRadius again
position.setPnlPositiveColor(null); // follow the chart’s up color againKeep the latest candles clear
Order and position lines support setBodyAlignment('left' | 'right') and getBodyAlignment(). You can also pass { bodyAlignment: 'left' } to either creation method, including on widget.activeChart(). Right is the default. Left moves the whole group to the plot's left edge: quantity, label, P&L and available action buttons. Position TP/SL, breakeven and drag-preview labels follow. Line length and price-axis placement stay independent. Use bodyPaddingLeft: 16 or setBodyPaddingLeft(16) to inset left-aligned controls by 16 pixels. This API is available in the standalone build, including the minified distribution.
const position = widget.createPositionLine({ bodyAlignment: 'left', bodyPaddingLeft: 16 })
.setPrice(100).setQuantity('1').setText('Long');
const order = widget.createOrderLine({ bodyAlignment: 'left', bodyPaddingLeft: 16 })
.setPrice(95).setText('Buy limit');
// Call on container resize; choose the breakpoint for your layout.
function alignLineControls(containerWidth: number) {
const alignment = containerWidth < 600 ? 'left' : 'right';
position.setBodyAlignment(alignment);
order.setBodyAlignment(alignment);
}Tint trading sessions
widget.setSessions(bands) tints sessions behind the bars and paints a matching ribbon on the time axis; widget.getSessions() reads them back, and DEFAULT_US_SESSIONS is exported for the two US equity bands. The chart ships with none, because crypto trades around the clock. A band's from and to are HH:mm read on the symbol's own clock, the zone resolveSymbol reported, unless the band names its own timezone; to at or before from wraps midnight, and days (0 is Sunday, 6 is Saturday) defaults to weekdays. The colour carries its own alpha, in rgba() or hex-with-alpha form.
import { DEFAULT_US_SESSIONS } from '@warburgai/vp-charts';
widget.setSessions(DEFAULT_US_SESSIONS);
widget.setSessions([
{ name: 'Frankfurt', from: '09:00', to: '17:30', color: 'rgba(41, 98, 255, 0.05)' },
{ name: 'Tokyo', from: '09:00', to: '15:00', color: '#ff98000d', timezone: 'Asia/Tokyo' },
]);
widget.setSessions([]); // clears themEarlier builds read the hours in UTC whatever the symbol’s zone, so a US pre-market band landed in the middle of the New York night. A band with no zone on a UTC symbol draws exactly as before.
Feed the footprint chart
The footprint chart type draws volume per price level inside each bar, and it draws only what your datafeed supplies; it never invents a distribution from the candle. Three optional ways to supply it, and they compose: levels on the bars getBars returns, getFootprint(symbolInfo, resolution, period, onResult, onError) for a history window, and subscribeTrades / unsubscribeTrades for live prints. The last two are called only while the footprint type is showing, so implementing them costs nothing on a candle chart.
const feed = {
...bars,
getFootprint(symbolInfo, resolution, { from, to }, onResult, onError) {
// from / to in SECONDS; each slice's time is the bar's open in MILLISECONDS
fetchFlow(symbolInfo.ticker, resolution, from, to).then(
(rows) => onResult(rows.map((r) => ({ time: r.openMs, levels: r.levels, levelStep: 0.5 }))),
(e) => onError(String(e)),
);
},
subscribeTrades(symbolInfo, resolution, onTrade, guid) {
tapes.set(guid, openTape(symbolInfo.ticker, (t) =>
onTrade({ price: t.px, size: t.qty, side: t.aggressor, timestamp: t.ms })));
},
unsubscribeTrades(guid) { tapes.get(guid)?.close(); tapes.delete(guid); },
};On a bar, absent and empty are different facts: no levels means you supplied no order flow for that bar and the column is left blank; levels: [] means nothing traded. levelStep is the price grid the levels sit on (absent: the symbol's tick, then the levels' own spacing) and footprintSource grades the view as exchange_trades, sub_candles or synthetic, with anything below the first drawn with a quality ring. A slice from getFootprint requires levels; a trade's side is the aggressor and it lands in the bar its timestamp falls in. The same three methods are available to a hosted embed through its datafeed bridge, and the fields are described on the embed datafeed reference.
What is deliberately not in it
| Not included | Why |
|---|---|
| Market data | We distribute a charting engine, not a data licence. The embed's public feeds are a convenience attached to a free product, and they do not come with the library. |
| A server | Nothing in the package calls home. There is no licence check at runtime, no telemetry and no request to us, which is also why an air-gapped deployment works and why we cannot tell you how many charts you are running. |
| The community indicator shop | Reading and running indicators other people published is a hosted service tied to an account. The scripting ENGINE is in the package; the catalogue is not. |
Asking for it
The library is not a download link, and that is the one thing it has in common with every other product of its kind: it ships under an agreement, so there is a conversation before there are files.
Write to [support@verticalprop.com](mailto:support@verticalprop.com) with what you are building, roughly how many people will use it, and where the market data is coming from. That last one matters more than it looks: it is the part with no default, and it is where an evaluation usually stalls.