Skip to main content

Symbol Suggest Provider

SymbolSuggestProvider is responsible for retrieving instruments data for DXcharts React.

There's multiple cases, when SymbolSuggestProvider is used:

  • on initial chart load to retrieve info about initialInstrument provided via initialChartConfig or from Layout if exists
  • when user types in the search input (main instrument and compare instruments) to get suggestions for instruments

Apart from instrument's symbol, description and type, SymbolSuggestProvider also responsible for providing some more useful info about instrument:

tradingHours

tradingHours is, obviously, trading hours for the instrument.

NewYorkETH() is a schedule format provided by dxFeed.

If you use a different market data vendor, the schedule format may differ from NewYorkETH().

const symbol = 'DANOY';
const tradingHours = NewYorkETH();

priceIncrements

priceIncrements is an array of numbers that represent the price steps for an instrument on Y-Axis in the next format:

Read more about priceIncrements in Price precision

Example

import { SymbolSuggestProvider } from '@dx-private/dxchart5-react/dist/providers/symbol-suggest-provider';
import { Instrument } from '@dx-private/dxchart5-react/dist/chart/model/instrument.model';
const MOCK_CHART_INSTRUMENTS: Instrument[] = [
{
symbol: 'IBM',
description: 'International Business Machines Corp',
type: 'STOCK',
priceIncrements: [0.01, 10, 0.1],
},
{
symbol: 'AAPL',
description: 'Apple Inc',
type: 'STOCK',
priceIncrements: [0.0001, 1, 0.01],
},
{
symbol: 'MSFT',
description: 'Microsoft Corp',
type: 'STOCK',
priceIncrements: [0.0001, 5, 0.01, 50, 0.1],
},
{
symbol: 'BAC',
description: 'Bank of America Corporation',
type: 'STOCK',
priceIncrements: [0.0001, 5, 0.01, 50, 0.1],
},
{
symbol: 'AXP',
description: 'American Express Co',
type: 'STOCK',
priceIncrements: [0.0001, 5, 0.01, 50, 0.1],
},
{
symbol: 'ADDYY',
description: 'ADIDAS AG S/ADR by adidas AG',
type: 'STOCK',
priceIncrements: [0.01],
},
{
symbol: 'GOOG',
description: 'Alphabet Inc Class C',
type: 'STOCK',
priceIncrements: [0.01, 1, 0.1],
},
{
symbol: 'VXN',
description: 'CBOY Nasdaq 1000 Volatility Index',
type: 'INDEX',
priceIncrements: [0.01, 1, 0.1],
},
{
symbol: 'SPX',
description: 'S&P 500 Index',
type: 'INDEX',
priceIncrements: [0.01, 1, 0.1],
},
{
symbol: 'CFH/KRW',
description: 'Swiss franc — South Korean won',
type: 'STOCK',
priceIncrements: [0.01, 1, 0.1],
},
{
symbol: 'CSCO',
description: 'Cisco Systems Inc',
type: 'STOCK',
priceIncrements: [0.01, 1, 0.05],
},
{
symbol: 'VIX',
description: 'CDOE Volatility S&P 500 Index',
type: 'INDEX',
priceIncrements: [0.001, 1, 0.01],
},
{
symbol: 'INTC',
description: 'Intel Corp',
type: 'STOCK',
priceIncrements: [0.001, 1, 0.01],
},
];
/**
* Creates mock implementation of {@link SymbolSuggestProvider}.
*/
export const createMockSymbolSuggestProvider = (): SymbolSuggestProvider => {
const doesInstrumentContainString = (search: string) => (instrument: Instrument) => {
const { symbol, description } = instrument;
const nameLoweCase = symbol.toLowerCase();
const descriptionLoweCase = description?.toLowerCase();
const searchLowerCase = search.toLowerCase();
return nameLoweCase.includes(searchLowerCase) || descriptionLoweCase?.includes(searchLowerCase);
};
const searchInstrument = (search: string) => MOCK_CHART_INSTRUMENTS.filter(doesInstrumentContainString(search));
/**
* Method is called by `dxcharts-react` when user selects an {@link Instrument} from the search list
* and on `chart` initilization.
*
* This method should return all the details about an {@link Instrument`}.
*/
const findInstrument = (symbol: Instrument['symbol']) => {
const instrument = searchInstrument(symbol)[0] ?? { symbol, type: 'MOCK', priceIncrements: [0.01] };
return Promise.resolve(instrument);
};
/**
* Method is called by `dxcharts-react` every time user types smth in the
* symbol search input, either for main {@link Instrument} or for compare.
*/
const searchInstruments = (search: string) => {
return Promise.resolve(searchInstrument(search));
};
/**
* `dxcharts-react` calls this method when selected {@link Instrument} changes.
* You can use this method to update some state on external storage if you need.
*
* `chartId` is an identifier of the chart in multichart where the {@link Instrument} was changed.
*
* For example, if you use 2x2 layout type and change {@link Instrument} on top-left chart, `chartId` will be '0';
*/
const onInstrumentChanged = (symbol: string | undefined, chartId: string) => {};
return {
findInstrument,
searchInstruments,
onInstrumentChanged,
};
};

