Charting and the Datafeed¶
The dashboard's price chart is the licensed TradingView Advanced Charts library, hosted by src/components/chart/onix-advanced-chart.tsx against vendored assets under public/static/charting_library/ and matching types under src/vendor/charting-library/. This page describes how ONIX's own code feeds that library data — symbol resolution, resolutions, bar history, live updates, caching, and gap handling — without restating TradingView's own documentation, which is not reproduced here. A second, unrelated charting library, lightweight-charts, remains in use only on the older /lab and /workspace consoles (src/components/engine-chart.tsx, src/renderer/lightweight-charts-renderer.ts); the dashboard chart described below does not use it.
The datafeed is a thin adapter over spine.v2¶
src/datafeed/gateway-datafeed.ts implements TradingView's IBasicDataFeed interface entirely against the backend's spine.v2 HTTP/SSE surface, through the shared @/gateway/v2 transport (fetchV2, subscribeToV2Stream). The module's own documentation is explicit that it invents nothing: "no invented instruments, no invented prices, no lane-local reimplementation of a transport another lane already built and tests." It implements only IBasicDataFeed, deliberately not IDatafeedQuotesApi — the code notes that the backend's live quote events are "currently silent on Hyperliquid (measured: 0 quote counters)," so an absent quotes implementation is treated as the honest answer rather than a guessed one.
Symbol resolution. resolveSymbol looks up an instrument through /v2/instruments, then derives every LibrarySymbolInfo field from what the backend actually published: pricescale/minmov are computed exactly from the instrument's own price_increment decimal string (priceScaleFromIncrement), volume_precision comes from the published size_precision, and supported_resolutions is read from the same published resolution roster onReady uses — "so the two can never advertise different things."
Resolutions are a published contract, not a hardcoded menu¶
Historically this datafeed hardcoded a resolution menu that included entries the backend did not actually serve ('3' and '120'), which produced a chart that offered timeframes it could not answer. At this pin, the resolution menu is read at runtime from GET /v2/resolutions (src/datafeed/resolution-contract.ts), which publishes, per resolution key (e.g. 1m, 4h, 1d), the exact canonical Nautilus bar_type suffix, whether the series is read off the durable 1-minute catalog (source: "catalog") or derived on demand from it (source: "derived"), and its exact step in nanoseconds. src/datafeed/resolution-map.ts is the one module that converts between that backend vocabulary and TradingView's resolution grammar (resolutionForBarTypeComponents, barTypeComponentsForResolution), and both onReady and resolveSymbol derive their advertised roster from the same cached read, so the chart never offers a timeframe it cannot serve. The result observed by the project: submitting the chart-spelled bar type to a backtest was measured, on a real Nautilus BacktestNode, to silently return zero iterations and zero orders against the correctly-spelled series' 1,492 iterations and 52 orders — which is why a separate re-spelling function (catalogBarTypeSuffix) exists specifically for the backtest path.
History versus live bars¶
getBars translates one TradingView PeriodParams request into one or more paginated GET /v2/bars reads, following the backend's opaque next_cursor up to a bounded ceiling (20 pages of up to 1,000 bars each). A countBack-priority request anchors on the newest bars at or before an end timestamp; a plain window request translates TradingView's half-open, bucket-open convention into the backend's inclusive, bucket-close convention (ts_event), with the exact conversion documented in planBarsRequest. Every returned row's bar_type is checked against the exact series that was requested before any bar is converted, specifically to prevent a wrong-series answer from silently shifting every displayed timestamp by one step.
Live updates (subscribeBars) poll the newest closed bar (count_back=1) on a short interval and open one spine.v2 ticks SSE subscription per chart series. Because the backend "publishes only OFFICIAL, CLOSED bars" and has no partial-bar field, subscribeBars builds a provisional current candle itself from the same trade ticks the gateway streams, so the forming bar updates live instead of sitting frozen until it closes. The provisional bar is discarded — never merged — the instant an official closed bar for its bucket arrives. A monotonic guard on the combined (closed + provisional) series ensures the chart's displayed time axis is never stepped backwards, because TradingView "hard-refuses time moving backwards" on its realtime channel (a "time violation," per the code's own note on a measured production defect).
Gaps, reconnects, and continuity¶
Reconnect behavior on the tick stream follows a documented "reconnect-resubscribe-backfill" policy rather than a full chart reset: on reopen, the subscriber treats its in-progress provisional accumulation as incomplete and re-reads a bounded window (30 bars) of the newest closed bars to backfill anything missed. onResetCacheNeededCallback — which would blank and refetch the whole visible chart — is retained only as a last resort, for the one case backfill cannot repair on its own: a hole, where a bar the chart already carries a later bar for was never actually delivered, and the library's own monotonic ordering would otherwise make it permanently unreachable.
One limitation is stated candidly in the code rather than papered over: whether a given empty page from /v2/bars means "history genuinely ends here" or "the backend's still-backfilling catalog has nothing at this window right now" cannot currently be distinguished by the frontend, because the backend does not yet publish an explicit exhaustion/coverage signal on that route. The datafeed therefore always answers TradingView's noData as false rather than guess — (unverified as an engine fact: no coverage signal exists yet for this route at the pin; tracked by the project's own docs/contract-requests/026-backtest-run-controls.md §6 for the adjacent catalog-suffix problem). This is a deliberate, documented honesty tradeoff, not an oversight.
Numeric precision at the one boundary that needs it¶
Everywhere else in the datafeed and the gateway layer, prices and nanosecond timestamps are carried as exact decimal strings and BigInt, per a repository-wide convention enforced by lint rule. v2BarToTvBar and provisionalBarToTvBar are called out in the module's own documentation as "the ONLY functions in src/datafeed/** that may call Number(...)," because TradingView's own Bar shape is contractually a bag of IEEE-754 doubles — the charting library's wire format, not a choice this project made.
What is verified here versus what is a target¶
The behavior described above — resolution discovery, bar-type re-spelling, provisional-candle construction, reconnect-resubscribe-backfill, and the numeric-precision boundary — is verified against the datafeed's implementation and its accompanying unit tests at this pin (src/datafeed/gateway-datafeed.test.ts, resolution-map.test.ts, resolution-input.test.ts). The exhaustion/coverage signal noted above is a target-contract item: requested of the backend, not yet implemented. See ../backend/index.md for how the backend itself describes bar aggregation and catalog authority, and capability-status.md for the consolidated status table.
Evidence and source pins for this page
Verified. Current behaviour, confirmed in source at the pinned commit.
Verified on against the following immutable sources:
frontend@99f49dae:src/datafeed/gateway-datafeed.tsfrontend@99f49dae:src/datafeed/resolution-map.tsfrontend@99f49dae:src/datafeed/resolution-contract.tsfrontend@99f49dae:src/components/chart/onix-advanced-chart.tsxfrontend@99f49dae:AGENTS.md
Status tokens are defined on the documentation and status model page. Every pin on this site is listed under versions and source pins.