Datafeed API
Overview
The library Library allow you to connect market data to the chart in two ways. You can implement your own datafeed utilizing the API or use the built-in UDF adapter. The following article describes the Datafeed API and reveals some implementation details. If you need a step-by-step guide, refer to the How to connect data via Datafeed API tutorial.
Datafeed API is a set of methods that you should implement in JavaScript and assign to the datafeed
property in Widget Constructor. The library calls these methods to access and process data. In response, you should evoke the supplied callbacks to provide data to the library.
All Datafeed API members are listed in the IDatafeedChartApi
interface. If you use Trading Terminal, you should also implement some additional methods.
Asynchronous callbacks
Note that all callbacks should be evoked asynchronously. In context of the JavaScript Event Loop, the callbacks can only be evoked within different MacroTask. Otherwise, the Uncaught RangeError: Maximum call stack size exceeded issue might occur.
If you have data ready at the time of a request, you can set a delay as demonstrated below to ensure that a callback is only evoked when the library is ready.
setTimeout(() => { historyCallback(data); }, 0);
Note that the library can modify bar data that you provide utilizing callbacks. Pass a copy of the data to avoid potential issues.
Advanced Charts methods
The following methods relate to Advanced Charts and Trading Terminal.
onReady
The library calls the onReady
method when the chart is initialized. This method supplies the library with the datafeed configuration data such as supported symbol types, exchanges, time intervals (resolution), currency codes and more. Call the OnReadyCallback
and pass a DatafeedConfiguration
object as a parameter:
onReady: (callback) => {
console.log('[onReady]: Method call');
setTimeout(() => callback(configurationData));
}
The following code sample shows the DatafeedConfiguration
implementation:
const configurationData = {
supports_search: true,
supports_group_request: false,
supports_marks: true,
supports_timescale_marks: true,
supports_time: true,
exchanges: [
{ value: "", name: "All Exchanges", desc: "" },
{ value: "NasdaqNM", name: "NasdaqNM", desc: "NasdaqNM" },
{ value: "NYSE", name: "NYSE", desc: "NYSE" }
],
symbols_types: [
{ name: "All types", value: "" },
{ name: "Stock", value: "stock" },
{ name: "Index", value: "index" }
],
supported_resolutions: ["D", "2D", "3D", "W", "3W", "M", "6M"]
}
searchSymbols
The library calls the searchSymbols
method to request symbols that match some user input. Pass the resulting array of symbols as a parameter to SearchSymbolsCallback
.
searchSymbols: async (
userInput,
exchange,
symbolType,
onResultReadyCallback,
) => {
console.log('[searchSymbols]: Method call');
const symbols = await getMatchingSymbolsFromBackend(userInput, exchange, symbolType);
onResultReadyCallback(newSymbols);
}
As a result, the library gets an array of SearchSymbolResultItem
objects that have the following format:
[
{
"symbol": "<short symbol name>",
"full_name": "<full symbol name>", // e.g. BTCE:BTCUSD
"description": "<symbol description>",
"exchange": "<symbol exchange name>",
"ticker": "<symbol ticker name>",
"type": "stock" // "futures"/"crypto"/"forex"/"index"
},
{
//...
}
]
If no symbol is found, pass an empty array to SearchSymbolsCallback
.
You can adjust the frequency of search requests utilizing the symbol_search_request_delay
property.
resolveSymbol
The library calls the resolveSymbol
method to get symbol information such as the exchange, time zone, trading hours, etc. Specify this information in a LibrarySymbolInfo
object as demonstrated below:
const symbolInfo = {
ticker: 'BTCUSD',
name: 'BTCUSD',
description: 'Bitcoin/USD',
type: symbolItem.type,
session: '24x7',
timezone: 'Etc/UTC',
exchange: 'Example Exchange',
minmov: 1,
pricescale: 100,
has_intraday: false,
has_no_volume: true,
has_weekly_and_monthly: false,
supported_resolutions: ['1', '5', '30', '60', '1D', '1W'],
volume_precision: 2,
data_status: 'streaming',
};
Pass symbol information as a parameter to ResolveCallback
. If the symbol cannot be resolved, call ErrorCallback
and specify an error message.
resolveSymbol: async (
symbolName,
onSymbolResolvedCallback,
onResolveErrorCallback,
extension
) => {
try {
const symbolInfo = await getSymbolInfoFromBackend(symbolName, extension);
onSymbolResolvedCallback(symbolInfo);
} catch (err) {
onResolveErrorCallback(err.message);
}
}
getBars
The library calls getBars
to get historical data in a certain range. To transfer the requested data, pass an array of Bar
objects to HistoryCallback
.
The library caches historical data. Therefore, you don't need to implement a client-side cache.
Bar order
The array of Bar
items should be arranged in ascending chronological order, meaning that the timestamps of the bars should be getting bigger for each bar in the array. For example, [1484179200, 1484265600, 1484611200, ...]
.
Note that for daily, weekly, and monthly bars, the time
value should represent the beginning of the trading day at 00:00:00 UTC, not the beginning of the session.
Correct amount of data
The library calculates the amount of data that is necessary to fill the chart space and requests it in getBars
. You cannot change this amount. Return data to getBars
based on the following PeriodParams
properties:
from
— Unix timestamp of the leftmost requested bar. The library requires data in the[from, to)
time range.to
— Unix timestamp of the rightmost requested bar (not inclusive).countBack
— the required amount of bars to load.
It is more important to pass the required number of bars than to match the [from, to)
time range for the following reasons:
- The library might miscalculate the
from
value. It may happen if you provide incorrectsession
orsession_holidays
values. In this case, the[from, to)
range does not represent the required number of bars. - The library calculates the correct
from
value, but your backend does not contain enough bars in the[from, to)
range. It might happen if the market was opened, but the symbol was not traded.
In both cases, the library calls getBars
multiple times in order to get the missing data. It might cause potential issues. To avoid them, consider the following recommendations:
- Your response should always include all the existing data for the requested range.
- If the number of bars in the requested range is less than the
countBack
value, you should include earlier bars until thecountBack
count is reached. For example, the chart requests 300 bars in the range[2019-06-01T00:00:00..2020-01-01T00:00:00)
, and your backend have only 250 bars in the requested period. Return these 250 bars and 50 bars prior to2019-06-01T00:00:00
. - In the unlikely case that the number of bars in the requested range is larger than the
countBack
value, then you should return all the bars in that range instead of truncating it to thecountBack
length. - If there is no data left, set
noData
totrue
to prevent further requests.
Previously, it was necessary to specify noData
and nextTime
to load data outside the requested range. For now, you can send this data in response to the current request. However, you can still use these properties if your datafeed supports only the from
/to
properties and requires another request from the library.
The library can request more bars than are visible because some indicators require additional history, for example, Moving Average with the length 10
.
subscribeBars
The library calls subscribeBars
to receive real-time updates for a symbol. Call SubscribeBarsCallback
every time you want to update the most recent bar or add a new one.
You cannot update a historical bar utilizing this method. Otherwise, you get an error. If you need to change historical data, you should call onResetCacheNeededCallback
and then chart.resetData()
to redraw the chart.
If you return a bar that has the same time
value as the most recent bar, then the most recent bar is replaced. Consider the following example:
The Bar
object has the {time, close, open, high, low, volume}
structure. The most recent bar (in pseudo-code) is {1419411578413, 10, 12, 9, 11}
. You call onRealtimeCallback({1419411578413, 10, 14, 9, 14})
. As the bar with the time 1419411578413
already exists, and it is the most recent one, the library replaces the entire bar making the most recent bar {1419411578413, 10, 14, 9, 14}
.
Refer to the tutorial to see the example of subscribeBars
implementation.
unsubscribeBars
The library calls unsubscribeBars
to stop receiving updates for the symbol when the user selects another symbol on the chart. The listenerGuid
argument contains the same object that was passed to subscribeBars
before.
Refer to the tutorial to see the example of unsubscribeBars
implementation.
The library can have multiple subscriptions at the same time. Your datafeed implementation should treat subscribe and unsubscribe calls for different resolutions/symbols as independent events and should not assume they will be called in any particular order or without any delay.
The library provides a unique subscriber ID as a parameter when it calls subscribeBars
and unsubscribeBars
. This unique ID should be used to track subscriptions.
getMarks
The library calls getMarks
to request marks for the visible bar range. The library assumes that you call GetMarksCallback
once per getMarks
call. Pass an array of Mark
objects as a callback parameter.
Only ten marks can be attached to a bar. The time of each mark must match the time of a bar. For example, if the bar times are 2023-01-01
, 2023-01-08
, and 2023-01-15
, then a mark cannot have the time 2023-01-05
.
This method is called only if your datafeed supports marks.
The code sample below demonstrates the example of getMarks
implementation:
getMarks = (symbolInfo, startDate, endDate, onDataCallback, resolution) => {
console.log('getMarks');
onDataCallback(
[
{
id: 1,
time: endDate,
color: 'red',
text: ['This is the mark pop-up text.'],
label: 'M',
labelFontColor: 'blue',
minSize: 25
},
{
id: 2,
time: endDate + 5260000, // 2 months
color: 'red',
text: ['Second marker'],
label: 'S',
labelFontColor: 'green',
minSize: 25
}
]);
};
getTimescaleMarks
The library calls getTimescaleMarks
to request timescale marks for the visible bar range. The library assumes that you call GetMarksCallback
once per getTimescaleMarks
call. Pass an array of TimescaleMark
objects as a callback parameter.
These method is called only if your datafeed supports marks.
The code sample below demonstrates the example of getTimescaleMarks
implementation:
getTimescaleMarks = (
symbolInfo,
startDate,
endDate,
onDataCallback,
resolution
) => {
// optional
console.log('getTimescaleMarks');
let marks = [];
if (symbolInfo.name === 'AAPL') {
marks = [
{
id: 1,
time: startDate,
color: 'red',
label: 'Aa',
minSize: 30,
tooltip: [
'Lorem',
'Ipsum',
'Dolor',
'Sit',
]
},
{
id: 2,
time: startDate + 5260000, // 2 months
color: 'blue',
label: 'B',
minSize: 30,
tooltip: [
'Amet',
'Consectetur',
'Adipiscing',
'Elit',
]
}
];
} else {
marks = [
{
id: 'String id',
time: endDate,
color: 'red',
label: 'T',
tooltip: ['Nulla']
}
];
}
onDataCallback(marks);
};
getServerTime
You can display the countdown to the bar closes on the price scale. To do this, set the configuration flag supports_time
to true
. If time on the user machine is incorrect, the countdown is also incorrect. You should implement getServerTime
to adjust the countdown.
The library calls getServerTime
to request the server time. Pass the time value as a parameter to ServerTimeCallback
. The callback should be called only once. Specify time in Unix format, for example, 1445324591
.
getVolumeProfileResolutionForPeriod
The library calls getVolumeProfileResolutionForPeriod
to request the resolution that is used to calculate the Volume Profile Visible Range indicator. Implement this method if you want to calculate the indicator more accurately. The implementation depends on how much data you can transfer to the library and the depth of data in your datafeed.
If this method is not specified, the library uses currentResolution
.
Trading Terminal methods
The following methods are available only in Trading Terminal.
getQuotes
Trading Terminal calls getQuotes
to request quote data that is used to display the Watchlist, Details, Order Dialog, and DOM widgets. To transfer the requested data, pass an array of QuoteData objects as a parameter to QuotesCallback. The library assumes to receive necessary data in a single callback. The example of QuoteData is demonstrated below:
{
"s": "ok",
"d": [
{
"s": "ok",
"n": "NasdaqNM:AAPL",
"v": {
"ch": 0,
"chp": 0,
"short_name": "AAPL",
"exchange": "",
"original_name": "NasdaqNM:AAPL",
"description": "NasdaqNM:AAPL",
"lp": 173.68,
"ask": 173.68,
"bid": 173.68,
"open_price": 173.68,
"high_price": 173.68,
"low_price": 173.68,
"prev_close_price": 172.77,
"volume": 173.68
}
}
],
"source": "Quandl"
}
Note that Percentage change value, Ask/Bid buttons and lines also require quote data. They are not displayed on the chart if getQuotes
is not implemented.
subscribeQuotes
Trading Terminal calls subscribeQuotes
to receive real-time quote updates for certain symbols. Call QuotesCallback every time you want to update the quotes and pass an array of QuoteData objects as a parameter.
unsubscribeQuotes
Trading Terminal calls unsubscribeQuotes
to stop receiving updates for the symbol when the user removes it from the Watchlist or selects another symbol on the chart. The listenerGuid
argument contains the same object that was passed to subscribeQuotes
before.
subscribeDepth
Trading Terminal calls subscribeDepth
to receive real-time Level 2 (DOM) data for a symbol. Call DOMCallback every time you want to update the quotes and pass a DOMData object as a parameter.
Note that you should specify the broker_config property in the Widget Constructor and set supportLevel2Data to true
. Otherwise, the library does not call the subscribeDepth
/unsubscribeDepth
methods.
This method should return a unique identifier (subscriberUID
) that is used to unsubscribe from updates.
unsubscribeDepth
Trading Terminal calls unsubscribeDepth
to stop receiving DOM data updates. The subscriberUID
argument contains the same object that was returned by subscribeDepth
.