Сохранение раскладки
ChartStatePersistence собирает раскладку графика — опции, панели, ценовые шкалы, серии, индикаторы и графические объекты — в один проверенный JSON-снимок и восстанавливает его обратно. Данные баров в снимок не попадают: сохраняется конфигурация, а котировки приходят из вашего источника.
Слой не владеет ни хранилищем, ни правилом именования ключей. Куда писать (файл, бэкенд, localStorage, IndexedDB) и как разделять снимки (по раскладке, по инструменту, по пользователю) решает приложение — через реализацию ChartStateStorage и функцию key.
Создание и обновление
Импорт — из точки входа @stocksharp/chart/persistence:
import { createChart, CandlestickSeries } from '@stocksharp/chart';
import { DrawingController } from '@stocksharp/chart/drawings';
import {
ChartStatePersistence,
NativeChartLayoutAdapter,
IndicatorEngineStateAdapter,
type ChartStateStorage,
type IndicatorEnginePersistenceApi,
} from '@stocksharp/chart/persistence';
declare const indicatorEngine: IndicatorEnginePersistenceApi;
const chart = createChart(document.querySelector<HTMLElement>('#chart')!, {});
chart.addSeries(CandlestickSeries, { id: 'price', upColor: '#26a69a', downColor: '#ef5350' });
const storage: ChartStateStorage = {
load: key => localStorage.getItem(key),
save: (key, value) => { localStorage.setItem(key, value); },
remove: key => { localStorage.removeItem(key); },
};
const persistence = new ChartStatePersistence<{ layoutId: string; symbol: string }>({
layout: new NativeChartLayoutAdapter({ chart, mainPaneId: 'main' }),
indicators: new IndicatorEngineStateAdapter({ engine: indicatorEngine }),
drawings: new DrawingController({ chart }),
storage,
key: ({ layoutId, symbol }) => `chart:${layoutId}:${symbol}`,
pretty: true,
});
const context = { layoutId: 'desk', symbol: 'BTC@IMEX' };
await persistence.save(context);
const restored = await persistence.load(context);
if (restored !== null) {
console.log(restored.state.panes.length);
console.log(restored.drawings.skipped); // объекты неизвестных типов пропущены, а не потеряли восстановление
}
Параметр-тип TContext — это то, что вы передаёте в save, load и remove; функция key превращает контекст в строку ключа и обязана вернуть непустую строку. pretty: true записывает JSON с отступами.
DrawingController должен быть тем же экземпляром, который обслуживает рисование на графике, — иначе сохранится пустой набор объектов.
Адаптеры
ChartStatePersistence не знает ни о нативном API графика, ни о движке индикаторов: он работает через два адаптера. Готовые реализации входят в тот же модуль, но при своей архитектуре можно передать собственные — интерфейсы ChartStateLayoutAdapter (capture, restore) и ChartStateIndicatorAdapter (capture, clear, restore) открыты.
NativeChartLayoutAdapter снимает и возвращает раскладку самого графика: опции графика, панели с их порядком, высотой, минимальной высотой и состоянием (normal, minimized, maximized), настройки ценовых шкал, а также серии с их типом, панелью, шкалой и стилевыми опциями. Опции конструктора:
chart— экземпляр графика (обязателен).mainPaneId— идентификатор корневой панели, которая переживает восстановление; по умолчаниюmain, иначе первая панель.createSeries(series, pane)— своё создание серии вместо реестра типов, когда серию нужно подключить к источнику данных.includeSeries(series)— фильтр: серия, для которой вернулиfalse, не сохраняется и не удаляется при восстановлении.onRemoveSeries(series)— вызывается для чужой серии, которую восстановление всё же вынуждено было отцепить, потому что её панель не входит в загружаемую раскладку.onUnknownSeries(series)— тип серии отсутствует в реестре.
Серия с опцией persist: false исключается из снимка так же, как отклонённая фильтром includeSeries.
IndicatorEngineStateAdapter сохраняет конфигурацию индикаторов — тип, параметры, стили отрисовки, привязку к панели и шкале, видимость и источник — но не вычисленные значения: после восстановления они считаются заново. Опции конструктора:
engine— движок индикаторов, реализующийIndicatorEnginePersistenceApi(getIndicators,removeAll,add,setVisible).resolveTargetPaneId(indicator)— сопоставление сохранённой панели с панелью хоста, когда идентификаторы различаются.onUnknownIndicator(indicator)— движок не смог создать индикатор такого типа.onUnknownStyle(indicator, styleId)— в стилях встретился идентификатор, которого у индикатора нет.
Индикатор, считающийся по выводу другого индикатора, восстанавливается после него: адаптер сам упорядочивает цепочку источников и сообщает об ошибке, если ссылка ведёт на отсутствующий индикатор или граф зациклен.
Формат состояния и миграции
Снимок описывается типом ChartStateV1 с полями schemaVersion, chartOptions, panes, series, indicators, drawings; текущая версия схемы — константа CHART_STATE_SCHEMA_VERSION (равна 1).
serializeChartState(state, { pretty })— проверить и превратить состояние в строку JSON.deserializeChartState(value, { migrations })— разобрать строку (или принять уже готовый объект), прогнать миграции до текущей версии и проверить результат.normalizeChartStateV1(value)— проверка и заморозка состояния: посторонние ключи, дубли идентификаторов, ссылки на несуществующие панели и раскладка без единой панели отклоняются.normalizePersistedObject(value, path, { omitUndefined })— глубокое копирование произвольного JSON в неизменяемый объект; циклы, нечисловые значения, чрезмерная вложенность и ключи__proto__,prototype,constructorзапрещены.
Старые снимки поднимаются пошаговыми миграциями. Общий реестр chartStateMigrations уже содержит переход с версии 0 на версию 1, а свои шаги регистрируют так:
import {
ChartStateMigrationRegistry,
deserializeChartState,
} from '@stocksharp/chart/persistence';
const migrations = new ChartStateMigrationRegistry();
migrations.register(1, state => ({ ...state, schemaVersion: 2 }));
const state = deserializeChartState(json, { migrations });
Каждая миграция переводит состояние ровно на одну версию вперёд и обязана выставить новое значение schemaVersion. Снимок, версия которого выше поддерживаемой, к загрузке не принимается.
Публичные методы
snapshot()— собрать текущее состояние графика вChartStateV1, не обращаясь к хранилищу.restore(state)— применить состояние к графику; возвращает{ state, drawings }, гдеdrawingsсодержит спискиrestoredиskipped.save(context)— снять снимок, сериализовать и записать в хранилище по вычисленному ключу; возвращает сохранённое состояние.load(context)— прочитать запись по ключу, прогнать миграции и восстановить её;null, если записи нет.remove(context)— удалить запись из хранилища.
Порядок восстановления фиксирован: сначала освобождаются индикаторы, затем восстанавливается раскладка панелей и серий, затем индикаторы, и последними — графические объекты.
Модуль экспортирует также типы для описания снимка и адаптеров: ChartStateLayoutSnapshot, ChartStateRestoreResult, ChartStatePersistenceOptions, PersistedPane, PersistedPriceScale, PersistedSeries, PersistedIndicator, PersistedDrawing, PersistedChartOptions, PersistedSeriesOptions, PersistedIndicatorParameters, PersistedIndicatorStyles, PersistedObject, PersistedJsonValue, PersistableIndicatorEntry, RawChartState, ChartStateMigration, MaybePromise.