obv 수정
This commit is contained in:
@@ -123,7 +123,12 @@ const TradeSignalMiniChart: React.FC<Props> = ({
|
||||
}
|
||||
syncChartLayout();
|
||||
applySignalMarkers();
|
||||
void mgr?.repairDualLineSeries();
|
||||
if (!mgr) return;
|
||||
[0, 100, 500, 1200].forEach(delay => {
|
||||
setTimeout(() => {
|
||||
void managerRef.current?.repairDualLineSeries();
|
||||
}, delay);
|
||||
});
|
||||
}, [syncChartLayout, applySignalMarkers]);
|
||||
|
||||
const handleManagerReady = useCallback((mgr: ChartManager) => {
|
||||
@@ -185,6 +190,16 @@ const TradeSignalMiniChart: React.FC<Props> = ({
|
||||
|
||||
const chartDataReady = barsReady && obvBarsReady;
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled || !chartDataReady) return;
|
||||
const mgr = managerRef.current;
|
||||
if (!mgr?.hasMainSeries()) return;
|
||||
const timers = [200, 800, 1600].map(delay =>
|
||||
setTimeout(() => void mgr.repairDualLineSeries(), delay),
|
||||
);
|
||||
return () => timers.forEach(clearTimeout);
|
||||
}, [enabled, chartDataReady, chartIndicators, market, timeframe]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
chartLiveReadyRef.current = false;
|
||||
pendingBarRef.current = null;
|
||||
|
||||
@@ -748,23 +748,18 @@ export class ChartManager {
|
||||
return loadGen !== this._dataGeneration;
|
||||
}
|
||||
|
||||
/** OBV — RSI/CCI와 동일한 공유 price scale, 이전 autoscale 오버라이드 제거 */
|
||||
private _resetObvSharedScale(entry: IndicatorEntry): void {
|
||||
if (entry.type !== 'OBV') return;
|
||||
for (const s of entry.seriesList) {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(s as ISeriesApi<any>).applyOptions({ autoscaleInfoProvider: undefined });
|
||||
} catch { /* ok */ }
|
||||
}
|
||||
const anchor = entry.seriesList[0];
|
||||
if (!anchor) return;
|
||||
try {
|
||||
anchor.priceScale().applyOptions({
|
||||
visible: true,
|
||||
scaleMargins: { top: 0.1, bottom: 0.1 },
|
||||
});
|
||||
} catch { /* ok */ }
|
||||
/** 이중 plot 지표에서 누락된 plotId 목록 (series 개수만으로는 plot0 누락 감지 불가) */
|
||||
private _dualLineMissingPlotIds(
|
||||
entry: IndicatorEntry,
|
||||
config: IndicatorConfig,
|
||||
def: ReturnType<typeof getIndicatorDef> | undefined,
|
||||
): string[] {
|
||||
const plotDefs = config.plots ?? def?.plots ?? [];
|
||||
const existing = new Set(entry.seriesMeta.map(m => m.plotId));
|
||||
return plotDefs
|
||||
.filter(p => p.type !== 'histogram' && config.plotVisibility?.[p.id] !== false)
|
||||
.map(p => p.id)
|
||||
.filter(id => !existing.has(id));
|
||||
}
|
||||
|
||||
private _appendMissingPlotSeriesToLists(
|
||||
@@ -813,17 +808,13 @@ export class ChartManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** 스타일 갱신·초기 추가 후 OBV 등 본선(plot0) 시리즈 누락 보정 */
|
||||
/** 스타일 갱신·초기 추가 후 OBV 등 본선(plot0) 시리즈 누락 보정 + 전체 setData */
|
||||
private async _ensureDualLinePlotSeriesForEntry(entry: IndicatorEntry): Promise<void> {
|
||||
if (!DUAL_LINE_INDICATOR_TYPES.has(entry.type)) return;
|
||||
const config = enrichIndicatorConfig(entry.config!);
|
||||
const def = getIndicatorDef(config.type);
|
||||
if (!def) return;
|
||||
const plotDefs = config.plots ?? def.plots ?? [];
|
||||
const expectedCount = plotDefs.filter(
|
||||
p => p.type !== 'histogram' && config.plotVisibility?.[p.id] !== false,
|
||||
).length;
|
||||
if (entry.seriesList.length >= expectedCount) return;
|
||||
if (this._dualLineMissingPlotIds(entry, config, def).length === 0) return;
|
||||
if (this.rawBars.length === 0) return;
|
||||
|
||||
const pane = entry.paneIndex ?? this._readSeriesPaneIndex(entry.seriesList[0]) ?? 2;
|
||||
@@ -839,8 +830,8 @@ export class ChartManager {
|
||||
indicatorHidden,
|
||||
def,
|
||||
);
|
||||
if (config.type === 'OBV' && !indicatorHidden && entry.seriesList.length > 0) {
|
||||
this._resetObvSharedScale(entry);
|
||||
if (entry.seriesList.length > 0) {
|
||||
this._setIndicatorPlotsFullData(entry, result.plots as Record<string, PlotData>, config);
|
||||
}
|
||||
entry.config = config;
|
||||
if (entry.seriesList.length > 0) {
|
||||
@@ -952,12 +943,12 @@ export class ChartManager {
|
||||
);
|
||||
}
|
||||
|
||||
if (config.type === 'OBV' && !indicatorHidden && seriesList.length > 0) {
|
||||
const obvEntry: IndicatorEntry = {
|
||||
if (DUAL_LINE_INDICATOR_TYPES.has(config.type) && !indicatorHidden && seriesList.length > 0) {
|
||||
const dualEntry: IndicatorEntry = {
|
||||
id: config.id, type: config.type, seriesList, seriesMeta,
|
||||
paneIndex: pane, alertLines: [], hlineRefs: [], config,
|
||||
};
|
||||
this._resetObvSharedScale(obvEntry);
|
||||
this._setIndicatorPlotsFullData(dualEntry, result.plots as Record<string, PlotData>, config);
|
||||
}
|
||||
|
||||
// hlines(과열선/중앙선/침체선)는 첫 번째 시리즈에만 한 번 생성 (중복 방지)
|
||||
@@ -1013,6 +1004,9 @@ export class ChartManager {
|
||||
this.indicators.set(config.id, entry);
|
||||
if (DUAL_LINE_INDICATOR_TYPES.has(config.type)) {
|
||||
await this._ensureDualLinePlotSeriesForEntry(entry);
|
||||
if (entry.seriesList.length > 0 && !indicatorHidden) {
|
||||
this._setIndicatorPlotsFullData(entry, result.plots as Record<string, PlotData>, config);
|
||||
}
|
||||
}
|
||||
this.applyIndicatorStyle(config);
|
||||
if (config.type === 'BollingerBands' && !indicatorHidden) {
|
||||
@@ -1069,15 +1063,20 @@ export class ChartManager {
|
||||
this._notifyPaneLayout();
|
||||
}
|
||||
|
||||
/** OBV·RSI 등 본선(plot0) 시리즈 누락 시 재생성 (미니 차트·스타일 갱신 후) */
|
||||
/** OBV·RSI 등 본선(plot0) 시리즈 누락 시 재생성 + 전체 plot setData */
|
||||
async repairDualLineSeries(): Promise<void> {
|
||||
for (const entry of this.indicators.values()) {
|
||||
if (!DUAL_LINE_INDICATOR_TYPES.has(entry.type)) continue;
|
||||
if (!DUAL_LINE_INDICATOR_TYPES.has(entry.type) || !entry.config) continue;
|
||||
await this._ensureDualLinePlotSeriesForEntry(entry);
|
||||
if (entry.config) this.applyIndicatorStyle(entry.config);
|
||||
if (entry.type === 'OBV' && entry.seriesList.length > 0) {
|
||||
this._resetObvSharedScale(entry);
|
||||
}
|
||||
if (entry.seriesList.length === 0 || this.rawBars.length === 0) continue;
|
||||
try {
|
||||
const config = enrichIndicatorConfig(entry.config);
|
||||
const result = await calculateIndicator(
|
||||
config.type, this.rawBars.slice(), config.params ?? {},
|
||||
);
|
||||
this._setIndicatorPlotsFullData(entry, result.plots as Record<string, PlotData>, config);
|
||||
this.applyIndicatorStyle(config);
|
||||
} catch { /* ok */ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1087,6 +1086,7 @@ export class ChartManager {
|
||||
await this.addIndicator(config, { skipLayout: true });
|
||||
}
|
||||
if (configs.length > 0) {
|
||||
await this.repairDualLineSeries();
|
||||
this._finalizeIndicatorPaneLayout();
|
||||
this._refreshAllIchimokuClouds();
|
||||
this.syncChartOverlayVisibility();
|
||||
@@ -1876,8 +1876,17 @@ export class ChartManager {
|
||||
|
||||
for (const [, entry] of this.indicators.entries()) {
|
||||
if (dataGenAtStart !== this._dataGeneration) return;
|
||||
if (!entry.config || entry.seriesList.length === 0) continue;
|
||||
if (!entry.config) continue;
|
||||
try {
|
||||
if (DUAL_LINE_INDICATOR_TYPES.has(entry.type)) {
|
||||
const config = enrichIndicatorConfig(entry.config);
|
||||
const def = getIndicatorDef(entry.type);
|
||||
if (this._dualLineMissingPlotIds(entry, config, def).length > 0) {
|
||||
await this._ensureDualLinePlotSeriesForEntry(entry);
|
||||
}
|
||||
}
|
||||
if (entry.seriesList.length === 0) continue;
|
||||
|
||||
const { plots } = await calculateIndicator(
|
||||
entry.config.type, barsSnapshot, entry.config.params ?? {}
|
||||
);
|
||||
@@ -2099,8 +2108,17 @@ export class ChartManager {
|
||||
const barsSnapshot = this.rawBars.slice();
|
||||
for (const [, entry] of this.indicators.entries()) {
|
||||
if (dataGenAtStart !== this._dataGeneration) return;
|
||||
if (!entry.config || entry.seriesList.length === 0) continue;
|
||||
if (!entry.config) continue;
|
||||
try {
|
||||
if (DUAL_LINE_INDICATOR_TYPES.has(entry.type)) {
|
||||
const config = enrichIndicatorConfig(entry.config);
|
||||
const def = getIndicatorDef(entry.type);
|
||||
if (this._dualLineMissingPlotIds(entry, config, def).length > 0) {
|
||||
await this._ensureDualLinePlotSeriesForEntry(entry);
|
||||
}
|
||||
}
|
||||
if (entry.seriesList.length === 0) continue;
|
||||
|
||||
const { plots } = await calculateIndicator(
|
||||
entry.config.type, barsSnapshot, entry.config.params ?? {}
|
||||
);
|
||||
@@ -2984,10 +3002,6 @@ export class ChartManager {
|
||||
}
|
||||
}
|
||||
|
||||
if (entry.type === 'OBV' && entry.seriesList.length > 0) {
|
||||
this._resetObvSharedScale(entry);
|
||||
}
|
||||
|
||||
if (entry.type === 'BollingerBands') {
|
||||
detachBbBandFill(entry);
|
||||
if (this._shouldShowBbBandFillForConfig(config)) {
|
||||
|
||||
@@ -479,7 +479,10 @@ function normalizeSignalLineIndicatorPlots(
|
||||
const order = ['plot0', 'plot1'];
|
||||
return order.indexOf(a.id) - order.indexOf(b.id);
|
||||
});
|
||||
return { plots: nextPlots, plotVisibility: { ...plotVisibility } };
|
||||
const nextVis = { ...plotVisibility };
|
||||
if (nextVis.plot0 !== false) nextVis.plot0 = true;
|
||||
if (nextVis.plot1 !== false) nextVis.plot1 = true;
|
||||
return { plots: nextPlots, plotVisibility: nextVis };
|
||||
}
|
||||
|
||||
const regSignal = registryPlots.find(p => p.id === 'plot1');
|
||||
|
||||
Reference in New Issue
Block a user