Загрузка данных
diff --git a/.gitignore b/.gitignore
index d1e2a9e..aedbf73 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,5 @@
dist
-types
+/types
node_modules
.vscode
.idea
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 84d127a..9c00068 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,20 @@
-# next
+# latest
+
+- `breaking change`: Технический идентификатор инструмента перенесён из поля `symbol` в новое обязательное поле `symbolId`
+- Добавлены необязательные поля `symbol` и `symbolName` для отображения короткого обозначения и полного названия инструмента
+- Если `symbol` или `symbolName` не переданы, их значения автоматически формируются на основе `symbolId`
+- `breaking change`: Метод `setSymbol` теперь принимает объект `{ symbolId, symbol?, symbolName? }`; старый формат больше не поддерживается
+
+# 0.1.12
- Добавлена поддержка хоткеев на элементы рисования (alt+t, alt+v, alt+h, alt+f, del, esc)
+# 0.1.11
+
+- Добавлен необязательный параметр `timeFormat?: TimeFormat` для настройки формата отображения времени
+- Добавлен необязательный параметр `dateFormat?: DateFormat` для настройки формата отображения даты
+- Добавлен необязательный параметр `interval?: Intervals | null` для задания начального интервала при инициализации графика
+
# 0.1.10
- Добавление логарифмической шкалы
diff --git a/src/components/CompareLegendRow/index.tsx b/src/components/CompareLegendRow/index.tsx
index c9e4eed..78f0b64 100644
--- a/src/components/CompareLegendRow/index.tsx
+++ b/src/components/CompareLegendRow/index.tsx
@@ -13,13 +13,13 @@ interface CompareLegendRowProps {
}
export function CompareLegendRow({ item, onRemove }: CompareLegendRowProps) {
- const { symbol, mode, value, color } = item;
+ const { symbolId, symbolName, mode, value, color } = item;
- const handleDelete = () => onRemove?.(symbol, mode);
+ const handleDelete = () => onRemove?.(symbolId, mode);
return (
<div className={styles.item}>
- <div className={styles.symbol}>{symbol}</div>
+ <div className={styles.symbol}>{symbolName}</div>
<div className={styles.priceWrapper}>
<div
diff --git a/src/core/Chart.ts b/src/core/Chart.ts
index da4f387..c1f82d3 100644
--- a/src/core/Chart.ts
+++ b/src/core/Chart.ts
@@ -113,7 +113,7 @@ export class Chart implements ISerializable<ChartSnapshot> {
private currentInterval: Intervals | null = null;
- private activeSymbols: string[] = [];
+ private activeSymbolIds: string[] = [];
private historyBatchRunning = false;
@@ -343,15 +343,16 @@ export class Chart implements ISerializable<ChartSnapshot> {
public getRealtimeApi() {
return {
getTimeframe: () => this.eventManager.getTimeframe(),
- getSymbols: () => this.activeSymbols,
- update: (symbol: string, candle: Candle) => {
- this.dataSource.updateRealtime(symbol, candle);
+ getSymbols: () => this.activeSymbolIds,
+ update: (symbolId: string, candle: Candle) => {
+ this.dataSource.updateRealtime(symbolId, candle);
},
};
}
public getSnapshot(): ChartSnapshot {
- const { seriesSelected, timeframe, dateFormat, timeFormat, interval } = this.eventManager.exportChartSettings();
+ const { seriesSelected, timeframe, dateFormat, timeFormat, interval, symbolInfo } =
+ this.eventManager.exportChartSettings();
return {
panes: this.paneManager.getSnapshot(),
@@ -360,7 +361,7 @@ export class Chart implements ISerializable<ChartSnapshot> {
dateFormat,
timeFormat,
interval,
- symbol: this.activeSymbols[0],
+ ...symbolInfo,
};
}
@@ -370,9 +371,9 @@ export class Chart implements ISerializable<ChartSnapshot> {
this.historyBatchRunning = true;
requestAnimationFrame(() => {
- const symbols = this.activeSymbols.slice();
+ const symbolIds = this.activeSymbolIds.slice();
- Promise.all(symbols.map((symbol) => this.dataSource.loadMoreHistory(symbol))).finally(() => {
+ Promise.all(symbolIds.map((symbolId) => this.dataSource.loadMoreHistory(symbolId))).finally(() => {
this.historyBatchRunning = false;
const range = this.lwcChart.timeScale().getVisibleLogicalRange();
@@ -384,7 +385,7 @@ export class Chart implements ISerializable<ChartSnapshot> {
});
};
- private setupDataSourceSubs() {
+ private setupDataSourceSubs(): void {
const getWarmupFrom = (): number => {
if (this.currentInterval && this.currentInterval !== Intervals.All) {
return getIntervalRange(this.currentInterval).from;
@@ -396,36 +397,36 @@ export class Chart implements ISerializable<ChartSnapshot> {
const { from } = range as IRange<number>;
- return from as number;
+ return from;
};
- const warmupSymbols = (symbols: string[]) => {
- if (!symbols.length) return;
+ const warmupSymbolIds = (symbolIds: string[]): void => {
+ if (!symbolIds.length) return;
const from = getWarmupFrom();
if (!from) return;
- Promise.all(symbols.map((symbol) => this.dataSource.loadTill(symbol, from))).catch((error) => {
+ Promise.all(symbolIds.map((symbolId) => this.dataSource.loadTill(symbolId, from))).catch((error) => {
console.error('[Chart] Ошибка при прогреве символов:', error);
});
};
- const symbols$ = combineLatest([this.eventManager.symbol(), this.compareManager.itemsObs()]).pipe(
- map(([main, items]) => Array.from(new Set([main, ...items.map((item) => item.symbol)]))),
+ const symbolIds$ = combineLatest([this.eventManager.symbolId(), this.compareManager.itemsObs()]).pipe(
+ map(([mainSymbolId, items]) => Array.from(new Set([mainSymbolId, ...items.map(({ symbolId }) => symbolId)]))),
);
this.subscriptions.add(
this.eventManager
.getInterval()
- .pipe(withLatestFrom(symbols$))
- .subscribe(([interval, symbols]) => {
+ .pipe(withLatestFrom(symbolIds$))
+ .subscribe(([interval, symbolIds]) => {
this.currentInterval = interval;
if (!interval) return;
if (interval === Intervals.All) {
- Promise.all(symbols.map((symbol) => this.dataSource.loadAllHistory(symbol)))
+ Promise.all(symbolIds.map((symbolId) => this.dataSource.loadAllHistory(symbolId)))
.then(() => {
requestAnimationFrame(() => this.lwcChart.timeScale().fitContent());
})
@@ -436,7 +437,7 @@ export class Chart implements ISerializable<ChartSnapshot> {
const { from, to } = getIntervalRange(interval);
- Promise.all(symbols.map((symbol) => this.dataSource.loadTill(symbol, from)))
+ Promise.all(symbolIds.map((symbolId) => this.dataSource.loadTill(symbolId, from)))
.then(() => {
this.lwcChart.timeScale().setVisibleRange({
from: from as Time,
@@ -450,29 +451,28 @@ export class Chart implements ISerializable<ChartSnapshot> {
);
this.subscriptions.add(
- symbols$.subscribe((symbols) => {
- const previousSymbols = this.activeSymbols;
+ symbolIds$.subscribe((symbolIds) => {
+ const previousSymbolIds = this.activeSymbolIds;
- this.activeSymbols = symbols;
+ this.activeSymbolIds = symbolIds;
+ this.dataSource.setSymbols(symbolIds);
- this.dataSource.setSymbols(symbols);
+ const previousSymbolIdsSet = new Set(previousSymbolIds);
+ const addedSymbolIds: string[] = [];
- const previousSymbolsSet = new Set(previousSymbols);
- const addedSymbols: string[] = [];
+ for (let index = 0; index < symbolIds.length; index += 1) {
+ const symbolId = symbolIds[index];
- for (let index = 0; index < symbols.length; index += 1) {
- const symbol = symbols[index];
-
- if (!symbol) continue;
- if (previousSymbolsSet.has(symbol)) {
+ if (!symbolId) continue;
+ if (previousSymbolIdsSet.has(symbolId)) {
continue;
}
- addedSymbols.push(symbol);
+ addedSymbolIds.push(symbolId);
}
- if (addedSymbols.length) {
- warmupSymbols(addedSymbols);
+ if (addedSymbolIds.length) {
+ warmupSymbolIds(addedSymbolIds);
}
}),
);
diff --git a/src/core/ChartSettings.ts b/src/core/ChartSettings.ts
index 97b5b9c..397640f 100644
--- a/src/core/ChartSettings.ts
+++ b/src/core/ChartSettings.ts
@@ -1,20 +1,27 @@
-import { ChartSeriesType, Intervals, IntervalsToTimeframe, TimeFormat } from '@src/types';
+import { ChartSeriesType, Intervals, IntervalsToTimeframe, SymbolInfo, SymbolInfoInput, TimeFormat } from '@src/types';
import { Timeframes } from '@src/types/timeframes';
-import { DateFormat } from '@src/utils';
+import { DateFormat, normalizeSymbolInfo } from '@src/utils';
export interface ChartSettings {
+ symbolInfo: SymbolInfo;
timeframe: Timeframes;
seriesSelected: ChartSeriesType;
- symbol: string;
timeFormat: TimeFormat;
dateFormat: DateFormat;
interval: Intervals | null;
}
-export type ChartSettingsSource = Partial<ChartSettings> | string;
+export type ChartSettingsSourceObject = Omit<Partial<ChartSettings>, 'symbolInfo'> & {
+ symbolInfo?: SymbolInfoInput;
+};
+
+export type ChartSettingsSource = ChartSettingsSourceObject | string;
function parseJsonIfString(value: unknown): unknown {
- if (typeof value !== 'string') return value;
+ if (typeof value !== 'string') {
+ return value;
+ }
+
try {
return JSON.parse(value);
} catch {
@@ -23,7 +30,10 @@ function parseJsonIfString(value: unknown): unknown {
}
function asPlainObject(value: unknown): Record<string, unknown> | undefined {
- if (typeof value !== 'object' || value === null || Array.isArray(value)) return undefined;
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
+ return undefined;
+ }
+
return value as Record<string, unknown>;
}
@@ -31,17 +41,36 @@ function asString(value: unknown): string | undefined {
return typeof value === 'string' ? value : undefined;
}
-function asStringList(value: unknown): string[] | undefined {
- if (!Array.isArray(value)) return undefined;
+function asSymbolInfo(value: unknown): SymbolInfo | undefined {
+ const symbolInfoObject = asPlainObject(value);
+
+ if (!symbolInfoObject) {
+ return undefined;
+ }
+
+ const symbolId = asString(symbolInfoObject.symbolId);
- return value.map(asString).filter((val) => val !== undefined);
+ if (symbolId === undefined) {
+ return undefined;
+ }
+
+ return normalizeSymbolInfo({
+ symbolId,
+ symbol: asString(symbolInfoObject.symbol),
+ symbolName: asString(symbolInfoObject.symbolName),
+ });
}
function asInterval(value: unknown): Intervals | null | undefined {
- if (value === null) return null;
+ if (value === null) {
+ return null;
+ }
const intervalString = asString(value);
- if (intervalString === undefined) return undefined;
+
+ if (intervalString === undefined) {
+ return undefined;
+ }
return intervalString in IntervalsToTimeframe ? (intervalString as Intervals) : undefined;
}
@@ -50,12 +79,14 @@ export function parseChartSettings(value: unknown): Partial<ChartSettings> {
const parsedValue = parseJsonIfString(value);
const settingsObject = asPlainObject(parsedValue);
- if (!settingsObject) return {};
+ if (!settingsObject) {
+ return {};
+ }
return {
+ symbolInfo: asSymbolInfo(settingsObject.symbolInfo),
timeframe: asString(settingsObject.timeframe) as Timeframes | undefined,
seriesSelected: asString(settingsObject.seriesSelected) as ChartSeriesType | undefined,
- symbol: asString(settingsObject.symbol),
timeFormat: asString(settingsObject.timeFormat) as TimeFormat | undefined,
dateFormat: asString(settingsObject.dateFormat) as DateFormat | undefined,
interval: asInterval(settingsObject.interval),
diff --git a/src/core/CompareManager.ts b/src/core/CompareManager.ts
index c450389..5f5b94b 100644
--- a/src/core/CompareManager.ts
+++ b/src/core/CompareManager.ts
@@ -10,14 +10,13 @@ import { IndicatorManager } from '@core/IndicatorManager';
import { PaneManager } from '@core/PaneManager';
import { PriceScale } from '@core/PriceScale';
import { COMPARE_COLOR_PALETTE } from '@src/theme';
-import { CompareItem, CompareMode, Direction, IndicatorConfig } from '@src/types';
+import { CompareItem, CompareMode, Direction, IndicatorConfig, SymbolInfo, SymbolInfoInput } from '@src/types';
import { IndicatorSnapshot } from '@src/types/snapshot';
-import { createFallbackColor, normalizeColor, normalizeSymbol } from '@src/utils';
+import { createFallbackColor, normalizeColor, normalizeSymbol, normalizeSymbolInfo } from '@src/utils';
-interface CompareEntry {
+interface CompareEntry extends CompareItem {
key: string;
- symbol: string;
- mode: CompareMode;
+ symbolId$: BehaviorSubject<string>;
symbol$: BehaviorSubject<string>;
entity: Indicator;
}
@@ -88,26 +87,38 @@ export class CompareManager {
public async setSymbolMode(
seriesType: SeriesType,
- symbolRaw: string,
+ symbolInfoInput: SymbolInfoInput,
mode: CompareMode,
paneId?: number,
): Promise<void> {
- const symbol = normalizeSymbol(symbolRaw);
+ const normalizedSymbolId = normalizeSymbol(symbolInfoInput.symbolId);
- if (!symbol) {
+ if (!normalizedSymbolId) {
return;
}
+ const symbolInfo = normalizeSymbolInfo({
+ ...symbolInfoInput,
+ symbolId: normalizedSymbolId,
+ });
+
+ if (!symbolInfo) {
+ return;
+ }
+
+ const { symbolId, symbol, symbolName } = symbolInfo;
+
if (mode === CompareMode.NewScale && this.isNewScaleDisabled() && !this.restoringInitialIndicators) {
return;
}
- const key = makeKey(symbol, mode);
+ const key = makeKey(symbolId, mode);
if (this.entries.has(key)) {
return;
}
+ const symbolId$ = new BehaviorSubject(symbolId);
const symbol$ = new BehaviorSubject(symbol);
const entity = this.indicatorManager.addEntity<Indicator>((zIndex, moveUp, moveDown) => {
@@ -127,7 +138,8 @@ export class CompareManager {
const usedColorsByIndicators = flatten(usedColorsByIndicatorsRaw).filter((color) => color !== undefined);
const usedColors = usedColorsByCompare.concat(usedColorsByIndicators);
- const config = getDefaultCompareIndicatorConfig(symbol, usedColors);
+
+ const config = getDefaultCompareIndicatorConfig(seriesType, symbolInfo, usedColors);
const associatedPane =
mode === CompareMode.NewPane
@@ -139,6 +151,7 @@ export class CompareManager {
return new Indicator({
id: key,
lwcChart: this.chart,
+ mainSymbolId$: symbolId$,
mainSymbol$: symbol$,
dataSource: this.dataSource,
associatedPane,
@@ -169,33 +182,36 @@ export class CompareManager {
this.entries.set(key, {
key,
+ symbolId,
symbol,
+ symbolName,
mode,
+ symbolId$,
symbol$,
entity,
});
this.commitEntriesChange();
- await this.dataSource.isReady(symbol);
+ await this.dataSource.isReady(symbolId);
}
- public removeSymbolMode(symbolRaw: string, mode: CompareMode): void {
- const symbol = normalizeSymbol(symbolRaw);
+ public removeSymbolMode(symbolIdRaw: string, mode: CompareMode): void {
+ const symbolId = normalizeSymbol(symbolIdRaw);
- if (!symbol) {
+ if (!symbolId) {
return;
}
- if (this.removeEntry(makeKey(symbol, mode))) {
+ if (this.removeEntry(makeKey(symbolId, mode))) {
this.commitEntriesChange();
}
}
- public removeSymbol(symbolRaw: string): void {
- const symbol = normalizeSymbol(symbolRaw);
+ public removeSymbol(symbolIdRaw: string): void {
+ const symbolId = normalizeSymbol(symbolIdRaw);
- if (!symbol) {
+ if (!symbolId) {
return;
}
@@ -205,7 +221,7 @@ export class CompareManager {
for (let index = 0; index < entries.length; index += 1) {
const [key, entry] = entries[index];
- if (entry.symbol !== symbol) {
+ if (entry.symbolId !== symbolId) {
continue;
}
@@ -229,8 +245,10 @@ export class CompareManager {
}
public getAllEntities() {
- return Array.from(this.entries.values()).map(({ symbol, entity, mode }) => ({
+ return Array.from(this.entries.values()).map(({ symbolId, symbol, symbolName, entity, mode }) => ({
+ symbolId,
symbol,
+ symbolName,
entity,
mode,
}));
@@ -247,26 +265,40 @@ export class CompareManager {
this.restoringInitialIndicators = true;
try {
- for (const indicator of initialIndicators) {
+ for (let index = 0; index < initialIndicators.length; index += 1) {
+ const indicator = initialIndicators[index];
+
if (indicator.indicatorType !== undefined) {
continue;
}
- if (!indicator.config?.label) {
+ const symbolInfoInput = indicator.config?.symbolInfo;
+
+ if (!symbolInfoInput) {
continue;
}
- const series = indicator.config.series[0];
+ const symbolInfo = normalizeSymbolInfo(symbolInfoInput);
+
+ if (!symbolInfo) {
+ continue;
+ }
+
+ const series = indicator.config?.series[0];
+
+ if (!series) {
+ continue;
+ }
const compareMode =
series.seriesOptions?.priceScaleId === Direction.Left
? CompareMode.NewScale
- : indicator.config.newPane
+ : indicator.config?.newPane
? CompareMode.NewPane
: CompareMode.Percentage;
// eslint-disable-next-line no-await-in-loop
- await this.setSymbolMode(series.name, indicator.config.label, compareMode, indicator.paneId);
+ await this.setSymbolMode(series.name, symbolInfo, compareMode, indicator.paneId);
}
} finally {
this.restoringInitialIndicators = false;
@@ -283,6 +315,7 @@ export class CompareManager {
this.entries.delete(key);
this.indicatorManager.removeEntity(entry.entity);
entry.entity.destroy();
+ entry.symbolId$.complete();
entry.symbol$.complete();
return true;
@@ -300,7 +333,9 @@ export class CompareManager {
for (let index = 0; index < values.length; index += 1) {
items.push({
+ symbolId: values[index].symbolId,
symbol: values[index].symbol,
+ symbolName: values[index].symbolName,
mode: values[index].mode,
});
@@ -376,8 +411,8 @@ export class CompareManager {
}
}
-function makeKey(symbol: string, mode: CompareMode): string {
- return `${symbol}|${mode}`;
+function makeKey(symbolId: string, mode: CompareMode): string {
+ return `${symbolId}|${mode}`;
}
function getPaletteColorFromIndex(usedColors: Set<string>, startIndex: number): string {
@@ -392,12 +427,17 @@ function getPaletteColorFromIndex(usedColors: Set<string>, startIndex: number):
return createFallbackColor(usedColors.size);
}
-const getDefaultCompareIndicatorConfig = (symbol: string, usedColors: string[]): IndicatorConfig => {
+function getDefaultCompareIndicatorConfig(
+ seriesType: SeriesType,
+ symbolInfo: SymbolInfo,
+ usedColors: string[],
+): IndicatorConfig {
const reservedColors = new Set(usedColors.map(normalizeColor));
return {
+ symbolInfo,
newPane: true,
- label: symbol,
+ label: symbolInfo.symbolName,
series: [
{
name: 'Line', // todo: change with enum
@@ -409,4 +449,4 @@ const getDefaultCompareIndicatorConfig = (symbol: string, usedColors: string[]):
},
],
};
-};
+}
diff --git a/src/core/EventManager.ts b/src/core/EventManager.ts
index c99268d..9a957a0 100644
--- a/src/core/EventManager.ts
+++ b/src/core/EventManager.ts
@@ -1,10 +1,16 @@
-import { BehaviorSubject, combineLatest, map, Observable, Subscription } from 'rxjs';
+import { BehaviorSubject, combineLatest, distinctUntilChanged, map, Observable, Subscription } from 'rxjs';
-import { ChartOptionsModel, ChartSeriesType, Intervals, TimeFormat } from '@src/types';
+import { ChartOptionsModel, ChartSeriesType, Intervals, SymbolInfo, SymbolInfoInput, TimeFormat } from '@src/types';
import { Defaults } from '@src/types/defaults';
import { Timeframes } from '@src/types/timeframes';
-import { DateFormat, getTimeframeByInterval, shouldShowTime } from '@src/utils';
+import {
+ areSymbolInfosEqual,
+ DateFormat,
+ getTimeframeByInterval,
+ normalizeSymbolInfo,
+ shouldShowTime,
+} from '@src/utils';
import { ChartSettings, ChartSettingsSource, parseChartSettings } from './ChartSettings';
import { UndoKey, UndoRedo } from './UndoRedo';
@@ -12,7 +18,7 @@ import { UndoKey, UndoRedo } from './UndoRedo';
interface EventManagerParams {
initialTimeframe: Timeframes;
initialSeries: ChartSeriesType;
- initialSymbol: string;
+ initialSymbolInfo: SymbolInfoInput;
initialTimeFormat?: TimeFormat;
initialDateFormat?: DateFormat;
initialInterval?: Intervals | null;
@@ -30,7 +36,7 @@ interface SetWithHistoryOptions {
export class EventManager {
private timeframe$: BehaviorSubject<Timeframes>;
private seriesSelected$: BehaviorSubject<ChartSeriesType>;
- private symbol$: BehaviorSubject<string>;
+ private symbolInfo$: BehaviorSubject<SymbolInfo>;
private timeFormat$: BehaviorSubject<TimeFormat>;
private dateFormat$: BehaviorSubject<DateFormat>;
private interval$: BehaviorSubject<Intervals | null>;
@@ -42,22 +48,28 @@ export class EventManager {
constructor({
initialTimeframe,
initialSeries,
- initialSymbol,
+ initialSymbolInfo,
initialTimeFormat,
initialDateFormat,
initialInterval = null,
}: EventManagerParams) {
+ const normalizedSymbolInfo = normalizeSymbolInfo(initialSymbolInfo);
+
+ if (!normalizedSymbolInfo) {
+ throw new Error('[EventManager] symbolId is required');
+ }
+
this.timeframe$ = new BehaviorSubject<Timeframes>(initialTimeframe);
- this.interval$ = new BehaviorSubject<Intervals | null>(initialInterval);
this.seriesSelected$ = new BehaviorSubject<ChartSeriesType>(initialSeries);
- this.symbol$ = new BehaviorSubject<string>(initialSymbol);
+ this.symbolInfo$ = new BehaviorSubject<SymbolInfo>(normalizedSymbolInfo);
this.timeFormat$ = new BehaviorSubject<TimeFormat>(initialTimeFormat ?? Defaults.timeFormat);
this.dateFormat$ = new BehaviorSubject<DateFormat>(initialDateFormat ?? Defaults.dateFormat);
+ this.interval$ = new BehaviorSubject<Intervals | null>(initialInterval);
this.undoRedo = new UndoRedo({
timeframe: (value) => this.timeframe$.next(value),
seriesSelected: (value) => this.seriesSelected$.next(value),
- symbol: (value) => this.symbol$.next(value),
+ symbolInfo: (value) => this.symbolInfo$.next(value),
timeFormat: (value) => this.timeFormat$.next(value),
dateFormat: (value) => this.dateFormat$.next(value),
interval: (value) => this.interval$.next(value),
@@ -93,7 +105,7 @@ export class EventManager {
return this.timeframe$.value;
}
- public setInterval = (next: Intervals, options?: SetWithHistoryOptions) => {
+ public setInterval = (next: Intervals, options?: SetWithHistoryOptions): void => {
const timeframe = getTimeframeByInterval(next);
this.undoRedo.group(() => {
@@ -102,18 +114,64 @@ export class EventManager {
});
};
- public resetInterval = (options?: SetWithHistoryOptions) =>
+ public resetInterval = (options?: SetWithHistoryOptions): void =>
this.setWithHistory('interval', this.interval$, null, options);
public getInterval(): Observable<Intervals | null> {
return this.interval$.asObservable();
}
- public setSymbol = (next: string, options?: SetWithHistoryOptions) =>
- this.setWithHistory('symbol', this.symbol$, next, options);
+ public setSymbol = (symbolInfoInput: SymbolInfoInput, options?: SetWithHistoryOptions): void => {
+ const nextSymbolInfo = normalizeSymbolInfo(symbolInfoInput);
+
+ if (!nextSymbolInfo) {
+ return;
+ }
+
+ const currentSymbolInfo = this.symbolInfo$.value;
+
+ if (areSymbolInfosEqual(currentSymbolInfo, nextSymbolInfo)) {
+ return;
+ }
+
+ this.setWithHistory('symbolInfo', this.symbolInfo$, nextSymbolInfo, options);
+ };
public getSymbol(): Observable<string> {
- return this.symbol$.asObservable();
+ return this.symbolId();
+ }
+
+ public getSymbolInfo(): SymbolInfo {
+ return this.symbolInfo$.value;
+ }
+
+ public getSymbolId(): string {
+ return this.symbolInfo$.value.symbolId;
+ }
+
+ public symbolInfo(): Observable<SymbolInfo> {
+ return this.symbolInfo$.pipe(distinctUntilChanged(areSymbolInfosEqual));
+ }
+
+ public symbolId(): Observable<string> {
+ return this.symbolInfo$.pipe(
+ map(({ symbolId }) => symbolId),
+ distinctUntilChanged(),
+ );
+ }
+
+ public symbol(): Observable<string> {
+ return this.symbolInfo$.pipe(
+ map(({ symbol }) => symbol),
+ distinctUntilChanged(),
+ );
+ }
+
+ public symbolName(): Observable<string> {
+ return this.symbolInfo$.pipe(
+ map(({ symbolName }) => symbolName),
+ distinctUntilChanged(),
+ );
}
public setTimeFormat = (next: TimeFormat, options?: SetWithHistoryOptions): void =>
@@ -133,16 +191,12 @@ export class EventManager {
);
}
- public setTimeframe = (next: Timeframes, options?: SetWithHistoryOptions) =>
+ public setTimeframe = (next: Timeframes, options?: SetWithHistoryOptions): void =>
this.undoRedo.group(() => {
this.resetInterval(options);
this.setWithHistory('timeframe', this.timeframe$, next, options);
});
- public symbol(): Observable<string> {
- return this.symbol$.asObservable();
- }
-
public timeframe(): Observable<Timeframes> {
return this.timeframe$.asObservable();
}
@@ -155,7 +209,7 @@ export class EventManager {
return this.timeframe$.asObservable();
}
- public setSeriesSelected = (next: ChartSeriesType, options?: SetWithHistoryOptions) =>
+ public setSeriesSelected = (next: ChartSeriesType, options?: SetWithHistoryOptions): void =>
this.setWithHistory('seriesSelected', this.seriesSelected$, next, options);
public getSelectedSeries(): Observable<ChartSeriesType> {
@@ -176,9 +230,9 @@ export class EventManager {
public exportChartSettings(): ChartSettings {
return {
+ symbolInfo: this.symbolInfo$.value,
timeframe: this.timeframe$.value,
seriesSelected: this.seriesSelected$.value,
- symbol: this.symbol$.value,
timeFormat: this.timeFormat$.value,
dateFormat: this.dateFormat$.value,
interval: this.interval$.value,
@@ -186,11 +240,12 @@ export class EventManager {
}
public importChartSettings(settings: ChartSettingsSource): void {
- const { symbol, seriesSelected, timeframe, timeFormat, dateFormat, interval } = parseChartSettings(settings);
+ const { symbolInfo, seriesSelected, timeframe, timeFormat, dateFormat, interval } = parseChartSettings(settings);
+
const setOptions = { history: false };
- if (symbol) {
- this.setSymbol(symbol, setOptions);
+ if (symbolInfo) {
+ this.setSymbol(symbolInfo, setOptions);
}
if (seriesSelected) {
this.setSeriesSelected(seriesSelected, setOptions);
@@ -207,6 +262,7 @@ export class EventManager {
}
if (timeframe) {
this.setTimeframe(timeframe, setOptions);
+ return;
}
if (interval === null) {
this.resetInterval(setOptions);
@@ -219,7 +275,7 @@ export class EventManager {
this.timeframe$.complete();
this.controlBarVisible$.complete();
this.interval$.complete();
- this.symbol$.complete();
+ this.symbolInfo$.complete();
this.seriesSelected$.complete();
}
}
diff --git a/src/core/Indicator.ts b/src/core/Indicator.ts
index b0d9ee7..ddb29db 100644
--- a/src/core/Indicator.ts
+++ b/src/core/Indicator.ts
@@ -13,6 +13,7 @@ import { DOMObjectSnapshot, IndicatorSnapshot, ISerializable } from '@src/types/
type IIndicator = DOMObject;
export interface IndicatorParams extends DOMObjectParams {
+ mainSymbolId$: Observable<string>;
mainSymbol$: Observable<string>;
lwcChart: IChartApi;
dataSource: DataSource;
@@ -28,6 +29,7 @@ export class Indicator extends DOMObject implements ISerializable<IndicatorSnaps
private seriesMap: Map<string, SeriesStrategies> = new Map();
private lwcChart: IChartApi;
private dataSource: DataSource;
+ private mainSymbolId$: Observable<string>;
private mainSymbol$: Observable<string>;
private associatedPane: Pane;
private config: IndicatorConfig;
@@ -45,6 +47,7 @@ export class Indicator extends DOMObject implements ISerializable<IndicatorSnaps
onDelete,
moveUp,
moveDown,
+ mainSymbolId$,
mainSymbol$,
associatedPane,
paneId,
@@ -53,6 +56,7 @@ export class Indicator extends DOMObject implements ISerializable<IndicatorSnaps
super({ id, name: config?.label ?? id, zIndex, onDelete, moveUp, moveDown, paneId });
this.lwcChart = lwcChart;
this.dataSource = dataSource;
+ this.mainSymbolId$ = mainSymbolId$;
this.mainSymbol$ = mainSymbol$;
this.indicatorType = type;
this.config = config;
@@ -200,6 +204,7 @@ export class Indicator extends DOMObject implements ISerializable<IndicatorSnaps
: undefined,
seriesOptions,
priceScaleOptions,
+ mainSymbolId$: this.mainSymbolId$,
mainSymbol$: this.mainSymbol$,
mainSerie$: this.associatedPane.getMainSerie(),
showSymbolLabel: false,
diff --git a/src/core/IndicatorManager.ts b/src/core/IndicatorManager.ts
index 4a11436..0ce3936 100644
--- a/src/core/IndicatorManager.ts
+++ b/src/core/IndicatorManager.ts
@@ -1,5 +1,5 @@
import { IChartApi } from 'lightweight-charts';
-import { BehaviorSubject, map, Observable } from 'rxjs';
+import { BehaviorSubject, Observable } from 'rxjs';
import { DataSource } from '@core/DataSource';
import { DOMModel } from '@core/DOMModel';
@@ -85,8 +85,8 @@ export class IndicatorManager {
onDelete: this.deleteIndicator,
moveUp,
moveDown,
-
- mainSymbol$: this.eventManager.getSymbol(),
+ mainSymbolId$: this.eventManager.symbolId(),
+ mainSymbol$: this.eventManager.symbol(),
lwcChart: this.lwcChart,
dataSource: this.dataSource,
associatedPane,
diff --git a/src/core/Legend.ts b/src/core/Legend.ts
index b57a6b1..b4aa179 100644
--- a/src/core/Legend.ts
+++ b/src/core/Legend.ts
@@ -33,7 +33,9 @@ export interface Ohlc {
}
export interface CompareLegendItem {
+ symbolId: string;
symbol: string;
+ symbolName: string;
mode: CompareMode;
value: string;
color: string;
@@ -80,7 +82,7 @@ export class Legend {
private config: OHLCConfig;
private mainSeries!: SeriesStrategies;
- private mainSymbol = '';
+ private mainSymbolName = '';
private isChartHovered = false;
private model$ = new BehaviorSubject<LegendModel>([]);
private tooltipVisability = new BehaviorSubject<boolean>(false);
@@ -110,10 +112,8 @@ export class Legend {
this.openIndicatorSettings = openIndicatorSettings;
this.subscriptions.add(
- this.eventManager.symbol().subscribe((symbol) => {
- const symbolParts = symbol.split(':');
-
- this.mainSymbol = symbolParts[symbolParts.length - 1] || symbol;
+ this.eventManager.symbolName().subscribe((symbolName) => {
+ this.mainSymbolName = symbolName;
this.updateWithLastCandle();
}),
);
@@ -189,7 +189,7 @@ export class Legend {
model.push({
id: `main-series-${this.paneId}`,
- name: this.mainSymbol,
+ name: this.mainSymbolName,
values: series as Partial<Record<keyof Ohlc, { value: number | string | Time; color: string; name: string }>>,
isIndicator: false,
});
@@ -266,7 +266,7 @@ export class Legend {
model.push({
id: `main-series-${this.paneId}`,
- name: this.mainSymbol,
+ name: this.mainSymbolName,
values: series as Partial<Record<keyof Ohlc, { value: number | string | Time; color: string; name: string }>>,
isIndicator: false,
});
diff --git a/src/core/MoexChart.tsx b/src/core/MoexChart.tsx
index 7cf2b96..611e81f 100644
--- a/src/core/MoexChart.tsx
+++ b/src/core/MoexChart.tsx
@@ -17,8 +17,8 @@ import { FullscreenController } from '@src/core/Fullscreen';
import { configureThemeStore } from '@src/theme/store';
import { ThemeKey, ThemeMode } from '@src/theme/types';
import { Locale, setLocale, t } from '@src/translations';
-import { Candle, ChartSeriesType, ChartTypeOptions, OHLCConfig, TooltipConfig } from '@src/types';
-import { ISerializable, MoexChartSnapshot } from '@src/types/snapshot';
+import { Candle, ChartSeriesType, ChartTypeOptions, OHLCConfig, SymbolInfoInput, TooltipConfig } from '@src/types';
+import { ISerializable, MoexChartSnapshot, MoexChartSnapshotInput } from '@src/types/snapshot';
import { Timeframes } from '@src/types/timeframes';
import { setPricePrecision } from '@src/utils';
@@ -79,7 +79,7 @@ export interface ChartCollectionPreset {
startRealtime: (
getSymbols: () => string[],
getTimeframe: () => Timeframes,
- update: (symbol: string, candle: Candle) => void,
+ update: (symbolId: string, candle: Candle) => void,
periodMs?: number,
) => () => void;
theme: ThemeKey; // 'mb' | 'mxt' | 'tr'
@@ -91,7 +91,7 @@ export interface ChartCollectionPreset {
}
export interface IMoexChart {
- snapshot: MoexChartSnapshot;
+ snapshot: MoexChartSnapshotInput;
chartCollectionPreset: ChartCollectionPreset;
container: HTMLElement;
@@ -130,12 +130,17 @@ export class MoexChart implements ISerializable<MoexChartSnapshot> {
setPricePrecision(config.chartCollectionPreset.ohlc.precision);
- const { chartSeriesType, symbol, timeframe, interval, dateFormat, timeFormat } = config.snapshot.charts[0];
+ const { chartSeriesType, symbolId, symbol, symbolName, timeframe, interval, dateFormat, timeFormat } =
+ config.snapshot.charts[0];
this.eventManager = new EventManager({
initialTimeframe: timeframe,
initialSeries: chartSeriesType,
- initialSymbol: symbol,
+ initialSymbolInfo: {
+ symbolId,
+ symbol,
+ symbolName,
+ },
initialTimeFormat: timeFormat,
initialDateFormat: dateFormat,
initialInterval: interval,
@@ -251,14 +256,14 @@ export class MoexChart implements ISerializable<MoexChartSnapshot> {
return this.chart.getCompareManager();
}
- public setSnapshot(snap: MoexChartSnapshot) {
+ public setSnapshot(snapshot: MoexChartSnapshotInput) {
const configConstructorLike: IMoexChart = {
- snapshot: snap,
+ snapshot,
chartCollectionPreset: this.chartCollectionPresetSettings,
container: this.rootContainer,
};
- this.destroy();
+ this.destroy();
this.setup(configConstructorLike);
}
@@ -272,10 +277,8 @@ export class MoexChart implements ISerializable<MoexChartSnapshot> {
return res;
}
- public setSymbol(symbol: string): void {
- if (!symbol) return;
-
- this.eventManager.setSymbol(symbol);
+ public setSymbol(symbolInfo: SymbolInfoInput): void {
+ this.eventManager.setSymbol(symbolInfo);
}
private renderAttachments(config: IMoexChart, toggleToolbar: () => boolean) {
diff --git a/src/core/Pane.tsx b/src/core/Pane.tsx
index 2b228aa..147d21f 100644
--- a/src/core/Pane.tsx
+++ b/src/core/Pane.tsx
@@ -402,7 +402,8 @@ export class Pane implements ISerializable<PaneSnapshot> {
const next = ensureDefined(SeriesFactory.create(nextSeries))({
lwcChart,
dataSource,
- mainSymbol$: this.eventManager.getSymbol(),
+ mainSymbolId$: this.eventManager.symbolId(),
+ mainSymbol$: this.eventManager.symbol(),
mainSerie$: this.mainSeries,
});
diff --git a/src/core/PriceAxisLabels/PriceAxisLabels.ts b/src/core/PriceAxisLabels/PriceAxisLabels.ts
index 6a7fdcb..c559ea0 100644
--- a/src/core/PriceAxisLabels/PriceAxisLabels.ts
+++ b/src/core/PriceAxisLabels/PriceAxisLabels.ts
@@ -153,9 +153,7 @@ export class PriceAxisLabels {
constructor({ mainSeries$, mainSymbol$, compareEntities$, indicatorEntities$ }: PriceAxisLabelsParams) {
this.subscriptions.add(
mainSymbol$.subscribe((symbol) => {
- const symbolParts = symbol.split(':');
-
- this.mainSymbol = symbolParts[symbolParts.length - 1] || symbol;
+ this.mainSymbol = symbol;
this.applyDisplayMode();
this.scheduleUpdate();
diff --git a/src/core/Series/BaseSeries.ts b/src/core/Series/BaseSeries.ts
index 74e19e0..335be26 100644
--- a/src/core/Series/BaseSeries.ts
+++ b/src/core/Series/BaseSeries.ts
@@ -68,6 +68,7 @@ export interface IBaseSeries<TSeries extends SeriesType> extends ISeriesApi<TSer
export interface BaseSeriesParams<TSeries extends SeriesType = SeriesType> {
lwcChart: IChartApi;
dataSource: DataSource;
+ mainSymbolId$: Observable<string>;
mainSymbol$: Observable<string>;
mainSerie$: BehaviorSubject<SeriesStrategies | null>;
customFormatter?: (params: IndicatorDataFormatter<TSeries>) => SeriesDataItemTypeMap<Time>[TSeries][];
@@ -102,6 +103,7 @@ export abstract class BaseSeries<TSeries extends SeriesType> implements IBaseSer
protected customFormatter?: (params: IndicatorDataFormatter<TSeries>) => SeriesDataItemTypeMap<Time>[TSeries][];
protected lwcChart: IChartApi;
+ protected mainSymbolId$: Observable<string>;
protected mainSymbol$: Observable<string>;
protected mainSerie$: BehaviorSubject<SeriesStrategies | null>;
protected paneIndex: number | null = null;
@@ -114,6 +116,7 @@ export abstract class BaseSeries<TSeries extends SeriesType> implements IBaseSer
constructor({
lwcChart,
+ mainSymbolId$,
mainSymbol$,
mainSerie$,
customFormatter,
@@ -132,6 +135,7 @@ export abstract class BaseSeries<TSeries extends SeriesType> implements IBaseSer
this.lwcChart = lwcChart;
this.customFormatter = customFormatter;
+ this.mainSymbolId$ = mainSymbolId$;
this.mainSymbol$ = mainSymbol$;
this.mainSerie$ = mainSerie$;
this.showSymbolLabel = showSymbolLabel;
@@ -383,26 +387,32 @@ export abstract class BaseSeries<TSeries extends SeriesType> implements IBaseSer
protected subscribeDataSource = (dataSource: DataSource): void => {
const minMove = getPricePrecisionStep();
+ this.lwcSeries.applyOptions({
+ priceFormat: {
+ type: 'custom',
+ minMove,
+ formatter: (price: number) => formatPrice(price) ?? String(price),
+ },
+ });
+
this.subscriptions.add(
this.mainSymbol$.pipe(distinctUntilChanged()).subscribe((symbol) => {
this.lwcSeries.applyOptions({
- // todo: на каждый апдейт dataSource сеттим options. Не оптимально
title: this.showSymbolLabel ? symbol : '',
- priceFormat: {
- type: 'custom',
- minMove,
- formatter: (price: number) => formatPrice(price) ?? String(price),
- },
});
+ }),
+ );
+ this.subscriptions.add(
+ this.mainSymbolId$.pipe(distinctUntilChanged()).subscribe((symbolId) => {
this.dataSub?.unsubscribe();
this.realtimeSub?.unsubscribe();
- this.dataSub = dataSource.subscribe(symbol, (next) => {
+ this.dataSub = dataSource.subscribe(symbolId, (next) => {
this.dataSourceSubscription(next);
});
- this.realtimeSub = dataSource.subscribeRealtime(symbol, (next: Candle) => {
+ this.realtimeSub = dataSource.subscribeRealtime(symbolId, (next: Candle) => {
this.dataSourceRealtimeSubscription(next);
});
}),
diff --git a/src/core/UndoRedo.ts b/src/core/UndoRedo.ts
index 581b616..25bab25 100644
--- a/src/core/UndoRedo.ts
+++ b/src/core/UndoRedo.ts
@@ -1,4 +1,4 @@
-import { ChartSeriesType, Intervals, TimeFormat, Timeframes } from '@src/types';
+import { ChartSeriesType, Intervals, SymbolInfo, TimeFormat, Timeframes } from '@src/types';
import { DateFormat } from '@src/utils';
export type UndoKey = keyof UndoConfig;
@@ -6,7 +6,7 @@ export type UndoKey = keyof UndoConfig;
interface UndoConfig {
timeframe: (value: Timeframes) => void;
seriesSelected: (value: ChartSeriesType) => void;
- symbol: (value: string) => void;
+ symbolInfo: (value: SymbolInfo) => void;
timeFormat: (value: TimeFormat) => void;
dateFormat: (value: DateFormat) => void;
interval: (value: Intervals | null) => void;
diff --git a/src/types/compare.ts b/src/types/compare.ts
index b8ef60f..03f3644 100644
--- a/src/types/compare.ts
+++ b/src/types/compare.ts
@@ -1,10 +1,11 @@
+import { SymbolInfo } from '@src/types/symbol';
+
export enum CompareMode {
Percentage = 'PCT',
NewScale = 'SCALE',
NewPane = 'PANE',
}
-export interface CompareItem {
- symbol: string;
+export interface CompareItem extends SymbolInfo {
mode: CompareMode;
}
diff --git a/src/types/index.ts b/src/types/index.ts
index 4bbf66a..df65df6 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -8,5 +8,6 @@ export * from './ohlc';
export * from './settings';
export * from './snapshot';
export * from './state';
+export * from './symbol';
export * from './timeframes';
export * from './timeScale';
diff --git a/src/types/indicator.ts b/src/types/indicator.ts
index ca45dca..7e4580f 100644
--- a/src/types/indicator.ts
+++ b/src/types/indicator.ts
@@ -15,14 +15,12 @@ import { indicatorLabelById, IndicatorsIds } from '@src/constants';
import { IndicatorDataFormatter } from '@src/core/Indicators';
import { ChartSeriesType } from '@src/types/chart';
import { SettingField } from '@src/types/settings';
+import { SymbolInfoInput } from '@src/types/symbol';
export type IndicatorData = HistogramData<Time> | LineData<Time> | WhitespaceData<Time>;
-
export type IndicatorType = 'SMA' | 'EMA' | 'RSI' | 'OHLC' | 'VOL';
-
export type MASource = 'open' | 'high' | 'low' | 'close';
-
-export type IndicatorLabel = (ReturnType<typeof indicatorLabelById>)[IndicatorsIds];
+export type IndicatorLabel = ReturnType<typeof indicatorLabelById>[IndicatorsIds];
export interface IndicatorStateConfig {
type: IndicatorType;
@@ -48,6 +46,7 @@ export interface IndicatorConfig {
series: IndicatorSerie[];
settings?: SettingField[];
newPane?: boolean;
+ symbolInfo?: SymbolInfoInput;
label?: string;
seriesLabels?: Record<string, string>;
paletteStartIndex?: number;
diff --git a/src/types/snapshot.ts b/src/types/snapshot.ts
index 157346b..5349f05 100644
--- a/src/types/snapshot.ts
+++ b/src/types/snapshot.ts
@@ -3,6 +3,7 @@ import { DrawingsManagerSnapshot } from '@core/DrawingsManager';
import { ChartSeriesType, DateFormat, IndicatorsIds, Intervals, Timeframes } from '@lib';
import { Direction } from '@src/types/chart';
import { IndicatorConfig } from '@src/types/indicator';
+import { SymbolInfo, SymbolInfoInput } from '@src/types/symbol';
import { TimeFormat } from '@src/types/timeScale';
import type { PriceScaleMode } from 'lightweight-charts';
@@ -13,10 +14,14 @@ export interface ISerializable<T extends object> {
getSnapshot: () => T;
}
-export interface InitialSnapshot {
+export interface InitialSnapshot extends SymbolInfoInput {
timeframe: Timeframes; // todo: move to snap
chartSeriesType: ChartSeriesType; // todo: move to snap
- symbol: string; // todo: move to snap
+}
+
+export interface MoexChartSnapshotInput {
+ // settings: ChartSettingsSnapshot;
+ charts: ChartSnapshotInput[];
}
export interface MoexChartSnapshot {
@@ -24,16 +29,18 @@ export interface MoexChartSnapshot {
charts: ChartSnapshot[];
}
-export interface ChartSnapshot {
+interface ChartSnapshotBase {
timeframe: Timeframes;
chartSeriesType: ChartSeriesType;
- symbol: string;
timeFormat?: TimeFormat;
dateFormat?: DateFormat;
interval?: Intervals | null;
panes: PaneSnapshot[];
}
+export interface ChartSnapshotInput extends ChartSnapshotBase, SymbolInfoInput {}
+export interface ChartSnapshot extends ChartSnapshotBase, SymbolInfo {}
+
export interface PriceScaleSnapshot {
side: PriceScaleSide;
mode: PriceScaleMode;
diff --git a/src/types/symbol.ts b/src/types/symbol.ts
new file mode 100644
index 0000000..a797934
--- /dev/null
+++ b/src/types/symbol.ts
@@ -0,0 +1,11 @@
+export interface SymbolInfo {
+ symbolId: string;
+ symbol: string;
+ symbolName: string;
+}
+
+export interface SymbolInfoInput {
+ symbolId: string;
+ symbol?: string;
+ symbolName?: string;
+}
diff --git a/src/utils/index.ts b/src/utils/index.ts
index e589bb1..2fc00de 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -12,6 +12,7 @@ export * from './normalizeSeriesData';
export * from './normalizeSymbol';
export * from './parseTimeframe';
export * from './precision';
+export * from './symbolInfo';
export * from './timeframeToSeconds';
export * from './typeGuards';
export * from './useObservable';
diff --git a/src/utils/symbolInfo.ts b/src/utils/symbolInfo.ts
new file mode 100644
index 0000000..b8c1906
--- /dev/null
+++ b/src/utils/symbolInfo.ts
@@ -0,0 +1,41 @@
+import type { SymbolInfo, SymbolInfoInput } from '@src/types';
+
+export function getSymbolFromSymbolId(symbolId: string): string {
+ const normalizedSymbolId = symbolId.trim();
+
+ if (!normalizedSymbolId) {
+ return '';
+ }
+
+ const separatorIndex = normalizedSymbolId.lastIndexOf(':');
+
+ if (separatorIndex === -1) {
+ return normalizedSymbolId;
+ }
+
+ const symbol = normalizedSymbolId.slice(separatorIndex + 1).trim();
+
+ return symbol || normalizedSymbolId;
+}
+
+export function normalizeSymbolInfo(input: SymbolInfoInput): SymbolInfo | undefined {
+ const symbolId = input.symbolId?.trim();
+
+ if (!symbolId) {
+ return undefined;
+ }
+
+ const symbolFromId = getSymbolFromSymbolId(symbolId);
+ const symbol = input.symbol?.trim() || symbolFromId;
+ const symbolName = input.symbolName?.trim() || symbol;
+
+ return {
+ symbolId,
+ symbol,
+ symbolName,
+ };
+}
+
+export function areSymbolInfosEqual(left: SymbolInfo, right: SymbolInfo): boolean {
+ return left.symbolId === right.symbolId && left.symbol === right.symbol && left.symbolName === right.symbolName;
+}
diff --git a/stories/MB/MB.stories.tsx b/stories/MB/MB.stories.tsx
index e5b193d..2380f72 100644
--- a/stories/MB/MB.stories.tsx
+++ b/stories/MB/MB.stories.tsx
@@ -1,11 +1,13 @@
import { useEffect, useRef, useState } from 'react';
-import { DateFormat, IMoexChart, Locale, MoexChart, Timeframes } from '@lib';
+import { CompareMode, DateFormat, IMoexChart, Locale, MoexChart, Timeframes } from '@lib';
import { Portal } from '@lib/components/Portal';
import { IndicatorsIds } from '@lib/constants';
import { CompareManager } from '@lib/core/CompareManager';
-import { CompareMode } from '@lib/types';
+import { SymbolInfoInput } from '@lib/types';
+
+import { getSymbolFromSymbolId } from '@lib/utils';
import { dataSourceProvider } from '../common';
@@ -105,7 +107,7 @@ const args: MBProps = {
{
timeframe: Timeframes['10s'],
chartSeriesType: 'Candlestick',
- symbol: 'APPL',
+ symbolId: 'APPL',
panes: [
{
// empty panes deletes automatically
@@ -183,6 +185,12 @@ export const MB: Story = {
},
};
+const COMPARE_ITEMS: SymbolInfoInput[] = [
+ { symbolId: 'TQBR:SBER', symbol: 'SBER', symbolName: 'Сбербанк' },
+ { symbolId: 'APAX' },
+ { symbolId: 'SOL' },
+];
+
const Modal = ({ onClose, compareManager }: { onClose: () => void; compareManager: CompareManager | null }) => {
const [isNewScaleDisabled, setIsNewScaleDisabled] = useState(false);
@@ -234,22 +242,22 @@ const Modal = ({ onClose, compareManager }: { onClose: () => void; compareManage
...containerStyles,
}}
>
- {['SBER', 'APAX', 'SOL'].map((symbol) => (
+ {COMPARE_ITEMS.map((symbolInfo) => (
<div
- key={symbol}
+ key={symbolInfo.symbolId}
style={{ display: 'flex', justifyContent: 'space-between', gap: 16 }}
>
- <span>{symbol}</span>
+ <span>{symbolInfo.symbolName ?? symbolInfo.symbol ?? getSymbolFromSymbolId(symbolInfo.symbolId)}</span>
<div style={{ display: 'flex', gap: 8 }}>
<button
- onClick={() => compareManager?.setSymbolMode('Line', symbol, CompareMode.Percentage)}
+ onClick={() => compareManager?.setSymbolMode('Line', symbolInfo, CompareMode.Percentage)}
style={buttonStyles}
type="button"
>
%
</button>
<button
- onClick={() => compareManager?.setSymbolMode('Line', symbol, CompareMode.NewScale)}
+ onClick={() => compareManager?.setSymbolMode('Line', symbolInfo, CompareMode.NewScale)}
style={{
backgroundColor: isNewScaleDisabled ? 'darkgray' : 'lightgray',
padding: '2px 8px',
@@ -261,7 +269,7 @@ const Modal = ({ onClose, compareManager }: { onClose: () => void; compareManage
Новая шкала
</button>
<button
- onClick={() => compareManager?.setSymbolMode('Line', symbol, CompareMode.NewPane)}
+ onClick={() => compareManager?.setSymbolMode('Line', symbolInfo, CompareMode.NewPane)}
style={buttonStyles}
type="button"
>
diff --git a/stories/MXT/MXT.stories.tsx b/stories/MXT/MXT.stories.tsx
index 5d8b61f..874c426 100644
--- a/stories/MXT/MXT.stories.tsx
+++ b/stories/MXT/MXT.stories.tsx
@@ -85,7 +85,7 @@ const args: MXTProps = {
{
timeframe: Timeframes['1m'],
chartSeriesType: 'Candlestick',
- symbol: 'APPL',
+ symbolId: 'APPL',
panes: [
{
// empty panes deletes automatically
diff --git a/stories/TradeRadar/TradeRadar.stories.tsx b/stories/TradeRadar/TradeRadar.stories.tsx
index 99f3b84..3d812f6 100644
--- a/stories/TradeRadar/TradeRadar.stories.tsx
+++ b/stories/TradeRadar/TradeRadar.stories.tsx
@@ -5,7 +5,8 @@ import { createPortal } from 'react-dom';
import { CompareManager } from '@core/CompareManager';
import { DateFormat, IMoexChart, Locale, MoexChart, Timeframes } from '@lib';
import { IndicatorsIds } from '@lib/constants';
-import { CompareMode } from '@lib/types';
+import { CompareMode, SymbolInfoInput } from '@lib/types';
+import { getSymbolFromSymbolId } from '@lib/utils';
// import { argTypes } from '../argTypes';
@@ -131,7 +132,7 @@ const args: TRProps = {
{
timeframe: Timeframes['10s'],
chartSeriesType: 'Candlestick',
- symbol: 'appl',
+ symbolId: 'appl',
panes: [
{
// empty panes deletes automatically
@@ -219,6 +220,12 @@ export const TradeRadar: Story = {
},
};
+const COMPARE_ITEMS: SymbolInfoInput[] = [
+ { symbolId: 'TQBR:SBER', symbol: 'SBER', symbolName: 'Sberbank' },
+ { symbolId: 'APAX' },
+ { symbolId: 'SOL' },
+];
+
const Modal = ({ onClose, compareManager }: { onClose: () => void; compareManager: CompareManager | null }) => {
const [isNewScaleDisabled, setIsNewScaleDisabled] = useState(false);
@@ -262,22 +269,22 @@ const Modal = ({ onClose, compareManager }: { onClose: () => void; compareManage
backgroundColor: 'white',
}}
>
- {['SBER', 'APAX', 'SOL'].map((symbol) => (
+ {COMPARE_ITEMS.map((symbolInfo) => (
<div
- key={symbol}
+ key={symbolInfo.symbolId}
style={{ display: 'flex', justifyContent: 'space-between', gap: 16 }}
>
- <span>{symbol}</span>
+ <span>{symbolInfo.symbolName ?? symbolInfo.symbol ?? getSymbolFromSymbolId(symbolInfo.symbolId)}</span>
<div style={{ display: 'flex', gap: 8 }}>
<button
- onClick={() => compareManager?.setSymbolMode('Line', symbol, CompareMode.Percentage)}
+ onClick={() => compareManager?.setSymbolMode('Line', symbolInfo, CompareMode.Percentage)}
style={{ backgroundColor: 'lightgray', padding: '2px 8px' }}
type="button"
>
%
</button>
<button
- onClick={() => compareManager?.setSymbolMode('Line', symbol, CompareMode.NewScale)}
+ onClick={() => compareManager?.setSymbolMode('Line', symbolInfo, CompareMode.NewScale)}
style={{
backgroundColor: isNewScaleDisabled ? 'darkgray' : 'lightgray',
padding: '2px 8px',
@@ -289,7 +296,7 @@ const Modal = ({ onClose, compareManager }: { onClose: () => void; compareManage
Новая шкала
</button>
<button
- onClick={() => compareManager?.setSymbolMode('Line', symbol, CompareMode.NewPane)}
+ onClick={() => compareManager?.setSymbolMode('Line', symbolInfo, CompareMode.NewPane)}
style={{ backgroundColor: 'lightgray', padding: '2px 8px' }}
type="button"
>