From 91d52e9e26a81f8d4d2e15800cac38a63a850f51 Mon Sep 17 00:00:00 2001 From: Macbook Date: Tue, 9 Jun 2026 09:00:00 +0900 Subject: [PATCH] =?UTF-8?q?OBV=20=EC=8B=A0=ED=98=B8=EC=84=A0=20=EC=A0=90?= =?UTF-8?q?=EC=84=A0=20=EC=B2=98=EB=A6=AC=EB=A1=9C=20=EB=B3=B8=EC=84=A0?= =?UTF-8?q?=EA=B3=BC=20=EC=8B=9C=EA=B0=81=20=EA=B5=AC=EB=B6=84=20=EB=B3=B4?= =?UTF-8?q?=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 진단 로그로 두 시리즈가 정상 생성됨을 확인 (series:2, visible:true, color:#2196F3) - 미니차트는 800봉 누적 OBV 계산 → 마지막 200봉 구간에서 OBV와 SMA(9)가 수렴, 주황 신호선이 파란 본선을 덮어 1개 선처럼 보이는 것이 원인 - registry plot1에 lineStyle:'dashed' 추가 - normalizeSignalLineIndicatorPlots OBV 분기에서 plot0=solid, plot1=dashed 강제 Co-authored-by: Cursor --- frontend/src/utils/ChartManager.ts | 22 ---------------------- frontend/src/utils/indicatorRegistry.ts | 10 +++++++++- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/frontend/src/utils/ChartManager.ts b/frontend/src/utils/ChartManager.ts index 63d3845..dc8fa00 100644 --- a/frontend/src/utils/ChartManager.ts +++ b/frontend/src/utils/ChartManager.ts @@ -928,23 +928,6 @@ export class ChartManager { ); } - // ── OBV 진단 로그 (개발용) ──────────────────────────────────────────── - if (config.type === 'OBV') { - const plotKeys = Object.keys(result.plots); - const seriesIds = seriesMeta.map(m => m.plotId); - const volSum = this.rawBars.reduce((s, b) => s + (Number(b.volume) || 0), 0); - // eslint-disable-next-line no-console - console.error( - '[OBV Debug] series:', seriesList.length, - '| plotIds:', seriesIds, - '| resultKeys:', plotKeys, - '| plotVisibility:', JSON.stringify(config.plotVisibility), - '| plot0Color:', config.plots?.find(p => p.id === 'plot0')?.color, - '| bars:', this.rawBars.length, - '| volSum>0:', volSum > 0, - '| pane:', pane, - ); - } // hlines(과열선/중앙선/침체선)는 첫 번째 시리즈에만 한 번 생성 (중복 방지) const hlineRefs: IPriceLine[] = []; @@ -2921,11 +2904,6 @@ export class ChartManager { } else { opts['color'] = plot.color ?? '#aaa'; } - // ── OBV plot0 스타일 진단 로그 ────────────────────────────────────────── - if (entry.type === 'OBV' && pid === 'plot0') { - // eslint-disable-next-line no-console - console.error('[OBV Style] plot0 opts:', JSON.stringify({ visible: opts['visible'], color: opts['color'], lineWidth: opts['lineWidth'] })); - } // eslint-disable-next-line @typescript-eslint/no-explicit-any series.applyOptions(opts as any); }); diff --git a/frontend/src/utils/indicatorRegistry.ts b/frontend/src/utils/indicatorRegistry.ts index 01db767..c0079d8 100644 --- a/frontend/src/utils/indicatorRegistry.ts +++ b/frontend/src/utils/indicatorRegistry.ts @@ -349,7 +349,7 @@ export const INDICATOR_REGISTRY: IndicatorDef[] = [ { type:'HistoricalVolatility',name:'Historical Volatility', koreanName:'역사적변동성', shortName:'HV', category:'Volatility', overlay:false, defaultParams:{length:20, annualBars:252}, plots:[{id:'plot0',title:'HV', color:'#E91E63',type:'line',lineWidth:2}], description:'역사적 변동성 (연화)' }, // ── Volume ──────────────────────────────────────────────────────────────── - { type:'OBV', name:'On Balance Volume', koreanName:'잔량균형지수', shortName:'OBV', category:'Volume', overlay:false, defaultParams:{maType:'SMA', maLength:9}, plots:[{id:'plot0',title:'OBV', color:'#2196F3',type:'line',lineWidth:2},{id:'plot1',title:'Signal',color:'#FF9800',type:'line',lineWidth:1}], hlines:[], description:'잔량 균형 지수 (OBV + 이동평균 신호선)' }, + { type:'OBV', name:'On Balance Volume', koreanName:'잔량균형지수', shortName:'OBV', category:'Volume', overlay:false, defaultParams:{maType:'SMA', maLength:9}, plots:[{id:'plot0',title:'OBV', color:'#2196F3',type:'line',lineWidth:2},{id:'plot1',title:'Signal',color:'#FF9800',type:'line',lineWidth:1,lineStyle:'dashed'}], hlines:[], description:'잔량 균형 지수 (OBV + 이동평균 신호선)' }, { type:'VR', name:'Volume Ratio', koreanName:'거래량비율', shortName:'VR', category:'Volume', overlay:false, defaultParams:{length:10}, plots:[{id:'plot0',title:'VR', color:'#AB47BC',type:'line',lineWidth:2}], hlines:[{price:200,color:'#EF5350'},{price:100,color:'#607D8B'},{price:50,color:'#4CAF50'}], description:'거래량 비율 (VR)' }, { type:'Disparity', name:'Disparity Index', koreanName:'이격도', shortName:'Disp', category:'Oscillators', overlay:false, defaultParams:{ultraLength:5, shortLength:10, midLength:20, longLength:60, src:'close'}, plots:[{id:'plot0',title:'초단기',color:'#E91E63',type:'line',lineWidth:1},{id:'plot1',title:'단기',color:'#FF9800',type:'line',lineWidth:1},{id:'plot2',title:'중기',color:'#4CAF50',type:'line',lineWidth:1},{id:'plot3',title:'장기',color:'#2196F3',type:'line',lineWidth:1}], hlines:[{price:100,color:'#607D8B'}], description:'이격도 (종가÷이평×100)' }, { type:'Psychological', name:'Psychological Line', koreanName:'심리도', shortName:'Psy', category:'Oscillators', overlay:false, defaultParams:{length:12}, plots:[{id:'plot0',title:'심리도',color:'#00BCD4',type:'line',lineWidth:2}], hlines:[{price:75,color:'#EF5350',label:'과열선'},{price:50,color:'#607D8B',label:'중앙선'},{price:25,color:'#4CAF50',label:'침체선'}], description:'심리도 (상승일 비율)' }, @@ -481,6 +481,14 @@ function normalizeSignalLineIndicatorPlots( type: 'line' as const, color: reg.color, lineWidth: Math.max(2, normalized.lineWidth ?? reg.lineWidth ?? 2), + lineStyle: 'solid' as const, + }; + } + // OBV 신호선 — 값이 본선과 거의 같더라도 점선으로 시각적 구분 보장 + if (p.id === 'plot1') { + return { + ...normalized, + lineStyle: 'dashed' as const, }; } return normalized;