추가수정
This commit is contained in:
@@ -740,50 +740,61 @@ export class ChartManager {
|
||||
|
||||
/** 이중 plot 지표 — addIndicator 루프에서 누락된 plot 시리즈 추가 */
|
||||
/**
|
||||
* OBV pane: plot0(본선)=하반부, plot1(신호선)=상반부로 scale 분리.
|
||||
* 두 선이 언제나 pane의 서로 다른 절반에 표시되어 항상 시각적으로 구분됨.
|
||||
* OBV pane: plot0(본선)=하반부, plot1(신호선)=상반부로 분리.
|
||||
* 각 series의 autoscaleInfoProvider에서 priceRange를 오프셋 처리하여
|
||||
* 독립 scale에서도 항상 pane의 서로 다른 절반에 표시되도록 보장.
|
||||
*
|
||||
* 원리: plot0는 [pMin, pMax + pSpan] 범위를 사용 → 실제 데이터가 하반부에 위치
|
||||
* plot1는 [pMin - pSpan, pMax] 범위를 사용 → 실제 데이터가 상반부에 위치
|
||||
*/
|
||||
private _applyObvTightScale(
|
||||
seriesList: ISeriesApi<SeriesType>[],
|
||||
plots: Record<string, PlotData>,
|
||||
seriesMeta?: IndicatorEntry['seriesMeta'],
|
||||
): void {
|
||||
seriesList.forEach((s, i) => {
|
||||
const plotId = seriesMeta?.[i]?.plotId;
|
||||
try {
|
||||
const ps = s.priceScale();
|
||||
if (plotId === 'plot1') {
|
||||
// 신호선: 상반부 (하단 55% 여백)
|
||||
ps.applyOptions({ scaleMargins: { top: 0.03, bottom: 0.55 }, visible: false });
|
||||
} else {
|
||||
// 본선(plot0): 하반부 (상단 55% 여백)
|
||||
ps.applyOptions({ scaleMargins: { top: 0.55, bottom: 0.03 } });
|
||||
}
|
||||
} catch { /* ok */ }
|
||||
});
|
||||
// plot0에 전체 값 범위 기준 autoscaleInfoProvider (과거 극값에 의한 압축 방지)
|
||||
const allPts: number[] = [];
|
||||
for (const key of ['plot0', 'plot1']) {
|
||||
const pts = (plots[key] ?? []).filter(p => p.value != null && isFinite(p.value as number));
|
||||
allPts.push(...pts.slice(-60).map(p => p.value as number));
|
||||
}
|
||||
if (allPts.length > 0) {
|
||||
if (allPts.length === 0) return;
|
||||
|
||||
const minV = Math.min(...allPts);
|
||||
const maxV = Math.max(...allPts);
|
||||
const span = Math.max(1, maxV - minV);
|
||||
const pad = span * 0.15;
|
||||
seriesList.forEach(s => {
|
||||
const pad = span * 0.12;
|
||||
const pMin = minV - pad;
|
||||
const pMax = maxV + pad;
|
||||
const pSpan = pMax - pMin;
|
||||
|
||||
seriesList.forEach((s, i) => {
|
||||
const plotId = seriesMeta?.[i]?.plotId;
|
||||
try {
|
||||
if (plotId === 'plot1') {
|
||||
// 신호선: 독립 scale, 상반부 — priceRange 를 하방으로 pSpan 확장
|
||||
// [pMin - pSpan, pMax] 에서 실제 데이터 [pMin, pMax] 가 상위 50% 차지
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(s as ISeriesApi<any>).applyOptions({
|
||||
autoscaleInfoProvider: () => ({
|
||||
priceRange: { minValue: minV - pad, maxValue: maxV + pad },
|
||||
margins: { above: 0, below: 0 },
|
||||
priceRange: { minValue: pMin - pSpan, maxValue: pMax },
|
||||
margins: { above: 0.04, below: 0 },
|
||||
}),
|
||||
});
|
||||
} catch { /* ok */ }
|
||||
// 신호선 scale axis 숨김
|
||||
try { s.priceScale().applyOptions({ visible: false }); } catch { /* ok */ }
|
||||
} else {
|
||||
// OBV 본선: 하반부 — priceRange 를 상방으로 pSpan 확장
|
||||
// [pMin, pMax + pSpan] 에서 실제 데이터 [pMin, pMax] 가 하위 50% 차지
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(s as ISeriesApi<any>).applyOptions({
|
||||
autoscaleInfoProvider: () => ({
|
||||
priceRange: { minValue: pMin, maxValue: pMax + pSpan },
|
||||
margins: { above: 0, below: 0.04 },
|
||||
}),
|
||||
});
|
||||
}
|
||||
} catch { /* ok */ }
|
||||
});
|
||||
}
|
||||
|
||||
private _appendMissingPlotSeriesToLists(
|
||||
@@ -3023,19 +3034,14 @@ export class ChartManager {
|
||||
}
|
||||
}
|
||||
|
||||
// OBV: 스타일 갱신 후 pane 분리 scaleMargins 재적용
|
||||
if (entry.type === 'OBV' && entry.seriesList.length > 0) {
|
||||
entry.seriesList.forEach((s, i) => {
|
||||
const pid = entry.seriesMeta[i]?.plotId;
|
||||
// OBV: 스타일 갱신 후 autoscaleInfoProvider 기반 상하 분리 재적용
|
||||
if (entry.type === 'OBV' && entry.seriesList.length > 0 && this.rawBars.length > 0) {
|
||||
(async () => {
|
||||
try {
|
||||
const ps = s.priceScale();
|
||||
if (pid === 'plot1') {
|
||||
ps.applyOptions({ scaleMargins: { top: 0.03, bottom: 0.55 }, visible: false });
|
||||
} else {
|
||||
ps.applyOptions({ scaleMargins: { top: 0.55, bottom: 0.03 } });
|
||||
}
|
||||
const result = await calculateIndicator('OBV', this.rawBars.slice(), entry.config?.params ?? {});
|
||||
this._applyObvTightScale(entry.seriesList, result.plots as Record<string, PlotData>, entry.seriesMeta);
|
||||
} catch { /* ok */ }
|
||||
});
|
||||
})();
|
||||
}
|
||||
|
||||
if (entry.type === 'BollingerBands') {
|
||||
|
||||
Reference in New Issue
Block a user