in web/frontend/src/app/pages/streams/components/deltix-charts/charts/deltix-charts.component.ts [235:488]
ngOnInit() {
this.globalSettings$ = this.globalSettingService.getFilters();
this.currentTab$ = this.appStore.pipe(select(getActiveOrFirstTab));
this.httpError$ = this.everChartFeedService.onHttpError();
this.httpErrorText$ = this.httpError$.pipe(map(httpError => httpError.error.message || httpError.error.error));
this.noPoints$ = this.everChartFeedService.onNoPoints();
this.globalSettings$.pipe(takeUntil(this.destroy$)).subscribe((filters) => {
const filter_date_format = filters.dateFormat[0];
const filter_time_format = filters.timeFormat[0];
this.filter_timezone = filters.timezone[0];
this.date_format = filter_date_format;
this.time_format = filter_time_format;
this.datetime_separator = getFormatSeparator(this.date_format);
this.format = filter_date_format + ' ' + filter_time_format;
this.bsFormat = filter_date_format.toUpperCase() + ' ' + filter_time_format;
this.bsFormat = this.bsFormat.replace('tt', 'A');
this.bsFormat = this.bsFormat.replace(/f/g, 'S');
});
this.chartExchangeService
.onManuallyCHanged()
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
this.retry();
});
this.chartDate$ = combineLatest([this.globalSettings$, this.currentTab$]).pipe(
filter(([settings, tab]) => Boolean(tab?.filter?.from && tab?.filter?.to)),
map(([settings, tab]) => {
if (
new Date(tab.filter.to).getTime() - new Date(tab.filter.from).getTime() >
1000 * 60 * 60 * 24
) {
return '';
}
return formatDateTime(tab.filter.from, settings.dateFormat[0], settings.timezone[0].name);
}),
);
this.resize$
.pipe(debounceTime(350), distinctUntilChanged(equal), takeUntil(this.destroy$))
.subscribe(({width, height}) => {
this.everChartFeedService.setWidth(width);
this.onContainerResize(width, height);
});
this.tooltipData$ = combineLatest([
this.mouseMove$.pipe(
distinctUntilChanged(
(p: { time: number; points: any; yVal: number }, c) =>
`${p.time}-${p.yVal}` === `${c.time}-${c.yVal}`,
),
),
this.currentTab$,
this.hideTooltip$,
]).pipe(
auditTime(75),
switchMap(([moveEvent, tab, hideTooltip]) => {
const storageAndColors$: Observable<[{ colors: StoredColorsMap, showLines: string[] }, Partial<DeltixChartStorage> | null]> = moveEvent.points && tab.filter.chart_type === ChartTypes.LINEAR ?
combineLatest([this.linearChartsService.showLinesAndColors(), this.everChartFeedService.storage$.pipe(filter(s => !!s?.data))]) :
of([{colors: {}, showLines: []}, null]);
return storageAndColors$.pipe(
map(([linesAndColors, storage]) => [moveEvent, tab, hideTooltip, linesAndColors, storage]),
);
}),
map(([moveEvent, tab, hideTooltip, linesAndColors, storage]: [MouseMoveEvent, TabModel, boolean, { colors: StoredColorsMap, showLines: string[] }, Partial<DeltixChartStorage> | null]) => {
if (!moveEvent.time || hideTooltip) {
return null;
}
const point = JSON.parse(JSON.stringify(moveEvent.points));
const isBars = barChartTypes.includes(tab?.filter.chart_type);
const isBBO = tab?.filter.chart_type === ChartTypes.TRADES_BBO;
const isLinear = tab.filter.chart_type === ChartTypes.LINEAR;
if (this.pointIsTrade(point, moveEvent.yVal)) {
delete point.BBO;
} else {
delete point.TRADES;
}
const borderGreen =
(point.BARS && point.BARS.open < point.BARS.close) ||
(point.BBO && point.BBO.askPrice > point.BBO.bidPrice);
const borderRed =
(point.BARS && point.BARS.open > point.BARS.close) ||
(point.BBO && point.BBO.askPrice < point.BBO.bidPrice);
const borderBlue = !borderRed && !borderGreen;
let borderColor = null;
const colorNames = {};
Object.keys(linesAndColors.colors).forEach(key => colorNames[this.linearId(key)] = key);
if (isLinear) {
const lineName = Object.keys(point).find(key => moveEvent.yVal === point[key].value);
if (lineName) {
borderColor = this.colorToString(linesAndColors.colors[colorNames[lineName]]);
}
}
let from;
let to;
if (point.BARS) {
const aggregation = tab.filter.period.aggregation;
from = this.formatTooltipBarTime(aggregation, moveEvent.time - tab.filter.period.aggregation);
to = this.formatTooltipBarTime(aggregation, moveEvent.time);
}
const values = {};
if (isLinear) {
const firstKey = Object.keys(point)[0];
storage.data?.find(p => {
const isCurrent = p.time === point[firstKey].time;
Object.keys(p.points).forEach(key => {
values[key] = p.points[key].value;
});
return isCurrent;
});
}
const linearData = linesAndColors.showLines.map(line => {
let value = values[this.linearId(line)];
if (value === undefined || isNaN(value)) {
value = '-';
}
return {
name: line,
value,
isHighlight: moveEvent.yVal === point[this.linearId(line)]?.value,
highlightColor: this.colorToString(linesAndColors.colors[line]),
};
});
return {
time: formatDateTime(moveEvent.time, this.format, this.filter_timezone.name),
point,
from,
to,
yVal: moveEvent.yVal,
isBars,
isL2: tab?.filter.chart_type === ChartTypes.PRICES_L2,
isLinear,
linearData,
isBBO,
borderGreen,
borderRed,
borderBlue,
borderColor,
};
}),
);
this.magnetCoordinates$ = combineLatest([
this.mouseMove$,
this.currentTab$.pipe(
map((tab) => ({
from: tab?.filter ? new Date(tab.filter.from).getTime() : 0,
to: tab?.filter ? new Date(tab.filter.to).getTime() : 0,
})),
),
]).pipe(map((([data, {from, to}]) => {
const {width, height} = this.getSize();
if (!data.time) {
return null;
}
let yInPx = 0;
const widthInMs = to - from;
const xCoordRatio = data.time <= to ? (data.time - from) / widthInMs : 0.95;
const x = Math.max(0, Math.round(xCoordRatio * width));
const pads = this.appFacade.getStateFor('everChart', '1').app.pads;
if (pads.LINEAR) {
const pad = pads.LINEAR;
const decimalsL = pad.max.next.toString().split('.')[1]?.length || 0;
const multi = Math.pow(10, decimalsL);
yInPx = this.getYCoordinate(data.yVal * multi, height, pad.min.next * multi, pad.max.next * multi);
}
return {
x,
y: data.y,
yInPx,
};
})));
this.tooltipPosition$ = combineLatest([
this.magnetCoordinates$,
this.tooltipDimensions(),
this.mouseMove$,
]).pipe(
map(([coordinates, dimensions, data]) => {
if (!coordinates || !data.time) {
return null;
}
const {height} = this.getSize();
const bottomPadding = 25;
const mousePadding = 10;
const dimensionHeight =
typeof dimensions.height === 'function' ? dimensions.height(data) : dimensions.height;
const dimensionWidth =
typeof dimensions.width === 'function' ? dimensions.width(data) : dimensions.width;
const xInPx = coordinates.x - mousePadding;
const left = xInPx - dimensionWidth < 0 ? xInPx + mousePadding * 2 : xInPx - dimensionWidth;
return {
top: Math.min(
Math.max(0, coordinates.y - dimensionHeight - mousePadding),
height - bottomPadding - dimensionHeight,
),
left,
height: dimensionHeight,
width: dimensionWidth,
};
}),
);
this.tooltipPosition$.pipe(takeUntil(this.destroy$)).subscribe((position) => {
if (!position) {
return;
}
this.elementRef.nativeElement.style.setProperty('--tooltip-top', `${position.top}px`);
this.elementRef.nativeElement.style.setProperty('--tooltip-left', `${position.left}px`);
this.elementRef.nativeElement.style.setProperty('--tooltip-height', `${position.height}px`);
this.elementRef.nativeElement.style.setProperty('--tooltip-width', `${position.width}px`);
});
this.hideLine$ = combineLatest([
this.dragging$.pipe(distinctUntilChanged()),
this.endOfStreamOutOfRange$,
this.httpError$,
this.noPoints$,
]).pipe(
map(
([dragging, outOfRange, httpError, noPoints]) =>
dragging || outOfRange || !!httpError || noPoints,
),
);
}