Real-world example: dxFeed IPF Provider

The following example shows a complete implementation of a Symbol Suggest Provider that connects to a dxFeed IPF (Instrument Profile Format) endpoint. This provider:

  • Fetches instruments from an IPF endpoint with authentication
  • Implements smart search ranking - exact symbol matches first, then partial matches
  • Handles crypto instruments - automatically detects and corrects crypto instrument types
  • Caches results and limits response size for performance
Adapting to your API

This example uses dxFeed IPF format, but you can adapt it to any API:

  1. Replace fetch URLs with your API endpoints
  2. Replace parseIpfFile with your JSON parsing (usually just JSON.parse)
  3. Map your API response to Instrument format:
    {
    symbol: string, // Required
    description?: string, // Optional but recommended
    type: string, // Required: "STOCK", "FOREX", "CRYPTO", etc.
    tradingHours?: string, // Optional: schedule format
    priceIncrements?: number[] // Optional: [0.01] for stocks, [0.000001] for forex
    }
  4. Simplify search ranking if needed - you can return API results as-is
  5. Adapt authentication to your method (Bearer token, API key, etc.)

See comments in the code for detailed adaptation instructions.

import { Instrument, InstrumentTypes } from '@dx-private/dxchart5-react/dist/chart/model/instrument.model';
import { SymbolSuggestProvider } from '@dx-private/dxchart5-react/dist/providers/symbol-suggest-provider';
import { getCapitalLetters } from '@dx-private/dxchart5-react/dist/utils/string.utils';
import { array, either, option } from 'fp-ts';
import { sort } from 'fp-ts/Array';
import { Ord, fromCompare } from 'fp-ts/Ord';
import { mapWithIndex } from 'fp-ts/Record';
import { constVoid, pipe } from 'fp-ts/function';
/**
* Example: Symbol Suggest Provider implementation using dxFeed IPF (Instrument Profile Format)
*
* This provider fetches instrument data from a dxFeed IPF endpoint. It implements:
* - searchInstruments: searches for instruments by text query with smart ranking
* - findInstrument: retrieves a single instrument by symbol
* - onInstrumentChanged: callback for instrument changes (optional)
*
* IMPORTANT FOR YOUR IMPLEMENTATION:
* - You don't need to use IPF format - adapt the fetch logic to your own API
* - The key is to return Instrument objects with: symbol, description, type, tradingHours, priceIncrements
* - Search ranking is optional but improves UX - prioritize exact matches
* - Caching is recommended to reduce API calls
*
* The provider includes:
* - Basic authentication via Authorization header
* - Smart search ranking (exact matches first, then partial matches)
* - Crypto instrument type detection and correction
* - Result caching and limiting
*/
export interface DxFeedIPFSymbolSuggestConfig {
readonly ipfURL: string;
readonly username: string;
readonly password: string;
readonly customSearchParams?: string;
}
/**
* Helper: Calculate search relevance weight for ranking
*
* This function assigns weights to search results to prioritize the most relevant matches.
* Lower weight = higher priority (sorted ascending).
*
* You can adapt this logic to your needs or use a simpler approach:
* - Simple: just filter by symbol/description contains search text
* - Advanced: use full-text search on your backend and return pre-sorted results
*/
export const calcInstrumentWeight = (instrument: Instrument, search: string): number => {
let weight = 0;
if (instrument.symbol.toUpperCase() === search.toUpperCase()) {
weight -= 100000; // Exact symbol match - highest priority
}
if (instrument.symbol.toUpperCase().startsWith(search.toUpperCase())) {
weight -= 10000; // Symbol starts with search - high priority
}
if (instrument.symbol.toUpperCase().includes(search.toUpperCase())) {
weight -= 1000; // Symbol contains search - medium priority
}
if (instrument.description?.toUpperCase().startsWith(search.toUpperCase())) {
weight -= 100; // Description starts with search - lower priority
}
if (instrument.description?.toUpperCase().includes(search.toUpperCase())) {
weight -= 10; // Description contains search - even lower
}
if (getCapitalLetters(instrument.description ?? '').includes(search.toUpperCase())) {
weight -= 1; // Acronym match - lowest priority
}
return weight;
};
// Helper: Create sorting function based on search query
const instrumentsSort = (search: string): Ord<Instrument> => {
return fromCompare((a: Instrument, b: Instrument) => {
const aWeight = calcInstrumentWeight(a, search);
const bWeight = calcInstrumentWeight(b, search);
return aWeight === bWeight ? 0 : aWeight > bWeight ? 1 : -1;
});
};
type HeadersAllowed = Record<'Authorization', string>;
/**
* Helper: Request multiple instruments from IPF endpoint
*
* NOTE: This example uses dxFeed IPF format. For your implementation:
* - Replace fetch URL with your API endpoint
* - Replace parseIpfFile with your JSON parsing logic
* - Adapt query parameters to match your API
* - Your API should return an array of instruments with: symbol, description, type, tradingHours, priceIncrements
*/
export const requestIPFPromise = (
url: string,
params: { text?: string; limit?: number; customParams?: string } | undefined,
headersData: HeadersAllowed,
): Promise<Instrument[]> => {
const headers = new Headers();
pipe(
headersData,
mapWithIndex((key, value) => headers.set(key, value)),
);
// Build query string - adapt this to your API format
const query = url + (params ? `?mode=ui&text=${params.text}&limit=${params.limit || 50}${params.customParams ? `&${params.customParams}` : ''}` : '');
return fetch(query, { headers })
.then(res => res.text())
.then(parseIpfFile)
.then(res => {
if (either.isRight(res)) {
return res.right;
}
throw res.left;
});
};
/**
* Helper: Request single instrument by symbol
*
* This is called when the chart needs to load a specific instrument (e.g., on initial load).
* Your API should return a single instrument object or throw an error if not found.
*/
export const requestIPFSinglePromise = (
url: string,
symbol: string,
headersData: HeadersAllowed,
): Promise<Instrument> => {
const headers = new Headers();
pipe(
headersData,
mapWithIndex((key, value) => headers.set(key, value)),
);
const query = `${url}?SYMBOL=${symbol}`;
return fetch(query, { headers })
.then(res => res.text())
.then(parseIpfFile)
.then(res => {
if (either.isRight(res)) {
return pipe(
res.right,
array.findFirst(item => item.symbol === symbol),
option.fold(
() => {
if (res.right[0] !== undefined) {
return res.right[0];
} else {
throw new Error('instrument not found');
}
},
instrument =>
isCrypto(instrument.symbol) ? { ...instrument, type: InstrumentTypes.CRYPTO } : instrument,
),
);
}
throw res.left;
});
};
/**
* Helper: Detect crypto instruments (format: SYMBOL:CX or SYMBOL:CXSUFFIX)
*
* This is specific to dxFeed format. Adapt this to your instrument type detection logic.
* For example, if your API returns instrument.type === 'CRYPTO', you don't need this.
*/
const isCrypto = (symbol: string) => /^[^:]+:CX.*$/.test(symbol);
/**
* Helper: Parse IPF file format
*
* NOTE: This is a placeholder - real IPF parsing is complex and specific to dxFeed format.
* For your implementation, replace this with your JSON parsing:
*
* const parseIpfFile = (src: string): either.Either<Error, Instrument[]> => {
* try {
* const data = JSON.parse(src);
* return either.right(data.instruments); // Adapt to your API response structure
* } catch (error) {
* return either.left(new Error('Failed to parse response'));
* }
* };
*/
const parseIpfFile = (src: string): either.Either<Error, Instrument[]> => {
// This is a simplified example - real IPF parsing is more complex
// You would need to parse the IPF format structure here
// For your API, replace with JSON.parse() and map to Instrument[]
return either.right([]);
};
/**
* Helper: Prepare instrument data (correct crypto type, set price increments)
*
* This function ensures instruments have the correct type and price increments.
* IMPORTANT: priceIncrements is required for proper Y-axis scaling.
*
* Common priceIncrements values:
* - Stocks: [0.01] or [0.001]
* - Forex: [0.00001] or [0.000001]
* - Crypto: [0.000001] or [0.00000001]
* - Futures: depends on contract specifications
*/
const prepareInstrumentData = (data: Instrument): Instrument => {
switch (data.type) {
case InstrumentTypes.FOREX:
return {
...data,
priceIncrements: [0.000001],
type: isCrypto(data.symbol) ? InstrumentTypes.CRYPTO : data.type,
};
default:
return data;
}
};
/**
* Creates a Symbol Suggest Provider that connects to dxFeed IPF endpoint
*
* ADAPTATION GUIDE FOR YOUR API:
*
* 1. Replace fetch URLs with your API endpoints
* 2. Replace parseIpfFile with your JSON parsing (usually just JSON.parse)
* 3. Map your API response to Instrument format:
* {
* symbol: string, // Required: instrument symbol (e.g., "AAPL")
* description?: string, // Optional: display name (e.g., "Apple Inc.")
* type: string, // Required: "STOCK", "FOREX", "CRYPTO", etc.
* tradingHours?: string, // Optional: schedule format (e.g., "NewYorkETH()")
* priceIncrements?: number[] // Optional: price steps for Y-axis (e.g., [0.01])
* }
* 4. Adapt authentication to your method (Bearer token, API key, etc.)
* 5. Simplify search ranking if needed - you can just return API results as-is
*
* @param config - Configuration with IPF URL and authentication credentials
* @returns SymbolSuggestProvider instance
*/
export const createDxFeedIpfSymbolSuggestProvider = (config: DxFeedIPFSymbolSuggestConfig): SymbolSuggestProvider => {
const ipfAuth: HeadersAllowed = {
Authorization: `Basic ${btoa(`${config.username}:${config.password}`)}`,
};
/**
* searchInstruments: Called when user types in the symbol search box
*
* Requirements:
* - Return Promise<Instrument[]> - array of matching instruments
* - Return empty array [] if search is empty
* - Limit results (e.g., top 30) for performance
* - Sort by relevance (optional but recommended)
*/
function searchInstruments(search: string) {
if (search === '') {
return Promise.resolve([]);
}
const params = {
text: search,
limit: 50, // Request more than needed for sorting
customParams: config.customSearchParams,
};
return requestIPFPromise(config.ipfURL, params, ipfAuth)
.then(instruments => instruments.map(i => prepareInstrumentData(i)))
.then(instruments => sort(instrumentsSort(search))(instruments).slice(0, 30)); // Return top 30
}
/**
* findInstrument: Called when chart needs to load a specific instrument
*
* Requirements:
* - Return Promise<Instrument> for the given symbol
* - Throw error if instrument not found
* - This is called on initial chart load and when switching instruments
*/
function findInstrument(symbol: Instrument['symbol']): Promise<Instrument> {
return requestIPFSinglePromise(config.ipfURL, symbol, ipfAuth).then(prepareInstrumentData);
}
return {
findInstrument,
searchInstruments,
onInstrumentChanged: constVoid, // Optional: implement if you need to track instrument changes
};
};

