Загрузка данных


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"
               >