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


diff --git a/package-lock.json b/package-lock.json
index ad067c16b..ae9d41638 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -39,7 +39,7 @@
         "markdown-it-link-attributes": "^4.0.1",
         "markdown-it-underline": "^1.0.1",
         "meow": "^8.1.2",
-        "moex-chart": "^0.1.12",
+        "moex-chart": "^0.1.13-dev.1",
         "rc-virtual-list": "^3.14.5",
         "react": "^18.2.0",
         "react-chartjs-2": "^5.2.0",
@@ -28809,9 +28809,9 @@
       }
     },
     "node_modules/moex-chart": {
-      "version": "0.1.12",
-      "resolved": "https://nexus-dev.tech.moex.com/repository/trade-radar-npm-private-group/moex-chart/-/moex-chart-0.1.12.tgz",
-      "integrity": "sha1-0uaftIpE4fqxiyRu7ZNEseyXAcM=",
+      "version": "0.1.13-dev.1",
+      "resolved": "https://nexus-dev.tech.moex.com/repository/trade-radar-npm-private-group/moex-chart/-/moex-chart-0.1.13-dev.1.tgz",
+      "integrity": "sha1-9NYDPHV+t3agqhu2FTkHFRthMfM=",
       "dependencies": {
         "@dnd-kit/core": "^6.1.0",
         "@dnd-kit/modifiers": "^7.0.0",
@@ -60585,9 +60585,9 @@
       "dev": true
     },
     "moex-chart": {
-      "version": "0.1.12",
-      "resolved": "https://nexus-dev.tech.moex.com/repository/trade-radar-npm-private-group/moex-chart/-/moex-chart-0.1.12.tgz",
-      "integrity": "sha1-0uaftIpE4fqxiyRu7ZNEseyXAcM=",
+      "version": "0.1.13-dev.1",
+      "resolved": "https://nexus-dev.tech.moex.com/repository/trade-radar-npm-private-group/moex-chart/-/moex-chart-0.1.13-dev.1.tgz",
+      "integrity": "sha1-9NYDPHV+t3agqhu2FTkHFRthMfM=",
       "requires": {
         "@dnd-kit/core": "^6.1.0",
         "@dnd-kit/modifiers": "^7.0.0",
diff --git a/package.json b/package.json
index df913f9be..73d90934c 100644
--- a/package.json
+++ b/package.json
@@ -146,7 +146,7 @@
     "markdown-it-link-attributes": "^4.0.1",
     "markdown-it-underline": "^1.0.1",
     "meow": "^8.1.2",
-    "moex-chart": "^0.1.12",
+    "moex-chart": "^0.1.13-dev.1",
     "rc-virtual-list": "^3.14.5",
     "react": "^18.2.0",
     "react-chartjs-2": "^5.2.0",
diff --git a/src/components/InstrumentSearch/components/Actions/MoexChartActions.tsx b/src/components/InstrumentSearch/components/Actions/MoexChartActions.tsx
index 4fad0da7a..63170eaae 100644
--- a/src/components/InstrumentSearch/components/Actions/MoexChartActions.tsx
+++ b/src/components/InstrumentSearch/components/Actions/MoexChartActions.tsx
@@ -6,12 +6,12 @@ import { Button } from '@uikit/Button';
 import styles from './styles.module.scss';
 
 type TMoexChartActionsProps = {
-  selectedInstrumentRows: [] | Contract[];
+  selectedInstrumentRows: Contract[];
   isNewScaleDisabled?: boolean;
   customActionsFooterHandlers: {
-    handlePercent: (symbol: string) => void;
-    handleNewScale: (symbol: string) => void;
-    handleNewPanel: (symbol: string) => void;
+    handlePercent: (instrument: Contract) => void;
+    handleNewScale: (instrument: Contract) => void;
+    handleNewPanel: (instrument: Contract) => void;
   };
 };
 
@@ -22,16 +22,16 @@ export const MoexChartActions = ({
 }: TMoexChartActionsProps) => {
   const { handlePercent, handleNewScale, handleNewPanel } = customActionsFooterHandlers;
 
-  const selectedSymbol = selectedInstrumentRows[0]?.issKey;
-  const isButtonDisabled = !selectedSymbol;
+  const selectedInstrument = selectedInstrumentRows[0];
+  const isButtonDisabled = !selectedInstrument?.issKey;
 
   return (
     <div className={styles.actionWrapper}>
       <Button
         text="%"
         onClick={() => {
-          if (selectedSymbol) {
-            handlePercent(selectedSymbol);
+          if (selectedInstrument?.issKey) {
+            handlePercent(selectedInstrument);
           }
         }}
         disabled={isButtonDisabled}
@@ -40,8 +40,8 @@ export const MoexChartActions = ({
       <Button
         text="Новая шкала"
         onClick={() => {
-          if (selectedSymbol) {
-            handleNewScale(selectedSymbol);
+          if (selectedInstrument?.issKey) {
+            handleNewScale(selectedInstrument);
           }
         }}
         disabled={isButtonDisabled || isNewScaleDisabled}
@@ -50,8 +50,8 @@ export const MoexChartActions = ({
       <Button
         text="Новая панель"
         onClick={() => {
-          if (selectedSymbol) {
-            handleNewPanel(selectedSymbol);
+          if (selectedInstrument?.issKey) {
+            handleNewPanel(selectedInstrument);
           }
         }}
         disabled={isButtonDisabled}
diff --git a/src/components/InstrumentSearch/types/hooks.ts b/src/components/InstrumentSearch/types/hooks.ts
index a623c5b80..d82fa0da3 100644
--- a/src/components/InstrumentSearch/types/hooks.ts
+++ b/src/components/InstrumentSearch/types/hooks.ts
@@ -74,9 +74,9 @@ export type InstrumentSearchType = {
    * TODO: убрать, когда разработаем свою модалку для moex_chart
    */
   customActionsFooterHandlers?: {
-    handlePercent: (symbol: string) => void;
-    handleNewScale: (symbol: string) => void;
-    handleNewPanel: (symbol: string) => void;
+    handlePercent: (instrument: Contract) => void;
+    handleNewScale: (instrument: Contract) => void;
+    handleNewPanel: (instrument: Contract) => void;
   };
   isNewScaleDisabled?: boolean;
 };
diff --git a/src/widgets/Chart/__tests__/CompareModal.test.tsx b/src/widgets/Chart/__tests__/CompareModal.test.tsx
new file mode 100644
index 000000000..183efc828
--- /dev/null
+++ b/src/widgets/Chart/__tests__/CompareModal.test.tsx
@@ -0,0 +1,256 @@
+import { act, render } from '@testing-library/react';
+
+import { CompareMode } from 'moex-chart';
+import React from 'react';
+
+import { InstrumentSearch } from '@components/InstrumentSearch';
+
+import { CompareModal } from '../components/MoexChart/components/CompareModal';
+
+import type { Contract } from '@modules/contracts';
+import type { __CompareManager__ } from 'moex-chart';
+import type { MutableRefObject } from 'react';
+
+jest.mock('moex-chart', () => ({
+  __esModule: true,
+  CompareMode: {
+    Percentage: 'PCT',
+    NewScale: 'SCALE',
+    NewPane: 'PANE',
+  },
+}));
+
+jest.mock('@components/InstrumentSearch', () => ({
+  InstrumentSearch: jest.fn(() => null),
+}));
+
+interface CompareActions {
+  handlePercent: (instrument: Contract) => void;
+  handleNewScale: (instrument: Contract) => void;
+  handleNewPanel: (instrument: Contract) => void;
+}
+
+interface InstrumentSearchMockProps {
+  widgetId: number;
+  variant: string;
+  isOpen: boolean;
+  setOpen: (isOpen: boolean) => void;
+  isNewScaleDisabled: boolean;
+  customActionsFooterHandlers: CompareActions;
+}
+
+describe('CompareModal', () => {
+  const mockInstrumentSearch = InstrumentSearch as jest.Mock;
+
+  const mockSetOpen = jest.fn();
+  const mockSetSymbolMode = jest.fn();
+  const mockIsNewScaleDisabled = jest.fn();
+  const mockIsNewScaleDisabledObservable = jest.fn();
+  const mockSubscribe = jest.fn();
+  const mockUnsubscribe = jest.fn();
+
+  let newScaleDisabledListener: ((disabled: boolean) => void) | null;
+  let compareManager: __CompareManager__;
+  let compareManagerRef: MutableRefObject<__CompareManager__ | null>;
+
+  const getInstrumentSearchProps = (): InstrumentSearchMockProps => {
+    const lastCall = mockInstrumentSearch.mock.calls[mockInstrumentSearch.mock.calls.length - 1];
+
+    return lastCall?.[0] as InstrumentSearchMockProps;
+  };
+
+  const renderComponent = (isOpen = true) =>
+    render(
+      <CompareModal
+        onClose={jest.fn()}
+        widgetId={42}
+        compareManager={compareManagerRef}
+        isOpen={isOpen}
+        setOpen={mockSetOpen}
+      />,
+    );
+
+  beforeEach(() => {
+    jest.clearAllMocks();
+
+    newScaleDisabledListener = null;
+
+    mockIsNewScaleDisabled.mockReturnValue(false);
+    mockSetSymbolMode.mockResolvedValue(undefined);
+
+    mockSubscribe.mockImplementation((listener: (disabled: boolean) => void) => {
+      newScaleDisabledListener = listener;
+
+      return {
+        unsubscribe: mockUnsubscribe,
+      };
+    });
+
+    mockIsNewScaleDisabledObservable.mockReturnValue({
+      subscribe: mockSubscribe,
+    });
+
+    compareManager = {
+      setSymbolMode: mockSetSymbolMode,
+      isNewScaleDisabled: mockIsNewScaleDisabled,
+      isNewScaleDisabledObservable: mockIsNewScaleDisabledObservable,
+    } as unknown as __CompareManager__;
+
+    compareManagerRef = {
+      current: compareManager,
+    };
+  });
+
+  it('should pass modal properties and current scale state to instrument search', () => {
+    // Arrange
+    mockIsNewScaleDisabled.mockReturnValue(true);
+
+    // Act
+    renderComponent();
+
+    const instrumentSearchProps = getInstrumentSearchProps();
+
+    // Assert
+    expect(instrumentSearchProps.widgetId).toBe(42);
+    expect(instrumentSearchProps.variant).toBe('single');
+    expect(instrumentSearchProps.isOpen).toBe(true);
+    expect(instrumentSearchProps.setOpen).toBe(mockSetOpen);
+    expect(instrumentSearchProps.isNewScaleDisabled).toBe(true);
+    expect(instrumentSearchProps.customActionsFooterHandlers).toEqual({
+      handlePercent: expect.any(Function),
+      handleNewScale: expect.any(Function),
+      handleNewPanel: expect.any(Function),
+    });
+  });
+
+  it('should subscribe to new scale disabled state', () => {
+    // Arrange & Act
+    renderComponent();
+
+    // Assert
+    expect(mockIsNewScaleDisabled).toHaveBeenCalledTimes(1);
+    expect(mockIsNewScaleDisabledObservable).toHaveBeenCalledTimes(1);
+    expect(mockSubscribe).toHaveBeenCalledTimes(1);
+  });
+
+  it('should update new scale disabled state from manager observable', () => {
+    // Arrange
+    renderComponent();
+
+    // Act
+    act(() => {
+      newScaleDisabledListener?.(true);
+    });
+
+    // Assert
+    expect(getInstrumentSearchProps().isNewScaleDisabled).toBe(true);
+  });
+
+  it('should unsubscribe from manager observable on unmount', () => {
+    // Arrange
+    const { unmount } = renderComponent();
+
+    // Act
+    unmount();
+
+    // Assert
+    expect(mockUnsubscribe).toHaveBeenCalledTimes(1);
+  });
+
+  it('should not subscribe when modal is closed', () => {
+    // Arrange & Act
+    renderComponent(false);
+
+    // Assert
+    expect(mockIsNewScaleDisabled).not.toHaveBeenCalled();
+    expect(mockIsNewScaleDisabledObservable).not.toHaveBeenCalled();
+    expect(getInstrumentSearchProps().isNewScaleDisabled).toBe(false);
+  });
+
+  it('should not subscribe when compare manager is unavailable', () => {
+    // Arrange
+    compareManagerRef.current = null;
+
+    // Act
+    renderComponent();
+
+    // Assert
+    expect(mockIsNewScaleDisabled).not.toHaveBeenCalled();
+    expect(mockIsNewScaleDisabledObservable).not.toHaveBeenCalled();
+    expect(getInstrumentSearchProps().isNewScaleDisabled).toBe(false);
+  });
+
+  it.each([
+    ['handlePercent', CompareMode.Percentage],
+    ['handleNewScale', CompareMode.NewScale],
+    ['handleNewPanel', CompareMode.NewPane],
+  ] as const)('should add compare instrument using %s action', (handlerName, mode) => {
+    // Arrange
+    renderComponent();
+
+    const instrument = {
+      issKey: 'MXSE:TQBR:SBER',
+      displayName: 'Сбербанк',
+      symbol: 'SBER',
+    } as Contract;
+
+    const handlers = getInstrumentSearchProps().customActionsFooterHandlers;
+
+    // Act
+    handlers[handlerName](instrument);
+
+    // Assert
+    expect(mockSetSymbolMode).toHaveBeenCalledTimes(1);
+    expect(mockSetSymbolMode).toHaveBeenCalledWith(
+      'Line',
+      {
+        symbol: 'MXSE:TQBR:SBER',
+        symbolName: 'Сбербанк',
+        symbolTicker: 'SBER',
+      },
+      mode,
+    );
+  });
+
+  it('should use request symbol when display name and ticker are empty', () => {
+    // Arrange
+    renderComponent();
+
+    const instrument = {
+      issKey: 'MXSE:TQBR:SBER',
+      displayName: '',
+      symbol: '',
+    } as Contract;
+
+    // Act
+    getInstrumentSearchProps().customActionsFooterHandlers.handlePercent(instrument);
+
+    // Assert
+    expect(mockSetSymbolMode).toHaveBeenCalledWith(
+      'Line',
+      {
+        symbol: 'MXSE:TQBR:SBER',
+        symbolName: 'MXSE:TQBR:SBER',
+        symbolTicker: 'MXSE:TQBR:SBER',
+      },
+      CompareMode.Percentage,
+    );
+  });
+
+  it('should not add compare instrument without issKey', () => {
+    // Arrange
+    renderComponent();
+
+    const instrument = {
+      issKey: '',
+      displayName: 'Сбербанк',
+      symbol: 'SBER',
+    } as Contract;
+
+    // Act
+    getInstrumentSearchProps().customActionsFooterHandlers.handlePercent(instrument);
+
+    // Assert
+    expect(mockSetSymbolMode).not.toHaveBeenCalled();
+  });
+});
diff --git a/src/widgets/Chart/__tests__/MoexChart.test.tsx b/src/widgets/Chart/__tests__/MoexChart.test.tsx
new file mode 100644
index 000000000..839d59c72
--- /dev/null
+++ b/src/widgets/Chart/__tests__/MoexChart.test.tsx
@@ -0,0 +1,230 @@
+import { act, render } from '@testing-library/react';
+
+import React from 'react';
+
+import { CompareModal } from '../components/MoexChart/components/CompareModal';
+import { SymbolSearchModal } from '../components/MoexChart/components/SymbolSearchModal';
+import { useMoexChart } from '../components/MoexChart/hooks';
+import MoexChartComponent from '../components/MoexChart/MoexChart';
+
+import type { Contract } from '@modules/contracts';
+import type { __CompareManager__ } from 'moex-chart';
+import type { MutableRefObject } from 'react';
+
+jest.mock('../components/MoexChart/hooks', () => ({
+  useMoexChart: jest.fn(),
+}));
+
+jest.mock('../components/MoexChart/components/CompareModal', () => ({
+  CompareModal: jest.fn(() => null),
+}));
+
+jest.mock('../components/MoexChart/components/SymbolSearchModal', () => ({
+  SymbolSearchModal: jest.fn(() => null),
+}));
+
+interface CompareModalMockProps {
+  widgetId: number;
+  isOpen: boolean;
+  setOpen: (isOpen: boolean) => void;
+  onClose: () => void;
+  compareManager: MutableRefObject<__CompareManager__ | null>;
+}
+
+interface SymbolSearchModalMockProps {
+  widgetId: number;
+  isOpen: boolean;
+  setOpen: (isOpen: boolean) => void;
+  onSymbolChange: (instrument: Contract) => void;
+}
+
+describe('MoexChart', () => {
+  const mockUseMoexChart = useMoexChart as jest.Mock;
+  const mockCompareModal = CompareModal as jest.Mock;
+  const mockSymbolSearchModal = SymbolSearchModal as jest.Mock;
+
+  const mockSetIsCompareOpen = jest.fn();
+  const mockSetIsSymbolSearchOpen = jest.fn();
+  const mockAddInstrumentFromModal = jest.fn();
+
+  let containerRef: MutableRefObject<HTMLDivElement | null>;
+  let compareManagerRef: MutableRefObject<__CompareManager__ | null>;
+
+  const renderComponent = () =>
+    render(
+      <MoexChartComponent
+        symbol="MXSE:TQBR:SBER"
+        symbolName="Сбербанк"
+        symbolTicker="SBER"
+        widgetId={42}
+        addInstrumentFromModal={mockAddInstrumentFromModal}
+      />,
+    );
+
+  beforeEach(() => {
+    jest.clearAllMocks();
+
+    containerRef = {
+      current: null,
+    };
+
+    compareManagerRef = {
+      current: null,
+    };
+
+    mockUseMoexChart.mockReturnValue({
+      containerRef,
+      isCompareOpen: false,
+      isSymbolSearchOpen: false,
+      compareManagerRef,
+      setIsCompareOpen: mockSetIsCompareOpen,
+      setIsSymbolSearchOpen: mockSetIsSymbolSearchOpen,
+      saveSnapshot: jest.fn(),
+      applySnapshot: jest.fn(),
+      hasSavedSnapshot: false,
+    });
+  });
+
+  it('should initialize chart hook with symbol, name and ticker', () => {
+    // Arrange & Act
+    renderComponent();
+
+    // Assert
+    expect(mockUseMoexChart).toHaveBeenCalledTimes(1);
+    expect(mockUseMoexChart).toHaveBeenCalledWith({
+      symbol: 'MXSE:TQBR:SBER',
+      symbolName: 'Сбербанк',
+      symbolTicker: 'SBER',
+      indicativeData: undefined,
+    });
+  });
+
+  it('should attach chart container ref', () => {
+    // Arrange & Act
+    renderComponent();
+
+    // Assert
+    expect(containerRef.current).toBeInstanceOf(HTMLDivElement);
+  });
+
+  it('should not render modals when they are closed', () => {
+    // Arrange & Act
+    renderComponent();
+
+    // Assert
+    expect(mockCompareModal).not.toHaveBeenCalled();
+    expect(mockSymbolSearchModal).not.toHaveBeenCalled();
+  });
+
+  it('should render compare modal with chart manager', () => {
+    // Arrange
+    mockUseMoexChart.mockReturnValue({
+      containerRef,
+      isCompareOpen: true,
+      isSymbolSearchOpen: false,
+      compareManagerRef,
+      setIsCompareOpen: mockSetIsCompareOpen,
+      setIsSymbolSearchOpen: mockSetIsSymbolSearchOpen,
+      saveSnapshot: jest.fn(),
+      applySnapshot: jest.fn(),
+      hasSavedSnapshot: false,
+    });
+
+    // Act
+    renderComponent();
+
+    const compareModalProps = mockCompareModal.mock.calls[0]?.[0] as CompareModalMockProps;
+
+    // Assert
+    expect(compareModalProps.widgetId).toBe(42);
+    expect(compareModalProps.isOpen).toBe(true);
+    expect(compareModalProps.setOpen).toBe(mockSetIsCompareOpen);
+    expect(compareModalProps.compareManager).toBe(compareManagerRef);
+  });
+
+  it('should close compare modal through onClose callback', () => {
+    // Arrange
+    mockUseMoexChart.mockReturnValue({
+      containerRef,
+      isCompareOpen: true,
+      isSymbolSearchOpen: false,
+      compareManagerRef,
+      setIsCompareOpen: mockSetIsCompareOpen,
+      setIsSymbolSearchOpen: mockSetIsSymbolSearchOpen,
+      saveSnapshot: jest.fn(),
+      applySnapshot: jest.fn(),
+      hasSavedSnapshot: false,
+    });
+
+    renderComponent();
+
+    const compareModalProps = mockCompareModal.mock.calls[0]?.[0] as CompareModalMockProps;
+
+    // Act
+    act(() => {
+      compareModalProps.onClose();
+    });
+
+    // Assert
+    expect(mockSetIsCompareOpen).toHaveBeenCalledWith(false);
+  });
+
+  it('should render symbol search modal', () => {
+    // Arrange
+    mockUseMoexChart.mockReturnValue({
+      containerRef,
+      isCompareOpen: false,
+      isSymbolSearchOpen: true,
+      compareManagerRef,
+      setIsCompareOpen: mockSetIsCompareOpen,
+      setIsSymbolSearchOpen: mockSetIsSymbolSearchOpen,
+      saveSnapshot: jest.fn(),
+      applySnapshot: jest.fn(),
+      hasSavedSnapshot: false,
+    });
+
+    // Act
+    renderComponent();
+
+    const symbolSearchModalProps = mockSymbolSearchModal.mock.calls[0]?.[0] as SymbolSearchModalMockProps;
+
+    // Assert
+    expect(symbolSearchModalProps.widgetId).toBe(42);
+    expect(symbolSearchModalProps.isOpen).toBe(true);
+    expect(symbolSearchModalProps.setOpen).toBe(mockSetIsSymbolSearchOpen);
+  });
+
+  it('should pass selected instrument from symbol search modal', () => {
+    // Arrange
+    mockUseMoexChart.mockReturnValue({
+      containerRef,
+      isCompareOpen: false,
+      isSymbolSearchOpen: true,
+      compareManagerRef,
+      setIsCompareOpen: mockSetIsCompareOpen,
+      setIsSymbolSearchOpen: mockSetIsSymbolSearchOpen,
+      saveSnapshot: jest.fn(),
+      applySnapshot: jest.fn(),
+      hasSavedSnapshot: false,
+    });
+
+    renderComponent();
+
+    const symbolSearchModalProps = mockSymbolSearchModal.mock.calls[0]?.[0] as SymbolSearchModalMockProps;
+
+    const instrument = {
+      issKey: 'MXSE:TQBR:GAZP',
+      displayName: 'Газпром',
+      symbol: 'GAZP',
+    } as Contract;
+
+    // Act
+    act(() => {
+      symbolSearchModalProps.onSymbolChange(instrument);
+    });
+
+    // Assert
+    expect(mockAddInstrumentFromModal).toHaveBeenCalledTimes(1);
+    expect(mockAddInstrumentFromModal).toHaveBeenCalledWith([instrument]);
+  });
+});
diff --git a/src/widgets/Chart/__tests__/SymbolSearchModal.test.tsx b/src/widgets/Chart/__tests__/SymbolSearchModal.test.tsx
index b61b7fc18..543d392cb 100644
--- a/src/widgets/Chart/__tests__/SymbolSearchModal.test.tsx
+++ b/src/widgets/Chart/__tests__/SymbolSearchModal.test.tsx
@@ -1,128 +1,275 @@
-import { render } from '@testing-library/react';
-import React from 'react';
+import { renderHook } from '@testing-library/react';
 
-import { InstrumentSearch } from '@components/InstrumentSearch';
+import { useAppSelect } from '@hooks/useAppSelector';
+import { useContracts } from '@modules/contracts';
+import { filterByUniqIssKey } from '@utils/filterByUniqIssKey';
+import { DEFAULT_SYMBOL } from '@widgets/Chart/const';
 
-import { SymbolSearchModal } from '../components/MoexChart/components/SymbolSearchModal';
+import { useChartPublicContext } from '../hooks/useChartPublicContext';
 
 import type { Contract } from '@modules/contracts';
 
-jest.mock('@components/InstrumentSearch', () => ({
-  InstrumentSearch: jest.fn(() => null),
+// Mock the dependencies
+jest.mock('@hooks/useAppSelector');
+jest.mock('@modules/contracts');
+jest.mock('@utils/filterByUniqIssKey');
+
+jest.mock('@api/index', () => ({
+  updateMenuLocked: jest.fn(),
+  widgetsController: {
+    delete: jest.fn(),
+  },
+  widgetPropertiesController: {
+    update: jest.fn(),
+  },
+  workspaceController: {
+    update: jest.fn(),
+  },
 }));
 
-interface InstrumentSearchMockProps {
-  widgetId: number;
-  variant: string;
-  isOpen: boolean;
-  setOpen: (isOpen: boolean) => void;
-  addInstruments: (instruments: Contract[]) => void;
-}
-
-describe('SymbolSearchModal', () => {
-  const mockInstrumentSearch = InstrumentSearch as jest.Mock;
-
-  const mockSetOpen = jest.fn();
-  const mockOnSymbolChange = jest.fn();
-
-  const renderComponent = (): InstrumentSearchMockProps => {
-    render(
-      <SymbolSearchModal
-        widgetId={42}
-        isOpen
-        setOpen={mockSetOpen}
-        onSymbolChange={mockOnSymbolChange}
-      />,
-    );
+jest.mock('@api/controllers/workspace', () => ({
+  workspaceController: {
+    update: jest.fn(),
+  },
+}));
+
+describe('useChartPublicContext', () => {
+  const mockUseAppSelect = useAppSelect as jest.Mock;
+  const mockUseContracts = useContracts as jest.Mock;
+  const mockFilterByUniqIssKey = filterByUniqIssKey as jest.Mock;
+
+  const mockContracts = [
+    {
+      issKey: DEFAULT_SYMBOL,
+      displayName: 'Инструмент по умолчанию',
+      symbol: 'DEFAULT',
+      // ... other contract properties
+    },
+    {
+      issKey: 'MOEX:TEST1',
+      displayName: 'Test Instrument 1',
+      symbol: 'TEST1',
+      // ... other contract properties
+    },
+    {
+      issKey: 'MOEX:TEST2',
+      displayName: 'Test Instrument 2',
+      symbol: 'TEST2',
+      // ... other contract properties
+    },
+    {
+      issKey: 'MOEX:TEST3',
+      displayName: 'Test Instrument 3',
+      symbol: 'TEST3',
+      // ... other contract properties
+    },
+  ] as Contract[];
 
-    return mockInstrumentSearch.mock.calls[0]?.[0] as InstrumentSearchMockProps;
+  const mockWidget = {
+    id: 1,
+    name: 'Test Widget',
+    type: 'graphic',
+    master: 123,
+    externalProperties: [
+      {
+        key: 'instrument',
+        value: 'MOEX:TEST1',
+      },
+    ],
+    // ... other widget properties
+  };
+
+  const mockPublicContext = {
+    instrument: 'MOEX:TEST1',
   };
 
   beforeEach(() => {
     jest.clearAllMocks();
+
+    // Mock the useAppSelect to return our test data
+    mockUseAppSelect.mockImplementation((selector) => {
+      if (selector.toString().includes('publicContext')) {
+        return mockPublicContext;
+      }
+
+      if (selector.toString().includes('widgets')) {
+        return mockWidget;
+      }
+
+      return {};
+    });
+
+    // Mock useContracts to return our test contracts
+    mockUseContracts.mockReturnValue({
+      contracts: mockContracts,
+    });
+
+    // Mock filterByUniqIssKey to return the same contracts
+    mockFilterByUniqIssKey.mockImplementation((contracts: Contract[]) => contracts);
   });
 
-  it('should pass modal properties to instrument search', () => {
-    // Arrange & Act
-    const instrumentSearchProps = renderComponent();
+  it('should return the correct issKey when a matching instrument is found', () => {
+    // Arrange
+    const mockSetCurrInstrument = jest.fn();
+    const mockGetMasterInstrumentFromPublicContext = jest.fn().mockReturnValue('MOEX:TEST1');
+
+    // Act
+    const { result } = renderHook(() =>
+      useChartPublicContext({
+        widgetId: 1,
+        setCurrInstrument: mockSetCurrInstrument,
+        getMasterInstrumentFromPublicContext: mockGetMasterInstrumentFromPublicContext,
+      }),
+    );
 
     // Assert
-    expect(instrumentSearchProps.widgetId).toBe(42);
-    expect(instrumentSearchProps.variant).toBe('single');
-    expect(instrumentSearchProps.isOpen).toBe(true);
-    expect(instrumentSearchProps.setOpen).toBe(mockSetOpen);
-    expect(instrumentSearchProps.addInstruments).toEqual(expect.any(Function));
+    expect(result.current.issKey).toBe('MOEX:TEST1');
   });
 
-  it('should change symbol after instrument selection', () => {
+  it('should pass instrument id, display name and ticker when a matching instrument is found', () => {
     // Arrange
-    const instrumentSearchProps = renderComponent();
+    const mockSetCurrInstrument = jest.fn();
+    const mockGetMasterInstrumentFromPublicContext = jest.fn().mockReturnValue('MOEX:TEST2');
+
+    // Act
+    renderHook(() =>
+      useChartPublicContext({
+        widgetId: 1,
+        setCurrInstrument: mockSetCurrInstrument,
+        getMasterInstrumentFromPublicContext: mockGetMasterInstrumentFromPublicContext,
+      }),
+    );
 
-    const instrument = {
-      issKey: 'MOEX:SBER',
-    } as Contract;
+    // Assert
+    expect(mockSetCurrInstrument).toHaveBeenCalledTimes(1);
+    expect(mockSetCurrInstrument).toHaveBeenCalledWith('MOEX:TEST2', 'Test Instrument 2', 'TEST2');
+  });
+
+  it('should return undefined when no matching instrument is found', () => {
+    // Arrange
+    const mockSetCurrInstrument = jest.fn();
+    const mockGetMasterInstrumentFromPublicContext = jest.fn().mockReturnValue('MOEX:NONEXISTENT');
 
     // Act
-    instrumentSearchProps.addInstruments([instrument]);
+    const { result } = renderHook(() =>
+      useChartPublicContext({
+        widgetId: 1,
+        setCurrInstrument: mockSetCurrInstrument,
+        getMasterInstrumentFromPublicContext: mockGetMasterInstrumentFromPublicContext,
+      }),
+    );
 
     // Assert
-    expect(mockOnSymbolChange).toHaveBeenCalledTimes(1);
-    expect(mockOnSymbolChange).toHaveBeenCalledWith('MOEX:SBER');
+    expect(result.current.issKey).toBeUndefined();
+    expect(mockSetCurrInstrument).not.toHaveBeenCalled();
   });
 
-  it('should close modal after instrument selection', () => {
+  it('should return undefined when getMasterInstrumentFromPublicContext returns null', () => {
     // Arrange
-    const instrumentSearchProps = renderComponent();
+    const mockSetCurrInstrument = jest.fn();
+    const mockGetMasterInstrumentFromPublicContext = jest.fn().mockReturnValue(null);
 
-    const instrument = {
-      issKey: 'MOEX:SBER',
-    } as Contract;
+    // Act
+    const { result } = renderHook(() =>
+      useChartPublicContext({
+        widgetId: 1,
+        setCurrInstrument: mockSetCurrInstrument,
+        getMasterInstrumentFromPublicContext: mockGetMasterInstrumentFromPublicContext,
+      }),
+    );
+
+    // Assert
+    expect(result.current.issKey).toBeUndefined();
+  });
+
+  it('should return undefined when getMasterInstrumentFromPublicContext returns undefined', () => {
+    // Arrange
+    const mockSetCurrInstrument = jest.fn();
+    const mockGetMasterInstrumentFromPublicContext = jest.fn().mockReturnValue(undefined);
 
     // Act
-    instrumentSearchProps.addInstruments([instrument]);
+    const { result } = renderHook(() =>
+      useChartPublicContext({
+        widgetId: 1,
+        setCurrInstrument: mockSetCurrInstrument,
+        getMasterInstrumentFromPublicContext: mockGetMasterInstrumentFromPublicContext,
+      }),
+    );
 
     // Assert
-    expect(mockSetOpen).toHaveBeenCalledTimes(1);
-    expect(mockSetOpen).toHaveBeenCalledWith(false);
+    expect(result.current.issKey).toBeUndefined();
   });
 
-  it('should not change symbol when instruments list is empty', () => {
+  it('should set default instrument with display name and ticker when context value is empty', () => {
     // Arrange
-    const instrumentSearchProps = renderComponent();
+    const mockSetCurrInstrument = jest.fn();
+    const mockGetMasterInstrumentFromPublicContext = jest.fn().mockReturnValue(null);
 
     // Act
-    instrumentSearchProps.addInstruments([]);
+    renderHook(() =>
+      useChartPublicContext({
+        widgetId: 1,
+        setCurrInstrument: mockSetCurrInstrument,
+        getMasterInstrumentFromPublicContext: mockGetMasterInstrumentFromPublicContext,
+      }),
+    );
 
     // Assert
-    expect(mockOnSymbolChange).not.toHaveBeenCalled();
-    expect(mockSetOpen).not.toHaveBeenCalled();
+    expect(mockSetCurrInstrument).toHaveBeenCalledTimes(1);
+    expect(mockSetCurrInstrument).toHaveBeenCalledWith(DEFAULT_SYMBOL, 'Инструмент по умолчанию', 'DEFAULT');
   });
 
-  it('should not change symbol when selected instrument has no issKey', () => {
+  it('should resolve instrument when widget is not found', () => {
     // Arrange
-    const instrumentSearchProps = renderComponent();
+    const mockSetCurrInstrument = jest.fn();
+    const mockGetMasterInstrumentFromPublicContext = jest.fn().mockReturnValue('MOEX:TEST1');
+
+    mockUseAppSelect.mockImplementation((selector) => {
+      if (selector.toString().includes('publicContext')) {
+        return mockPublicContext;
+      }
+
+      if (selector.toString().includes('widgets')) {
+        return null;
+      }
+
+      return {};
+    });
 
     // Act
-    instrumentSearchProps.addInstruments([{} as Contract]);
+    const { result } = renderHook(() =>
+      useChartPublicContext({
+        widgetId: 999,
+        setCurrInstrument: mockSetCurrInstrument,
+        getMasterInstrumentFromPublicContext: mockGetMasterInstrumentFromPublicContext,
+      }),
+    );
 
     // Assert
-    expect(mockOnSymbolChange).not.toHaveBeenCalled();
-    expect(mockSetOpen).not.toHaveBeenCalled();
+    expect(result.current.issKey).toBe('MOEX:TEST1');
+    expect(mockSetCurrInstrument).toHaveBeenCalledWith('MOEX:TEST1', 'Test Instrument 1', 'TEST1');
   });
 
-  it('should not change symbol when selected instrument has empty issKey', () => {
+  it('should not set instrument when contracts array is empty', () => {
     // Arrange
-    const instrumentSearchProps = renderComponent();
+    const mockSetCurrInstrument = jest.fn();
+    const mockGetMasterInstrumentFromPublicContext = jest.fn().mockReturnValue('MOEX:TEST1');
 
-    const instrument = {
-      issKey: '',
-    } as Contract;
+    mockUseContracts.mockReturnValue({
+      contracts: [],
+    });
 
     // Act
-    instrumentSearchProps.addInstruments([instrument]);
+    const { result } = renderHook(() =>
+      useChartPublicContext({
+        widgetId: 1,
+        setCurrInstrument: mockSetCurrInstrument,
+        getMasterInstrumentFromPublicContext: mockGetMasterInstrumentFromPublicContext,
+      }),
+    );
 
     // Assert
-    expect(mockOnSymbolChange).not.toHaveBeenCalled();
-    expect(mockSetOpen).not.toHaveBeenCalled();
+    expect(result.current.issKey).toBeUndefined();
+    expect(mockSetCurrInstrument).not.toHaveBeenCalled();
   });
 });
diff --git a/src/widgets/Chart/__tests__/dataSourceProvide.test.ts b/src/widgets/Chart/__tests__/dataSourceProvide.test.ts
index 68529685a..18dc7d236 100644
--- a/src/widgets/Chart/__tests__/dataSourceProvide.test.ts
+++ b/src/widgets/Chart/__tests__/dataSourceProvide.test.ts
@@ -247,12 +247,12 @@ describe('DataSourceProvider', () => {
 
     // Assert
     expect(mockRequestRealtimeBars).toHaveBeenCalledWith({
-      currencyPair: 'MOEX.SBER',
+      currencyPair: 'moex.sber',
       interval: '1',
-      ticker: 'MOEX:SBER',
+      ticker: 'moex:sber',
       indicativeData: undefined,
     });
-    expect(mockUpdate).toHaveBeenCalledWith('MOEX:SBER', localMockedBar);
+    expect(mockUpdate).toHaveBeenCalledWith('moex:sber', localMockedBar);
   });
 
   it('should request realtime data with indicative data', async () => {
diff --git a/src/widgets/Chart/__tests__/useChartComponentFacade.test.tsx b/src/widgets/Chart/__tests__/useChartComponentFacade.test.tsx
new file mode 100644
index 000000000..01f477bdf
--- /dev/null
+++ b/src/widgets/Chart/__tests__/useChartComponentFacade.test.tsx
@@ -0,0 +1,370 @@
+import { act, renderHook } from '@testing-library/react';
+
+import { useDispatch } from 'react-redux';
+
+import { communicator } from '@core/comm';
+import { useAppSelect } from '@hooks/useAppSelector';
+import { CORPACTIONS_OPEN_EXESTED_WIDGET_EVENT, HIGHLIGHT_WIDGET_EVENT } from '@modules/widgets/shared';
+import { addContentPropsToWidget, unbindWidgets } from '@store/slices/widgets';
+import { getState } from '@store/store';
+import { useWidgetsBind } from '@utils/hooks/useWidgetsBind';
+
+import useChartComponentFacade from '../hooks/useChartComponentFacade';
+import { useChartPublicContext } from '../hooks/useChartPublicContext';
+
+import type { WidgetProperties } from '../properties/types';
+import type { ChartContainerProps } from '../types';
+
+jest.mock('@store/store', () => ({
+  getState: jest.fn(),
+}));
+jest.mock('@store/slices/widgets', () => ({
+  addContentPropsToWidget: jest.fn(),
+  unbindWidgets: jest.fn(),
+}));
+jest.mock('react-redux', () => ({
+  useDispatch: jest.fn(),
+}));
+jest.mock('@core/comm', () => ({
+  communicator: {
+    listen: jest.fn(),
+  },
+}));
+jest.mock('@hooks/useAppSelector', () => ({
+  useAppSelect: jest.fn(),
+}));
+jest.mock('@utils/hooks/useWidgetsBind', () => ({
+  useWidgetsBind: jest.fn(),
+}));
+jest.mock('../hooks/useChartPublicContext', () => ({
+  useChartPublicContext: jest.fn(),
+}));
+
+interface PublicContextMockParams {
+  setCurrInstrument: (instrumentId: string, instrumentName?: string, instrumentTicker?: string) => void;
+}
+
+describe('useChartComponentFacade', () => {
+  const mockUseDispatch = useDispatch as jest.Mock;
+  const mockUseAppSelect = useAppSelect as jest.Mock;
+  const mockAddContentPropsToWidget = addContentPropsToWidget as unknown as jest.Mock;
+  const mockUnbindWidgets = unbindWidgets as unknown as jest.Mock;
+  const mockGetState = getState as jest.Mock;
+  const mockUseWidgetsBind = useWidgetsBind as jest.Mock;
+  const mockUseChartPublicContext = useChartPublicContext as jest.Mock;
+  const mockCommunicatorListen = communicator.listen as jest.Mock;
+
+  const mockDispatch = jest.fn();
+  const mockTriggerRelatedWidgetsToUpdate = jest.fn();
+  const mockGetMasterInstrumentFromPublicContext = jest.fn();
+
+  const mockUnsubscribeCorpActions = jest.fn();
+  const mockUnsubscribeHighlighter = jest.fn();
+
+  const widgetProperties = {
+    chartState: {
+      savedInstrument: 'MOEX:SBER',
+      savedInstrumentName: 'Сбербанк',
+      savedInstrumentTicker: 'SBER',
+      interval: '1m',
+    },
+    indicativeData: {
+      id: 1,
+      key: 'INDICATIVE',
+      secId: 'INAV',
+      instrumentName: 'Индикатив',
+      settlement: 'Расчётный',
+      firmName: 'Тестовая фирма',
+    },
+  } as WidgetProperties;
+
+  const renderFacade = () =>
+    renderHook(() =>
+      useChartComponentFacade({
+        widgetId: 42,
+      } as ChartContainerProps),
+    );
+
+  beforeEach(() => {
+    jest.clearAllMocks();
+
+    mockUseDispatch.mockReturnValue(mockDispatch);
+    mockUseAppSelect.mockReturnValue(widgetProperties);
+
+    mockGetState.mockReturnValue({
+      widgets: {
+        widgets: [
+          {
+            id: 42,
+            widgetContentProps: widgetProperties,
+          },
+        ],
+      },
+    });
+
+    mockUseWidgetsBind.mockReturnValue({
+      triggerRelatedWidgetsToUpdate: mockTriggerRelatedWidgetsToUpdate,
+      getMasterInstrumentFromPublicContext: mockGetMasterInstrumentFromPublicContext,
+    });
+
+    mockUseChartPublicContext.mockReturnValue({
+      issKey: undefined,
+    });
+
+    mockAddContentPropsToWidget.mockImplementation((payload) => ({
+      type: 'widgets/addContentPropsToWidget',
+      payload,
+    }));
+
+    mockUnbindWidgets.mockImplementation((payload) => ({
+      type: 'widgets/unbindWidgets',
+      payload,
+    }));
+
+    mockCommunicatorListen.mockImplementation(({ messageType }, listener) => {
+      if (messageType === CORPACTIONS_OPEN_EXESTED_WIDGET_EVENT) {
+        return mockUnsubscribeCorpActions;
+      }
+
+      if (messageType === HIGHLIGHT_WIDGET_EVENT) {
+        return mockUnsubscribeHighlighter;
+      }
+
+      return jest.fn();
+    });
+  });
+
+  it('should initialize symbol, display name and ticker from widget properties', () => {
+    // Arrange & Act
+    const { result } = renderFacade();
+
+    // Assert
+    expect(result.current.currentInstrument).toBe('MOEX:SBER');
+    expect(result.current.currentInstrumentName).toBe('Сбербанк');
+    expect(result.current.currentInstrumentTicker).toBe('SBER');
+  });
+
+  it('should update and save instrument selected from modal', () => {
+    // Arrange
+    const { result } = renderFacade();
+
+    jest.clearAllMocks();
+
+    // Act
+    act(() => {
+      result.current.addInstrumentFromModal([
+        {
+          issKey: 'MOEX:GAZP',
+          displayName: 'Газпром',
+          symbol: 'GAZP',
+        },
+      ]);
+    });
+
+    // Assert
+    expect(result.current.currentInstrument).toBe('MOEX:GAZP');
+    expect(result.current.currentInstrumentName).toBe('Газпром');
+    expect(result.current.currentInstrumentTicker).toBe('GAZP');
+
+    expect(mockUnbindWidgets).toHaveBeenCalledWith({
+      widgetId: 42,
+    });
+
+    expect(mockAddContentPropsToWidget).toHaveBeenCalledWith({
+      id: 42,
+      withoutSend: true,
+      widgetContentProps: {
+        chartState: {
+          ...widgetProperties.chartState,
+          savedInstrument: 'MOEX:GAZP',
+          savedInstrumentName: 'Газпром',
+          savedInstrumentTicker: 'GAZP',
+        },
+        indicativeData: undefined,
+      },
+    });
+
+    expect(mockTriggerRelatedWidgetsToUpdate).toHaveBeenCalledWith('MOEX:GAZP');
+  });
+
+  it('should update name and ticker without unbinding when symbol is unchanged', () => {
+    // Arrange
+    const { result } = renderFacade();
+
+    jest.clearAllMocks();
+
+    // Act
+    act(() => {
+      result.current.addInstrumentFromModal([
+        {
+          issKey: 'MOEX:SBER',
+          displayName: 'Сбербанк ПАО',
+          symbol: 'SBERP',
+        },
+      ]);
+    });
+
+    // Assert
+    expect(result.current.currentInstrument).toBe('MOEX:SBER');
+    expect(result.current.currentInstrumentName).toBe('Сбербанк ПАО');
+    expect(result.current.currentInstrumentTicker).toBe('SBERP');
+
+    expect(mockUnbindWidgets).not.toHaveBeenCalled();
+    expect(mockTriggerRelatedWidgetsToUpdate).not.toHaveBeenCalled();
+
+    expect(mockAddContentPropsToWidget).toHaveBeenCalledWith({
+      id: 42,
+      withoutSend: true,
+      widgetContentProps: {
+        chartState: {
+          ...widgetProperties.chartState,
+          savedInstrument: 'MOEX:SBER',
+          savedInstrumentName: 'Сбербанк ПАО',
+          savedInstrumentTicker: 'SBERP',
+        },
+        indicativeData: widgetProperties.indicativeData,
+      },
+    });
+  });
+
+  it('should send dropped instrument update immediately', () => {
+    // Arrange
+    const { result } = renderFacade();
+
+    jest.clearAllMocks();
+
+    // Act
+    act(() => {
+      result.current.onDropInstruments(
+        {
+          issKey: 'MOEX:LKOH',
+          displayName: 'Лукойл',
+          symbol: 'LKOH',
+        },
+        true,
+      );
+    });
+
+    // Assert
+    expect(mockAddContentPropsToWidget).toHaveBeenCalledWith(
+      expect.objectContaining({
+        id: 42,
+        withoutSend: false,
+        widgetContentProps: expect.objectContaining({
+          chartState: expect.objectContaining({
+            savedInstrument: 'MOEX:LKOH',
+            savedInstrumentName: 'Лукойл',
+            savedInstrumentTicker: 'LKOH',
+          }),
+        }),
+      }),
+    );
+  });
+
+  it('should update instrument from public context without unbinding widget', () => {
+    // Arrange
+    renderFacade();
+
+    const publicContextParams = mockUseChartPublicContext.mock.calls[0]?.[0] as PublicContextMockParams;
+
+    jest.clearAllMocks();
+
+    // Act
+    act(() => {
+      publicContextParams.setCurrInstrument('MOEX:ROSN', 'Роснефть', 'ROSN');
+    });
+
+    // Assert
+    expect(mockUnbindWidgets).not.toHaveBeenCalled();
+
+    expect(mockAddContentPropsToWidget).toHaveBeenCalledWith(
+      expect.objectContaining({
+        withoutSend: false,
+        widgetContentProps: expect.objectContaining({
+          chartState: expect.objectContaining({
+            savedInstrument: 'MOEX:ROSN',
+            savedInstrumentName: 'Роснефть',
+            savedInstrumentTicker: 'ROSN',
+          }),
+        }),
+      }),
+    );
+  });
+
+  it('should use instrument id as name and ticker when metadata is unavailable', () => {
+    // Arrange
+    renderFacade();
+
+    const corpActionsListener = mockCommunicatorListen.mock.calls.find(
+      ([options]) => options.messageType === CORPACTIONS_OPEN_EXESTED_WIDGET_EVENT,
+    )?.[1] as (message: Record<number, string>) => void;
+
+    // Act
+    act(() => {
+      corpActionsListener({
+        42: 'MOEX:UNKNOWN',
+      });
+    });
+
+    // Assert
+    expect(mockAddContentPropsToWidget).toHaveBeenCalledWith(
+      expect.objectContaining({
+        widgetContentProps: expect.objectContaining({
+          chartState: expect.objectContaining({
+            savedInstrument: 'MOEX:UNKNOWN',
+            savedInstrumentName: 'MOEX:UNKNOWN',
+            savedInstrumentTicker: 'MOEX:UNKNOWN',
+          }),
+        }),
+      }),
+    );
+  });
+
+  it('should not update instrument when modal selection is empty', () => {
+    // Arrange
+    const { result } = renderFacade();
+
+    jest.clearAllMocks();
+
+    // Act
+    act(() => {
+      result.current.addInstrumentFromModal([]);
+    });
+
+    // Assert
+    expect(mockAddContentPropsToWidget).not.toHaveBeenCalled();
+    expect(mockUnbindWidgets).not.toHaveBeenCalled();
+    expect(mockTriggerRelatedWidgetsToUpdate).not.toHaveBeenCalled();
+  });
+
+  it('should update widget highlight state', () => {
+    // Arrange
+    const { result } = renderFacade();
+
+    const highlighterListener = mockCommunicatorListen.mock.calls.find(
+      ([options]) => options.messageType === HIGHLIGHT_WIDGET_EVENT,
+    )?.[1] as (message: Record<number, boolean>) => void;
+
+    // Act
+    act(() => {
+      highlighterListener({
+        42: true,
+      });
+    });
+
+    // Assert
+    expect(result.current.isOver).toBe(true);
+  });
+
+  it('should unsubscribe communicator listeners on unmount', () => {
+    // Arrange
+    const { unmount } = renderFacade();
+
+    // Act
+    unmount();
+
+    // Assert
+    expect(mockUnsubscribeCorpActions).toHaveBeenCalledTimes(1);
+    expect(mockUnsubscribeHighlighter).toHaveBeenCalledTimes(1);
+  });
+});
diff --git a/src/widgets/Chart/__tests__/useChartPublicContext.test.tsx b/src/widgets/Chart/__tests__/useChartPublicContext.test.tsx
index 666aa3073..bf2e80ff0 100644
--- a/src/widgets/Chart/__tests__/useChartPublicContext.test.tsx
+++ b/src/widgets/Chart/__tests__/useChartPublicContext.test.tsx
@@ -7,6 +7,8 @@ import { DEFAULT_SYMBOL } from '@widgets/Chart/const';
 
 import { useChartPublicContext } from '../hooks/useChartPublicContext';
 
+import type { Contract } from '@modules/contracts';
+
 // Mock the dependencies
 jest.mock('@hooks/useAppSelector');
 jest.mock('@modules/contracts');
@@ -24,6 +26,7 @@ jest.mock('@api/index', () => ({
     update: jest.fn(),
   },
 }));
+
 jest.mock('@api/controllers/workspace', () => ({
   workspaceController: {
     update: jest.fn(),
@@ -36,32 +39,43 @@ describe('useChartPublicContext', () => {
   const mockFilterByUniqIssKey = filterByUniqIssKey as jest.Mock;
 
   const mockContracts = [
+    {
+      issKey: DEFAULT_SYMBOL,
+      displayName: 'Инструмент по умолчанию',
+      symbol: 'DEFAULT',
+      // ... other contract properties
+    },
     {
       issKey: 'MOEX:TEST1',
-      instrName: 'Test Instrument 1',
+      displayName: 'Test Instrument 1',
       symbol: 'TEST1',
       // ... other contract properties
     },
     {
       issKey: 'MOEX:TEST2',
-      instrName: 'Test Instrument 2',
+      displayName: 'Test Instrument 2',
       symbol: 'TEST2',
       // ... other contract properties
     },
     {
       issKey: 'MOEX:TEST3',
-      instrName: 'Test Instrument 3',
+      displayName: 'Test Instrument 3',
       symbol: 'TEST3',
       // ... other contract properties
     },
-  ];
+  ] as Contract[];
 
   const mockWidget = {
     id: 1,
     name: 'Test Widget',
     type: 'graphic',
     master: 123,
-    externalProperties: [{ key: 'instrument', value: 'MOEX:TEST1' }],
+    externalProperties: [
+      {
+        key: 'instrument',
+        value: 'MOEX:TEST1',
+      },
+    ],
     // ... other widget properties
   };
 
@@ -77,9 +91,11 @@ describe('useChartPublicContext', () => {
       if (selector.toString().includes('publicContext')) {
         return mockPublicContext;
       }
+
       if (selector.toString().includes('widgets')) {
         return mockWidget;
       }
+
       return {};
     });
 
@@ -89,7 +105,7 @@ describe('useChartPublicContext', () => {
     });
 
     // Mock filterByUniqIssKey to return the same contracts
-    mockFilterByUniqIssKey.mockImplementation((contracts, keys) => contracts);
+    mockFilterByUniqIssKey.mockImplementation((contracts: Contract[]) => contracts);
   });
 
   it('should return the correct issKey when a matching instrument is found', () => {
@@ -110,13 +126,13 @@ describe('useChartPublicContext', () => {
     expect(result.current.issKey).toBe('MOEX:TEST1');
   });
 
-  it('should return undefined when no matching instrument is found', () => {
+  it('should pass instrument id and display name when a matching instrument is found', () => {
     // Arrange
     const mockSetCurrInstrument = jest.fn();
-    const mockGetMasterInstrumentFromPublicContext = jest.fn().mockReturnValue('MOEX:NONEXISTENT');
+    const mockGetMasterInstrumentFromPublicContext = jest.fn().mockReturnValue('MOEX:TEST2');
 
     // Act
-    const { result } = renderHook(() =>
+    renderHook(() =>
       useChartPublicContext({
         widgetId: 1,
         setCurrInstrument: mockSetCurrInstrument,
@@ -125,13 +141,14 @@ describe('useChartPublicContext', () => {
     );
 
     // Assert
-    expect(result.current.issKey).toBeUndefined();
+    expect(mockSetCurrInstrument).toHaveBeenCalledTimes(1);
+    expect(mockSetCurrInstrument).toHaveBeenCalledWith('MOEX:TEST2', 'Test Instrument 2', 'TEST2');
   });
 
-  it('should return undefined when getMasterInstrumentFromPublicContext returns null', () => {
+  it('should return undefined when no matching instrument is found', () => {
     // Arrange
     const mockSetCurrInstrument = jest.fn();
-    const mockGetMasterInstrumentFromPublicContext = jest.fn().mockReturnValue(null);
+    const mockGetMasterInstrumentFromPublicContext = jest.fn().mockReturnValue('MOEX:NONEXISTENT');
 
     // Act
     const { result } = renderHook(() =>
@@ -144,12 +161,13 @@ describe('useChartPublicContext', () => {
 
     // Assert
     expect(result.current.issKey).toBeUndefined();
+    expect(mockSetCurrInstrument).not.toHaveBeenCalled();
   });
 
-  it('should return undefined when getMasterInstrumentFromPublicContext returns undefined', () => {
+  it('should return undefined when getMasterInstrumentFromPublicContext returns null', () => {
     // Arrange
     const mockSetCurrInstrument = jest.fn();
-    const mockGetMasterInstrumentFromPublicContext = jest.fn().mockReturnValue(undefined);
+    const mockGetMasterInstrumentFromPublicContext = jest.fn().mockReturnValue(null);
 
     // Act
     const { result } = renderHook(() =>
@@ -164,13 +182,13 @@ describe('useChartPublicContext', () => {
     expect(result.current.issKey).toBeUndefined();
   });
 
-  it('should set current instrument to DEFAULT_SYMBOL when no field value and widget has master', () => {
+  it('should return undefined when getMasterInstrumentFromPublicContext returns undefined', () => {
     // Arrange
     const mockSetCurrInstrument = jest.fn();
-    const mockGetMasterInstrumentFromPublicContext = jest.fn().mockReturnValue(null);
+    const mockGetMasterInstrumentFromPublicContext = jest.fn().mockReturnValue(undefined);
 
     // Act
-    renderHook(() =>
+    const { result } = renderHook(() =>
       useChartPublicContext({
         widgetId: 1,
         setCurrInstrument: mockSetCurrInstrument,
@@ -179,13 +197,13 @@ describe('useChartPublicContext', () => {
     );
 
     // Assert
-    expect(mockSetCurrInstrument).toHaveBeenCalledWith(DEFAULT_SYMBOL);
+    expect(result.current.issKey).toBeUndefined();
   });
 
-  it('should set current instrument to the found issKey when a matching instrument exists', () => {
+  it('should set current instrument to DEFAULT_SYMBOL with display name when no field value and widget has master', () => {
     // Arrange
     const mockSetCurrInstrument = jest.fn();
-    const mockGetMasterInstrumentFromPublicContext = jest.fn().mockReturnValue('MOEX:TEST2');
+    const mockGetMasterInstrumentFromPublicContext = jest.fn().mockReturnValue(null);
 
     // Act
     renderHook(() =>
@@ -197,7 +215,8 @@ describe('useChartPublicContext', () => {
     );
 
     // Assert
-    expect(mockSetCurrInstrument).toHaveBeenCalledWith('MOEX:TEST2');
+    expect(mockSetCurrInstrument).toHaveBeenCalledTimes(1);
+    expect(mockSetCurrInstrument).toHaveBeenCalledWith(DEFAULT_SYMBOL, 'Инструмент по умолчанию', 'DEFAULT');
   });
 
   it('should handle case when widget is not found in widgets array', () => {
@@ -205,14 +224,16 @@ describe('useChartPublicContext', () => {
     const mockSetCurrInstrument = jest.fn();
     const mockGetMasterInstrumentFromPublicContext = jest.fn().mockReturnValue('MOEX:TEST1');
 
-    // Mock useAppSelect to return null for widget (widget not found)
+    // Mock useAppSelect to return null for widget
     mockUseAppSelect.mockImplementation((selector) => {
       if (selector.toString().includes('publicContext')) {
         return mockPublicContext;
       }
+
       if (selector.toString().includes('widgets')) {
         return null; // Widget not found
       }
+
       return {};
     });
 
@@ -227,6 +248,7 @@ describe('useChartPublicContext', () => {
 
     // Assert
     expect(result.current.issKey).toBe('MOEX:TEST1');
+    expect(mockSetCurrInstrument).toHaveBeenCalledWith('MOEX:TEST1', 'Test Instrument 1', 'TEST1');
   });
 
   it('should handle case when contracts array is empty', () => {
@@ -250,5 +272,6 @@ describe('useChartPublicContext', () => {
 
     // Assert
     expect(result.current.issKey).toBeUndefined();
+    expect(mockSetCurrInstrument).not.toHaveBeenCalled();
   });
 });
diff --git a/src/widgets/Chart/__tests__/useMoexchart.test.tsx b/src/widgets/Chart/__tests__/useMoexchart.test.tsx
index 2396e0910..9a4670b33 100644
--- a/src/widgets/Chart/__tests__/useMoexchart.test.tsx
+++ b/src/widgets/Chart/__tests__/useMoexchart.test.tsx
@@ -4,11 +4,11 @@ import { MoexChart, Timeframes } from 'moex-chart';
 import React from 'react';
 
 import { useChangeProperties, useSelectProperties } from '@modules/widgetProperties';
-import { ChartIndicativeData } from '@widgets/Chart/types';
 
 import { useMoexChart } from '../components/MoexChart/hooks';
 
-// Mock the dependencies
+import type { ChartIndicativeData } from '@widgets/Chart/types';
+
 jest.mock('@modules/widgetProperties');
 
 jest.mock('../components/MoexChart/dataSourceProvide', () => ({
@@ -59,13 +59,38 @@ const mockedDataSourceProvide = jest.requireMock('../components/MoexChart/dataSo
 
 type TimeframeValue = (typeof Timeframes)[keyof typeof Timeframes];
 
+interface MockCompareSeries {
+  name: string;
+  seriesOptions?: {
+    priceScaleId?: string;
+    color?: string;
+  };
+}
+
+interface MockIndicatorConfig {
+  symbol?: string;
+  symbolTicker?: string;
+  label?: string;
+  newPane?: boolean;
+  series: MockCompareSeries[];
+}
+
+interface MockIndicatorSnapshot {
+  id?: string;
+  indicatorType?: string;
+  dataSource?: unknown;
+  config?: MockIndicatorConfig;
+}
+
 interface MockPaneSnapshot {
-  indicators: unknown[];
+  indicators: MockIndicatorSnapshot[];
 }
 
 interface MockChartSnapshot {
   charts: {
     symbol: string;
+    symbolName?: string;
+    symbolTicker?: string;
     timeframe: TimeframeValue;
     chartSeriesType: string;
     panes: MockPaneSnapshot[];
@@ -88,7 +113,8 @@ interface MockMoexChartConfig {
 }
 
 interface MockMoexChartState {
-  tf?: TimeframeValue;
+  timeframe: TimeframeValue;
+  initialInterval?: string;
   savedData?: string;
 }
 
@@ -102,6 +128,9 @@ type TimeframeChangeCallback = (timeframe: TimeframeValue) => void;
 
 interface TestComponentProps {
   symbol?: string;
+  symbolName?: string;
+  symbolTicker?: string;
+  indicativeData?: ChartIndicativeData;
 }
 
 describe('useMoexChart', () => {
@@ -117,6 +146,7 @@ describe('useMoexChart', () => {
   const mockSetSnapshot = jest.fn();
   const mockSetSymbol = jest.fn();
   const mockGetCompareManager = jest.fn();
+  const mockSetSettings = jest.fn();
 
   const mockDataSource = jest.fn();
   const mockStartRealtime = jest.fn();
@@ -131,6 +161,8 @@ describe('useMoexChart', () => {
     charts: [
       {
         symbol: 'OLD:SYMBOL',
+        symbolName: 'Old instrument',
+        symbolTicker: 'OLD',
         timeframe: Timeframes['5m'],
         chartSeriesType: 'Candlestick',
         panes: [
@@ -146,10 +178,17 @@ describe('useMoexChart', () => {
   let lastMoexChartConfig: MockMoexChartConfig | null = null;
   let mockMoexChartState: MockMoexChartState | undefined;
 
-  const TestComponent = ({ symbol = 'MOEX:SBER' }: TestComponentProps): React.ReactElement => {
+  const TestComponent = ({
+    symbol = 'MOEX:SBER',
+    symbolName = 'Сбербанк',
+    symbolTicker = 'SBER',
+    indicativeData,
+  }: TestComponentProps): React.ReactElement => {
     hookResult = useMoexChart({
       symbol,
-      indicativeData: undefined,
+      symbolName,
+      symbolTicker,
+      indicativeData,
     });
 
     return <div ref={hookResult.containerRef} />;
@@ -157,12 +196,13 @@ describe('useMoexChart', () => {
 
   beforeEach(() => {
     jest.clearAllMocks();
+    jest.useFakeTimers();
 
     hookResult = null;
     lastMoexChartConfig = null;
 
     mockMoexChartState = {
-      tf: Timeframes['1m'],
+      timeframe: Timeframes['1m'],
       savedData: undefined,
     };
 
@@ -177,8 +217,8 @@ describe('useMoexChart', () => {
     });
 
     mockGetDataSource.mockImplementation(
-      (_indicativeData?: ChartIndicativeData, callback?: (timeframe: Timeframes) => void) =>
-        (timeframe: Timeframes) => {
+      (_indicativeData?: ChartIndicativeData, callback?: (timeframe: TimeframeValue) => void) =>
+        (timeframe: TimeframeValue) => {
           callback?.(timeframe);
 
           return mockDataSource();
@@ -203,46 +243,66 @@ describe('useMoexChart', () => {
         getSnapshot: mockGetSnapshot,
         setSnapshot: mockSetSnapshot,
         setSymbol: mockSetSymbol,
+        setSettings: mockSetSettings,
         getCompareManager: mockGetCompareManager,
       };
     });
   });
 
-  it('should create moex chart with current symbol and timeframe', () => {
+  afterEach(() => {
+    jest.clearAllTimers();
+    jest.useRealTimers();
+  });
+
+  it('should create moex chart with current symbol, name, ticker and timeframe', () => {
     // Arrange & Act
-    render(<TestComponent symbol="MOEX:SBER" />);
+    render(
+      <TestComponent
+        symbol="MOEX:SBER"
+        symbolName="Сбербанк"
+        symbolTicker="SBER"
+      />,
+    );
 
     // Assert
     expect(mockMoexChart).toHaveBeenCalledTimes(1);
     expect(mockDataSourceProvider).toHaveBeenCalledTimes(1);
-
     expect(lastMoexChartConfig?.container).toBeInstanceOf(HTMLDivElement);
     expect(lastMoexChartConfig?.snapshot.charts[0]?.symbol).toBe('MOEX:SBER');
+    expect(lastMoexChartConfig?.snapshot.charts[0]?.symbolName).toBe('Сбербанк');
+    expect(lastMoexChartConfig?.snapshot.charts[0]?.symbolTicker).toBe('SBER');
     expect(lastMoexChartConfig?.snapshot.charts[0]?.timeframe).toBe(Timeframes['1m']);
-
     expect(hookResult?.compareManagerRef.current).toBe(mockCompareManager);
+    expect(hookResult?.hasSavedSnapshot).toBe(false);
   });
 
-  it('should create chart with saved snapshot when saved data exists', () => {
+  it('should create chart with saved snapshot and current instrument data', () => {
     // Arrange
     mockMoexChartState = {
-      tf: Timeframes['5m'],
+      timeframe: Timeframes['5m'],
       savedData: JSON.stringify(mockSnapshot),
     };
 
     // Act
-    render(<TestComponent symbol="MOEX:GAZP" />);
+    render(
+      <TestComponent
+        symbol="MOEX:GAZP"
+        symbolName="Газпром"
+        symbolTicker="GAZP"
+      />,
+    );
 
     // Assert
     expect(lastMoexChartConfig?.snapshot.charts[0]?.symbol).toBe('MOEX:GAZP');
+    expect(lastMoexChartConfig?.snapshot.charts[0]?.symbolName).toBe('Газпром');
+    expect(lastMoexChartConfig?.snapshot.charts[0]?.symbolTicker).toBe('GAZP');
     expect(lastMoexChartConfig?.snapshot.charts[0]?.timeframe).toBe(Timeframes['5m']);
-
     expect(hookResult?.hasSavedSnapshot).toBe(true);
   });
 
   it('should save chart snapshot to widget properties', () => {
     // Arrange
-    render(<TestComponent symbol="MOEX:SBER" />);
+    render(<TestComponent />);
 
     // Act
     act(() => {
@@ -257,23 +317,131 @@ describe('useMoexChart', () => {
 
     const mockState: MockPropertiesState = {
       moexChartState: {
-        tf: Timeframes['1m'],
+        timeframe: Timeframes['1m'],
       },
     };
 
     updateCallback(mockState);
 
     expect(mockState.moexChartState).toEqual({
-      tf: Timeframes['1m'],
+      timeframe: Timeframes['1m'],
       savedData: JSON.stringify(mockSnapshot),
     });
   });
 
+  it('should serialize compare symbol, display name and ticker in snapshot', () => {
+    // Arrange
+    const snapshotWithIndicators: MockChartSnapshot = {
+      charts: [
+        {
+          symbol: 'MOEX:SBER',
+          symbolName: 'Сбербанк',
+          symbolTicker: 'SBER',
+          timeframe: Timeframes['1m'],
+          chartSeriesType: 'Candlestick',
+          panes: [
+            {
+              indicators: [
+                {
+                  id: 'compare-gazp',
+                  indicatorType: undefined,
+                  dataSource: {
+                    subscription: {},
+                  },
+                  config: {
+                    symbol: 'MOEX:GAZP',
+                    symbolTicker: 'GAZP',
+                    label: 'Газпром',
+                    newPane: false,
+                    series: [
+                      {
+                        name: 'Line',
+                        seriesOptions: {
+                          priceScaleId: 'left',
+                          color: '#FFFFFF',
+                        },
+                      },
+                    ],
+                  },
+                },
+                {
+                  id: 'rsi',
+                  indicatorType: 'RSI',
+                  dataSource: {
+                    subscription: {},
+                  },
+                  config: {
+                    label: 'RSI',
+                    newPane: true,
+                    series: [
+                      {
+                        name: 'Line',
+                        seriesOptions: {
+                          priceScaleId: 'right',
+                        },
+                      },
+                    ],
+                  },
+                },
+              ],
+            },
+          ],
+        },
+      ],
+    };
+
+    mockGetSnapshot.mockReturnValue(snapshotWithIndicators);
+
+    render(<TestComponent />);
+
+    // Act
+    act(() => {
+      hookResult?.saveSnapshot();
+    });
+
+    const updateCallback = mockUpdateProperties.mock.calls[0]?.[0] as UpdatePropertiesCallback;
+
+    const mockState: MockPropertiesState = {
+      moexChartState: {
+        timeframe: Timeframes['1m'],
+      },
+    };
+
+    updateCallback(mockState);
+
+    const savedSnapshot = JSON.parse(mockState.moexChartState?.savedData ?? '{}') as MockChartSnapshot;
+    const [compareIndicator, regularIndicator] = savedSnapshot.charts[0]?.panes[0]?.indicators ?? [];
+
+    // Assert
+    expect(compareIndicator).toEqual({
+      id: 'compare-gazp',
+      config: {
+        symbol: 'MOEX:GAZP',
+        symbolTicker: 'GAZP',
+        label: 'Газпром',
+        newPane: false,
+        series: [
+          {
+            name: 'Line',
+            seriesOptions: {
+              priceScaleId: 'left',
+            },
+          },
+        ],
+      },
+    });
+
+    expect(regularIndicator).toEqual({
+      id: 'rsi',
+      indicatorType: 'RSI',
+    });
+  });
+
   it('should not save snapshot when chart does not return snapshot', () => {
     // Arrange
     mockGetSnapshot.mockReturnValue(undefined);
 
-    render(<TestComponent symbol="MOEX:SBER" />);
+    render(<TestComponent />);
 
     // Act
     act(() => {
@@ -285,14 +453,20 @@ describe('useMoexChart', () => {
     expect(mockUpdateProperties).not.toHaveBeenCalled();
   });
 
-  it('should apply saved snapshot with current symbol and timeframe', () => {
+  it('should apply saved snapshot with current instrument data and timeframe', () => {
     // Arrange
     mockMoexChartState = {
-      tf: Timeframes['5m'],
+      timeframe: Timeframes['5m'],
       savedData: JSON.stringify(mockSnapshot),
     };
 
-    render(<TestComponent symbol="MOEX:SBER" />);
+    render(
+      <TestComponent
+        symbol="MOEX:SBER"
+        symbolName="Сбербанк"
+        symbolTicker="SBER"
+      />,
+    );
 
     // Act
     act(() => {
@@ -306,6 +480,8 @@ describe('useMoexChart', () => {
         {
           ...mockSnapshot.charts[0],
           symbol: 'MOEX:SBER',
+          symbolName: 'Сбербанк',
+          symbolTicker: 'SBER',
           timeframe: Timeframes['5m'],
         },
       ],
@@ -316,7 +492,7 @@ describe('useMoexChart', () => {
 
   it('should update compare modal state', () => {
     // Arrange
-    render(<TestComponent symbol="MOEX:SBER" />);
+    render(<TestComponent />);
 
     // Act
     act(() => {
@@ -329,7 +505,7 @@ describe('useMoexChart', () => {
 
   it('should open compare modal from chart preset callback', () => {
     // Arrange
-    render(<TestComponent symbol="MOEX:SBER" />);
+    render(<TestComponent />);
 
     // Act
     act(() => {
@@ -342,7 +518,7 @@ describe('useMoexChart', () => {
 
   it('should update symbol search modal state', () => {
     // Arrange
-    render(<TestComponent symbol="MOEX:SBER" />);
+    render(<TestComponent />);
 
     // Act
     act(() => {
@@ -355,7 +531,7 @@ describe('useMoexChart', () => {
 
   it('should open symbol search modal from chart preset callback', () => {
     // Arrange
-    render(<TestComponent symbol="MOEX:SBER" />);
+    render(<TestComponent />);
 
     // Act
     act(() => {
@@ -366,82 +542,157 @@ describe('useMoexChart', () => {
     expect(hookResult?.isSymbolSearchOpen).toBe(true);
   });
 
-  it('should normalize and change main symbol', () => {
+  it('should update chart when external instrument changes', () => {
     // Arrange
-    render(<TestComponent symbol="MOEX:SBER" />);
+    const { rerender } = render(<TestComponent />);
 
     // Act
-    act(() => {
-      hookResult?.setMainSymbol('  MOEX:GAZP  ');
-    });
+    rerender(
+      <TestComponent
+        symbol="MOEX:GAZP"
+        symbolName="Газпром"
+        symbolTicker="GAZP"
+      />,
+    );
 
     // Assert
     expect(mockSetSymbol).toHaveBeenCalledTimes(1);
-    expect(mockSetSymbol).toHaveBeenCalledWith('MOEX:GAZP');
+    expect(mockSetSymbol).toHaveBeenCalledWith('MOEX:GAZP', 'Газпром', 'GAZP');
   });
 
-  it('should not change main symbol when value is empty', () => {
+  it('should update chart when only external symbol name changes', () => {
     // Arrange
-    render(<TestComponent symbol="MOEX:SBER" />);
+    const { rerender } = render(<TestComponent />);
 
     // Act
-    act(() => {
-      hookResult?.setMainSymbol('');
-      hookResult?.setMainSymbol('   ');
-    });
+    rerender(
+      <TestComponent
+        symbol="MOEX:SBER"
+        symbolName="Сбербанк ПАО"
+        symbolTicker="SBER"
+      />,
+    );
 
     // Assert
-    expect(mockSetSymbol).not.toHaveBeenCalled();
+    expect(mockSetSymbol).toHaveBeenCalledTimes(1);
+    expect(mockSetSymbol).toHaveBeenCalledWith('MOEX:SBER', 'Сбербанк ПАО', 'SBER');
   });
 
-  it('should not change main symbol when it is already selected', () => {
+  it('should update chart when only external symbol ticker changes', () => {
     // Arrange
-    render(<TestComponent symbol="MOEX:SBER" />);
+    const { rerender } = render(<TestComponent />);
 
     // Act
-    act(() => {
-      hookResult?.setMainSymbol('MOEX:SBER');
-    });
+    rerender(
+      <TestComponent
+        symbol="MOEX:SBER"
+        symbolName="Сбербанк"
+        symbolTicker="SBERP"
+      />,
+    );
 
     // Assert
-    expect(mockSetSymbol).not.toHaveBeenCalled();
+    expect(mockSetSymbol).toHaveBeenCalledTimes(1);
+    expect(mockSetSymbol).toHaveBeenCalledWith('MOEX:SBER', 'Сбербанк', 'SBERP');
   });
 
-  it('should update chart when external symbol changes', () => {
+  it('should use symbol as display name when symbol name is empty', () => {
     // Arrange
-    const { rerender } = render(<TestComponent symbol="MOEX:SBER" />);
+    const { rerender } = render(<TestComponent />);
 
     // Act
-    rerender(<TestComponent symbol="MOEX:GAZP" />);
+    rerender(
+      <TestComponent
+        symbol="MOEX:SBER"
+        symbolName=""
+        symbolTicker="SBER"
+      />,
+    );
 
     // Assert
     expect(mockSetSymbol).toHaveBeenCalledTimes(1);
-    expect(mockSetSymbol).toHaveBeenCalledWith('MOEX:GAZP');
+    expect(mockSetSymbol).toHaveBeenCalledWith('MOEX:SBER', 'MOEX:SBER', 'SBER');
+  });
+
+  it('should use symbol as ticker when symbol ticker is empty', () => {
+    // Arrange
+    const { rerender } = render(<TestComponent />);
+
+    // Act
+    rerender(
+      <TestComponent
+        symbol="MOEX:SBER"
+        symbolName="Сбербанк"
+        symbolTicker=""
+      />,
+    );
+
+    // Assert
+    expect(mockSetSymbol).toHaveBeenCalledTimes(1);
+    expect(mockSetSymbol).toHaveBeenCalledWith('MOEX:SBER', 'Сбербанк', 'MOEX:SBER');
+  });
+
+  it('should not update chart when symbol, name and ticker are unchanged', () => {
+    // Arrange
+    const { rerender } = render(<TestComponent />);
+
+    // Act
+    rerender(<TestComponent />);
+
+    // Assert
+    expect(mockSetSymbol).not.toHaveBeenCalled();
+  });
+
+  it('should not update chart when external symbol is empty', () => {
+    // Arrange
+    const { rerender } = render(<TestComponent />);
+
+    // Act
+    rerender(
+      <TestComponent
+        symbol=""
+        symbolName="Пустой инструмент"
+        symbolTicker="EMPTY"
+      />,
+    );
+
+    // Assert
+    expect(mockSetSymbol).not.toHaveBeenCalled();
   });
 
-  it('should not recreate chart when external symbol changes', () => {
+  it('should not recreate chart when external instrument changes', () => {
     // Arrange
-    const { rerender } = render(<TestComponent symbol="MOEX:SBER" />);
+    const { rerender } = render(<TestComponent />);
 
     // Act
-    rerender(<TestComponent symbol="MOEX:GAZP" />);
+    rerender(
+      <TestComponent
+        symbol="MOEX:GAZP"
+        symbolName="Газпром"
+        symbolTicker="GAZP"
+      />,
+    );
 
     // Assert
     expect(mockMoexChart).toHaveBeenCalledTimes(1);
   });
 
-  it('should apply saved snapshot with symbol selected from search modal', () => {
+  it('should apply saved snapshot with externally selected instrument', () => {
     // Arrange
     mockMoexChartState = {
-      tf: Timeframes['5m'],
+      timeframe: Timeframes['5m'],
       savedData: JSON.stringify(mockSnapshot),
     };
 
-    render(<TestComponent symbol="MOEX:SBER" />);
+    const { rerender } = render(<TestComponent />);
 
-    act(() => {
-      hookResult?.setMainSymbol('MOEX:GAZP');
-    });
+    rerender(
+      <TestComponent
+        symbol="MOEX:GAZP"
+        symbolName="Газпром"
+        symbolTicker="GAZP"
+      />,
+    );
 
     // Act
     act(() => {
@@ -455,15 +706,50 @@ describe('useMoexChart', () => {
         {
           ...mockSnapshot.charts[0],
           symbol: 'MOEX:GAZP',
+          symbolName: 'Газпром',
+          symbolTicker: 'GAZP',
           timeframe: Timeframes['5m'],
         },
       ],
     });
   });
 
+  it('should initialize indicative instrument with id, name and ticker', () => {
+    // Arrange
+    const indicativeData: ChartIndicativeData = {
+      id: 1,
+      title: 'Indicative instrument',
+      secId: 'INAV',
+      instrumentName: 'Индикатив',
+      settlement: 'Расчётный',
+      firmName: 'Тестовая фирма',
+      key: '2xOFZ:INAV',
+    };
+
+    // Act
+    render(
+      <TestComponent
+        symbol="2xOFZ:INAV"
+        symbolName="Индикатив Расчётный"
+        symbolTicker="INAV"
+        indicativeData={indicativeData}
+      />,
+    );
+
+    // Assert
+    expect(mockGetDataSource).toHaveBeenCalledWith(indicativeData, expect.any(Function));
+    expect(lastMoexChartConfig?.snapshot.charts[0]).toEqual(
+      expect.objectContaining({
+        symbol: '2xOFZ:INAV',
+        symbolName: 'Индикатив Расчётный',
+        symbolTicker: 'INAV',
+      }),
+    );
+  });
+
   it('should pass realtime params to data source provider', () => {
     // Arrange
-    render(<TestComponent symbol="MOEX:SBER" />);
+    render(<TestComponent />);
 
     const getSymbols = jest.fn(() => ['MOEX:SBER']);
     const getTimeframe = jest.fn(() => Timeframes['1m']);
@@ -478,13 +764,12 @@ describe('useMoexChart', () => {
       getTimeframe,
       update,
     });
-
     expect(unsubscribe).toBe(mockRealtimeUnsubscribe);
   });
 
   it('should update timeframe from data source callback', () => {
     // Arrange
-    render(<TestComponent symbol="MOEX:SBER" />);
+    render(<TestComponent />);
 
     const timeframeChangeCallback = mockGetDataSource.mock.calls[0]?.[1] as TimeframeChangeCallback;
 
@@ -500,18 +785,18 @@ describe('useMoexChart', () => {
 
     const mockState: MockPropertiesState = {
       moexChartState: {
-        tf: Timeframes['1m'],
+        timeframe: Timeframes['1m'],
       },
     };
 
     updateCallback(mockState);
 
-    expect(mockState.moexChartState?.tf).toBe(Timeframes['5m']);
+    expect(mockState.moexChartState?.timeframe).toBe(Timeframes['5m']);
   });
 
   it('should not update timeframe when it is the same as current timeframe', () => {
     // Arrange
-    render(<TestComponent symbol="MOEX:SBER" />);
+    render(<TestComponent />);
 
     const timeframeChangeCallback = mockGetDataSource.mock.calls[0]?.[1] as TimeframeChangeCallback;
 
@@ -526,7 +811,7 @@ describe('useMoexChart', () => {
 
   it('should destroy chart on unmount', () => {
     // Arrange
-    const { unmount } = render(<TestComponent symbol="MOEX:SBER" />);
+    const { unmount } = render(<TestComponent />);
 
     // Act
     unmount();
diff --git a/src/widgets/Chart/components/MoexChart/MoexChart.tsx b/src/widgets/Chart/components/MoexChart/MoexChart.tsx
index 86e7bb981..03ca8faf6 100644
--- a/src/widgets/Chart/components/MoexChart/MoexChart.tsx
+++ b/src/widgets/Chart/components/MoexChart/MoexChart.tsx
@@ -3,67 +3,71 @@ import React from 'react';
 import { Contract } from '@modules/contracts';
 import { SymbolSearchModal } from '@widgets/Chart/components/MoexChart/components/SymbolSearchModal';
 
-import { ChartIndicativeData } from '../../types';
+import { ChartIndicativeData, SelectedInstrument } from '../../types';
 
 import { CompareModal } from './components/CompareModal';
 import { useMoexChart } from './hooks';
 
 import 'moex-chart/dist/styles.css';
 
-type TRProps = {
-  fullName: string;
+interface TRProps {
+  symbol: string;
+  symbolName: string;
+  symbolTicker: string;
   indicativeData?: ChartIndicativeData | undefined;
   widgetId: number;
-  addInstrumentFromModal: (instruments: Pick<Contract, 'issKey'>[]) => void;
-};
+  addInstrumentFromModal: (instruments: SelectedInstrument[]) => void;
+}
 
-export default React.memo(({ fullName, indicativeData, widgetId, addInstrumentFromModal }: TRProps) => {
-  const {
-    containerRef,
-    isCompareOpen,
-    isSymbolSearchOpen,
-    compareManagerRef,
-    setIsCompareOpen,
-    setIsSymbolSearchOpen,
-    setMainSymbol,
-  } = useMoexChart({
-    indicativeData,
-    symbol: fullName,
-  });
+export default React.memo(
+  ({ symbol, symbolName, symbolTicker, indicativeData, widgetId, addInstrumentFromModal }: TRProps) => {
+    const {
+      containerRef,
+      isCompareOpen,
+      isSymbolSearchOpen,
+      compareManagerRef,
+      setIsCompareOpen,
+      setIsSymbolSearchOpen,
+    } = useMoexChart({
+      indicativeData,
+      symbol,
+      symbolName,
+      symbolTicker,
+    });
 
-  const handleSymbolChange = (symbol: string): void => {
-    addInstrumentFromModal([{ issKey: symbol }]);
-    setMainSymbol(symbol);
-  };
+    const handleSymbolChange = (instrument: Contract): void => {
+      addInstrumentFromModal([instrument]);
+    };
 
-  return (
-    <div
-      style={{
-        flex: '1 1 0',
-        minHeight: 0,
-        minWidth: 0,
-      }}
-    >
-      <div ref={containerRef} />
+    return (
+      <div
+        style={{
+          flex: '1 1 0',
+          minHeight: 0,
+          minWidth: 0,
+        }}
+      >
+        <div ref={containerRef} />
 
-      {isCompareOpen && (
-        <CompareModal
-          onClose={() => setIsCompareOpen(false)}
-          compareManager={compareManagerRef}
-          widgetId={widgetId}
-          isOpen={isCompareOpen}
-          setOpen={setIsCompareOpen}
-        />
-      )}
+        {isCompareOpen && (
+          <CompareModal
+            onClose={() => setIsCompareOpen(false)}
+            compareManager={compareManagerRef}
+            widgetId={widgetId}
+            isOpen={isCompareOpen}
+            setOpen={setIsCompareOpen}
+          />
+        )}
 
-      {isSymbolSearchOpen && (
-        <SymbolSearchModal
-          widgetId={widgetId}
-          isOpen={isSymbolSearchOpen}
-          setOpen={setIsSymbolSearchOpen}
-          onSymbolChange={handleSymbolChange}
-        />
-      )}
-    </div>
-  );
-});
+        {isSymbolSearchOpen && (
+          <SymbolSearchModal
+            widgetId={widgetId}
+            isOpen={isSymbolSearchOpen}
+            setOpen={setIsSymbolSearchOpen}
+            onSymbolChange={handleSymbolChange}
+          />
+        )}
+      </div>
+    );
+  },
+);
diff --git a/src/widgets/Chart/components/MoexChart/components/CompareModal.tsx b/src/widgets/Chart/components/MoexChart/components/CompareModal.tsx
index 092f6df1a..8c6d67725 100644
--- a/src/widgets/Chart/components/MoexChart/components/CompareModal.tsx
+++ b/src/widgets/Chart/components/MoexChart/components/CompareModal.tsx
@@ -2,6 +2,7 @@ import { __CompareManager__, CompareMode } from 'moex-chart';
 import React, { MutableRefObject, useEffect, useState } from 'react';
 
 import { InstrumentSearch } from '@components/InstrumentSearch';
+import { Contract } from '@modules/contracts';
 
 export const CompareModal = ({
   compareManager,
@@ -33,16 +34,34 @@ export const CompareModal = ({
     return () => subscription.unsubscribe();
   }, [compareManager, isOpen]);
 
-  const handlePercent = (symbol: string) => {
-    compareManager?.current?.setSymbolMode('Line', symbol, CompareMode.Percentage);
+  const setCompareMode = (instrument: Contract, mode: CompareMode): void => {
+    const symbol = instrument.issKey;
+
+    if (!symbol) {
+      return;
+    }
+
+    compareManager.current?.setSymbolMode(
+      'Line',
+      {
+        symbol,
+        symbolName: instrument.displayName || symbol,
+        symbolTicker: instrument.symbol || symbol,
+      },
+      mode,
+    );
+  };
+
+  const handlePercent = (instrument: Contract): void => {
+    setCompareMode(instrument, CompareMode.Percentage);
   };
 
-  const handleNewScale = (symbol: string) => {
-    compareManager?.current?.setSymbolMode('Line', symbol, CompareMode.NewScale);
+  const handleNewScale = (instrument: Contract): void => {
+    setCompareMode(instrument, CompareMode.NewScale);
   };
 
-  const handleNewPanel = (symbol: string) => {
-    compareManager?.current?.setSymbolMode('Line', symbol, CompareMode.NewPane);
+  const handleNewPanel = (instrument: Contract): void => {
+    setCompareMode(instrument, CompareMode.NewPane);
   };
 
   return (
diff --git a/src/widgets/Chart/components/MoexChart/components/SymbolSearchModal.tsx b/src/widgets/Chart/components/MoexChart/components/SymbolSearchModal.tsx
index efb08af16..10347dfc3 100644
--- a/src/widgets/Chart/components/MoexChart/components/SymbolSearchModal.tsx
+++ b/src/widgets/Chart/components/MoexChart/components/SymbolSearchModal.tsx
@@ -8,18 +8,18 @@ interface SymbolSearchModalProps {
   widgetId: number;
   isOpen: boolean;
   setOpen: (isOpen: boolean) => void;
-  onSymbolChange: (symbol: string) => void;
+  onSymbolChange: (instrument: Contract) => void;
 }
 
 export const SymbolSearchModal = ({ widgetId, isOpen, setOpen, onSymbolChange }: SymbolSearchModalProps) => {
   const handleAddInstruments = (instruments: Contract[]) => {
-    const symbol = instruments[0]?.issKey;
+    const selectedInstrument = instruments[0];
 
-    if (!symbol) {
+    if (!selectedInstrument?.issKey) {
       return;
     }
 
-    onSymbolChange(symbol);
+    onSymbolChange(selectedInstrument);
     setOpen(false);
   };
 
diff --git a/src/widgets/Chart/components/MoexChart/constants.ts b/src/widgets/Chart/components/MoexChart/constants.ts
index 288036c50..737666528 100644
--- a/src/widgets/Chart/components/MoexChart/constants.ts
+++ b/src/widgets/Chart/components/MoexChart/constants.ts
@@ -3,7 +3,7 @@ import { DateFormat, IndicatorsIds, Locale, Timeframes } from 'moex-chart';
 import type { ChartCollectionPreset, IMoexChart, MoexChartSnapshot } from 'moex-chart';
 
 type ChartCollectionPresetConfig = Omit<ChartCollectionPreset, 'getDataSource' | 'startRealtime'>;
-type ChartSnapshotItemConfig = Omit<MoexChartSnapshot['charts'][number], 'symbol'>;
+type ChartSnapshotItemConfig = Omit<MoexChartSnapshot['charts'][number], 'symbol' | 'symbolName' | 'symbolTicker'>;
 type MoexChartSnapshotConfig = Omit<MoexChartSnapshot, 'charts'> & {
   charts: ChartSnapshotItemConfig[];
 };
diff --git a/src/widgets/Chart/components/MoexChart/hooks/useMoexchart.ts b/src/widgets/Chart/components/MoexChart/hooks/useMoexchart.ts
index 58c504900..895ce171a 100644
--- a/src/widgets/Chart/components/MoexChart/hooks/useMoexchart.ts
+++ b/src/widgets/Chart/components/MoexChart/hooks/useMoexchart.ts
@@ -3,10 +3,9 @@ import { MoexChart, Timeframes } from 'moex-chart';
 import { useEffect, useRef, useState } from 'react';
 
 import { useChangeProperties, useSelectProperties } from '@modules/widgetProperties';
+import { WidgetProperties } from '@widgets/Chart/properties/types';
 import { ChartIndicativeData } from '@widgets/Chart/types';
 
-import { WidgetProperties } from '../../../properties/types';
-
 import { MOEX_CHART_CONFIG } from '../constants';
 import { DataSourceProvider } from '../dataSourceProvide';
 
@@ -14,10 +13,12 @@ import type { __CompareManager__, IMoexChart } from 'moex-chart';
 
 interface TUseMoexChartProps {
   symbol: string;
+  symbolName: string;
+  symbolTicker: string;
   indicativeData?: ChartIndicativeData;
 }
 
-export const useMoexChart = ({ symbol, indicativeData }: TUseMoexChartProps) => {
+export const useMoexChart = ({ symbol, symbolName, symbolTicker, indicativeData }: TUseMoexChartProps) => {
   const moexChartState = useSelectProperties((wProps: Partial<WidgetProperties>) => wProps.moexChartState);
 
   const { updateProperties } = useChangeProperties<WidgetProperties>();
@@ -29,30 +30,35 @@ export const useMoexChart = ({ symbol, indicativeData }: TUseMoexChartProps) =>
   const chartRef = useRef<MoexChart | null>(null);
   const compareManagerRef = useRef<null | __CompareManager__>(null);
   const currentSymbolRef = useRef(symbol);
+  const currentSymbolNameRef = useRef(symbolName || symbol);
+  const currentSymbolTickerRef = useRef(symbolTicker);
 
   const timeframeRef = useRef<Timeframes | undefined>(moexChartState?.timeframe);
   const savedDataRef = useRef<string | undefined>(moexChartState?.savedData);
   const updateTimeframeRef = useRef<((tf: Timeframes) => void) | null>(null);
 
   useEffect(() => {
-    if (!symbol || currentSymbolRef.current === symbol) {
+    if (!symbol) {
       return;
     }
 
-    currentSymbolRef.current = symbol;
-    chartRef.current?.setSymbol(symbol);
-  }, [symbol]);
+    const nextSymbolName = symbolName || symbol;
+    const nextSymbolTicker = symbolTicker || symbol;
 
-  const setMainSymbol = (nextSymbol: string) => {
-    const normalizedSymbol = nextSymbol.trim();
+    const symbolChanged = currentSymbolRef.current !== symbol;
+    const symbolNameChanged = currentSymbolNameRef.current !== nextSymbolName;
+    const symbolTickerChanged = currentSymbolTickerRef.current !== nextSymbolTicker;
 
-    if (!normalizedSymbol || currentSymbolRef.current === normalizedSymbol) {
+    if (!symbolChanged && !symbolNameChanged && !symbolTickerChanged) {
       return;
     }
 
-    currentSymbolRef.current = normalizedSymbol;
-    chartRef.current?.setSymbol(normalizedSymbol);
-  };
+    currentSymbolRef.current = symbol;
+    currentSymbolNameRef.current = nextSymbolName;
+    currentSymbolTickerRef.current = nextSymbolTicker;
+
+    chartRef.current?.setSymbol(symbol, nextSymbolName, nextSymbolTicker);
+  }, [symbol, symbolName, symbolTicker]);
 
   useEffect(() => {
     timeframeRef.current = moexChartState?.timeframe;
@@ -96,6 +102,8 @@ export const useMoexChart = ({ symbol, indicativeData }: TUseMoexChartProps) =>
               config:
                 indicator.indicatorType === undefined && indicator.config?.label && compareSeries
                   ? {
+                      symbol: indicator.config.symbol,
+                      symbolTicker: indicator.config.symbolTicker,
                       label: indicator.config.label,
                       newPane: indicator.config.newPane,
                       series: [
@@ -138,6 +146,8 @@ export const useMoexChart = ({ symbol, indicativeData }: TUseMoexChartProps) =>
       charts: savedSnapshot.charts.map((chartSnapshot) => ({
         ...chartSnapshot,
         symbol: currentSymbolRef.current,
+        symbolName: currentSymbolNameRef.current,
+        symbolTicker: currentSymbolTickerRef.current,
         timeframe,
       })),
     });
@@ -166,6 +176,8 @@ export const useMoexChart = ({ symbol, indicativeData }: TUseMoexChartProps) =>
         charts: savedSnapshot.charts.map((chartSnapshot) => ({
           ...chartSnapshot,
           symbol: currentSymbolRef.current,
+          symbolName: currentSymbolNameRef.current,
+          symbolTicker: currentSymbolTickerRef.current,
           timeframe,
         })),
       },
@@ -208,7 +220,6 @@ export const useMoexChart = ({ symbol, indicativeData }: TUseMoexChartProps) =>
     compareManagerRef,
     setIsCompareOpen,
     setIsSymbolSearchOpen,
-    setMainSymbol,
     saveSnapshot,
     applySnapshot,
     hasSavedSnapshot: Boolean(moexChartState?.savedData),
diff --git a/src/widgets/Chart/hooks/useChartComponentFacade.tsx b/src/widgets/Chart/hooks/useChartComponentFacade.tsx
index ece1436b8..254d48f30 100644
--- a/src/widgets/Chart/hooks/useChartComponentFacade.tsx
+++ b/src/widgets/Chart/hooks/useChartComponentFacade.tsx
@@ -13,9 +13,8 @@ import { DEFAULT_SYMBOL } from '../const';
 import { useChartPublicContext } from './useChartPublicContext';
 
 import type { WidgetProperties } from '../properties/types';
-import type { ChartContainerProps } from '../types';
+import type { ChartContainerProps, SelectedInstrument } from '../types';
 
-import type { Contract } from '@modules/contracts';
 import type { Dispatch, SetStateAction } from 'react';
 import type { Widget } from 'types/Widgets';
 
@@ -23,10 +22,12 @@ interface UseChartComponentFacadeReturn {
   dropDownOpen: boolean;
   setDropdownOpen: Dispatch<SetStateAction<boolean>>;
   currentInstrument: string;
+  currentInstrumentName: string;
+  currentInstrumentTicker: string;
   isWidgetHeaderContextMenuOpen: boolean;
   setIsWidgetHeaderContextMenuOpen: Dispatch<SetStateAction<boolean>>;
-  onDropInstruments: (val: string, withUpdate?: boolean) => void;
-  addInstrumentFromModal: (instruments: Pick<Contract, 'issKey'>[]) => void;
+  onDropInstruments: (instrument: SelectedInstrument, withUpdate?: boolean) => void;
+  addInstrumentFromModal: (instruments: SelectedInstrument[]) => void;
   isOver: boolean;
 }
 
@@ -49,11 +50,18 @@ export default function useChartComponentFacade(props: ChartContainerProps): Use
   ) as WidgetProperties | undefined;
 
   const initialInstrument = widgetProperties?.chartState?.savedInstrument ?? DEFAULT_SYMBOL;
+  const initialInstrumentName = widgetProperties?.chartState?.savedInstrumentName ?? initialInstrument;
+  const initialInstrumentTicker = widgetProperties?.chartState?.savedInstrumentTicker ?? initialInstrument;
 
   // TODO временно реф из-за непредсказуемого изменения если используется useState,
   // нужен рефакторинг и вернуть обратно useState
   const currentInstrumentRef = useRef(initialInstrument);
+  const currentInstrumentNameRef = useRef(initialInstrumentName);
+  const currentInstrumentTickerRef = useRef(initialInstrumentTicker);
+
   const [currentInstrument, setCurrentInstrument] = useState(initialInstrument);
+  const [currentInstrumentName, setCurrentInstrumentName] = useState(initialInstrumentName);
+  const [currentInstrumentTicker, setCurrentInstrumentTicker] = useState(initialInstrumentTicker);
 
   const { triggerRelatedWidgetsToUpdate, getMasterInstrumentFromPublicContext } = useWidgetsBind({
     widgetId,
@@ -95,60 +103,91 @@ export default function useChartComponentFacade(props: ChartContainerProps): Use
   );
 
   const onInstrumentChange = useCallback(
-    (newVal: string, withUpdate?: boolean, unbind = true): void => {
+    (newVal: string, newName?: string, newTicker?: string, withUpdate?: boolean, unbind = true): void => {
       if (!newVal) {
         return;
       }
 
       setIsWidgetHeaderContextMenuOpen(false);
 
-      if (currentInstrumentRef.current === newVal) {
+      const symbolChanged = currentInstrumentRef.current !== newVal;
+
+      const normalizedName = newName?.trim();
+      const normalizedTicker = newTicker?.trim();
+
+      const nextInstrumentName = normalizedName || (symbolChanged ? newVal : currentInstrumentNameRef.current);
+      const nextInstrumentTicker = normalizedTicker || (symbolChanged ? newVal : currentInstrumentTickerRef.current);
+
+      const nameChanged = currentInstrumentNameRef.current !== nextInstrumentName;
+      const tickerChanged = currentInstrumentTickerRef.current !== nextInstrumentTicker;
+
+      if (!symbolChanged && !nameChanged && !tickerChanged) {
         return;
       }
 
-      currentInstrumentRef.current = newVal;
-      setCurrentInstrument(newVal);
+      if (symbolChanged) {
+        currentInstrumentRef.current = newVal;
+        setCurrentInstrument(newVal);
+
+        if (unbind) {
+          dispatch(unbindWidgets({ widgetId }));
+        }
+      }
+
+      if (nameChanged) {
+        currentInstrumentNameRef.current = nextInstrumentName;
+        setCurrentInstrumentName(nextInstrumentName);
+      }
 
-      if (unbind) {
-        dispatch(unbindWidgets({ widgetId }));
+      if (tickerChanged) {
+        currentInstrumentTickerRef.current = nextInstrumentTicker;
+        setCurrentInstrumentTicker(nextInstrumentTicker);
       }
 
       // при смене инструмента очищаем индикативные данные виджета график
       // т.к. логика для графика индикатива построена на наличии в widgetContentProps данных indicativeData
       saveContentProps({
         savedInstrument: newVal,
+        savedInstrumentName: nextInstrumentName,
+        savedInstrumentTicker: nextInstrumentTicker,
         withUpdate,
-        cleanIndicativeData: true,
+        cleanIndicativeData: symbolChanged,
       });
 
-      triggerRelatedWidgetsToUpdateRef.current(newVal);
+      if (symbolChanged) {
+        triggerRelatedWidgetsToUpdateRef.current(newVal);
+      }
     },
     [dispatch, saveContentProps, widgetId],
   );
 
   const addInstrumentFromModal = useCallback(
-    (instruments: Pick<Contract, 'issKey'>[]) => {
-      const issKey = instruments[0]?.issKey;
+    (instruments: SelectedInstrument[]): void => {
+      const instrument = instruments[0];
 
-      if (!issKey) {
+      if (!instrument?.issKey) {
         return;
       }
 
-      onInstrumentChange(issKey);
+      onInstrumentChange(instrument.issKey, instrument.displayName, instrument.symbol);
     },
     [onInstrumentChange],
   );
 
   const onInstrumentChangeFromBind = useCallback(
-    (instrumentId: string) => {
-      onInstrumentChange(instrumentId, true, false);
+    (instrumentId: string, instrumentName?: string, instrumentTicker?: string): void => {
+      onInstrumentChange(instrumentId, instrumentName, instrumentTicker, true, false);
     },
     [onInstrumentChange],
   );
 
   const onDropInstruments = useCallback(
-    (val: string, withUpdate?: boolean): void => {
-      onInstrumentChange(val, withUpdate);
+    (instrument: SelectedInstrument, withUpdate?: boolean): void => {
+      if (!instrument.issKey) {
+        return;
+      }
+
+      onInstrumentChange(instrument.issKey, instrument.displayName, instrument.symbol, withUpdate);
     },
     [onInstrumentChange],
   );
@@ -172,7 +211,7 @@ export default function useChartComponentFacade(props: ChartContainerProps): Use
         const issKey = (message as Record<number, string>)[widgetId];
 
         if (issKey) {
-          addInstrumentFromModal([{ issKey }]);
+          onInstrumentChange(issKey);
         }
       },
     );
@@ -194,12 +233,14 @@ export default function useChartComponentFacade(props: ChartContainerProps): Use
       unsubscribe();
       unsubscribeHighlighter();
     };
-  }, [addInstrumentFromModal, widgetId]);
+  }, [onInstrumentChange, widgetId]);
 
   return {
     dropDownOpen,
     setDropdownOpen,
     currentInstrument,
+    currentInstrumentName,
+    currentInstrumentTicker,
     isWidgetHeaderContextMenuOpen,
     setIsWidgetHeaderContextMenuOpen,
     onDropInstruments,
diff --git a/src/widgets/Chart/hooks/useChartPublicContext.ts b/src/widgets/Chart/hooks/useChartPublicContext.ts
index 0edfadcae..e8999c74d 100644
--- a/src/widgets/Chart/hooks/useChartPublicContext.ts
+++ b/src/widgets/Chart/hooks/useChartPublicContext.ts
@@ -8,7 +8,7 @@ import { DEFAULT_SYMBOL } from '@widgets/Chart/const';
 import { Widget } from 'types/Widgets';
 
 interface UseChartPublicContextArg {
-  setCurrInstrument: (instrumentId: string) => void;
+  setCurrInstrument: (instrumentId: string, instrumentName?: string, instrumentTicker?: string) => void;
   widgetId: Widget['id'];
   getMasterInstrumentFromPublicContext: () => string | number | null | undefined;
 }
@@ -39,12 +39,13 @@ export function useChartPublicContext({
   useEffect(() => {
     const fieldValue = getMasterInstrumentFromPublicContext();
     if (!fieldValue && widget?.master) {
-      setCurrInstrument(DEFAULT_SYMBOL);
+      const defaultInstrument = instruments.find((item) => item.issKey === DEFAULT_SYMBOL);
+      setCurrInstrument(DEFAULT_SYMBOL, defaultInstrument?.displayName, defaultInstrument?.symbol);
       return;
     }
-    const instrKey = instruments.find((item) => item.issKey === fieldValue)?.issKey;
-    if (instrKey) {
-      setCurrInstrument(instrKey);
+    const instrument = instruments.find((item) => item.issKey === fieldValue);
+    if (instrument?.issKey) {
+      setCurrInstrument(instrument.issKey, instrument.displayName, instrument.symbol);
     }
     // eslint-disable-next-line react-hooks/exhaustive-deps -- Посмотреть этот момент.
   }, [publicContext, instruments, widget?.externalProperties, setCurrInstrument]);
diff --git a/src/widgets/Chart/index.tsx b/src/widgets/Chart/index.tsx
index e38b1552a..f6175b50a 100644
--- a/src/widgets/Chart/index.tsx
+++ b/src/widgets/Chart/index.tsx
@@ -22,6 +22,8 @@ export const Chart: FC<ChartContainerProps> = function (props): JSX.Element {
     dropDownOpen,
     setDropdownOpen,
     currentInstrument,
+    currentInstrumentName,
+    currentInstrumentTicker,
     setIsWidgetHeaderContextMenuOpen,
     isWidgetHeaderContextMenuOpen,
     onDropInstruments,
@@ -49,7 +51,8 @@ export const Chart: FC<ChartContainerProps> = function (props): JSX.Element {
 
   const instrumentName = indicativeData
     ? `${indicativeData.instrumentName} ${indicativeData.settlement} - ${indicativeData.firmName}`
-    : contractsInstrumentName;
+    : currentInstrumentName || contractsInstrumentName || currentInstrument;
+  const instrumentTicker = indicativeData ? indicativeData.secId : currentInstrumentTicker || currentInstrument;
 
   return (
     <DNDWrapper
@@ -78,7 +81,9 @@ export const Chart: FC<ChartContainerProps> = function (props): JSX.Element {
           }}
         >
           <MoexChart
-            fullName={currentInstrument}
+            symbol={currentInstrument}
+            symbolName={instrumentName}
+            symbolTicker={instrumentTicker}
             indicativeData={indicativeData}
             widgetId={props.widgetId}
             addInstrumentFromModal={addInstrumentFromModal}
diff --git a/src/widgets/Chart/properties/types.ts b/src/widgets/Chart/properties/types.ts
index afdbdf473..3a5cc442d 100644
--- a/src/widgets/Chart/properties/types.ts
+++ b/src/widgets/Chart/properties/types.ts
@@ -2,11 +2,15 @@ import { Contract } from '@modules/contracts/types';
 
 import { ChartIndicativeData } from '../types';
 
+import type { WidgetProperties as BaseWidgetProperties } from '@modules/widgetProperties/types';
+
 import type { Timeframes } from 'moex-chart';
 
-export type WidgetProperties = {
+export interface WidgetProperties extends BaseWidgetProperties {
   chartState: {
     savedInstrument: Contract['issKey'] | null;
+    savedInstrumentName?: string;
+    savedInstrumentTicker?: string;
     interval: string;
     savedData?: string;
   };
@@ -15,4 +19,4 @@ export type WidgetProperties = {
     timeframe: Timeframes;
     savedData?: string;
   };
-};
+}
diff --git a/src/widgets/Chart/types.ts b/src/widgets/Chart/types.ts
index bbc0155d5..519b47289 100644
--- a/src/widgets/Chart/types.ts
+++ b/src/widgets/Chart/types.ts
@@ -1,12 +1,22 @@
-import type { WidgetContentBasicProps } from 'types/Widgets';
+import { Contract } from '@modules/contracts';
+
 import type { WidgetProperties } from './properties/types';
 
-export type ChartIndicativeData = {
+import type { WidgetProperties as BaseWidgetProperties } from '@modules/widgetProperties/types';
+import type { WidgetContentBasicProps } from 'types/Widgets';
+
+export interface ChartIndicativeData extends BaseWidgetProperties {
   secId: string;
   instrumentName: string;
   settlement: string;
   firmName: string;
   key: string;
-};
+}
+
+export interface SelectedInstrument {
+  issKey: Contract['issKey'];
+  displayName: Contract['displayName'];
+  symbol: Contract['symbol'];
+}
 
 export type ChartContainerProps = WidgetContentBasicProps<WidgetProperties>;