실시간 차트 보조지표 값 소수점 표시

This commit is contained in:
Macbook
2026-05-30 10:39:50 +09:00
parent 33c2d4b337
commit c7ba13e7ed
23 changed files with 226 additions and 40 deletions
@@ -97,7 +97,12 @@ public class GcAppSettings {
@Builder.Default @Builder.Default
private Boolean btShowPrice = true; private Boolean btShowPrice = true;
/** 보조지표 우측 가격축 라벨·설명·금액 하이라이트 (기본 true) */ /** 캔들 영역(오버레이) 우측 가격축 라벨·설명·금액 하이라이트 (기본 true) */
@Column(name = "chart_candle_area_price_labels", nullable = false)
@Builder.Default
private Boolean chartCandleAreaPriceLabels = true;
/** 하단 보조지표 pane 우측 가격축 라벨·설명·금액 하이라이트 (기본 true) */
@Column(name = "chart_series_price_labels", nullable = false) @Column(name = "chart_series_price_labels", nullable = false)
@Builder.Default @Builder.Default
private Boolean chartSeriesPriceLabels = true; private Boolean chartSeriesPriceLabels = true;
@@ -90,6 +90,8 @@ public class AppSettingsService {
Boolean.parseBoolean(d.get("btAutoPopup").toString())); Boolean.parseBoolean(d.get("btAutoPopup").toString()));
if (d.containsKey("btShowPrice")) s.setBtShowPrice( if (d.containsKey("btShowPrice")) s.setBtShowPrice(
Boolean.parseBoolean(d.get("btShowPrice").toString())); Boolean.parseBoolean(d.get("btShowPrice").toString()));
if (d.containsKey("chartCandleAreaPriceLabels")) s.setChartCandleAreaPriceLabels(
Boolean.parseBoolean(d.get("chartCandleAreaPriceLabels").toString()));
if (d.containsKey("chartSeriesPriceLabels")) s.setChartSeriesPriceLabels( if (d.containsKey("chartSeriesPriceLabels")) s.setChartSeriesPriceLabels(
Boolean.parseBoolean(d.get("chartSeriesPriceLabels").toString())); Boolean.parseBoolean(d.get("chartSeriesPriceLabels").toString()));
if (d.containsKey("chartVolumeVisible")) s.setChartVolumeVisible( if (d.containsKey("chartVolumeVisible")) s.setChartVolumeVisible(
@@ -198,6 +200,7 @@ public class AppSettingsService {
m.put("syncOptions", parseJson(s.getSyncOptionsJson())); m.put("syncOptions", parseJson(s.getSyncOptionsJson()));
m.put("btAutoPopup", s.getBtAutoPopup() != null ? s.getBtAutoPopup() : true); m.put("btAutoPopup", s.getBtAutoPopup() != null ? s.getBtAutoPopup() : true);
m.put("btShowPrice", s.getBtShowPrice() != null ? s.getBtShowPrice() : true); m.put("btShowPrice", s.getBtShowPrice() != null ? s.getBtShowPrice() : true);
m.put("chartCandleAreaPriceLabels", s.getChartCandleAreaPriceLabels() != null ? s.getChartCandleAreaPriceLabels() : true);
m.put("chartSeriesPriceLabels", s.getChartSeriesPriceLabels() != null ? s.getChartSeriesPriceLabels() : true); m.put("chartSeriesPriceLabels", s.getChartSeriesPriceLabels() != null ? s.getChartSeriesPriceLabels() : true);
m.put("chartVolumeVisible", s.getChartVolumeVisible() != null ? s.getChartVolumeVisible() : true); m.put("chartVolumeVisible", s.getChartVolumeVisible() != null ? s.getChartVolumeVisible() : true);
m.put("chartLiveReceiveHighlight", s.getChartLiveReceiveHighlight() != null ? s.getChartLiveReceiveHighlight() : true); m.put("chartLiveReceiveHighlight", s.getChartLiveReceiveHighlight() != null ? s.getChartLiveReceiveHighlight() : true);
@@ -0,0 +1,12 @@
-- ============================================================
-- V49: 캔들 차트 영역(오버레이 지표) 가격축 라벨·설명 — 보조지표 pane 과 분리
-- chart_series_price_labels = 하단 보조지표 pane (기존)
-- chart_candle_area_price_labels = 캔들·거래량 pane 위 오버레이 (pane 0~1)
-- ============================================================
ALTER TABLE gc_app_settings
ADD COLUMN chart_candle_area_price_labels TINYINT(1) NOT NULL DEFAULT 1
COMMENT '캔들 영역 오버레이 지표 가격축 라벨·설명 (1=ON, 0=OFF)';
UPDATE gc_app_settings
SET chart_candle_area_price_labels = chart_series_price_labels;
+23 -6
View File
@@ -634,6 +634,7 @@ function App() {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [appSettingsLoaded, dbLoaded]); }, [appSettingsLoaded, dbLoaded]);
const chartCandleAreaPriceLabels = appDefaults.chartCandleAreaPriceLabels ?? true;
const chartSeriesPriceLabels = appDefaults.chartSeriesPriceLabels ?? true; const chartSeriesPriceLabels = appDefaults.chartSeriesPriceLabels ?? true;
const chartVolumeVisible = appDefaults.chartVolumeVisible ?? true; const chartVolumeVisible = appDefaults.chartVolumeVisible ?? true;
const chartLiveReceiveHighlight = appDefaults.chartLiveReceiveHighlight ?? true; const chartLiveReceiveHighlight = appDefaults.chartLiveReceiveHighlight ?? true;
@@ -1007,9 +1008,13 @@ function App() {
useEffect(() => { useEffect(() => {
if (!appSettingsLoaded) return; if (!appSettingsLoaded) return;
managerRef.current?.setSeriesPriceLabelsEnabled(chartSeriesPriceLabels); managerRef.current?.setCandleAreaPriceLabelsEnabled(chartCandleAreaPriceLabels);
slotRefs.current.forEach(slot => slot?.setSeriesPriceLabelsEnabled(chartSeriesPriceLabels)); managerRef.current?.setIndicatorAreaPriceLabelsEnabled(chartSeriesPriceLabels);
}, [appSettingsLoaded, chartSeriesPriceLabels]); slotRefs.current.forEach(slot => {
slot?.setCandleAreaPriceLabelsEnabled(chartCandleAreaPriceLabels);
slot?.setIndicatorAreaPriceLabelsEnabled(chartSeriesPriceLabels);
});
}, [appSettingsLoaded, chartCandleAreaPriceLabels, chartSeriesPriceLabels]);
useEffect(() => { useEffect(() => {
if (!appSettingsLoaded) return; if (!appSettingsLoaded) return;
@@ -1789,6 +1794,7 @@ function App() {
<TrendSearchPage <TrendSearchPage
theme={theme} theme={theme}
chartRealtimeSource={(appDefaults.chartRealtimeSource ?? 'BACKEND_STOMP') as ChartRealtimeSource} chartRealtimeSource={(appDefaults.chartRealtimeSource ?? 'BACKEND_STOMP') as ChartRealtimeSource}
chartCandleAreaPriceLabels={appDefaults.chartCandleAreaPriceLabels}
chartSeriesPriceLabels={appDefaults.chartSeriesPriceLabels} chartSeriesPriceLabels={appDefaults.chartSeriesPriceLabels}
tickers={marketTickers} tickers={marketTickers}
/> />
@@ -1858,11 +1864,17 @@ function App() {
onAddIndicatorToChart={handleAddIndicator} onAddIndicatorToChart={handleAddIndicator}
onRemoveIndicatorFromChart={handleRemoveByType} onRemoveIndicatorFromChart={handleRemoveByType}
onIndicatorListOrderChange={handleIndicatorListOrderChange} onIndicatorListOrderChange={handleIndicatorListOrderChange}
chartCandleAreaPriceLabels={chartCandleAreaPriceLabels}
onChartCandleAreaPriceLabels={v => {
saveAppDef({ chartCandleAreaPriceLabels: v });
managerRef.current?.setCandleAreaPriceLabelsEnabled(v);
slotRefs.current.forEach(slot => slot?.setCandleAreaPriceLabelsEnabled(v));
}}
chartSeriesPriceLabels={chartSeriesPriceLabels} chartSeriesPriceLabels={chartSeriesPriceLabels}
onChartSeriesPriceLabels={v => { onChartSeriesPriceLabels={v => {
saveAppDef({ chartSeriesPriceLabels: v }); saveAppDef({ chartSeriesPriceLabels: v });
managerRef.current?.setSeriesPriceLabelsEnabled(v); managerRef.current?.setIndicatorAreaPriceLabelsEnabled(v);
slotRefs.current.forEach(slot => slot?.setSeriesPriceLabelsEnabled(v)); slotRefs.current.forEach(slot => slot?.setIndicatorAreaPriceLabelsEnabled(v));
}} }}
chartVolumeVisible={chartVolumeVisible} chartVolumeVisible={chartVolumeVisible}
onChartVolumeVisible={v => { onChartVolumeVisible={v => {
@@ -2201,6 +2213,7 @@ function App() {
onSymbolChange={activeSlot === 0 ? handleActiveSlotSymbolChange : undefined} onSymbolChange={activeSlot === 0 ? handleActiveSlotSymbolChange : undefined}
magnetMode={magnetMode} magnetMode={magnetMode}
onTradeOrderRequest={(req, idx) => applyTradeFill(req, idx)} onTradeOrderRequest={(req, idx) => applyTradeFill(req, idx)}
chartCandleAreaPriceLabels={chartCandleAreaPriceLabels}
chartSeriesPriceLabels={chartSeriesPriceLabels} chartSeriesPriceLabels={chartSeriesPriceLabels}
chartVolumeVisible={chartVolumeVisible} chartVolumeVisible={chartVolumeVisible}
chartPaneSeparator={chartPaneSeparator} chartPaneSeparator={chartPaneSeparator}
@@ -2242,6 +2255,7 @@ function App() {
onSymbolChange={activeSlot === i + 1 ? handleActiveSlotSymbolChange : undefined} onSymbolChange={activeSlot === i + 1 ? handleActiveSlotSymbolChange : undefined}
magnetMode={magnetMode} magnetMode={magnetMode}
onTradeOrderRequest={(req, idx) => applyTradeFill(req, idx)} onTradeOrderRequest={(req, idx) => applyTradeFill(req, idx)}
chartCandleAreaPriceLabels={chartCandleAreaPriceLabels}
chartSeriesPriceLabels={chartSeriesPriceLabels} chartSeriesPriceLabels={chartSeriesPriceLabels}
chartVolumeVisible={chartVolumeVisible} chartVolumeVisible={chartVolumeVisible}
chartPaneSeparator={chartPaneSeparator} chartPaneSeparator={chartPaneSeparator}
@@ -2301,7 +2315,8 @@ function App() {
} else { } else {
pendingRealtimeBarRef.current = null; pendingRealtimeBarRef.current = null;
} }
mgr.setSeriesPriceLabelsEnabled(chartSeriesPriceLabels); mgr.setCandleAreaPriceLabelsEnabled(chartCandleAreaPriceLabels);
mgr.setIndicatorAreaPriceLabelsEnabled(chartSeriesPriceLabels);
const wrapper = document.querySelector('.chart-wrapper') as HTMLElement | null; const wrapper = document.querySelector('.chart-wrapper') as HTMLElement | null;
mgr.setVolumeVisible(chartVolumeVisible, wrapper?.clientHeight); mgr.setVolumeVisible(chartVolumeVisible, wrapper?.clientHeight);
mgr.setPaneSeparatorOptions(chartPaneSeparator); mgr.setPaneSeparatorOptions(chartPaneSeparator);
@@ -2347,6 +2362,8 @@ function App() {
onToggleIndicatorHidden={handleToggleIndicatorHidden} onToggleIndicatorHidden={handleToggleIndicatorHidden}
onOpenIndicatorSettings={id => setSettingsModalId(id)} onOpenIndicatorSettings={id => setSettingsModalId(id)}
volumeVisible={chartVolumeVisible} volumeVisible={chartVolumeVisible}
candleAreaPriceLabelsEnabled={chartCandleAreaPriceLabels}
indicatorAreaPriceLabelsEnabled={chartSeriesPriceLabels}
paneSeparatorOptions={chartPaneSeparator} paneSeparatorOptions={chartPaneSeparator}
/> />
{isSingleLoadingMore && ( {isSingleLoadingMore && (
+22 -4
View File
@@ -117,8 +117,10 @@ export interface ChartSlotHandle {
setIndicators: (inds: IndicatorConfig[]) => void; setIndicators: (inds: IndicatorConfig[]) => void;
/** 크로스헤어 자석모드 설정 */ /** 크로스헤어 자석모드 설정 */
setCrosshairMagnet: (enabled: boolean) => void; setCrosshairMagnet: (enabled: boolean) => void;
/** 가격축 라벨·설명 on/off */ /** 가격축 라벨·설명 on/off (캔들·보조지표 동일 값) */
setSeriesPriceLabelsEnabled: (enabled: boolean) => void; setSeriesPriceLabelsEnabled: (enabled: boolean) => void;
setCandleAreaPriceLabelsEnabled: (enabled: boolean) => void;
setIndicatorAreaPriceLabelsEnabled: (enabled: boolean) => void;
/** 거래량 pane on/off */ /** 거래량 pane on/off */
setVolumeVisible: (visible: boolean) => void; setVolumeVisible: (visible: boolean) => void;
setPaneSeparatorOptions: (opts: ChartPaneSeparatorOptions) => void; setPaneSeparatorOptions: (opts: ChartPaneSeparatorOptions) => void;
@@ -172,7 +174,9 @@ export interface ChartSlotProps {
magnetMode?: 'off' | 'weak' | 'strong'; magnetMode?: 'off' | 'weak' | 'strong';
/** 차트 우클릭 → 매수·매도 패널 자동 입력 */ /** 차트 우클릭 → 매수·매도 패널 자동 입력 */
onTradeOrderRequest?: (req: { market: string; price: number; side: 'buy' | 'sell' }, slotIndex: number) => void; onTradeOrderRequest?: (req: { market: string; price: number; side: 'buy' | 'sell' }, slotIndex: number) => void;
/** 보조지표 우측 가격축 라벨·설명 표시 */ /** 캔들 영역 오버레이 지표 가격축 라벨·설명 */
chartCandleAreaPriceLabels?: boolean;
/** 하단 보조지표 pane 가격축 라벨·설명 */
chartSeriesPriceLabels?: boolean; chartSeriesPriceLabels?: boolean;
/** 차트 하단 거래량 바 표시 */ /** 차트 하단 거래량 바 표시 */
chartVolumeVisible?: boolean; chartVolumeVisible?: boolean;
@@ -206,6 +210,7 @@ const ChartSlot = forwardRef<ChartSlotHandle, ChartSlotProps>(function ChartSlot
onSymbolChange, onSymbolChange,
magnetMode = 'off', magnetMode = 'off',
onTradeOrderRequest, onTradeOrderRequest,
chartCandleAreaPriceLabels = true,
chartSeriesPriceLabels = true, chartSeriesPriceLabels = true,
chartVolumeVisible = true, chartVolumeVisible = true,
chartRealtimeSource = 'BACKEND_STOMP', chartRealtimeSource = 'BACKEND_STOMP',
@@ -331,6 +336,12 @@ const ChartSlot = forwardRef<ChartSlotHandle, ChartSlotProps>(function ChartSlot
setSeriesPriceLabelsEnabled: (enabled: boolean) => { setSeriesPriceLabelsEnabled: (enabled: boolean) => {
managerRef.current?.setSeriesPriceLabelsEnabled(enabled); managerRef.current?.setSeriesPriceLabelsEnabled(enabled);
}, },
setCandleAreaPriceLabelsEnabled: (enabled: boolean) => {
managerRef.current?.setCandleAreaPriceLabelsEnabled(enabled);
},
setIndicatorAreaPriceLabelsEnabled: (enabled: boolean) => {
managerRef.current?.setIndicatorAreaPriceLabelsEnabled(enabled);
},
setVolumeVisible: (visible: boolean) => { setVolumeVisible: (visible: boolean) => {
managerRef.current?.setVolumeVisible(visible); managerRef.current?.setVolumeVisible(visible);
}, },
@@ -349,7 +360,11 @@ const ChartSlot = forwardRef<ChartSlotHandle, ChartSlotProps>(function ChartSlot
}, [magnetMode]); }, [magnetMode]);
useEffect(() => { useEffect(() => {
managerRef.current?.setSeriesPriceLabelsEnabled(chartSeriesPriceLabels); managerRef.current?.setCandleAreaPriceLabelsEnabled(chartCandleAreaPriceLabels);
}, [chartCandleAreaPriceLabels]);
useEffect(() => {
managerRef.current?.setIndicatorAreaPriceLabelsEnabled(chartSeriesPriceLabels);
}, [chartSeriesPriceLabels]); }, [chartSeriesPriceLabels]);
useEffect(() => { useEffect(() => {
@@ -855,6 +870,8 @@ const ChartSlot = forwardRef<ChartSlotHandle, ChartSlotProps>(function ChartSlot
drawingsVisible={!compactMode} drawingsVisible={!compactMode}
showHoverToolbar={!compactMode} showHoverToolbar={!compactMode}
volumeVisible={chartVolumeVisible} volumeVisible={chartVolumeVisible}
candleAreaPriceLabelsEnabled={chartCandleAreaPriceLabels}
indicatorAreaPriceLabelsEnabled={chartSeriesPriceLabels}
paneSeparatorOptions={chartPaneSeparator} paneSeparatorOptions={chartPaneSeparator}
crosshairInfoVisible={chartCrosshairInfoVisible} crosshairInfoVisible={chartCrosshairInfoVisible}
onCrosshair={data => { onCrosshair={data => {
@@ -873,7 +890,8 @@ const ChartSlot = forwardRef<ChartSlotHandle, ChartSlotProps>(function ChartSlot
} else { } else {
pendingRealtimeBarRef.current = null; pendingRealtimeBarRef.current = null;
} }
mgr.setSeriesPriceLabelsEnabled(chartSeriesPriceLabels); mgr.setCandleAreaPriceLabelsEnabled(chartCandleAreaPriceLabels);
mgr.setIndicatorAreaPriceLabelsEnabled(chartSeriesPriceLabels);
mgr.setVolumeVisible(chartVolumeVisible); mgr.setVolumeVisible(chartVolumeVisible);
if (chartPaneSeparator) mgr.setPaneSeparatorOptions(chartPaneSeparator); if (chartPaneSeparator) mgr.setPaneSeparatorOptions(chartPaneSeparator);
// Time sync: 가시 시간 범위 변화 구독 // Time sync: 가시 시간 범위 변화 구독
@@ -331,7 +331,7 @@ export const SettingsOutputFooter: React.FC<{
<div className="ism-row ism-ichimoku-extra-row"> <div className="ism-row ism-ichimoku-extra-row">
<span className="ism-label"> ·</span> <span className="ism-label"> ·</span>
<div className="ism-control"> <div className="ism-control">
<label className="ism-toggle" title="이 지표만 우측 가격축 금액·설명 표시 (차트 설정 전역 스위치가 켜져 있을 때 적용)"> <label className="ism-toggle" title="이 지표만 우측 가격축 금액·설명 표시 (해당 영역·차트 설정 전역 스위치가 켜져 있을 때 적용)">
<input <input
type="checkbox" type="checkbox"
checked={lastValueVisible} checked={lastValueVisible}
+36 -2
View File
@@ -110,6 +110,8 @@ interface SettingsPageProps {
onRemoveIndicatorFromChart?: (type: string) => void; onRemoveIndicatorFromChart?: (type: string) => void;
/** 보조지표 설정 목록 순서 변경 → 차트 pane 순서 */ /** 보조지표 설정 목록 순서 변경 → 차트 pane 순서 */
onIndicatorListOrderChange?: (orderedTypes: string[]) => void; onIndicatorListOrderChange?: (orderedTypes: string[]) => void;
chartCandleAreaPriceLabels?: boolean;
onChartCandleAreaPriceLabels?: (v: boolean) => void;
chartSeriesPriceLabels?: boolean; chartSeriesPriceLabels?: boolean;
onChartSeriesPriceLabels?: (v: boolean) => void; onChartSeriesPriceLabels?: (v: boolean) => void;
chartVolumeVisible?: boolean; chartVolumeVisible?: boolean;
@@ -732,6 +734,8 @@ interface ChartPanelProps {
onChartRealtimeSource?: (v: string) => void; onChartRealtimeSource?: (v: string) => void;
magnetMode?: 'off' | 'weak' | 'strong'; magnetMode?: 'off' | 'weak' | 'strong';
onMagnetMode?: (m: 'off' | 'weak' | 'strong') => void; onMagnetMode?: (m: 'off' | 'weak' | 'strong') => void;
chartCandleAreaPriceLabels?: boolean;
onChartCandleAreaPriceLabels?: (v: boolean) => void;
chartSeriesPriceLabels?: boolean; chartSeriesPriceLabels?: boolean;
onChartSeriesPriceLabels?: (v: boolean) => void; onChartSeriesPriceLabels?: (v: boolean) => void;
chartVolumeVisible?: boolean; chartVolumeVisible?: boolean;
@@ -752,6 +756,8 @@ const ChartPanel: React.FC<ChartPanelProps> = ({
chartRealtimeSource = 'BACKEND_STOMP', chartRealtimeSource = 'BACKEND_STOMP',
onChartRealtimeSource, onChartRealtimeSource,
magnetMode = 'off', onMagnetMode, magnetMode = 'off', onMagnetMode,
chartCandleAreaPriceLabels = true,
onChartCandleAreaPriceLabels,
chartSeriesPriceLabels = true, chartSeriesPriceLabels = true,
onChartSeriesPriceLabels, onChartSeriesPriceLabels,
chartVolumeVisible = true, chartVolumeVisible = true,
@@ -869,9 +875,33 @@ const ChartPanel: React.FC<ChartPanelProps> = ({
<span className="stg-toggle-slider" /> <span className="stg-toggle-slider" />
</label> </label>
</SettingRow> </SettingRow>
</SettingSection>
<SettingSection title="지표 가격축 라벨·설명">
<div className="stg-subsection-label">
<span className="stg-subsection-desc">· pane (· )</span>
</div>
<SettingRow <SettingRow
label="지표 가격축 라벨·설명" label="가격축 라벨·설명"
desc="켜면 보조지표 선마다 우측 가격축에 금액 하이라이트와 설명(전환선, 기준선 등)이 표시됩니다. 끄면 선만 표시됩니다." desc="켜면 오버레이 지표 선마다 우측 가격축에 금액 설명(전환선, 기준선 등)이 표시됩니다."
>
<label className="stg-toggle">
<input
type="checkbox"
checked={chartCandleAreaPriceLabels}
onChange={e => onChartCandleAreaPriceLabels?.(e.target.checked)}
/>
<span className="stg-toggle-slider" />
</label>
</SettingRow>
<div className="stg-subsection-label stg-subsection-label--spaced">
<span className="stg-subsection-desc"> pane에 RSI·MACD </span>
</div>
<SettingRow
label="가격축 라벨·설명"
desc="켜면 하단 보조지표 pane의 선마다 우측 가격축에 금액과 설명이 표시됩니다. 끄면 선만 표시됩니다."
> >
<label className="stg-toggle"> <label className="stg-toggle">
<input <input
@@ -1611,6 +1641,8 @@ const SettingsPage: React.FC<SettingsPageProps> = ({
onAddIndicatorToChart, onAddIndicatorToChart,
onRemoveIndicatorFromChart, onRemoveIndicatorFromChart,
onIndicatorListOrderChange, onIndicatorListOrderChange,
chartCandleAreaPriceLabels = true,
onChartCandleAreaPriceLabels,
chartSeriesPriceLabels = true, chartSeriesPriceLabels = true,
onChartSeriesPriceLabels, onChartSeriesPriceLabels,
chartVolumeVisible = true, chartVolumeVisible = true,
@@ -1709,6 +1741,8 @@ const SettingsPage: React.FC<SettingsPageProps> = ({
onChartRealtimeSource={onChartRealtimeSource} onChartRealtimeSource={onChartRealtimeSource}
magnetMode={magnetMode} magnetMode={magnetMode}
onMagnetMode={onMagnetMode} onMagnetMode={onMagnetMode}
chartCandleAreaPriceLabels={chartCandleAreaPriceLabels}
onChartCandleAreaPriceLabels={onChartCandleAreaPriceLabels}
chartSeriesPriceLabels={chartSeriesPriceLabels} chartSeriesPriceLabels={chartSeriesPriceLabels}
onChartSeriesPriceLabels={onChartSeriesPriceLabels} onChartSeriesPriceLabels={onChartSeriesPriceLabels}
chartVolumeVisible={chartVolumeVisible} chartVolumeVisible={chartVolumeVisible}
+19 -5
View File
@@ -137,7 +137,11 @@ interface TradingChartProps {
volumeVisible?: boolean; volumeVisible?: boolean;
/** 하단 호버 줌·스크롤 툴바 (기본 true) */ /** 하단 호버 줌·스크롤 툴바 (기본 true) */
showHoverToolbar?: boolean; showHoverToolbar?: boolean;
/** 보조지표 우측 가격축 라벨·설명 (차트 설정, 기본 true) */ /** 캔들 영역 오버레이 지표 가격축 라벨·설명 (기본 true) */
candleAreaPriceLabelsEnabled?: boolean;
/** 하단 보조지표 pane 가격축 라벨·설명 (기본 true) */
indicatorAreaPriceLabelsEnabled?: boolean;
/** @deprecated candleArea + indicatorArea 동일 값 */
seriesPriceLabelsEnabled?: boolean; seriesPriceLabelsEnabled?: boolean;
/** 실시간 차트 화면 표시 여부 — false 이면 숨김 중 무거운 재로드 지연 */ /** 실시간 차트 화면 표시 여부 — false 이면 숨김 중 무거운 재로드 지연 */
chartVisible?: boolean; chartVisible?: boolean;
@@ -185,7 +189,9 @@ const TradingChart: React.FC<TradingChartProps> = ({
displayTimezone = DEFAULT_DISPLAY_TIMEZONE, displayTimezone = DEFAULT_DISPLAY_TIMEZONE,
volumeVisible = true, volumeVisible = true,
showHoverToolbar = true, showHoverToolbar = true,
seriesPriceLabelsEnabled = true, candleAreaPriceLabelsEnabled,
indicatorAreaPriceLabelsEnabled,
seriesPriceLabelsEnabled,
chartVisible = true, chartVisible = true,
paneSeparatorOptions, paneSeparatorOptions,
paneLayoutClamp = false, paneLayoutClamp = false,
@@ -335,9 +341,16 @@ const TradingChart: React.FC<TradingChartProps> = ({
mgr.setVolumeVisible(volumeVisible, wrapperH > 0 ? wrapperH : undefined); mgr.setVolumeVisible(volumeVisible, wrapperH > 0 ? wrapperH : undefined);
}, [volumeVisible]); }, [volumeVisible]);
const resolvedCandlePriceLabels = candleAreaPriceLabelsEnabled ?? seriesPriceLabelsEnabled ?? true;
const resolvedIndicatorPriceLabels = indicatorAreaPriceLabelsEnabled ?? seriesPriceLabelsEnabled ?? true;
useEffect(() => { useEffect(() => {
managerRef.current?.setSeriesPriceLabelsEnabled(seriesPriceLabelsEnabled); managerRef.current?.setCandleAreaPriceLabelsEnabled(resolvedCandlePriceLabels);
}, [seriesPriceLabelsEnabled]); }, [resolvedCandlePriceLabels]);
useEffect(() => {
managerRef.current?.setIndicatorAreaPriceLabelsEnabled(resolvedIndicatorPriceLabels);
}, [resolvedIndicatorPriceLabels]);
useEffect(() => { useEffect(() => {
if (!paneSeparatorOptions) return; if (!paneSeparatorOptions) return;
@@ -773,7 +786,8 @@ const TradingChart: React.FC<TradingChartProps> = ({
const wrapperH = wrapperRef.current?.clientHeight ?? 0; const wrapperH = wrapperRef.current?.clientHeight ?? 0;
mgr.setVolumeVisible(false, wrapperH > 0 ? wrapperH : undefined); mgr.setVolumeVisible(false, wrapperH > 0 ? wrapperH : undefined);
} }
mgr.setSeriesPriceLabelsEnabled(seriesPriceLabelsEnabled); mgr.setCandleAreaPriceLabelsEnabled(resolvedCandlePriceLabels);
mgr.setIndicatorAreaPriceLabelsEnabled(resolvedIndicatorPriceLabels);
if (paneSeparatorOptions) mgr.setPaneSeparatorOptions(paneSeparatorOptions); if (paneSeparatorOptions) mgr.setPaneSeparatorOptions(paneSeparatorOptions);
onManagerReady(mgr); onManagerReady(mgr);
@@ -36,6 +36,7 @@ import '../styles/virtualTradingDashboard.css';
interface Props { interface Props {
theme?: Theme; theme?: Theme;
chartRealtimeSource?: ChartRealtimeSource; chartRealtimeSource?: ChartRealtimeSource;
chartCandleAreaPriceLabels?: boolean;
chartSeriesPriceLabels?: boolean; chartSeriesPriceLabels?: boolean;
tickers?: Map<string, TickerData>; tickers?: Map<string, TickerData>;
} }
@@ -43,6 +44,7 @@ interface Props {
const TrendSearchPage: React.FC<Props> = ({ const TrendSearchPage: React.FC<Props> = ({
theme = 'dark', theme = 'dark',
chartRealtimeSource = 'BACKEND_STOMP', chartRealtimeSource = 'BACKEND_STOMP',
chartCandleAreaPriceLabels = true,
chartSeriesPriceLabels = true, chartSeriesPriceLabels = true,
tickers, tickers,
}) => { }) => {
@@ -256,6 +258,7 @@ const TrendSearchPage: React.FC<Props> = ({
flashMarkets={flashMarkets} flashMarkets={flashMarkets}
theme={theme} theme={theme}
chartRealtimeSource={chartRealtimeSource} chartRealtimeSource={chartRealtimeSource}
chartCandleAreaPriceLabels={chartCandleAreaPriceLabels}
chartSeriesPriceLabels={chartSeriesPriceLabels} chartSeriesPriceLabels={chartSeriesPriceLabels}
chartPaneSeparator={defaults.chartPaneSeparator} chartPaneSeparator={defaults.chartPaneSeparator}
tickers={tickers} tickers={tickers}
@@ -108,6 +108,7 @@ const VirtualTradingPage: React.FC<Props> = ({
const appChartDefaults = resolveAppDefaults(appSettings); const appChartDefaults = resolveAppDefaults(appSettings);
const chartRealtimeSource = (appChartDefaults.chartRealtimeSource const chartRealtimeSource = (appChartDefaults.chartRealtimeSource
?? 'BACKEND_STOMP') as ChartRealtimeSource; ?? 'BACKEND_STOMP') as ChartRealtimeSource;
const chartCandleAreaPriceLabels = appChartDefaults.chartCandleAreaPriceLabels;
const chartSeriesPriceLabels = appChartDefaults.chartSeriesPriceLabels; const chartSeriesPriceLabels = appChartDefaults.chartSeriesPriceLabels;
const chartPaneSeparator = appChartDefaults.chartPaneSeparator; const chartPaneSeparator = appChartDefaults.chartPaneSeparator;
const chartLiveReceiveHighlight = appChartDefaults.chartLiveReceiveHighlight; const chartLiveReceiveHighlight = appChartDefaults.chartLiveReceiveHighlight;
@@ -453,6 +454,7 @@ const VirtualTradingPage: React.FC<Props> = ({
onSelectMarket={handleSelectMarket} onSelectMarket={handleSelectMarket}
theme={theme} theme={theme}
chartRealtimeSource={chartRealtimeSource} chartRealtimeSource={chartRealtimeSource}
chartCandleAreaPriceLabels={chartCandleAreaPriceLabels}
chartSeriesPriceLabels={chartSeriesPriceLabels} chartSeriesPriceLabels={chartSeriesPriceLabels}
chartPaneSeparator={chartPaneSeparator} chartPaneSeparator={chartPaneSeparator}
chartLiveReceiveHighlight={chartLiveReceiveHighlight} chartLiveReceiveHighlight={chartLiveReceiveHighlight}
@@ -23,6 +23,7 @@ interface Props {
timeframe: string; timeframe: string;
theme?: Theme; theme?: Theme;
chartRealtimeSource?: ChartRealtimeSource; chartRealtimeSource?: ChartRealtimeSource;
chartCandleAreaPriceLabels?: boolean;
chartSeriesPriceLabels?: boolean; chartSeriesPriceLabels?: boolean;
chartPaneSeparator?: import('../../types/chartPaneSeparator').ChartPaneSeparatorOptions; chartPaneSeparator?: import('../../types/chartPaneSeparator').ChartPaneSeparatorOptions;
} }
@@ -42,6 +43,7 @@ const TrendSearchCardChart: React.FC<Props> = ({
timeframe, timeframe,
theme = 'dark', theme = 'dark',
chartRealtimeSource = 'BACKEND_STOMP', chartRealtimeSource = 'BACKEND_STOMP',
chartCandleAreaPriceLabels = true,
chartSeriesPriceLabels = true, chartSeriesPriceLabels = true,
chartPaneSeparator, chartPaneSeparator,
}) => { }) => {
@@ -197,7 +199,8 @@ const TrendSearchCardChart: React.FC<Props> = ({
magnifierEnabled={false} magnifierEnabled={false}
volumeVisible volumeVisible
showHoverToolbar={false} showHoverToolbar={false}
seriesPriceLabelsEnabled={chartSeriesPriceLabels} candleAreaPriceLabelsEnabled={chartCandleAreaPriceLabels}
indicatorAreaPriceLabelsEnabled={chartSeriesPriceLabels}
paneSeparatorOptions={chartPaneSeparator} paneSeparatorOptions={chartPaneSeparator}
/> />
</div> </div>
@@ -19,6 +19,7 @@ interface Props {
timeframe: string; timeframe: string;
theme?: Theme; theme?: Theme;
chartRealtimeSource?: ChartRealtimeSource; chartRealtimeSource?: ChartRealtimeSource;
chartCandleAreaPriceLabels?: boolean;
chartSeriesPriceLabels?: boolean; chartSeriesPriceLabels?: boolean;
chartPaneSeparator?: import('../../types/chartPaneSeparator').ChartPaneSeparatorOptions; chartPaneSeparator?: import('../../types/chartPaneSeparator').ChartPaneSeparatorOptions;
} }
@@ -39,6 +40,7 @@ const TrendSearchChartPanel: React.FC<Props> = ({
timeframe, timeframe,
theme = 'dark', theme = 'dark',
chartRealtimeSource = 'BACKEND_STOMP', chartRealtimeSource = 'BACKEND_STOMP',
chartCandleAreaPriceLabels = true,
chartSeriesPriceLabels = true, chartSeriesPriceLabels = true,
chartPaneSeparator, chartPaneSeparator,
}) => { }) => {
@@ -184,7 +186,8 @@ const TrendSearchChartPanel: React.FC<Props> = ({
magnifierEnabled={false} magnifierEnabled={false}
volumeVisible volumeVisible
showHoverToolbar={false} showHoverToolbar={false}
seriesPriceLabelsEnabled={chartSeriesPriceLabels} candleAreaPriceLabelsEnabled={chartCandleAreaPriceLabels}
indicatorAreaPriceLabelsEnabled={chartSeriesPriceLabels}
paneSeparatorOptions={chartPaneSeparator} paneSeparatorOptions={chartPaneSeparator}
/> />
</div> </div>
@@ -21,6 +21,7 @@ interface Props {
onSelect?: () => void; onSelect?: () => void;
theme?: Theme; theme?: Theme;
chartRealtimeSource?: ChartRealtimeSource; chartRealtimeSource?: ChartRealtimeSource;
chartCandleAreaPriceLabels?: boolean;
chartSeriesPriceLabels?: boolean; chartSeriesPriceLabels?: boolean;
chartPaneSeparator?: import('../../types/chartPaneSeparator').ChartPaneSeparatorOptions; chartPaneSeparator?: import('../../types/chartPaneSeparator').ChartPaneSeparatorOptions;
ticker?: TickerData; ticker?: TickerData;
@@ -58,6 +59,7 @@ const TrendSearchResultCard: React.FC<Props> = ({
onSelect, onSelect,
theme = 'dark', theme = 'dark',
chartRealtimeSource = 'BACKEND_STOMP', chartRealtimeSource = 'BACKEND_STOMP',
chartCandleAreaPriceLabels = true,
chartSeriesPriceLabels = true, chartSeriesPriceLabels = true,
chartPaneSeparator, chartPaneSeparator,
ticker, ticker,
@@ -140,6 +142,7 @@ const TrendSearchResultCard: React.FC<Props> = ({
timeframe={timeframe} timeframe={timeframe}
theme={theme} theme={theme}
chartRealtimeSource={chartRealtimeSource} chartRealtimeSource={chartRealtimeSource}
chartCandleAreaPriceLabels={chartCandleAreaPriceLabels}
chartSeriesPriceLabels={chartSeriesPriceLabels} chartSeriesPriceLabels={chartSeriesPriceLabels}
chartPaneSeparator={chartPaneSeparator} chartPaneSeparator={chartPaneSeparator}
/> />
@@ -16,6 +16,7 @@ interface Props {
flashMarkets?: Set<string>; flashMarkets?: Set<string>;
theme?: Theme; theme?: Theme;
chartRealtimeSource?: ChartRealtimeSource; chartRealtimeSource?: ChartRealtimeSource;
chartCandleAreaPriceLabels?: boolean;
chartSeriesPriceLabels?: boolean; chartSeriesPriceLabels?: boolean;
chartPaneSeparator?: import('../../types/chartPaneSeparator').ChartPaneSeparatorOptions; chartPaneSeparator?: import('../../types/chartPaneSeparator').ChartPaneSeparatorOptions;
tickers?: Map<string, TickerData>; tickers?: Map<string, TickerData>;
@@ -37,6 +38,7 @@ const TrendSearchResultsCardGrid: React.FC<Props> = ({
flashMarkets, flashMarkets,
theme = 'dark', theme = 'dark',
chartRealtimeSource = 'BACKEND_STOMP', chartRealtimeSource = 'BACKEND_STOMP',
chartCandleAreaPriceLabels = true,
chartSeriesPriceLabels = true, chartSeriesPriceLabels = true,
chartPaneSeparator, chartPaneSeparator,
tickers, tickers,
@@ -85,6 +87,7 @@ const TrendSearchResultsCardGrid: React.FC<Props> = ({
onSelect={onSelect ? () => onSelect(row) : undefined} onSelect={onSelect ? () => onSelect(row) : undefined}
theme={theme} theme={theme}
chartRealtimeSource={chartRealtimeSource} chartRealtimeSource={chartRealtimeSource}
chartCandleAreaPriceLabels={chartCandleAreaPriceLabels}
chartSeriesPriceLabels={chartSeriesPriceLabels} chartSeriesPriceLabels={chartSeriesPriceLabels}
chartPaneSeparator={chartPaneSeparator} chartPaneSeparator={chartPaneSeparator}
ticker={tickers?.get(row.market)} ticker={tickers?.get(row.market)}
@@ -28,7 +28,9 @@ const VirtualStrategyChartPopup: React.FC<Props> = ({
onClose, onClose,
}) => { }) => {
const { settings: appSettings } = useAppSettings(); const { settings: appSettings } = useAppSettings();
const chartSeriesPriceLabels = resolveAppDefaults(appSettings).chartSeriesPriceLabels; const chartDefaults = resolveAppDefaults(appSettings);
const chartCandleAreaPriceLabels = chartDefaults.chartCandleAreaPriceLabels;
const chartSeriesPriceLabels = chartDefaults.chartSeriesPriceLabels;
const ko = getKoreanName(market); const ko = getKoreanName(market);
const sym = market.replace(/^KRW-/, ''); const sym = market.replace(/^KRW-/, '');
@@ -56,6 +58,7 @@ const VirtualStrategyChartPopup: React.FC<Props> = ({
theme={theme} theme={theme}
running={running} running={running}
chartRealtimeSource={chartRealtimeSource} chartRealtimeSource={chartRealtimeSource}
chartCandleAreaPriceLabels={chartCandleAreaPriceLabels}
chartSeriesPriceLabels={chartSeriesPriceLabels} chartSeriesPriceLabels={chartSeriesPriceLabels}
/> />
</AppPopup> </AppPopup>
@@ -42,6 +42,7 @@ interface Props {
onSelect?: () => void; onSelect?: () => void;
theme?: Theme; theme?: Theme;
chartRealtimeSource?: ChartRealtimeSource; chartRealtimeSource?: ChartRealtimeSource;
chartCandleAreaPriceLabels?: boolean;
chartSeriesPriceLabels?: boolean; chartSeriesPriceLabels?: boolean;
chartPaneSeparator?: import('../../types/chartPaneSeparator').ChartPaneSeparatorOptions; chartPaneSeparator?: import('../../types/chartPaneSeparator').ChartPaneSeparatorOptions;
chartLiveReceiveHighlight?: boolean; chartLiveReceiveHighlight?: boolean;
@@ -72,6 +73,7 @@ const VirtualTargetCard: React.FC<Props> = ({
onSelect, onSelect,
theme = 'dark', theme = 'dark',
chartRealtimeSource = 'BACKEND_STOMP', chartRealtimeSource = 'BACKEND_STOMP',
chartCandleAreaPriceLabels = true,
chartSeriesPriceLabels = true, chartSeriesPriceLabels = true,
chartPaneSeparator, chartPaneSeparator,
chartLiveReceiveHighlight = true, chartLiveReceiveHighlight = true,
@@ -194,6 +196,7 @@ const VirtualTargetCard: React.FC<Props> = ({
theme={theme} theme={theme}
running={running} running={running}
chartRealtimeSource={chartRealtimeSource} chartRealtimeSource={chartRealtimeSource}
chartCandleAreaPriceLabels={chartCandleAreaPriceLabels}
chartSeriesPriceLabels={chartSeriesPriceLabels} chartSeriesPriceLabels={chartSeriesPriceLabels}
chartPaneSeparator={chartPaneSeparator} chartPaneSeparator={chartPaneSeparator}
chartTimeframe={chartTimeframe} chartTimeframe={chartTimeframe}
@@ -30,6 +30,7 @@ interface Props {
theme?: Theme; theme?: Theme;
running?: boolean; running?: boolean;
chartRealtimeSource?: ChartRealtimeSource; chartRealtimeSource?: ChartRealtimeSource;
chartCandleAreaPriceLabels?: boolean;
chartSeriesPriceLabels?: boolean; chartSeriesPriceLabels?: boolean;
chartPaneSeparator?: import('../../types/chartPaneSeparator').ChartPaneSeparatorOptions; chartPaneSeparator?: import('../../types/chartPaneSeparator').ChartPaneSeparatorOptions;
/** 전체보기 분할 pane — 캔버스 높이 100% */ /** 전체보기 분할 pane — 캔버스 높이 100% */
@@ -46,6 +47,7 @@ const VirtualTargetCardChart: React.FC<Props> = ({
theme = 'dark', theme = 'dark',
running = false, running = false,
chartRealtimeSource = 'BACKEND_STOMP', chartRealtimeSource = 'BACKEND_STOMP',
chartCandleAreaPriceLabels = true,
chartSeriesPriceLabels = true, chartSeriesPriceLabels = true,
chartPaneSeparator, chartPaneSeparator,
fillHeight = false, fillHeight = false,
@@ -261,7 +263,8 @@ const VirtualTargetCardChart: React.FC<Props> = ({
showPaneLegend={false} showPaneLegend={false}
showCandlePaneControls={false} showCandlePaneControls={false}
defaultCandleOnly defaultCandleOnly
seriesPriceLabelsEnabled={chartSeriesPriceLabels} candleAreaPriceLabelsEnabled={chartCandleAreaPriceLabels}
indicatorAreaPriceLabelsEnabled={chartSeriesPriceLabels}
paneSeparatorOptions={chartPaneSeparator} paneSeparatorOptions={chartPaneSeparator}
/> />
</div> </div>
@@ -32,6 +32,7 @@ interface Props {
onExit: () => void; onExit: () => void;
theme?: Theme; theme?: Theme;
chartRealtimeSource?: ChartRealtimeSource; chartRealtimeSource?: ChartRealtimeSource;
chartCandleAreaPriceLabels?: boolean;
chartSeriesPriceLabels?: boolean; chartSeriesPriceLabels?: boolean;
chartPaneSeparator?: import('../../types/chartPaneSeparator').ChartPaneSeparatorOptions; chartPaneSeparator?: import('../../types/chartPaneSeparator').ChartPaneSeparatorOptions;
chartLiveReceiveHighlight?: boolean; chartLiveReceiveHighlight?: boolean;
@@ -55,6 +56,7 @@ const VirtualTargetFocusView: React.FC<Props> = ({
onExit, onExit,
theme = 'dark', theme = 'dark',
chartRealtimeSource = 'BACKEND_STOMP', chartRealtimeSource = 'BACKEND_STOMP',
chartCandleAreaPriceLabels = true,
chartSeriesPriceLabels = true, chartSeriesPriceLabels = true,
chartPaneSeparator, chartPaneSeparator,
chartLiveReceiveHighlight = true, chartLiveReceiveHighlight = true,
@@ -114,6 +116,7 @@ const VirtualTargetFocusView: React.FC<Props> = ({
theme={theme} theme={theme}
running={running} running={running}
chartRealtimeSource={chartRealtimeSource} chartRealtimeSource={chartRealtimeSource}
chartCandleAreaPriceLabels={chartCandleAreaPriceLabels}
chartSeriesPriceLabels={chartSeriesPriceLabels} chartSeriesPriceLabels={chartSeriesPriceLabels}
chartPaneSeparator={chartPaneSeparator} chartPaneSeparator={chartPaneSeparator}
fillHeight fillHeight
@@ -31,6 +31,7 @@ interface Props {
onSelectMarket?: (market: string) => void; onSelectMarket?: (market: string) => void;
theme?: Theme; theme?: Theme;
chartRealtimeSource?: ChartRealtimeSource; chartRealtimeSource?: ChartRealtimeSource;
chartCandleAreaPriceLabels?: boolean;
chartSeriesPriceLabels?: boolean; chartSeriesPriceLabels?: boolean;
chartPaneSeparator?: import('../../types/chartPaneSeparator').ChartPaneSeparatorOptions; chartPaneSeparator?: import('../../types/chartPaneSeparator').ChartPaneSeparatorOptions;
chartLiveReceiveHighlight?: boolean; chartLiveReceiveHighlight?: boolean;
@@ -48,6 +49,7 @@ const VirtualTargetGrid: React.FC<Props> = ({
onSelectMarket, onSelectMarket,
theme = 'dark', theme = 'dark',
chartRealtimeSource = 'BACKEND_STOMP', chartRealtimeSource = 'BACKEND_STOMP',
chartCandleAreaPriceLabels = true,
chartSeriesPriceLabels = true, chartSeriesPriceLabels = true,
chartPaneSeparator, chartPaneSeparator,
chartLiveReceiveHighlight = true, chartLiveReceiveHighlight = true,
@@ -133,6 +135,7 @@ const VirtualTargetGrid: React.FC<Props> = ({
onExit={() => setFocusMarket(null)} onExit={() => setFocusMarket(null)}
theme={theme} theme={theme}
chartRealtimeSource={chartRealtimeSource} chartRealtimeSource={chartRealtimeSource}
chartCandleAreaPriceLabels={chartCandleAreaPriceLabels}
chartSeriesPriceLabels={chartSeriesPriceLabels} chartSeriesPriceLabels={chartSeriesPriceLabels}
chartPaneSeparator={chartPaneSeparator} chartPaneSeparator={chartPaneSeparator}
chartLiveReceiveHighlight={chartLiveReceiveHighlight} chartLiveReceiveHighlight={chartLiveReceiveHighlight}
@@ -172,7 +175,8 @@ const VirtualTargetGrid: React.FC<Props> = ({
onSelect={onSelectMarket ? () => onSelectMarket(t.market) : undefined} onSelect={onSelectMarket ? () => onSelectMarket(t.market) : undefined}
theme={theme} theme={theme}
chartRealtimeSource={chartRealtimeSource} chartRealtimeSource={chartRealtimeSource}
chartSeriesPriceLabels={chartSeriesPriceLabels} chartCandleAreaPriceLabels={chartCandleAreaPriceLabels}
chartSeriesPriceLabels={chartSeriesPriceLabels}
chartPaneSeparator={chartPaneSeparator} chartPaneSeparator={chartPaneSeparator}
chartLiveReceiveHighlight={chartLiveReceiveHighlight} chartLiveReceiveHighlight={chartLiveReceiveHighlight}
ticker={tickers?.get(t.market)} ticker={tickers?.get(t.market)}
+1
View File
@@ -142,6 +142,7 @@ export function resolveAppDefaults(s: AppSettingsDto) {
syncOptions: (s.syncOptions as unknown as SyncOptions | null) ?? DEFAULT_SYNC, syncOptions: (s.syncOptions as unknown as SyncOptions | null) ?? DEFAULT_SYNC,
btAutoPopup: s.btAutoPopup ?? true, btAutoPopup: s.btAutoPopup ?? true,
btShowPrice: s.btShowPrice ?? true, btShowPrice: s.btShowPrice ?? true,
chartCandleAreaPriceLabels: s.chartCandleAreaPriceLabels ?? s.chartSeriesPriceLabels ?? true,
chartSeriesPriceLabels: s.chartSeriesPriceLabels ?? true, chartSeriesPriceLabels: s.chartSeriesPriceLabels ?? true,
chartVolumeVisible: s.chartVolumeVisible ?? true, chartVolumeVisible: s.chartVolumeVisible ?? true,
chartLiveReceiveHighlight: s.chartLiveReceiveHighlight ?? true, chartLiveReceiveHighlight: s.chartLiveReceiveHighlight ?? true,
+61 -15
View File
@@ -245,8 +245,10 @@ export class ChartManager {
/** 현재 크로스헤어가 위에 있는 시리즈의 entryId ('__main__' | indicatorId | null) */ /** 현재 크로스헤어가 위에 있는 시리즈의 entryId ('__main__' | indicatorId | null) */
private _crosshairEntryId: string | null = null; private _crosshairEntryId: string | null = null;
/** 우측 가격축 라벨·plot 설명(전환선 등)·금액 색상 하이라이트 */ /** 캔들 pane(0~1) 오버레이 지표 — 우측 가격축 라벨·설명 */
private _seriesPriceLabelsEnabled = true; private _candleAreaPriceLabelsEnabled = true;
/** 하단 보조지표 pane(2+) — 우측 가격축 라벨·설명 */
private _indicatorAreaPriceLabelsEnabled = true;
/** 차트 하단 거래량 pane 표시 */ /** 차트 하단 거래량 pane 표시 */
private _volumeVisible = true; private _volumeVisible = true;
@@ -514,12 +516,31 @@ export class ChartManager {
fmt, fmt,
); );
this.chart.applyOptions({ this.chart.applyOptions({
localization: { timeFormatter, priceFormatter: formatChartAxisPrice }, localization: { timeFormatter },
timeScale: { tickMarkFormatter: this._tickMarkFormatter() }, timeScale: { tickMarkFormatter: this._tickMarkFormatter() },
}); });
this._applyAllSeriesPriceFormats();
this._notifyPaneLayout(); this._notifyPaneLayout();
} }
/** pane별 priceFormat — 전역 priceFormatter 는 보조지표 라벨 포맷을 덮어씀 */
private _applyAllSeriesPriceFormats(): void {
if (this.mainSeries) {
try {
this.mainSeries.applyOptions({ priceFormat: MAIN_PRICE_FORMAT } as Parameters<ISeriesApi<SeriesType>['applyOptions']>[0]);
} catch { /* ok */ }
}
for (const entry of this.indicators.values()) {
const fmt = priceFormatForIndicatorPane(entry.paneIndex ?? 0);
if (!fmt) continue;
for (const series of entry.seriesList) {
try {
series.applyOptions({ priceFormat: fmt } as Parameters<ISeriesApi<SeriesType>['applyOptions']>[0]);
} catch { /* ok */ }
}
}
}
setDisplayTimezone(tz: string, timeframe?: Timeframe, chartTimeFormat?: string): void { setDisplayTimezone(tz: string, timeframe?: Timeframe, chartTimeFormat?: string): void {
this.displayTimezone = tz || DEFAULT_DISPLAY_TIMEZONE; this.displayTimezone = tz || DEFAULT_DISPLAY_TIMEZONE;
if (timeframe) this.displayTimeframe = timeframe; if (timeframe) this.displayTimeframe = timeframe;
@@ -688,7 +709,7 @@ export class ChartManager {
seriesMeta.push({ plotId: plotDef.id, isHistogram: true, color: plotDef.color }); seriesMeta.push({ plotId: plotDef.id, isHistogram: true, color: plotDef.color });
} else { } else {
const isPlotVisible = !indicatorHidden && config.plotVisibility?.[plotDef.id] !== false; const isPlotVisible = !indicatorHidden && config.plotVisibility?.[plotDef.id] !== false;
const showPriceLabel = this.shouldShowSeriesPriceLabel(config); const showPriceLabel = this.shouldShowSeriesPriceLabel(config, true, pane);
const showSeriesTitle = pane < 2 && showPriceLabel; const showSeriesTitle = pane < 2 && showPriceLabel;
series = this.chart.addSeries(LineSeries, { series = this.chart.addSeries(LineSeries, {
color: plotDef.color, color: plotDef.color,
@@ -777,6 +798,7 @@ export class ChartManager {
this._removeOrphanSubPanes(); this._removeOrphanSubPanes();
if (this._activeIndicatorPaneIndices().size > 0) { if (this._activeIndicatorPaneIndices().size > 0) {
this._resyncIndicatorPaneIndices(); this._resyncIndicatorPaneIndices();
this._applyAllSeriesPriceFormats();
} else { } else {
this._trimTrailingEmptySubPanes(); this._trimTrailingEmptySubPanes();
} }
@@ -1253,7 +1275,7 @@ export class ChartManager {
); );
} }
const showPriceLabel = this.shouldShowSeriesPriceLabel(_config, info.lastVal); const showPriceLabel = this.shouldShowSeriesPriceLabel(_config, info.lastVal, pane);
const series = this.chart.addSeries(LineSeries, { const series = this.chart.addSeries(LineSeries, {
color: info.color, color: info.color,
lineWidth: 1, lineWidth: 1,
@@ -2275,16 +2297,35 @@ export class ChartManager {
series.applyOptions(opts as any); series.applyOptions(opts as any);
} }
/** 차트 설정: 보조지표 우측 가격축 라벨·설명 on/off */ /** 차트 설정: 캔들·보조지표 영역 가격축 라벨·설명 (동일 값이면 기존 API 호환) */
setSeriesPriceLabelsEnabled(enabled: boolean): void { setSeriesPriceLabelsEnabled(enabled: boolean): void {
this._seriesPriceLabelsEnabled = enabled; this.setCandleAreaPriceLabelsEnabled(enabled);
this.setIndicatorAreaPriceLabelsEnabled(enabled);
}
setCandleAreaPriceLabelsEnabled(enabled: boolean): void {
this._candleAreaPriceLabelsEnabled = enabled;
this._refreshPriceLabelStyles();
}
setIndicatorAreaPriceLabelsEnabled(enabled: boolean): void {
this._indicatorAreaPriceLabelsEnabled = enabled;
this._refreshPriceLabelStyles();
}
getCandleAreaPriceLabelsEnabled(): boolean {
return this._candleAreaPriceLabelsEnabled;
}
getIndicatorAreaPriceLabelsEnabled(): boolean {
return this._indicatorAreaPriceLabelsEnabled;
}
private _refreshPriceLabelStyles(): void {
for (const entry of this.indicators.values()) { for (const entry of this.indicators.values()) {
this.applyIndicatorStyle(entry.config); this.applyIndicatorStyle(entry.config);
} }
} this._applyAllSeriesPriceFormats();
getSeriesPriceLabelsEnabled(): boolean {
return this._seriesPriceLabelsEnabled;
} }
/** 차트 설정: 거래량 pane 표시 on/off */ /** 차트 설정: 거래량 pane 표시 on/off */
@@ -2308,8 +2349,12 @@ export class ChartManager {
private shouldShowSeriesPriceLabel( private shouldShowSeriesPriceLabel(
config: IndicatorConfig, config: IndicatorConfig,
plotAllowsLastValue = true, plotAllowsLastValue = true,
paneIndex = 0,
): boolean { ): boolean {
return this._seriesPriceLabelsEnabled const areaEnabled = paneIndex < 2
? this._candleAreaPriceLabelsEnabled
: this._indicatorAreaPriceLabelsEnabled;
return areaEnabled
&& plotAllowsLastValue && plotAllowsLastValue
&& config.lastValueVisible !== false; && config.lastValueVisible !== false;
} }
@@ -2318,8 +2363,9 @@ export class ChartManager {
config: IndicatorConfig, config: IndicatorConfig,
plot: PlotDef | undefined, plot: PlotDef | undefined,
plotAllowsLastValue = true, plotAllowsLastValue = true,
paneIndex = 0,
): string { ): string {
if (!this.shouldShowSeriesPriceLabel(config, plotAllowsLastValue)) return ''; if (!this.shouldShowSeriesPriceLabel(config, plotAllowsLastValue, paneIndex)) return '';
if (config.type === 'IchimokuCloud' && plot?.id) { if (config.type === 'IchimokuCloud' && plot?.id) {
return getIchimokuPlotTitle(plot.id); return getIchimokuPlotTitle(plot.id);
} }
@@ -2362,10 +2408,10 @@ export class ChartManager {
if (entry.type === 'IchimokuCloud') { if (entry.type === 'IchimokuCloud') {
plotAllows = entry.seriesMeta[i]?.plotId !== 'plot2'; plotAllows = entry.seriesMeta[i]?.plotId !== 'plot2';
} }
const showPriceLabel = this.shouldShowSeriesPriceLabel(config, plotAllows); const showPriceLabel = this.shouldShowSeriesPriceLabel(config, plotAllows, paneIdx);
opts['lastValueVisible'] = showPriceLabel; opts['lastValueVisible'] = showPriceLabel;
opts['title'] = paneIdx < 2 && showPriceLabel opts['title'] = paneIdx < 2 && showPriceLabel
? this.seriesTitleForPlot(config, plot, plotAllows) ? this.seriesTitleForPlot(config, plot, plotAllows, paneIdx)
: ''; : '';
} else { } else {
opts['color'] = plot.color ?? '#aaa'; opts['color'] = plot.color ?? '#aaa';
+3 -1
View File
@@ -386,7 +386,9 @@ export interface AppSettingsDto {
btAutoPopup?: boolean; btAutoPopup?: boolean;
/** 백테스팅 매수/매도 마커에 금액 표시 여부 (기본 true) */ /** 백테스팅 매수/매도 마커에 금액 표시 여부 (기본 true) */
btShowPrice?: boolean; btShowPrice?: boolean;
/** 보조지표 우측 가격축 라벨·설명·금액 하이라이트 (기본 true) */ /** 캔들 영역 오버레이 지표 가격축 라벨·설명 (기본 true) */
chartCandleAreaPriceLabels?: boolean;
/** 하단 보조지표 pane 가격축 라벨·설명 (기본 true) */
chartSeriesPriceLabels?: boolean; chartSeriesPriceLabels?: boolean;
/** 차트 하단 거래량 바 표시 (기본 true) */ /** 차트 하단 거래량 바 표시 (기본 true) */
chartVolumeVisible?: boolean; chartVolumeVisible?: boolean;
+1
View File
@@ -471,6 +471,7 @@ export interface AppSettingsDto {
/** 백테스팅 매수/매도 마커에 금액 표시 여부 (기본 true) */ /** 백테스팅 매수/매도 마커에 금액 표시 여부 (기본 true) */
btShowPrice?: boolean; btShowPrice?: boolean;
/** 보조지표 우측 가격축 라벨·설명·금액 하이라이트 (기본 true) */ /** 보조지표 우측 가격축 라벨·설명·금액 하이라이트 (기본 true) */
chartCandleAreaPriceLabels?: boolean;
chartSeriesPriceLabels?: boolean; chartSeriesPriceLabels?: boolean;
/** 차트 하단 거래량 바 표시 (기본 true) */ /** 차트 하단 거래량 바 표시 (기본 true) */
chartVolumeVisible?: boolean; chartVolumeVisible?: boolean;