diff --git a/frontend/src/components/TradeNotificationListPage.tsx b/frontend/src/components/TradeNotificationListPage.tsx index 48e4ca2..b2ab159 100644 --- a/frontend/src/components/TradeNotificationListPage.tsx +++ b/frontend/src/components/TradeNotificationListPage.tsx @@ -63,6 +63,7 @@ import { type TradeNotificationGridPresetId, } from '../utils/tradeNotificationGridPresets'; import { sortTradeNotifications } from '../utils/tradeListSort'; +import { getKoreanName } from '../utils/marketNameCache'; type RightTab = 'trade' | 'orderbook'; @@ -380,9 +381,14 @@ export const TradeNotificationListPage: React.FC = ({ items = items.filter(n => { const mkt = n.market.toLowerCase(); const sym = n.market.includes('-') ? n.market.split('-')[1].toLowerCase() : mkt; + const ko = ( + tickers?.get(n.market)?.koreanName ?? + getKoreanName(n.market) + ).toLowerCase(); const strat = (n.strategyName ?? '').toLowerCase(); const sigKo = n.signalType === 'BUY' ? '매수' : '매도'; return ( + ko.includes(normalizedQuery) || mkt.includes(normalizedQuery) || sym.includes(normalizedQuery) || strat.includes(normalizedQuery) || @@ -391,7 +397,7 @@ export const TradeNotificationListPage: React.FC = ({ }); } return items; - }, [allNotifications, filter, candleFilter, normalizedQuery]); + }, [allNotifications, filter, candleFilter, normalizedQuery, tickers]); const sorted = useMemo( () => sortTradeNotifications(filtered, listSort), @@ -525,7 +531,7 @@ export const TradeNotificationListPage: React.FC = ({ setQuery(e.target.value)} aria-label="알림 목록 검색" diff --git a/frontend/src/components/tradeNotification/TradeSignalMiniChart.tsx b/frontend/src/components/tradeNotification/TradeSignalMiniChart.tsx index c2a9675..edbb077 100644 --- a/frontend/src/components/tradeNotification/TradeSignalMiniChart.tsx +++ b/frontend/src/components/tradeNotification/TradeSignalMiniChart.tsx @@ -21,6 +21,7 @@ import { DEFAULT_DISPLAY_TIMEZONE } from '../../utils/timezone'; import { useIndicatorSettings } from '../../hooks/useIndicatorSettings'; import { useSessionCandleOverlayControls } from '../../hooks/useSessionCandleOverlayControls'; import { ensurePaperChartOverlays } from '../../utils/strategyToChartIndicators'; +import { mergeGlobalDefaultsOntoChartIndicators } from '../../utils/indicatorMainConfig'; import { enrichIndicatorConfig } from '../../utils/indicatorRegistry'; import { applyNotificationSignalMarkers, @@ -69,7 +70,11 @@ const TradeSignalMiniChart: React.FC = ({ signalMarkerRef.current = signalMarker; const baseIndicators = useMemo( - () => ensurePaperChartOverlays(indicators, getParams, getVisualConfig).map(enrichIndicatorConfig), + () => mergeGlobalDefaultsOntoChartIndicators( + ensurePaperChartOverlays(indicators, getParams, getVisualConfig), + getParams, + getVisualConfig, + ).map(enrichIndicatorConfig), [indicators, getParams, getVisualConfig], ); diff --git a/frontend/src/utils/ChartManager.ts b/frontend/src/utils/ChartManager.ts index d56f02f..35b60ce 100644 --- a/frontend/src/utils/ChartManager.ts +++ b/frontend/src/utils/ChartManager.ts @@ -225,6 +225,21 @@ function sortDualLinePlotDefs(plotDefs: PlotDef[]): PlotDef[] { ); } +/** + * OBV 등 이중 plot — 설정 lineWidth 기준 굵은 선 먼저(하단), 가는 선 나중(상단). + * 값이 겹쳐도 점선·실선·색상(설정값)이 함께 보이도록 z-order만 조정. + */ +function sortPlotDefsForSeriesAdd(type: string, plotDefs: PlotDef[]): PlotDef[] { + const sorted = sortDualLinePlotDefs(plotDefs); + if (type !== 'OBV') return sorted; + return [...sorted].sort((a, b) => { + const wA = a.lineWidth ?? 1; + const wB = b.lineWidth ?? 1; + if (wA !== wB) return wB - wA; + return DUAL_LINE_PLOT_ORDER.indexOf(a.id) - DUAL_LINE_PLOT_ORDER.indexOf(b.id); + }); +} + export class ChartManager { private chart: IChartApi; private container: HTMLElement; @@ -761,7 +776,7 @@ export class ChartManager { indicatorHidden: boolean, def: ReturnType, ): void { - const plotDefs = sortDualLinePlotDefs(config.plots ?? def?.plots ?? []); + const plotDefs = sortPlotDefsForSeriesAdd(config.type, config.plots ?? def?.plots ?? []); const existingIds = new Set(seriesMeta.map(m => m.plotId)); const indicatorPriceFormat = priceFormatForIndicatorPane(pane); @@ -882,7 +897,7 @@ export class ChartManager { const plotDefsRaw = config.plots ?? def?.plots ?? []; const plotDefs = DUAL_LINE_INDICATOR_TYPES.has(config.type) - ? sortDualLinePlotDefs(plotDefsRaw) + ? sortPlotDefsForSeriesAdd(config.type, plotDefsRaw) : plotDefsRaw; for (const plotDef of plotDefs) { diff --git a/frontend/src/utils/strategyToChartIndicators.ts b/frontend/src/utils/strategyToChartIndicators.ts index bfbca51..5d6d4e9 100644 --- a/frontend/src/utils/strategyToChartIndicators.ts +++ b/frontend/src/utils/strategyToChartIndicators.ts @@ -13,6 +13,7 @@ import { type HLineDef, } from './indicatorRegistry'; import { initializeIndicatorConfigForEditor } from './indicatorSettingsEditor'; +import { mergeGlobalDefaultsOntoChartIndicators } from './indicatorMainConfig'; import { buildMainIndicatorConfig } from './indicatorMainConfig'; import { normalizeSmaConfig, smaPeriodKey } from './smaConfig'; import { normalizeIchimokuConfig } from './ichimokuConfig'; @@ -266,7 +267,11 @@ export function buildStrategyChartIndicators( const cfg = buildOneIndicator(ref, getParams, getVisual); if (cfg) result.push(cfg); } - return result; + return mergeGlobalDefaultsOntoChartIndicators( + result, + getParams, + (type, defaultPlots, defaultHlines) => getVisual(type, defaultPlots ?? [], defaultHlines ?? []), + ); } const MA_OVERLAY_TYPES = new Set(['SMA', 'EMA']);