macd 그래프 오류
This commit is contained in:
@@ -181,6 +181,13 @@ function seriesByPlotId(
|
|||||||
return i >= 0 ? entry.seriesList[i] : undefined;
|
return i >= 0 ? entry.seriesList[i] : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** hline·음영 프리미티브 부착 대상 — histogram+line 지표(MACD 등)는 line 시리즈에 부착 */
|
||||||
|
function hlineAnchorSeries(entry: Pick<IndicatorEntry, 'seriesList' | 'seriesMeta'>): ISeriesApi<SeriesType> | undefined {
|
||||||
|
if (entry.seriesList.length === 0) return undefined;
|
||||||
|
const lineIdx = entry.seriesMeta.findIndex(m => !m.isHistogram);
|
||||||
|
return entry.seriesList[lineIdx >= 0 ? lineIdx : 0];
|
||||||
|
}
|
||||||
|
|
||||||
function shouldShowBbBandFill(config: IndicatorConfig): boolean {
|
function shouldShowBbBandFill(config: IndicatorConfig): boolean {
|
||||||
const bg = resolveBbBandBackground(config.bandBackground);
|
const bg = resolveBbBandBackground(config.bandBackground);
|
||||||
if (!bg.visible) return false;
|
if (!bg.visible) return false;
|
||||||
@@ -1052,7 +1059,7 @@ export class ChartManager {
|
|||||||
const indicatorHidden = !this._isIndicatorEffectivelyVisible(config);
|
const indicatorHidden = !this._isIndicatorEffectivelyVisible(config);
|
||||||
|
|
||||||
for (const pl of entry.hlineRefs) {
|
for (const pl of entry.hlineRefs) {
|
||||||
try { entry.seriesList[0]?.removePriceLine(pl); } catch { /* ok */ }
|
try { hlineAnchorSeries(entry)?.removePriceLine(pl); } catch { /* ok */ }
|
||||||
}
|
}
|
||||||
entry.hlineRefs = [];
|
entry.hlineRefs = [];
|
||||||
for (const s of entry.seriesList) {
|
for (const s of entry.seriesList) {
|
||||||
@@ -1228,7 +1235,7 @@ export class ChartManager {
|
|||||||
let fillPrimitive: IndicatorFillPrimitive | undefined;
|
let fillPrimitive: IndicatorFillPrimitive | undefined;
|
||||||
|
|
||||||
if (!indicatorHidden && seriesList.length > 0) {
|
if (!indicatorHidden && seriesList.length > 0) {
|
||||||
const firstSeries = seriesList[0];
|
const hlineHost = hlineAnchorSeries({ seriesList, seriesMeta });
|
||||||
const hlines = config.hlines ?? def?.hlines ?? [];
|
const hlines = config.hlines ?? def?.hlines ?? [];
|
||||||
|
|
||||||
// HLineStyle → lightweight-charts LineStyle 변환
|
// HLineStyle → lightweight-charts LineStyle 변환
|
||||||
@@ -1238,28 +1245,30 @@ export class ChartManager {
|
|||||||
return LineStyle.Dashed; // 기본값
|
return LineStyle.Dashed; // 기본값
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const hl of hlines) {
|
if (hlineHost) {
|
||||||
if (hl.visible === false) continue;
|
for (const hl of hlines) {
|
||||||
const pl = firstSeries.createPriceLine({
|
if (hl.visible === false) continue;
|
||||||
price: hl.price,
|
const pl = hlineHost.createPriceLine({
|
||||||
color: hl.color,
|
price: hl.price,
|
||||||
lineWidth: Math.min(4, hl.lineWidth ?? 1) as 1|2|3|4,
|
color: hl.color,
|
||||||
lineStyle: toLineStyle(hl.lineStyle),
|
lineWidth: Math.min(4, hl.lineWidth ?? 1) as 1|2|3|4,
|
||||||
axisLabelVisible: false,
|
lineStyle: toLineStyle(hl.lineStyle),
|
||||||
});
|
axisLabelVisible: false,
|
||||||
hlineRefs.push(pl);
|
});
|
||||||
}
|
hlineRefs.push(pl);
|
||||||
|
}
|
||||||
|
|
||||||
// 과열선/침체선 레이블이 있는 hlines 가 있으면 음영 프리미티브 부착
|
// 과열선/침체선 레이블이 있는 hlines 가 있으면 음영 프리미티브 부착
|
||||||
const prices = hlines.filter(h => h.visible !== false).map(h => h.price);
|
const prices = hlines.filter(h => h.visible !== false).map(h => h.price);
|
||||||
const hasZone = hlines.some(h => {
|
const hasZone = hlines.some(h => {
|
||||||
const lbl = h.label ?? getHLineLabel(h.price, prices);
|
const lbl = h.label ?? getHLineLabel(h.price, prices);
|
||||||
return (lbl === '과열선' || lbl === '침체선') && h.visible !== false;
|
return (lbl === '과열선' || lbl === '침체선') && h.visible !== false;
|
||||||
});
|
});
|
||||||
const hasBg = config.hlinesBackground?.visible !== false;
|
const hasBg = config.hlinesBackground?.visible !== false;
|
||||||
if (hasZone || hasBg) {
|
if (hasZone || hasBg) {
|
||||||
fillPrimitive = new IndicatorFillPrimitive(hlines, config.hlinesBackground);
|
fillPrimitive = new IndicatorFillPrimitive(hlines, config.hlinesBackground);
|
||||||
firstSeries.attachPrimitive(fillPrimitive);
|
hlineHost.attachPrimitive(fillPrimitive);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1482,11 +1491,12 @@ export class ChartManager {
|
|||||||
const entry = this.indicators.get(id);
|
const entry = this.indicators.get(id);
|
||||||
if (!entry) return false;
|
if (!entry) return false;
|
||||||
if (entry.seriesList.length > 0) {
|
if (entry.seriesList.length > 0) {
|
||||||
|
const hlineHost = hlineAnchorSeries(entry);
|
||||||
for (const pl of entry.hlineRefs) {
|
for (const pl of entry.hlineRefs) {
|
||||||
try { entry.seriesList[0].removePriceLine(pl); } catch { /* ok */ }
|
try { hlineHost?.removePriceLine(pl); } catch { /* ok */ }
|
||||||
}
|
}
|
||||||
if (entry.fillPrimitive) {
|
if (entry.fillPrimitive) {
|
||||||
try { entry.seriesList[0].detachPrimitive(entry.fillPrimitive); } catch { /* ok */ }
|
try { hlineHost?.detachPrimitive(entry.fillPrimitive); } catch { /* ok */ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (entry.cloudPlugin) {
|
if (entry.cloudPlugin) {
|
||||||
@@ -1518,8 +1528,8 @@ export class ChartManager {
|
|||||||
try { this.mainSeries?.detachPrimitive(entry.cloudPlugin); } catch { /* ok */ }
|
try { this.mainSeries?.detachPrimitive(entry.cloudPlugin); } catch { /* ok */ }
|
||||||
for (const s of entry.seriesList) { try { s.detachPrimitive(entry.cloudPlugin); } catch { /* ok */ } }
|
for (const s of entry.seriesList) { try { s.detachPrimitive(entry.cloudPlugin); } catch { /* ok */ } }
|
||||||
}
|
}
|
||||||
if (entry.fillPrimitive && entry.seriesList[0]) {
|
if (entry.fillPrimitive) {
|
||||||
try { entry.seriesList[0].detachPrimitive(entry.fillPrimitive); } catch { /* ok */ }
|
try { hlineAnchorSeries(entry)?.detachPrimitive(entry.fillPrimitive); } catch { /* ok */ }
|
||||||
}
|
}
|
||||||
detachBbBandFill(entry);
|
detachBbBandFill(entry);
|
||||||
for (const s of entry.seriesList) { try { this.chart.removeSeries(s); } catch { /* ok */ } }
|
for (const s of entry.seriesList) { try { this.chart.removeSeries(s); } catch { /* ok */ } }
|
||||||
@@ -3524,7 +3534,7 @@ export class ChartManager {
|
|||||||
|
|
||||||
// ── hlines(과열선/중앙선/침체선) 동기화 ─────────────────────────────────
|
// ── hlines(과열선/중앙선/침체선) 동기화 ─────────────────────────────────
|
||||||
if (entry.seriesList.length > 0) {
|
if (entry.seriesList.length > 0) {
|
||||||
const firstSeries = entry.seriesList[0];
|
const hlineHost = hlineAnchorSeries(entry);
|
||||||
const hlines = config.hlines ?? def?.hlines ?? [];
|
const hlines = config.hlines ?? def?.hlines ?? [];
|
||||||
|
|
||||||
const toLineStyle2 = (s?: string): number => {
|
const toLineStyle2 = (s?: string): number => {
|
||||||
@@ -3535,19 +3545,19 @@ export class ChartManager {
|
|||||||
|
|
||||||
// 기존 price line 제거
|
// 기존 price line 제거
|
||||||
for (const pl of entry.hlineRefs) {
|
for (const pl of entry.hlineRefs) {
|
||||||
try { firstSeries.removePriceLine(pl); } catch { /* 이미 제거됨 */ }
|
try { hlineHost?.removePriceLine(pl); } catch { /* 이미 제거됨 */ }
|
||||||
}
|
}
|
||||||
entry.hlineRefs = [];
|
entry.hlineRefs = [];
|
||||||
|
|
||||||
if (entry.fillPrimitive) {
|
if (entry.fillPrimitive) {
|
||||||
try { firstSeries.detachPrimitive(entry.fillPrimitive); } catch { /* ok */ }
|
try { hlineHost?.detachPrimitive(entry.fillPrimitive); } catch { /* ok */ }
|
||||||
entry.fillPrimitive = undefined;
|
entry.fillPrimitive = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (indicatorVisible) {
|
if (indicatorVisible && hlineHost) {
|
||||||
for (const hl of hlines) {
|
for (const hl of hlines) {
|
||||||
if (hl.visible === false) continue;
|
if (hl.visible === false) continue;
|
||||||
const pl = firstSeries.createPriceLine({
|
const pl = hlineHost.createPriceLine({
|
||||||
price: hl.price,
|
price: hl.price,
|
||||||
color: hl.color,
|
color: hl.color,
|
||||||
lineWidth: Math.min(4, hl.lineWidth ?? 1) as 1|2|3|4,
|
lineWidth: Math.min(4, hl.lineWidth ?? 1) as 1|2|3|4,
|
||||||
@@ -3565,7 +3575,7 @@ export class ChartManager {
|
|||||||
const hasBg = config.hlinesBackground?.visible !== false;
|
const hasBg = config.hlinesBackground?.visible !== false;
|
||||||
if (hasZone || hasBg) {
|
if (hasZone || hasBg) {
|
||||||
entry.fillPrimitive = new IndicatorFillPrimitive(hlines, config.hlinesBackground);
|
entry.fillPrimitive = new IndicatorFillPrimitive(hlines, config.hlinesBackground);
|
||||||
firstSeries.attachPrimitive(entry.fillPrimitive);
|
hlineHost.attachPrimitive(entry.fillPrimitive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3873,14 +3883,14 @@ export class ChartManager {
|
|||||||
} as Parameters<ISeriesApi<SeriesType>['applyOptions']>[0]);
|
} as Parameters<ISeriesApi<SeriesType>['applyOptions']>[0]);
|
||||||
} catch { /* ok */ }
|
} catch { /* ok */ }
|
||||||
}
|
}
|
||||||
const first = entry.seriesList[0];
|
const hlineHost = hlineAnchorSeries(entry);
|
||||||
if (first) {
|
if (hlineHost) {
|
||||||
for (const pl of entry.hlineRefs) {
|
for (const pl of entry.hlineRefs) {
|
||||||
try { first.removePriceLine(pl); } catch { /* ok */ }
|
try { hlineHost.removePriceLine(pl); } catch { /* ok */ }
|
||||||
}
|
}
|
||||||
entry.hlineRefs = [];
|
entry.hlineRefs = [];
|
||||||
if (entry.fillPrimitive) {
|
if (entry.fillPrimitive) {
|
||||||
try { first.detachPrimitive(entry.fillPrimitive); } catch { /* ok */ }
|
try { hlineHost.detachPrimitive(entry.fillPrimitive); } catch { /* ok */ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (entry.cloudPlugin) {
|
if (entry.cloudPlugin) {
|
||||||
|
|||||||
@@ -88,6 +88,10 @@ function resolveMergedHLinePrice(
|
|||||||
savedPrice: number,
|
savedPrice: number,
|
||||||
defPrice: number,
|
defPrice: number,
|
||||||
): number {
|
): number {
|
||||||
|
// MACD·TRIX 등 registry 기본 중앙선이 0인 지표: 잘못 저장된 종가급 값(69872 등) 교정
|
||||||
|
if (roleLabel === '중앙선' && defPrice === 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (roleLabel === '중앙선' && savedPrice === 0 && defPrice !== 0) {
|
if (roleLabel === '중앙선' && savedPrice === 0 && defPrice !== 0) {
|
||||||
return defPrice;
|
return defPrice;
|
||||||
}
|
}
|
||||||
@@ -205,7 +209,7 @@ export function normalizeHLines(
|
|||||||
const hasUnder = withFixedLabels.some(h => getLabel(h) === '침체선');
|
const hasUnder = withFixedLabels.some(h => getLabel(h) === '침체선');
|
||||||
|
|
||||||
if (hasOver && hasCenter && hasUnder) {
|
if (hasOver && hasCenter && hasUnder) {
|
||||||
return withFixedLabels.sort((a, b) => b.price - a.price);
|
return clampZeroCenterHLines(withFixedLabels, defaults).sort((a, b) => b.price - a.price);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = [...withFixedLabels];
|
const result = [...withFixedLabels];
|
||||||
@@ -245,7 +249,22 @@ export function normalizeHLines(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.sort((a, b) => b.price - a.price);
|
return clampZeroCenterHLines(result, defaults).sort((a, b) => b.price - a.price);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** registry 기본 중앙선이 0인 지표에서 잘못 저장된 중앙선 가격을 0으로 고정 */
|
||||||
|
function clampZeroCenterHLines(hlines: HLineDef[], defaults?: HLineDef[]): HLineDef[] {
|
||||||
|
const defPrices = (defaults ?? []).map(d => d.price);
|
||||||
|
const defCenter = (defaults ?? []).find(
|
||||||
|
d => (d.label ?? getHLineLabel(d.price, defPrices)) === '중앙선',
|
||||||
|
);
|
||||||
|
if (defCenter?.price !== 0) return hlines;
|
||||||
|
|
||||||
|
const prices = hlines.map(h => h.price);
|
||||||
|
return hlines.map(h => {
|
||||||
|
const lbl = h.label ?? getHLineLabel(h.price, prices);
|
||||||
|
return lbl === '중앙선' ? { ...h, price: 0 } : h;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IndicatorDef {
|
export interface IndicatorDef {
|
||||||
|
|||||||
Reference in New Issue
Block a user