캔들차트, 이동평균선, 일목균형표, 볼린저밴드 보이기 숨기기 기능적용
This commit is contained in:
@@ -59,6 +59,12 @@ import {
|
||||
resolveIchimokuCloudColors,
|
||||
resolveIchimokuDisplacements,
|
||||
} from './ichimokuConfig';
|
||||
import {
|
||||
type ChartOverlayToggleKey,
|
||||
type ChartOverlayVisibility,
|
||||
DEFAULT_CHART_OVERLAY_VISIBILITY,
|
||||
chartOverlayKeyForType,
|
||||
} from './chartOverlayVisibility';
|
||||
import type { PlotDef, HLineStyle } from './indicatorRegistry';
|
||||
import { mapHistogramSeriesData, histogramBarColor } from './plotColorUtils';
|
||||
import {
|
||||
@@ -230,6 +236,10 @@ export class ChartManager {
|
||||
/** 실시간 전략 체크 마커 */
|
||||
private liveStrategyMarkers: MarkerData[] = [];
|
||||
private compareSeriesMap = new Map<string, ISeriesApi<SeriesType>>();
|
||||
/** 캔들 pane 오버레이 그룹 표시 (config.hidden 과 별도 — in-place 토글용) */
|
||||
private _chartOverlayVisibility: ChartOverlayVisibility = {
|
||||
...DEFAULT_CHART_OVERLAY_VISIBILITY,
|
||||
};
|
||||
|
||||
// ── 인디케이터 실시간 갱신 스로틀 ─────────────────────────────────────────
|
||||
private _indUpdateTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
@@ -668,7 +678,7 @@ export class ChartManager {
|
||||
config = enrichIndicatorConfig(config);
|
||||
if (this.indicators.has(config.id) || this.rawBars.length === 0) return;
|
||||
const dataGenAtStart = this._dataGeneration;
|
||||
const indicatorHidden = config.hidden === true;
|
||||
const indicatorHidden = !this._isIndicatorEffectivelyVisible(config);
|
||||
const def = getIndicatorDef(config.type);
|
||||
const seriesList: ISeriesApi<SeriesType>[] = [];
|
||||
const seriesMeta: IndicatorEntry['seriesMeta'] = [];
|
||||
@@ -855,6 +865,7 @@ export class ChartManager {
|
||||
if (configs.length > 0) {
|
||||
this._finalizeIndicatorPaneLayout();
|
||||
this._refreshAllIchimokuClouds();
|
||||
this.syncChartOverlayVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -930,6 +941,7 @@ export class ChartManager {
|
||||
|
||||
this._finalizeIndicatorPaneLayout();
|
||||
this._refreshAllIchimokuClouds();
|
||||
this.syncChartOverlayVisibility();
|
||||
}
|
||||
|
||||
removeIndicator(id: string): void {
|
||||
@@ -1786,7 +1798,7 @@ export class ChartManager {
|
||||
plugin.setColors(colors.bullishColor, colors.bearishColor);
|
||||
plugin.setVisibility(effectiveBullish, effectiveBearish);
|
||||
|
||||
if (config.hidden === true) {
|
||||
if (!this._isIndicatorEffectivelyVisible(config)) {
|
||||
plugin.setCloudData([]);
|
||||
return;
|
||||
}
|
||||
@@ -2343,6 +2355,89 @@ export class ChartManager {
|
||||
return result;
|
||||
}
|
||||
|
||||
getChartOverlayVisibility(): ChartOverlayVisibility {
|
||||
return { ...this._chartOverlayVisibility };
|
||||
}
|
||||
|
||||
setChartOverlayVisibility(visibility: ChartOverlayVisibility): void {
|
||||
this._chartOverlayVisibility = { ...visibility };
|
||||
this.syncChartOverlayVisibility();
|
||||
}
|
||||
|
||||
setChartOverlayGroupVisible(key: ChartOverlayToggleKey, visible: boolean): void {
|
||||
this._chartOverlayVisibility = {
|
||||
...this._chartOverlayVisibility,
|
||||
[key]: visible,
|
||||
};
|
||||
this.syncChartOverlayVisibility();
|
||||
}
|
||||
|
||||
/** 사용자 hidden + 세션 오버레이 토글 — entry.config.hidden 은 사용자 의도만 반영 */
|
||||
private _isIndicatorEffectivelyVisible(config: IndicatorConfig): boolean {
|
||||
const overlayKey = chartOverlayKeyForType(config.type);
|
||||
if (overlayKey && !this._chartOverlayVisibility[overlayKey]) return false;
|
||||
return config.hidden !== true;
|
||||
}
|
||||
|
||||
/** 오버레이·plotVisibility 반영해 시리즈 visible 만 갱신 (플롯 정의 없어도 전체 시리즈 대상) */
|
||||
private _applySeriesVisibilityToEntry(entry: IndicatorEntry): void {
|
||||
const config = entry.config;
|
||||
if (!config) return;
|
||||
const indicatorVisible = this._isIndicatorEffectivelyVisible(config);
|
||||
|
||||
entry.seriesList.forEach((series, i) => {
|
||||
const plotId = entry.seriesMeta[i]?.plotId;
|
||||
const visible = indicatorVisible
|
||||
&& (plotId == null || config.plotVisibility?.[plotId] !== false);
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
series.applyOptions({ visible } as any);
|
||||
} catch { /* ok */ }
|
||||
});
|
||||
}
|
||||
|
||||
/** 오버레이 그룹·캔들 visible — 시리즈 재생성 없이 applyOptions 만 사용 */
|
||||
syncChartOverlayVisibility(): void {
|
||||
if (this.mainSeries) {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(this.mainSeries as ISeriesApi<any>).applyOptions({
|
||||
visible: this._chartOverlayVisibility.candle,
|
||||
});
|
||||
} catch { /* ok */ }
|
||||
}
|
||||
for (const entry of this.indicators.values()) {
|
||||
const config = entry.config;
|
||||
if (!config) continue;
|
||||
const key = chartOverlayKeyForType(config.type);
|
||||
if (!key) continue;
|
||||
|
||||
this._applySeriesVisibilityToEntry(entry);
|
||||
|
||||
if (entry.type === 'BollingerBands') {
|
||||
detachBbBandFill(entry);
|
||||
if (this._isIndicatorEffectivelyVisible(config)) {
|
||||
attachBbBandFill(entry, config);
|
||||
entry.bbFillPrimitive?.requestRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
if (entry.type === 'IchimokuCloud' && entry.cloudPlugin) {
|
||||
if (!this._isIndicatorEffectivelyVisible(config)) {
|
||||
entry.cloudPlugin.setCloudData([]);
|
||||
} else {
|
||||
void calculateIndicator(
|
||||
'IchimokuCloud',
|
||||
this.rawBars,
|
||||
config.params ?? {},
|
||||
).then(({ plots }) => {
|
||||
this._applyIchimokuCloudPlugin(entry.cloudPlugin!, plots, config);
|
||||
}).catch(() => { /* ignore */ });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 인디케이터 전체 가시성 토글 (시리즈 visible 옵션 변경) */
|
||||
toggleIndicatorVisible(id: string, visible: boolean): void {
|
||||
const entry = this.indicators.get(id);
|
||||
@@ -2451,19 +2546,25 @@ export class ChartManager {
|
||||
config = enrichIndicatorConfig(config);
|
||||
const entry = this.indicators.get(config.id);
|
||||
if (!entry) return;
|
||||
entry.config = config;
|
||||
const prevUserHidden = entry.config?.hidden === true;
|
||||
const def = getIndicatorDef(config.type);
|
||||
const plotDefs = config.plots ?? def?.plots ?? [];
|
||||
|
||||
const plotById = Object.fromEntries(plotDefs.map((p: PlotDef) => [p.id, p]));
|
||||
|
||||
const indicatorVisible = this._isIndicatorEffectivelyVisible(config);
|
||||
|
||||
entry.seriesList.forEach((series, i) => {
|
||||
let plot: PlotDef | undefined;
|
||||
const pid = entry.seriesMeta[i]?.plotId;
|
||||
plot = (pid ? plotById[pid] : undefined) ?? plotDefs[i];
|
||||
if (!plot) return;
|
||||
const indicatorVisible = config.hidden !== true;
|
||||
const visible = indicatorVisible && config.plotVisibility?.[plot.id] !== false;
|
||||
const visible = indicatorVisible
|
||||
&& (pid == null || config.plotVisibility?.[pid] !== false);
|
||||
if (!plot) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
series.applyOptions({ visible } as any);
|
||||
return;
|
||||
}
|
||||
const paneIdx = entry.paneIndex ?? 0;
|
||||
const indicatorPriceFormat = priceFormatForIndicatorPane(paneIdx);
|
||||
|
||||
@@ -2516,7 +2617,7 @@ export class ChartManager {
|
||||
entry.fillPrimitive = undefined;
|
||||
}
|
||||
|
||||
if (config.hidden !== true) {
|
||||
if (indicatorVisible) {
|
||||
for (const hl of hlines) {
|
||||
if (hl.visible === false) continue;
|
||||
const pl = firstSeries.createPriceLine({
|
||||
@@ -2544,7 +2645,7 @@ export class ChartManager {
|
||||
|
||||
if (entry.type === 'BollingerBands') {
|
||||
detachBbBandFill(entry);
|
||||
if (config.hidden !== true) {
|
||||
if (indicatorVisible) {
|
||||
attachBbBandFill(entry, config);
|
||||
entry.bbFillPrimitive?.requestRefresh();
|
||||
}
|
||||
@@ -2560,7 +2661,17 @@ export class ChartManager {
|
||||
}).catch(() => { /* ignore */ });
|
||||
}
|
||||
|
||||
entry.config = config;
|
||||
const overlayKey = chartOverlayKeyForType(config.type);
|
||||
const overlayOn = overlayKey == null || this._chartOverlayVisibility[overlayKey];
|
||||
const toStore = { ...config };
|
||||
if (overlayKey) {
|
||||
if (overlayOn && config.hidden !== true) {
|
||||
delete toStore.hidden;
|
||||
} else if (!overlayOn && toStore.hidden === true && !prevUserHidden && config.hidden !== true) {
|
||||
delete toStore.hidden;
|
||||
}
|
||||
}
|
||||
entry.config = toStore;
|
||||
if (entry.seriesMeta.some(m => m.isHistogram)) {
|
||||
void this._repaintHistogramSeries(config.id, config);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user