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