투자관리 차트탭 기능 수정

This commit is contained in:
Macbook
2026-05-31 16:31:25 +09:00
parent 1b7c39e11f
commit 78f00dbaa5
14 changed files with 935 additions and 111 deletions
+36 -34
View File
@@ -264,6 +264,9 @@ export class ChartManager {
/** 알림 목록 미니 차트 등 고정 높이 — pane 최소 px·비율 완화 */
private _compactPaneLayout = false;
/** 캔들+거래량 그룹 vs 보조 pane 그룹 stretch 비율 (투자관리·백테스트 분석 차트) */
private _paneAreaRatio: { candle: number; aux: number } | null = null;
constructor(
container: HTMLElement,
theme: Theme,
@@ -2054,10 +2057,6 @@ export class ChartManager {
private _viewportLwcHooked = false;
private readonly _lwcViewportHandler = (): void => { this._notifyViewport(); };
/** 캔들 pane 시간축 — 드래그 패닝 중 transform 동기화용 */
private _candleAxisPanActive = false;
private _candleAxisPanOffsetPx = 0;
private _notifyViewport(): void {
for (const cb of this._viewportListeners) {
try { cb(); } catch { /* ok */ }
@@ -2096,29 +2095,6 @@ export class ChartManager {
this._notifyViewport();
}
/** 캔들 pane 시간축 드래그 패닝 종료 — 라벨 위치를 최종 논리 범위로 재정렬 */
endCandleAxisPan(): void {
if (!this._candleAxisPanActive) return;
this._candleAxisPanActive = false;
this._candleAxisPanOffsetPx = 0;
this._notifyViewport();
}
isCandleAxisPanActive(): boolean {
return this._candleAxisPanActive;
}
getCandleAxisPanOffsetPx(): number {
return this._candleAxisPanOffsetPx;
}
private _ensureCandleAxisPan(): void {
if (this._candleAxisPanActive) return;
this._candleAxisPanActive = true;
this._candleAxisPanOffsetPx = 0;
this._notifyViewport();
}
/** visible logical range → 플롯 X (LWC 좌표 API와 동일한 선형 매핑) */
private _logicalToAxisX(logical: number, lr: { from: number; to: number }, plotWidth: number): number {
const span = lr.to - lr.from;
@@ -3040,9 +3016,6 @@ export class ChartManager {
options?: { allowVerticalPan?: boolean },
): void {
if (deltaX !== 0) {
this._ensureCandleAxisPan();
this._candleAxisPanOffsetPx += deltaX;
const ts = this.chart.timeScale();
const lr = ts.getVisibleLogicalRange();
if (lr) {
@@ -3272,6 +3245,11 @@ export class ChartManager {
return this.rawBars.length;
}
/** 현재 차트에 로드된 캔들 스냅샷 (과거 추가 로드·백테스트 재계산용) */
getRawBars(): OHLCVBar[] {
return this.rawBars.slice();
}
/** 두 시각(초) 사이에 포함된 봉 개수 — 기간(날짜 범위) 측정 라벨용 */
countBarsBetweenTime(t0: number, t1: number): number {
const lo = Math.min(t0, t1);
@@ -3296,15 +3274,15 @@ export class ChartManager {
* 사용자가 보고 있던 구간이 유지되도록 합니다.
* - 모든 보조지표를 전체 데이터 기준으로 재계산합니다.
*/
async prependBars(olderBars: OHLCVBar[]): Promise<void> {
if (!this.mainSeries || olderBars.length === 0) return;
async prependBars(olderBars: OHLCVBar[]): Promise<number> {
if (!this.mainSeries || olderBars.length === 0) return 0;
const firstExistingTime = this.rawBars[0]?.time as number | undefined;
const newBars = firstExistingTime !== undefined
? olderBars.filter(b => (b.time as number) < firstExistingTime)
: olderBars;
if (newBars.length === 0) return;
if (newBars.length === 0) return 0;
// 현재 가시 논리 범위 저장 (prepend 후 복원용)
const logicalRange = this.chart.timeScale().getVisibleLogicalRange();
@@ -3349,6 +3327,7 @@ export class ChartManager {
// 보조지표 전체 재계산 (새 데이터가 왼쪽에 추가됐으므로 전체 갱신 필요)
await this._refreshAllIndicatorsData();
return newBars.length;
}
/** 모든 보조지표를 현재 rawBars 기준으로 전체 재계산하고 시리즈 데이터를 교체합니다. */
@@ -3628,6 +3607,21 @@ export class ChartManager {
*
* @param availableHeight 외부에서 측정한 실제 가용 높이(px). 스크롤 필요 여부 판단에만 사용.
*/
/**
* 캔들 영역 vs 보조지표 영역 높이 비율 (예: 보조 1개 → 2:1, 2~5개 → 1:1, 6+ → 1:2)
* auxWeight 가 0 이면 비율 해제.
*/
setPaneAreaRatio(candleWeight: number, auxWeight: number): void {
if (auxWeight <= 0) {
this._paneAreaRatio = null;
} else {
this._paneAreaRatio = {
candle: Math.max(0.1, candleWeight),
aux: Math.max(0.1, auxWeight),
};
}
}
private _volumeFrac(H: number): number {
if (!this._volumeVisible || this._candleOnlyLayout) return 0;
return Math.min(Math.max(60 / H, 0.04), 0.12);
@@ -3750,7 +3744,15 @@ export class ChartManager {
? this._lastLayoutAvailableHeight
: this.container.clientHeight);
const N = this._activeIndicatorPaneIndices().size;
const { mainFrac } = this._paneHeightFractions(N, this._volumeFrac(H), H);
const volFrac = this._volumeFrac(H);
let mainFrac: number;
if (this._paneAreaRatio && N > 0) {
const total = this._paneAreaRatio.candle + this._paneAreaRatio.aux;
const candleGroupFrac = this._paneAreaRatio.candle / total;
mainFrac = Math.max(0.12, candleGroupFrac - volFrac);
} else {
mainFrac = this._paneHeightFractions(N, volFrac, H).mainFrac;
}
return this._applyPaneStretchFactors(mainFrac, availableHeight);
}