커스텀

This commit is contained in:
Macbook
2026-06-17 02:01:05 +09:00
parent 89db874759
commit 7377507894
2 changed files with 104 additions and 3 deletions
@@ -449,8 +449,8 @@ const ChartRightToolbar: React.FC<ChartRightToolbarProps> = ({
? paneItems.find(p => p.id === openMenuId) ? paneItems.find(p => p.id === openMenuId)
: null; : null;
const showCustomBtn = !!(showOverlayIcons && !overlayControlsOnly && onCustomOverlayActiveChange && onOpenCustomOverlaySettings); const showCustomBtn = !!(showOverlayIcons && onCustomOverlayActiveChange && onOpenCustomOverlaySettings);
const showCustom1Btn = !!(showOverlayIcons && !overlayControlsOnly && onCustom1OverlayActiveChange && onOpenCustom1OverlaySettings); const showCustom1Btn = !!(showOverlayIcons && onCustom1OverlayActiveChange && onOpenCustomOverlaySettings);
const customMenuOpen = openMenuId === CUSTOM_MENU_ID; const customMenuOpen = openMenuId === CUSTOM_MENU_ID;
const custom1MenuOpen = openMenuId === CUSTOM1_MENU_ID; const custom1MenuOpen = openMenuId === CUSTOM1_MENU_ID;
@@ -1,6 +1,7 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import TradingChart from '../TradingChart'; import TradingChart from '../TradingChart';
import ChartCustomOverlaySettingsModal from '../ChartCustomOverlaySettingsModal';
import { MarketSearchPanel } from '../MarketSearchPanel'; import { MarketSearchPanel } from '../MarketSearchPanel';
import type { ChartType, IndicatorConfig, OHLCVBar, Theme, Timeframe } from '../../types'; import type { ChartType, IndicatorConfig, OHLCVBar, Theme, Timeframe } from '../../types';
import type { ChartManager } from '../../utils/ChartManager'; import type { ChartManager } from '../../utils/ChartManager';
@@ -22,6 +23,14 @@ import {
} from '../../utils/backtestWarmup'; } from '../../utils/backtestWarmup';
import type { EvalIndicatorParams } from '../../utils/strategyEvaluationParams'; import type { EvalIndicatorParams } from '../../utils/strategyEvaluationParams';
import { DISPLAY_COUNT } from '../../hooks/useUpbitData'; import { DISPLAY_COUNT } from '../../hooks/useUpbitData';
import {
createDefaultChartCustomOverlaySelection,
isCustomOverlaySlotActive,
resolveActiveCustomOverlaySlot,
syncChartManagerCustomOverlaysByActiveSlot,
type ChartCustomOverlaySelection,
type ChartCustomOverlaySlotId,
} from '../../utils/chartCustomOverlay';
import { import {
chartOverlayKeyForType, chartOverlayKeyForType,
DEFAULT_CHART_OVERLAY_VISIBILITY, DEFAULT_CHART_OVERLAY_VISIBILITY,
@@ -202,6 +211,73 @@ const StrategyEvaluationChart: React.FC<Props> = ({
}); });
}, []); }, []);
const [customOverlaySelection, setCustomOverlaySelection] = useState<ChartCustomOverlaySelection>(
() => createDefaultChartCustomOverlaySelection(),
);
const [custom1OverlaySelection, setCustom1OverlaySelection] = useState<ChartCustomOverlaySelection>(
() => createDefaultChartCustomOverlaySelection(),
);
const [activeCustomOverlaySlot, setActiveCustomOverlaySlot] = useState<ChartCustomOverlaySlotId | null>(null);
const [customOverlaySettingsSlot, setCustomOverlaySettingsSlot] = useState<ChartCustomOverlaySlotId | null>(null);
const customOverlayActive = isCustomOverlaySlotActive(activeCustomOverlaySlot, 'custom');
const custom1OverlayActive = isCustomOverlaySlotActive(activeCustomOverlaySlot, 'custom1');
const syncCustomOverlaysToManager = useCallback((mgr: ChartManager) => {
syncChartManagerCustomOverlaysByActiveSlot(
mgr,
activeCustomOverlaySlot,
customOverlaySelection,
custom1OverlaySelection,
);
}, [activeCustomOverlaySlot, customOverlaySelection, custom1OverlaySelection]);
const handleCustomOverlayActiveChange = useCallback((active: boolean) => {
const nextSlot = resolveActiveCustomOverlaySlot('custom', active, activeCustomOverlaySlot);
setActiveCustomOverlaySlot(nextSlot);
const mgr = managerRef.current;
if (mgr) {
syncChartManagerCustomOverlaysByActiveSlot(
mgr,
nextSlot,
customOverlaySelection,
custom1OverlaySelection,
);
}
}, [activeCustomOverlaySlot, customOverlaySelection, custom1OverlaySelection]);
const handleCustom1OverlayActiveChange = useCallback((active: boolean) => {
const nextSlot = resolveActiveCustomOverlaySlot('custom1', active, activeCustomOverlaySlot);
setActiveCustomOverlaySlot(nextSlot);
const mgr = managerRef.current;
if (mgr) {
syncChartManagerCustomOverlaysByActiveSlot(
mgr,
nextSlot,
customOverlaySelection,
custom1OverlaySelection,
);
}
}, [activeCustomOverlaySlot, customOverlaySelection, custom1OverlaySelection]);
const handleSaveCustomOverlaySelection = useCallback((
slot: ChartCustomOverlaySlotId,
selection: ChartCustomOverlaySelection,
) => {
const nextCustomSel = slot === 'custom' ? selection : customOverlaySelection;
const nextCustom1Sel = slot === 'custom1' ? selection : custom1OverlaySelection;
if (slot === 'custom') setCustomOverlaySelection(selection);
else setCustom1OverlaySelection(selection);
setCustomOverlaySettingsSlot(null);
const mgr = managerRef.current;
if (!mgr) return;
syncChartManagerCustomOverlaysByActiveSlot(
mgr,
activeCustomOverlaySlot,
nextCustomSel,
nextCustom1Sel,
);
}, [activeCustomOverlaySlot, customOverlaySelection, custom1OverlaySelection]);
const auxPaneCount = useMemo( const auxPaneCount = useMemo(
() => countNonOverlayIndicatorPanes(indicators, isOverlayType), () => countNonOverlayIndicatorPanes(indicators, isOverlayType),
[indicators], [indicators],
@@ -367,12 +443,13 @@ const StrategyEvaluationChart: React.FC<Props> = ({
managerRef.current = mgr; managerRef.current = mgr;
setChartMgr(mgr); setChartMgr(mgr);
mgr.setChartOverlayVisibility(overlayVisibilityRef.current); mgr.setChartOverlayVisibility(overlayVisibilityRef.current);
syncCustomOverlaysToManager(mgr);
if (paneAreaRatio && paneAreaRatio.aux > 0) { if (paneAreaRatio && paneAreaRatio.aux > 0) {
mgr.setPaneAreaRatio(paneAreaRatio.candle, paneAreaRatio.aux); mgr.setPaneAreaRatio(paneAreaRatio.candle, paneAreaRatio.aux);
} }
applyMarkers(); applyMarkers();
fitChartViewport(); fitChartViewport();
}, [paneAreaRatio, applyMarkers, fitChartViewport]); }, [paneAreaRatio, applyMarkers, fitChartViewport, syncCustomOverlaysToManager]);
const onCandlesReady = useCallback(() => { const onCandlesReady = useCallback(() => {
const mgr = managerRef.current; const mgr = managerRef.current;
@@ -625,6 +702,12 @@ const StrategyEvaluationChart: React.FC<Props> = ({
showCandleOverlayControls showCandleOverlayControls
candleOverlayToggles={candleOverlayToggles} candleOverlayToggles={candleOverlayToggles}
onToggleCandleOverlay={handleToggleCandleOverlay} onToggleCandleOverlay={handleToggleCandleOverlay}
customOverlayActive={customOverlayActive}
onCustomOverlayActiveChange={handleCustomOverlayActiveChange}
onOpenCustomOverlaySettings={() => setCustomOverlaySettingsSlot('custom')}
custom1OverlayActive={custom1OverlayActive}
onCustom1OverlayActiveChange={handleCustom1OverlayActiveChange}
onOpenCustom1OverlaySettings={() => setCustomOverlaySettingsSlot('custom1')}
magnifierEnabled={magnifierActive} magnifierEnabled={magnifierActive}
onMagnifierClose={() => setMagnifierActive(false)} onMagnifierClose={() => setMagnifierActive(false)}
resolveInitialDisplayCount={resolveInitialDisplayCount} resolveInitialDisplayCount={resolveInitialDisplayCount}
@@ -637,6 +720,24 @@ const StrategyEvaluationChart: React.FC<Props> = ({
onSelectedBarIndexChange={onSelectedBarIndexChange} onSelectedBarIndexChange={onSelectedBarIndexChange}
/> />
)} )}
{customOverlaySettingsSlot === 'custom' && (
<ChartCustomOverlaySettingsModal
title="Custom 표시 설정"
selection={customOverlaySelection}
onSave={sel => handleSaveCustomOverlaySelection('custom', sel)}
onCancel={() => setCustomOverlaySettingsSlot(null)}
smaParams={baseIndicators.find(i => i.type === 'SMA')?.params}
/>
)}
{customOverlaySettingsSlot === 'custom1' && (
<ChartCustomOverlaySettingsModal
title="Custom1 표시 설정"
selection={custom1OverlaySelection}
onSave={sel => handleSaveCustomOverlaySelection('custom1', sel)}
onCancel={() => setCustomOverlaySettingsSlot(null)}
smaParams={baseIndicators.find(i => i.type === 'SMA')?.params}
/>
)}
</> </>
)} )}
{!loading && !error && bars.length === 0 && ( {!loading && !error && bars.length === 0 && (