Skip to Content
API ReferenceData Feed

πŸ”Œ Datafeed API

The Datafeed API defines the interface for implementing custom data sources in the GoCharting SDK. It provides methods for fetching market data, resolving symbols, and providing real-time updates.

πŸ“‹ Datafeed Interface

The Datafeed interface has 2 required methods and 5 optional methods:

Required Methods:

  • getBars() - Fetch historical bar data
  • resolveSymbol() - Resolve symbol information

Optional Methods:

  • searchSymbols() - Enable symbol search functionality
  • subscribeTicks() - Provide real-time data updates
  • unsubscribeTicks() - Cancel real-time subscriptions
  • getMarks() - Display marks/events on chart
  • getTimescaleMarks() - Display timescale marks
  • destroy() - Cleanup resources when chart is destroyed
type Datafeed = { // Required getBars( symbolInfo: SymbolInfo, resolution: string | Resolution, periodParams: PeriodParams ): Promise<BarsResult | UDFResponse>; resolveSymbol( symbolName: string, onResolve: (symbolInfo: SymbolInfo) => void, onError: (error: string) => void ): void; // Optional searchSymbols?( userInput: string, exchange: string, symbolType: string, onResultReadyCallback: (result: SearchResult[]) => void ): void; subscribeTicks?( symbolInfo: SymbolInfo, resolution: string | Resolution, onRealtimeCallback: (data: Bar | Tick | TradeMessage) => void, subscriberUID: string, onResetCacheNeededCallback?: () => void ): void; unsubscribeTicks?(subscriberUID: string): void; getMarks?( symbolInfo: SymbolInfo, startDate: number, endDate: number, onDataCallback: (marks: Mark[]) => void, resolution: string | Resolution ): void; getTimescaleMarks?( symbolInfo: SymbolInfo, from: number, to: number, onDataCallback: (marks: TimescaleMark[]) => void, resolution: string | Resolution ): void; destroy?(): void; };

πŸ“‹ Required Methods

getBars(symbolInfo, resolution, periodParams)

Fetches historical bar data for the specified symbol and time range. This is a required method.

Signature:

getBars( symbolInfo: SymbolInfo, resolution: string | Resolution, periodParams: PeriodParams ): Promise<BarsResult | UDFResponse>

Parameters:

  • symbolInfo (SymbolInfo): Symbol information from resolveSymbol
  • resolution (string | Resolution): Time interval (e.g., β€œ1m”, β€œ1H”, β€œ1D”) or Resolution object {scale, units}
  • periodParams (PeriodParams): Time range parameters:
    { from: Date; // Start time to: Date; // End time firstDataRequest: boolean; // True if this is the first request rows?: number; // Number of bars requested }

Returns: Promise resolving to either:

  1. BarsResult format (recommended):
{ bars: Bar[]; // Array of bar objects meta?: { // Optional metadata noData?: boolean; nextTime?: number; }; }
  1. UDF format (legacy):
{ s: "ok" | "no_data" | "error"; t: number[]; // Unix timestamps in seconds o: number[]; // Open prices h: number[]; // High prices l: number[]; // Low prices c: number[]; // Close prices v: number[]; // Volumes }

Implementation Example (BarsResult format):

async getBars(symbolInfo, resolution, periodParams) { const { from, to, firstDataRequest } = periodParams; // Convert resolution to your API format const interval = typeof resolution === "string" ? resolution : `${resolution.scale}${resolution.units}`; try { // Fetch data from your API const response = await fetch( `/api/bars?symbol=${symbolInfo.symbol}&interval=${interval}&from=${from.getTime()}&to=${to.getTime()}` ); const data = await response.json(); // Return in BarsResult format return { bars: data.map(bar => ({ time: bar.timestamp, // Unix timestamp in milliseconds open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume, })), }; } catch (error) { console.error("getBars error:", error); return { bars: [], meta: { noData: true } }; } }

Implementation Example (UDF format):

async getBars(symbolInfo, resolution, periodParams) { const { from, to } = periodParams; try { const response = await fetch( `/api/bars?symbol=${symbolInfo.symbol}&interval=${resolution}&from=${from.getTime()}&to=${to.getTime()}` ); const data = await response.json(); // Return in UDF format return { s: "ok", t: data.map(bar => Math.floor(bar.timestamp / 1000)), // Seconds o: data.map(bar => bar.open), h: data.map(bar => bar.high), l: data.map(bar => bar.low), c: data.map(bar => bar.close), v: data.map(bar => bar.volume), }; } catch (error) { console.error("getBars error:", error); return { s: "no_data" }; } }

getTimeMarks(symbolInfo, from, to, resolution)

Fetches time marks (events) for the specified symbol and time range. Time marks appear as colored circles on the chart with hover tooltips and optional vertical lines.

Parameters:

  • symbolInfo (object): Symbol information from resolveSymbol
  • from (number): Start time as Unix timestamp in seconds
  • to (number): End time as Unix timestamp in seconds
  • resolution (object): Resolution object with {scale, units} format

Implementation Example:

async getTimeMarks(symbolInfo, from, to, resolution) { // Fetch time marks from your API const response = await fetch(`/api/timemarks?symbol=${symbolInfo.symbol}&from=${from}&to=${to}`); const events = await response.json(); // Return marks in UDF format return events.map(event => ({ id: event.id, // Unique identifier (string|number) time: event.timestamp, // Unix timestamp in seconds color: event.color || 'blue', // CSS color for circle text: event.description, // Tooltip text (string or array) label: event.label || 'E', // Single character for circle labelFontColor: event.labelColor || 'white', // Label text color minSize: event.size || 25 // Minimum circle size in pixels })); }

Real-world Examples:

// Example 1: Earnings Report { id: 'earnings_2024_q1', time: 1704067200, // January 1, 2024 color: 'green', text: ['Q1 Earnings', 'EPS: $2.45', 'Revenue: $12.3B'], label: 'E', labelFontColor: 'white', minSize: 30 } // Example 2: News Event { id: 'news_merger_announcement', time: 1704153600, // January 2, 2024 color: '#ff6b35', text: 'Major acquisition announced', label: 'N', labelFontColor: 'white', minSize: 25 } // Example 3: Dividend Payment { id: 'dividend_2024_q1', time: 1704240000, // January 3, 2024 color: 'purple', text: ['Dividend Payment', '$0.85 per share', 'Ex-date: Jan 3, 2024'], label: 'D', labelFontColor: 'yellow', minSize: 28 }

Return Format Specification:

Each time mark object must contain these properties:

PropertyTypeRequiredDescription
idstring|numberβœ…Unique identifier for the mark
timenumberβœ…Unix timestamp in seconds
colorstringβœ…CSS color for the mark circle (e.g., β€˜red’, β€˜#ff0000’, β€˜rgb(255,0,0)β€˜)
textstring|arrayβœ…Tooltip content. Arrays display each item on separate lines
labelstringβœ…Single character to display in the circle
labelFontColorstring❌Color for the label text (default: β€˜white’)
minSizenumber❌Minimum circle size in pixels (default: 25)

Supported Text Formats:

// Single line tooltip text: "Earnings announcement"; // Multi-line tooltip (recommended for detailed events) text: [ "Q4 Earnings Report", "EPS: $3.21 (Beat by $0.15)", "Revenue: $15.2B (Up 12% YoY)", "Guidance: Raised for 2024", ]; // HTML is not supported - use plain text only

Color Examples:

// Named colors color: "red"; color: "blue"; color: "green"; color: "purple"; color: "orange"; // Hex colors color: "#ff0000"; // Red color: "#00ff00"; // Green color: "#0000ff"; // Blue color: "#ff6b35"; // Orange // RGB colors color: "rgb(255, 0, 0)"; // Red color: "rgba(255, 0, 0, 0.8)"; // Semi-transparent red

Returns: Promise<object> - Object with bars array and optional meta

resolveSymbol(symbolName, onResolve, onError)

Resolves a symbol name to detailed symbol information.

Warning

Symbol Naming Convention: The symbol field must NOT contain special characters such as /, \, :, *, ?, ", <, >, or |.

Use only alphanumeric characters (e.g., EURUSD instead of EUR/USD, BTCUSDT instead of BTC/USDT). Human-readable names with special characters can be placed in the name or description field.

async resolveSymbol(symbolName, onResolve, onError) { try { // Parse symbol name (e.g., "BYBIT:FUTURE:BTCUSDT") const [exchange, segment, symbol] = symbolName.split(':'); // Fetch symbol info from your API const response = await fetch(`/api/symbols/${exchange}/${segment}/${symbol}`); const symbolData = await response.json(); // The API returns a complete symbolInfo object with all required fields // You can use it directly or map specific fields as needed const symbolInfo = { // Basic Information exchange: symbolData.exchange, segment: symbolData.segment, symbol: symbolData.symbol, name: symbolData.name, asset_type: symbolData.asset_type, source_id: symbolData.source_id, // Pair Information (for crypto/forex) pair: symbolData.pair, // Trading Information tradeable: symbolData.tradeable, is_index: symbolData.is_index, is_formula: symbolData.is_formula, // Data Feed Information delay_seconds: symbolData.delay_seconds, data_status: symbolData.data_status, data_source_location: symbolData.data_source_location, // Display Information industry: symbolData.industry, symbol_logo_urls: symbolData.symbol_logo_urls, // Price & Volume Precision contract_size: symbolData.contract_size, tick_size: symbolData.tick_size, display_tick_size: symbolData.display_tick_size, volume_size_increment: symbolData.volume_size_increment, max_tick_precision: symbolData.max_tick_precision, max_volume_precision: symbolData.max_volume_precision, quote_currency: symbolData.quote_currency, // Futures-specific future_type: symbolData.future_type, // Feature Support supports: symbolData.supports, // Exchange Information exchange_info: symbolData.exchange_info, // Legacy/Compatibility Fields ticker: symbolData.symbol, full_name: `${symbolData.exchange}:${symbolData.segment}:${symbolData.symbol}`, description: symbolData.name, type: symbolData.asset_type?.toLowerCase() || 'crypto', session: symbolData.exchange_info?.hours?.every(h => h.open) ? '24x7' : '0930-1600', timezone: symbolData.exchange_info?.zone || 'UTC', has_intraday: true, has_daily: true, supported_resolutions: symbolData.exchange_info?.valid_intervals || ['1', '5', '15', '30', '60', '1D'], volume_precision: symbolData.max_volume_precision }; onResolve(symbolInfo); } catch (error) { onError('Symbol not found'); } }

Parameters:

  • symbolName (string): Symbol to resolve (e.g., β€œNASDAQ:AAPL”)
  • onResolve (function): Callback for successful resolution
  • onError (function): Callback for errors

πŸ”§ Optional Methods

searchSymbols(userInput, callback) (Optional)

Searches for symbols based on user input. If not implemented, symbol search functionality will be disabled in the chart.

async searchSymbols(userInput, callback) { try { const response = await fetch(`/api/search?q=${encodeURIComponent(userInput)}`); const results = await response.json(); const symbols = results.map(item => ({ symbol: item.symbol, full_name: item.full_name, description: item.description, exchange: item.exchange, ticker: item.ticker, type: item.type, key: item.key || `${item.exchange}:${item.segment || 'FUTURE'}:${item.symbol}` // REQUIRED: Unique key for symbol resolution })); callback(symbols); } catch (error) { callback([]); } }

Parameters:

  • userInput (string): Search query
  • callback (function): Callback with search results

_init(datacenter, token)

Initializes the data provider with connection parameters.

async _init(datacenter, token) { this.datacenter = datacenter; this.token = token; this.initialized = true; // Initialize your API connection await this.connectToAPI(); console.log('DataProvider initialized'); }

subscribeTicks(symbolInfo, resolution, onRealtimeCallback, subscriberUID, onResetCacheNeededCallback)

Subscribes to real-time tick data for the specified symbol.

Parameters:

  • symbolInfo - Symbol information object
  • resolution - Resolution object with {scale, units} format
  • onRealtimeCallback - Function to call with real-time trade data
  • subscriberUID - Unique identifier for this subscription
  • onResetCacheNeededCallback - Function to call when cache reset is needed

Implementation Example:

subscribeTicks(symbolInfo, resolution, onRealtimeCallback, subscriberUID, onResetCacheNeededCallback) { // Subscribe to real-time data stream const channel = `publicTrade.${symbolInfo.symbol}`; this.websocket.subscribe(channel, (data) => { // Process each trade in the data array data.forEach(trade => { const { T: timestamp, s, S: side, p: price, i, v: size } = trade; const tradeMessage = { type: "trade", productId: `${symbolInfo.exchange}:${symbolInfo.segment}:${s}`, symbol: s, exchange: symbolInfo.exchange, segment: symbolInfo.segment, timeStamp: new Date(timestamp), tradeID: i, price: Number(price), quantity: Number(size), amount: Number(price) * Number(size), side: side.toUpperCase(), }; onRealtimeCallback(tradeMessage); }); }); }

unsubscribeTicks(subscriberUID)

Unsubscribes from real-time tick data.

unsubscribeTicks(subscriberUID) { // Remove the subscription this.websocket.unsubscribe(subscriberUID); }

πŸ”„ Bar-to-Tick Conversion Helper

If your data provider only supports real-time bars (OHLC) instead of individual ticks, you can convert each bar into 4 tick events and push them to the SDK. This is useful for data sources that aggregate trades into bars before streaming.

When to Use This

  • Your WebSocket or API returns aggregated bar data (OHLC) instead of raw trades
  • You want to update the chart in real-time but only have bar-level granularity
  • Your data provider streams 1-second or 1-minute bars

Helper Function

/** * Converts a real-time bar (OHLC) into individual ticks. * * Tick order depends on bar direction: * - Bullish bar (Close > Open): Open β†’ Low β†’ High β†’ Close * - Bearish bar (Close < Open): Open β†’ High β†’ Low β†’ Close * * @param {Object} bar - Bar with { timestamp, open, high, low, close, volume } * @param {Object} symbolInfo - Symbol information from resolveSymbol * @returns {Array} Array of 4 tick objects ready for onRealtimeCallback */ function convertBarToTicks(bar, symbolInfo) { const { timestamp, open, high, low, close } = bar; const barTimestamp = typeof timestamp === "number" ? timestamp : new Date(timestamp).getTime(); const isBullish = close > open; // Determine tick order based on bar direction // Bullish: O β†’ L β†’ H β†’ C (price dips first, then rises) // Bearish: O β†’ H β†’ L β†’ C (price rises first, then falls) const tickSequence = isBullish ? [ { price: open, label: "OPEN", side: "BUY" }, { price: low, label: "LOW", side: "BUY" }, { price: high, label: "HIGH", side: "SELL" }, { price: close, label: "CLOSE", side: "BUY" }, ] : [ { price: open, label: "OPEN", side: "SELL" }, { price: high, label: "HIGH", side: "BUY" }, { price: low, label: "LOW", side: "SELL" }, { price: close, label: "CLOSE", side: "SELL" }, ]; // Convert to trade format expected by SDK return tickSequence.map((tick, index) => ({ type: "trade", productId: `${symbolInfo.exchange}:${symbolInfo.segment}:${symbolInfo.symbol}`, symbol: symbolInfo.symbol, exchange: symbolInfo.exchange, segment: symbolInfo.segment, timeStamp: new Date(barTimestamp + (index + 1) * 100), // Small offset between ticks tradeID: `bar_${barTimestamp}_${index + 1}`, price: Number(tick.price), quantity: 1, amount: Number(tick.price), side: tick.side, })); }

Usage Example

subscribeTicks(symbolInfo, resolution, onRealtimeCallback, subscriberUID, onResetCacheNeededCallback) { // Subscribe to bar stream from your data provider this.barWebSocket.subscribe(`bars.${symbolInfo.symbol}`, (bar) => { // bar = { timestamp: 1704067200000, open: 100.5, high: 101.2, low: 100.1, close: 101.0 } // Convert bar to ticks const ticks = convertBarToTicks(bar, symbolInfo); // Push each tick to the SDK // IMPORTANT: Push Open first, then High/Low (based on direction), then Close last ticks.forEach((tick, index) => { // Optional: Add small delay between ticks for smoother animation setTimeout(() => { onRealtimeCallback(tick); }, index * 50); // 50ms between each tick }); }); }

Key Points

  1. Always push Open first - The first tick should be the Open price
  2. Push Close last - The final tick should always be the Close price
  3. High/Low order matters - For bullish bars, Low comes before High; for bearish bars, High comes before Low
  4. Use current timestamps - Ensure timestamps are current (not historical) for the SDK to update the chart
  5. Maintain tick sequence - Don’t randomize the order; it affects how the candle builds visually

πŸ“… Duration-Based Date Range Helper

When implementing getBars, you may need to calculate explicit start and end dates based on a duration (e.g., β€œ1D”, β€œ1M”, β€œ1Y”) instead of using a fixed row count like rows=200. The SDK provides a utility function for this.

getDateRangeForDuration(duration)

Calculates start and end timestamps based on a duration string. This is useful when your data provider API requires explicit date ranges instead of row counts.

Import:

import { getDateRangeForDuration } from "@anthropic/gocharting-sdk";

Parameters:

  • duration (string): Duration string - "1D", "5D", "15D", "1M", "3M", "6M", "1Y", "5Y", "All"

Returns:

{ start_date: number, // Start timestamp in milliseconds end_date: number, // End timestamp in milliseconds (current time) interval: string // Recommended interval for this duration }

Duration to Interval Mapping:

DurationIntervalTime Range
"1D""1m"1 day
"5D""15m"5 days
"15D""30m"15 days
"1M""1h"30 days
"3M""2h"90 days
"6M""4h"180 days
"1Y""1D"365 days
"5Y""1W"5 years
"All""1M"20 years (max)

Usage Example

import { getDateRangeForDuration } from "@anthropic/gocharting-sdk"; // In your datafeed implementation async getBars(symbolInfo, resolution, periodParams) { const { duration } = periodParams; // If duration is provided, calculate date range if (duration) { const { start_date, end_date, interval } = getDateRangeForDuration(duration); // Fetch data using date range instead of row count const response = await fetch( `/api/bars?symbol=${symbolInfo.symbol}&interval=${interval}&start=${start_date}&end=${end_date}` ); const data = await response.json(); return formatBarsResponse(data); } // Fallback to standard row-based fetching const { from, to, rows } = periodParams; // ... standard implementation }

When to Use This

  • Your data provider API requires start_date and end_date parameters
  • You want to fetch exactly the right amount of data for a duration (e.g., 1440 bars for 1 day of 1-minute data)
  • You’re implementing duration buttons (1D, 5D, 1M, etc.) in your chart UI
  • You want to avoid over-fetching or under-fetching data

πŸ“Š Data Formats

UDF Bar Format (getBars return)

{ s: "ok", // Status: "ok" | "no_data" | "error" t: [1609459200, 1609459260], // Unix timestamps in seconds (array) o: [100.50, 101.20], // Open prices (array) h: [102.75, 103.40], // High prices (array) l: [99.25, 100.80], // Low prices (array) c: [101.80, 102.60], // Close prices (array) v: [1500000, 1200000] // Volumes (array) }

Trade Message Format (subscribeTicks callback)

{ type: "trade", // Always "trade" productId: "BYBIT:FUTURE:BTCUSDT", // Exchange:Segment:Symbol format symbol: "BTCUSDT", // Symbol name exchange: "BYBIT", // Exchange name segment: "FUTURE", // Market segment timeStamp: new Date(), // JavaScript Date object tradeID: "abc123", // Unique trade identifier price: 45123.45, // Trade price as number quantity: 0.123, // Trade quantity as number amount: 5555.67, // Trade amount (price * quantity) side: "BUY" | "SELL" // Trade side (uppercase) }

Resolution Object Format

{ scale: "minutes" | "hours" | "days" | "weeks" | "months", units: 1 | 5 | 15 | 30 | 60 | 240 // Number of scale units } // Examples: // 1 minute: { scale: "minutes", units: 1 } // 5 minutes: { scale: "minutes", units: 5 } // 1 hour: { scale: "hours", units: 1 } // 4 hours: { scale: "hours", units: 4 } // 1 day: { scale: "days", units: 1 }

Symbol Info Format (resolveSymbol return)

{ // Basic Symbol Information exchange: 'BYBIT', // Exchange name segment: 'FUTURE', // Market segment (FUTURE, SPOT, OPTION, etc.) symbol: 'BTCUSDT', // Trading symbol name: 'BTC / USDT PERPETUAL FUTURES', // Display name asset_type: 'CRYPTO', // Asset type: 'CRYPTO' | 'EQUITY' | 'FOREX' | 'COMMODITY' source_id: 'BTCUSDT', // Source identifier from exchange // Symbol Pair Information (for crypto/forex) pair: { from: 'BTC', // Base currency to: 'USDT' // Quote currency }, // Trading Information tradeable: true, // Whether symbol is tradeable is_index: false, // Whether symbol is an index is_formula: false, // Whether symbol is a formula/calculated // Data Feed Information delay_seconds: 600, // Data delay in seconds (0 for real-time) data_status: 'delayed_streaming', // 'streaming' | 'endofday' | 'pulsed' | 'delayed_streaming' data_source_location: 'blr1', // Data source location/datacenter // Display Information industry: 'technology', // Industry classification (optional) symbol_logo_urls: [ // Array of logo URLs (optional) 'https://upload.wikimedia.org/wikipedia/commons/a/a4/Flag_of_the_United_States.svg' ], // Price & Volume Precision contract_size: 1, // Contract size (for futures/options) tick_size: 0.1, // Minimum price movement display_tick_size: 1, // Display tick size volume_size_increment: 0.001, // Minimum volume increment max_tick_precision: 1, // Maximum decimal places for price max_volume_precision: 3, // Maximum decimal places for volume quote_currency: 'USDT', // Quote currency // Futures-specific (if applicable) future_type: 'PERP', // 'PERP' for perpetual, or expiry date for dated futures // Exchange Information exchange_info: { name: 'bybit', // Exchange name (lowercase) code: 'BYBIT', // Exchange code (uppercase) country_cd: 'US', // Country code zone: 'UTC', // Timezone has_unique_trade_id: true, // Whether exchange provides unique trade IDs logo_url: 'https://gocharting.com/avatar/Bybit_Logo.svg', // Exchange logo URL holidays: null, // Array of holiday dates (null if 24/7) hours: [ // Trading hours for each day (0=Sunday, 6=Saturday) { open: true }, // Sunday { open: true }, // Monday { open: true }, // Tuesday { open: true }, // Wednesday { open: true }, // Thursday { open: true }, // Friday { open: true } // Saturday ], contains_ambiguous_symbols: false, // Whether exchange has ambiguous symbol names valid_intervals: [ // Supported timeframes '1m', '3m', '5m', '10m', '15m', '30m', '1h', '2h', '4h', '12h', '1D', '1W', '1M' ] }, // Legacy/Compatibility Fields (still supported) ticker: 'BTCUSDT', // Ticker symbol (same as symbol) full_name: 'BYBIT:FUTURE:BTCUSDT', // Full symbol name with exchange:segment:symbol description: 'BTC / USDT PERPETUAL FUTURES', // Human-readable description (same as name) type: 'crypto', // Symbol type (derived from asset_type) session: '24x7', // Trading session timezone: 'UTC', // Timezone (from exchange_info.zone) has_intraday: true, // Supports intraday data has_daily: true, // Supports daily data supported_resolutions: [ // Supported timeframes (from exchange_info.valid_intervals) '1', '3', '5', '10', '15', '30', '60', '120', '240', '720', '1D', '1W', '1M' ], volume_precision: 3 // Volume decimal places (same as max_volume_precision) }

Search Results Format (searchSymbols callback)

[ { symbol: "BTCUSDT", // Symbol identifier full_name: "BYBIT:BTCUSDT", // Full symbol name with exchange description: "Bitcoin / Tether", // Human-readable description exchange: "BYBIT", // Exchange name type: "crypto", // Symbol type key: "BYBIT:FUTURE:BTCUSDT", // REQUIRED: Unique key for symbol resolution and compare functionality }, { symbol: "ETHUSDT", full_name: "BYBIT:ETHUSDT", description: "Ethereum / Tether", exchange: "BYBIT", type: "crypto", key: "BYBIT:FUTURE:ETHUSDT", // REQUIRED: Unique key for symbol resolution and compare functionality type: "crypto", }, ];

Period Parameters Format (getBars parameter)

{ from: new Date('2024-01-01'), // Start date (JavaScript Date object) to: new Date('2024-01-31'), // End date (JavaScript Date object) firstDataRequest: true, // Boolean: true for initial load, false for historical rows: 300 // Number of bars to fetch }

Time Marks Format (getTimeMarks return)

[ { id: "earnings_2024_q1", // Unique identifier (string|number) time: 1704067200, // Unix timestamp in seconds color: "green", // CSS color for circle text: ["Q1 Earnings", "EPS: $2.45", "Revenue: $12.3B"], // Tooltip (string|array) label: "E", // Single character for circle labelFontColor: "white", // Optional: label text color minSize: 30, // Optional: minimum circle size in pixels }, { id: "news_announcement", time: 1704153600, color: "#ff6b35", text: "Major acquisition announced", label: "N", labelFontColor: "white", minSize: 25, }, ];

Search Result Format

{ symbol: 'AAPL', full_name: 'NASDAQ:AAPL', description: 'Apple Inc.', exchange: 'NASDAQ', ticker: 'AAPL', type: 'stock' }

πŸ“Š Trading Data Formats

When using the SDK with trading features enabled, you’ll need to provide broker data using the setBrokerAccounts() method. This section documents the exact format expected for orderBook, tradeBook, and positions.

Order Book Format (Active Orders)

The orderBook array contains all active (pending) orders. Each order must follow this structure:

orderBook: [ { // Required Fields orderId: "ORDER_001", // Unique order identifier (string) datetime: new Date(), // Order creation date (Date object) timeStamp: new Date().getTime(), // Order timestamp in milliseconds (number) status: "open", // Order status: "open" | "filled" | "cancelled" | "rejected" price: 50000, // Order price (number, 0 for market orders) size: 0.1, // Order quantity (number) productId: "BYBIT:FUTURE:BTCUSDT", // Full symbol identifier (EXCHANGE:SEGMENT:SYMBOL) remainingSize: 0.1, // Remaining unfilled quantity (number) orderType: "limit", // Order type: "limit" | "market" | "stop" | "stopLimit" side: "buy", // Order side: "buy" | "sell" exchange: "BYBIT", // Exchange name (string) symbol: "BTCUSDT", // Base symbol name (string) broker: "demo", // Broker identifier (string) productType: "FUTURE", // Product type: "FUTURE" | "SPOT" | "OPTION" security: { // Security information object symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, key: "demo-BYBIT:FUTURE:BTCUSDT-ORDER_001", // Unique key (broker-productId-orderId) isGC: true, // GoCharting flag (boolean) // Optional Fields lastTradeTimestamp: null, // Last trade timestamp (number | null) cost: null, // Order cost (number | null) trades: [], // Associated trades array fee: { // Fee information currency: "USDT", cost: 0.0, rate: 0.0, }, info: {}, // Additional broker-specific info fillPrice: null, // Fill price if partially filled (number | null) avgFillPrice: null, // Average fill price (number | null) filledSize: 0, // Filled quantity (number) modifiedAt: null, // Last modification time (number | null) takeProfit: null, // Take profit price (number | null) stopLoss: null, // Stop loss price (number | null) paperTraderKey: null, // Paper trading key (string | null) validity: "DAY", // Order validity: "DAY" | "GTC" | "IOC" | "FOK" commissions: 0, // Commission amount (number) stopPrice: null, // Stop trigger price for stop orders (number | null) rejReason: null, // Rejection reason if rejected (string | null) userTag: null, // User-defined tag (string | null) }, ];

Trade Book Format (Executed Trades)

The tradeBook array contains the history of executed trades:

tradeBook: [ { // Required Fields tradeId: "TRADE_001", // Unique trade identifier (string) orderId: "ORDER_FILLED", // Associated order ID (string) datetime: new Date(Date.now() - 3600000), // Trade execution date (Date object) timeStamp: new Date(Date.now() - 3600000).getTime(), // Trade timestamp in milliseconds (number) status: "filled", // Trade status: "filled" | "partial" price: 49500, // Execution price (number) tradeSize: 0.05, // Trade quantity (number) tradeValue: 2475, // Trade value (price Γ— quantity) (number) productId: "BYBIT:FUTURE:BTCUSDT", // Full symbol identifier (EXCHANGE:SEGMENT:SYMBOL) orderType: "Market", // Original order type (string) side: "buy", // Trade side: "buy" | "sell" broker: "demo", // Broker identifier (string) productType: "FUTURE", // Product type: "FUTURE" | "SPOT" | "OPTION" security: { // Security information object symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, key: "demo-BYBIT:FUTURE:BTCUSDT-TRADE_001", // Unique key (broker-productId-tradeId) isGC: true, // GoCharting flag (boolean) // Optional Fields lastTradeTimestamp: null, // Last trade timestamp (number | null) cost: null, // Trade cost (number | null) takerOrMaker: null, // Taker/Maker flag: "taker" | "maker" | null fee: { // Fee information currency: "USDT", cost: 2.475, rate: 0.001, }, info: {}, // Additional broker-specific info paperTraderKey: null, // Paper trading key (string | null) type: "tradeExecuted", // Trade type (string) remainingSize: "0", // Remaining order size (string) orderDetails: { // Original order details orderId: "ORDER_FILLED", orderType: "market", }, triggerLogicPending: false, // Trigger logic flag (boolean) spread: 0, // Bid-ask spread at execution (number) }, ];

Positions Format (Open Positions)

The positions array contains current open positions:

positions: [ { // Required Fields size: 0.05, // Net position size (number, positive = long, negative = short) size_currency: 0.05, // Position quantity in base currency (number) price: 49500, // Average entry price (number) amount: 2475, // Position value (price Γ— quantity) (number) productId: "BYBIT:FUTURE:BTCUSDT", // Full symbol identifier (EXCHANGE:SEGMENT:SYMBOL) broker: "demo", // Broker identifier (string) productType: "FUTURE", // Product type: "FUTURE" | "SPOT" | "OPTION" currency: "USDT", // Position currency (string) id: "POS_001", // Position identifier (string) underlying: "BYBIT:FUTURE:BTCUSDT", // Underlying asset identifier (string) unPnl: 25, // Unrealized profit/loss (number) rPnl: 0, // Realized profit/loss (number) pnl: 25, // Total profit/loss (number) security: { // Security information object symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, symbol: "BTCUSDT", // Base symbol name (string) segment: "FUTURE", // Market segment (string) exchange: "BYBIT", // Exchange name (string) key: "demo-BYBIT:FUTURE:BTCUSDT-POS_001", // Unique key (broker-productId-positionId) isGC: true, // GoCharting flag (boolean) // Optional Fields openPosVal: 0, // Open position value (number) closedPosVal: 0, // Closed position value (number) paperTraderKey: null, // Paper trading key (string | null) pnlMultiplier: 1, // PnL calculation multiplier (number) positions: { // Detailed position breakdown (object) "demo-BYBIT:FUTURE:BTCUSDT-FUTURE": { positionId: "demo-BYBIT:FUTURE:BTCUSDT-FUTURE", positionFlag: true, security: { symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, positionStatus: "open", // Position status: "open" | "closed" entry: 49500, // Entry price (number) exit: null, // Exit price if closed (number | null) size: 0.05, // Position size (number) positionSize: 0.05, // Position size duplicate (number) transaction_type: "real", // Transaction type: "real" | "paper" trades: [], // Associated trades array broker: "demo", currency: "USDT", productId: "BYBIT:FUTURE:BTCUSDT", ticker: "BYBIT:FUTURE:BTCUSDT", created: null, // Creation timestamp (number | null) symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", underlying: "BYBIT:FUTURE:BTCUSDT", series: null, // Series for options (string | null) strike: null, // Strike price for options (number | null) optionType: null, // Option type: "call" | "put" | null expiration_timestamp: null, // Expiration timestamp for options (number | null) strategyId: "1000", strategyType: "1000", strategyLabel: "Real", strategyColor: "#000000", strategyFlag: true, strategyStatus: "open", stratgeyFormulae: null, fee: { currency: "USDT", cost: 0.0, rate: 0.0, }, side: "buy", // Position side: "buy" | "sell" modified: new Date().getTime(), // Last modified timestamp (number) productType: "FUTURE", rpnl: 0, // Realized PnL (number) totalBuyQty: 0.05, // Total buy quantity (number) avgBuyPrice: 49500, // Average buy price (number) totalSellQty: 0, // Total sell quantity (number) avgSellPrice: 0, // Average sell price (number) }, }, }, ];

Account List Format

The accountList array contains trading account information:

accountList: [ { // Required Fields account_id: "DEMO_001", // Unique account identifier (string) AccountID: "DEMO_001", // Alternative account ID field (string) currency: "USD", // Account base currency (string) balance: 100000, // Current account balance (number) // Optional Fields AccountType: "Demo Trading", // Account type description (string) label: "Demo Account (Trading)", // Display label for UI (string) equity: 100000, // Account equity value (number) margin: 0, // Used margin amount (number) freeMargin: 100000, // Available margin for trading (number) }, ];

Complete setBrokerAccounts Example

// Complete example with all trading data chartInstance.setBrokerAccounts({ accountList: [ { account_id: "DEMO_001", AccountID: "DEMO_001", AccountType: "Demo Trading", label: "Demo Account", currency: "USD", balance: 100000, equity: 100000, margin: 0, freeMargin: 100000, }, ], orderBook: [ { orderId: "ORDER_001", datetime: new Date(), timeStamp: new Date().getTime(), status: "open", price: 50000, size: 0.1, productId: "BYBIT:FUTURE:BTCUSDT", remainingSize: 0.1, orderType: "limit", side: "buy", exchange: "BYBIT", symbol: "BTCUSDT", broker: "demo", productType: "FUTURE", security: { symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, key: "demo-BYBIT:FUTURE:BTCUSDT-ORDER_001", isGC: true, validity: "GTC", fee: { currency: "USDT", cost: 0.0, rate: 0.001 }, }, ], tradeBook: [ { tradeId: "TRADE_001", orderId: "ORDER_FILLED", datetime: new Date(Date.now() - 3600000), timeStamp: new Date(Date.now() - 3600000).getTime(), status: "filled", price: 49500, tradeSize: 0.05, tradeValue: 2475, productId: "BYBIT:FUTURE:BTCUSDT", orderType: "market", side: "buy", broker: "demo", productType: "FUTURE", security: { symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, key: "demo-BYBIT:FUTURE:BTCUSDT-TRADE_001", isGC: true, fee: { currency: "USDT", cost: 2.475, rate: 0.001 }, }, ], positions: [ { size: 0.05, size_currency: 0.05, price: 49500, amount: 2475, productId: "BYBIT:FUTURE:BTCUSDT", broker: "demo", productType: "FUTURE", currency: "USDT", id: "POS_001", underlying: "BYBIT:FUTURE:BTCUSDT", unPnl: 25, rPnl: 0, pnl: 25, security: { symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, symbol: "BTCUSDT", segment: "FUTURE", exchange: "BYBIT", key: "demo-BYBIT:FUTURE:BTCUSDT-POS_001", isGC: true, }, ], });
Important

Key Requirements for Trading Data:

  1. productId Format: Always use EXCHANGE:SEGMENT:SYMBOL format (e.g., "BYBIT:FUTURE:BTCUSDT")
  2. Timestamps: Use JavaScript Date objects for datetime fields and milliseconds for timeStamp fields
  3. Unique Keys: The key field should follow the pattern broker-productId-id for uniqueness
  4. Security Object: Always include the security object with at least symbol, exchange, segment, tick_size, and lot_size
  5. Side Values: Use lowercase "buy" or "sell" for consistency
  6. Status Values: Use lowercase status strings ("open", "filled", "cancelled", etc.)

For more details on trading integration, see the Trading API Documentation.

🎯 Complete Example

Here’s a complete example implementing a datafeed for a REST API:

// Create a complete datafeed object for REST API integration const apiDatafeed = { apiBaseUrl: 'https://api.example.com', apiKey: 'your-api-key', subscriptions: new Map(), // Optional initialization method async _init(datacenter, token) { this.datacenter = datacenter; this.token = token; this.initialized = true; console.log("API Datafeed initialized"); }, async getBars(symbolInfo, resolution, periodParams) { const { from, to } = periodParams; try { const response = await fetch( `${this.apiBaseUrl}/bars?symbol=${symbolInfo.name}&resolution=${resolution}&from=${from}&to=${to}`, { headers: { Authorization: `Bearer ${this.apiKey}`, }, } ); const data = await response.json(); return { bars: data.bars.map((bar) => ({ time: bar.time * 1000, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume, })), meta: { noData: data.bars.length === 0, }, }; } catch (error) { console.error("Failed to fetch bars:", error); return { bars: [], meta: { noData: true } }; } } async resolveSymbol(symbolName, onResolve, onError) { try { const response = await fetch( `${this.apiBaseUrl}/symbols/${symbolName}`, { headers: { Authorization: `Bearer ${this.apiKey}`, }, } ); const symbolData = await response.json(); const symbolInfo = { name: symbolData.name, full_name: symbolData.full_name, description: symbolData.description, type: symbolData.type, session: symbolData.session || "24x7", timezone: symbolData.timezone || "Etc/UTC", ticker: symbolData.ticker, tick_size: symbolData.tick_size || 0.01, // Minimum price movement has_intraday: true, has_daily: true, supported_resolutions: symbolData.supported_resolutions || [ "1", "5", "15", "30", "60", "1D", ], }; onResolve(symbolInfo); } catch (error) { onError("Symbol not found"); } } async searchSymbols(userInput, callback) { try { const response = await fetch( `${this.apiBaseUrl}/search?q=${encodeURIComponent(userInput)}`, { headers: { Authorization: `Bearer ${this.apiKey}`, }, } ); const results = await response.json(); callback(results); } catch (error) { callback([]); } } }; // πŸš€ Usage - pass the datafeed object directly with simplified API const chart = createChart("#chart", { symbol: "NASDAQ:AAPL", interval: "1D", datafeed: apiDatafeed, // Pass the object directly licenseKey: "YOUR_LICENSE_KEY", });

For more advanced data provider implementations, see the examples section.

Last updated on