Key features

  • Authentication: Uses Basic Auth with username/password (adapt to your method)
  • Search ranking: Prioritizes exact matches, then symbol starts with, then contains, then description matches (optional but improves UX)
  • Crypto detection: Automatically converts FOREX-type instruments to CRYPTO if symbol matches crypto pattern (adapt to your instrument type logic)
  • Result limiting: Returns top 30 results after sorting (recommended for performance)

API reference

SymbolSuggestProvider

Symbol suggest provider fetches instrument data

SymbolSuggestProvider.findInstrument
SymbolSuggestProvider.findInstrument(symbol: string): Promise<Instrument>

By given instrument symbol returns exactly one instrument (or throws error)

Parameters
symbol: string
Returns
Promise<Instrument>
SymbolSuggestProvider.searchInstruments
SymbolSuggestProvider.searchInstruments(search: string): Promise<Instrument[]>

By given search string returns Promise of suggest result.

Parameters
search: string
Returns
Promise<Instrument[]>
SymbolSuggestProvider.onInstrumentChanged
SymbolSuggestProvider.onInstrumentChanged(symbol: string, chartId: string): void

Callback when instrument changed in specific chartId (multichart 0,1,2,3 supported).

Parameters
symbol: string
- new instrument symbol
chartId: string
- chart on which instrument was changed; if all 4 charts changed - callback will be called 4 times.
Returns
void