π 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 dataresolveSymbol()- Resolve symbol information
Optional Methods:
searchSymbols()- Enable symbol search functionalitysubscribeTicks()- Provide real-time data updatesunsubscribeTicks()- Cancel real-time subscriptionsgetMarks()- Display marks/events on chartgetTimescaleMarks()- Display timescale marksdestroy()- 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 fromresolveSymbolresolution(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:
- BarsResult format (recommended):
{
bars: Bar[]; // Array of bar objects
meta?: { // Optional metadata
noData?: boolean;
nextTime?: number;
};
}- 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 fromresolveSymbolfrom(number): Start time as Unix timestamp in secondsto(number): End time as Unix timestamp in secondsresolution(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:
| Property | Type | Required | Description |
|---|---|---|---|
id | string|number | β | Unique identifier for the mark |
time | number | β | Unix timestamp in seconds |
color | string | β | CSS color for the mark circle (e.g., βredβ, β#ff0000β, βrgb(255,0,0)β) |
text | string|array | β | Tooltip content. Arrays display each item on separate lines |
label | string | β | Single character to display in the circle |
labelFontColor | string | β | Color for the label text (default: βwhiteβ) |
minSize | number | β | 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 onlyColor 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 redReturns: Promise<object> - Object with bars array and optional meta
resolveSymbol(symbolName, onResolve, onError)
Resolves a symbol name to detailed symbol information.
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 resolutiononError(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 querycallback(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 objectresolution- Resolution object with{scale, units}formatonRealtimeCallback- Function to call with real-time trade datasubscriberUID- Unique identifier for this subscriptiononResetCacheNeededCallback- 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
- Always push Open first - The first tick should be the Open price
- Push Close last - The final tick should always be the Close price
- High/Low order matters - For bullish bars, Low comes before High; for bearish bars, High comes before Low
- Use current timestamps - Ensure timestamps are current (not historical) for the SDK to update the chart
- 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:
| Duration | Interval | Time 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_dateandend_dateparameters - 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,
},
],
});Key Requirements for Trading Data:
- productId Format: Always use
EXCHANGE:SEGMENT:SYMBOLformat (e.g.,"BYBIT:FUTURE:BTCUSDT") - Timestamps: Use JavaScript
Dateobjects fordatetimefields and milliseconds fortimeStampfields - Unique Keys: The
keyfield should follow the patternbroker-productId-idfor uniqueness - Security Object: Always include the security object with at least
symbol,exchange,segment,tick_size, andlot_size - Side Values: Use lowercase
"buy"or"sell"for consistency - 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",
});π Related Documentation
- Chart API - Main chart component
- Configuration API - Chart configuration options
- Examples - Working implementations
- Integration Tutorial - Step-by-step guide
For more advanced data provider implementations, see the examples section.