Skip to Content
TutorialsImplement DatafeedDatafeed implementation

Datafeed implementation

Implement history and symbol resolution. After this step the chart loads candles; live updates come in Streaming implementation.

Required methods

MethodRole
resolveSymbolMap a symbol string → GoCharting security / SymbolInfo (precision, session, intervals, exchange_info, …)
getBarsReturn historical OHLCV for a time range

Optional but useful: searchSymbols so the top-bar search works.

resolveSymbol

onResolve receives a security / SymbolInfo object in GoCharting’s native shape (not a minimal ticker stub). Production backends typically return this JSON as-is from symbol lookup.

resolveSymbol(symbolName, onResolve, onError) { // Example: "BYBIT:FUTURE:BTCUSDT" const parts = symbolName.split(":"); const exchange = parts[0] || "BYBIT"; const segment = parts[1] || "FUTURE"; const symbol = parts[parts.length - 1] || "BTCUSDT"; try { // Prefer fetching this object from your symbol API. // Shape below matches what GoCharting uses internally. onResolve({ exchange, segment, symbol, name: "BTC / USDT PERPETUAL FUTURES", asset_type: "CRYPTO", source_id: symbol, pair: { from: "BTC", to: "USDT" }, tradeable: true, is_index: false, is_formula: false, delay_seconds: 0, data_status: "streaming", // or "delayed_streaming" | "endofday" | "pulsed" industry: "technology", symbol_logo_urls: [ "https://upload.wikimedia.org/wikipedia/commons/a/a4/Flag_of_the_United_States.svg", ], contract_size: 1, tick_size: 0.1, display_tick_size: 1, volume_size_increment: 0.001, max_tick_precision: 1, max_volume_precision: 3, quote_currency: "USDT", future_type: "PERP", data_source_location: "blr1", supports: { footprint: true, }, exchange_info: { name: "bybit", code: "BYBIT", country_cd: "US", zone: "UTC", has_unique_trade_id: true, logo_url: "https://gocharting.com/avatar/Bybit_Logo.svg", holidays: null, hours: [ { open: true }, { open: true }, { open: true }, { open: true }, { open: true }, { open: true }, { open: true }, ], contains_ambiguous_symbols: false, valid_intervals: [ "1m", "3m", "5m", "10m", "15m", "30m", "1h", "2h", "4h", "12h", "1D", "1W", "1M", ], }, // Compatibility fields some chart paths still read ticker: symbol, full_name: `${exchange}:${segment}:${symbol}`, description: "BTC / USDT PERPETUAL FUTURES", type: "crypto", session: "24x7", timezone: "UTC", has_intraday: true, has_daily: true, supported_resolutions: [ "1m", "3m", "5m", "10m", "15m", "30m", "1h", "2h", "4h", "12h", "1D", "1W", "1M", ], }); } catch (e) { onError(String(e?.message || e)); } }

Call exactly one of onResolve or onError.

Important fields for chart behaviour:

FieldWhy it matters
exchange / segment / symbolIdentity used in streams and history requests
nameDisplay title (also used as fallback if symbol is missing)
tick_size, max_tick_precision, max_volume_precisionPrice/volume formatting
exchange_info.valid_intervalsInterval picker allowlist
exchange_info.hours / zoneSession and timezone
data_status, delay_secondsLive vs delayed labelling
supports.footprintWhether footprint features are enabled for the symbol

See Datafeed API — Symbol Info Format for the full field reference.

getBars

async getBars(symbolInfo, resolution, periodParams) { const { from, to, firstDataRequest, rows } = periodParams; const qs = new URLSearchParams({ symbol: symbolInfo.full_name || symbolInfo.name, interval: String(resolution), from: String(from.getTime()), to: String(to.getTime()), }); if (rows != null) qs.set("rows", String(rows)); const res = await fetch(`https://api.example.com/bars?${qs}`); if (!res.ok) { return { bars: [], meta: { noData: true } }; } const raw = await res.json(); const bars = (raw.bars || raw).map((b) => ({ time: b.time ?? b.timestamp, // Unix timestamp in milliseconds open: Number(b.open), high: Number(b.high), low: Number(b.low), close: Number(b.close), volume: Number(b.volume ?? 0), })); return { bars, meta: { noData: bars.length === 0, // nextTime: optional unix ms hint for pagination }, }; }

Period params

FieldMeaning
from / toDate range requested
firstDataRequesttrue on the first load for this symbol/interval
rowsOptional bar count hint

Return { bars: [], meta: { noData: true } } when there is nothing in range (do not throw for empty history).

For the hosted demo history/tick protocol, see Demo WebSocket.

Optional: searchSymbols

searchSymbols(userInput, exchange, symbolType, onResultReadyCallback) { onResultReadyCallback({ searchInProgress: true, items: [] }); fetch(`https://api.example.com/search?q=${encodeURIComponent(userInput)}`) .then((r) => r.json()) .then((items) => { onResultReadyCallback({ searchInProgress: false, items: items.map((it) => ({ symbol: it.symbol, full_name: it.full_name, description: it.description, exchange: it.exchange, type: it.type || "crypto", })), }); }) .catch(() => { onResultReadyCallback({ searchInProgress: false, items: [] }); }); }

Optional: searchSymbolsPaginated

Preferred when present (chart search uses offset: 0, limit: 50). Lab: phase6-searchSymbolsPaginated-lab.html.

searchSymbolsPaginated(options, onResult) { const { userInput, exchange, symbolType, limit, offset } = options; const page = yourBackend.searchPage({ userInput, exchange, symbolType, limit, offset }); onResult(page); // SearchResult[] or { searchInProgress, items } }

The callback receives an object { searchInProgress, items } — not a bare array (same for searchSymbols).

Optional: onReady / UDF adapter

Implement onReady to advertise exchanges, symbol types, and supported_resolutions (used by getIntervals() when the symbol has no valid_intervals). Or use UDFCompatibleDatafeed against a UDF HTTP backend. Lab: phase6-onReady-udf-lab.html.

Limitation: GoCharting does not client-aggregate higher timeframes from daily the way TradingView does with daily_multipliers (2D, 6M, …). Return bars for the requested resolution. Details: TV → GC migration — Known limitations.

Putting it together

const datafeed = { resolveSymbol(symbolName, onResolve, onError) { /* as above */ }, async getBars(symbolInfo, resolution, periodParams) { /* as above */ }, searchSymbols(userInput, exchange, symbolType, onResultReadyCallback) { /* as above */ }, }; createChart("#chart", { datafeed, symbol: "BYBIT:FUTURE:BTCUSDT", interval: "1m", licenseKey: "demo-550e8400-e29b-41d4-a716-446655440000", });

Checklist

  • resolveSymbol always calls onResolve or onError
  • Resolved security includes exchange / segment / symbol, precision fields, and preferably exchange_info
  • Bars include open / high / low / close (and usually volume + time)
  • Empty ranges use meta.noData: true
  • Search (if implemented) uses { searchInProgress, items }

Next

Streaming implementation — push live ticks with subscribeTicks

Last updated on