fix: 일목 구름을 rawBars 기준으로 계산해 dev/프로덕션 동일화
서버 프로덕션 번들에서 lightweight-charts-indicators plot3·plot4가 spanA와 spanB에 동일 값을 줄 수 있어 구름이 사라짐. 로컬 vite dev는 정상. plot0~4 의존을 제거하고 OHLCV rawBars만으로 선행스팬·구름을 계산한다. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1368,7 +1368,7 @@ export class ChartManager {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 특정 바 인덱스에서 Donchian 중선을 계산 */
|
/** 특정 바 인덱스에서 Donchian 중선 (전환선·기준선·선행스팬B 공통) */
|
||||||
private _donchianAt(idx: number, period: number): number | null {
|
private _donchianAt(idx: number, period: number): number | null {
|
||||||
const start = idx - period + 1;
|
const start = idx - period + 1;
|
||||||
if (start < 0) return null;
|
if (start < 0) return null;
|
||||||
@@ -1380,33 +1380,31 @@ export class ChartManager {
|
|||||||
return (hi + lo) / 2;
|
return (hi + lo) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 구름 데이터 빌드: 과거(역사 데이터) + 미래 26봉 포함 */
|
/**
|
||||||
|
* 일목 구름: rawBars만으로 계산 (vite dev / 프로덕션 번들 동일 결과).
|
||||||
|
* lightweight-charts-indicators 의 plot3·plot4는 서버 프로덕션 빌드에서
|
||||||
|
* spanA===spanB 로 붕괴할 수 있어 사용하지 않는다.
|
||||||
|
*/
|
||||||
private _buildIchimokuCloudData(
|
private _buildIchimokuCloudData(
|
||||||
_spanAPlot: PlotData,
|
conversionPeriods: number,
|
||||||
_spanBPlot: PlotData,
|
basePeriods: number,
|
||||||
convPlot: PlotData,
|
|
||||||
basePlot: PlotData,
|
|
||||||
displacement: number,
|
displacement: number,
|
||||||
laggingPeriod: number,
|
laggingPeriod: number,
|
||||||
): IchimokuCloudPoint[] {
|
): IchimokuCloudPoint[] {
|
||||||
// 라이브러리의 plot3/plot4(SpanA/SpanB)는 프로덕션 빌드에서
|
|
||||||
// 동일한 값을 반환하는 버그가 있으므로, convPlot(전환선)/basePlot(기준선)과
|
|
||||||
// rawBars 기반 _donchianAt으로 직접 재계산한다.
|
|
||||||
const N = this.rawBars.length;
|
const N = this.rawBars.length;
|
||||||
const historical: IchimokuCloudPoint[] = [];
|
const historical: IchimokuCloudPoint[] = [];
|
||||||
|
|
||||||
for (let i = displacement - 1; i < N; i++) {
|
for (let i = displacement; i < N; i++) {
|
||||||
const srcIdx = i - (displacement - 1);
|
const srcIdx = i - displacement;
|
||||||
const conv = convPlot[srcIdx]?.value;
|
const tenkan = this._donchianAt(srcIdx, conversionPeriods);
|
||||||
const base = basePlot[srcIdx]?.value;
|
const kijun = this._donchianAt(srcIdx, basePeriods);
|
||||||
if (conv == null || isNaN(conv) || base == null || isNaN(base)) continue;
|
if (tenkan == null || kijun == null) continue;
|
||||||
const spanA = (conv + base) / 2;
|
const spanA = (tenkan + kijun) / 2;
|
||||||
const spanB = this._donchianAt(srcIdx, laggingPeriod);
|
const spanB = this._donchianAt(srcIdx, laggingPeriod);
|
||||||
if (spanB === null) continue;
|
if (spanB == null) continue;
|
||||||
historical.push({ time: this.rawBars[i].time, spanA, spanB });
|
historical.push({ time: this.rawBars[i].time, spanA, spanB });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 미래 구름
|
|
||||||
if (N < 2) return historical;
|
if (N < 2) return historical;
|
||||||
const interval = this.rawBars[N - 1].time - this.rawBars[N - 2].time;
|
const interval = this.rawBars[N - 1].time - this.rawBars[N - 2].time;
|
||||||
const lastTime = this.rawBars[N - 1].time;
|
const lastTime = this.rawBars[N - 1].time;
|
||||||
@@ -1415,12 +1413,12 @@ export class ChartManager {
|
|||||||
for (let k = 1; k <= displacement; k++) {
|
for (let k = 1; k <= displacement; k++) {
|
||||||
const srcIdx = N - displacement - 1 + k;
|
const srcIdx = N - displacement - 1 + k;
|
||||||
if (srcIdx < 0 || srcIdx >= N) continue;
|
if (srcIdx < 0 || srcIdx >= N) continue;
|
||||||
const conv = convPlot[srcIdx]?.value;
|
const tenkan = this._donchianAt(srcIdx, conversionPeriods);
|
||||||
const base = basePlot[srcIdx]?.value;
|
const kijun = this._donchianAt(srcIdx, basePeriods);
|
||||||
if (conv == null || isNaN(conv) || base == null || isNaN(base)) continue;
|
if (tenkan == null || kijun == null) continue;
|
||||||
const spanA = (conv + base) / 2;
|
const spanA = (tenkan + kijun) / 2;
|
||||||
const spanB = this._donchianAt(srcIdx, laggingPeriod);
|
const spanB = this._donchianAt(srcIdx, laggingPeriod);
|
||||||
if (spanB === null) continue;
|
if (spanB == null) continue;
|
||||||
future.push({ time: lastTime + k * interval, spanA, spanB });
|
future.push({ time: lastTime + k * interval, spanA, spanB });
|
||||||
}
|
}
|
||||||
return [...historical, ...future];
|
return [...historical, ...future];
|
||||||
@@ -1782,17 +1780,13 @@ export class ChartManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const spanAPlot = plots['plot3'] as PlotData | undefined;
|
|
||||||
const spanBPlot = plots['plot4'] as PlotData | undefined;
|
|
||||||
const convPlot = plots['plot0'] as PlotData ?? [];
|
|
||||||
const basePlot = plots['plot1'] as PlotData ?? [];
|
|
||||||
if (!spanAPlot || !spanBPlot) return;
|
|
||||||
|
|
||||||
const { senkou: displacement } = resolveIchimokuDisplacements(config.params);
|
const { senkou: displacement } = resolveIchimokuDisplacements(config.params);
|
||||||
|
const conversionPeriods = (config.params?.conversionPeriods as number) ?? 9;
|
||||||
|
const basePeriods = (config.params?.basePeriods as number) ?? 26;
|
||||||
const laggingPeriod = (config.params?.laggingSpan2Periods as number) ?? 52;
|
const laggingPeriod = (config.params?.laggingSpan2Periods as number) ?? 52;
|
||||||
|
|
||||||
const cloud = this._buildIchimokuCloudData(
|
const cloud = this._buildIchimokuCloudData(
|
||||||
spanAPlot, spanBPlot, convPlot, basePlot, displacement, laggingPeriod,
|
conversionPeriods, basePeriods, displacement, laggingPeriod,
|
||||||
);
|
);
|
||||||
plugin.setCloudData(cloud.length >= 2 ? cloud : []);
|
plugin.setCloudData(cloud.length >= 2 ? cloud : []);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user