Загрузка данных
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 04d654637e831bd14ea6e962d2d302d44e674394..b1f96b1fc018e4887e0e6b91a80f1b2dceddf7a1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,13 @@
# latest
+- Вместе с индикатороми теперь сохраняются и их настройки
+
+# 0.1.17
+
- Продлена ось x вправо
- Дродаун таймфреймов теперь по умолчанию раскрыт
- Применены стили к скролбару интервалов
+- Исправлен баг с занулением ema индикатора, как слева(при неудачных попытках его расчета) так и справа(в realtime)
- Исправлена работа Undo/Redo для дровингов
# 0.1.16
diff --git a/src/core/Chart.ts b/src/core/Chart.ts
index 28502b1b279687e696ce6f9e79d5e6e2af150d77..eac6f68cfd52fe8ed880d17045559410b3bd1ef5 100644
--- a/src/core/Chart.ts
+++ b/src/core/Chart.ts
@@ -42,7 +42,7 @@ import {
} from '@src/types';
import { Defaults } from '@src/types/defaults';
import { DayjsOffset, Intervals, intervalsToDayjs } from '@src/types/intervals';
-import { ChartSnapshot, ISerializable, PaneSnapshot } from '@src/types/snapshot';
+import { ChartSnapshot, CompareSnapshot, IndicatorSnapshot, ISerializable, PaneSnapshot } from '@src/types/snapshot';
import { formatCompactNumber } from '@src/utils';
import { createTickMarkFormatter, formatDate } from '@src/utils/formatter';
@@ -75,7 +75,10 @@ interface ChartParams {
lwcChartConfig: ChartConfig;
}
-function splitIndicatorSnapshots(panes: PaneSnapshot[]) {
+function splitIndicatorSnapshots(panes: PaneSnapshot[]): {
+ compareSnapshots: CompareSnapshot[];
+ indicatorSnapshots: IndicatorSnapshot[];
+} {
const snapshots = panes.flatMap(({ id, indicators }) =>
indicators.map((indicator) => ({
...indicator,
@@ -83,9 +86,20 @@ function splitIndicatorSnapshots(panes: PaneSnapshot[]) {
})),
);
+ function isIndicatorSnapshot(
+ input: (IndicatorSnapshot | CompareSnapshot) & { indicatorType?: unknown },
+ ): input is IndicatorSnapshot {
+ return input.indicatorType !== undefined;
+ }
+ function isCompareSnapshot(
+ input: (IndicatorSnapshot | CompareSnapshot) & { indicatorType?: unknown },
+ ): input is CompareSnapshot {
+ return input.indicatorType === undefined;
+ }
+
return {
- indicatorSnapshots: snapshots.filter(({ indicatorType }) => indicatorType !== undefined),
- compareSnapshots: snapshots.filter(({ indicatorType }) => indicatorType === undefined),
+ indicatorSnapshots: snapshots.filter((x) => isIndicatorSnapshot(x)),
+ compareSnapshots: snapshots.filter((x) => isCompareSnapshot(x)),
};
}
@@ -181,22 +195,24 @@ export class Chart implements ISerializable<ChartSnapshot> {
const { indicatorSnapshots, compareSnapshots } = splitIndicatorSnapshots(panesSnapshot);
this.indicatorManager = new IndicatorManager({
+ lwcChart: this.lwcChart,
eventManager,
- initialIndicators: indicatorSnapshots,
- DOM: this.DOM,
dataSource: this.dataSource,
- lwcChart: this.lwcChart,
paneManager: this.paneManager,
+
+ initialIndicators: indicatorSnapshots,
+ DOM: this.DOM,
chartOptions: lwcChartConfig.chartOptions,
});
this.compareManager = new CompareManager({
chart: this.lwcChart,
- initialIndicators: compareSnapshots,
- eventManager: this.eventManager,
+ eventManager,
dataSource: this.dataSource,
- indicatorManager: this.indicatorManager,
paneManager: this.paneManager,
+
+ initialIndicators: compareSnapshots,
+ indicatorManager: this.indicatorManager,
});
this.paneManager.start({
diff --git a/src/core/CompareManager.ts b/src/core/CompareManager.ts
index 9a4e68d4ef065ed98818615493a8d8cc93644afe..6c12e9991457f56f8d25bf14030e80f0901fabdc 100644
--- a/src/core/CompareManager.ts
+++ b/src/core/CompareManager.ts
@@ -11,7 +11,7 @@ import { PaneManager } from '@core/PaneManager';
import { PriceScale } from '@core/PriceScale';
import { COMPARE_COLOR_PALETTE } from '@src/theme';
import { CompareItem, CompareMode, Direction, IndicatorConfig, SymbolInfo, SymbolInfoInput } from '@src/types';
-import { IndicatorSnapshot } from '@src/types/snapshot';
+import { CompareSnapshot } from '@src/types/snapshot';
import { createFallbackColor, normalizeColor, normalizeSymbol, normalizeSymbolInfo } from '@src/utils';
interface CompareEntry extends CompareItem {
@@ -25,7 +25,7 @@ interface CompareManagerParams {
dataSource: DataSource;
indicatorManager: IndicatorManager;
paneManager: PaneManager;
- initialIndicators?: IndicatorSnapshot[];
+ initialIndicators?: CompareSnapshot[];
}
export class CompareManager {
@@ -245,39 +245,37 @@ export class CompareManager {
this.entitiesSubject.complete();
}
- private async setup(initialIndicators: IndicatorSnapshot[]): Promise<void> {
+ private setup = async (compareIndicators: CompareSnapshot[]): Promise<void> => {
this.restoringInitialIndicators = true;
try {
- for (const indicator of initialIndicators) {
- const { config } = indicator;
+ for (const compareIndicator of compareIndicators) {
+ const { scale, symbolInfo, seriesName, paneId } = compareIndicator;
- if (indicator.indicatorType !== undefined || !config?.symbolInfo) {
- continue;
- }
-
- const symbolInfo = normalizeSymbolInfo(config.symbolInfo);
+ const symbolInfoNormalized = normalizeSymbolInfo(symbolInfo);
- if (!symbolInfo) {
+ if (!symbolInfoNormalized) {
continue;
}
- const series = config.series[0];
+ if (this.paneManager.getMainPane().getId() !== paneId && scale === Direction.Left) {
+ throw new Error('[CompareManager]: несколько шкал на второстеменном пейне не поддерживаются');
+ }
const compareMode =
- series.seriesOptions?.priceScaleId === Direction.Left
+ scale === Direction.Left
? CompareMode.NewScale
- : config.newPane
- ? CompareMode.NewPane
- : CompareMode.Percentage;
+ : this.paneManager.getMainPane().getId() === paneId
+ ? CompareMode.Percentage
+ : CompareMode.NewPane;
// eslint-disable-next-line no-await-in-loop
- await this.setSymbolMode(series.name, symbolInfo, compareMode, indicator.paneId);
+ await this.setSymbolMode(seriesName, symbolInfoNormalized, compareMode, paneId!);
}
} finally {
this.restoringInitialIndicators = false;
}
- }
+ };
private removeEntry(key: string): boolean {
const entry = this.entries.get(key);
diff --git a/src/core/Indicator.ts b/src/core/Indicator.ts
index 2b49366cd244807b20d410600fad3fce6641aacb..6aa60bcb058c2735bb8f93591c0d52b6eeb5712d 100644
--- a/src/core/Indicator.ts
+++ b/src/core/Indicator.ts
@@ -7,8 +7,8 @@ import { Pane } from '@core/Pane';
import { indicatorLabelById, indicatorSeriesLabelById, IndicatorsIds } from '@src/constants';
import { SeriesFactory, SeriesStrategies } from '@src/modules/series-strategies/SeriesFactory';
-import { ChartTypeOptions, IndicatorConfig, SettingsValues } from '@src/types';
-import { DOMObjectSnapshot, IndicatorSnapshot, ISerializable } from '@src/types/snapshot';
+import { ChartTypeOptions, Direction, IndicatorConfig, SettingsValues } from '@src/types';
+import { CompareSnapshot, DOMObjectSnapshot, IndicatorSnapshot, ISerializable } from '@src/types/snapshot';
type IIndicator = DOMObject;
@@ -19,11 +19,13 @@ export interface IndicatorParams extends DOMObjectParams {
dataSource: DataSource;
associatedPane: Pane;
config: IndicatorConfig;
+ settings?: SettingsValues;
type?: IndicatorsIds;
chartOptions?: ChartTypeOptions;
}
-export class Indicator extends DOMObject implements ISerializable<IndicatorSnapshot> {
+// todo: сделать отдельно представление для Compare, и попобовать убрать CompareManager из Chart
+export class Indicator extends DOMObject implements ISerializable<IndicatorSnapshot | CompareSnapshot> {
private indicatorType?: IndicatorsIds;
private series: SeriesStrategies[] = [];
private seriesMap: Map<string, SeriesStrategies> = new Map();
@@ -52,6 +54,7 @@ export class Indicator extends DOMObject implements ISerializable<IndicatorSnaps
associatedPane,
paneId,
config,
+ settings,
}: IndicatorParams) {
super({ id, name: config?.label ?? id, zIndex, onDelete, moveUp, moveDown, paneId });
this.lwcChart = lwcChart;
@@ -62,7 +65,7 @@ export class Indicator extends DOMObject implements ISerializable<IndicatorSnaps
this.config = config;
this.name = this.getLabel();
- this.settings = this.getDefaultSettings();
+ this.settings = { ...this.getDefaultSettings(), ...settings };
this.associatedPane = associatedPane;
@@ -72,12 +75,7 @@ export class Indicator extends DOMObject implements ISerializable<IndicatorSnaps
}
public recreateSeries() {
- this.series.forEach((s) => {
- s.destroy();
- });
-
- this.series = [];
- this.seriesMap.clear();
+ this.destroySeries();
this.createSeries();
}
@@ -133,15 +131,15 @@ export class Indicator extends DOMObject implements ISerializable<IndicatorSnaps
return this.config;
}
- public getIndicatorType(): IndicatorSnapshot['indicatorType'] {
+ public getIndicatorType(): IndicatorSnapshot['indicatorType'] | undefined {
return this.indicatorType;
}
- public getSettings(): SettingsValues {
+ public getSettingsValues(): SettingsValues {
return { ...this.settings };
}
- public getSettingsConfig() {
+ public getSettingsFields() {
return this.config.settings ?? [];
}
@@ -153,8 +151,7 @@ export class Indicator extends DOMObject implements ISerializable<IndicatorSnaps
this.settings = settings;
// todo: обновлять данные серий без удаления и повторного создания
- this.destroySeries();
- this.createSeries();
+ this.recreateSeries();
this.notifyDataChanged();
}
@@ -173,14 +170,27 @@ export class Indicator extends DOMObject implements ISerializable<IndicatorSnaps
super.hide();
}
- public override getSnapshot(): DOMObjectSnapshot & IndicatorSnapshot {
+ public override getSnapshot(): DOMObjectSnapshot & (IndicatorSnapshot | CompareSnapshot) {
const domSnap = super.getSnapshot();
+ if (this.indicatorType) {
+ return {
+ ...domSnap,
+ indicatorType: this.indicatorType,
+ settings: this.settings,
+ };
+ }
+
+ const seriesName = this.config.series[0]?.name;
+ const scale = this.config.series[0]?.seriesOptions?.priceScaleId as Direction;
+ if (!scale || !seriesName) {
+ throw new Error('[Indicator]: невозможно сохранить состояние compare индикатора');
+ }
return {
...domSnap,
- dataSource: this.dataSource,
- indicatorType: this.indicatorType,
- config: this.config,
+ symbolInfo: this.config.symbolInfo!,
+ seriesName,
+ scale,
};
}
diff --git a/src/core/IndicatorManager.ts b/src/core/IndicatorManager.ts
index 0ce39369c0f956b96ef1ca184b58657a4bad7cfd..ee4b55953d180a47d20a31188cc5bf91aa742d98 100644
--- a/src/core/IndicatorManager.ts
+++ b/src/core/IndicatorManager.ts
@@ -6,6 +6,7 @@ import { DOMModel } from '@core/DOMModel';
import { EventManager } from '@core/EventManager';
import { Indicator } from '@core/Indicator';
import { PaneManager } from '@core/PaneManager';
+import { IndicatorsIds } from '@src/constants';
import { DOMObject } from '@src/core/DOMObject';
import { indicatorsMap as indicatorsConfigMap } from '@src/core/Indicators';
import { ChartTypeOptions, IndicatorConfig } from '@src/types';
@@ -63,11 +64,10 @@ export class IndicatorManager {
const id = snap.id ?? `${snap.indicatorType}-${crypto.randomUUID()}`;
- const baseConfig = snap.config ?? (indicatorsConfigMap()[snap.indicatorType] as IndicatorConfig);
-
- const config = snap.config
- ? baseConfig
- : applyNextIndicatorColors(baseConfig, this.getUsedIndicatorColorsByType(snap.indicatorType));
+ const config = getConfigByIndicatorType({
+ indicatorType: snap.indicatorType,
+ existedIndicators: this.indicatorsMap$.value,
+ });
const associatedPane =
snap.paneId !== undefined
@@ -91,6 +91,7 @@ export class IndicatorManager {
dataSource: this.dataSource,
associatedPane,
config,
+ settings: snap.settings,
type: snap.indicatorType,
chartOptions: this.chartOptions,
});
@@ -103,20 +104,6 @@ export class IndicatorManager {
this.entities$.next(Array.from(indicatorsMap.values()));
}
- private getUsedIndicatorColorsByType(indicatorType: IndicatorSnapshot['indicatorType']): string[] {
- const colors: string[] = [];
-
- this.indicatorsMap$.value.forEach((indicator) => {
- if (indicator.getIndicatorType() !== indicatorType) {
- return;
- }
-
- colors.push(...getIndicatorColors(indicator.getConfig()));
- });
-
- return colors;
- }
-
public getIndicators() {
return this.indicatorsMap$;
}
@@ -145,3 +132,36 @@ export class IndicatorManager {
return this.entities$.asObservable();
}
}
+
+function getConfigByIndicatorType({
+ indicatorType,
+ existedIndicators,
+}: {
+ indicatorType: IndicatorsIds;
+ existedIndicators: Map<string, Indicator>;
+}): IndicatorConfig {
+ const configWithAppliesSettings = indicatorsConfigMap()[indicatorType] as IndicatorConfig;
+ const usedColors = getUsedIndicatorColorsByType({ indicatorType, existedIndicators });
+
+ return applyNextIndicatorColors(configWithAppliesSettings, usedColors);
+}
+
+function getUsedIndicatorColorsByType({
+ indicatorType,
+ existedIndicators,
+}: {
+ indicatorType: IndicatorSnapshot['indicatorType'];
+ existedIndicators: Map<string, Indicator>;
+}): string[] {
+ const colors: string[] = [];
+
+ existedIndicators.forEach((indicator) => {
+ if (indicator.getIndicatorType() !== indicatorType) {
+ return;
+ }
+
+ colors.push(...getIndicatorColors(indicator.getConfig()));
+ });
+
+ return colors;
+}
diff --git a/src/core/Pane.tsx b/src/core/Pane.tsx
index 25cf2688736b7839c20fc75c9143d030c5018b10..e24286fb3413675bcfaf735c87ff716b7c8eaaed 100644
--- a/src/core/Pane.tsx
+++ b/src/core/Pane.tsx
@@ -24,6 +24,7 @@ import { SeriesFactory, SeriesStrategies } from '@src/modules/series-strategies/
import { t } from '@src/translations';
import { Direction, OHLCConfig, TooltipConfig } from '@src/types';
import {
+ CompareSnapshot,
DOMObjectSnapshot,
IndicatorSnapshot,
ISerializable,
@@ -258,7 +259,7 @@ export class Pane implements ISerializable<PaneSnapshot> {
}
public getSnapshot(): PaneSnapshot {
- const indicators: (DOMObjectSnapshot & IndicatorSnapshot)[] = [];
+ const indicators: (DOMObjectSnapshot & (IndicatorSnapshot | CompareSnapshot))[] = [];
this.indicatorsMap.value.forEach((indicator) => {
indicators.push(indicator.getSnapshot());
@@ -363,7 +364,7 @@ export class Pane implements ISerializable<PaneSnapshot> {
paneId: this.id,
paneIndex: this.paneIndex,
openIndicatorSettings: (indicatorId, indicator) => {
- let settings = indicator.getSettings();
+ let settings = indicator.getSettingsValues();
this.modalRenderer.renderComponent(
<EntitySettingsModal
@@ -371,7 +372,7 @@ export class Pane implements ISerializable<PaneSnapshot> {
{
key: 'arguments',
label: t('Arguments'),
- fields: indicator.getSettingsConfig(),
+ fields: indicator.getSettingsFields(),
},
]}
values={settings}
diff --git a/src/types/snapshot.ts b/src/types/snapshot.ts
index 5349f052eb58db54515c4c24c9064853995fd911..a1d04436fe8120ae664f6cb40f465cc26430eb04 100644
--- a/src/types/snapshot.ts
+++ b/src/types/snapshot.ts
@@ -1,11 +1,11 @@
-import { DataSource } from '@core/DataSource';
import { DrawingsManagerSnapshot } from '@core/DrawingsManager';
import { ChartSeriesType, DateFormat, IndicatorsIds, Intervals, Timeframes } from '@lib';
import { Direction } from '@src/types/chart';
-import { IndicatorConfig } from '@src/types/indicator';
import { SymbolInfo, SymbolInfoInput } from '@src/types/symbol';
import { TimeFormat } from '@src/types/timeScale';
+import { SettingsValues } from './settings';
+
import type { PriceScaleMode } from 'lightweight-charts';
export type PriceScaleSide = Direction.Left | Direction.Right;
@@ -49,15 +49,20 @@ export interface PriceScaleSnapshot {
export interface PaneSnapshot {
isMain: boolean;
id: number;
- indicators: IndicatorSnapshot[];
+ 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> {
- dataSource?: DataSource;
- indicatorType: IndicatorsIds | undefined; // if indicatorType is undefined, then its compareIndicator
- config?: IndicatorConfig;
+ indicatorType: IndicatorsIds; // if indicatorType is undefined, then its compareIndicator
+ settings?: SettingsValues;
}
// todo: move DrawingsManagerSnapshot here
diff --git a/stories/TradeRadar/TradeRadar.stories.tsx b/stories/TradeRadar/TradeRadar.stories.tsx
index d097064b696d204727cc0c3c75f1b8032b839774..fcd77c813b49b6aea0b2b2b803971fc20b7d33d5 100644
--- a/stories/TradeRadar/TradeRadar.stories.tsx
+++ b/stories/TradeRadar/TradeRadar.stories.tsx
@@ -5,7 +5,7 @@ import { createPortal } from 'react-dom';
import { CompareManager } from '@core/CompareManager';
import { DateFormat, IMoexChart, Locale, MoexChart, Timeframes } from '@lib';
import { IndicatorsIds } from '@lib/constants';
-import { CompareMode, SymbolInfoInput } from '@lib/types';
+import { CompareMode, Direction, SymbolInfoInput } from '@lib/types';
// import { argTypes } from '../argTypes';
@@ -141,7 +141,12 @@ const args: TRProps = {
id: 0,
indicators: [
{
- indicatorType: IndicatorsIds.Volume,
+ indicatorType: IndicatorsIds.Volume, // if indicatorType is undefined, then its compareIndicator
+ },
+ {
+ symbolInfo: { symbolId: 'TQBR:SBER' },
+ scale: Direction.Right,
+ seriesName: 'Line',
},
],
drawings: [],