Skip to main content

Events Data Provider

EventsDataProvider connects your economic events (such as earnings, dividends, splits, etc.) provider for selected instrument with DXcharts React application.

Here's how they look like on chart:

Example

import { mulberry32 } from '@dx-private/dxchart5-react/dist/utils/generator/random';
import { addDays, format } from 'date-fns';
const random = mulberry32(4596423324);
const MOCK_EVENTS = [
{
kind: 'earnings',
basic: parseFloat((random() * 8).toFixed(2)),
diluted: parseFloat((random() * 8).toFixed(2)),
timestamp: addDays(Date.now(), -1).getTime(),
periodEnding: addDays(Date.now(), -1).getTime(),
},
{
kind: 'dividends',
gross: `${(random() * 3).toFixed(2)}`,
timestamp: addDays(Date.now(), -2).getTime(),
},
{
kind: 'splits',
splitFrom: 1,
splitTo: 4,
timestamp: addDays(Date.now(), -3).getTime(),
},
{
kind: 'conference-calls',
referencePeriod: format(addDays(Date.now(), -4).getTime(), 'QQQ yyyy'),
eventType: 'Earning',
timestamp: addDays(Date.now(), -4).getTime(),
},
];
/**
* Creates mock implementation of {@link EventsDataProvider}.
*/
export const createMockEventDataProvider = () => {
const requestEventsData = () => Promise.resolve({ events: MOCK_EVENTS });
return {
requestEventsData,
};
};

Real-world example: dxFeed Fundamentals Provider

The following example shows a complete implementation of an Events Data Provider that connects to a dxFeed Fundamentals API. This provider:

  • Fetches corporate events (earnings, dividends, splits, conference calls) from dxFeed endpoints
  • Fetches data in parallel - requests earnings and corporate actions simultaneously for better performance
  • Transforms data from dxFeed format to chart format with proper timestamp conversion
  • Deduplicates events - removes events with identical timestamps
  • Caches responses to avoid duplicate API calls
