차트 세로크로스헤어 교차점 on off
This commit is contained in:
@@ -122,6 +122,11 @@ public class GcAppSettings {
|
|||||||
@Builder.Default
|
@Builder.Default
|
||||||
private Boolean chartCrosshairInfoVisible = true;
|
private Boolean chartCrosshairInfoVisible = true;
|
||||||
|
|
||||||
|
/** 세로 크로스헤어와 지표 선 교차점 점 표시 (기본 true) */
|
||||||
|
@Column(name = "chart_crosshair_markers_visible", nullable = false)
|
||||||
|
@Builder.Default
|
||||||
|
private Boolean chartCrosshairMarkersVisible = true;
|
||||||
|
|
||||||
/** 차트 마우스오버 줌·이동 플로팅 툴바(− + ⌐↔ ‹ ›) 표시 (기본 true) */
|
/** 차트 마우스오버 줌·이동 플로팅 툴바(− + ⌐↔ ‹ ›) 표시 (기본 true) */
|
||||||
@Column(name = "chart_hover_toolbar_visible", nullable = false)
|
@Column(name = "chart_hover_toolbar_visible", nullable = false)
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
|
|||||||
@@ -100,6 +100,8 @@ public class AppSettingsService {
|
|||||||
Boolean.parseBoolean(d.get("chartLiveReceiveHighlight").toString()));
|
Boolean.parseBoolean(d.get("chartLiveReceiveHighlight").toString()));
|
||||||
if (d.containsKey("chartCrosshairInfoVisible")) s.setChartCrosshairInfoVisible(
|
if (d.containsKey("chartCrosshairInfoVisible")) s.setChartCrosshairInfoVisible(
|
||||||
Boolean.parseBoolean(d.get("chartCrosshairInfoVisible").toString()));
|
Boolean.parseBoolean(d.get("chartCrosshairInfoVisible").toString()));
|
||||||
|
if (d.containsKey("chartCrosshairMarkersVisible")) s.setChartCrosshairMarkersVisible(
|
||||||
|
Boolean.parseBoolean(d.get("chartCrosshairMarkersVisible").toString()));
|
||||||
if (d.containsKey("chartHoverToolbarVisible")) s.setChartHoverToolbarVisible(
|
if (d.containsKey("chartHoverToolbarVisible")) s.setChartHoverToolbarVisible(
|
||||||
Boolean.parseBoolean(d.get("chartHoverToolbarVisible").toString()));
|
Boolean.parseBoolean(d.get("chartHoverToolbarVisible").toString()));
|
||||||
if (d.containsKey("chartLegendOptions")) s.setChartLegendOptionsJson(toJson(d.get("chartLegendOptions")));
|
if (d.containsKey("chartLegendOptions")) s.setChartLegendOptionsJson(toJson(d.get("chartLegendOptions")));
|
||||||
@@ -219,6 +221,7 @@ public class AppSettingsService {
|
|||||||
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);
|
||||||
m.put("chartCrosshairInfoVisible", s.getChartCrosshairInfoVisible() != null ? s.getChartCrosshairInfoVisible() : true);
|
m.put("chartCrosshairInfoVisible", s.getChartCrosshairInfoVisible() != null ? s.getChartCrosshairInfoVisible() : true);
|
||||||
|
m.put("chartCrosshairMarkersVisible", s.getChartCrosshairMarkersVisible() != null ? s.getChartCrosshairMarkersVisible() : true);
|
||||||
m.put("chartHoverToolbarVisible", s.getChartHoverToolbarVisible() != null ? s.getChartHoverToolbarVisible() : true);
|
m.put("chartHoverToolbarVisible", s.getChartHoverToolbarVisible() != null ? s.getChartHoverToolbarVisible() : true);
|
||||||
m.put("chartLegendOptions", parseJson(s.getChartLegendOptionsJson()));
|
m.put("chartLegendOptions", parseJson(s.getChartLegendOptionsJson()));
|
||||||
m.put("chartPaneSeparator", parseJson(s.getChartPaneSeparatorJson()));
|
m.put("chartPaneSeparator", parseJson(s.getChartPaneSeparatorJson()));
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE gc_app_settings
|
||||||
|
ADD COLUMN chart_crosshair_markers_visible BOOLEAN NOT NULL DEFAULT TRUE
|
||||||
|
COMMENT '세로 크로스헤어와 지표 선 교차점 점 표시';
|
||||||
@@ -52,6 +52,8 @@ const NULL_SETTINGS_PAGE_PROPS: SettingsPageChartBindings = {
|
|||||||
onChartLegendOptionsChange: () => {},
|
onChartLegendOptionsChange: () => {},
|
||||||
chartCrosshairInfoVisible: true,
|
chartCrosshairInfoVisible: true,
|
||||||
onChartCrosshairInfoVisible: () => {},
|
onChartCrosshairInfoVisible: () => {},
|
||||||
|
chartCrosshairMarkersVisible: true,
|
||||||
|
onChartCrosshairMarkersVisible: () => {},
|
||||||
chartHoverToolbarVisible: true,
|
chartHoverToolbarVisible: true,
|
||||||
onChartHoverToolbarVisible: () => {},
|
onChartHoverToolbarVisible: () => {},
|
||||||
chartPaneSeparator: undefined,
|
chartPaneSeparator: undefined,
|
||||||
|
|||||||
@@ -192,6 +192,8 @@ export interface SettingsPageChartBindings {
|
|||||||
onChartLegendOptionsChange: (patch: Partial<import('../types/chartLegend').ChartLegendVisibility>) => void;
|
onChartLegendOptionsChange: (patch: Partial<import('../types/chartLegend').ChartLegendVisibility>) => void;
|
||||||
chartCrosshairInfoVisible: boolean;
|
chartCrosshairInfoVisible: boolean;
|
||||||
onChartCrosshairInfoVisible: (v: boolean) => void;
|
onChartCrosshairInfoVisible: (v: boolean) => void;
|
||||||
|
chartCrosshairMarkersVisible: boolean;
|
||||||
|
onChartCrosshairMarkersVisible: (v: boolean) => void;
|
||||||
chartHoverToolbarVisible: boolean;
|
chartHoverToolbarVisible: boolean;
|
||||||
onChartHoverToolbarVisible: (v: boolean) => void;
|
onChartHoverToolbarVisible: (v: boolean) => void;
|
||||||
chartPaneSeparator: import('../types/chartPaneSeparator').ChartPaneSeparatorOptions | undefined;
|
chartPaneSeparator: import('../types/chartPaneSeparator').ChartPaneSeparatorOptions | undefined;
|
||||||
@@ -610,6 +612,7 @@ export function useChartWorkspace({
|
|||||||
const chartLiveReceiveHighlight = appDefaults.chartLiveReceiveHighlight ?? true;
|
const chartLiveReceiveHighlight = appDefaults.chartLiveReceiveHighlight ?? true;
|
||||||
const chartLegendOptions = resolveChartLegendOptions(appDefaults.chartLegendOptions);
|
const chartLegendOptions = resolveChartLegendOptions(appDefaults.chartLegendOptions);
|
||||||
const chartCrosshairInfoVisible = appDefaults.chartCrosshairInfoVisible ?? true;
|
const chartCrosshairInfoVisible = appDefaults.chartCrosshairInfoVisible ?? true;
|
||||||
|
const chartCrosshairMarkersVisible = appDefaults.chartCrosshairMarkersVisible ?? true;
|
||||||
const chartHoverToolbarVisible = appDefaults.chartHoverToolbarVisible ?? true;
|
const chartHoverToolbarVisible = appDefaults.chartHoverToolbarVisible ?? true;
|
||||||
const chartPaneSeparator = resolveChartPaneSeparatorOptions(appDefaults.chartPaneSeparator);
|
const chartPaneSeparator = resolveChartPaneSeparatorOptions(appDefaults.chartPaneSeparator);
|
||||||
const paperTradingEnabled = appDefaults.paperTradingEnabled ?? true;
|
const paperTradingEnabled = appDefaults.paperTradingEnabled ?? true;
|
||||||
@@ -1010,6 +1013,12 @@ export function useChartWorkspace({
|
|||||||
});
|
});
|
||||||
}, [appSettingsLoaded, chartCandleAreaPriceLabels, chartSeriesPriceLabels]);
|
}, [appSettingsLoaded, chartCandleAreaPriceLabels, chartSeriesPriceLabels]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!appSettingsLoaded) return;
|
||||||
|
managerRef.current?.setCrosshairMarkersVisible(chartCrosshairMarkersVisible);
|
||||||
|
slotRefs.current.forEach(slot => slot?.setCrosshairMarkersVisible(chartCrosshairMarkersVisible));
|
||||||
|
}, [appSettingsLoaded, chartCrosshairMarkersVisible]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!appSettingsLoaded) return;
|
if (!appSettingsLoaded) return;
|
||||||
const wrapper = document.querySelector('.chart-wrapper') as HTMLElement | null;
|
const wrapper = document.querySelector('.chart-wrapper') as HTMLElement | null;
|
||||||
@@ -1903,6 +1912,12 @@ export function useChartWorkspace({
|
|||||||
},
|
},
|
||||||
chartCrosshairInfoVisible,
|
chartCrosshairInfoVisible,
|
||||||
onChartCrosshairInfoVisible: v => saveAppDef({ chartCrosshairInfoVisible: v }),
|
onChartCrosshairInfoVisible: v => saveAppDef({ chartCrosshairInfoVisible: v }),
|
||||||
|
chartCrosshairMarkersVisible,
|
||||||
|
onChartCrosshairMarkersVisible: v => {
|
||||||
|
saveAppDef({ chartCrosshairMarkersVisible: v });
|
||||||
|
managerRef.current?.setCrosshairMarkersVisible(v);
|
||||||
|
slotRefs.current.forEach(slot => slot?.setCrosshairMarkersVisible(v));
|
||||||
|
},
|
||||||
chartHoverToolbarVisible,
|
chartHoverToolbarVisible,
|
||||||
onChartHoverToolbarVisible: v => saveAppDef({ chartHoverToolbarVisible: v }),
|
onChartHoverToolbarVisible: v => saveAppDef({ chartHoverToolbarVisible: v }),
|
||||||
chartPaneSeparator,
|
chartPaneSeparator,
|
||||||
@@ -1929,6 +1944,7 @@ export function useChartWorkspace({
|
|||||||
handleAddIndicator, handleRemoveByType, handleIndicatorListOrderChange,
|
handleAddIndicator, handleRemoveByType, handleIndicatorListOrderChange,
|
||||||
chartCandleAreaPriceLabels, chartSeriesPriceLabels, chartVolumeVisible,
|
chartCandleAreaPriceLabels, chartSeriesPriceLabels, chartVolumeVisible,
|
||||||
chartLiveReceiveHighlight, chartLegendOptions, chartCrosshairInfoVisible,
|
chartLiveReceiveHighlight, chartLegendOptions, chartCrosshairInfoVisible,
|
||||||
|
chartCrosshairMarkersVisible,
|
||||||
chartHoverToolbarVisible, chartPaneSeparator, saveAppDef, clearLiveMarkers,
|
chartHoverToolbarVisible, chartPaneSeparator, saveAppDef, clearLiveMarkers,
|
||||||
refreshPaperAccount, handleTimezoneChange, handleChartTimeFormatChange,
|
refreshPaperAccount, handleTimezoneChange, handleChartTimeFormatChange,
|
||||||
handleTradeAlertTimeFormatChange, displayTimezone, chartTimeFormat, tradeAlertTimeFormat,
|
handleTradeAlertTimeFormatChange, displayTimezone, chartTimeFormat, tradeAlertTimeFormat,
|
||||||
|
|||||||
@@ -137,6 +137,7 @@ export interface ChartSlotHandle {
|
|||||||
setSeriesPriceLabelsEnabled: (enabled: boolean) => void;
|
setSeriesPriceLabelsEnabled: (enabled: boolean) => void;
|
||||||
setCandleAreaPriceLabelsEnabled: (enabled: boolean) => void;
|
setCandleAreaPriceLabelsEnabled: (enabled: boolean) => void;
|
||||||
setIndicatorAreaPriceLabelsEnabled: (enabled: boolean) => void;
|
setIndicatorAreaPriceLabelsEnabled: (enabled: boolean) => void;
|
||||||
|
setCrosshairMarkersVisible: (enabled: boolean) => void;
|
||||||
/** 거래량 pane on/off */
|
/** 거래량 pane on/off */
|
||||||
setVolumeVisible: (visible: boolean) => void;
|
setVolumeVisible: (visible: boolean) => void;
|
||||||
/** 9·20일 신고가·신저가 표시 */
|
/** 9·20일 신고가·신저가 표시 */
|
||||||
@@ -382,6 +383,9 @@ const ChartSlot = forwardRef<ChartSlotHandle, ChartSlotProps>(function ChartSlot
|
|||||||
setIndicatorAreaPriceLabelsEnabled: (enabled: boolean) => {
|
setIndicatorAreaPriceLabelsEnabled: (enabled: boolean) => {
|
||||||
managerRef.current?.setIndicatorAreaPriceLabelsEnabled(enabled);
|
managerRef.current?.setIndicatorAreaPriceLabelsEnabled(enabled);
|
||||||
},
|
},
|
||||||
|
setCrosshairMarkersVisible: (enabled: boolean) => {
|
||||||
|
managerRef.current?.setCrosshairMarkersVisible(enabled);
|
||||||
|
},
|
||||||
setVolumeVisible: (visible: boolean) => {
|
setVolumeVisible: (visible: boolean) => {
|
||||||
managerRef.current?.setVolumeVisible(visible);
|
managerRef.current?.setVolumeVisible(visible);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -129,6 +129,8 @@ interface SettingsPageProps {
|
|||||||
onChartLegendOptionsChange?: (patch: Partial<ChartLegendVisibility>) => void;
|
onChartLegendOptionsChange?: (patch: Partial<ChartLegendVisibility>) => void;
|
||||||
chartCrosshairInfoVisible?: boolean;
|
chartCrosshairInfoVisible?: boolean;
|
||||||
onChartCrosshairInfoVisible?: (v: boolean) => void;
|
onChartCrosshairInfoVisible?: (v: boolean) => void;
|
||||||
|
chartCrosshairMarkersVisible?: boolean;
|
||||||
|
onChartCrosshairMarkersVisible?: (v: boolean) => void;
|
||||||
chartHoverToolbarVisible?: boolean;
|
chartHoverToolbarVisible?: boolean;
|
||||||
onChartHoverToolbarVisible?: (v: boolean) => void;
|
onChartHoverToolbarVisible?: (v: boolean) => void;
|
||||||
chartPaneSeparator?: ChartPaneSeparatorOptions;
|
chartPaneSeparator?: ChartPaneSeparatorOptions;
|
||||||
@@ -1010,6 +1012,8 @@ interface ChartPanelProps {
|
|||||||
onChartLegendOptionsChange?: (patch: Partial<ChartLegendVisibility>) => void;
|
onChartLegendOptionsChange?: (patch: Partial<ChartLegendVisibility>) => void;
|
||||||
chartCrosshairInfoVisible?: boolean;
|
chartCrosshairInfoVisible?: boolean;
|
||||||
onChartCrosshairInfoVisible?: (v: boolean) => void;
|
onChartCrosshairInfoVisible?: (v: boolean) => void;
|
||||||
|
chartCrosshairMarkersVisible?: boolean;
|
||||||
|
onChartCrosshairMarkersVisible?: (v: boolean) => void;
|
||||||
chartHoverToolbarVisible?: boolean;
|
chartHoverToolbarVisible?: boolean;
|
||||||
onChartHoverToolbarVisible?: (v: boolean) => void;
|
onChartHoverToolbarVisible?: (v: boolean) => void;
|
||||||
chartPaneSeparator?: ChartPaneSeparatorOptions;
|
chartPaneSeparator?: ChartPaneSeparatorOptions;
|
||||||
@@ -1036,6 +1040,8 @@ const ChartPanel: React.FC<ChartPanelProps> = ({
|
|||||||
onChartLegendOptionsChange,
|
onChartLegendOptionsChange,
|
||||||
chartCrosshairInfoVisible = true,
|
chartCrosshairInfoVisible = true,
|
||||||
onChartCrosshairInfoVisible,
|
onChartCrosshairInfoVisible,
|
||||||
|
chartCrosshairMarkersVisible = true,
|
||||||
|
onChartCrosshairMarkersVisible,
|
||||||
chartHoverToolbarVisible = true,
|
chartHoverToolbarVisible = true,
|
||||||
onChartHoverToolbarVisible,
|
onChartHoverToolbarVisible,
|
||||||
chartPaneSeparator,
|
chartPaneSeparator,
|
||||||
@@ -1248,6 +1254,19 @@ const ChartPanel: React.FC<ChartPanelProps> = ({
|
|||||||
<span className="stg-toggle-slider" />
|
<span className="stg-toggle-slider" />
|
||||||
</label>
|
</label>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
|
<SettingRow
|
||||||
|
label="교차점 점 표시"
|
||||||
|
desc="켜면 세로 크로스헤어가 캔들·보조지표 선과 만나는 위치에 색 점이 표시됩니다. 끄면 크로스헤어 선만 표시됩니다."
|
||||||
|
>
|
||||||
|
<label className="stg-toggle">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={chartCrosshairMarkersVisible}
|
||||||
|
onChange={e => onChartCrosshairMarkersVisible?.(e.target.checked)}
|
||||||
|
/>
|
||||||
|
<span className="stg-toggle-slider" />
|
||||||
|
</label>
|
||||||
|
</SettingRow>
|
||||||
</SettingSection>
|
</SettingSection>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -1955,6 +1974,8 @@ const SettingsPage: React.FC<SettingsPageProps> = ({
|
|||||||
onChartLegendOptionsChange,
|
onChartLegendOptionsChange,
|
||||||
chartCrosshairInfoVisible = true,
|
chartCrosshairInfoVisible = true,
|
||||||
onChartCrosshairInfoVisible,
|
onChartCrosshairInfoVisible,
|
||||||
|
chartCrosshairMarkersVisible = true,
|
||||||
|
onChartCrosshairMarkersVisible,
|
||||||
chartHoverToolbarVisible = true,
|
chartHoverToolbarVisible = true,
|
||||||
onChartHoverToolbarVisible,
|
onChartHoverToolbarVisible,
|
||||||
chartPaneSeparator,
|
chartPaneSeparator,
|
||||||
@@ -2077,6 +2098,8 @@ const SettingsPage: React.FC<SettingsPageProps> = ({
|
|||||||
onChartLegendOptionsChange={onChartLegendOptionsChange}
|
onChartLegendOptionsChange={onChartLegendOptionsChange}
|
||||||
chartCrosshairInfoVisible={chartCrosshairInfoVisible}
|
chartCrosshairInfoVisible={chartCrosshairInfoVisible}
|
||||||
onChartCrosshairInfoVisible={onChartCrosshairInfoVisible}
|
onChartCrosshairInfoVisible={onChartCrosshairInfoVisible}
|
||||||
|
chartCrosshairMarkersVisible={chartCrosshairMarkersVisible}
|
||||||
|
onChartCrosshairMarkersVisible={onChartCrosshairMarkersVisible}
|
||||||
chartHoverToolbarVisible={chartHoverToolbarVisible}
|
chartHoverToolbarVisible={chartHoverToolbarVisible}
|
||||||
onChartHoverToolbarVisible={onChartHoverToolbarVisible}
|
onChartHoverToolbarVisible={onChartHoverToolbarVisible}
|
||||||
chartPaneSeparator={chartPaneSeparator}
|
chartPaneSeparator={chartPaneSeparator}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { formatUpbitKrwPrice } from '../utils/safeFormat';
|
|||||||
import { useChartTimeFormat } from '../utils/chartTimeFormat';
|
import { useChartTimeFormat } from '../utils/chartTimeFormat';
|
||||||
import { setIndicatorChartContext } from '../utils/indicatorRegistry';
|
import { setIndicatorChartContext } from '../utils/indicatorRegistry';
|
||||||
import { DISPLAY_COUNT } from '../hooks/useUpbitData';
|
import { DISPLAY_COUNT } from '../hooks/useUpbitData';
|
||||||
|
import { useAppSettings, resolveAppDefaults } from '../hooks/useAppSettings';
|
||||||
|
|
||||||
/** Upbit 실시간 — 초기 히스토리가 이 개수 미만이면 setData 하지 않음 (종목 전환 직후 sparse 봉 → 캔들 폭 비정상) */
|
/** Upbit 실시간 — 초기 히스토리가 이 개수 미만이면 setData 하지 않음 (종목 전환 직후 sparse 봉 → 캔들 폭 비정상) */
|
||||||
const MIN_BARS_FOR_FULL_RELOAD = Math.min(50, Math.floor(DISPLAY_COUNT / 4));
|
const MIN_BARS_FOR_FULL_RELOAD = Math.min(50, Math.floor(DISPLAY_COUNT / 4));
|
||||||
@@ -165,6 +166,8 @@ interface TradingChartProps {
|
|||||||
showCandleOverlayControls?: boolean;
|
showCandleOverlayControls?: boolean;
|
||||||
/** 크로스헤어 이동 시 OHLC·보조지표 수치 표시 (기본 true) */
|
/** 크로스헤어 이동 시 OHLC·보조지표 수치 표시 (기본 true) */
|
||||||
crosshairInfoVisible?: boolean;
|
crosshairInfoVisible?: boolean;
|
||||||
|
/** 세로 크로스헤어와 선 그래프 교차점 점 표시 (미지정 시 전역 차트 설정) */
|
||||||
|
crosshairMarkersVisible?: boolean;
|
||||||
/** 외부 캔들 데이터 fetch 중 (실시간 차트 초기 로딩) */
|
/** 외부 캔들 데이터 fetch 중 (실시간 차트 초기 로딩) */
|
||||||
dataLoading?: boolean;
|
dataLoading?: boolean;
|
||||||
/** chartPaintReady 변경 알림 (워크스페이스 전체 로딩 오버레이용) */
|
/** chartPaintReady 변경 알림 (워크스페이스 전체 로딩 오버레이용) */
|
||||||
@@ -237,6 +240,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
|||||||
showChartRightToolbar,
|
showChartRightToolbar,
|
||||||
showCandleOverlayControls = false,
|
showCandleOverlayControls = false,
|
||||||
crosshairInfoVisible = true,
|
crosshairInfoVisible = true,
|
||||||
|
crosshairMarkersVisible,
|
||||||
dataLoading = false,
|
dataLoading = false,
|
||||||
onChartPaintReadyChange,
|
onChartPaintReadyChange,
|
||||||
defaultCandleOnly = false,
|
defaultCandleOnly = false,
|
||||||
@@ -533,6 +537,10 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
|||||||
mgr.setVolumeVisible(volumeVisible, wrapperH > 0 ? wrapperH : undefined);
|
mgr.setVolumeVisible(volumeVisible, wrapperH > 0 ? wrapperH : undefined);
|
||||||
}, [volumeVisible]);
|
}, [volumeVisible]);
|
||||||
|
|
||||||
|
const { settings } = useAppSettings();
|
||||||
|
const appDefaults = resolveAppDefaults(settings);
|
||||||
|
const resolvedCrosshairMarkers = crosshairMarkersVisible ?? appDefaults.chartCrosshairMarkersVisible ?? true;
|
||||||
|
|
||||||
const resolvedCandlePriceLabels = candleAreaPriceLabelsEnabled ?? seriesPriceLabelsEnabled ?? true;
|
const resolvedCandlePriceLabels = candleAreaPriceLabelsEnabled ?? seriesPriceLabelsEnabled ?? true;
|
||||||
const resolvedIndicatorPriceLabels = indicatorAreaPriceLabelsEnabled ?? seriesPriceLabelsEnabled ?? true;
|
const resolvedIndicatorPriceLabels = indicatorAreaPriceLabelsEnabled ?? seriesPriceLabelsEnabled ?? true;
|
||||||
|
|
||||||
@@ -544,6 +552,10 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
|||||||
managerRef.current?.setIndicatorAreaPriceLabelsEnabled(resolvedIndicatorPriceLabels);
|
managerRef.current?.setIndicatorAreaPriceLabelsEnabled(resolvedIndicatorPriceLabels);
|
||||||
}, [resolvedIndicatorPriceLabels]);
|
}, [resolvedIndicatorPriceLabels]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
managerRef.current?.setCrosshairMarkersVisible(resolvedCrosshairMarkers);
|
||||||
|
}, [resolvedCrosshairMarkers]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!paneSeparatorOptions) return;
|
if (!paneSeparatorOptions) return;
|
||||||
managerRef.current?.setPaneSeparatorOptions(paneSeparatorOptions);
|
managerRef.current?.setPaneSeparatorOptions(paneSeparatorOptions);
|
||||||
@@ -1025,6 +1037,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
|||||||
}
|
}
|
||||||
mgr.setCandleAreaPriceLabelsEnabled(resolvedCandlePriceLabels);
|
mgr.setCandleAreaPriceLabelsEnabled(resolvedCandlePriceLabels);
|
||||||
mgr.setIndicatorAreaPriceLabelsEnabled(resolvedIndicatorPriceLabels);
|
mgr.setIndicatorAreaPriceLabelsEnabled(resolvedIndicatorPriceLabels);
|
||||||
|
mgr.setCrosshairMarkersVisible(resolvedCrosshairMarkers);
|
||||||
if (paneSeparatorOptions) mgr.setPaneSeparatorOptions(paneSeparatorOptions);
|
if (paneSeparatorOptions) mgr.setPaneSeparatorOptions(paneSeparatorOptions);
|
||||||
if (auxIndicatorOnlyRef.current) mgr.applyAuxIndicatorOnlyLayout(true);
|
if (auxIndicatorOnlyRef.current) mgr.applyAuxIndicatorOnlyLayout(true);
|
||||||
onManagerReady(mgr);
|
onManagerReady(mgr);
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ interface Props {
|
|||||||
indicatorAreaEnabled: boolean;
|
indicatorAreaEnabled: boolean;
|
||||||
onCandleAreaChange: (enabled: boolean) => void;
|
onCandleAreaChange: (enabled: boolean) => void;
|
||||||
onIndicatorAreaChange: (enabled: boolean) => void;
|
onIndicatorAreaChange: (enabled: boolean) => void;
|
||||||
|
crosshairMarkersEnabled?: boolean;
|
||||||
|
onCrosshairMarkersChange?: (enabled: boolean) => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,8 +18,11 @@ const ChartPriceAxisLabelSettings: React.FC<Props> = ({
|
|||||||
indicatorAreaEnabled,
|
indicatorAreaEnabled,
|
||||||
onCandleAreaChange,
|
onCandleAreaChange,
|
||||||
onIndicatorAreaChange,
|
onIndicatorAreaChange,
|
||||||
|
crosshairMarkersEnabled = true,
|
||||||
|
onCrosshairMarkersChange,
|
||||||
className = '',
|
className = '',
|
||||||
}) => (
|
}) => (
|
||||||
|
<>
|
||||||
<section className={`seval-price-label-settings${className ? ` ${className}` : ''}`}>
|
<section className={`seval-price-label-settings${className ? ` ${className}` : ''}`}>
|
||||||
<h3 className="seval-price-label-settings-title">지표 가격축 라벨·설명</h3>
|
<h3 className="seval-price-label-settings-title">지표 가격축 라벨·설명</h3>
|
||||||
<div className="seval-price-label-settings-body">
|
<div className="seval-price-label-settings-body">
|
||||||
@@ -68,6 +73,30 @@ const ChartPriceAxisLabelSettings: React.FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
{onCrosshairMarkersChange && (
|
||||||
|
<section className={`seval-price-label-settings seval-crosshair-marker-settings${className ? ` ${className}` : ''}`}>
|
||||||
|
<h3 className="seval-price-label-settings-title">크로스헤어 교차점</h3>
|
||||||
|
<div className="seval-price-label-settings-body">
|
||||||
|
<div className="seval-price-label-row">
|
||||||
|
<div className="seval-price-label-row-text">
|
||||||
|
<span className="seval-price-label-row-title">교차점 점 표시</span>
|
||||||
|
<span className="seval-price-label-row-desc">
|
||||||
|
켜면 세로 크로스헤어가 캔들·보조지표 선과 만나는 위치에 색 점이 표시됩니다.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<label className="ism-toggle seval-price-label-toggle" title="크로스헤어 교차점 점 표시">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={crosshairMarkersEnabled}
|
||||||
|
onChange={e => onCrosshairMarkersChange(e.target.checked)}
|
||||||
|
/>
|
||||||
|
<span className="ism-toggle-slider" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default ChartPriceAxisLabelSettings;
|
export default ChartPriceAxisLabelSettings;
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ const StrategyEvaluationChartSettingsTab: React.FC<Props> = ({
|
|||||||
const appDefaults = useMemo(() => resolveAppDefaults(settings), [settings]);
|
const appDefaults = useMemo(() => resolveAppDefaults(settings), [settings]);
|
||||||
const candlePriceLabels = appDefaults.chartCandleAreaPriceLabels !== false;
|
const candlePriceLabels = appDefaults.chartCandleAreaPriceLabels !== false;
|
||||||
const indicatorPriceLabels = appDefaults.chartSeriesPriceLabels !== false;
|
const indicatorPriceLabels = appDefaults.chartSeriesPriceLabels !== false;
|
||||||
|
const crosshairMarkers = appDefaults.chartCrosshairMarkersVisible !== false;
|
||||||
|
|
||||||
const reloadFromDb = useCallback(() => {
|
const reloadFromDb = useCallback(() => {
|
||||||
const next: Record<string, IndicatorConfig> = {};
|
const next: Record<string, IndicatorConfig> = {};
|
||||||
@@ -221,6 +222,8 @@ const StrategyEvaluationChartSettingsTab: React.FC<Props> = ({
|
|||||||
indicatorAreaEnabled={indicatorPriceLabels}
|
indicatorAreaEnabled={indicatorPriceLabels}
|
||||||
onCandleAreaChange={v => saveAppSettings({ chartCandleAreaPriceLabels: v })}
|
onCandleAreaChange={v => saveAppSettings({ chartCandleAreaPriceLabels: v })}
|
||||||
onIndicatorAreaChange={v => saveAppSettings({ chartSeriesPriceLabels: v })}
|
onIndicatorAreaChange={v => saveAppSettings({ chartSeriesPriceLabels: v })}
|
||||||
|
crosshairMarkersEnabled={crosshairMarkers}
|
||||||
|
onCrosshairMarkersChange={v => saveAppSettings({ chartCrosshairMarkersVisible: v })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -177,6 +177,7 @@ export function resolveAppDefaults(s: AppSettingsDto) {
|
|||||||
chartVolumeVisible: s.chartVolumeVisible ?? true,
|
chartVolumeVisible: s.chartVolumeVisible ?? true,
|
||||||
chartLiveReceiveHighlight: s.chartLiveReceiveHighlight ?? true,
|
chartLiveReceiveHighlight: s.chartLiveReceiveHighlight ?? true,
|
||||||
chartCrosshairInfoVisible: s.chartCrosshairInfoVisible ?? true,
|
chartCrosshairInfoVisible: s.chartCrosshairInfoVisible ?? true,
|
||||||
|
chartCrosshairMarkersVisible: s.chartCrosshairMarkersVisible ?? true,
|
||||||
chartHoverToolbarVisible: s.chartHoverToolbarVisible ?? true,
|
chartHoverToolbarVisible: s.chartHoverToolbarVisible ?? true,
|
||||||
chartLegendOptions: resolveChartLegendOptions(
|
chartLegendOptions: resolveChartLegendOptions(
|
||||||
s.chartLegendOptions as Partial<ChartLegendVisibility> | null | undefined,
|
s.chartLegendOptions as Partial<ChartLegendVisibility> | null | undefined,
|
||||||
|
|||||||
@@ -347,6 +347,8 @@ export class ChartManager {
|
|||||||
private _candleAreaPriceLabelsEnabled = true;
|
private _candleAreaPriceLabelsEnabled = true;
|
||||||
/** 하단 보조지표 pane(2+) — 우측 가격축 라벨·설명 */
|
/** 하단 보조지표 pane(2+) — 우측 가격축 라벨·설명 */
|
||||||
private _indicatorAreaPriceLabelsEnabled = true;
|
private _indicatorAreaPriceLabelsEnabled = true;
|
||||||
|
/** 세로 크로스헤어와 선 그래프 교차점 점 표시 */
|
||||||
|
private _crosshairMarkersVisible = true;
|
||||||
|
|
||||||
/** 차트 하단 거래량 pane 표시 */
|
/** 차트 하단 거래량 pane 표시 */
|
||||||
private _volumeVisible = true;
|
private _volumeVisible = true;
|
||||||
@@ -596,15 +598,23 @@ export class ChartManager {
|
|||||||
borderUpColor: t.upColor, borderDownColor: t.downColor,
|
borderUpColor: t.upColor, borderDownColor: t.downColor,
|
||||||
wickUpColor: t.upColor, wickDownColor: t.downColor,
|
wickUpColor: t.upColor, wickDownColor: t.downColor,
|
||||||
priceFormat: MAIN_PRICE_FORMAT,
|
priceFormat: MAIN_PRICE_FORMAT,
|
||||||
|
...this._crosshairMarkerSeriesOptions(),
|
||||||
});
|
});
|
||||||
case 'bar':
|
case 'bar':
|
||||||
return this.chart.addSeries(BarSeries, { upColor: t.upColor, downColor: t.downColor, priceFormat: MAIN_PRICE_FORMAT });
|
return this.chart.addSeries(BarSeries, {
|
||||||
|
upColor: t.upColor, downColor: t.downColor, priceFormat: MAIN_PRICE_FORMAT,
|
||||||
|
...this._crosshairMarkerSeriesOptions(),
|
||||||
|
});
|
||||||
case 'line':
|
case 'line':
|
||||||
return this.chart.addSeries(LineSeries, { color: '#2962FF', lineWidth: 2, crosshairMarkerVisible: true, priceFormat: MAIN_PRICE_FORMAT });
|
return this.chart.addSeries(LineSeries, {
|
||||||
|
color: '#2962FF', lineWidth: 2, priceFormat: MAIN_PRICE_FORMAT,
|
||||||
|
...this._crosshairMarkerSeriesOptions(),
|
||||||
|
});
|
||||||
case 'area':
|
case 'area':
|
||||||
return this.chart.addSeries(AreaSeries, {
|
return this.chart.addSeries(AreaSeries, {
|
||||||
lineColor: '#2962FF', topColor: '#2962FF44', bottomColor: '#2962FF00', lineWidth: 2,
|
lineColor: '#2962FF', topColor: '#2962FF44', bottomColor: '#2962FF00', lineWidth: 2,
|
||||||
priceFormat: MAIN_PRICE_FORMAT,
|
priceFormat: MAIN_PRICE_FORMAT,
|
||||||
|
...this._crosshairMarkerSeriesOptions(),
|
||||||
});
|
});
|
||||||
case 'baseline':
|
case 'baseline':
|
||||||
return this.chart.addSeries(BaselineSeries, {
|
return this.chart.addSeries(BaselineSeries, {
|
||||||
@@ -612,6 +622,7 @@ export class ChartManager {
|
|||||||
topLineColor: '#26a69a', topFillColor1: '#26a69a44', topFillColor2: '#26a69a00',
|
topLineColor: '#26a69a', topFillColor1: '#26a69a44', topFillColor2: '#26a69a00',
|
||||||
bottomLineColor: '#ef5350', bottomFillColor1: '#ef535000', bottomFillColor2: '#ef535044',
|
bottomLineColor: '#ef5350', bottomFillColor1: '#ef535000', bottomFillColor2: '#ef535044',
|
||||||
priceFormat: MAIN_PRICE_FORMAT,
|
priceFormat: MAIN_PRICE_FORMAT,
|
||||||
|
...this._crosshairMarkerSeriesOptions(),
|
||||||
});
|
});
|
||||||
default:
|
default:
|
||||||
return this.chart.addSeries(CandlestickSeries, {
|
return this.chart.addSeries(CandlestickSeries, {
|
||||||
@@ -619,6 +630,7 @@ export class ChartManager {
|
|||||||
borderUpColor: t.upColor, borderDownColor: t.downColor,
|
borderUpColor: t.upColor, borderDownColor: t.downColor,
|
||||||
wickUpColor: t.upColor, wickDownColor: t.downColor,
|
wickUpColor: t.upColor, wickDownColor: t.downColor,
|
||||||
priceFormat: MAIN_PRICE_FORMAT,
|
priceFormat: MAIN_PRICE_FORMAT,
|
||||||
|
...this._crosshairMarkerSeriesOptions(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -979,8 +991,7 @@ export class ChartManager {
|
|||||||
lineStyle: plotSeriesLineStyle(plotDef.lineStyle),
|
lineStyle: plotSeriesLineStyle(plotDef.lineStyle),
|
||||||
priceLineVisible: false,
|
priceLineVisible: false,
|
||||||
lastValueVisible: showPriceLabel,
|
lastValueVisible: showPriceLabel,
|
||||||
crosshairMarkerVisible: true,
|
...this._crosshairMarkerSeriesOptions(plotId === 'plot0' ? 4 : 3),
|
||||||
crosshairMarkerRadius: plotId === 'plot0' ? 4 : 3,
|
|
||||||
title: showSeriesTitle ? this.seriesTitleForPlot(enriched, plotDef) : '',
|
title: showSeriesTitle ? this.seriesTitleForPlot(enriched, plotDef) : '',
|
||||||
visible: isPlotVisible,
|
visible: isPlotVisible,
|
||||||
priceScaleId: obvScaleId,
|
priceScaleId: obvScaleId,
|
||||||
@@ -1046,7 +1057,7 @@ export class ChartManager {
|
|||||||
lineWidth: Math.max(1, plotDef.lineWidth ?? regPlot?.lineWidth ?? 1) as 1 | 2 | 3 | 4,
|
lineWidth: Math.max(1, plotDef.lineWidth ?? regPlot?.lineWidth ?? 1) as 1 | 2 | 3 | 4,
|
||||||
lineStyle: plotSeriesLineStyle(plotDef.lineStyle),
|
lineStyle: plotSeriesLineStyle(plotDef.lineStyle),
|
||||||
priceLineVisible: false,
|
priceLineVisible: false,
|
||||||
crosshairMarkerVisible: true,
|
...this._crosshairMarkerSeriesOptions(),
|
||||||
lastValueVisible: showPriceLabel,
|
lastValueVisible: showPriceLabel,
|
||||||
title: showSeriesTitle ? this.seriesTitleForPlot(config, plotDef) : '',
|
title: showSeriesTitle ? this.seriesTitleForPlot(config, plotDef) : '',
|
||||||
visible: isPlotVisible,
|
visible: isPlotVisible,
|
||||||
@@ -1100,6 +1111,7 @@ export class ChartManager {
|
|||||||
lastValueVisible: showPriceLabel,
|
lastValueVisible: showPriceLabel,
|
||||||
title: showSeriesTitle ? this.seriesTitleForPlot(config, plotDef) : '',
|
title: showSeriesTitle ? this.seriesTitleForPlot(config, plotDef) : '',
|
||||||
visible: isPlotVisible,
|
visible: isPlotVisible,
|
||||||
|
...this._crosshairMarkerSeriesOptions(),
|
||||||
...(indicatorPriceFormat ? { priceFormat: indicatorPriceFormat } : {}),
|
...(indicatorPriceFormat ? { priceFormat: indicatorPriceFormat } : {}),
|
||||||
}, pane);
|
}, pane);
|
||||||
series.setData(filtered.map(p => ({ time: p.time as Time, value: p.value })));
|
series.setData(filtered.map(p => ({ time: p.time as Time, value: p.value })));
|
||||||
@@ -1277,7 +1289,7 @@ export class ChartManager {
|
|||||||
lineWidth: Math.max(1, plotDef.lineWidth ?? regPlot?.lineWidth ?? 1) as 1 | 2 | 3 | 4,
|
lineWidth: Math.max(1, plotDef.lineWidth ?? regPlot?.lineWidth ?? 1) as 1 | 2 | 3 | 4,
|
||||||
lineStyle: plotSeriesLineStyle(plotDef.lineStyle),
|
lineStyle: plotSeriesLineStyle(plotDef.lineStyle),
|
||||||
priceLineVisible: false,
|
priceLineVisible: false,
|
||||||
crosshairMarkerVisible: true,
|
...this._crosshairMarkerSeriesOptions(),
|
||||||
lastValueVisible: showPriceLabel,
|
lastValueVisible: showPriceLabel,
|
||||||
title: showSeriesTitle ? this.seriesTitleForPlot(config, plotDef) : '',
|
title: showSeriesTitle ? this.seriesTitleForPlot(config, plotDef) : '',
|
||||||
visible: isPlotVisible,
|
visible: isPlotVisible,
|
||||||
@@ -2089,6 +2101,7 @@ export class ChartManager {
|
|||||||
lastValueVisible: showPriceLabel && lineVisible,
|
lastValueVisible: showPriceLabel && lineVisible,
|
||||||
title: showPriceLabel && lineVisible ? info.title : '',
|
title: showPriceLabel && lineVisible ? info.title : '',
|
||||||
visible: lineVisible,
|
visible: lineVisible,
|
||||||
|
...this._crosshairMarkerSeriesOptions(),
|
||||||
}, pane);
|
}, pane);
|
||||||
series.setData(seriesData);
|
series.setData(seriesData);
|
||||||
seriesList.push(series);
|
seriesList.push(series);
|
||||||
@@ -3544,6 +3557,57 @@ export class ChartManager {
|
|||||||
return this._indicatorAreaPriceLabelsEnabled;
|
return this._indicatorAreaPriceLabelsEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setCrosshairMarkersVisible(enabled: boolean): void {
|
||||||
|
if (this._crosshairMarkersVisible === enabled) return;
|
||||||
|
this._crosshairMarkersVisible = enabled;
|
||||||
|
this._refreshCrosshairMarkerStyles();
|
||||||
|
}
|
||||||
|
|
||||||
|
getCrosshairMarkersVisible(): boolean {
|
||||||
|
return this._crosshairMarkersVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _crosshairMarkerSeriesOptions(radius?: number): {
|
||||||
|
crosshairMarkerVisible: boolean;
|
||||||
|
crosshairMarkerRadius?: number;
|
||||||
|
} {
|
||||||
|
return {
|
||||||
|
crosshairMarkerVisible: this._crosshairMarkersVisible,
|
||||||
|
...(radius != null ? { crosshairMarkerRadius: radius } : {}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private _refreshCrosshairMarkerStyles(): void {
|
||||||
|
const visible = this._crosshairMarkersVisible;
|
||||||
|
if (this.mainSeries) {
|
||||||
|
try {
|
||||||
|
this.mainSeries.applyOptions({ crosshairMarkerVisible: visible } as Parameters<ISeriesApi<SeriesType>['applyOptions']>[0]);
|
||||||
|
} catch { /* ok */ }
|
||||||
|
}
|
||||||
|
for (const s of this.compareSeriesMap.values()) {
|
||||||
|
try {
|
||||||
|
s.applyOptions({ crosshairMarkerVisible: visible } as Parameters<ISeriesApi<SeriesType>['applyOptions']>[0]);
|
||||||
|
} catch { /* ok */ }
|
||||||
|
}
|
||||||
|
for (const entry of this.indicators.values()) {
|
||||||
|
entry.seriesList.forEach((series, i) => {
|
||||||
|
if (entry.seriesMeta[i]?.isHistogram) return;
|
||||||
|
const pid = entry.seriesMeta[i]?.plotId;
|
||||||
|
const radius = entry.type === 'OBV' && pid === 'plot0'
|
||||||
|
? 4
|
||||||
|
: entry.type === 'OBV' && pid === 'plot1'
|
||||||
|
? 3
|
||||||
|
: undefined;
|
||||||
|
try {
|
||||||
|
series.applyOptions({
|
||||||
|
crosshairMarkerVisible: visible,
|
||||||
|
...(radius != null ? { crosshairMarkerRadius: radius } : {}),
|
||||||
|
} as Parameters<ISeriesApi<SeriesType>['applyOptions']>[0]);
|
||||||
|
} catch { /* ok */ }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private _refreshPriceLabelStyles(): void {
|
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);
|
||||||
@@ -3649,7 +3713,7 @@ export class ChartManager {
|
|||||||
: Math.max(1, plot.lineWidth ?? regPlot?.lineWidth ?? 1)) as 1 | 2 | 3 | 4)
|
: Math.max(1, plot.lineWidth ?? regPlot?.lineWidth ?? 1)) as 1 | 2 | 3 | 4)
|
||||||
: Math.max(1, plot.lineWidth ?? regPlot?.lineWidth ?? 1);
|
: Math.max(1, plot.lineWidth ?? regPlot?.lineWidth ?? 1);
|
||||||
opts['lineStyle'] = plotSeriesLineStyle(plot.lineStyle);
|
opts['lineStyle'] = plotSeriesLineStyle(plot.lineStyle);
|
||||||
opts['crosshairMarkerVisible'] = true;
|
opts['crosshairMarkerVisible'] = this._crosshairMarkersVisible;
|
||||||
let plotAllows = true;
|
let plotAllows = true;
|
||||||
if (entry.type === 'IchimokuCloud') {
|
if (entry.type === 'IchimokuCloud') {
|
||||||
plotAllows = entry.seriesMeta[i]?.plotId !== 'plot2';
|
plotAllows = entry.seriesMeta[i]?.plotId !== 'plot2';
|
||||||
@@ -4980,7 +5044,10 @@ export class ChartManager {
|
|||||||
// ─── Compare overlay ──────────────────────────────────────────────────────
|
// ─── Compare overlay ──────────────────────────────────────────────────────
|
||||||
addCompareSeries(symbol: string, bars: OHLCVBar[], color: string): void {
|
addCompareSeries(symbol: string, bars: OHLCVBar[], color: string): void {
|
||||||
if (this.compareSeriesMap.has(symbol)) return;
|
if (this.compareSeriesMap.has(symbol)) return;
|
||||||
const s = this.chart.addSeries(LineSeries, { color, lineWidth: 2, priceLineVisible: false });
|
const s = this.chart.addSeries(LineSeries, {
|
||||||
|
color, lineWidth: 2, priceLineVisible: false,
|
||||||
|
...this._crosshairMarkerSeriesOptions(),
|
||||||
|
});
|
||||||
const base = bars[0]?.close ?? 1;
|
const base = bars[0]?.close ?? 1;
|
||||||
s.setData(bars.map(b => ({ time: b.time as Time, value: (b.close / base - 1) * 100 })));
|
s.setData(bars.map(b => ({ time: b.time as Time, value: (b.close / base - 1) * 100 })));
|
||||||
this.compareSeriesMap.set(symbol, s);
|
this.compareSeriesMap.set(symbol, s);
|
||||||
|
|||||||
@@ -526,6 +526,8 @@ export interface AppSettingsDto {
|
|||||||
chartLiveReceiveHighlight?: boolean;
|
chartLiveReceiveHighlight?: boolean;
|
||||||
/** 크로스헤어 이동 시 OHLC·보조지표 수치 표시 (기본 true) */
|
/** 크로스헤어 이동 시 OHLC·보조지표 수치 표시 (기본 true) */
|
||||||
chartCrosshairInfoVisible?: boolean;
|
chartCrosshairInfoVisible?: boolean;
|
||||||
|
/** 세로 크로스헤어와 지표 선 교차점 점 표시 (기본 true) */
|
||||||
|
chartCrosshairMarkersVisible?: boolean;
|
||||||
/** 차트 마우스오버 줌·이동 플로팅 툴바 표시 (기본 true) */
|
/** 차트 마우스오버 줌·이동 플로팅 툴바 표시 (기본 true) */
|
||||||
chartHoverToolbarVisible?: boolean;
|
chartHoverToolbarVisible?: boolean;
|
||||||
/** 차트 상단 범례(tv-legend) 항목별 표시 옵션 */
|
/** 차트 상단 범례(tv-legend) 항목별 표시 옵션 */
|
||||||
|
|||||||
Reference in New Issue
Block a user