실시간 차트 보조지표명 겹침오류 수정
This commit is contained in:
@@ -643,13 +643,14 @@ export class ChartManager {
|
||||
} else {
|
||||
const isPlotVisible = !indicatorHidden && config.plotVisibility?.[plotDef.id] !== false;
|
||||
const showPriceLabel = this.shouldShowSeriesPriceLabel(config);
|
||||
const showSeriesTitle = pane < 2 && showPriceLabel;
|
||||
series = this.chart.addSeries(LineSeries, {
|
||||
color: plotDef.color,
|
||||
lineWidth: (plotDef.lineWidth ?? 1) as 1 | 2 | 3 | 4,
|
||||
lineStyle: plotSeriesLineStyle(plotDef.lineStyle),
|
||||
priceLineVisible: false,
|
||||
lastValueVisible: showPriceLabel,
|
||||
title: this.seriesTitleForPlot(config, plotDef),
|
||||
title: showSeriesTitle ? this.seriesTitleForPlot(config, plotDef) : '',
|
||||
visible: isPlotVisible,
|
||||
}, pane);
|
||||
series.setData(filtered.map(p => ({ time: p.time as Time, value: p.value })));
|
||||
@@ -732,6 +733,7 @@ export class ChartManager {
|
||||
}
|
||||
if (configs.length > 0 && this._activeIndicatorPaneIndices().size > 0) {
|
||||
this._removeOrphanSubPanes();
|
||||
this._resyncIndicatorPaneIndices();
|
||||
this.resetPaneHeights(this._lastLayoutAvailableHeight);
|
||||
this._notifyPaneLayout();
|
||||
}
|
||||
@@ -828,7 +830,10 @@ export class ChartManager {
|
||||
for (const c of configs) {
|
||||
if (this._detachIndicatorEntry(c.id)) any = true;
|
||||
}
|
||||
if (any) this._trimTrailingEmptySubPanes();
|
||||
if (any) {
|
||||
this._removeOrphanSubPanes();
|
||||
this._trimTrailingEmptySubPanes();
|
||||
}
|
||||
if (this._indicatorLoadStale(loadGen)) return;
|
||||
|
||||
for (const config of configs) {
|
||||
@@ -838,12 +843,173 @@ export class ChartManager {
|
||||
if (this._indicatorLoadStale(loadGen)) return;
|
||||
|
||||
this._removeOrphanSubPanes();
|
||||
this._resyncIndicatorPaneIndices();
|
||||
if (this._activeIndicatorPaneIndices().size > 0) {
|
||||
this.resetPaneHeights(this._lastLayoutAvailableHeight);
|
||||
this._notifyPaneLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 파라미터만 변경된 지표 — pane·시리즈를 유지하고 setData 로 갱신.
|
||||
* (detach/re-add 시 orphan pane·레이블 좌표 어긋남·이중 표시 방지)
|
||||
*/
|
||||
async refreshIndicatorParamsInPlace(configs: IndicatorConfig[]): Promise<void> {
|
||||
if (configs.length === 0) return;
|
||||
this.cancelPendingIndicatorUpdates();
|
||||
const loadGen = ++this._dataGeneration;
|
||||
const fallback: IndicatorConfig[] = [];
|
||||
|
||||
for (const raw of configs) {
|
||||
const config = enrichIndicatorConfig(raw);
|
||||
const def = getIndicatorDef(config.type);
|
||||
if (def?.returnsMarkers) {
|
||||
fallback.push(config);
|
||||
continue;
|
||||
}
|
||||
|
||||
const entry = this.indicators.get(config.id);
|
||||
if (!entry || entry.seriesList.length === 0 || entry.type !== config.type) {
|
||||
fallback.push(config);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await calculateIndicator(
|
||||
config.type, this.rawBars.slice(), config.params ?? {},
|
||||
);
|
||||
if (this._indicatorLoadStale(loadGen)) return;
|
||||
|
||||
if (!this._canApplyPlotsInPlace(entry, result.plots)) {
|
||||
fallback.push(config);
|
||||
continue;
|
||||
}
|
||||
|
||||
entry.config = config;
|
||||
if (config.type === 'IchimokuCloud') {
|
||||
this._setIchimokuPlotsFullData(entry, result.plots, config);
|
||||
if (entry.cloudPlugin) {
|
||||
this._applyIchimokuCloudPlugin(entry.cloudPlugin, result.plots, config);
|
||||
}
|
||||
} else {
|
||||
this._setIndicatorPlotsFullData(entry, result.plots, config);
|
||||
}
|
||||
this.applyIndicatorStyle(config);
|
||||
} catch (e) {
|
||||
console.error(`[Indicator in-place] ${config.type}:`, e);
|
||||
fallback.push(config);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._indicatorLoadStale(loadGen)) return;
|
||||
if (fallback.length > 0) {
|
||||
await this.refreshIndicators(fallback);
|
||||
return;
|
||||
}
|
||||
|
||||
this._resyncIndicatorPaneIndices();
|
||||
this._notifyPaneLayout();
|
||||
}
|
||||
|
||||
private _canApplyPlotsInPlace(
|
||||
entry: IndicatorEntry,
|
||||
plots: Record<string, PlotData>,
|
||||
): boolean {
|
||||
if (entry.seriesMeta.length === 0) return false;
|
||||
for (const meta of entry.seriesMeta) {
|
||||
const data = plots[meta.plotId] as PlotData | undefined;
|
||||
if (!data?.length) return false;
|
||||
const filtered = data.filter(p => p.value !== null && !isNaN(p.value));
|
||||
if (filtered.length === 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** 기존 시리즈에 전체 plot 데이터 재적용 (pane 유지) */
|
||||
private _setIndicatorPlotsFullData(
|
||||
entry: IndicatorEntry,
|
||||
plots: Record<string, PlotData>,
|
||||
config: IndicatorConfig,
|
||||
): void {
|
||||
const plotDefs = config.plots ?? getIndicatorDef(config.type)?.plots ?? [];
|
||||
const plotById = Object.fromEntries(plotDefs.map((p: PlotDef) => [p.id, p]));
|
||||
|
||||
for (let i = 0; i < entry.seriesList.length; i++) {
|
||||
const meta = entry.seriesMeta[i];
|
||||
if (!meta) continue;
|
||||
const plot = plotById[meta.plotId];
|
||||
const plotData = plots[meta.plotId] as PlotData | undefined;
|
||||
if (!plot || !plotData?.length) continue;
|
||||
const filtered = plotData.filter(p => p.value !== null && !isNaN(p.value));
|
||||
if (filtered.length === 0) continue;
|
||||
|
||||
const series = entry.seriesList[i];
|
||||
if (meta.isHistogram) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(series as ISeriesApi<any>).setData(mapHistogramSeriesData(filtered, plot));
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(series as ISeriesApi<any>).setData(
|
||||
filtered.map(p => ({ time: p.time as Time, value: p.value })),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.type === 'BollingerBands') {
|
||||
detachBbBandFill(entry);
|
||||
attachBbBandFill(entry, config);
|
||||
entry.bbFillPrimitive?.requestRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
private _setIchimokuPlotsFullData(
|
||||
entry: IndicatorEntry,
|
||||
plots: Record<string, PlotData>,
|
||||
config: IndicatorConfig,
|
||||
): void {
|
||||
const { senkou: displacement } = resolveIchimokuDisplacements(config.params);
|
||||
const laggingPeriod = (config.params?.laggingSpan2Periods as number) ?? 52;
|
||||
const convPlot = (plots['plot0'] as PlotData) ?? [];
|
||||
const basePlot = (plots['plot1'] as PlotData) ?? [];
|
||||
|
||||
for (let i = 0; i < entry.seriesList.length; i++) {
|
||||
const meta = entry.seriesMeta[i];
|
||||
if (!meta) continue;
|
||||
const plotData = plots[meta.plotId] as PlotData | undefined;
|
||||
if (!plotData?.length) continue;
|
||||
const filtered = plotData.filter(p => p.value !== null && !isNaN(p.value));
|
||||
if (filtered.length === 0) continue;
|
||||
|
||||
let seriesData: Array<{ time: Time; value: number }> =
|
||||
filtered.map(p => ({ time: p.time as Time, value: p.value }));
|
||||
|
||||
if (meta.plotId === 'plot3') {
|
||||
seriesData = seriesData.concat(
|
||||
this._ichimokuFutureSpanA(convPlot, basePlot, displacement),
|
||||
);
|
||||
} else if (meta.plotId === 'plot4') {
|
||||
seriesData = seriesData.concat(
|
||||
this._ichimokuFutureSpanB(displacement, laggingPeriod),
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(entry.seriesList[i] as ISeriesApi<any>).setData(seriesData);
|
||||
}
|
||||
}
|
||||
|
||||
/** LWC pane index ↔ entry.paneIndex 동기화 (removePane 후 어긋남 보정) */
|
||||
private _resyncIndicatorPaneIndices(): void {
|
||||
for (const entry of this.indicators.values()) {
|
||||
const first = entry.seriesList[0];
|
||||
if (!first) continue;
|
||||
try {
|
||||
const pi = first.getPane().paneIndex();
|
||||
if (pi >= 0) entry.paneIndex = pi;
|
||||
} catch { /* ok */ }
|
||||
}
|
||||
}
|
||||
|
||||
/** 메인 시리즈 마커 플러그인 해제 (setData 등 시리즈 교체 전 호출) */
|
||||
private _disposeMainMarkersPlugin(): void {
|
||||
if (!this.mainMarkersPlugin) return;
|
||||
@@ -1993,6 +2159,7 @@ export class ChartManager {
|
||||
config = enrichIndicatorConfig(config);
|
||||
const entry = this.indicators.get(config.id);
|
||||
if (!entry) return;
|
||||
entry.config = config;
|
||||
const def = getIndicatorDef(config.type);
|
||||
const plotDefs = config.plots ?? def?.plots ?? [];
|
||||
|
||||
@@ -2016,8 +2183,11 @@ export class ChartManager {
|
||||
plotAllows = entry.seriesMeta[i]?.plotId !== 'plot2';
|
||||
}
|
||||
const showPriceLabel = this.shouldShowSeriesPriceLabel(config, plotAllows);
|
||||
const paneIdx = entry.paneIndex ?? 0;
|
||||
opts['lastValueVisible'] = showPriceLabel;
|
||||
opts['title'] = this.seriesTitleForPlot(config, plot, plotAllows);
|
||||
opts['title'] = paneIdx < 2 && showPriceLabel
|
||||
? this.seriesTitleForPlot(config, plot, plotAllows)
|
||||
: '';
|
||||
} else {
|
||||
opts['color'] = plot.color ?? '#aaa';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user