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


diff --git a/src/components/InstrumentSearch/components/Wrapper/index.tsx b/src/components/InstrumentSearch/components/Wrapper/index.tsx
index b4ffc3827b1b020b1a6097d23c45576c77ed3043..1ee12b805534f162d71da020d8fd7144f7165730 100644
--- a/src/components/InstrumentSearch/components/Wrapper/index.tsx
+++ b/src/components/InstrumentSearch/components/Wrapper/index.tsx
@@ -10,13 +10,14 @@ type IWrapper = {
   children: ReactNode;
   setOpen: (b: boolean) => void;
   isOpen: boolean;
+  headerName?: string;
 };
 /** Компонент обёртка для модального окна  */
-export const Wrapper: React.FC<IWrapper> = ({ children, setOpen, isOpen }) => (
+export const Wrapper: React.FC<IWrapper> = ({ children, setOpen, isOpen, headerName }) => (
   <Modal isOpen={isOpen}>
     <div className={styles.modalWrapper}>
       <header>
-        <TextInput>Поиск инструментов</TextInput>
+        <TextInput>{ headerName ?? 'Поиск инструментов' }</TextInput>
         <div
           onClick={() => {
             setOpen(false);
diff --git a/src/components/InstrumentSearch/search.tsx b/src/components/InstrumentSearch/search.tsx
index c142a84b776ca2a529c68a19529b69e8450f598a..aed5205283aa8fe41eb1a85ce31a004236d6e85a 100644
--- a/src/components/InstrumentSearch/search.tsx
+++ b/src/components/InstrumentSearch/search.tsx
@@ -14,6 +14,7 @@ export const InstrumentSearch: FC<InstrumentSearchType> = ({
   addInstruments,
   customActionsFooterHandlers,
   isNewScaleDisabled,
+  headerName,
 }) => {
   const { filters, actions, table } = useData(variant, setOpen, addInstruments);
   const tableProps = { ...table, widgetId };
@@ -22,6 +23,7 @@ export const InstrumentSearch: FC<InstrumentSearchType> = ({
     <Wrapper
       setOpen={setOpen}
       isOpen={isOpen}
+      headerName={headerName}
     >
       <SearchInput {...filters} />
       <Filters {...filters} />
diff --git a/src/components/InstrumentSearch/types/hooks.ts b/src/components/InstrumentSearch/types/hooks.ts
index aa7d44a7ceb4efd3eb4f2c64608f7db2634ae8ab..aa6057d6535678ec67392e426c2fddfc04d5287e 100644
--- a/src/components/InstrumentSearch/types/hooks.ts
+++ b/src/components/InstrumentSearch/types/hooks.ts
@@ -80,6 +80,7 @@ export type InstrumentSearchType = {
     handleNewPanel: (instrument: Contract) => void;
   };
   isNewScaleDisabled?: boolean;
+  headerName?: string;
 };
 
 export type InstrumentType = Record<AllFiltersVariants, string | number | null | boolean>;
diff --git a/src/widgets/Chart/__tests__/dataSourceProvide.test.ts b/src/widgets/Chart/__tests__/dataSourceProvide.test.ts
index 2bbe173ff5427a9f87f8aad867603f29a5641f83..878e00daa82fa129c9b0ebd41f7cd646ce86de46 100644
--- a/src/widgets/Chart/__tests__/dataSourceProvide.test.ts
+++ b/src/widgets/Chart/__tests__/dataSourceProvide.test.ts
@@ -85,6 +85,7 @@ const runRealtimeTick = async (): Promise<void> => {
   await flushPromises();
 };
 
+
 describe('DataSourceProvider', () => {
   beforeEach(() => {
     jest.clearAllMocks();
@@ -204,26 +205,6 @@ describe('DataSourceProvider', () => {
     );
   });
 
-  it('should not request history data for default symbol', async () => {
-    const provider = new DataSourceProvider();
-    const dataSource = provider.getDataSource();
-
-    const result = await dataSource(Timeframes['1m'], DEFAULT_SYMBOL);
-
-    expect(result).toBeNull();
-    expect(mockRequestBars).not.toHaveBeenCalled();
-  });
-
-  it('should not request history data for default symbol with surrounding spaces', async () => {
-    const provider = new DataSourceProvider();
-    const dataSource = provider.getDataSource();
-
-    const result = await dataSource(Timeframes['1m'], `  ${DEFAULT_SYMBOL}  `);
-
-    expect(result).toBeNull();
-    expect(mockRequestBars).not.toHaveBeenCalled();
-  });
-
   it('should not request history data for empty symbol', async () => {
     const provider = new DataSourceProvider();
     const dataSource = provider.getDataSource();
@@ -589,6 +570,37 @@ describe('DataSourceProvider', () => {
     expect(mockUpdate).toHaveBeenCalledWith('MOEX:LKOH', realtimeCandle);
   });
 
+   it('loaded data for default symbol should be valid', async () => {
+    const firstStart = Math.floor(Date.parse('2026-07-30T04:00:00Z') / 1000);
+    const secondStart = Math.floor(Date.parse('2026-07-31T04:00:00Z') / 1000);
+
+    const historyData = [
+      ...Array.from({ length: 15 }, (_, hour) => createHourBar(firstStart, hour)),
+      ...Array.from({ length: 14 }, (_, hour) => createHourBar(secondStart, hour)),
+    ];
+
+    mockRequestBars.mockResolvedValue(historyData);
+
+    const provider = new DataSourceProvider();
+    const dataSource = provider.getDataSource();
+
+    const result = await dataSource(Timeframes['4h'], DEFAULT_SYMBOL.symbolId);
+
+    expect(result?.map(({ time }) => new Date(time * 1000).toISOString())).toEqual([
+      "2026-07-30T04:00:00.000Z",
+      "2026-07-30T08:00:00.000Z",
+      "2026-07-30T12:00:00.000Z",
+      "2026-07-30T16:00:00.000Z",
+      "2026-07-31T04:00:00.000Z",
+      "2026-07-31T08:00:00.000Z",
+      "2026-07-31T12:00:00.000Z",
+      "2026-07-31T16:00:00.000Z",
+    ]);
+
+    expect(result?.length).toBeGreaterThan(7);
+    expect(mockRequestBars).toHaveBeenCalled();
+  });
+
   it('should reset realtime convolution for a new trading session', async () => {
     const sessionStart = Math.floor(Date.parse('2026-07-30T03:00:00Z') / 1000);
     const historyData = Array.from({ length: 18 }, (_, hour) => createHourBar(sessionStart, hour));
@@ -654,25 +666,6 @@ describe('DataSourceProvider', () => {
     expect(mockUpdate).toHaveBeenCalledWith('moex:sber', mockBar);
   });
 
-  it('should not request realtime data for default symbol', async () => {
-    const provider = new DataSourceProvider();
-    const mockUpdate = jest.fn();
-
-    const unsubscribe = provider.startRealtime({
-      getSymbols: () => [DEFAULT_SYMBOL],
-      getTimeframe: () => Timeframes['1m'],
-      update: mockUpdate,
-      periodMs: 1000,
-    });
-
-    await runRealtimeTick();
-
-    unsubscribe();
-
-    expect(mockRequestRealtimeBars).not.toHaveBeenCalled();
-    expect(mockUpdate).not.toHaveBeenCalled();
-  });
-
   it('should not request realtime data for empty symbol', async () => {
     const provider = new DataSourceProvider();
 
diff --git a/src/widgets/Chart/__tests__/useChartPublicContext.test.tsx b/src/widgets/Chart/__tests__/useChartPublicContext.test.tsx
index 0b0f2057249d256e59239aeba58b38632c27e8f9..2b9711f0c62a608eb2112bcac73e7e776d8a4b2e 100644
--- a/src/widgets/Chart/__tests__/useChartPublicContext.test.tsx
+++ b/src/widgets/Chart/__tests__/useChartPublicContext.test.tsx
@@ -39,7 +39,7 @@ describe('useChartPublicContext', () => {
 
   const mockContracts = [
     {
-      issKey: DEFAULT_SYMBOL,
+      issKey: DEFAULT_SYMBOL.symbolId,
       displayName: 'Инструмент по умолчанию',
       symbol: 'DEFAULT',
       // ... other contract properties
@@ -219,11 +219,7 @@ describe('useChartPublicContext', () => {
 
     // Assert
     expect(mockSetCurrInstrument).toHaveBeenCalledTimes(1);
-    expect(mockSetCurrInstrument).toHaveBeenCalledWith({
-      symbolId: DEFAULT_SYMBOL,
-      symbol: 'DEFAULT',
-      symbolName: 'Инструмент по умолчанию',
-    });
+    expect(mockSetCurrInstrument).toHaveBeenCalledWith(DEFAULT_SYMBOL);
   });
 
   it('should resolve instrument when widget is not found', () => {
diff --git a/src/widgets/Chart/components/MoexChart/components/CompareModal.tsx b/src/widgets/Chart/components/MoexChart/components/CompareModal.tsx
index 6f48a72671648ca543bca0daaaabee539c5cdc91..98ffd54a8ffe7c3a11bb8b4f926ff61eea2c8be6 100644
--- a/src/widgets/Chart/components/MoexChart/components/CompareModal.tsx
+++ b/src/widgets/Chart/components/MoexChart/components/CompareModal.tsx
@@ -82,6 +82,7 @@ export const CompareModal = ({ compareManager, widgetId, isOpen, setOpen }: Comp
         handleNewScale,
         handleNewPanel,
       }}
+      headerName='Сравнить инструменты'
     />
   );
 };
diff --git a/src/widgets/Chart/components/MoexChart/dataSourceProvide.ts b/src/widgets/Chart/components/MoexChart/dataSourceProvide.ts
index b27ddb1faa10bc5584fc85c8e08415fc06e1d2f8..3ca863b40b5a5130537a4dbecac3b35c90efe8b0 100644
--- a/src/widgets/Chart/components/MoexChart/dataSourceProvide.ts
+++ b/src/widgets/Chart/components/MoexChart/dataSourceProvide.ts
@@ -3,7 +3,6 @@ import dayjs from 'dayjs';
 import duration from 'dayjs/plugin/duration';
 
 import { moexChartTimeConverter, moexChartToIssTimeframe } from '@utils/chartToReqTimeConverter';
-import { DEFAULT_SYMBOL } from '@widgets/Chart/const';
 
 import { requestBars, requestRealtimeBars } from '../../requestBars';
 import { ChartIndicativeData } from '../../types';
@@ -21,7 +20,7 @@ interface RealtimeState {
 function getRequestSymbol(symbolRaw?: string): string | undefined {
   const symbol = String(symbolRaw ?? '').trim();
 
-  if (!symbol || symbol === DEFAULT_SYMBOL) {
+  if (!symbol) {
     return undefined;
   }
 
diff --git a/src/widgets/Chart/const.ts b/src/widgets/Chart/const.ts
index e7bfbfac9c95acf7c2e8e3713f79d94c08f72dfa..eeffc33b560c98dc0e7b320cccae8c0cb8b2ce98 100644
--- a/src/widgets/Chart/const.ts
+++ b/src/widgets/Chart/const.ts
@@ -1 +1,5 @@
-export const DEFAULT_SYMBOL = '';
+export const DEFAULT_SYMBOL = {
+    symbolId: 'INDICES:SNDX:IMOEX',
+    symbol: "IMOEX",
+    symbolName: "Индекс МосБиржи"
+};
diff --git a/src/widgets/Chart/hooks/useChartComponentFacade.tsx b/src/widgets/Chart/hooks/useChartComponentFacade.tsx
index 9fd119f338c3712b8ae09525d4448beb7a60bfd7..4213d5932b3cf8d3316e48d68bad0e62e88a3a63 100644
--- a/src/widgets/Chart/hooks/useChartComponentFacade.tsx
+++ b/src/widgets/Chart/hooks/useChartComponentFacade.tsx
@@ -17,7 +17,7 @@ import type { WidgetProperties } from '../properties/types';
 import type { ChartContainerProps, SelectedInstrument } from '../types';
 
 import type { SymbolInfoInput } from '@mx-charts/charts';
-import type { Dispatch, SetStateAction } from 'react';
+import type { Dispatch, MutableRefObject, SetStateAction } from 'react';
 import type { Widget } from 'types/Widgets';
 
 interface UseChartComponentFacadeReturn {
@@ -36,67 +36,23 @@ type SaveContentPropsOptions = Partial<WidgetProperties['chartState']> & {
   cleanIndicativeData?: boolean;
 };
 
-export default function useChartComponentFacade(props: ChartContainerProps): UseChartComponentFacadeReturn {
-  const { widgetId } = props;
-
+function useInstrumentChange({
+  saveContentProps, 
+  setIsWidgetHeaderContextMenuOpen, 
+  currentSymbolInfoRef, 
+  setCurrentSymbolInfo, 
+  triggerRelatedWidgetsToUpdateRef, 
+  widgetId
+}: {
+  saveContentProps: (value: SaveContentPropsOptions) => void, 
+  setIsWidgetHeaderContextMenuOpen: (value: boolean) => void, 
+  currentSymbolInfoRef: MutableRefObject<SymbolInfoInput>, 
+  setCurrentSymbolInfo: (value: SymbolInfoInput) => void, 
+  triggerRelatedWidgetsToUpdateRef: MutableRefObject<(key: string) => void>, 
+  widgetId: number
+}) {
   const dispatch = useDispatch();
-
-  const [isOver, setIsOver] = useState(false);
-  const [dropDownOpen, setDropdownOpen] = useState(false);
-  const [isWidgetHeaderContextMenuOpen, setIsWidgetHeaderContextMenuOpen] = useState(false);
-
-  const widgetProperties = useAppSelect(
-    (state) => state.widgets.widgets.find(({ id }) => id === widgetId)?.widgetContentProps,
-  ) as WidgetProperties | undefined;
-
-  // TODO временно реф из-за непредсказуемого изменения если используется useState,
-  // нужен рефакторинг и вернуть обратно useState
-  const currentSymbolInfoRef = useRef<SymbolInfoInput>({
-    symbolId: widgetProperties?.chartState?.savedInstrument ?? DEFAULT_SYMBOL,
-    symbol: widgetProperties?.chartState?.savedSymbol,
-    symbolName: widgetProperties?.chartState?.savedSymbolName,
-  });
-
-  const [currentSymbolInfo, setCurrentSymbolInfo] = useState<SymbolInfoInput>(currentSymbolInfoRef.current);
-
-  const { triggerRelatedWidgetsToUpdate, getMasterInstrumentFromPublicContext } = useWidgetsBind({
-    widgetId,
-  });
-
-  const triggerRelatedWidgetsToUpdateRef = useRef(triggerRelatedWidgetsToUpdate);
-
-  useEffect(() => {
-    triggerRelatedWidgetsToUpdateRef.current = triggerRelatedWidgetsToUpdate;
-  }, [triggerRelatedWidgetsToUpdate]);
-
-  const saveContentProps = useCallback(
-    (value: SaveContentPropsOptions): void => {
-      const { withUpdate, cleanIndicativeData, ...chartStateValue } = value;
-
-      const oldProps = (getStore().getState().widgets.widgets.find(({ id }) => id === widgetId) as Widget | undefined)
-        ?.widgetContentProps;
-
-      if (!oldProps) {
-        return;
-      }
-
-      dispatch(
-        addContentPropsToWidget({
-          id: widgetId,
-          withoutSend: !withUpdate,
-          widgetContentProps: {
-            chartState: {
-              ...oldProps.chartState,
-              ...chartStateValue,
-            },
-            // при смене инструмента очищаем индикативные данные, переданные из виджета Индикативные котировки
-            indicativeData: cleanIndicativeData ? undefined : oldProps.indicativeData,
-          },
-        }),
-      );
-    },
-    [dispatch, widgetId],
-  );
+  const currentSymbolInfoRefCrunch = currentSymbolInfoRef
 
   const onInstrumentChange = useCallback(
     (symbolInfo: SymbolInfoInput, withUpdate?: boolean, unbind = true): void => {
@@ -108,7 +64,7 @@ export default function useChartComponentFacade(props: ChartContainerProps): Use
 
       setIsWidgetHeaderContextMenuOpen(false);
 
-      const currentValue = currentSymbolInfoRef.current;
+      const currentValue = currentSymbolInfoRefCrunch.current;
       const symbolIdChanged = currentValue.symbolId !== symbolId;
 
       const symbol = symbolInfo.symbol?.trim() || (symbolIdChanged ? undefined : currentValue.symbol);
@@ -127,7 +83,7 @@ export default function useChartComponentFacade(props: ChartContainerProps): Use
         return;
       }
 
-      currentSymbolInfoRef.current = nextSymbolInfo;
+      currentSymbolInfoRefCrunch.current = nextSymbolInfo;
       setCurrentSymbolInfo(nextSymbolInfo);
 
       if (symbolIdChanged && unbind) {
@@ -144,14 +100,14 @@ export default function useChartComponentFacade(props: ChartContainerProps): Use
         cleanIndicativeData: symbolIdChanged,
       });
 
-      if (symbolIdChanged) {
-        triggerRelatedWidgetsToUpdateRef.current(symbolId);
-      }
-    },
-    [dispatch, saveContentProps, widgetId],
-  );
+        if (symbolIdChanged) {
+          triggerRelatedWidgetsToUpdateRef.current(symbolId);
+        }
+      },
+      [dispatch, saveContentProps, widgetId, currentSymbolInfoRefCrunch, setCurrentSymbolInfo, setIsWidgetHeaderContextMenuOpen, triggerRelatedWidgetsToUpdateRef ],
+    );
 
-  const addInstrumentFromModal = useCallback(
+   const addInstrumentFromModal = useCallback(
     (instruments: SelectedInstrument[]): void => {
       const instrument = instruments[0];
 
@@ -182,6 +138,86 @@ export default function useChartComponentFacade(props: ChartContainerProps): Use
     [onInstrumentChange],
   );
 
+  return {addInstrumentFromModal, onInstrumentChangeFromBind, onDropInstrument, onInstrumentChange}
+}
+
+export default function useChartComponentFacade(props: ChartContainerProps): UseChartComponentFacadeReturn {
+  const { widgetId } = props;
+
+  const dispatch = useDispatch();
+
+  const [isOver, setIsOver] = useState(false);
+  const [dropDownOpen, setDropdownOpen] = useState(false);
+  const [isWidgetHeaderContextMenuOpen, setIsWidgetHeaderContextMenuOpen] = useState(false);
+
+  const widgetProperties = useAppSelect(
+    (state) => state.widgets.widgets.find(({ id }) => id === widgetId)?.widgetContentProps,
+  ) as WidgetProperties | undefined;
+
+  // TODO временно реф из-за непредсказуемого изменения если используется useState,
+  // нужен рефакторинг и вернуть обратно useState
+  const currentSymbolInfoRef = useRef<SymbolInfoInput>(DEFAULT_SYMBOL)
+
+  const savedInstrument = widgetProperties?.chartState?.savedInstrument
+
+  if(savedInstrument){
+    currentSymbolInfoRef.current = {
+      symbolId: savedInstrument,
+      symbol: widgetProperties?.chartState?.savedSymbol,
+      symbolName: widgetProperties?.chartState?.savedSymbolName,
+    };
+  }
+
+  const [currentSymbolInfo, setCurrentSymbolInfo] = useState<SymbolInfoInput>(currentSymbolInfoRef.current);
+
+  const { triggerRelatedWidgetsToUpdate, getMasterInstrumentFromPublicContext } = useWidgetsBind({
+    widgetId,
+  });
+
+  const triggerRelatedWidgetsToUpdateRef = useRef(triggerRelatedWidgetsToUpdate);
+
+  useEffect(() => {
+    triggerRelatedWidgetsToUpdateRef.current = triggerRelatedWidgetsToUpdate;
+  }, [triggerRelatedWidgetsToUpdate]);
+
+  const saveContentProps = useCallback(
+    (value: SaveContentPropsOptions): void => {
+      const { withUpdate, cleanIndicativeData, ...chartStateValue } = value;
+
+      const oldProps = (getStore().getState().widgets.widgets.find(({ id }) => id === widgetId) as Widget | undefined)
+        ?.widgetContentProps;
+
+      if (!oldProps) {
+        return;
+      }
+
+      dispatch(
+        addContentPropsToWidget({
+          id: widgetId,
+          withoutSend: !withUpdate,
+          widgetContentProps: {
+            chartState: {
+              ...oldProps.chartState,
+              ...chartStateValue,
+            },
+            // при смене инструмента очищаем индикативные данные, переданные из виджета Индикативные котировки
+            indicativeData: cleanIndicativeData ? undefined : oldProps.indicativeData,
+          },
+        }),
+      );
+    },
+    [dispatch, widgetId],
+  );
+
+  const { addInstrumentFromModal, onInstrumentChangeFromBind, onDropInstrument, onInstrumentChange } = useInstrumentChange({
+    saveContentProps, 
+    setIsWidgetHeaderContextMenuOpen, 
+    currentSymbolInfoRef, 
+    setCurrentSymbolInfo,
+    triggerRelatedWidgetsToUpdateRef, 
+    widgetId
+  })
+
   useChartPublicContext({
     setCurrInstrument: onInstrumentChangeFromBind,
     widgetId,
diff --git a/src/widgets/Chart/hooks/useChartPublicContext.ts b/src/widgets/Chart/hooks/useChartPublicContext.ts
index 6a71316e05c1e8a0d0cc7ab261306a3c5e165490..a50be700ce7b824360688d648ff5f1a792a9b15c 100644
--- a/src/widgets/Chart/hooks/useChartPublicContext.ts
+++ b/src/widgets/Chart/hooks/useChartPublicContext.ts
@@ -42,13 +42,7 @@ export function useChartPublicContext({
     const fieldValue = getMasterInstrumentFromPublicContext();
 
     if (!fieldValue && widget?.master) {
-      const defaultInstrument = instruments.find((instrument) => instrument.issKey === DEFAULT_SYMBOL);
-
-      setCurrInstrument({
-        symbolId: DEFAULT_SYMBOL,
-        symbol: defaultInstrument?.symbol ?? undefined,
-        symbolName: defaultInstrument?.displayName ?? undefined,
-      });
+      setCurrInstrument(DEFAULT_SYMBOL);
 
       return;
     }