Загрузка данных
import { DrawingsManagerSnapshot } from '../core/DrawingsManager';
import { ChartSeriesType, DateFormat, IndicatorsIds, Intervals, Timeframes } from '..';
import { Direction } from '../types/chart';
import { SymbolInfo, SymbolInfoInput } from '../types/symbol';
import { TimeFormat } from '../types/timeScale';
import { SettingsValues } from './settings';
import type { PriceScaleMode } from 'lightweight-charts';
export type PriceScaleSide = Direction.Left | Direction.Right;
export interface ISerializable<T extends object> {
getSnapshot: () => T;
}
export interface InitialSnapshot extends SymbolInfoInput {
timeframe: Timeframes;
chartSeriesType: ChartSeriesType;
}
export interface MoexChartSnapshotInput {
charts: ChartSnapshotInput[];
}
export interface MoexChartSnapshot {
charts: ChartSnapshot[];
}
interface ChartSnapshotBase {
timeframe: Timeframes;
chartSeriesType: ChartSeriesType;
timeFormat?: TimeFormat;
dateFormat?: DateFormat;
interval?: Intervals | null;
panes: PaneSnapshot[];
}
export interface ChartSnapshotInput extends ChartSnapshotBase, SymbolInfoInput {
}
export interface ChartSnapshot extends ChartSnapshotBase, SymbolInfo {
}
export interface PriceScaleSnapshot {
side: PriceScaleSide;
mode: PriceScaleMode;
}
export interface PaneSnapshot {
isMain: boolean;
id: number;
indicators: (IndicatorSnapshot | CompareSnapshot)[];
drawings: DrawingsManagerSnapshot;
priceScales?: PriceScaleSnapshot[];
}
export interface CompareSnapshot extends Partial<DOMObjectSnapshot> {
symbolInfo: SymbolInfoInput;
seriesName: ChartSeriesType;
scale: Direction;
}
export interface IndicatorSnapshot extends Partial<DOMObjectSnapshot> {
indicatorType: IndicatorsIds;
settings?: SettingsValues;
}
export interface DOMObjectSnapshot {
id: string;
name: string;
zIndex: number;
hidden: boolean;
paneId: number;
}
export {};
import { DateFormat, IndicatorsIds, Locale, Timeframes } from 'moex-chart';
import type { ChartCollectionPreset, IMoexChart, MoexChartSnapshotInput } from 'moex-chart';
type ChartCollectionPresetConfig = Omit<ChartCollectionPreset, 'getDataSource' | 'startRealtime'>;
type ChartSnapshotConfig = Omit<MoexChartSnapshotInput['charts'][number], 'symbolId'>;
type MoexChartSnapshotConfig = Omit<MoexChartSnapshotInput, 'charts'> & {
charts: ChartSnapshotConfig[];
};
type MoexChartConfig = Omit<IMoexChart, 'container' | 'chartCollectionPreset' | 'snapshot'> & {
snapshot: MoexChartSnapshotConfig;
chartCollectionPreset: ChartCollectionPresetConfig;
};
const MOEX_CHART_CONFIG: MoexChartConfig = {
snapshot: {
charts: [
{
chartSeriesType: 'Candlestick',
timeframe: Timeframes['10s'],
timeFormat: '24h',
dateFormat: DateFormat.DD_MM_YYYY_HH_mm_ss,
panes: [
{
isMain: true,
id: 0,
indicators: [
{
indicatorType: IndicatorsIds.Volume,
},
],
drawings: [],
},
],
},
],
},
chartCollectionPreset: {
undoRedoEnabled: true,
showMenuButton: true,
showBottomPanel: true,
showControlBar: true,
showFullscreenButton: true,
showSettingsButton: true,
showCompareButton: true,
tooltipConfig: {
showTooltip: false,
time: { visible: true, label: 'Время' },
close: { visible: true, label: 'Закр.' },
change: { visible: true, label: 'Изм.' },
volume: { visible: true, label: 'Объем' },
open: { visible: true, label: 'Откр.' },
high: { visible: true, label: 'Макс.' },
low: { visible: true, label: 'Мин.' },
},
supportedTimeframes: [
Timeframes['1m'],
Timeframes['5m'],
Timeframes['10m'],
Timeframes['15m'],
Timeframes['30m'],
Timeframes['45m'],
Timeframes['1h'],
Timeframes['2h'],
Timeframes['3h'],
Timeframes['4h'],
Timeframes['1d'],
Timeframes['1w'],
Timeframes['1М'],
],
supportedChartSeriesTypes: ['Candlestick', 'Line', 'Bar'],
theme: 'tr',
ohlc: {
show: true,
precision: 4,
},
mode: 'dark',
locale: Locale.rus,
},
lwcInheritedChartOptions: {
timeVisible: true,
secondsVisible: false,
},
};
export { MOEX_CHART_CONFIG };
import { Intervals, MoexChart, Timeframes } from 'moex-chart';
import { useCallback, 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 { MOEX_CHART_CONFIG } from '../constants';
import { DataSourceProvider } from '../dataSourceProvide';
import type { __CompareManager__, IMoexChart, SymbolInfoInput } from 'moex-chart';
interface TUseMoexChartProps {
symbolInfo: SymbolInfoInput;
indicativeData?: ChartIndicativeData;
}
export const useMoexChart = ({ symbolInfo, indicativeData }: TUseMoexChartProps) => {
const moexChartState = useSelectProperties(
(widgetProperties: Partial<WidgetProperties>) => widgetProperties.moexChartState,
);
const { updateProperties } = useChangeProperties<WidgetProperties>();
const [isCompareOpen, setIsCompareOpen] = useState(false);
const [isSymbolSearchOpen, setIsSymbolSearchOpen] = useState(false);
const containerRef = useRef<HTMLDivElement | null>(null);
const chartRef = useRef<MoexChart | null>(null);
const compareManagerRef = useRef<__CompareManager__ | null>(null);
const currentSymbolInfoRef = useRef<SymbolInfoInput>(symbolInfo);
const timeframeRef = useRef<Timeframes | undefined>(moexChartState?.timeframe);
const savedDataRef = useRef<string | undefined>(moexChartState?.savedData);
const initialIntervalRef = useRef<Intervals | undefined>(moexChartState?.initialInterval);
const updateTimeframeRef = useRef<((timeframe: Timeframes) => void) | null>(null);
useEffect(() => {
const currentSymbolInfo = currentSymbolInfoRef.current;
if (
currentSymbolInfo.symbolId === symbolInfo.symbolId &&
currentSymbolInfo.symbol === symbolInfo.symbol &&
currentSymbolInfo.symbolName === symbolInfo.symbolName
) {
return;
}
currentSymbolInfoRef.current = symbolInfo;
chartRef.current?.setSymbol(symbolInfo);
}, [symbolInfo]);
useEffect(() => {
timeframeRef.current = moexChartState?.timeframe;
savedDataRef.current = moexChartState?.savedData;
}, [moexChartState?.savedData, moexChartState?.timeframe]);
updateTimeframeRef.current = (timeframe: Timeframes): void => {
if (timeframeRef.current === timeframe) {
return;
}
timeframeRef.current = timeframe;
updateProperties((state) => {
state.moexChartState = {
...state.moexChartState,
timeframe,
};
});
};
const saveSnapshot = useCallback((): void => {
const snapshot = chartRef.current?.getSnapshot();
if (!snapshot) {
return;
}
const savedData = JSON.stringify(snapshot);
if (savedDataRef.current === savedData) {
return;
}
savedDataRef.current = savedData;
updateProperties((state) => {
state.moexChartState = {
...state.moexChartState,
timeframe: timeframeRef.current ?? Timeframes['1m'],
savedData,
};
});
}, [updateProperties]);
const getSnapshotWithCurrentSettings = (
snapshot: typeof MOEX_CHART_CONFIG.snapshot | IMoexChart['snapshot'],
timeframe: Timeframes,
initialInterval?: Intervals,
): IMoexChart['snapshot'] => ({
...snapshot,
charts: snapshot.charts.map((chartSnapshot) => ({
...chartSnapshot,
...currentSymbolInfoRef.current,
timeframe,
...(initialInterval ? { interval: initialInterval } : {}),
})),
});
const applySnapshot = (): void => {
if (!savedDataRef.current || !chartRef.current) {
return;
}
const savedSnapshot = JSON.parse(savedDataRef.current) as IMoexChart['snapshot'];
const timeframe = timeframeRef.current ?? Timeframes['1m'];
chartRef.current.setSnapshot(getSnapshotWithCurrentSettings(savedSnapshot, timeframe));
compareManagerRef.current = chartRef.current.getCompareManager();
};
useEffect(() => {
const container = containerRef.current;
if (!container) {
return undefined;
}
const timeframe = timeframeRef.current ?? Timeframes['1m'];
const initialInterval = initialIntervalRef.current;
const savedSnapshot = savedDataRef.current
? (JSON.parse(savedDataRef.current) as IMoexChart['snapshot'])
: MOEX_CHART_CONFIG.snapshot;
const dataProvider = new DataSourceProvider();
const chart = new MoexChart({
...MOEX_CHART_CONFIG,
container,
snapshot: getSnapshotWithCurrentSettings(savedSnapshot, timeframe, initialInterval),
chartCollectionPreset: {
...MOEX_CHART_CONFIG.chartCollectionPreset,
openCompareModal: () => setIsCompareOpen(true),
openSymbolSearchModal: () => setIsSymbolSearchOpen(true),
getDataSource: dataProvider.getDataSource(indicativeData, (nextTimeframe) => {
updateTimeframeRef.current?.(nextTimeframe);
}),
startRealtime: (getSymbols, getTimeframe, update) =>
dataProvider.startRealtime({
getSymbols,
getTimeframe,
update,
indicativeData,
}),
},
});
chartRef.current = chart;
compareManagerRef.current = chart.getCompareManager();
if (initialInterval) {
initialIntervalRef.current = undefined;
updateProperties((state) => {
state.moexChartState = {
...state.moexChartState,
initialInterval: undefined,
};
});
}
const intervalId = setInterval(() => {
saveSnapshot();
}, 1000);
return () => {
clearInterval(intervalId);
saveSnapshot();
chartRef.current = null;
compareManagerRef.current = null;
chart.destroy();
};
// eslint-disable-next-line react-hooks/exhaustive-deps -- график пересоздаётся только при изменении источника индикативных данных
}, [indicativeData?.key]);
return {
containerRef,
isCompareOpen,
isSymbolSearchOpen,
compareManagerRef,
setIsCompareOpen,
setIsSymbolSearchOpen,
saveSnapshot,
applySnapshot,
hasSavedSnapshot: Boolean(moexChartState?.savedData),
};
};
import { DataSourceParams } from './DataSource';
import { CompareManager } from '../core/CompareManager';
import { ThemeKey, ThemeMode } from '../theme/types';
import { Locale } from '../translations';
import { Candle, ChartSeriesType, ChartTypeOptions, OHLCConfig, SymbolInfoInput, TooltipConfig } from '../types';
import { ISerializable, MoexChartSnapshot, MoexChartSnapshotInput } from '../types/snapshot';
import { Timeframes } from '../types/timeframes';
import { ChartSettings, ChartSettingsSource } from './ChartSettings';
import 'exchange-elements/dist/fonts/inter/font.css';
import 'exchange-elements/dist/style.css';
import 'exchange-elements/dist/tokens/moex.css';
import '../styles/global.scss';
export interface ChartCollectionPreset {
undoRedoEnabled?: boolean;
showMenuButton?: boolean;
showBottomPanel?: boolean;
showControlBar?: boolean;
showFullscreenButton?: boolean;
showSettingsButton?: boolean;
showCompareButton?: boolean;
showSymbolSearchButton?: boolean;
/**
* Дефолтная конфигурация тултипа - всегда показывается по умолчанию.
* При добавлении/изменении полей в конфиге - они объединяются с дефолтными значениями.
*
* Полная кастомизация:
* @example
* ```typescript
* tooltipConfig: {
* time: { visible: true, label: 'Дата и время' },
* symbol: { visible: true, label: 'Инструмент' },
* close: { visible: true, label: 'Курс' },
* change: { visible: true, label: 'Изменение' },
* volume: { visible: true, label: 'Объем' },
* open: { visible: false },
* high: { visible: false },
* low: { visible: false }
* }
*```
*/
tooltipConfig?: TooltipConfig;
size?: {
width: number;
height: number;
} | false;
supportedTimeframes: Timeframes[];
supportedChartSeriesTypes: ChartSeriesType[];
getDataSource: DataSourceParams['getData'];
startRealtime: (getSymbols: () => string[], getTimeframe: () => Timeframes, update: (symbolId: string, candle: Candle) => void, periodMs?: number) => () => void;
theme: ThemeKey;
ohlc: OHLCConfig;
locale: Locale;
mode?: ThemeMode;
openCompareModal?: () => void;
openSymbolSearchModal?: () => void;
}
export interface IMoexChart {
snapshot: MoexChartSnapshotInput;
chartCollectionPreset: ChartCollectionPreset;
container: HTMLElement;
lwcInheritedChartOptions?: ChartTypeOptions;
}
export declare class MoexChart implements ISerializable<MoexChartSnapshot> {
private chart;
private resizeObserver?;
private eventManager;
private hotkeys;
private rootContainer;
private headerRenderer;
private modalRenderer;
private toolbarRenderer;
private controlBarRenderer?;
private footerRenderer?;
private drawingToolbarRenderer;
private timeScaleHoverController;
private dataSource;
private subscriptions;
private fullscreen;
private chartCollectionPresetSettings;
constructor(config: IMoexChart);
private setup;
setSettings(settings: ChartSettingsSource): void;
getSettings(): ChartSettings;
getRealtimeApi(): {
getTimeframe: () => Timeframes;
getSymbols: () => string[];
update: (symbolId: string, candle: Candle) => void;
};
getCompareManager(): CompareManager;
setSnapshot(snapshot: MoexChartSnapshotInput): void;
getSnapshot(): MoexChartSnapshot;
setSymbol(symbolInfo: SymbolInfoInput): void;
private renderAttachments;
/**
* Уничтожение графика и очистка ресурсов
* @returns void
*/
destroy(): void;
}