import {
ConferenceCallEvent,
DividendsEvent,
EventsData,
EventsDataProvider,
SplitsEvent,
EarningsEvent,
Event,
} from '@dx-private/dxchart5-react/dist/providers/events-data-provider';
/**
* NOTE: This example uses RxJS and fp-ts for advanced patterns.
*
* Dependencies required (install in your project):
* - rxjs: npm install rxjs
* - fp-ts: npm install fp-ts
* - date-fns: npm install date-fns
* - date-fns-tz: npm install date-fns-tz
*
* If you don't use these libraries, see simple-events-data-provider-example.ts
* for a simplified version without these dependencies.
*/
import { map, startWith } from 'rxjs/operators';
import { remoteData, RemoteData } from '@dx-private/dxchart5-react/dist/utils/remote-data';
// @ts-ignore - Example code, requires rxjs dependency
import { from, Observable, lastValueFrom } from 'rxjs';
import { sequenceT } from 'fp-ts/Apply';
import { parse } from 'date-fns';
import { pipe } from 'fp-ts/function';
import { array, number } from 'fp-ts';
import { contramap } from 'fp-ts/Eq';
// @ts-ignore - Example code, requires date-fns-tz dependency
import { fromZonedTime } from 'date-fns-tz';
import { observable } from 'fp-ts-rxjs';
/**
* Example: Events Data Provider implementation using dxFeed Fundamentals API
*
* This provider fetches corporate events (earnings, dividends, splits, conference calls)
* from a dxFeed Fundamentals endpoint. It implements:
* - requestEventsData: fetches all events for a given symbol
*
* IMPORTANT FOR YOUR IMPLEMENTATION:
* - You don't need to use RemoteData pattern - simple Promise<EventsData> is fine
* - You don't need RxJS - the interface only requires Promise
* - The key is to return EventsData with events array containing proper event types
* - Event types: 'earnings', 'dividends', 'splits', 'conference-calls'
* - Each event needs: kind, timestamp, and type-specific fields
*
* SIMPLIFIED VERSION (without RemoteData/RxJS):
* ```typescript
* export const createSimpleEventsProvider = (apiUrl: string): EventsDataProvider => {
* const requestEventsData = async (symbol: string): Promise<EventsData> => {
* const response = await fetch(`${apiUrl}/events/${symbol}`);
* const data = await response.json();
* return {
* events: data.events.map(transformToChartEvent)
* };
* };
* return { requestEventsData };
* };
* ```
*
* The provider includes:
* - Parallel fetching of earnings/conference calls and corporate actions (dividends/splits)
* - Data transformation from dxFeed format to chart format
* - Caching to avoid duplicate requests
* - Error handling with RemoteData pattern (optional - you can use try/catch)
* - Deduplication of events with same timestamp
*/
export interface DxFeedEventsDataProviderOptions {
readonly endpointUrl: string;
readonly username: string;
readonly password: string;
}
// These interfaces represent dxFeed API response format - adapt to your API
interface DxFeedEarning {
eventTime: number;
announceType: string;
eps: number;
estimatedEPS: number;
ymd: string; // Date in yyyyMMdd format
}
interface DxFeedCorporateAction {
eventTime: number;
type: string;
exYmd: string; // Ex-date in yyyyMMdd format
}
interface DxFeedDividend extends DxFeedCorporateAction {
type: 'DIVIDEND';
adjustmentValue: number;
}
interface DxFeedStockSplit extends DxFeedCorporateAction {
type: 'STOCK_SPLIT';
ext: {
splitFrom: number;
splitTo: number;
};
}
interface DxFeedConferenceCall extends DxFeedEarning {
announceType: 'CONFERENCE_CALL';
announceTime: string;
ymd: string;
referencePeriod: string;
eventType: string;
}
type EarningsAndConfCalls = Readonly<{ earnings: DxFeedEarning[]; confCalls: DxFeedConferenceCall[] }>;
type DividendsAndSplits = Readonly<{ dividends: DxFeedDividend[]; splits: DxFeedStockSplit[] }>;
const isStockSplit = (action: DxFeedCorporateAction): action is DxFeedStockSplit => action.type === 'STOCK_SPLIT';
const isDividend = (action: DxFeedCorporateAction): action is DxFeedDividend => action.type === 'DIVIDEND';
const isConferenceCall = (event: DxFeedEarning): event is DxFeedConferenceCall =>
event.announceType === 'CONFERENCE_CALL';
/**
* Fetch dividends and stock splits from corporate actions endpoint
*
* NOTE: This example fetches from two separate endpoints. Your API might:
* - Return all events in one endpoint
* - Have different endpoint structure
* - Use different authentication
*
* Adapt the fetch logic to match your API structure.
*/
const requestDividendsAndSplits = (
url: string,
instrument: string,
headers: Headers,
): Promise<RemoteData<Error, DividendsAndSplits>> => {
const queryUrl = `${url}/corporate-action/symbol/${instrument}`;
return fetch(queryUrl, { headers })
.then<DxFeedCorporateAction[]>(res => res.json())
.then(actions => {
const dividendsAndSplits = actions.reduce<DividendsAndSplits>(
(acc, action) => {
if (isDividend(action)) {
acc.dividends.push(action);
}
if (isStockSplit(action)) {
acc.splits.push(action);
}
return acc;
},
{ dividends: [], splits: [] },
);
return remoteData.success<Error, DividendsAndSplits>(dividendsAndSplits);
})
.catch(reason => remoteData.failure(new Error(reason)));
};
/**
* Fetch earnings and conference calls from earnings endpoint
*
* NOTE: This is dxFeed-specific. For your API:
* - Replace endpoint URL with your API
* - Adapt response parsing to your format
* - You might get all events in one call instead of two
*/
const requestEarningsAndConferenceCalls = (
url: string,
instrument: string,
headers: Headers,
): Promise<RemoteData<Error, EarningsAndConfCalls>> => {
const queryUrl = `${url}/earning/symbol/${instrument}`;
return fetch(queryUrl, { headers })
.then<DxFeedEarning[]>(res => res.json())
.then(events => {
const earningsAndConfCalls = events.reduce<EarningsAndConfCalls>(
(acc, event) => {
if (isConferenceCall(event)) {
acc.confCalls.push(event);
} else {
acc.earnings.push(event);
}
return acc;
},
{ earnings: [], confCalls: [] },
);
return remoteData.success<Error, EarningsAndConfCalls>(earningsAndConfCalls);
})
.catch(reason => remoteData.failure(new Error(reason)));
};
/**
* Main request function that fetches all events in parallel
*
* NOTE: This uses RxJS and RemoteData pattern. You can simplify this:
*
* ```typescript
* const requestEventsData = async (symbol: string): Promise<EventsData> => {
* try {
* // Fetch all events (or fetch separately and combine)
* const [earnings, corporateActions] = await Promise.all([
* fetch(`${apiUrl}/earnings/${symbol}`).then(r => r.json()),
* fetch(`${apiUrl}/corporate-actions/${symbol}`).then(r => r.json())
* ]);
*
* // Transform and combine
* const events = [
* ...earnings.map(toChartEarning),
* ...corporateActions.dividends.map(toChartDividends),
* ...corporateActions.splits.map(toChartSplits)
* ];
*
* return { events };
* } catch (error) {
* console.error('Failed to fetch events:', error);
* return { events: [] }; // Return empty on error
* }
* };
* ```
*/
export const requestDxFeedEvents = (
options: DxFeedEventsDataProviderOptions,
instrument: string,
): Observable<RemoteData<Error, EventsData>> => {
const headers = new Headers();
headers.set('Authorization', `Basic ${btoa(`${options.username}:${options.password}`)}`);
return from(
Promise.all([
requestEarningsAndConferenceCalls(options.endpointUrl, instrument, headers),
requestDividendsAndSplits(options.endpointUrl, instrument, headers),
]),
).pipe(
map(([earningsAndConfCalls, dividendsAndSplits]) =>
pipe(
sequenceT(remoteData.remoteData)(earningsAndConfCalls, dividendsAndSplits),
remoteData.map<[EarningsAndConfCalls, DividendsAndSplits], EventsData>(
([{ earnings, confCalls }, { dividends, splits }]) => {
const chartEarnings = removeSameTimestamps(earnings.map(toChartEarning));
const chartDividends = removeSameTimestamps(dividends.map(toChartDividends));
const chartSplits = removeSameTimestamps(splits.map(toChartSplits));
const chartConfCalls = removeSameTimestamps(confCalls.map(toChartConfCall));
return { events: [...chartDividends, ...chartEarnings, ...chartSplits, ...chartConfCalls] };
},
),
),
),
startWith(remoteData.pending),
);
};
/**
* Creates an Events Data Provider that connects to dxFeed Fundamentals API
*
* ADAPTATION GUIDE FOR YOUR API:
*
* 1. Replace fetch URLs with your API endpoints
* 2. Remove RemoteData/RxJS if you don't use them - just use Promise
* 3. Map your API response to EventsData format:
* {
* events: [
* { kind: 'earnings', timestamp: number, basic?: number, diluted?: number, periodEnding?: number },
* { kind: 'dividends', timestamp: number, gross: string },
* { kind: 'splits', timestamp: number, splitFrom: number, splitTo: number },
* { kind: 'conference-calls', timestamp: number, referencePeriod?: string, eventType?: string }
* ]
* }
* 4. Timestamp must be in milliseconds (Date.getTime() format)
* 5. Caching is recommended but optional
*
* @param options - Configuration with endpoint URL and authentication credentials
* @returns EventsDataProvider instance with caching
*/
export const createDxFeedEventsDataProvider = (options: DxFeedEventsDataProviderOptions): EventsDataProvider => {
const cache: Record<string, Promise<EventsData>> = {};
/**
* requestEventsData: Called when chart needs events for a symbol
*
* Requirements:
* - Return Promise<EventsData> with events array
* - Return { events: [] } if symbol is empty or on error
* - Caching is recommended to avoid duplicate API calls
* - Events should be sorted by timestamp (optional but recommended)
*/
const requestEventsData = (symbol: string): Promise<EventsData> => {
if (symbol === '') {
return Promise.resolve({ events: [] });
}
if (cache[symbol] !== undefined) {
return cache[symbol];
}
const eventsPromise = lastValueFrom(
requestDxFeedEvents(options, symbol).pipe(
observable.map(data => (remoteData.isSuccess(data) ? data.value : { events: [] })),
),
);
cache[symbol] = eventsPromise;
return eventsPromise;
};
return { requestEventsData };
};
/**
* Transform dxFeed date format (yyyyMMdd) to timestamp in milliseconds
*
* IMPORTANT: Chart expects timestamps in milliseconds (Date.getTime() format).
* If your API returns dates in different formats, adapt this function:
* - ISO string: new Date(dateString).getTime()
* - Unix timestamp (seconds): timestamp * 1000
* - Date object: date.getTime()
*/
const parseYMD = (ymd: string) => fromZonedTime(parse(ymd, 'yyyyMMdd', new Date()), 'UTC').getTime();
/**
* Transform dxFeed earnings to chart format
*
* Required fields:
* - kind: 'earnings'
* - timestamp: number (milliseconds)
*
* Optional fields:
* - basic: estimated EPS
* - diluted: actual EPS
* - periodEnding: period end timestamp
*/
const toChartEarning = (e: DxFeedEarning): EarningsEvent => ({
kind: 'earnings' as const,
basic: e.estimatedEPS,
diluted: e.eps,
timestamp: parseYMD(e.ymd),
periodEnding: parseYMD(e.ymd),
});
/**
* Transform dxFeed dividends to chart format
*
* Required fields:
* - kind: 'dividends'
* - timestamp: number (milliseconds)
* - gross: string (dividend amount)
*/
const toChartDividends = (e: DxFeedDividend): DividendsEvent => ({
kind: 'dividends' as const,
gross: `${e.adjustmentValue}`,
timestamp: parseYMD(e.exYmd),
});
/**
* Transform dxFeed stock splits to chart format
*
* Required fields:
* - kind: 'splits'
* - timestamp: number (milliseconds)
* - splitFrom: number (e.g., 1)
* - splitTo: number (e.g., 4 for 4:1 split)
*/
const toChartSplits = (e: DxFeedStockSplit): SplitsEvent => ({
kind: 'splits' as const,
splitFrom: e.ext.splitFrom,
splitTo: e.ext.splitTo,
timestamp: parseYMD(e.exYmd),
});
/**
* Transform dxFeed conference calls to chart format
*
* Required fields:
* - kind: 'conference-calls'
* - timestamp: number (milliseconds)
*
* Optional fields:
* - referencePeriod: string
* - eventType: string
*/
const toChartConfCall = (e: DxFeedConferenceCall): ConferenceCallEvent => ({
kind: 'conference-calls' as const,
timestamp: parseYMD(e.ymd),
referencePeriod: e.referencePeriod,
eventType: e.eventType || e.announceTime,
});
/**
* Remove duplicate events with same timestamp
*
* This is optional but recommended - prevents duplicate markers on chart
* if your API returns duplicate events.
*/
const removeSameTimestamps = (events: Event[]) =>
pipe(events, array.uniq(contramap((event: Event) => event.timestamp)(number.Eq)));

Key features

  • Parallel fetching: Uses Promise.all to fetch earnings and corporate actions simultaneously
  • RemoteData pattern: Uses RemoteData type for proper error handling
  • Date transformation: Converts dxFeed date format (yyyyMMdd) to timestamps
  • Event deduplication: Removes duplicate events using timestamp equality
  • Response caching: Caches promises per symbol to avoid duplicate requests

API reference

EventsDataProvider

Fetches fundamental economic events

EventsDataProvider.requestEventsData
EventsDataProvider.requestEventsData(symbol: string): Promise<EventsData>

Returns events data for a symbol

Parameters
symbol: string
- symbol to get {@link EventsData} for
Returns
Promise<EventsData>