import React, { useState, useCallback, useRef, useEffect } from 'react';
import ReactDOM from 'react-dom';
import {
  Line,
  LineChart,
  XAxis,
  YAxis,
  CartesianGrid,
  Tooltip,
  Legend,
  ResponsiveContainer,
  BarChart,
  Bar,
  ReferenceLine,
  Cell,
  Scatter,
  ComposedChart,
  Customized,
  ReferenceArea,
  Area,
} from 'recharts';
import { MacdResult } from '../types/stock';
import { CandleData } from '../services/backtestApi';
import { Box, Typography, Paper, useMediaQuery, useTheme, IconButton, Tooltip as MuiTooltip, Chip, TextField, Dialog, DialogTitle, DialogContent, DialogActions, Button, Radio, RadioGroup, FormControlLabel, ToggleButton, ToggleButtonGroup, CircularProgress, Slider, Menu, MenuItem, Checkbox, ListItemText, ListItemIcon, Divider, Backdrop } from '@mui/material';
import { KeyboardArrowDown, DragIndicator } from '@mui/icons-material';
import { ZoomIn, ZoomOut, TrendingUp, TrendingDown, Fullscreen, FullscreenExit, OpenInNew, Close, Opacity, ChevronLeft, ChevronRight, Info, InfoOutlined, ZoomInMap, AccessTime, ShowChart, Timeline, BarChart as BarChartIcon, Settings as SettingsIcon, FactCheck, Psychology } from '@mui/icons-material';
import {
  DndContext,
  closestCenter,
  KeyboardSensor,
  PointerSensor,
  useSensor,
  useSensors,
  DragEndEvent,
} from '@dnd-kit/core';
import {
  arrayMove,
  SortableContext,
  sortableKeyboardCoordinates,
  useSortable,
  verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { format, parseISO } from 'date-fns';
import { useChartColors } from '../contexts/ChartColorContext';
import { useIndicatorVisibility } from '../contexts/IndicatorVisibilityContext';
import { useIndicatorSettings } from '../contexts/IndicatorSettingsContext';
import { useIndicatorPopup } from '../contexts/IndicatorPopupContext';
import { loadIndicatorSettings } from './settings/IndicatorSettingsTab';
import ChartToolbar, { ChartTool } from './ChartToolbar';
import IndicatorSettingsPopup, { IndicatorType } from './IndicatorSettingsPopup';
import MASettingsDialog from './MASettingsDialog';
import IchimokuSettingsDialog from './IchimokuSettingsDialog';
import IndicatorParamsSettingsDialog from './IndicatorParamsSettingsDialog';
import ChartSettingsDialog from './ChartSettingsDialog';
import { 
  isCrossUp, 
  isCrossDown, 
  isBullishTrend, 
  isBearishTrend,
  TradingSignal,
  SignalType,
} from '../utils/trendStochasticStrategy';
import { getStrategyRules, StrategyRuleDto, ConditionGroupDto, StrategyConditionDto } from '../services/strategyRuleApi';
import { upbitApiService } from '../services/upbitApi';

// 보조지표 차트 순서 타입 (15가지 보조지표)
type IndicatorChartType = 'volume' | 'newPsychological' | 'investPsychological' | 'macd' | 'cci' | 'adx' | 'bwi' | 'dmi' | 'obv' | 'rsi' | 'stochastic' | 'williamsR' | 'trix' | 'volumeOsc' | 'vr' | 'disparity';

// 보조지표 ID와 설정 키 매핑 (15가지)
const indicatorChartMapping: { id: IndicatorChartType; settingKeys: string[] }[] = [
  { id: 'volume', settingKeys: ['volume'] },
  { id: 'newPsychological', settingKeys: ['newPsychological'] },
  { id: 'investPsychological', settingKeys: ['investPsychological'] },
  { id: 'macd', settingKeys: ['macdLine', 'macdHistogram'] },
  { id: 'cci', settingKeys: ['cci'] },
  { id: 'adx', settingKeys: ['adx'] },
  { id: 'bwi', settingKeys: ['bwi'] },
  { id: 'dmi', settingKeys: ['plusDi', 'minusDi'] },
  { id: 'obv', settingKeys: ['obv'] },
  { id: 'rsi', settingKeys: ['rsi'] },
  { id: 'stochastic', settingKeys: ['stochasticK', 'stochasticD', 'stochasticSlow'] },
  { id: 'williamsR', settingKeys: ['williamsR'] },
  { id: 'trix', settingKeys: ['trix', 'trixSignal'] },
  { id: 'volumeOsc', settingKeys: ['volumeOsc'] },
  { id: 'vr', settingKeys: ['vr'] },
  { id: 'disparity', settingKeys: ['disparity'] },
];

// SortableIndicatorChart 컴포넌트 (카드박스 스타일 + 리사이즈 핸들)
interface SortableIndicatorChartProps {
  id: string;
  children: React.ReactNode;
  isActive: boolean;
  indicatorHeight: number;
  onResize?: (newHeight: number) => void;
  isMaximized?: boolean;
  onMaximize?: () => void;
  onRestore?: () => void;
}

const SortableIndicatorChart: React.FC<SortableIndicatorChartProps> = ({ 
  id, 
  children, 
  isActive, 
  indicatorHeight, 
  onResize, 
  isMaximized, 
  onMaximize, 
  onRestore 
}) => {
  const {
    attributes,
    listeners,
    setNodeRef,
    transform,
    transition,
    isDragging,
  } = useSortable({ id, disabled: !isActive });

  const [isResizing, setIsResizing] = React.useState(false);
  const [startY, setStartY] = React.useState(0);
  const [startHeight, setStartHeight] = React.useState(indicatorHeight);

  const style = {
    transform: CSS.Transform.toString(transform),
    transition: isResizing ? 'none' : transition,
    opacity: isDragging ? 0.5 : 1,
  };

  // 리사이즈 시작
  const handleResizeStart = (e: React.MouseEvent) => {
    e.preventDefault();
    e.stopPropagation();
    setIsResizing(true);
    setStartY(e.clientY);
    setStartHeight(indicatorHeight);
  };

  // 최대화/복원 버튼 클릭 핸들러
  const handleMaximizeClick = (e: React.MouseEvent) => {
    e.preventDefault();
    e.stopPropagation();
    if (isMaximized && onRestore) {
      onRestore();
    } else if (!isMaximized && onMaximize) {
      onMaximize();
    }
  };

  // 리사이즈 중
  React.useEffect(() => {
    if (!isResizing) return;

    const handleMouseMove = (e: MouseEvent) => {
      const deltaY = e.clientY - startY;
      const newHeight = Math.max(30, Math.min(200, startHeight + deltaY));
      if (onResize) {
        onResize(newHeight);
      }
    };

    const handleMouseUp = () => {
      setIsResizing(false);
    };

    document.addEventListener('mousemove', handleMouseMove);
    document.addEventListener('mouseup', handleMouseUp);

    return () => {
      document.removeEventListener('mousemove', handleMouseMove);
      document.removeEventListener('mouseup', handleMouseUp);
    };
  }, [isResizing, startY, startHeight, onResize]);

  if (!isActive) return null;

  return (
    <Box
      ref={setNodeRef}
      style={style}
      sx={{ 
        position: 'relative',
        mb: 1,
        mx: 0,  // ✅ 좌우 마진 제거 (0.5 → 0)
        p: 0,   // ✅ 패딩 제거
        borderRadius: 1,
        bgcolor: 'background.paper',
        border: '1px solid',
        borderColor: 'divider',
        overflow: 'hidden',
        boxShadow: isDragging ? 4 : 1,
        ...(isMaximized && {
          position: 'fixed',
          top: '50%',
          left: '50%',
          transform: 'translate(-50%, -50%) !important',
          width: '90vw',
          height: '80vh',
          zIndex: 1300,
          mb: 0,
        }),
      }}
    >
      {/* 우측 컨트롤 버튼 그룹 */}
      <Box
        sx={{
          position: 'absolute',
          top: '50%',
          right: 4,
          transform: 'translateY(-50%)',
          zIndex: 20,
          display: 'flex',
          flexDirection: 'column',
          gap: 0.5,
          opacity: 0.4,
          '&:hover': {
            opacity: 1,
          },
          transition: 'opacity 0.2s',
        }}
      >
        {/* 최대화/복원 버튼 */}
        <IconButton
          size="small"
          onClick={handleMaximizeClick}
          sx={{
            p: 0.5,
            bgcolor: 'action.hover',
            borderRadius: 0.5,
            '&:hover': {
              bgcolor: 'action.selected',
            },
          }}
        >
          {isMaximized ? (
            <FullscreenExit sx={{ fontSize: 18, color: '#8b95a5' }} />
          ) : (
            <Fullscreen sx={{ fontSize: 18, color: '#8b95a5' }} />
          )}
        </IconButton>
        
        {/* 드래그 핸들 */}
        <Box
          {...attributes}
          {...listeners}
          sx={{
            cursor: 'grab',
            '&:active': { cursor: 'grabbing' },
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            p: 0.5,
            borderRadius: 0.5,
            bgcolor: 'action.hover',
          }}
        >
          <DragIndicator sx={{ fontSize: 18, color: '#8b95a5' }} />
        </Box>
      </Box>
      
      {children}
      
      {/* 리사이즈 핸들 - 하단 (최대화 상태에서는 숨김) */}
      {!isMaximized && (
        <Box
          onMouseDown={handleResizeStart}
          sx={{
            position: 'absolute',
            bottom: 0,
            left: 0,
            right: 0,
            height: 6,
            cursor: 'ns-resize',
            bgcolor: isResizing ? 'primary.main' : 'transparent',
            '&:hover': {
              bgcolor: 'primary.light',
            },
            transition: 'background-color 0.2s',
            zIndex: 15,
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
          }}
        >
          <Box sx={{ 
            width: 40, 
            height: 3, 
            bgcolor: isResizing ? 'primary.contrastText' : '#8b95a5', 
            borderRadius: 1,
            opacity: 0.6,
          }} />
        </Box>
      )}
    </Box>
  );
};

// 기본 투자 전략 설정 키
const DEFAULT_STRATEGY_KEY = 'defaultTradingStrategy';

// 기본 전략 설정 로드
const loadDefaultStrategy = (): string => {
  try {
    const saved = localStorage.getItem(DEFAULT_STRATEGY_KEY);
    return saved || 'TREND_STOCHASTIC';
  } catch {
    return 'TREND_STOCHASTIC';
  }
};

// 커스텀 YAxis Tick 컴포넌트 - 최상단 tick 숨김
const CustomYAxisTick = (props: any) => {
  const { x, y, payload, tickFormatter } = props;
  
  // y 좌표가 차트 영역 상단 근처(15px 이하)면 숨김 (최상단 tick)
  if (y !== undefined && y < 15) return null;
  
  const formattedValue = typeof tickFormatter === 'function' 
    ? tickFormatter(payload.value) 
    : payload.value;
  
  return (
    <g transform={`translate(${x},${y})`}>
      <text x={0} y={0} dy={4} textAnchor="end" fontSize={12} fill="#8b95a5">
        {formattedValue}
      </text>
    </g>
  );
};

// IndicatorChartsSection 컴포넌트 props
interface IndicatorChartsSectionProps {
  settings: any;
  order: any[];
  updateOrder: (newOrder: any[]) => void;
  updateSettings: (settings: any) => void;
  isMobile: boolean;
  chartData: any[];
  colors: any;
  indicatorSettings: any;
  showGrid: boolean;
  shouldShowXAxis: (name: string) => boolean;
  INDICATOR_HEIGHT: number;
  INDICATOR_XAXIS_EXTRA: number;
  CHART_MARGIN: any;
  CHART_MARGIN_WITH_XAXIS: any;
  getXAxisInterval: () => number;
  formatXAxisLabel: (value: string) => string;
  formatCompactNumber: (value: number) => string;
  getMacdYAxisDomain: () => [number, number];
  handleChartMouseMove: (state: any, chartType?: string) => void;
  handleChartMouseLeave: () => void;
  handleChartClick: (state: any, chartType?: string) => void;
  handleChartAreaClick: (chartType: string) => void;
  chartMode: string;
  activeTool: string | null;
  VerticalCrosshair: React.FC<any>;
  globalHoverDate: string | null;
  globalHoverYRatio: number | null;
  externalHoverMatchDate: string | null; // 외부 호버 시간 매칭 결과 (동기적)
  handleIndicatorMouseMove?: (e: React.MouseEvent<HTMLDivElement>) => void;
  handleIndicatorMouseLeave?: () => void;
  handleIndicatorTouchStart?: (e: React.TouchEvent<HTMLDivElement>) => void;
  handleIndicatorTouchMove?: (e: React.TouchEvent<HTMLDivElement>) => void;
  handleIndicatorTouchEnd?: () => void;
}

// 보조지표 차트 섹션 컴포넌트 (드래그 앤 드롭 지원)
const IndicatorChartsSection: React.FC<IndicatorChartsSectionProps> = ({
  settings,
  order,
  updateOrder,
  updateSettings,
  isMobile,
  chartData,
  colors,
  indicatorSettings,
  showGrid,
  shouldShowXAxis,
  INDICATOR_HEIGHT,
  INDICATOR_XAXIS_EXTRA,
  CHART_MARGIN,
  CHART_MARGIN_WITH_XAXIS,
  getXAxisInterval,
  formatXAxisLabel,
  formatCompactNumber,
  getMacdYAxisDomain,
  handleChartMouseMove,
  handleChartMouseLeave,
  handleChartClick,
  handleChartAreaClick,
  chartMode,
  activeTool,
  VerticalCrosshair,
  globalHoverDate,
  globalHoverYRatio,
  externalHoverMatchDate,
  handleIndicatorMouseMove,
  handleIndicatorMouseLeave,
  handleIndicatorTouchStart,
  handleIndicatorTouchMove,
  handleIndicatorTouchEnd,
}) => {
  const theme = useTheme();
  
  // 보조지표 타이틀 공통 스타일 (화이트 모드 시인성 개선)
  const indicatorTitleStyle = {
    position: 'absolute' as const,
    top: 2,
    left: 5,
    zIndex: 10,
    fontSize: '9px',
    color: 'text.secondary',
    fontWeight: 'bold',
    bgcolor: theme.palette.mode === 'dark' ? 'background.paper' : 'rgba(245, 247, 250, 0.98)',
    px: 0.5,
    py: 0.3,
    borderRadius: 0.5,
    border: theme.palette.mode === 'dark' ? 'none' : '1px solid rgba(0, 0, 0, 0.1)',
    boxShadow: theme.palette.mode === 'dark' ? 'none' : '0 1px 3px rgba(0, 0, 0, 0.08)',
  };
  
  // 사용자 정의 기준선 가져오기
  const { getCustomReferenceLines } = useIndicatorSettings();
  
  // 디버그: IndicatorChartsSection 렌더링 확인
  console.log('📊 IndicatorChartsSection 렌더링됨, chartData.length:', chartData?.length);
  
  // 크로스헤어 key에 사용할 날짜 (외부 호버 매칭 결과 우선)
  const crosshairKeyDate = externalHoverMatchDate || globalHoverDate || 'none';
  
  // onMouseMove 래퍼 (디버깅용)
  const handleChartMouseMoveWithLog = (state: any, chartType: string) => {
    console.log(`📍 IndicatorChart onMouseMove: chartType=${chartType}, hasActivePayload=${!!state?.activePayload}`);
    handleChartMouseMove(state, chartType);
  };
  
  // 차트 타입과 설정 매핑 (15가지 보조지표)
  const chartTypeToSettingCheck = (chartType: IndicatorChartType): boolean => {
    switch (chartType) {
      case 'volume': return settings.volume;
      case 'newPsychological': return settings.newPsychological;
      case 'investPsychological': return settings.investPsychological;
      case 'macd': return settings.macdLine || settings.macdHistogram;
      case 'cci': return settings.cci;
      case 'adx': return settings.adx;
      case 'bwi': return settings.bwi;
      case 'dmi': return settings.plusDi || settings.minusDi;
      case 'obv': return settings.obv;
      case 'rsi': return settings.rsi;
      case 'stochastic': return settings.stochasticK || settings.stochasticD || settings.stochasticSlow;
      case 'williamsR': return settings.williamsR;
      case 'trix': return settings.trix || settings.trixSignal;
      case 'volumeOsc': return settings.volumeOsc;
      case 'vr': return settings.vr;
      case 'disparity': return settings.disparity;
      default: return false;
    }
  };

  // order에서 차트 ID 매핑 (settingKey -> chartType) (15가지 보조지표)
  const getChartTypeFromOrderItem = (orderItem: any): IndicatorChartType | null => {
    const key = orderItem.settingKey;
    if (key === 'volume') return 'volume';
    if (key === 'newPsychological') return 'newPsychological';
    if (key === 'investPsychological') return 'investPsychological';
    if (key === 'macdLine' || key === 'macdHistogram') return 'macd';
    if (key === 'cci') return 'cci';
    if (key === 'adx') return 'adx';
    if (key === 'bwi') return 'bwi';
    if (key === 'plusDi' || key === 'minusDi') return 'dmi';
    if (key === 'obv') return 'obv';
    if (key === 'rsi') return 'rsi';
    if (key === 'stochasticK' || key === 'stochasticD' || key === 'stochasticSlow') return 'stochastic';
    if (key === 'williamsR') return 'williamsR';
    if (key === 'trix' || key === 'trixSignal') return 'trix';
    if (key === 'volumeOsc') return 'volumeOsc';
    if (key === 'vr') return 'vr';
    if (key === 'disparity') return 'disparity';
    return null;
  };

  // 활성화된 차트만 필터링하고 중복 제거
  const getActiveCharts = (): IndicatorChartType[] => {
    const seen = new Set<IndicatorChartType>();
    const result: IndicatorChartType[] = [];
    
    for (const orderItem of order) {
      const chartType = getChartTypeFromOrderItem(orderItem);
      if (chartType && !seen.has(chartType) && chartTypeToSettingCheck(chartType)) {
        seen.add(chartType);
        result.push(chartType);
      }
    }
    
    return result;
  };

  const activeCharts = getActiveCharts();

  const sensors = useSensors(
    useSensor(PointerSensor, {
      activationConstraint: {
        distance: 8,
      },
    }),
    useSensor(KeyboardSensor, {
      coordinateGetter: sortableKeyboardCoordinates,
    })
  );

  const handleDragEnd = (event: DragEndEvent) => {
    const { active, over } = event;

    if (over && active.id !== over.id) {
      const activeChartType = active.id as IndicatorChartType;
      const overChartType = over.id as IndicatorChartType;

      // order 배열에서 해당 차트들의 인덱스 찾기
      const activeOrderItems = order.filter(item => getChartTypeFromOrderItem(item) === activeChartType);
      const overOrderItems = order.filter(item => getChartTypeFromOrderItem(item) === overChartType);

      if (activeOrderItems.length > 0 && overOrderItems.length > 0) {
        const oldIndex = order.findIndex(item => item.id === activeOrderItems[0].id);
        const newIndex = order.findIndex(item => item.id === overOrderItems[0].id);

        if (oldIndex !== -1 && newIndex !== -1) {
          const newOrder = arrayMove([...order], oldIndex, newIndex);
          updateOrder(newOrder);
        }
      }
    }
  };

  // X축 표시 여부 결정 (마지막 활성 차트에만 표시)
  const shouldShowXAxisForChart = (chartType: IndicatorChartType): boolean => {
    return true; // 모든 차트에 X축 표시
  };

  // 보조지표 설정 팝업 상태
  const [settingsPopupOpen, setSettingsPopupOpen] = React.useState(false);
  const [selectedIndicator, setSelectedIndicator] = React.useState<IndicatorType | null>(null);

  // 최대화 상태 관리
  const [maximizedChart, setMaximizedChart] = React.useState<IndicatorChartType | null>(null);

  // 타이틀 클릭 핸들러
  const handleTitleClick = (indicatorType: IndicatorType) => (e: React.MouseEvent) => {
    e.stopPropagation();
    setSelectedIndicator(indicatorType);
    setSettingsPopupOpen(true);
  };

  // 최대화 핸들러
  const handleMaximize = (chartType: IndicatorChartType) => {
    setMaximizedChart(chartType);
  };

  // 복원 핸들러
  const handleRestore = () => {
    setMaximizedChart(null);
  };

  // 최대화 상태일 때 표시할 차트 결정
  const displayCharts = maximizedChart ? [maximizedChart] : activeCharts;

  if (activeCharts.length === 0) return null;

  return (
    <>
      {/* 최대화 시 백드롭 */}
      <Backdrop
        open={!!maximizedChart}
        onClick={handleRestore}
        sx={{ 
          zIndex: 1299,
          bgcolor: 'rgba(0, 0, 0, 0.7)',
        }}
      />
      
      <Box sx={{ order: 100 }}>
        <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
          <SortableContext items={activeCharts} strategy={verticalListSortingStrategy}>
          {displayCharts.map((chartType) => {
            // 최대화 시 차트 높이 계산 (뷰포트 높이의 80%에서 패딩과 여백을 뺀 값)
            const maximizedHeight = maximizedChart === chartType 
              ? Math.floor(window.innerHeight * 0.8) - 60 
              : INDICATOR_HEIGHT;
            
            // 차트 컨테이너 높이 (X축 여백 포함)
            const chartContainerHeight = maximizedHeight + INDICATOR_XAXIS_EXTRA;
            
            return (
              <SortableIndicatorChart 
                key={chartType} 
                id={chartType} 
                isActive={true}
                indicatorHeight={maximizedHeight}
                onResize={(newHeight) => updateSettings({ indicatorHeight: newHeight })}
                isMaximized={maximizedChart === chartType}
                onMaximize={() => handleMaximize(chartType)}
                onRestore={handleRestore}
              >
              {/* 거래량 차트 */}
              {chartType === 'volume' && (
                <Box 
                  onMouseMove={handleIndicatorMouseMove}
                  onMouseLeave={handleIndicatorMouseLeave}
                  onTouchStart={handleIndicatorTouchStart}
                  onTouchMove={handleIndicatorTouchMove}
                  onTouchEnd={handleIndicatorTouchEnd}
                  sx={{ 
                    height: `${chartContainerHeight}px`, 
                    position: 'relative',
                    mt: 0,
                    cursor: 'pointer',
                    touchAction: 'none',
                  }}>
                  <Typography 
                    variant="caption" 
                    onClick={handleTitleClick('volume')}
                    sx={{ 
                      ...indicatorTitleStyle,
                      cursor: 'pointer',
                      '&:hover': { color: 'primary.main', textDecoration: 'underline' }
                    }}
                  >
                    거래량
                  </Typography>
                  <ResponsiveContainer width="100%" height="100%">
                    <BarChart data={chartData} syncId="upbitChart" margin={CHART_MARGIN} onMouseMove={(state) => handleChartMouseMoveWithLog(state, 'volume')} onMouseLeave={handleChartMouseLeave} onClick={(state) => handleChartClick(state, 'volume')}>
                      {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                      <XAxis dataKey="date" hide={false} tick={{ fontSize: 9, fill: '#8b95a5' }} interval={getXAxisInterval()} tickFormatter={formatXAxisLabel} axisLine={{ stroke: '#252d3a' }} tickLine={{ stroke: '#252d3a' }} padding={{ left: 0, right: 0 }} />
                      <YAxis orientation="left" tick={<CustomYAxisTick />} width={60} tickFormatter={formatCompactNumber} stroke="#252d3a" />
                      {settings.tooltipEnabled && <Tooltip formatter={(value: any) => [formatCompactNumber(value), '거래량']} />}
                      {(chartMode === 'basic' || activeTool === 'crosshair') && (
                        <Customized key={`volume-crosshair-${crosshairKeyDate}-${globalHoverYRatio ?? 'none'}`} component={(props: any) => <VerticalCrosshair {...props} chartData={chartData} globalHoverDate={globalHoverDate} globalHoverYRatio={globalHoverYRatio} showHorizontal={true} />} />
                      )}
                      <Bar dataKey="volume" radius={[1, 1, 0, 0]} maxBarSize={8}>
                        {chartData.map((entry, index) => (
                          <Cell key={`vol-${index}`} fill={entry.volumeColor || '#42a5f5'} opacity={0.7} />
                        ))}
                      </Bar>
                    </BarChart>
                  </ResponsiveContainer>
                </Box>
              )}

              {/* MACD 차트 */}
              {chartType === 'macd' && (
                <Box 
                  onMouseMove={handleIndicatorMouseMove}
                  onMouseLeave={handleIndicatorMouseLeave}
                  onTouchStart={handleIndicatorTouchStart}
                  onTouchMove={handleIndicatorTouchMove}
                  onTouchEnd={handleIndicatorTouchEnd}
                  sx={{ 
                    height: `${chartContainerHeight}px`, 
                    position: 'relative',
                    mt: 0,
                    touchAction: 'none',
                    cursor: 'pointer',
                  }}>
                  <Typography 
                    variant="caption" 
                    onClick={handleTitleClick('macd')}
                    sx={{ 
                      ...indicatorTitleStyle,
                      cursor: 'pointer',
                      '&:hover': { color: 'primary.main', textDecoration: 'underline' }
                    }}
                  >
                    MACD ({indicatorSettings.macdFastPeriod},{indicatorSettings.macdSlowPeriod},{indicatorSettings.macdSignalPeriod})
                  </Typography>
                  <ResponsiveContainer width="100%" height="100%">
                    <ComposedChart data={chartData} syncId="upbitChart" margin={CHART_MARGIN} onMouseMove={(state) => handleChartMouseMoveWithLog(state, 'macd')} onMouseLeave={handleChartMouseLeave} onClick={(state) => handleChartClick(state, 'macd')}>
                      {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                      <XAxis dataKey="date" hide={false} tick={{ fontSize: 9, fill: '#8b95a5' }} interval={getXAxisInterval()} tickFormatter={formatXAxisLabel} axisLine={{ stroke: '#252d3a' }} tickLine={{ stroke: '#252d3a' }} padding={{ left: 0, right: 0 }} />
                      <YAxis orientation="left" tick={<CustomYAxisTick />} width={60} domain={getMacdYAxisDomain()} tickFormatter={formatCompactNumber} stroke="#252d3a" />
                      {settings.tooltipEnabled && <Tooltip formatter={(value: any, name: string) => [value?.toFixed(2) ?? '-', name]} />}
                      {indicatorSettings.macdZeroLineEnabled && <ReferenceLine y={0} stroke={colors.macdZeroLineColor || "#666"} strokeDasharray="2 2" strokeWidth={colors.macdZeroLineWidth || 0.5} />}
                      {/* 사용자 정의 기준선 */}
                      {getCustomReferenceLines('macd').map(line => (
                        <ReferenceLine 
                          key={line.id} 
                          y={line.value} 
                          stroke={line.color} 
                          strokeDasharray={line.lineStyle === 'solid' ? '0' : line.lineStyle === 'dashed' ? '5 5' : '2 2'} 
                          strokeWidth={1} 
                          label={{ value: `${line.name} (${line.value})`, position: 'right', fontSize: 9, fill: line.color }}
                        />
                      ))}
                      {(chartMode === 'basic' || activeTool === 'crosshair') && (
                        <Customized key={`macd-crosshair-${crosshairKeyDate}-${globalHoverYRatio ?? 'none'}`} component={(props: any) => <VerticalCrosshair {...props} chartData={chartData} globalHoverDate={globalHoverDate} globalHoverYRatio={globalHoverYRatio} showHorizontal={true} />} />
                      )}
                      {settings.macdHistogram && (
                        <Bar dataKey="macdHistogram" name="Histogram" radius={[1, 1, 0, 0]}>
                          {chartData.map((entry, index) => (
                            <Cell key={`hist-${index}`} fill={entry.histogramColor} />
                          ))}
                        </Bar>
                      )}
                      {settings.macdLine && (
                        <>
                          <Line type="monotone" dataKey="macdLine" stroke={colors.macdLine} strokeWidth={colors.macdLineWidth || 1.5} dot={false} name="MACD" connectNulls isAnimationActive={false} />
                          <Line type="monotone" dataKey="signalLine" stroke={colors.signalLine} strokeWidth={colors.signalLineWidth || 1.5} dot={false} name="Signal" connectNulls isAnimationActive={false} />
                        </>
                      )}
                    </ComposedChart>
                  </ResponsiveContainer>
                </Box>
              )}

              {/* Stochastic 차트 */}
              {chartType === 'stochastic' && (
                <Box 
                  onMouseMove={handleIndicatorMouseMove}
                  onMouseLeave={handleIndicatorMouseLeave}
                  onTouchStart={handleIndicatorTouchStart}
                  onTouchMove={handleIndicatorTouchMove}
                  onTouchEnd={handleIndicatorTouchEnd}
                  sx={{ 
                    height: `${chartContainerHeight}px`, 
                    position: 'relative',
                    mt: 0,
                    touchAction: 'none',
                    cursor: 'pointer',
                  }}>
                  <Typography 
                    variant="caption" 
                    onClick={handleTitleClick('stochastic')}
                    sx={{ 
                      ...indicatorTitleStyle,
                      cursor: 'pointer',
                      '&:hover': { color: 'primary.main', textDecoration: 'underline' }
                    }}
                  >
                    STOCHASTIC ({indicatorSettings.stochasticKPeriod},{indicatorSettings.stochasticDPeriod},{indicatorSettings.stochasticSlowing})
                  </Typography>
                  <ResponsiveContainer width="100%" height="100%">
                    <LineChart data={chartData} syncId="upbitChart" margin={CHART_MARGIN} onMouseMove={(state) => handleChartMouseMoveWithLog(state, 'stochastic')} onMouseLeave={handleChartMouseLeave} onClick={(state) => handleChartClick(state, 'stochastic')}>
                      {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                      <XAxis dataKey="date" hide={false} tick={{ fontSize: 9, fill: '#8b95a5' }} interval={getXAxisInterval()} tickFormatter={formatXAxisLabel} axisLine={{ stroke: '#252d3a' }} tickLine={{ stroke: '#252d3a' }} padding={{ left: 0, right: 0 }} />
                      <YAxis orientation="left" tick={<CustomYAxisTick />} width={60} domain={[0, 100]} stroke="#252d3a" />
                      {settings.tooltipEnabled && <Tooltip formatter={(value: any, name: string) => [value?.toFixed(2) ?? '-', name]} />}
                      <ReferenceLine y={indicatorSettings.stochasticOverbought} stroke={colors.stochasticOverboughtColor || "#ef5350"} strokeWidth={0.5} label={{ value: String(indicatorSettings.stochasticOverbought), position: 'right', fontSize: 9, fill: colors.stochasticOverboughtColor || '#ef5350' }} />
                      <ReferenceLine y={indicatorSettings.stochasticOversold} stroke={colors.stochasticOversoldColor || "#26a69a"} strokeWidth={0.5} label={{ value: String(indicatorSettings.stochasticOversold), position: 'right', fontSize: 9, fill: colors.stochasticOversoldColor || '#26a69a' }} />
                      {/* 사용자 정의 기준선 */}
                      {getCustomReferenceLines('stochastic').map(line => (
                        <ReferenceLine 
                          key={line.id} 
                          y={line.value} 
                          stroke={line.color} 
                          strokeDasharray={line.lineStyle === 'solid' ? '0' : line.lineStyle === 'dashed' ? '5 5' : '2 2'} 
                          strokeWidth={1} 
                          label={{ value: `${line.name} (${line.value})`, position: 'right', fontSize: 9, fill: line.color }}
                        />
                      ))}
                      {(chartMode === 'basic' || activeTool === 'crosshair') && (
                        <Customized key={`crosshair-${crosshairKeyDate}-${globalHoverYRatio ?? 'none'}`} component={(props: any) => <VerticalCrosshair {...props} chartData={chartData} globalHoverDate={globalHoverDate} globalHoverYRatio={globalHoverYRatio} showHorizontal={true} />} />
                      )}
                      <Line type="monotone" dataKey="stochasticK" stroke={colors.stochasticK} strokeWidth={colors.stochasticKWidth || 1.5} dot={false} name="%K" connectNulls isAnimationActive={false} />
                      <Line type="monotone" dataKey="stochasticD" stroke={colors.stochasticD} strokeWidth={colors.stochasticDWidth || 1.5} dot={false} name="%D" connectNulls isAnimationActive={false} />
                    </LineChart>
                  </ResponsiveContainer>
                </Box>
              )}

              {/* RSI 차트 */}
              {chartType === 'rsi' && (
                <Box 
                  onMouseMove={handleIndicatorMouseMove}
                  onMouseLeave={handleIndicatorMouseLeave}
                  onTouchStart={handleIndicatorTouchStart}
                  onTouchMove={handleIndicatorTouchMove}
                  onTouchEnd={handleIndicatorTouchEnd}
                  sx={{ 
                    height: `${chartContainerHeight}px`, 
                    position: 'relative',
                    mt: 0,
                    touchAction: 'none',
                    cursor: 'pointer',
                  }}>
                  <Typography 
                    variant="caption" 
                    onClick={handleTitleClick('rsi')}
                    sx={{ 
                      ...indicatorTitleStyle,
                      cursor: 'pointer',
                      '&:hover': { color: 'primary.main', textDecoration: 'underline' }
                    }}
                  >
                    RSI ({indicatorSettings.rsiPeriod}, {indicatorSettings.rsiSignalPeriod})
                  </Typography>
                  <ResponsiveContainer width="100%" height="100%">
                    <LineChart data={chartData} syncId="upbitChart" margin={CHART_MARGIN} onMouseMove={(state) => handleChartMouseMoveWithLog(state, 'rsi')} onMouseLeave={handleChartMouseLeave} onClick={(state) => handleChartClick(state, 'rsi')}>
                      {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                      <XAxis dataKey="date" hide={false} tick={{ fontSize: 9, fill: '#8b95a5' }} interval={getXAxisInterval()} tickFormatter={formatXAxisLabel} axisLine={{ stroke: '#252d3a' }} tickLine={{ stroke: '#252d3a' }} padding={{ left: 0, right: 0 }} />
                      <YAxis orientation="left" tick={<CustomYAxisTick />} width={60} domain={[0, 100]} ticks={[indicatorSettings.rsiOversold, 50, indicatorSettings.rsiOverbought]} stroke="#252d3a" />
                      {settings.tooltipEnabled && <Tooltip formatter={(value: any, name: string) => [value?.toFixed(2) ?? '-', name === 'rsiSignal' ? 'Signal' : name]} />}
                      {indicatorSettings.rsiOverboughtEnabled && <ReferenceLine y={indicatorSettings.rsiOverbought} stroke={colors.rsiOverboughtColor || "#ef5350"} strokeWidth={colors.rsiOverboughtWidth || 0.5} label={{ value: indicatorSettings.rsiOverbought, position: 'right', fontSize: 9, fill: colors.rsiOverboughtColor || '#ef5350' }} />}
                      {indicatorSettings.rsiMiddleEnabled && <ReferenceLine y={indicatorSettings.rsiMiddle} stroke={colors.rsiMiddleColor || "#666"} strokeDasharray="2 2" strokeWidth={colors.rsiMiddleWidth || 0.5} label={{ value: indicatorSettings.rsiMiddle, position: 'right', fontSize: 9, fill: colors.rsiMiddleColor || '#666' }} />}
                      {indicatorSettings.rsiOversoldEnabled && <ReferenceLine y={indicatorSettings.rsiOversold} stroke={colors.rsiOversoldColor || "#26a69a"} strokeWidth={colors.rsiOversoldWidth || 0.5} label={{ value: indicatorSettings.rsiOversold, position: 'right', fontSize: 9, fill: colors.rsiOversoldColor || '#26a69a' }} />}
                      {/* 사용자 정의 기준선 */}
                      {getCustomReferenceLines('rsi').map(line => (
                        <ReferenceLine 
                          key={line.id} 
                          y={line.value} 
                          stroke={line.color} 
                          strokeDasharray={line.lineStyle === 'solid' ? '0' : line.lineStyle === 'dashed' ? '5 5' : '2 2'} 
                          strokeWidth={1} 
                          label={{ value: `${line.name} (${line.value})`, position: 'right', fontSize: 9, fill: line.color }}
                        />
                      ))}
                      {(chartMode === 'basic' || activeTool === 'crosshair') && (
                        <Customized key={`crosshair-${crosshairKeyDate}-${globalHoverYRatio ?? 'none'}`} component={(props: any) => <VerticalCrosshair {...props} chartData={chartData} globalHoverDate={globalHoverDate} globalHoverYRatio={globalHoverYRatio} showHorizontal={true} />} />
                      )}
                      {/* RSI 라인 */}
                      {indicatorSettings.rsiEnabled && (
                        <Line type="monotone" dataKey="rsi" stroke={colors.rsiColor || '#AB47BC'} strokeWidth={colors.rsiWidth || 1.5} dot={false} name="RSI" connectNulls isAnimationActive={false} />
                      )}
                      {/* RSI Signal 라인 */}
                      {indicatorSettings.rsiSignalEnabled && (
                        <Line type="monotone" dataKey="rsiSignal" stroke={colors.rsiSignalColor || '#FF7043'} strokeWidth={colors.rsiSignalWidth || 1} dot={false} name="rsiSignal" connectNulls isAnimationActive={false}  />
                      )}
                    </LineChart>
                  </ResponsiveContainer>
                </Box>
              )}

              {/* ADX 차트 */}
              {chartType === 'adx' && (
                <Box 
                  onMouseMove={handleIndicatorMouseMove}
                  onMouseLeave={handleIndicatorMouseLeave}
                  onTouchStart={handleIndicatorTouchStart}
                  onTouchMove={handleIndicatorTouchMove}
                  onTouchEnd={handleIndicatorTouchEnd}
                  sx={{ 
                    height: `${chartContainerHeight}px`, 
                    position: 'relative',
                    mt: 0,
                    touchAction: 'none',
                    cursor: 'pointer',
                  }}>
                  <Typography 
                    variant="caption" 
                    onClick={handleTitleClick('adx')}
                    sx={{ 
                      ...indicatorTitleStyle,
                      cursor: 'pointer',
                      '&:hover': { color: 'primary.main', textDecoration: 'underline' }
                    }}
                  >
                    ADX ({indicatorSettings.adxPeriod})
                  </Typography>
                  <ResponsiveContainer width="100%" height="100%">
                    <LineChart data={chartData} syncId="upbitChart" margin={CHART_MARGIN} onMouseMove={(state) => handleChartMouseMoveWithLog(state, 'adx')} onMouseLeave={handleChartMouseLeave} onClick={(state) => handleChartClick(state, 'adx')}>
                      {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                      <XAxis dataKey="date" hide={false} tick={{ fontSize: 9, fill: '#8b95a5' }} interval={getXAxisInterval()} tickFormatter={formatXAxisLabel} axisLine={{ stroke: '#252d3a' }} tickLine={{ stroke: '#252d3a' }} padding={{ left: 0, right: 0 }} />
                      <YAxis orientation="left" tick={<CustomYAxisTick />} width={60} domain={[0, 100]} stroke="#252d3a" />
                      {settings.tooltipEnabled && <Tooltip formatter={(value: any, name: string) => [value?.toFixed(2) ?? '-', name]} />}
                      {indicatorSettings.adxStrongEnabled && <ReferenceLine y={indicatorSettings.adxStrongThreshold} stroke={colors.adxStrongColor || "#4CAF50"} strokeWidth={colors.adxStrongWidth || 0.5} label={{ value: String(indicatorSettings.adxStrongThreshold), position: 'right', fontSize: 9, fill: colors.adxStrongColor || '#4CAF50' }} />}
                      {indicatorSettings.adxWeakEnabled && <ReferenceLine y={indicatorSettings.adxWeakThreshold} stroke={colors.adxWeakColor || "#666"} strokeWidth={colors.adxWeakWidth || 0.5} label={{ value: String(indicatorSettings.adxWeakThreshold), position: 'right', fontSize: 9, fill: colors.adxWeakColor || '#666' }} />}
                      {/* 사용자 정의 기준선 */}
                      {getCustomReferenceLines('adx').map(line => (
                        <ReferenceLine 
                          key={line.id} 
                          y={line.value} 
                          stroke={line.color} 
                          strokeDasharray={line.lineStyle === 'solid' ? '0' : line.lineStyle === 'dashed' ? '5 5' : '2 2'} 
                          strokeWidth={1} 
                          label={{ value: `${line.name} (${line.value})`, position: 'right', fontSize: 9, fill: line.color }}
                        />
                      ))}
                      {(chartMode === 'basic' || activeTool === 'crosshair') && (
                        <Customized key={`crosshair-${crosshairKeyDate}-${globalHoverYRatio ?? 'none'}`} component={(props: any) => <VerticalCrosshair {...props} chartData={chartData} globalHoverDate={globalHoverDate} globalHoverYRatio={globalHoverYRatio} showHorizontal={true} />} />
                      )}
                      {indicatorSettings.adxEnabled && <Line type="monotone" dataKey="adx" stroke={colors.adxColor || '#9C27B0'} strokeWidth={colors.adxWidth || 1.5} dot={false} name="ADX" connectNulls isAnimationActive={false} />}
                      {indicatorSettings.plusDiEnabled && <Line type="monotone" dataKey="plusDI" stroke={colors.plusDiColor || '#26a69a'} strokeWidth={colors.plusDiWidth || 1} dot={false} name="+DI" connectNulls isAnimationActive={false} />}
                      {indicatorSettings.minusDiEnabled && <Line type="monotone" dataKey="minusDI" stroke={colors.minusDiColor || '#ef5350'} strokeWidth={colors.minusDiWidth || 1} dot={false} name="-DI" connectNulls isAnimationActive={false} />}
                    </LineChart>
                  </ResponsiveContainer>
                </Box>
              )}

              {/* CCI 차트 */}
              {chartType === 'cci' && (() => {
                console.log(`🔍 [CCI 차트] 렌더링 시작:`, {
                  chartDataLength: chartData?.length,
                  chartMode,
                  activeTool,
                  globalHoverDate,
                  globalHoverYRatio,
                  crosshairKeyDate,
                  shouldShowCrosshair: (chartMode === 'basic' || activeTool === 'crosshair'),
                  firstDataSample: chartData?.[0]
                });
                return (
                <Box 
                  onMouseMove={handleIndicatorMouseMove}
                  onMouseLeave={handleIndicatorMouseLeave}
                  onTouchStart={handleIndicatorTouchStart}
                  onTouchMove={handleIndicatorTouchMove}
                  onTouchEnd={handleIndicatorTouchEnd}
                  sx={{ 
                    height: `${chartContainerHeight}px`, 
                    position: 'relative',
                    mt: 0,
                    touchAction: 'none',
                    cursor: 'pointer',
                  }}>
                  <Typography 
                    variant="caption" 
                    onClick={handleTitleClick('cci')}
                    sx={{ 
                      ...indicatorTitleStyle,
                      cursor: 'pointer',
                      '&:hover': { color: 'primary.main', textDecoration: 'underline' }
                    }}
                  >
                    CCI ({indicatorSettings.cciPeriod}, {indicatorSettings.cciSignalPeriod})
                  </Typography>
                  <ResponsiveContainer width="100%" height="100%">
                    <LineChart data={chartData} syncId="upbitChart" margin={CHART_MARGIN} onMouseMove={(state) => handleChartMouseMoveWithLog(state, 'cci')} onMouseLeave={handleChartMouseLeave} onClick={(state) => handleChartClick(state, 'cci')}>
                      {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                      <XAxis dataKey="date" hide={false} tick={{ fontSize: 9, fill: '#8b95a5' }} interval={getXAxisInterval()} tickFormatter={formatXAxisLabel} axisLine={{ stroke: '#252d3a' }} tickLine={{ stroke: '#252d3a' }} padding={{ left: 0, right: 0 }} />
                      <YAxis orientation="left" tick={<CustomYAxisTick />} width={60} stroke="#252d3a" />
                      {settings.tooltipEnabled && <Tooltip formatter={(value: any, name: string) => [value?.toFixed(2) ?? '-', name]} />}
                      {indicatorSettings.cciOverboughtEnabled && <ReferenceLine y={indicatorSettings.cciOverbought} stroke={colors.cciOverboughtColor || "#ef5350"} strokeWidth={colors.cciOverboughtWidth || 0.5} label={{ value: String(indicatorSettings.cciOverbought), position: 'right', fontSize: 9, fill: colors.cciOverboughtColor || '#ef5350' }} />}
                      {indicatorSettings.cciOversoldEnabled && <ReferenceLine y={indicatorSettings.cciOversold} stroke={colors.cciOversoldColor || "#26a69a"} strokeWidth={colors.cciOversoldWidth || 0.5} label={{ value: String(indicatorSettings.cciOversold), position: 'right', fontSize: 9, fill: colors.cciOversoldColor || '#26a69a' }} />}
                      {indicatorSettings.cciMiddleEnabled && <ReferenceLine y={indicatorSettings.cciMiddle} stroke={colors.cciMiddleColor || "#666"} strokeDasharray="2 2" strokeWidth={colors.cciMiddleWidth || 0.5} label={{ value: String(indicatorSettings.cciMiddle), position: 'right', fontSize: 9, fill: colors.cciMiddleColor || '#666' }} />}
                      {/* 사용자 정의 기준선 */}
                      {getCustomReferenceLines('cci').map(line => (
                        <ReferenceLine 
                          key={line.id} 
                          y={line.value} 
                          stroke={line.color} 
                          strokeDasharray={line.lineStyle === 'solid' ? '0' : line.lineStyle === 'dashed' ? '5 5' : '2 2'} 
                          strokeWidth={1} 
                          label={{ value: `${line.name} (${line.value})`, position: 'right', fontSize: 9, fill: line.color }}
                        />
                      ))}
                      {(chartMode === 'basic' || activeTool === 'crosshair') && (
                        <Customized key={`cci-crosshair-${crosshairKeyDate}-${globalHoverYRatio ?? 'none'}`} component={(props: any) => {
                          console.log(`🎯 [CCI Customized] props:`, { 
                            hasChartData: !!props.chartData, 
                            chartDataLength: props.chartData?.length,
                            globalHoverDate,
                            globalHoverYRatio 
                          });
                          return <VerticalCrosshair {...props} chartData={chartData} globalHoverDate={globalHoverDate} globalHoverYRatio={globalHoverYRatio} showHorizontal={true} />;
                        }} />
                      )}
                      {/* CCI 라인 */}
                      {indicatorSettings.cciEnabled && (
                        <Line type="monotone" dataKey="cci" stroke={colors.cciColor || '#00BCD4'} strokeWidth={colors.cciWidth || 1.5} dot={false} name="CCI" connectNulls isAnimationActive={false} />
                      )}
                      {/* CCI Signal 라인 */}
                      {indicatorSettings.cciSignalEnabled && (
                        <Line type="monotone" dataKey="cciSignal" stroke={colors.cciSignalColor || '#FF7043'} strokeWidth={colors.cciSignalWidth || 1} dot={false} name="cciSignal" connectNulls isAnimationActive={false}  />
                      )}
                    </LineChart>
                  </ResponsiveContainer>
                </Box>
                );
              })()}

              {/* TRIX 차트 */}
              {chartType === 'trix' && (
                <Box 
                  onMouseMove={handleIndicatorMouseMove}
                  onMouseLeave={handleIndicatorMouseLeave}
                  onTouchStart={handleIndicatorTouchStart}
                  onTouchMove={handleIndicatorTouchMove}
                  onTouchEnd={handleIndicatorTouchEnd}
                  sx={{ 
                    height: `${chartContainerHeight}px`, 
                    position: 'relative',
                    mt: 0,
                    touchAction: 'none',
                    cursor: 'pointer',
                  }}>
                  <Typography 
                    variant="caption" 
                    onClick={handleTitleClick('trix')}
                    sx={{ 
                      ...indicatorTitleStyle,
                      cursor: 'pointer',
                      '&:hover': { color: 'primary.main', textDecoration: 'underline' }
                    }}
                  >
                    TRIX ({indicatorSettings.trixPeriod},{indicatorSettings.trixSignalPeriod})
                  </Typography>
                  <ResponsiveContainer width="100%" height="100%">
                    <LineChart data={chartData} syncId="upbitChart" margin={CHART_MARGIN} onMouseMove={(state) => handleChartMouseMoveWithLog(state, 'trix')} onMouseLeave={handleChartMouseLeave} onClick={(state) => handleChartClick(state, 'trix')}>
                      {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                      <XAxis dataKey="date" hide={false} tick={{ fontSize: 9, fill: '#8b95a5' }} interval={getXAxisInterval()} tickFormatter={formatXAxisLabel} axisLine={{ stroke: '#252d3a' }} tickLine={{ stroke: '#252d3a' }} padding={{ left: 0, right: 0 }} />
                      <YAxis orientation="left" tick={<CustomYAxisTick />} width={60} stroke="#252d3a" />
                      {settings.tooltipEnabled && <Tooltip formatter={(value: any, name: string) => [value?.toFixed(2) ?? '-', name]} />}
                      {indicatorSettings.trixOverboughtEnabled && <ReferenceLine y={indicatorSettings.trixOverbought} stroke={colors.trixOverboughtColor || "#ef5350"} strokeWidth={colors.trixOverboughtWidth || 0.5} label={{ value: String(indicatorSettings.trixOverbought), position: 'right', fontSize: 9, fill: colors.trixOverboughtColor || '#ef5350' }} />}
                      {indicatorSettings.trixMiddleEnabled && <ReferenceLine y={indicatorSettings.trixMiddle} stroke={colors.trixMiddleColor || "#666"} strokeDasharray="2 2" strokeWidth={colors.trixMiddleWidth || 0.5} label={{ value: String(indicatorSettings.trixMiddle), position: 'right', fontSize: 9, fill: colors.trixMiddleColor || '#666' }} />}
                      {indicatorSettings.trixOversoldEnabled && <ReferenceLine y={indicatorSettings.trixOversold} stroke={colors.trixOversoldColor || "#26a69a"} strokeWidth={colors.trixOversoldWidth || 0.5} label={{ value: String(indicatorSettings.trixOversold), position: 'right', fontSize: 9, fill: colors.trixOversoldColor || '#26a69a' }} />}
                      {/* 사용자 정의 기준선 */}
                      {getCustomReferenceLines('trix').map(line => (
                        <ReferenceLine 
                          key={line.id} 
                          y={line.value} 
                          stroke={line.color} 
                          strokeDasharray={line.lineStyle === 'dashed' ? '3 3' : line.lineStyle === 'dotted' ? '1 2' : '0'}
                          strokeWidth={0.5}
                          label={{ value: line.name, position: 'right', fill: line.color, fontSize: 9 }}
                        />
                      ))}
                      {(chartMode === 'basic' || activeTool === 'crosshair') && (
                        <Customized key={`crosshair-${crosshairKeyDate}-${globalHoverYRatio ?? 'none'}`} component={(props: any) => <VerticalCrosshair {...props} chartData={chartData} globalHoverDate={globalHoverDate} globalHoverYRatio={globalHoverYRatio} showHorizontal={true} />} />
                      )}
                      <Line type="monotone" dataKey="trix" stroke={colors.trixColor || '#F50057'} strokeWidth={colors.trixWidth || 1.5} dot={false} name="TRIX" connectNulls isAnimationActive={false} />
                      <Line type="monotone" dataKey="trixSignal" stroke={colors.trixSignalColor || '#18FFFF'} strokeWidth={colors.trixSignalWidth || 1.5} dot={false} name="Signal" connectNulls isAnimationActive={false}  />
                    </LineChart>
                  </ResponsiveContainer>
                </Box>
              )}

              {/* 이격도 차트 */}
              {chartType === 'disparity' && (
                <Box 
                  onMouseMove={handleIndicatorMouseMove}
                  onMouseLeave={handleIndicatorMouseLeave}
                  onTouchStart={handleIndicatorTouchStart}
                  onTouchMove={handleIndicatorTouchMove}
                  onTouchEnd={handleIndicatorTouchEnd}
                  sx={{ 
                    height: `${chartContainerHeight}px`, 
                    position: 'relative',
                    mt: 0,
                    touchAction: 'none',
                    cursor: 'pointer',
                  }}>
                  <Typography 
                    variant="caption" 
                    onClick={handleTitleClick('disparity')}
                    sx={{ 
                      ...indicatorTitleStyle,
                      cursor: 'pointer',
                      '&:hover': { color: 'primary.main', textDecoration: 'underline' }
                    }}
                  >
                    이격도 (초단기:{indicatorSettings.disparityUltraShortPeriod || 5}, 단기:{indicatorSettings.disparityShortPeriod || 20}, 중기:{indicatorSettings.disparityMidPeriod || 60}, 장기:{indicatorSettings.disparityLongPeriod || 120})
                  </Typography>
                  <ResponsiveContainer width="100%" height="100%">
                    <LineChart data={chartData} syncId="upbitChart" margin={CHART_MARGIN} onMouseMove={(state) => handleChartMouseMoveWithLog(state, 'disparity')} onMouseLeave={handleChartMouseLeave} onClick={(state) => handleChartClick(state, 'disparity')}>
                      {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                      <XAxis dataKey="date" hide={false} tick={{ fontSize: 9, fill: '#8b95a5' }} interval={getXAxisInterval()} tickFormatter={formatXAxisLabel} axisLine={{ stroke: '#252d3a' }} tickLine={{ stroke: '#252d3a' }} padding={{ left: 0, right: 0 }} />
                      <YAxis orientation="left" tick={<CustomYAxisTick />} width={60} stroke="#252d3a" domain={[80, 120]} tickFormatter={(v: number) => `${v}%`} />
                      {settings.tooltipEnabled && <Tooltip formatter={(value: any, name: string) => [`${value?.toFixed(2) ?? '-'}%`, name]} />}
                      <ReferenceLine y={100} stroke="#666" strokeDasharray="2 2" strokeWidth={1} label={{ value: '100%', position: 'right', fontSize: 9, fill: '#666' }} />
                      <ReferenceLine y={120} stroke="#ef4444" strokeWidth={0.5} label={{ value: '120', position: 'right', fontSize: 9, fill: '#ef4444' }} />
                      <ReferenceLine y={80} stroke="#22c55e" strokeWidth={0.5} label={{ value: '80', position: 'right', fontSize: 9, fill: '#22c55e' }} />
                      {/* 사용자 정의 기준선 */}
                      {getCustomReferenceLines('disparity').map(line => (
                        <ReferenceLine 
                          key={line.id} 
                          y={line.value} 
                          stroke={line.color} 
                          strokeDasharray={line.lineStyle === 'dashed' ? '3 3' : line.lineStyle === 'dotted' ? '1 2' : '0'}
                          strokeWidth={0.5}
                          label={{ value: line.name, position: 'right', fill: line.color, fontSize: 9 }}
                        />
                      ))}
                      {(chartMode === 'basic' || activeTool === 'crosshair') && (
                        <Customized key={`crosshair-${crosshairKeyDate}-${globalHoverYRatio ?? 'none'}`} component={(props: any) => <VerticalCrosshair {...props} chartData={chartData} globalHoverDate={globalHoverDate} globalHoverYRatio={globalHoverYRatio} showHorizontal={true} />} />
                      )}
                      <Line type="monotone" dataKey="disparityUltraShort" stroke={colors.disparityUltraShortColor || '#FF6B35'} strokeWidth={colors.disparityUltraShortWidth || 1.5} dot={false} name="이격도(초단기)" connectNulls isAnimationActive={false} />
                      <Line type="monotone" dataKey="disparityShort" stroke={colors.disparityShortColor || '#4CAF50'} strokeWidth={colors.disparityShortWidth || 2} dot={false} name="이격도(단기)" connectNulls isAnimationActive={false} />
                      <Line type="monotone" dataKey="disparityMid" stroke={colors.disparityMidColor || '#2196F3'} strokeWidth={colors.disparityMidWidth || 2} dot={false} name="이격도(중기)" connectNulls isAnimationActive={false} />
                      <Line type="monotone" dataKey="disparityLong" stroke={colors.disparityLongColor || '#9C27B0'} strokeWidth={colors.disparityLongWidth || 2} dot={false} name="이격도(장기)" connectNulls isAnimationActive={false} />
                    </LineChart>
                  </ResponsiveContainer>
                </Box>
              )}

              {/* 신심리도 차트 */}
              {chartType === 'newPsychological' && (
                <Box 
                  onMouseMove={handleIndicatorMouseMove}
                  onMouseLeave={handleIndicatorMouseLeave}
                  onTouchStart={handleIndicatorTouchStart}
                  onTouchMove={handleIndicatorTouchMove}
                  onTouchEnd={handleIndicatorTouchEnd}
                  sx={{ 
                    height: `${chartContainerHeight}px`, 
                    position: 'relative',
                    mt: 0,
                    touchAction: 'none',
                    cursor: 'pointer',
                  }}>
                  <Typography 
                    variant="caption" 
                    onClick={handleTitleClick('newPsychological')}
                    sx={{ 
                      ...indicatorTitleStyle,
                      cursor: 'pointer',
                      '&:hover': { color: 'primary.main', textDecoration: 'underline' }
                    }}
                  >
                    신심리도 ({indicatorSettings.newPsychologicalPeriod})
                  </Typography>
                  <ResponsiveContainer width="100%" height="100%">
                    <LineChart data={chartData} syncId="upbitChart" margin={CHART_MARGIN} onMouseMove={(state) => handleChartMouseMoveWithLog(state, 'newPsychological')} onMouseLeave={handleChartMouseLeave} onClick={(state) => handleChartClick(state, 'newPsychological')}>
                      {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                      <XAxis dataKey="date" hide={false} tick={{ fontSize: 9, fill: '#8b95a5' }} interval={getXAxisInterval()} tickFormatter={formatXAxisLabel} axisLine={{ stroke: '#252d3a' }} tickLine={{ stroke: '#252d3a' }} padding={{ left: 0, right: 0 }} />
                      <YAxis orientation="left" tick={<CustomYAxisTick />} width={60} stroke="#252d3a" domain={[-100, 100]} />
                      {settings.tooltipEnabled && <Tooltip formatter={(value: any, name: string) => [value?.toFixed(2) ?? '-', name]} />}
                      {/* 과열/중앙값/침체 기준선 */}
                      {indicatorSettings.newPsychologicalOverboughtEnabled && <ReferenceLine y={indicatorSettings.newPsychologicalOverbought} stroke={colors.newPsychologicalOverboughtColor || '#ef4444'} strokeWidth={colors.newPsychologicalOverboughtWidth || 0.5} label={{ value: String(indicatorSettings.newPsychologicalOverbought), position: 'right', fontSize: 9, fill: colors.newPsychologicalOverboughtColor || '#ef4444' }} />}
                      {indicatorSettings.newPsychologicalMiddleEnabled && <ReferenceLine y={indicatorSettings.newPsychologicalMiddle} stroke={colors.newPsychologicalNeutralColor || '#666'} strokeDasharray="2 2" strokeWidth={colors.newPsychologicalNeutralWidth || 0.5} label={{ value: String(indicatorSettings.newPsychologicalMiddle), position: 'right', fontSize: 9, fill: colors.newPsychologicalNeutralColor || '#666' }} />}
                      {indicatorSettings.newPsychologicalOversoldEnabled && <ReferenceLine y={indicatorSettings.newPsychologicalOversold} stroke={colors.newPsychologicalOversoldColor || '#22c55e'} strokeWidth={colors.newPsychologicalOversoldWidth || 0.5} label={{ value: String(indicatorSettings.newPsychologicalOversold), position: 'right', fontSize: 9, fill: colors.newPsychologicalOversoldColor || '#22c55e' }} />}
                      {/* 사용자 정의 기준선 */}
                      {getCustomReferenceLines('newPsychological').map(line => (
                        <ReferenceLine 
                          key={line.id} 
                          y={line.value} 
                          stroke={line.color} 
                          strokeDasharray={line.lineStyle === 'dashed' ? '3 3' : line.lineStyle === 'dotted' ? '1 2' : '0'}
                          strokeWidth={0.5}
                          label={{ value: line.name, position: 'right', fill: line.color, fontSize: 9 }}
                        />
                      ))}
                      {(chartMode === 'basic' || activeTool === 'crosshair') && (
                        <Customized key={`crosshair-${crosshairKeyDate}-${globalHoverYRatio ?? 'none'}`} component={(props: any) => <VerticalCrosshair {...props} chartData={chartData} globalHoverDate={globalHoverDate} globalHoverYRatio={globalHoverYRatio} showHorizontal={true} />} />
                      )}
                      <Line type="monotone" dataKey="newPsychological" stroke={colors.newPsychologicalColor || '#9C27B0'} strokeWidth={colors.newPsychologicalWidth || 1.5} dot={false} name="신심리도" connectNulls isAnimationActive={false} />
                    </LineChart>
                  </ResponsiveContainer>
                </Box>
              )}

              {/* 투자심리도 차트 */}
              {chartType === 'investPsychological' && (
                <Box 
                  onMouseMove={handleIndicatorMouseMove}
                  onMouseLeave={handleIndicatorMouseLeave}
                  onTouchStart={handleIndicatorTouchStart}
                  onTouchMove={handleIndicatorTouchMove}
                  onTouchEnd={handleIndicatorTouchEnd}
                  sx={{ 
                    height: `${chartContainerHeight}px`, 
                    position: 'relative',
                    mt: 0,
                    touchAction: 'none',
                    cursor: 'pointer',
                  }}>
                  <Typography 
                    variant="caption" 
                    onClick={handleTitleClick('investPsychological')}
                    sx={{ 
                      ...indicatorTitleStyle,
                      cursor: 'pointer',
                      '&:hover': { color: 'primary.main', textDecoration: 'underline' }
                    }}
                  >
                    투자심리도 ({indicatorSettings.investPsychologicalPeriod})
                  </Typography>
                  <ResponsiveContainer width="100%" height="100%">
                    <LineChart data={chartData} syncId="upbitChart" margin={CHART_MARGIN} onMouseMove={(state) => handleChartMouseMoveWithLog(state, 'investPsychological')} onMouseLeave={handleChartMouseLeave} onClick={(state) => handleChartClick(state, 'investPsychological')}>
                      {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                      <XAxis dataKey="date" hide={false} tick={{ fontSize: 9, fill: '#8b95a5' }} interval={getXAxisInterval()} tickFormatter={formatXAxisLabel} axisLine={{ stroke: '#252d3a' }} tickLine={{ stroke: '#252d3a' }} padding={{ left: 0, right: 0 }} />
                      <YAxis orientation="left" tick={<CustomYAxisTick />} width={60} stroke="#252d3a" domain={[0, 100]} />
                      {settings.tooltipEnabled && <Tooltip formatter={(value: any, name: string) => [value?.toFixed(2) ?? '-', name]} />}
                      {indicatorSettings.investPsychologicalOverboughtEnabled && <ReferenceLine y={indicatorSettings.investPsychologicalOverbought} stroke={colors.investPsychologicalOverboughtColor || "#ef4444"} strokeWidth={colors.investPsychologicalOverboughtWidth || 0.5} label={{ value: String(indicatorSettings.investPsychologicalOverbought), position: 'right', fontSize: 9, fill: colors.investPsychologicalOverboughtColor || '#ef4444' }} />}
                      {indicatorSettings.investPsychologicalOversoldEnabled && <ReferenceLine y={indicatorSettings.investPsychologicalOversold} stroke={colors.investPsychologicalOversoldColor || "#22c55e"} strokeWidth={colors.investPsychologicalOversoldWidth || 0.5} label={{ value: String(indicatorSettings.investPsychologicalOversold), position: 'right', fontSize: 9, fill: colors.investPsychologicalOversoldColor || '#22c55e' }} />}
                      {indicatorSettings.investPsychologicalMiddleEnabled && <ReferenceLine y={indicatorSettings.investPsychologicalMiddle} stroke={colors.investPsychologicalMiddleColor || "#666"} strokeDasharray="2 2" strokeWidth={colors.investPsychologicalMiddleWidth || 0.5} label={{ value: String(indicatorSettings.investPsychologicalMiddle), position: 'right', fontSize: 9, fill: colors.investPsychologicalMiddleColor || '#666' }} />}
                      {/* 사용자 정의 기준선 */}
                      {getCustomReferenceLines('investPsychological').map(line => (
                        <ReferenceLine 
                          key={line.id} 
                          y={line.value} 
                          stroke={line.color} 
                          strokeDasharray={line.lineStyle === 'dashed' ? '3 3' : line.lineStyle === 'dotted' ? '1 2' : '0'}
                          strokeWidth={0.5}
                          label={{ value: line.name, position: 'right', fill: line.color, fontSize: 9 }}
                        />
                      ))}
                      {(chartMode === 'basic' || activeTool === 'crosshair') && (
                        <Customized key={`crosshair-${crosshairKeyDate}-${globalHoverYRatio ?? 'none'}`} component={(props: any) => <VerticalCrosshair {...props} chartData={chartData} globalHoverDate={globalHoverDate} globalHoverYRatio={globalHoverYRatio} showHorizontal={true} />} />
                      )}
                      <Line type="monotone" dataKey="investPsychological" stroke={colors.investPsychologicalColor || '#E91E63'} strokeWidth={colors.investPsychologicalWidth || 1.5} dot={false} name="투자심리도" connectNulls isAnimationActive={false} />
                    </LineChart>
                  </ResponsiveContainer>
                </Box>
              )}

              {/* BWI 차트 */}
              {chartType === 'bwi' && (
                <Box 
                  onMouseMove={handleIndicatorMouseMove}
                  onMouseLeave={handleIndicatorMouseLeave}
                  onTouchStart={handleIndicatorTouchStart}
                  onTouchMove={handleIndicatorTouchMove}
                  onTouchEnd={handleIndicatorTouchEnd}
                  sx={{ 
                    height: `${chartContainerHeight}px`, 
                    position: 'relative',
                    mt: 0,
                    touchAction: 'none',
                    cursor: 'pointer',
                  }}>
                  <Typography 
                    variant="caption" 
                    onClick={handleTitleClick('bwi')}
                    sx={{ 
                      ...indicatorTitleStyle,
                      cursor: 'pointer',
                      '&:hover': { color: 'primary.main', textDecoration: 'underline' }
                    }}
                  >
                    BWI ({indicatorSettings.bwiPeriod})
                  </Typography>
                  <ResponsiveContainer width="100%" height="100%">
                    <LineChart data={chartData} syncId="upbitChart" margin={CHART_MARGIN} onMouseMove={(state) => handleChartMouseMoveWithLog(state, 'bwi')} onMouseLeave={handleChartMouseLeave} onClick={(state) => handleChartClick(state, 'bwi')}>
                      {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                      <XAxis dataKey="date" hide={false} tick={{ fontSize: 9, fill: '#8b95a5' }} interval={getXAxisInterval()} tickFormatter={formatXAxisLabel} axisLine={{ stroke: '#252d3a' }} tickLine={{ stroke: '#252d3a' }} padding={{ left: 0, right: 0 }} />
                      <YAxis orientation="left" tick={<CustomYAxisTick />} width={60} stroke="#252d3a" domain={['auto', 'auto']} />
                      {settings.tooltipEnabled && <Tooltip formatter={(value: any, name: string) => [value?.toFixed(2) ?? '-', name]} />}
                      {/* 사용자 정의 기준선 */}
                      {getCustomReferenceLines('bwi').map(line => (
                        <ReferenceLine 
                          key={line.id} 
                          y={line.value} 
                          stroke={line.color} 
                          strokeDasharray={line.lineStyle === 'dashed' ? '3 3' : line.lineStyle === 'dotted' ? '1 2' : '0'}
                          strokeWidth={0.5}
                          label={{ value: line.name, position: 'right', fill: line.color, fontSize: 9 }}
                        />
                      ))}
                      {(chartMode === 'basic' || activeTool === 'crosshair') && (
                        <Customized key={`crosshair-${crosshairKeyDate}-${globalHoverYRatio ?? 'none'}`} component={(props: any) => <VerticalCrosshair {...props} chartData={chartData} globalHoverDate={globalHoverDate} globalHoverYRatio={globalHoverYRatio} showHorizontal={true} />} />
                      )}
                      <Line type="monotone" dataKey="bwi" stroke={colors.bwiColor || '#FF9800'} strokeWidth={colors.bwiWidth || 1.5} dot={false} name="BWI" connectNulls isAnimationActive={false} />
                    </LineChart>
                  </ResponsiveContainer>
                </Box>
              )}

              {/* DMI 차트 */}
              {chartType === 'dmi' && (
                <Box 
                  onMouseMove={handleIndicatorMouseMove}
                  onMouseLeave={handleIndicatorMouseLeave}
                  onTouchStart={handleIndicatorTouchStart}
                  onTouchMove={handleIndicatorTouchMove}
                  onTouchEnd={handleIndicatorTouchEnd}
                  sx={{ 
                    height: `${chartContainerHeight}px`, 
                    position: 'relative',
                    mt: 0,
                    touchAction: 'none',
                    cursor: 'pointer',
                  }}>
                  <Typography 
                    variant="caption" 
                    onClick={handleTitleClick('dmi')}
                    sx={{ 
                      ...indicatorTitleStyle,
                      cursor: 'pointer',
                      '&:hover': { color: 'primary.main', textDecoration: 'underline' }
                    }}
                  >
                    DMI ({indicatorSettings.dmiPeriod})
                  </Typography>
                  <ResponsiveContainer width="100%" height="100%">
                    <LineChart data={chartData} syncId="upbitChart" margin={CHART_MARGIN} onMouseMove={(state) => handleChartMouseMoveWithLog(state, 'dmi')} onMouseLeave={handleChartMouseLeave} onClick={(state) => handleChartClick(state, 'dmi')}>
                      {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                      <XAxis dataKey="date" hide={false} tick={{ fontSize: 9, fill: '#8b95a5' }} interval={getXAxisInterval()} tickFormatter={formatXAxisLabel} axisLine={{ stroke: '#252d3a' }} tickLine={{ stroke: '#252d3a' }} padding={{ left: 0, right: 0 }} />
                      <YAxis orientation="left" tick={<CustomYAxisTick />} width={60} stroke="#252d3a" domain={[0, 'auto']} />
                      {settings.tooltipEnabled && <Tooltip formatter={(value: any, name: string) => [value?.toFixed(2) ?? '-', name]} />}
                      {/* 사용자 정의 기준선 */}
                      {getCustomReferenceLines('dmi').map(line => (
                        <ReferenceLine 
                          key={line.id} 
                          y={line.value} 
                          stroke={line.color} 
                          strokeDasharray={line.lineStyle === 'dashed' ? '3 3' : line.lineStyle === 'dotted' ? '1 2' : '0'}
                          strokeWidth={0.5}
                          label={{ value: line.name, position: 'right', fill: line.color, fontSize: 9 }}
                        />
                      ))}
                      {(chartMode === 'basic' || activeTool === 'crosshair') && (
                        <Customized key={`crosshair-${crosshairKeyDate}-${globalHoverYRatio ?? 'none'}`} component={(props: any) => <VerticalCrosshair {...props} chartData={chartData} globalHoverDate={globalHoverDate} globalHoverYRatio={globalHoverYRatio} showHorizontal={true} />} />
                      )}
                      {indicatorSettings.plusDiEnabled && <Line type="monotone" dataKey="dmiPlusDI" stroke={colors.plusDiColor || "#26a69a"} strokeWidth={colors.plusDiWidth || 1} dot={false} name="+DI" connectNulls isAnimationActive={false} />}
                      {indicatorSettings.minusDiEnabled && <Line type="monotone" dataKey="dmiMinusDI" stroke={colors.minusDiColor || "#ef5350"} strokeWidth={colors.minusDiWidth || 1} dot={false} name="-DI" connectNulls isAnimationActive={false} />}
                      {indicatorSettings.dmiAdxEnabled && <Line type="monotone" dataKey="dmiAdx" stroke={colors.adxColor || "#9C27B0"} strokeWidth={colors.adxWidth || 1.5} dot={false} name="ADX" connectNulls isAnimationActive={false} />}
                    </LineChart>
                  </ResponsiveContainer>
                </Box>
              )}

              {/* OBV 차트 */}
              {chartType === 'obv' && (
                <Box 
                  onMouseMove={handleIndicatorMouseMove}
                  onMouseLeave={handleIndicatorMouseLeave}
                  onTouchStart={handleIndicatorTouchStart}
                  onTouchMove={handleIndicatorTouchMove}
                  onTouchEnd={handleIndicatorTouchEnd}
                  sx={{ 
                    height: `${chartContainerHeight}px`, 
                    position: 'relative',
                    mt: 0,
                    touchAction: 'none',
                    cursor: 'pointer',
                  }}>
                  <Typography 
                    variant="caption" 
                    onClick={handleTitleClick('obv')}
                    sx={{ 
                      ...indicatorTitleStyle,
                      cursor: 'pointer',
                      '&:hover': { color: 'primary.main', textDecoration: 'underline' }
                    }}
                  >
                    OBV ({indicatorSettings.obvPeriod}, {indicatorSettings.obvSignalPeriod})
                  </Typography>
                  <ResponsiveContainer width="100%" height="100%">
                    <LineChart data={chartData} syncId="upbitChart" margin={CHART_MARGIN} onMouseMove={(state) => handleChartMouseMoveWithLog(state, 'obv')} onMouseLeave={handleChartMouseLeave} onClick={(state) => handleChartClick(state, 'obv')}>
                      {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                      <XAxis dataKey="date" hide={false} tick={{ fontSize: 9, fill: '#8b95a5' }} interval={getXAxisInterval()} tickFormatter={formatXAxisLabel} axisLine={{ stroke: '#252d3a' }} tickLine={{ stroke: '#252d3a' }} padding={{ left: 0, right: 0 }} />
                      <YAxis orientation="left" tick={<CustomYAxisTick />} width={60} stroke="#252d3a" domain={['auto', 'auto']} tickFormatter={formatCompactNumber} />
                      {settings.tooltipEnabled && <Tooltip formatter={(value: any, name: string) => [value?.toFixed(0) ?? '-', name]} />}
                      {/* 사용자 정의 기준선 */}
                      {getCustomReferenceLines('obv').map(line => (
                        <ReferenceLine 
                          key={line.id} 
                          y={line.value} 
                          stroke={line.color} 
                          strokeDasharray={line.lineStyle === 'dashed' ? '3 3' : line.lineStyle === 'dotted' ? '1 2' : '0'}
                          strokeWidth={0.5}
                          label={{ value: line.name, position: 'right', fill: line.color, fontSize: 9 }}
                        />
                      ))}
                      {(chartMode === 'basic' || activeTool === 'crosshair') && (
                        <Customized key={`crosshair-${crosshairKeyDate}-${globalHoverYRatio ?? 'none'}`} component={(props: any) => <VerticalCrosshair {...props} chartData={chartData} globalHoverDate={globalHoverDate} globalHoverYRatio={globalHoverYRatio} showHorizontal={true} />} />
                      )}
                      {/* OBV 라인 */}
                      <Line type="monotone" dataKey="obv" stroke={colors.obvColor || '#2196F3'} strokeWidth={colors.obvWidth || 1.5} dot={false} name="OBV" connectNulls isAnimationActive={false} />
                      {/* OBV Signal 라인 */}
                      <Line type="monotone" dataKey="obvSignal" stroke={colors.obvSignalColor || '#FF7043'} strokeWidth={colors.obvSignalWidth || 1} dot={false} name="obvSignal" connectNulls isAnimationActive={false}  />
                    </LineChart>
                  </ResponsiveContainer>
                </Box>
              )}

              {/* Williams %R 차트 */}
              {chartType === 'williamsR' && (
                <Box 
                  onMouseMove={handleIndicatorMouseMove}
                  onMouseLeave={handleIndicatorMouseLeave}
                  onTouchStart={handleIndicatorTouchStart}
                  onTouchMove={handleIndicatorTouchMove}
                  onTouchEnd={handleIndicatorTouchEnd}
                  sx={{ 
                    height: `${chartContainerHeight}px`, 
                    position: 'relative',
                    mt: 0,
                    touchAction: 'none',
                    cursor: 'pointer',
                  }}>
                  <Typography 
                    variant="caption" 
                    onClick={handleTitleClick('williamsR')}
                    sx={{ 
                      ...indicatorTitleStyle,
                      cursor: 'pointer',
                      '&:hover': { color: 'primary.main', textDecoration: 'underline' }
                    }}
                  >
                    Williams %R ({indicatorSettings.williamsRPeriod})
                  </Typography>
                  <ResponsiveContainer width="100%" height="100%">
                    <LineChart data={chartData} syncId="upbitChart" margin={CHART_MARGIN} onMouseMove={(state) => handleChartMouseMoveWithLog(state, 'williamsR')} onMouseLeave={handleChartMouseLeave} onClick={(state) => handleChartClick(state, 'williamsR')}>
                      {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                      <XAxis dataKey="date" hide={false} tick={{ fontSize: 9, fill: '#8b95a5' }} interval={getXAxisInterval()} tickFormatter={formatXAxisLabel} axisLine={{ stroke: '#252d3a' }} tickLine={{ stroke: '#252d3a' }} padding={{ left: 0, right: 0 }} />
                      <YAxis orientation="left" tick={<CustomYAxisTick />} width={60} stroke="#252d3a" domain={[-100, 0]} />
                      {settings.tooltipEnabled && <Tooltip formatter={(value: any, name: string) => [value?.toFixed(2) ?? '-', name]} />}
                      {/* 과열/중간/침체 기준선 */}
                      <ReferenceLine y={indicatorSettings.williamsROverbought} stroke={colors.williamsROverboughtColor || '#ef4444'} strokeWidth={colors.williamsROverboughtWidth || 0.5} label={{ value: String(indicatorSettings.williamsROverbought), position: 'right', fontSize: 9, fill: colors.williamsROverboughtColor || '#ef4444' }} />
                      <ReferenceLine y={indicatorSettings.williamsRMiddle} stroke={colors.williamsRMiddleColor || '#666'} strokeDasharray="2 2" strokeWidth={colors.williamsRMiddleWidth || 0.5} label={{ value: String(indicatorSettings.williamsRMiddle), position: 'right', fontSize: 9, fill: colors.williamsRMiddleColor || '#666' }} />
                      <ReferenceLine y={indicatorSettings.williamsROversold} stroke={colors.williamsROversoldColor || '#22c55e'} strokeWidth={colors.williamsROversoldWidth || 0.5} label={{ value: String(indicatorSettings.williamsROversold), position: 'right', fontSize: 9, fill: colors.williamsROversoldColor || '#22c55e' }} />
                      {/* 사용자 정의 기준선 */}
                      {getCustomReferenceLines('williamsR').map(line => (
                        <ReferenceLine 
                          key={line.id} 
                          y={line.value} 
                          stroke={line.color} 
                          strokeDasharray={line.lineStyle === 'solid' ? '0' : line.lineStyle === 'dashed' ? '5 5' : '2 2'} 
                          strokeWidth={1} 
                          label={{ value: `${line.name} (${line.value})`, position: 'right', fontSize: 9, fill: line.color }}
                        />
                      ))}
                      {(chartMode === 'basic' || activeTool === 'crosshair') && (
                        <Customized key={`crosshair-${crosshairKeyDate}-${globalHoverYRatio ?? 'none'}`} component={(props: any) => <VerticalCrosshair {...props} chartData={chartData} globalHoverDate={globalHoverDate} globalHoverYRatio={globalHoverYRatio} showHorizontal={true} />} />
                      )}
                      <Line type="monotone" dataKey="williamsR" stroke={colors.williamsRColor || '#FF5722'} strokeWidth={colors.williamsRWidth || 1.5} dot={false} name="Williams %R" connectNulls isAnimationActive={false} />
                    </LineChart>
                  </ResponsiveContainer>
                </Box>
              )}

              {/* VolumeOSC 차트 */}
              {chartType === 'volumeOsc' && (
                <Box 
                  sx={{ 
                    height: `${chartContainerHeight}px`, 
                    position: 'relative',
                    mt: 0,
                    cursor: 'pointer',
                  }}>
                  <Typography 
                    variant="caption" 
                    onClick={handleTitleClick('volumeOsc')}
                    sx={{ 
                      ...indicatorTitleStyle,
                      cursor: 'pointer',
                      '&:hover': { color: 'primary.main', textDecoration: 'underline' }
                    }}
                  >
                    VolumeOSC ({indicatorSettings.volumeOscShortPeriod},{indicatorSettings.volumeOscLongPeriod})
                  </Typography>
                  <ResponsiveContainer width="100%" height="100%">
                    <ComposedChart data={chartData} syncId="upbitChart" margin={CHART_MARGIN} onMouseMove={(state) => handleChartMouseMoveWithLog(state, 'volumeOsc')} onMouseLeave={handleChartMouseLeave} onClick={(state) => handleChartClick(state, 'volumeOsc')}>
                      {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                      <XAxis dataKey="date" hide={false} tick={{ fontSize: 9, fill: '#8b95a5' }} interval={getXAxisInterval()} tickFormatter={formatXAxisLabel} axisLine={{ stroke: '#252d3a' }} tickLine={{ stroke: '#252d3a' }} padding={{ left: 0, right: 0 }} />
                      <YAxis orientation="left" tick={<CustomYAxisTick />} width={60} stroke="#252d3a" domain={['auto', 'auto']} tickFormatter={(v: number) => `${v.toFixed(0)}%`} />
                      {settings.tooltipEnabled && <Tooltip formatter={(value: any, name: string) => [`${value?.toFixed(2) ?? '-'}%`, name]} />}
                      {indicatorSettings.volumeOscZeroLineEnabled && <ReferenceLine y={0} stroke={colors.volumeOscZeroLineColor || "#666"} strokeDasharray="2 2" strokeWidth={colors.volumeOscZeroLineWidth || 0.5} />}
                      {/* 사용자 정의 기준선 */}
                      {getCustomReferenceLines('volumeOsc').map(line => (
                        <ReferenceLine 
                          key={line.id} 
                          y={line.value} 
                          stroke={line.color} 
                          strokeDasharray={line.lineStyle === 'dashed' ? '3 3' : line.lineStyle === 'dotted' ? '1 2' : '0'}
                          strokeWidth={0.5}
                          label={{ value: line.name, position: 'right', fill: line.color, fontSize: 9 }}
                        />
                      ))}
                      {(chartMode === 'basic' || activeTool === 'crosshair') && (
                        <Customized key={`crosshair-${crosshairKeyDate}-${globalHoverYRatio ?? 'none'}`} component={(props: any) => <VerticalCrosshair {...props} chartData={chartData} globalHoverDate={globalHoverDate} globalHoverYRatio={globalHoverYRatio} showHorizontal={true} />} />
                      )}
                      <Bar dataKey="volumeOsc" name="VolumeOSC" radius={[1, 1, 0, 0]}>
                        {chartData.map((entry: any, index: number) => (
                          <Cell key={`vosc-${index}`} fill={(entry.volumeOsc ?? 0) >= 0 ? '#4CAF50' : '#f44336'} />
                        ))}
                      </Bar>
                    </ComposedChart>
                  </ResponsiveContainer>
                </Box>
              )}

              {/* VR 차트 */}
              {chartType === 'vr' && (
                <Box 
                  sx={{ 
                    height: `${chartContainerHeight}px`, 
                    position: 'relative',
                    mt: 0,
                    cursor: 'pointer',
                  }}>
                  <Typography 
                    variant="caption" 
                    onClick={handleTitleClick('vr')}
                    sx={{ 
                      ...indicatorTitleStyle,
                      cursor: 'pointer',
                      '&:hover': { color: 'primary.main', textDecoration: 'underline' }
                    }}
                  >
                    VR ({indicatorSettings.vrPeriod})
                  </Typography>
                  <ResponsiveContainer width="100%" height="100%">
                    <LineChart data={chartData} syncId="upbitChart" margin={CHART_MARGIN} onMouseMove={(state) => handleChartMouseMoveWithLog(state, 'vr')} onMouseLeave={handleChartMouseLeave} onClick={(state) => handleChartClick(state, 'vr')}>
                      {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                      <XAxis dataKey="date" hide={false} tick={{ fontSize: 9, fill: '#8b95a5' }} interval={getXAxisInterval()} tickFormatter={formatXAxisLabel} axisLine={{ stroke: '#252d3a' }} tickLine={{ stroke: '#252d3a' }} padding={{ left: 0, right: 0 }} />
                      <YAxis orientation="left" tick={<CustomYAxisTick />} width={60} stroke="#252d3a" domain={[0, 'auto']} />
                      {settings.tooltipEnabled && <Tooltip formatter={(value: any, name: string) => [value?.toFixed(2) ?? '-', name]} />}
                      {indicatorSettings.vrMiddleEnabled && <ReferenceLine y={indicatorSettings.vrMiddle} stroke={colors.vrMiddleColor || "#666"} strokeDasharray="2 2" strokeWidth={colors.vrMiddleWidth || 0.5} label={{ value: String(indicatorSettings.vrMiddle), position: 'right', fontSize: 9, fill: colors.vrMiddleColor || '#666' }} />}
                      {indicatorSettings.vrOversoldEnabled && <ReferenceLine y={indicatorSettings.vrOversold} stroke={colors.vrOversoldColor || "#22c55e"} strokeWidth={colors.vrOversoldWidth || 0.5} label={{ value: String(indicatorSettings.vrOversold), position: 'right', fontSize: 9, fill: colors.vrOversoldColor || '#22c55e' }} />}
                      {indicatorSettings.vrOverboughtEnabled && <ReferenceLine y={indicatorSettings.vrOverbought} stroke={colors.vrOverboughtColor || "#ef4444"} strokeWidth={colors.vrOverboughtWidth || 0.5} label={{ value: String(indicatorSettings.vrOverbought), position: 'right', fontSize: 9, fill: colors.vrOverboughtColor || '#ef4444' }} />}
                      {/* 사용자 정의 기준선 */}
                      {getCustomReferenceLines('vr').map(line => (
                        <ReferenceLine 
                          key={line.id} 
                          y={line.value} 
                          stroke={line.color} 
                          strokeDasharray={line.lineStyle === 'dashed' ? '3 3' : line.lineStyle === 'dotted' ? '1 2' : '0'}
                          strokeWidth={0.5}
                          label={{ value: line.name, position: 'right', fill: line.color, fontSize: 9 }}
                        />
                      ))}
                      {(chartMode === 'basic' || activeTool === 'crosshair') && (
                        <Customized key={`crosshair-${crosshairKeyDate}-${globalHoverYRatio ?? 'none'}`} component={(props: any) => <VerticalCrosshair {...props} chartData={chartData} globalHoverDate={globalHoverDate} globalHoverYRatio={globalHoverYRatio} showHorizontal={true} />} />
                      )}
                      <Line type="monotone" dataKey="vr" stroke={colors.vrColor || '#FFC107'} strokeWidth={colors.vrWidth || 1.5} dot={false} name="VR" connectNulls isAnimationActive={false} />
                    </LineChart>
                  </ResponsiveContainer>
                </Box>
              )}
            </SortableIndicatorChart>
            );
          })}
        </SortableContext>
      </DndContext>
      
      {/* 보조지표 설정 팝업 */}
      <IndicatorSettingsPopup
        open={settingsPopupOpen}
        onClose={() => setSettingsPopupOpen(false)}
        indicatorType={selectedIndicator}
      />
    </Box>
    </>
  );
};

// 기본 전략 로직 제거됨 - 전략 규칙 관리(StrategyRule)만 사용
// StochSignal, calculateStochSignals 삭제됨

// 분석 통계 타입
interface AnalysisStatistics {
  totalDays: number;
  buySignals: number;
  sellSignals: number;
  priceChangePercent: number;
  currentPrice: number;
  currentSignal: string;
}

// 백테스팅 매매 신호 타입
interface BacktestTradeSignal {
  date: string;       // 신호 발생 날짜/시간
  type: 'BUY' | 'SELL'; // 매수/매도
  price: number;      // 가격
  note?: string;      // 메모 (선택)
}

interface MacdChartProps {
  data: any[];  // MacdResult[] | IndicatorData[] - 유연한 타입 지원
  // ★ candleData 직접 전달 옵션 - CandlestickChart와 동일한 데이터 사용
  candleData?: CandleData[]; // candleData가 있으면 이것을 우선 사용
  title?: string;
  symbol?: string; // 암호화폐 심볼 (KRW-BTC 등)
  interval?: string; // 시간 간격 (1m, 3m, 5m, 1h, 1d, 1w, 1M)
  onIntervalChange?: (interval: string) => void; // 시간 간격 변경 콜백
  statistics?: AnalysisStatistics | null; // 분석 통계
  onScrollLoadMore?: (direction: 'past' | 'future', referenceDate: string, dataCount?: number) => void; // 스크롤 시 데이터 로드 콜백
  isLoadingMore?: boolean; // 추가 데이터 로딩 중 여부
  // 커서 기반 네비게이션
  showLeftButton?: boolean; // 좌측 버튼 표시 여부 (기본: true)
  showRightButton?: boolean; // 우측 버튼 표시 여부 (기본: false, cursor < 0일 때만 true)
  // 추세 강도 (정배열/역배열 판단용)
  trendStrength?: number; // -100 ~ +100, 양수: 정배열, 음수: 역배열
  // 전체화면 모드
  isFullscreen?: boolean; // 전체화면 여부
  onToggleFullscreen?: () => void; // 전체화면 토글 콜백
  // 백테스팅 매매 신호 (투자전략 페이지용)
  backtestSignals?: BacktestTradeSignal[]; // 백테스팅 결과 매매 신호
  showBacktestSignals?: boolean; // 매매 신호 표시 여부 (기본: true)
  onToggleBacktestSignals?: (show: boolean) => void; // 매매 신호 표시 토글 콜백
  // 매매 마커 클릭/더블클릭 이벤트
  onMarkerClick?: (timestamp: string, tradeType: 'BUY' | 'SELL') => void; // 마커 클릭 핸들러
  // 차트 상세보기
  onChartDetailClick?: (centerIndex: number, chartType: string, signal?: 'BUY' | 'SELL' | null) => void; // 차트 상세보기 클릭 핸들러
  // 차트 hover 이벤트 (팝업 연동용)
  onChartHover?: (index: number, chartType: string) => void; // 차트 hover 시 콜백
  // 차트 클릭 이벤트 (팝업 갱신용)
  onChartAreaClick?: (index: number, chartType: string) => void; // 차트 영역 클릭 시 콜백
  // 크로스헤어 X 좌표 변경 이벤트 (CandlestickChart 동기화용)
  onCrosshairXChange?: (x: number | null) => void; // 실제 마우스 X 픽셀 좌표
  // 기간 이동 컨트롤
  periodLabel?: string; // 현재 기간 라벨 (예: "2023.01 ~ 2023.03")
  onPreviousPeriod?: () => void; // 이전 기간 이동 콜백
  onNextPeriod?: () => void; // 다음 기간 이동 콜백
  isPreviousPeriodDisabled?: boolean; // 이전 기간 버튼 비활성화 여부
  isNextPeriodDisabled?: boolean; // 다음 기간 버튼 비활성화 여부
  isPeriodLoading?: boolean; // 기간 데이터 로딩 중 여부
  hideMainChart?: boolean; // 메인 캔들차트 숨김 (보조지표만 표시)
  // 크로스헤어 동기화 (외부 차트와 연동)
  externalHoverTime?: string | null; // 외부에서 전달받은 호버 시간 (ISO 문자열)
  onHoverTimeChange?: (time: string | null) => void; // 호버 시간 변경 콜백
  // X축 범위 동기화 (외부 차트와 연동)
  externalViewRange?: { start: number; end: number } | null; // 외부에서 전달받은 표시 범위
  onViewRangeChange?: (range: { start: number; end: number }) => void; // 표시 범위 변경 콜백
  // 기간 측정 도구 더블클릭 이벤트
  onMeasureDoubleClick?: (drawingId: string) => void; // 기간 측정 도구 더블클릭 핸들러
}

// 그리기 객체 타입 정의
interface DrawingObject {
  id: string;
  type: 'trendLine' | 'horizontalLine' | 'verticalLine' | 'rectangle' | 'fibonacci' | 'text' | 'brush' | 'measure';
  points: { x: number; y: number }[];
  color: string;
  text?: string;
  priceStart?: number;
  priceEnd?: number;
  dateStart?: string;
  dateEnd?: string;
  // 스타일 속성
  strokeWidth?: number; // 선 굵기 (기본값: 1)
  strokeStyle?: 'solid' | 'dashed' | 'dotted'; // 선 종류
  fillColor?: string; // 배경색 (사각형용)
  fillOpacity?: number; // 배경 투명도 (0~1)
}

// 그리기 이력 관리용 타입
interface DrawingHistory {
  drawings: DrawingObject[];
  index: number;
}

// 캔들스틱 커스텀 렌더러 (업비트 스타일)
const Candlestick = (props: any) => {
  const { xAxisMap, yAxisMap, chartWidth, chartHeight, data, offset, margin, candleRising, candleFalling } = props;
  
  // 디버깅: 렌더링 정보 출력 (Hook은 항상 최상위에서 호출)
  React.useEffect(() => {
    // 디버깅 로그 제거됨
  }, [data, offset, xAxisMap, yAxisMap]);
  
  if (!xAxisMap || !yAxisMap || !data) {
    console.warn('Candlestick: 필수 props 누락', { xAxisMap: !!xAxisMap, yAxisMap: !!yAxisMap, data: !!data });
    return null;
  }
  
  const xAxis = xAxisMap[0];
  // yAxisId="price"로 지정된 Y축 찾기 (yAxisMap에서 'price' 키 또는 인덱스 0 사용)
  const yAxis = yAxisMap['price'] || yAxisMap[0];
  
  if (!xAxis || !yAxis) {
    console.warn('Candlestick: axis 누락', { xAxis: !!xAxis, yAxis: !!yAxis, yAxisMapKeys: Object.keys(yAxisMap) });
    return null;
  }
  
  const xScale = xAxis.scale;
  const yScale = yAxis.scale;
  
  // bandwidth 계산 (최소값 보장)
  const rawBandwidth = xScale.bandwidth ? xScale.bandwidth() : 0;
  const availableWidth = offset?.width || chartWidth || 800;
  const dataCount = data.length;
  const calculatedBandwidth = availableWidth / dataCount;
  const bandwidth = Math.max(rawBandwidth, calculatedBandwidth, 5); // 최소 5px
  
  // 클리핑 영역 계산
  const { left = 0, top = 0, width = 0, height = 0 } = offset || {};
  const clipId = `candlestick-clip-${Math.random().toString(36).substr(2, 9)}`;
  
  return (
    <g>
      <defs>
        <clipPath id={clipId}>
          <rect x={left} y={top} width={width} height={height} />
        </clipPath>
      </defs>
      <g clipPath={`url(#${clipId})`}>
        {data.map((entry: any, index: number) => {
          // 데이터 유효성 검사 - 0도 유효한 값으로 처리
          if (entry.open === null || entry.open === undefined || 
              entry.close === null || entry.close === undefined || 
              entry.high === null || entry.high === undefined || 
              entry.low === null || entry.low === undefined) {
            if (index < 3) {
              console.warn(`캔들 ${index} 스킵 (OHLC 누락):`, {
                date: entry.date,
                open: entry.open,
                high: entry.high,
                low: entry.low,
                close: entry.close
              });
            }
            return null;
          }
          
          const xValue = xScale(entry.date);
          if (xValue === undefined) {
            if (index < 3) {
              console.warn(`캔들 ${index} 스킵 (xValue undefined):`, entry.date);
            }
            return null;
          }
          
          const centerX = xValue + bandwidth / 2;
          const candleWidth = Math.max(bandwidth * 0.7, 3); // 최소 3px
          
          const highY = yScale(entry.high);
          const lowY = yScale(entry.low);
          const openY = yScale(entry.open);
          const closeY = yScale(entry.close);
          
          // Y 좌표 유효성 검사
          if (isNaN(highY) || isNaN(lowY) || isNaN(openY) || isNaN(closeY)) {
            return null;
          }
          
          const isRising = entry.close >= entry.open;
          // Context에서 가져온 색상 사용
          const color = isRising ? (candleRising || '#c62828') : (candleFalling || '#1565c0');
          
          const bodyTop = Math.min(openY, closeY);
          const bodyHeight = Math.max(Math.abs(openY - closeY), 1);
          
          return (
            <g key={`candle-${index}`}>
              {/* High-Low 선 */}
              <line
                x1={centerX}
                y1={highY}
                x2={centerX}
                y2={lowY}
                stroke={color}
                strokeWidth={1}
              />
              {/* Open-Close 박스 */}
              <rect
                x={centerX - candleWidth / 2}
                y={bodyTop}
                width={candleWidth}
                height={bodyHeight}
                fill={color}
                stroke={color}
                strokeWidth={1}
              />
            </g>
          );
        })}
      </g>
    </g>
  );
};

// BUILD_MARKER_20251219_1100 - XAxis always visible, KRW format, alignment fixed - FINAL
const MacdChart: React.FC<MacdChartProps> = ({ 
  data, 
  candleData: candleDataProp, // ★ candleData 직접 전달 옵션
  title = 'MACD 분석', 
  symbol: symbolProp,
  interval: intervalProp = '1d', 
  onIntervalChange, 
  statistics,
  onScrollLoadMore,
  isLoadingMore = false,
  showLeftButton = true,
  showRightButton = false,
  trendStrength: trendStrengthProp = 0,
  isFullscreen = false,
  onToggleFullscreen,
  backtestSignals = [], // 전략 규칙 기반 백테스트 신호
  onMarkerClick,
  onChartDetailClick,
  onChartHover,
  onChartAreaClick,
  // 기간 이동 컨트롤
  periodLabel,
  onPreviousPeriod,
  onNextPeriod,
  isPreviousPeriodDisabled = false,
  isNextPeriodDisabled = false,
  isPeriodLoading = false,
  hideMainChart = false,
  externalHoverTime,
  onHoverTimeChange,
  onMeasureDoubleClick,
  onCrosshairXChange,
  externalViewRange,
  onViewRangeChange,
}) => {
  // 디버그: 전달받은 props 확인
  React.useEffect(() => {
    if (data.length > 0) {
      console.log(`📥 MacdChart props.data 수신: 첫번째=${data[0]?.date || data[0]?.timestamp}, 마지막=${data[data.length - 1]?.date || data[data.length - 1]?.timestamp}, 총 ${data.length}개, externalViewRange=${JSON.stringify(externalViewRange)}`);
    }
  }, [data.length, externalViewRange]);
  
  // 디버그: externalHoverTime 변경 추적 (렌더링 시점에 직접 출력)
  console.log(`🎯 MacdChart 렌더링 - externalHoverTime: ${externalHoverTime}`);
  
  React.useEffect(() => {
    console.log(`🎯 MacdChart useEffect - externalHoverTime: ${externalHoverTime}`);
  }, [externalHoverTime]);
  
  const theme = useTheme();
  const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
  const { colors } = useChartColors();
  const { settings, order, updateSettings, updateOrder } = useIndicatorVisibility();
  const { setChartData, setChartMargin, setHoveredDate: setPopupHoveredDate, setHoveredIndex: setPopupHoveredIndex, hoveredDate: popupHoveredDate, isPopupOpen } = useIndicatorPopup();
  
  // 보조지표 타이틀 공통 스타일 (화이트 모드 시인성 개선)
  const indicatorTitleStyle = {
    position: 'absolute' as const,
    top: 2,
    left: 5,
    zIndex: 10,
    fontSize: '9px',
    color: 'text.secondary',
    fontWeight: 'bold',
    bgcolor: theme.palette.mode === 'dark' ? 'background.paper' : 'rgba(245, 247, 250, 0.98)',
    px: 0.5,
    py: 0.3,
    borderRadius: 0.5,
    border: theme.palette.mode === 'dark' ? 'none' : '1px solid rgba(0, 0, 0, 0.1)',
    boxShadow: theme.palette.mode === 'dark' ? 'none' : '0 1px 3px rgba(0, 0, 0, 0.08)',
  };
  
  // 보조지표 설정값 (Context에서 실시간 반영)
  const { indicatorSettings, getCustomReferenceLines } = useIndicatorSettings();
  
  // 시간 간격 내부 상태 (props와 동기화)
  const [selectedInterval, setSelectedInterval] = useState(intervalProp);
  
  // props의 interval이 변경되면 내부 상태 동기화
  React.useEffect(() => {
    setSelectedInterval(intervalProp);
  }, [intervalProp]);

  // 시간 간격 변경 핸들러 (StockDetailPanel과 동일한 방식)
  const handleIntervalClick = (newInterval: string) => {
    if (!newInterval) return;
    
    // 즉시 UI 업데이트
    setSelectedInterval(newInterval);
    
    // 부모 컴포넌트에 알림하여 데이터 갱신
    if (onIntervalChange) {
      onIntervalChange(newInterval);
    }
  };
  
  // 돋보기 기능 상태
  const [magnifierEnabled, setMagnifierEnabled] = React.useState(false);
  const [magnifierPosition, setMagnifierPosition] = React.useState<{ x: number; y: number } | null>(null);
  const chartRef = React.useRef<HTMLDivElement>(null);
  const [chartMousePos, setChartMousePos] = React.useState<{ x: number; y: number } | null>(null);
  
  // 드롭다운 메뉴 앵커 상태
  const [intervalMenuAnchor, setIntervalMenuAnchor] = useState<null | HTMLElement>(null);
  const [maMenuAnchor, setMaMenuAnchor] = useState<null | HTMLElement>(null);
  const [ichimokuMenuAnchor, setIchimokuMenuAnchor] = useState<null | HTMLElement>(null);
  const [indicatorMenuAnchor, setIndicatorMenuAnchor] = useState<null | HTMLElement>(null);
  
  // MA 설정 다이얼로그 상태
  const [maSettingsDialogOpen, setMaSettingsDialogOpen] = useState(false);
  
  // 일목균형표 설정 다이얼로그 상태
  const [ichimokuSettingsDialogOpen, setIchimokuSettingsDialogOpen] = useState(false);
  
  // 보조지표 설정 다이얼로그 상태
  const [indicatorSettingsDialogOpen, setIndicatorSettingsDialogOpen] = useState(false);
  
  // 통합 차트 설정 다이얼로그 상태
  const [chartSettingsDialogOpen, setChartSettingsDialogOpen] = useState(false);
  
  // 차트 모드 상태 (기본차트 / 트레이딩뷰)
  const [chartMode, setChartMode] = useState<'basic' | 'tradingView'>('basic');
  
  // 차트 도구 상태
  const [activeTool, setActiveTool] = useState<ChartTool>('crosshair');
  const [showGrid, setShowGrid] = useState(true);
  const [zoomLevel, setZoomLevel] = useState(1);
  
  // 그리기 관련 상태
  const [drawings, setDrawings] = useState<DrawingObject[]>([]);
  const [isDrawing, setIsDrawing] = useState(false);
  const [currentDrawing, setCurrentDrawing] = useState<DrawingObject | null>(null);
  const [drawingHistory, setDrawingHistory] = useState<DrawingObject[][]>([[]]);
  const [historyIndex, setHistoryIndex] = useState(0);
  const drawingLayerRef = useRef<SVGSVGElement>(null);
  
  // 사각형 편집 상태 (리사이즈, 이동)
  const [selectedDrawingId, setSelectedDrawingId] = useState<string | null>(null);
  const [editingMode, setEditingMode] = useState<'none' | 'move' | 'resize-n' | 'resize-s' | 'resize-e' | 'resize-w' | 'resize-ne' | 'resize-nw' | 'resize-se' | 'resize-sw'>('none');
  const [editStartPos, setEditStartPos] = useState<{ x: number; y: number } | null>(null);
  const [editStartPoints, setEditStartPoints] = useState<{ x: number; y: number }[] | null>(null);
  
  // 그리기 객체 스타일 편집 다이얼로그 상태
  const [styleEditDialogOpen, setStyleEditDialogOpen] = useState(false);
  const [editingDrawingId, setEditingDrawingId] = useState<string | null>(null);
  const [editingStyle, setEditingStyle] = useState<{
    color: string;
    strokeWidth: number;
    strokeStyle: 'solid' | 'dashed' | 'dotted';
    fillColor: string;
    fillOpacity: number;
  }>({
    color: '#2962ff',
    strokeWidth: 1,
    strokeStyle: 'solid',
    fillColor: 'rgba(41, 98, 255, 0.2)',
    fillOpacity: 0.2,
  });
  
  // 텍스트 입력 다이얼로그 상태
  const [textDialogOpen, setTextDialogOpen] = useState(false);
  const [textDialogValue, setTextDialogValue] = useState('');
  const [pendingTextPosition, setPendingTextPosition] = useState<{ x: number; y: number } | null>(null);
  
  // 보조지표 팝업 상태
  const [indicatorPopupOpen, setIndicatorPopupOpen] = useState(false);
  const [popupTransparency, setPopupTransparency] = useState(100); // 불투명도 0% = 완전 투명, 100% = 불투명 (기본값: 100%)
  
  // 돋보기 기능 상태
  const [magnifierActive, setMagnifierActive] = useState(false);
  const [magnifierPos, setMagnifierPos] = useState<{ x: number; y: number } | null>(null);
  const magnifierContainerRef = useRef<HTMLDivElement>(null);
  
  // 돋보기 모드 활성화/비활성화
  useEffect(() => {
    setMagnifierActive(activeTool === 'magnifier');
    if (activeTool !== 'magnifier') {
      setMagnifierPos(null);
    }
  }, [activeTool]);
  
  // 전역 마우스 위치 (모든 차트 동기화용) - 전략체크에서 사용하므로 미리 선언
  const [globalHoverIndex, setGlobalHoverIndex] = React.useState<number | null>(null);
  const [globalHoverLabel, setGlobalHoverLabel] = React.useState<string | null>(null);
  const [globalHoverDate, setGlobalHoverDate] = React.useState<string | null>(null);
  const [globalHoverChartX, setGlobalHoverChartX] = React.useState<number | null>(null);
  const [globalHoverChartY, setGlobalHoverChartY] = React.useState<number | null>(null);
  // Y축 상대적 비율 (0 = 상단, 1 = 하단) - 보조지표와 캔들차트 간 동기화용
  const [globalHoverYRatio, setGlobalHoverYRatio] = React.useState<number | null>(null);
  // 차트 영역 offset 정보 저장
  const [chartOffset, setChartOffset] = React.useState<{ top: number; left: number; width: number; height: number } | null>(null);
  
  // ✅ 외부 동기화 활성화 여부 (onHoverTimeChange prop 존재 여부로 결정)
  const isExternalSyncEnabled = !!onHoverTimeChange;
  
  // 외부 호버 시간 동기화용 ref (외부 동기화 활성화 시에만 사용)
  const isExternalUpdateRef = React.useRef(false);
  const lastExternalHoverTimeRef = React.useRef<string | null | undefined>(null);
  
  // 전략체크 팝업 상태
  const [strategyCheckPopupOpen, setStrategyCheckPopupOpen] = useState(false);
  const [strategyCheckTransparency, setStrategyCheckTransparency] = useState(0); // 0% = 불투명, 100% = 완전 투명
  const [strategyCheckPosition, setStrategyCheckPosition] = useState({ x: 100, y: 100 });
  const [strategyCheckDragging, setStrategyCheckDragging] = useState(false);
  const [strategyCheckDragStart, setStrategyCheckDragStart] = useState({ mouseX: 0, mouseY: 0, posX: 0, posY: 0 });
  const [strategyRules, setStrategyRules] = useState<StrategyRuleDto[]>([]);
  const [strategyCheckCurrentTime, setStrategyCheckCurrentTime] = useState(false); // 현재시점 체크
  const [strategyPopupSize, setStrategyPopupSize] = useState({ width: 500, height: 400 });
  const [strategyPopupResizing, setStrategyPopupResizing] = useState(false);
  const [strategyPopupResizeStart, setStrategyPopupResizeStart] = useState({ mouseX: 0, mouseY: 0, width: 0, height: 0 });
  
  // AI 테스트 팝업 상태
  const [aiTestPopupOpen, setAiTestPopupOpen] = useState(false);
  const [aiTestLoading, setAiTestLoading] = useState(false);
  const [aiTestResult, setAiTestResult] = useState<string>('');
  const [aiTestError, setAiTestError] = useState<string | null>(null);
  
  // AI 분석 요청 함수 (비동기로 UI 블로킹 없이 실행)
  const handleAiTest = useCallback(async () => {
    if (!data || data.length === 0) {
      setAiTestError('차트 데이터가 없습니다.');
      setAiTestPopupOpen(true);
      return;
    }
    
    // 이미 로딩 중이면 무시
    if (aiTestLoading) return;
    
    setAiTestLoading(true);
    setAiTestError(null);
    setAiTestResult('');
    // 팝업은 응답 후에만 열도록 수정
    
    try {
      // 최근 100개 캔들 데이터 수집
      const recentData = data.slice(-100);
      
      // 차트 정보 요약
      const latestCandle = recentData[recentData.length - 1];
      const chartSummary = {
        symbol: title || symbolProp || 'Unknown',
        interval: intervalProp || '1d',
        dataCount: recentData.length,
        latestPrice: latestCandle?.close,
        latestDate: latestCandle?.timestamp || latestCandle?.date,
        priceRange: {
          high: Math.max(...recentData.map(d => d.high || d.close)),
          low: Math.min(...recentData.map(d => d.low || d.close)),
        },
        // 보조지표 정보
        indicators: {
          macd: latestCandle?.macdLine,
          signal: latestCandle?.signalLine,
          histogram: latestCandle?.macdHistogram,
          rsi: latestCandle?.rsi,
          stochasticK: latestCandle?.stochasticK,
          stochasticD: latestCandle?.stochasticD,
          adx: latestCandle?.adx,
          cci: latestCandle?.cci13 || latestCandle?.cci,
          sma5: latestCandle?.sma5,
          sma10: latestCandle?.sma10,
          sma20: latestCandle?.sma20,
          sma60: latestCandle?.sma60,
          sma120: latestCandle?.sma120,
        },
        // 최근 가격 변동
        recentPrices: recentData.slice(-10).map(d => ({
          date: d.date,
          open: d.open,
          high: d.high,
          low: d.low,
          close: d.close,
          volume: d.volume,
        })),
      };
      
      // LLM 요청 프롬프트 생성
      const prompt = `당신은 주식/암호화폐 차트 분석 전문가입니다. 다음 차트 데이터를 분석해주세요.

## 종목 정보
- 종목: ${chartSummary.symbol}
- 시간 간격: ${chartSummary.interval}
- 분석 데이터: 최근 ${chartSummary.dataCount}개 캔들

## 현재 가격 정보
- 최신 가격: ${chartSummary.latestPrice?.toLocaleString() || 'N/A'}
- 최신 날짜: ${chartSummary.latestDate || 'N/A'}
- 기간 내 최고가: ${chartSummary.priceRange.high?.toLocaleString() || 'N/A'}
- 기간 내 최저가: ${chartSummary.priceRange.low?.toLocaleString() || 'N/A'}

## 보조지표 현황
- MACD Line: ${chartSummary.indicators.macd?.toFixed(2) || 'N/A'}
- Signal Line: ${chartSummary.indicators.signal?.toFixed(2) || 'N/A'}
- MACD Histogram: ${chartSummary.indicators.histogram?.toFixed(2) || 'N/A'}
- RSI: ${chartSummary.indicators.rsi?.toFixed(2) || 'N/A'}
- Stochastic %K: ${chartSummary.indicators.stochasticK?.toFixed(2) || 'N/A'}
- Stochastic %D: ${chartSummary.indicators.stochasticD?.toFixed(2) || 'N/A'}
- ADX: ${chartSummary.indicators.adx?.toFixed(2) || 'N/A'}
- CCI: ${chartSummary.indicators.cci?.toFixed(2) || 'N/A'}

## 이동평균선
- SMA 5: ${chartSummary.indicators.sma5?.toLocaleString() || 'N/A'}
- SMA 10: ${chartSummary.indicators.sma10?.toLocaleString() || 'N/A'}
- SMA 20: ${chartSummary.indicators.sma20?.toLocaleString() || 'N/A'}
- SMA 60: ${chartSummary.indicators.sma60?.toLocaleString() || 'N/A'}
- SMA 120: ${chartSummary.indicators.sma120?.toLocaleString() || 'N/A'}

## 최근 10개 캔들 데이터
${chartSummary.recentPrices.map((p, i) => `${i + 1}. ${p.date}: 시가 ${p.open?.toLocaleString()}, 고가 ${p.high?.toLocaleString()}, 저가 ${p.low?.toLocaleString()}, 종가 ${p.close?.toLocaleString()}, 거래량 ${p.volume?.toLocaleString()}`).join('\n')}

위 데이터를 바탕으로 다음 내용을 분석해주세요:
1. 현재 추세 분석 (상승/하락/횡보)
2. 주요 보조지표 해석
3. 단기/중기 전망
4. 주의해야 할 지지선/저항선
5. 종합 의견 및 매매 전략 제안

한국어로 답변해주세요.`;

      // LLM 서버로 요청 (Ollama) - 서버에서는 exdev.co.kr:11434 사용
      const ollamaBase = process.env.REACT_APP_OLLAMA_URL || 
        (typeof window !== 'undefined' && window.location.hostname !== 'localhost' && window.location.hostname !== '127.0.0.1'
          ? `${window.location.protocol}//${window.location.hostname}:11434`
          : 'http://localhost:11434');
      const response = await fetch(`${ollamaBase}/api/generate`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          model: 'qwen3:4b', // Qwen3 4B 모델
          prompt: prompt,
          stream: false,
        }),
      });
      
      if (!response.ok) {
        throw new Error(`LLM 서버 응답 오류: ${response.status}`);
      }
      
      const result = await response.json();
      setAiTestResult(result.response || '응답을 받지 못했습니다.');
      // 응답 성공 시 팝업 열기
      setAiTestPopupOpen(true);
      
    } catch (error: any) {
      console.error('AI 분석 오류:', error);
      setAiTestError(error.message || 'LLM 서버 연결 실패. 로컬 LLM 서버(Ollama)가 실행 중인지 확인해주세요.');
      // 에러 시에도 팝업 열어서 오류 메시지 표시
      setAiTestPopupOpen(true);
    } finally {
      setAiTestLoading(false);
    }
  }, [data, title, symbolProp, intervalProp, aiTestLoading]);
  
  // Web Worker를 통한 전략 평가 결과
  interface StrategyEvaluationResult {
    strategyId: number;
    strategyName: string;
    enabled: boolean;
    evaluation: {
      buy: { total: number; matched: number; percentage: number; conditions: { description: string; matched: boolean }[] };
      sell: { total: number; matched: number; percentage: number; conditions: { description: string; matched: boolean }[] };
    };
  }
  const [workerEvaluationResults, setWorkerEvaluationResults] = useState<StrategyEvaluationResult[]>([]);
  const [workerReferenceTime, setWorkerReferenceTime] = useState<string>('');
  const [isEvaluating, setIsEvaluating] = useState(false);
  const strategyWorkerRef = useRef<Worker | null>(null);
  const lastEvaluatedIndexRef = useRef<number>(-1);
  
  // 전략 체크를 위한 추가 데이터 로드 상태
  const [strategyChartData, setStrategyChartData] = useState<any[]>([]);
  const [isLoadingStrategyData, setIsLoadingStrategyData] = useState(false);
  const [strategyDataLoadError, setStrategyDataLoadError] = useState<string | null>(null);
  const strategyDataLoadedRef = useRef(false);
  
  // 심볼 추출 (props에서 전달받거나 title에서 추출)
  const extractSymbol = useCallback(() => {
    if (symbolProp) return symbolProp;
    // title에서 심볼 추출 시도 (예: "비트코인 (KRW-BTC)" 형식)
    const match = title?.match(/\(?(KRW-[A-Z0-9]+)\)?/);
    if (match) return match[1];
    // data에서 심볼 추출 시도 (MacdResult에는 symbol이 없으므로 any 타입으로 체크)
    if (data.length > 0 && (data[0] as any).symbol) return (data[0] as any).symbol;
    return null;
  }, [symbolProp, title, data]);
  
  // 전략 체크를 위한 데이터 로드 (보조지표 계산에 필요한 충분한 데이터)
  const loadStrategyCheckData = useCallback(async () => {
    const symbol = extractSymbol();
    if (!symbol || !strategyCheckPopupOpen) return;
    
    // 이미 충분한 데이터가 있으면 로드하지 않음 (최소 200개 이상)
    if (data.length >= 200 && strategyDataLoadedRef.current) {
      setStrategyChartData(data);
      return;
    }
    
    setIsLoadingStrategyData(true);
    setStrategyDataLoadError(null);
    
    try {
      console.log(`📊 전략 체크용 데이터 로드: ${symbol} ${intervalProp}`);
      
      // ✅ 보조지표 설정 로드
      const indicatorSettings = loadIndicatorSettings();
      
      // 업비트 API를 통해 충분한 데이터 요청 (최소 300개) + 보조지표 파라미터 포함
      const response = await upbitApiService.analyzeCryptoMacd({
        symbol,
        interval: intervalProp,
        count: 300, // 보조지표 계산에 충분한 데이터
        // ✅ 모든 보조지표 파라미터 전송
        fastPeriod: indicatorSettings.macdFastPeriod || 12,
        slowPeriod: indicatorSettings.macdSlowPeriod || 26,
        signalPeriod: indicatorSettings.macdSignalPeriod || 9,
        stochasticKPeriod: indicatorSettings.stochasticKPeriod || 12,
        stochasticDPeriod: indicatorSettings.stochasticDPeriod || 5,
        stochasticSlowing: indicatorSettings.stochasticSlowing || 5,
        rsiPeriod: indicatorSettings.rsiPeriod || 9,
        cciPeriod: indicatorSettings.cciPeriod || 13,
        cciSignalPeriod: indicatorSettings.cciSignalPeriod || 10,
        williamsRPeriod: indicatorSettings.williamsRPeriod || 11,
        adxPeriod: indicatorSettings.adxPeriod || 11,
        dmiPeriod: indicatorSettings.dmiPeriod || 14,
        adxSmoothing: indicatorSettings.adxSmoothing || 14,
        bwiPeriod: indicatorSettings.bwiPeriod || 20,
        obvPeriod: indicatorSettings.obvPeriod || 9,
        obvSignalPeriod: indicatorSettings.obvSignalPeriod || 9,
        trixPeriod: indicatorSettings.trixPeriod || 12,
        trixSignalPeriod: indicatorSettings.trixSignalPeriod || 9,
        volumeOscShortPeriod: indicatorSettings.volumeOscShortPeriod || 10,
        volumeOscLongPeriod: indicatorSettings.volumeOscLongPeriod || 20,
        vrPeriod: indicatorSettings.vrPeriod || 10,
        newPsychologicalPeriod: indicatorSettings.newPsychologicalPeriod || 5,
        investPsychologicalPeriod: indicatorSettings.investPsychologicalPeriod || 10,
        disparityUltraShortPeriod: indicatorSettings.disparityUltraShortPeriod || 5,
        disparityShortPeriod: indicatorSettings.disparityShortPeriod || 10,
        disparityMidPeriod: indicatorSettings.disparityMidPeriod || 20,
        disparityLongPeriod: indicatorSettings.disparityLongPeriod || 60,
        bollingerPeriod: settings.bollingerPeriod || 20,
        bollingerStdDev: settings.bollingerStdDev || 2.0,
      });
      
      console.log(`✅ 전략 체크용 데이터 로드 완료: ${response.data.length}개`);
      setStrategyChartData(response.data);
      strategyDataLoadedRef.current = true;
    } catch (error) {
      console.error('전략 체크용 데이터 로드 실패:', error);
      setStrategyDataLoadError('데이터 로드 실패. 기존 데이터를 사용합니다.');
      // 실패 시 기존 데이터 사용
      setStrategyChartData(data);
    } finally {
      setIsLoadingStrategyData(false);
    }
  }, [extractSymbol, strategyCheckPopupOpen, data, intervalProp]);
  
  // 전략 체크 팝업 열릴 때 데이터 로드
  useEffect(() => {
    if (strategyCheckPopupOpen) {
      loadStrategyCheckData();
    } else {
      // 팝업 닫힐 때 플래그 리셋
      strategyDataLoadedRef.current = false;
    }
  }, [strategyCheckPopupOpen, loadStrategyCheckData]);
  
  // 인터벌 변경 시 데이터 다시 로드
  useEffect(() => {
    if (strategyCheckPopupOpen) {
      strategyDataLoadedRef.current = false;
      loadStrategyCheckData();
    }
  }, [intervalProp]);
  
  // 전략 체크에 사용할 데이터 (로드된 데이터 우선, chartData 선언 전이므로 data 사용)
  const strategyCheckChartData = strategyChartData.length > 0 ? strategyChartData : data;
  
  // Web Worker 초기화 및 메시지 핸들링
  useEffect(() => {
    // Worker 생성
    const workerCode = `
      // 단일 조건 평가 함수
      function evaluateCondition(condition, currentData, prevData) {
        if (!currentData || condition.enabled === false) return false;
        const { indicatorType, conditionType, targetValue, targetValue2 } = condition;
        try {
          switch (indicatorType) {
            case 'RSI': {
              const rsiValue = currentData.rsi;
              const prevRsi = prevData?.rsi;
              if (rsiValue == null) return false;
              switch (conditionType) {
                case 'ABOVE': return rsiValue > (targetValue || 50);
                case 'BELOW': return rsiValue < (targetValue || 50);
                case 'CROSS_UP': return prevRsi != null && prevRsi <= (targetValue || 50) && rsiValue > (targetValue || 50);
                case 'CROSS_DOWN': return prevRsi != null && prevRsi >= (targetValue || 50) && rsiValue < (targetValue || 50);
                case 'OVERBOUGHT': return rsiValue >= (targetValue || 70);
                case 'OVERSOLD': return rsiValue <= (targetValue || 30);
                case 'RANGE': return rsiValue >= (targetValue || 30) && rsiValue <= (targetValue2 || 70);
                default: return false;
              }
            }
            case 'STOCHASTIC':
            case 'STOCHASTIC_K':
            case 'STOCHASTIC_D': {
              const kValue = currentData.stochasticK;
              const dValue = currentData.stochasticD;
              const prevK = prevData?.stochasticK;
              const prevD = prevData?.stochasticD;
              const value = indicatorType === 'STOCHASTIC_D' ? dValue : kValue;
              const prevValue = indicatorType === 'STOCHASTIC_D' ? prevD : prevK;
              if (value == null) return false;
              switch (conditionType) {
                case 'ABOVE': return value > (targetValue || 50);
                case 'BELOW': return value < (targetValue || 50);
                case 'CROSS_UP': return prevValue != null && prevValue <= (targetValue || 50) && value > (targetValue || 50);
                case 'CROSS_DOWN': return prevValue != null && prevValue >= (targetValue || 50) && value < (targetValue || 50);
                case 'GOLDEN_CROSS': return prevK != null && prevD != null && kValue != null && dValue != null && prevK <= prevD && kValue > dValue;
                case 'DEAD_CROSS': return prevK != null && prevD != null && kValue != null && dValue != null && prevK >= prevD && kValue < dValue;
                case 'OVERBOUGHT': return value >= (targetValue || 80);
                case 'OVERSOLD': return value <= (targetValue || 20);
                default: return false;
              }
            }
            case 'MACD':
            case 'MACD_LINE':
            case 'MACD_SIGNAL':
            case 'MACD_HISTOGRAM': {
              const macdLine = currentData.macdLine;
              const signalLine = currentData.signalLine;
              const histogram = currentData.macdHistogram;
              const prevMacd = prevData?.macdLine;
              const prevSignal = prevData?.signalLine;
              const prevHist = prevData?.macdHistogram;
              let value = null, prevValue = null;
              if (indicatorType === 'MACD_SIGNAL') { value = signalLine; prevValue = prevSignal; }
              else if (indicatorType === 'MACD_HISTOGRAM') { value = histogram; prevValue = prevHist; }
              else { value = macdLine; prevValue = prevMacd; }
              if (value == null) return false;
              switch (conditionType) {
                case 'ABOVE': return value > (targetValue || 0);
                case 'BELOW': return value < (targetValue || 0);
                case 'CROSS_UP': return prevValue != null && prevValue <= (targetValue || 0) && value > (targetValue || 0);
                case 'CROSS_DOWN': return prevValue != null && prevValue >= (targetValue || 0) && value < (targetValue || 0);
                case 'GOLDEN_CROSS': return prevMacd != null && prevSignal != null && macdLine != null && signalLine != null && prevMacd <= prevSignal && macdLine > signalLine;
                case 'DEAD_CROSS': return prevMacd != null && prevSignal != null && macdLine != null && signalLine != null && prevMacd >= prevSignal && macdLine < signalLine;
                case 'HISTOGRAM_POSITIVE': return histogram != null && histogram > 0;
                case 'HISTOGRAM_NEGATIVE': return histogram != null && histogram < 0;
                default: return false;
              }
            }
            case 'ADX': {
              const adxValue = currentData.adx;
              if (adxValue == null) return false;
              switch (conditionType) {
                case 'ABOVE': return adxValue > (targetValue || 25);
                case 'BELOW': return adxValue < (targetValue || 25);
                case 'STRONG_TREND': return adxValue >= (targetValue || 25);
                case 'WEAK_TREND': return adxValue < (targetValue || 20);
                default: return false;
              }
            }
            case 'BOLLINGER':
            case 'BOLLINGER_UPPER':
            case 'BOLLINGER_LOWER': {
              const close = currentData.close;
              const upper = currentData.bollingerUpper;
              const lower = currentData.bollingerLower;
              const middle = currentData.bollingerMiddle;
              if (close == null) return false;
              switch (conditionType) {
                case 'ABOVE_UPPER': return upper != null && close > upper;
                case 'BELOW_LOWER': return lower != null && close < lower;
                case 'INSIDE_BANDS': return upper != null && lower != null && close <= upper && close >= lower;
                case 'ABOVE_MIDDLE': return middle != null && close > middle;
                case 'BELOW_MIDDLE': return middle != null && close < middle;
                default: return false;
              }
            }
            case 'MA':
            case 'MA5':
            case 'MA10':
            case 'MA20':
            case 'MA60':
            case 'MA120': {
              const close = currentData.close;
              const prevClose = prevData?.close;
              let maValue = null, prevMa = null;
              switch (indicatorType) {
                case 'MA5': maValue = currentData.sma5 || currentData.ma5; prevMa = prevData?.sma5 || prevData?.ma5; break;
                case 'MA10': maValue = currentData.sma10 || currentData.ma10; prevMa = prevData?.sma10 || prevData?.ma10; break;
                case 'MA20': maValue = currentData.sma20 || currentData.ma20; prevMa = prevData?.sma20 || prevData?.ma20; break;
                case 'MA60': maValue = currentData.sma60 || currentData.ma60; prevMa = prevData?.sma60 || prevData?.ma60; break;
                case 'MA120': maValue = currentData.sma120 || currentData.ma120; prevMa = prevData?.sma120 || prevData?.ma120; break;
                default: maValue = currentData.sma20 || currentData.ma20; prevMa = prevData?.sma20 || prevData?.ma20;
              }
              if (close == null || maValue == null) return false;
              switch (conditionType) {
                case 'ABOVE': return close > maValue;
                case 'BELOW': return close < maValue;
                case 'CROSS_UP': return prevClose != null && prevMa != null && prevClose <= prevMa && close > maValue;
                case 'CROSS_DOWN': return prevClose != null && prevMa != null && prevClose >= prevMa && close < maValue;
                case 'UPTREND': return maValue > (prevMa || 0);
                case 'DOWNTREND': return maValue < (prevMa || Infinity);
                default: return false;
              }
            }
            case 'PRICE': {
              const close = currentData.close;
              const prevClose = prevData?.close;
              if (close == null) return false;
              switch (conditionType) {
                case 'ABOVE': return close > (targetValue || 0);
                case 'BELOW': return close < (targetValue || 0);
                case 'CHANGE_PERCENT': return prevClose != null && ((close - prevClose) / prevClose) * 100 >= (targetValue || 0);
                default: return false;
              }
            }
            case 'VOLUME': {
              const volume = currentData.volume;
              const prevVolume = prevData?.volume;
              if (volume == null) return false;
              switch (conditionType) {
                case 'ABOVE': return volume > (targetValue || 0);
                case 'INCREASING': return prevVolume != null && volume > prevVolume;
                default: return false;
              }
            }
            default: return false;
          }
        } catch { return false; }
      }

      function evaluateConditionGroup(group, currentData, prevData) {
        const results = [];
        let total = 0, matched = 0;
        const conditions = group.conditions || group.strategyConditions || [];
        for (const condition of conditions) {
          if (condition.enabled === false) continue;
          const isMatched = evaluateCondition(condition, currentData, prevData);
          const description = condition.description || (condition.indicatorType + ' ' + condition.conditionType + (condition.targetValue ? ' ' + condition.targetValue : ''));
          results.push({ description, matched: isMatched });
          total++;
          if (isMatched) matched++;
        }
        const childGroups = group.childGroups || group.children || [];
        for (const childGroup of childGroups) {
          const childResult = evaluateConditionGroup(childGroup, currentData, prevData);
          total += childResult.total;
          matched += childResult.matched;
          results.push(...childResult.conditions);
        }
        return { total, matched, conditions: results };
      }

      function evaluateStrategyRule(rule, currentData, prevData) {
        const buyResult = { total: 0, matched: 0, percentage: 0, conditions: [] };
        const sellResult = { total: 0, matched: 0, percentage: 0, conditions: [] };
        
        // 이동평균선 값 가져오는 헬퍼
        function getMAValue(data, period) {
          if (!data) return null;
          // smaXX 필드 우선, 없으면 maXX
          return data['sma' + period] || data['ma' + period] || null;
        }
        
        // 매수 이동평균선 조건 1
        if (rule.enableMaBuy) {
          buyResult.total++;
          const shortPeriod = rule.maShortPeriod1Buy || 20;
          const longPeriod = rule.maLongPeriod1Buy || 60;
          const shortMA = getMAValue(currentData, shortPeriod);
          const longMA = getMAValue(currentData, longPeriod);
          const prevShortMA = getMAValue(prevData, shortPeriod);
          const prevLongMA = getMAValue(prevData, longPeriod);
          const isGolden = rule.maCrossType1Buy === 'GOLDEN_CROSS';
          var matched = false;
          if (shortMA != null && longMA != null && prevShortMA != null && prevLongMA != null) {
            matched = isGolden 
              ? (prevShortMA < prevLongMA && shortMA >= longMA)
              : (prevShortMA > prevLongMA && shortMA <= longMA);
          }
          buyResult.conditions.push({ 
            description: 'MA' + shortPeriod + '/' + longPeriod + ' ' + (isGolden ? '골든' : '데드') + '크로스', 
            matched: matched 
          });
          if (matched) buyResult.matched++;
        }
        
        // 매수 이동평균선 조건 2
        if (rule.enableMaCondition2Buy) {
          buyResult.total++;
          const shortPeriod = rule.maShortPeriod2Buy || 5;
          const longPeriod = rule.maLongPeriod2Buy || 20;
          const shortMA = getMAValue(currentData, shortPeriod);
          const longMA = getMAValue(currentData, longPeriod);
          const prevShortMA = getMAValue(prevData, shortPeriod);
          const prevLongMA = getMAValue(prevData, longPeriod);
          const isGolden = rule.maCrossType2Buy === 'GOLDEN_CROSS';
          var matched2 = false;
          if (shortMA != null && longMA != null && prevShortMA != null && prevLongMA != null) {
            matched2 = isGolden 
              ? (prevShortMA < prevLongMA && shortMA >= longMA)
              : (prevShortMA > prevLongMA && shortMA <= longMA);
          }
          buyResult.conditions.push({ 
            description: 'MA' + shortPeriod + '/' + longPeriod + ' ' + (isGolden ? '골든' : '데드') + '크로스', 
            matched: matched2 
          });
          if (matched2) buyResult.matched++;
        }
        
        // 매수 보조지표 조건 그룹 (buyConditionGroups가 있으면 항상 평가)
        if (rule.buyConditionGroups && rule.buyConditionGroups.length > 0) {
          for (const group of rule.buyConditionGroups) {
            const result = evaluateConditionGroup(group, currentData, prevData);
            buyResult.total += result.total;
            buyResult.matched += result.matched;
            buyResult.conditions.push(...result.conditions);
          }
        }
        
        // 매도 이동평균선 조건 1
        if (rule.enableMaSell) {
          sellResult.total++;
          const shortPeriod = rule.maShortPeriod1Sell || 20;
          const longPeriod = rule.maLongPeriod1Sell || 60;
          const shortMA = getMAValue(currentData, shortPeriod);
          const longMA = getMAValue(currentData, longPeriod);
          const prevShortMA = getMAValue(prevData, shortPeriod);
          const prevLongMA = getMAValue(prevData, longPeriod);
          const isDead = rule.maCrossType1Sell === 'DEAD_CROSS';
          var matchedSell = false;
          if (shortMA != null && longMA != null && prevShortMA != null && prevLongMA != null) {
            matchedSell = isDead 
              ? (prevShortMA > prevLongMA && shortMA <= longMA)
              : (prevShortMA < prevLongMA && shortMA >= longMA);
          }
          sellResult.conditions.push({ 
            description: 'MA' + shortPeriod + '/' + longPeriod + ' ' + (isDead ? '데드' : '골든') + '크로스', 
            matched: matchedSell 
          });
          if (matchedSell) sellResult.matched++;
        }
        
        // 매도 이동평균선 조건 2
        if (rule.enableMaCondition2Sell) {
          sellResult.total++;
          const shortPeriod = rule.maShortPeriod2Sell || 5;
          const longPeriod = rule.maLongPeriod2Sell || 20;
          const shortMA = getMAValue(currentData, shortPeriod);
          const longMA = getMAValue(currentData, longPeriod);
          const prevShortMA = getMAValue(prevData, shortPeriod);
          const prevLongMA = getMAValue(prevData, longPeriod);
          const isDead = rule.maCrossType2Sell === 'DEAD_CROSS';
          var matchedSell2 = false;
          if (shortMA != null && longMA != null && prevShortMA != null && prevLongMA != null) {
            matchedSell2 = isDead 
              ? (prevShortMA > prevLongMA && shortMA <= longMA)
              : (prevShortMA < prevLongMA && shortMA >= longMA);
          }
          sellResult.conditions.push({ 
            description: 'MA' + shortPeriod + '/' + longPeriod + ' ' + (isDead ? '데드' : '골든') + '크로스', 
            matched: matchedSell2 
          });
          if (matchedSell2) sellResult.matched++;
        }
        
        // 매도 보조지표 조건 그룹 (sellConditionGroups가 있으면 항상 평가)
        if (rule.sellConditionGroups && rule.sellConditionGroups.length > 0) {
          for (const group of rule.sellConditionGroups) {
            const result = evaluateConditionGroup(group, currentData, prevData);
            sellResult.total += result.total;
            sellResult.matched += result.matched;
            sellResult.conditions.push(...result.conditions);
          }
        }
        
        buyResult.percentage = buyResult.total > 0 ? Math.round((buyResult.matched / buyResult.total) * 100) : 0;
        sellResult.percentage = sellResult.total > 0 ? Math.round((sellResult.matched / sellResult.total) * 100) : 0;
        return { buy: buyResult, sell: sellResult };
      }

      self.onmessage = function(e) {
        const { type, strategies, currentData, prevData, currentIndex, referenceTime } = e.data;
        if (type !== 'evaluate') return;
        try {
          const results = strategies.map(function(strategy) {
            return {
              strategyId: strategy.id,
              strategyName: strategy.name,
              enabled: strategy.enabled,
              evaluation: evaluateStrategyRule(strategy, currentData, prevData)
            };
          });
          self.postMessage({ type: 'result', results: results, currentIndex: currentIndex, referenceTime: referenceTime });
        } catch (error) {
          self.postMessage({ type: 'error', error: String(error), currentIndex: currentIndex, referenceTime: referenceTime });
        }
      };
    `;
    
    const blob = new Blob([workerCode], { type: 'application/javascript' });
    const workerUrl = URL.createObjectURL(blob);
    const worker = new Worker(workerUrl);
    
    worker.onmessage = (e) => {
      const { type, results, referenceTime } = e.data;
      if (type === 'result') {
        // 디버깅: 평가 결과 출력 (더 명확하게)
        console.log('✅ 전략 평가 결과:', {
          referenceTime,
          resultCount: results?.length
        });
        // 첫 번째 전략의 상세 결과 출력
        if (results && results.length > 0) {
          const first = results[0];
          console.log('📋 첫 번째 전략 상세:', {
            name: first.strategyName,
            buyTotal: first.evaluation.buy.total,
            buyMatched: first.evaluation.buy.matched,
            buyPct: first.evaluation.buy.percentage,
            buyConditions: first.evaluation.buy.conditions?.length || 0,
            sellTotal: first.evaluation.sell.total,
            sellMatched: first.evaluation.sell.matched,
            sellPct: first.evaluation.sell.percentage,
            sellConditions: first.evaluation.sell.conditions?.length || 0
          });
        }
        setWorkerEvaluationResults(results);
        setWorkerReferenceTime(referenceTime);
        setIsEvaluating(false);
      } else if (type === 'error') {
        console.error('Worker 평가 오류:', e.data.error);
        setIsEvaluating(false);
      }
    };
    
    worker.onerror = (error) => {
      console.error('Worker 오류:', error);
      setIsEvaluating(false);
    };
    
    strategyWorkerRef.current = worker;
    
    return () => {
      worker.terminate();
      URL.revokeObjectURL(workerUrl);
      strategyWorkerRef.current = null;
    };
  }, []);
  
  // 전략 평가 요청 (쓰로틀링 적용)
  const requestStrategyEvaluation = useCallback(() => {
    const dataToUse = strategyCheckChartData;
    
    // 디버깅: 조건 체크
    console.log('🔍 전략 평가 조건 체크:', {
      popupOpen: strategyCheckPopupOpen,
      hasWorker: !!strategyWorkerRef.current,
      strategyCount: strategyRules.length,
      dataCount: dataToUse.length,
      isLoading: isLoadingStrategyData,
      globalHoverDate,
      currentTime: strategyCheckCurrentTime
    });
    
    if (!strategyCheckPopupOpen || !strategyWorkerRef.current || strategyRules.length === 0 || dataToUse.length === 0 || isLoadingStrategyData) {
      console.log('⚠️ 전략 평가 조건 미충족 - early return');
      return;
    }
    
    // globalHoverIndex 기반으로 strategyCheckChartData에서 인덱스 찾기
    let currentIndex: number;
    if (strategyCheckCurrentTime) {
      currentIndex = dataToUse.length - 1;
    } else if (globalHoverIndex !== null && globalHoverIndex >= 0) {
      // globalHoverIndex는 화면에 표시된 chartData의 인덱스
      // strategyCheckChartData에서 비율로 대략적인 위치 계산
      // 또는 마지막 N개 데이터 중 해당 위치 사용
      const dataLength = data.length;
      const displayRatio = dataLength > 0 ? globalHoverIndex / Math.min(120, dataLength) : 0;
      const estimatedIndex = Math.min(
        Math.floor(dataToUse.length * (1 - (1 - displayRatio) * 0.12)), // 마지막 12% 범위 내
        dataToUse.length - 1
      );
      
      // 더 정확한 방법: globalHoverIndex를 data 끝에서부터의 오프셋으로 사용
      const offsetFromEnd = Math.min(120, dataLength) - globalHoverIndex - 1;
      currentIndex = Math.max(0, dataToUse.length - 1 - offsetFromEnd);
      
      console.log('🔎 인덱스 찾기:', {
        globalHoverIndex,
        offsetFromEnd,
        currentIndex,
        dataToUseLength: dataToUse.length
      });
    } else {
      currentIndex = dataToUse.length - 1;
    }
    
    // 동일한 인덱스면 재평가하지 않음 (마우스 이동 시에도 평가하도록 조건 완화)
    if (currentIndex === lastEvaluatedIndexRef.current && !isEvaluating && currentIndex !== dataToUse.length - 1) {
      console.log('⏭️ 동일 인덱스 스킵:', { currentIndex, lastIndex: lastEvaluatedIndexRef.current });
      return;
    }
    
    const currentData = dataToUse[currentIndex];
    const prevData = currentIndex > 0 ? dataToUse[currentIndex - 1] : null;
    
    if (!currentData) return;
    
    const referenceTime = currentData.date || new Date().toLocaleString();
    
    // 디버깅: 전략 평가 요청 정보 출력
    console.log('📊 전략 평가 요청:', {
      currentIndex,
      date: currentData.date,
      sma5: currentData.sma5,
      sma10: currentData.sma10,
      sma20: currentData.sma20,
      sma60: currentData.sma60,
      rsi: currentData.rsi,
      macdLine: currentData.macdLine,
      strategyCount: strategyRules.length,
      firstStrategy: strategyRules[0] ? {
        name: strategyRules[0].name,
        enableMaBuy: strategyRules[0].enableMaBuy,
        enableMaSell: strategyRules[0].enableMaSell,
        buyGroupsCount: strategyRules[0].buyConditionGroups?.length,
        sellGroupsCount: strategyRules[0].sellConditionGroups?.length
      } : null
    });
    
    lastEvaluatedIndexRef.current = currentIndex;
    setIsEvaluating(true);
    
    strategyWorkerRef.current.postMessage({
      type: 'evaluate',
      strategies: strategyRules,
      currentData,
      prevData,
      currentIndex,
      referenceTime
    });
  }, [strategyCheckPopupOpen, strategyRules, strategyCheckChartData, strategyCheckCurrentTime, globalHoverDate, globalHoverIndex, data, isEvaluating, isLoadingStrategyData]);
  
  // 전략 평가 트리거 (쓰로틀링 150ms)
  const throttledEvaluationRef = useRef<NodeJS.Timeout | null>(null);
  useEffect(() => {
    if (!strategyCheckPopupOpen || isLoadingStrategyData) return;
    
    // 쓰로틀링: 150ms 간격으로만 평가 요청
    if (throttledEvaluationRef.current) {
      clearTimeout(throttledEvaluationRef.current);
    }
    
    throttledEvaluationRef.current = setTimeout(() => {
      requestStrategyEvaluation();
    }, 150);
    
    return () => {
      if (throttledEvaluationRef.current) {
        clearTimeout(throttledEvaluationRef.current);
      }
    };
  }, [strategyCheckPopupOpen, globalHoverDate, globalHoverIndex, strategyCheckCurrentTime, strategyCheckChartData.length, requestStrategyEvaluation, isLoadingStrategyData]);
  
  // 전략 팝업 드래그/리사이즈를 위한 document 레벨 이벤트 핸들러
  useEffect(() => {
    if (!strategyCheckDragging && !strategyPopupResizing) return;
    
    const handleMouseMove = (e: MouseEvent) => {
      if (strategyCheckDragging) {
        const dx = e.clientX - strategyCheckDragStart.mouseX;
        const dy = e.clientY - strategyCheckDragStart.mouseY;
        setStrategyCheckPosition({
          x: strategyCheckDragStart.posX + dx,
          y: strategyCheckDragStart.posY + dy,
        });
      }
      if (strategyPopupResizing) {
        const dx = e.clientX - strategyPopupResizeStart.mouseX;
        const dy = e.clientY - strategyPopupResizeStart.mouseY;
        setStrategyPopupSize({
          width: Math.max(300, strategyPopupResizeStart.width + dx),
          height: Math.max(200, strategyPopupResizeStart.height + dy),
        });
      }
    };
    
    const handleMouseUp = () => {
      setStrategyCheckDragging(false);
      setStrategyPopupResizing(false);
    };
    
    const handleTouchMove = (e: TouchEvent) => {
      if (e.touches.length !== 1) return;
      const touch = e.touches[0];
      if (strategyCheckDragging) {
        e.preventDefault();
        const dx = touch.clientX - strategyCheckDragStart.mouseX;
        const dy = touch.clientY - strategyCheckDragStart.mouseY;
        setStrategyCheckPosition({
          x: strategyCheckDragStart.posX + dx,
          y: strategyCheckDragStart.posY + dy,
        });
      }
      if (strategyPopupResizing) {
        e.preventDefault();
        const dx = touch.clientX - strategyPopupResizeStart.mouseX;
        const dy = touch.clientY - strategyPopupResizeStart.mouseY;
        setStrategyPopupSize({
          width: Math.max(300, strategyPopupResizeStart.width + dx),
          height: Math.max(200, strategyPopupResizeStart.height + dy),
        });
      }
    };
    
    const handleTouchEnd = () => {
      setStrategyCheckDragging(false);
      setStrategyPopupResizing(false);
    };
    
    document.addEventListener('mousemove', handleMouseMove);
    document.addEventListener('mouseup', handleMouseUp);
    document.addEventListener('touchmove', handleTouchMove, { passive: false });
    document.addEventListener('touchend', handleTouchEnd);
    
    return () => {
      document.removeEventListener('mousemove', handleMouseMove);
      document.removeEventListener('mouseup', handleMouseUp);
      document.removeEventListener('touchmove', handleTouchMove);
      document.removeEventListener('touchend', handleTouchEnd);
    };
  }, [strategyCheckDragging, strategyPopupResizing, strategyCheckDragStart, strategyPopupResizeStart]);
  
  // 전략 규칙 목록 로드
  useEffect(() => {
    const loadStrategyRules = async () => {
      try {
        const rules = await getStrategyRules();
        console.log('전략 규칙 로드됨:', rules);
        setStrategyRules(rules.filter(r => r.enabled));
      } catch (error) {
        console.error('전략 규칙 로드 실패:', error);
      }
    };
    loadStrategyRules();
  }, []);
  
  // 전략 조건 평가 함수
  const evaluateCondition = (
    condition: StrategyConditionDto,
    currentData: any,
    prevData: any | null
  ): boolean => {
    if (!currentData) return false;
    
    // enabled 필드가 있으면 체크
    if ((condition as any).enabled === false) return false;
    
    const { indicatorType, conditionType, targetValue, period } = condition;
    
    // 지표 값 가져오기
    const getValue = (type: string, data: any): number | null => {
      switch (type) {
        case 'RSI': return data.rsi;
        case 'MACD': return data.macd;
        case 'MACD_SIGNAL': return data.macdSignal;
        case 'MACD_HISTOGRAM': return data.macdHistogram;
        case 'STOCHASTIC_K': return data.stochasticK;
        case 'STOCHASTIC_D': return data.stochasticD;
        case 'ADX': return data.adx;
        case 'PLUS_DI': return data.plusDI;
        case 'MINUS_DI': return data.minusDI;
        case 'CCI': return data.cci;
        case 'WILLIAMS_R': return data.williamsR;
        case 'OBV': return data.obv;
        case 'TRIX': return data.trix;
        case 'DISPARITY': return data.disparityShort;  // 기본값은 단기(20일) 이격도
        case 'BWI': return data.bwi;
        case 'VOLUME': return data.volume;
        case 'PRICE': return data.close;
        case 'MA': return data[`ma${period}`] || data.ma20;
        case 'BOLLINGER_UPPER': return data.bollingerUpper;
        case 'BOLLINGER_MIDDLE': return data.bollingerMiddle;
        case 'BOLLINGER_LOWER': return data.bollingerLower;
        default: return null;
      }
    };
    
    const currentValue = getValue(indicatorType, currentData);
    const prevValue = prevData ? getValue(indicatorType, prevData) : null;
    
    if (currentValue == null) return false;
    
    switch (conditionType) {
      case 'ABOVE':
        return currentValue > (targetValue || 0);
      case 'BELOW':
        return currentValue < (targetValue || 0);
      case 'CROSS_UP':
        return prevValue != null && prevValue < (targetValue || 0) && currentValue >= (targetValue || 0);
      case 'CROSS_DOWN':
        return prevValue != null && prevValue > (targetValue || 0) && currentValue <= (targetValue || 0);
      case 'INCREASING':
        return prevValue != null && currentValue > prevValue;
      case 'DECREASING':
        return prevValue != null && currentValue < prevValue;
      case 'GOLDEN_CROSS':
        // MACD와 시그널 또는 두 MA의 골든크로스
        if (indicatorType === 'MACD') {
          const currSignal = currentData.macdSignal;
          const prevSignal = prevData?.macdSignal;
          return prevSignal != null && currSignal != null && 
                 prevValue! < prevSignal && currentValue >= currSignal;
        }
        return false;
      case 'DEAD_CROSS':
        if (indicatorType === 'MACD') {
          const currSignal = currentData.macdSignal;
          const prevSignal = prevData?.macdSignal;
          return prevSignal != null && currSignal != null && 
                 prevValue! > prevSignal && currentValue <= currSignal;
        }
        return false;
      case 'OVERBOUGHT':
        return currentValue >= (targetValue || 70);
      case 'OVERSOLD':
        return currentValue <= (targetValue || 30);
      case 'ZERO_CROSS_UP':
        return prevValue != null && prevValue < 0 && currentValue >= 0;
      case 'ZERO_CROSS_DOWN':
        return prevValue != null && prevValue > 0 && currentValue <= 0;
      default:
        return false;
    }
  };
  
  // 조건 그룹 평가 함수
  const evaluateConditionGroup = (
    group: ConditionGroupDto,
    currentData: any,
    prevData: any | null
  ): { total: number; matched: number; conditions: { description: string; matched: boolean }[] } => {
    const results: { description: string; matched: boolean }[] = [];
    let total = 0;
    let matched = 0;
    
    // 그룹 자체에 조건 정보가 있는 경우 (일부 API 응답 형식)
    const groupAny = group as any;
    
    // 개별 조건 평가 (conditions 배열)
    const conditions = group.conditions || groupAny.strategyConditions || [];
    for (const condition of conditions) {
      // enabled 필드 체크
      if (condition.enabled === false) continue;
      
      const isMatched = evaluateCondition(condition, currentData, prevData);
      const description = condition.description || 
        `${condition.indicatorType} ${condition.conditionType}${condition.targetValue ? ` ${condition.targetValue}` : ''}`;
      results.push({ description, matched: isMatched });
      total++;
      if (isMatched) matched++;
    }
    
    // 자식 그룹 평가
    const childGroups = group.childGroups || groupAny.children || [];
    for (const childGroup of childGroups) {
      const childResult = evaluateConditionGroup(childGroup, currentData, prevData);
      total += childResult.total;
      matched += childResult.matched;
      results.push(...childResult.conditions);
    }
    
    return { total, matched, conditions: results };
  };
  
  // 전략 규칙 평가 (매수/매도 분리)
  const evaluateStrategyRule = (
    rule: StrategyRuleDto,
    currentData: any,
    prevData: any | null
  ): { 
    buy: { total: number; matched: number; conditions: { description: string; matched: boolean }[] };
    sell: { total: number; matched: number; conditions: { description: string; matched: boolean }[] };
  } => {
    let buyTotal = 0, buyMatched = 0;
    let sellTotal = 0, sellMatched = 0;
    const buyConditions: { description: string; matched: boolean }[] = [];
    const sellConditions: { description: string; matched: boolean }[] = [];
    
    // 매수 이동평균선 조건 1
    if (rule.enableMaBuy) {
      buyTotal++;
      const shortMA = currentData?.[`ma${rule.maShortPeriod1Buy}`] || currentData?.ma20;
      const longMA = currentData?.[`ma${rule.maLongPeriod1Buy}`] || currentData?.ma60;
      const prevShortMA = prevData?.[`ma${rule.maShortPeriod1Buy}`] || prevData?.ma20;
      const prevLongMA = prevData?.[`ma${rule.maLongPeriod1Buy}`] || prevData?.ma60;
      const isGolden = rule.maCrossType1Buy === 'GOLDEN_CROSS';
      const matched = isGolden 
        ? (prevShortMA < prevLongMA && shortMA >= longMA)
        : (prevShortMA > prevLongMA && shortMA <= longMA);
      buyConditions.push({ 
        description: `MA${rule.maShortPeriod1Buy}/${rule.maLongPeriod1Buy} ${isGolden ? '골든' : '데드'}크로스`, 
        matched 
      });
      if (matched) buyMatched++;
    }
    
    // 매수 이동평균선 조건 2
    if (rule.enableMaCondition2Buy) {
      buyTotal++;
      const shortMA = currentData?.[`ma${rule.maShortPeriod2Buy}`] || currentData?.ma5;
      const longMA = currentData?.[`ma${rule.maLongPeriod2Buy}`] || currentData?.ma20;
      const prevShortMA = prevData?.[`ma${rule.maShortPeriod2Buy}`] || prevData?.ma5;
      const prevLongMA = prevData?.[`ma${rule.maLongPeriod2Buy}`] || prevData?.ma20;
      const isGolden = rule.maCrossType2Buy === 'GOLDEN_CROSS';
      const matched = isGolden 
        ? (prevShortMA < prevLongMA && shortMA >= longMA)
        : (prevShortMA > prevLongMA && shortMA <= longMA);
      buyConditions.push({ 
        description: `MA${rule.maShortPeriod2Buy}/${rule.maLongPeriod2Buy} ${isGolden ? '골든' : '데드'}크로스`, 
        matched 
      });
      if (matched) buyMatched++;
    }
    
    // 매수 보조지표 조건 그룹
    if (rule.enableAuxBuy && rule.buyConditionGroups) {
      for (const group of rule.buyConditionGroups) {
        const result = evaluateConditionGroup(group, currentData, prevData);
        buyTotal += result.total;
        buyMatched += result.matched;
        buyConditions.push(...result.conditions);
      }
    }
    
    // 매도 이동평균선 조건 1
    if (rule.enableMaSell) {
      sellTotal++;
      const shortMA = currentData?.[`ma${rule.maShortPeriod1Sell}`] || currentData?.ma20;
      const longMA = currentData?.[`ma${rule.maLongPeriod1Sell}`] || currentData?.ma60;
      const prevShortMA = prevData?.[`ma${rule.maShortPeriod1Sell}`] || prevData?.ma20;
      const prevLongMA = prevData?.[`ma${rule.maLongPeriod1Sell}`] || prevData?.ma60;
      const isDead = rule.maCrossType1Sell === 'DEAD_CROSS';
      const matched = isDead 
        ? (prevShortMA > prevLongMA && shortMA <= longMA)
        : (prevShortMA < prevLongMA && shortMA >= longMA);
      sellConditions.push({ 
        description: `MA${rule.maShortPeriod1Sell}/${rule.maLongPeriod1Sell} ${isDead ? '데드' : '골든'}크로스`, 
        matched 
      });
      if (matched) sellMatched++;
    }
    
    // 매도 이동평균선 조건 2
    if (rule.enableMaCondition2Sell) {
      sellTotal++;
      const shortMA = currentData?.[`ma${rule.maShortPeriod2Sell}`] || currentData?.ma5;
      const longMA = currentData?.[`ma${rule.maLongPeriod2Sell}`] || currentData?.ma20;
      const prevShortMA = prevData?.[`ma${rule.maShortPeriod2Sell}`] || prevData?.ma5;
      const prevLongMA = prevData?.[`ma${rule.maLongPeriod2Sell}`] || prevData?.ma20;
      const isDead = rule.maCrossType2Sell === 'DEAD_CROSS';
      const matched = isDead 
        ? (prevShortMA > prevLongMA && shortMA <= longMA)
        : (prevShortMA < prevLongMA && shortMA >= longMA);
      sellConditions.push({ 
        description: `MA${rule.maShortPeriod2Sell}/${rule.maLongPeriod2Sell} ${isDead ? '데드' : '골든'}크로스`, 
        matched 
      });
      if (matched) sellMatched++;
    }
    
    // 매도 보조지표 조건 그룹
    if (rule.enableAuxSell && rule.sellConditionGroups) {
      for (const group of rule.sellConditionGroups) {
        const result = evaluateConditionGroup(group, currentData, prevData);
        sellTotal += result.total;
        sellMatched += result.matched;
        sellConditions.push(...result.conditions);
      }
    }
    
    return {
      buy: { total: buyTotal, matched: buyMatched, conditions: buyConditions },
      sell: { total: sellTotal, matched: sellMatched, conditions: sellConditions },
    };
  };
  
  // 차트 상세보기를 위한 hover 상태 추적
  // useRef를 사용하여 즉시 최신 값에 접근 가능하도록 함 (setState의 비동기 문제 해결)
  const hoveredDataIndexRef = useRef<number>(-1);
  const hoveredChartTypeRef = useRef<string>('candle');
  const hoveredSignalRef = useRef<'BUY' | 'SELL' | null>(null);
  const [hoveredDataIndex, setHoveredDataIndex] = useState<number>(-1);
  const [hoveredChartType, setHoveredChartType] = useState<string>('candle');
  const [hoveredSignal, setHoveredSignal] = useState<'BUY' | 'SELL' | null>(null);
  const [popupPosition, setPopupPosition] = useState({ x: 0, y: 0 });
  const [popupSize, setPopupSize] = useState({ width: 1200, height: 600 });
  const [isDragging, setIsDragging] = useState(false);
  const [isResizing, setIsResizing] = useState(false);
  const [resizeDirection, setResizeDirection] = useState<string>('');
  const [dragStart, setDragStart] = useState({ mouseX: 0, mouseY: 0, posX: 0, posY: 0 });
  const [resizeStart, setResizeStart] = useState({ mouseX: 0, mouseY: 0, width: 0, height: 0, posX: 0, posY: 0 });
  const popupRef = useRef<HTMLDivElement>(null);
  
  // 팝업 드래그 핸들러 - 마우스 다운
  const handlePopupMouseDown = (e: React.MouseEvent) => {
    // 버튼이나 인터랙티브 요소에서 시작된 경우 드래그하지 않음
    const target = e.target as HTMLElement;
    if (target.closest('button, input, [role="slider"], .MuiSlider-root, .MuiChip-root, .MuiIconButton-root')) {
      return;
    }
    
    e.preventDefault();
    setIsDragging(true);
    setDragStart({
      mouseX: e.clientX,
      mouseY: e.clientY,
      posX: popupPosition.x,
      posY: popupPosition.y
    });
  };
  
  // 팝업 드래그 핸들러 - 터치 시작 (모바일/태블릿)
  const handlePopupTouchStart = (e: React.TouchEvent) => {
    // 버튼이나 인터랙티브 요소에서 시작된 경우 드래그하지 않음
    const target = e.target as HTMLElement;
    if (target.closest('button, input, [role="slider"], .MuiSlider-root, .MuiChip-root, .MuiIconButton-root')) {
      return;
    }
    
    if (e.touches.length === 1) {
      const touch = e.touches[0];
      setIsDragging(true);
      setDragStart({
        mouseX: touch.clientX,
        mouseY: touch.clientY,
        posX: popupPosition.x,
        posY: popupPosition.y
      });
    }
  };
  
  // 팝업 드래그 핸들러 - 마우스/터치 이동
  useEffect(() => {
    const handleMove = (e: MouseEvent | TouchEvent) => {
      if (!isDragging) return;
      
      let clientX: number, clientY: number;
      if ('touches' in e) {
        if (e.touches.length === 0) return;
        clientX = e.touches[0].clientX;
        clientY = e.touches[0].clientY;
        e.preventDefault(); // 터치 시 스크롤 방지
      } else {
        clientX = e.clientX;
        clientY = e.clientY;
      }
      
      const deltaX = clientX - dragStart.mouseX;
      const deltaY = clientY - dragStart.mouseY;
      
      setPopupPosition({
        x: dragStart.posX + deltaX,
        y: dragStart.posY + deltaY
      });
    };
    
    const handleEnd = () => {
      setIsDragging(false);
    };
    
    if (isDragging) {
      document.addEventListener('mousemove', handleMove);
      document.addEventListener('mouseup', handleEnd);
      document.addEventListener('touchmove', handleMove, { passive: false });
      document.addEventListener('touchend', handleEnd);
      document.body.style.cursor = 'move';
      document.body.style.userSelect = 'none';
    }
    
    return () => {
      document.removeEventListener('mousemove', handleMove);
      document.removeEventListener('mouseup', handleEnd);
      document.removeEventListener('touchmove', handleMove);
      document.removeEventListener('touchend', handleEnd);
      document.body.style.cursor = '';
      document.body.style.userSelect = '';
    };
  }, [isDragging, dragStart]);
  
  // 팝업 열릴 때 위치 및 크기 초기화
  useEffect(() => {
    if (indicatorPopupOpen) {
      setPopupPosition({ x: 0, y: 0 });
      setPopupSize({ width: Math.min(1200, window.innerWidth * 0.9), height: Math.min(600, window.innerHeight * 0.7) });
    }
  }, [indicatorPopupOpen]);
  
  // 리사이즈 핸들러 - 마우스
  const handleResizeMouseDown = (e: React.MouseEvent, direction: string) => {
    e.preventDefault();
    e.stopPropagation();
    setIsResizing(true);
    setResizeDirection(direction);
    setResizeStart({
      mouseX: e.clientX,
      mouseY: e.clientY,
      width: popupSize.width,
      height: popupSize.height,
      posX: popupPosition.x,
      posY: popupPosition.y
    });
  };
  
  // 리사이즈 핸들러 - 터치 (모바일/태블릿)
  const handleResizeTouchStart = (e: React.TouchEvent, direction: string) => {
    e.stopPropagation();
    if (e.touches.length === 1) {
      const touch = e.touches[0];
      setIsResizing(true);
      setResizeDirection(direction);
      setResizeStart({
        mouseX: touch.clientX,
        mouseY: touch.clientY,
        width: popupSize.width,
        height: popupSize.height,
        posX: popupPosition.x,
        posY: popupPosition.y
      });
    }
  };
  
  // 리사이즈 이벤트 리스너 (마우스 + 터치)
  useEffect(() => {
    const handleMove = (e: MouseEvent | TouchEvent) => {
      if (!isResizing) return;
      
      let clientX: number, clientY: number;
      if ('touches' in e) {
        if (e.touches.length === 0) return;
        clientX = e.touches[0].clientX;
        clientY = e.touches[0].clientY;
        e.preventDefault(); // 터치 시 스크롤 방지
      } else {
        clientX = e.clientX;
        clientY = e.clientY;
      }
      
      const deltaX = clientX - resizeStart.mouseX;
      const deltaY = clientY - resizeStart.mouseY;
      
      let newWidth = resizeStart.width;
      let newHeight = resizeStart.height;
      let newPosX = resizeStart.posX;
      let newPosY = resizeStart.posY;
      
      // 방향에 따른 크기 및 위치 조정
      if (resizeDirection.includes('e')) {
        newWidth = Math.max(400, resizeStart.width + deltaX);
      }
      if (resizeDirection.includes('w')) {
        newWidth = Math.max(400, resizeStart.width - deltaX);
        newPosX = resizeStart.posX + deltaX;
      }
      if (resizeDirection.includes('s')) {
        newHeight = Math.max(300, resizeStart.height + deltaY);
      }
      if (resizeDirection.includes('n')) {
        newHeight = Math.max(300, resizeStart.height - deltaY);
        newPosY = resizeStart.posY + deltaY;
      }
      
      setPopupSize({ width: newWidth, height: newHeight });
      if (resizeDirection.includes('w') || resizeDirection.includes('n')) {
        setPopupPosition({ x: newPosX, y: newPosY });
      }
    };
    
    const handleEnd = () => {
      setIsResizing(false);
      setResizeDirection('');
    };
    
    if (isResizing) {
      document.addEventListener('mousemove', handleMove);
      document.addEventListener('mouseup', handleEnd);
      document.addEventListener('touchmove', handleMove, { passive: false });
      document.addEventListener('touchend', handleEnd);
      document.body.style.userSelect = 'none';
    }
    
    return () => {
      document.removeEventListener('mousemove', handleMove);
      document.removeEventListener('mouseup', handleEnd);
      document.removeEventListener('touchmove', handleMove);
      document.removeEventListener('touchend', handleEnd);
      document.body.style.userSelect = '';
    };
  }, [isResizing, resizeStart, resizeDirection]);
  
  // 줌/팬 상태
  const DISPLAY_COUNT = 120; // 한 화면에 표시할 기본 데이터 개수
  
  // 내부 viewRange 상태 (externalViewRange가 없을 때 사용)
  const [internalViewRange, setInternalViewRange] = useState({ start: Math.max(0, data.length - DISPLAY_COUNT), end: data.length });
  
  // 실제 사용할 viewRange: externalViewRange가 있으면 우선 사용
  // ★ hideMainChart=true일 때도 externalViewRange 사용 (캔들차트와 X축 범위 일치)
  const viewRange = React.useMemo(() => {
    // candleDataProp이 있으면 그것의 길이, 없으면 data.length 사용
    const dataLen = (candleDataProp && candleDataProp.length > 0) ? candleDataProp.length : (data.length || 1);
    
    // externalViewRange가 있으면 무조건 사용 (hideMainChart 여부와 관계없이)
    if (externalViewRange) {
      // 외부에서 전달된 범위가 유효한지 확인
      const validStart = Math.max(0, Math.min(externalViewRange.start, dataLen - 1));
      const validEnd = Math.max(validStart + 1, Math.min(externalViewRange.end, dataLen));
      console.log(`🔧 MacdChart viewRange 동기화: externalViewRange=${JSON.stringify(externalViewRange)}, dataLen=${dataLen}, result=${validStart}~${validEnd}`);
      return { start: validStart, end: validEnd };
    }
    
    // hideMainChart=true이고 externalViewRange가 없으면 전체 데이터 표시
    if (hideMainChart) {
      console.log(`📊 MacdChart hideMainChart=true, externalViewRange 없음: 전체 데이터 표시, dataLen=${dataLen}`);
      return { start: 0, end: dataLen };
    }
    
    console.log(`🔧 MacdChart viewRange 내부 사용: internalViewRange=${JSON.stringify(internalViewRange)}`);
    return internalViewRange;
  }, [hideMainChart, externalViewRange, candleDataProp, data.length, internalViewRange]);
  
  // internalViewRange setter (externalViewRange가 없을 때만 사용)
  const setViewRange = React.useCallback((newRange: { start: number; end: number } | ((prev: { start: number; end: number }) => { start: number; end: number })) => {
    if (!externalViewRange) {
      setInternalViewRange(newRange);
    }
  }, [externalViewRange]);
  
  // 데이터가 변경되면 viewRange 리셋 (externalViewRange가 없는 경우에만)
  // 매수/매도 신호가 있으면 첫 번째 신호 위치로 자동 스크롤
  // 의존성: data의 첫 번째 타임스탬프와 길이 (데이터 변경 감지)
  const dataKey = React.useMemo(() => {
    if (data.length === 0) return '';
    // ✅ 데이터의 첫/마지막 타임스탬프 포함하여 변경 감지 강화
    const firstTime = data[0]?.timestamp || data[0]?.date || '';
    const lastTime = data[data.length - 1]?.timestamp || data[data.length - 1]?.date || '';
    return `${firstTime}-${lastTime}-${data.length}`;
  }, [data]);
  
  // ✅ 데이터 길이 변경 추적용 ref
  const prevDataLengthRef = React.useRef(data.length);
  
  React.useEffect(() => {
    // externalViewRange가 있으면 외부 동기화가 우선
    if (externalViewRange) return;
    
    if (data.length === 0) return;
    
    // ✅ 데이터 길이가 변경되었는지 확인 (기간 변경 감지)
    const dataLengthChanged = prevDataLengthRef.current !== data.length;
    prevDataLengthRef.current = data.length;
    
    // 첫 번째 매수/매도 신호 인덱스 찾기
    const firstSignalIndex = data.findIndex(d => d.signal === 'BUY' || d.signal === 'SELL');
    
    if (firstSignalIndex !== -1) {
      // 신호가 있으면 해당 위치가 화면 중앙에 오도록 범위 설정
      const halfDisplay = Math.floor(DISPLAY_COUNT / 2);
      const newStart = Math.max(0, firstSignalIndex - halfDisplay);
      const newEnd = Math.min(data.length, newStart + DISPLAY_COUNT);
      setInternalViewRange({ start: newStart, end: newEnd });
      console.log(`📊 [MacdChart] viewRange 리셋 (신호 기준): ${newStart} ~ ${newEnd}`);
    } else {
      // 신호가 없으면 마지막 데이터 기준으로 설정
      const newStart = Math.max(0, data.length - DISPLAY_COUNT);
      setInternalViewRange({ start: newStart, end: data.length });
      // ✅ 데이터 길이 변경 시 로그 출력
      if (dataLengthChanged) {
        console.log(`📊 [MacdChart] 기간 변경 감지: 데이터 길이 ${prevDataLengthRef.current} → ${data.length}, viewRange 리셋: ${newStart} ~ ${data.length}`);
      }
    }
    setZoomLevel(1);
  }, [dataKey, externalViewRange]);
  
  // 줌 레벨이 변경되면 viewRange 업데이트
  const prevZoomLevelRef = React.useRef(zoomLevel);
  React.useEffect(() => {
    const totalPoints = data.length;
    if (totalPoints === 0) return;
    
    // zoomLevel이 실제로 변경되지 않았으면 return (초기화 시 중복 실행 방지)
    if (prevZoomLevelRef.current === zoomLevel) return;
    prevZoomLevelRef.current = zoomLevel;
    
    const visiblePoints = Math.max(30, Math.floor(DISPLAY_COUNT / zoomLevel));
    
    // 현재 표시 끝점을 기준으로 시작점 조절
    const newEnd = viewRange.end;
    const newStart = Math.max(0, newEnd - visiblePoints);
    
    setViewRange({ start: newStart, end: newEnd });
  }, [zoomLevel]);
  
  // 과거 데이터 로드 (API 호출)
  const handleLoadPastData = () => {
    if (data.length === 0) return;
    
    // 현재 표시 중인 첫 번째 데이터의 날짜를 기준으로 과거 데이터 요청
    // timestamp가 있으면 timestamp 사용, 없으면 date 사용
    const firstItem = data[0];
    const firstDate = firstItem.timestamp || firstItem.date;
    const currentDataCount = data.length; // 현재 데이터 개수 전달

    if (onScrollLoadMore) {
      onScrollLoadMore('past', firstDate, currentDataCount);
    }
  };
  
  // 미래(최신) 데이터 로드 (API 호출)
  const handleLoadFutureData = () => {
    if (data.length === 0) return;
    
    // 현재 표시 중인 마지막 데이터의 날짜를 기준으로 미래 데이터 요청
    const lastItem = data[data.length - 1];
    const lastDate = lastItem.timestamp || lastItem.date;
    const currentDataCount = data.length; // 현재 데이터 개수 전달
    
    if (onScrollLoadMore) {
      onScrollLoadMore('future', lastDate, currentDataCount);
    }
  };
  
  // 그리기 이력에 현재 상태 저장
  const saveToHistory = useCallback((newDrawings: DrawingObject[]) => {
    const newHistory = drawingHistory.slice(0, historyIndex + 1);
    newHistory.push([...newDrawings]);
    setDrawingHistory(newHistory);
    setHistoryIndex(newHistory.length - 1);
  }, [drawingHistory, historyIndex]);
  
  // 그리기 객체 더블클릭 핸들러 (스타일 편집 다이얼로그 열기)
  const handleDrawingDoubleClick = useCallback((drawingId: string) => {
    const drawing = drawings.find(d => d.id === drawingId);
    if (!drawing) return;
    
    // measure 도구는 외부 핸들러로 전달
    if (drawing.type === 'measure' && onMeasureDoubleClick) {
      onMeasureDoubleClick(drawingId);
      return;
    }
    
    setEditingDrawingId(drawingId);
    setEditingStyle({
      color: drawing.color || '#2962ff',
      strokeWidth: drawing.strokeWidth || 1,
      strokeStyle: drawing.strokeStyle || 'solid',
      fillColor: drawing.fillColor || 'rgba(41, 98, 255, 0.2)',
      fillOpacity: drawing.fillOpacity ?? 0.2,
    });
    setStyleEditDialogOpen(true);
  }, [drawings, onMeasureDoubleClick]);
  
  // 스타일 적용 함수
  const applyDrawingStyle = useCallback(() => {
    if (!editingDrawingId) return;
    
    const newDrawings = drawings.map(d => {
      if (d.id === editingDrawingId) {
        return {
          ...d,
          color: editingStyle.color,
          strokeWidth: editingStyle.strokeWidth,
          strokeStyle: editingStyle.strokeStyle,
          fillColor: editingStyle.fillColor,
          fillOpacity: editingStyle.fillOpacity,
        };
      }
      return d;
    });
    
    setDrawings(newDrawings);
    saveToHistory(newDrawings);
    setStyleEditDialogOpen(false);
    setEditingDrawingId(null);
  }, [editingDrawingId, editingStyle, drawings, saveToHistory]);
  
  // 그리기 객체 삭제 함수
  const deleteDrawing = useCallback((drawingId: string) => {
    const newDrawings = drawings.filter(d => d.id !== drawingId);
    setDrawings(newDrawings);
    saveToHistory(newDrawings);
    setStyleEditDialogOpen(false);
    setEditingDrawingId(null);
    setSelectedDrawingId(null);
  }, [drawings, saveToHistory]);
  
  // 디버깅용 데이터 확인 (필요시 주석 해제)
  // React.useEffect(() => {
  //   console.log('MacdChart 데이터:', { dataLength: data.length, interval: selectedInterval });
  // }, [data, selectedInterval]);
  
  // ★ 데이터를 시간순으로 정렬 (오래된 것 → 최신)
  // ★ candleData가 있으면 MacdResult 형식으로 변환하여 사용
  // CandlestickChart와 동일한 데이터를 사용하여 X축 범위 일치
  const effectiveData = React.useMemo((): MacdResult[] => {
    if (candleDataProp && candleDataProp.length > 0) {
      console.log(`📊 MacdChart candleData 사용: 첫번째=${candleDataProp[0].time}, 마지막=${candleDataProp[candleDataProp.length-1].time}, 총 ${candleDataProp.length}개`);
      return candleDataProp.map(candle => ({
        date: candle.time,
        timestamp: candle.time,
        open: candle.open,
        high: candle.high,
        low: candle.low,
        close: candle.close,
        volume: candle.volume || 0,
        // MacdResult 필수 필드 (나중에 계산됨)
        macdLine: null,
        signalLine: null,
        macdHistogram: null,
        signal: candle.isBuyPoint ? 'BUY' : candle.isSellPoint ? 'SELL' : '',
      }));
    }
    return data;
  }, [candleDataProp, data]);
  
  // lightweight-charts와 recharts의 X축을 일치시키기 위해 필수
  const sortedData = React.useMemo(() => {
    if (effectiveData.length === 0) return [];
    const sorted = [...effectiveData].sort((a, b) => {
      const timeA = new Date(a.timestamp || a.date).getTime();
      const timeB = new Date(b.timestamp || b.date).getTime();
      return timeA - timeB;
    });
    // 디버그: 정렬 결과 확인
    if (sorted.length > 0) {
      console.log(`📊 MacdChart sortedData 범위: 첫번째=${sorted[0].timestamp || sorted[0].date}, 마지막=${sorted[sorted.length-1].timestamp || sorted[sorted.length-1].date}, 총 ${sorted.length}개`);
    }
    return sorted;
  }, [effectiveData]);
  
  // 단순이동평균선(SMA) 계산 - 정렬된 데이터 사용
  const calculateSMA = (period: number) => {
    const ma: (number | null)[] = [];
    for (let i = 0; i < sortedData.length; i++) {
      // 충분한 데이터가 없으면 null 반환 (정확한 MA 계산을 위해)
      if (i < period - 1) {
        ma.push(null);
        continue;
      }
      
      let sum = 0;
      for (let j = 0; j < period; j++) {
        sum += sortedData[i - j].close;
      }
      
      ma.push(sum / period);
    }
    return ma;
  };

  // 지수이동평균선(EMA) 계산 - 정렬된 데이터 사용
  const calculateEMA = (period: number) => {
    const ema: (number | null)[] = [];
    const multiplier = 2 / (period + 1); // EMA 가중치
    
    for (let i = 0; i < sortedData.length; i++) {
      if (i < period - 1) {
        ema.push(null);
        continue;
      }
      
      if (i === period - 1) {
        // 첫 번째 EMA는 SMA로 시작
        let sum = 0;
        for (let j = 0; j < period; j++) {
          sum += sortedData[i - j].close;
        }
        ema.push(sum / period);
      } else {
        // EMA = (현재가 - 이전 EMA) × 승수 + 이전 EMA
        const prevEma = ema[i - 1];
        if (prevEma !== null) {
          const currentEma = (sortedData[i].close - prevEma) * multiplier + prevEma;
          ema.push(currentEma);
        } else {
          ema.push(null);
        }
      }
    }
    return ema;
  };

  // 기존 호환용 SMA 계산
  const calculateMA = calculateSMA;
  
  const ma5 = calculateSMA(5);
  const ma10 = calculateSMA(10);
  const ma12 = calculateSMA(12);
  const ma20 = calculateSMA(20);
  const ma26 = calculateSMA(26);
  const ma50 = calculateSMA(50);
  const ma60 = calculateSMA(60);
  const ma120 = calculateSMA(120);
  const ma200 = calculateSMA(200);
  const ma300 = calculateSMA(300);
  const ma480 = calculateSMA(480);
  
  // 사용자 정의 이동평균선 계산 (maLines 기반 - SMA/EMA 구분)
  const customMAData: { [key: string]: (number | null)[] } = {};
  if (settings.maLines) {
    settings.maLines.forEach(maConfig => {
      if (maConfig.enabled) {
        const key = `${maConfig.period}_${maConfig.type}`;
        if (!customMAData[key]) {
          // 타입에 따라 SMA 또는 EMA 계산
          customMAData[key] = maConfig.type === 'EMA' 
            ? calculateEMA(maConfig.period) 
            : calculateSMA(maConfig.period);
        }
      }
    });
  }
  
  // === 신규 보조지표 계산 ===
  
  // 1. 신심리도 (New Psychological Line) - n일간 상승일 수 / n * 100
  // Backend와 동일한 로직: i < period일 때는 null, i >= period일 때부터 계산
  const calculateNewPsychological = (period: number): (number | null)[] => {
    const result: (number | null)[] = [];
    for (let i = 0; i < data.length; i++) {
      if (i < period) {  // ✅ Backend와 일치: period 미만은 null
        result.push(null);
        continue;
      }
      let upDays = 0;
      for (let j = 0; j < period; j++) {
        const idx = i - j;
        const prevIdx = idx - 1;
        // prevIdx >= 0 체크는 항상 true (i >= period이고 j < period이므로 idx >= 1)
        if (data[idx]?.close > data[prevIdx]?.close) {
          upDays++;
        }
      }
      result.push((upDays / period) * 100);
    }
    return result;
  };
  
  // 2. 투자심리도 (Investor Psychological Line) - n일간 상승 거래량 / 전체 거래량 * 100
  // Backend와 동일한 로직: i < period일 때는 null, idx > 0일 때만 거래량 포함
  const calculateInvestPsychological = (period: number): (number | null)[] => {
    const result: (number | null)[] = [];
    for (let i = 0; i < data.length; i++) {
      if (i < period) {  // ✅ Backend와 일치: period 미만은 null
        result.push(null);
        continue;
      }
      let upVolume = 0;
      let totalVolume = 0;
      for (let j = 0; j < period; j++) {
        const idx = i - j;
        // ✅ Backend와 일치: idx > 0일 때만 포함 (idx=0은 제외, 이전 데이터 없음)
        if (idx > 0 && data[idx]?.volume) {
          totalVolume += data[idx].volume;
          if (data[idx].close > data[idx - 1].close) {
            upVolume += data[idx].volume;
          }
        }
      }
      result.push(totalVolume > 0 ? (upVolume / totalVolume) * 100 : 50.0);
    }
    return result;
  };
  
  const newPsychologicalData = calculateNewPsychological(indicatorSettings.newPsychologicalPeriod || 5);
  const investPsychologicalData = calculateInvestPsychological(indicatorSettings.investPsychologicalPeriod || 10);
  
  // 2. BWI (Bollinger Band Width Index) - (상단밴드 - 하단밴드) / 중간밴드 * 100
  const calculateBWI = (period: number, stdDev: number = 2): (number | null)[] => {
    const result: (number | null)[] = [];
    for (let i = 0; i < data.length; i++) {
      if (i < period - 1) {
        result.push(null);
        continue;
      }
      // 중간밴드 (SMA)
      let sum = 0;
      for (let j = 0; j < period; j++) {
        sum += data[i - j].close;
      }
      const middle = sum / period;
      
      // 표준편차
      let variance = 0;
      for (let j = 0; j < period; j++) {
        variance += Math.pow(data[i - j].close - middle, 2);
      }
      const std = Math.sqrt(variance / period);
      
      // 상단/하단 밴드
      const upper = middle + stdDev * std;
      const lower = middle - stdDev * std;
      
      // BWI
      const bwi = middle > 0 ? ((upper - lower) / middle) * 100 : null;
      result.push(bwi);
    }
    return result;
  };
  
  const bwiData = calculateBWI(indicatorSettings.bwiPeriod || 20);
  
  // 3. OBV (On Balance Volume) - 누적 거래량
  const calculateOBV = (): (number | null)[] => {
    const result: (number | null)[] = [];
    let obv = 0;
    for (let i = 0; i < data.length; i++) {
      if (i === 0) {
        obv = data[i].volume || 0;
      } else {
        const prevClose = data[i - 1].close;
        const currClose = data[i].close;
        const volume = data[i].volume || 0;
        
        if (currClose > prevClose) {
          obv += volume;
        } else if (currClose < prevClose) {
          obv -= volume;
        }
        // currClose === prevClose 이면 OBV 변동 없음
      }
      result.push(obv);
    }
    return result;
  };
  
  const obvData = calculateOBV();
  
  // 4. Williams %R - (최고가 - 현재가) / (최고가 - 최저가) * -100
  const calculateWilliamsR = (period: number): (number | null)[] => {
    const result: (number | null)[] = [];
    for (let i = 0; i < data.length; i++) {
      if (i < period - 1) {
        result.push(null);
        continue;
      }
      let highestHigh = -Infinity;
      let lowestLow = Infinity;
      for (let j = 0; j < period; j++) {
        const d = data[i - j];
        const high = d.high ?? d.close;
        const low = d.low ?? d.close;
        if (high > highestHigh) highestHigh = high;
        if (low < lowestLow) lowestLow = low;
      }
      const range = highestHigh - lowestLow;
      if (range === 0) {
        result.push(-50); // 중간값
      } else {
        result.push(((highestHigh - data[i].close) / range) * -100);
      }
    }
    return result;
  };
  
  const williamsRData = calculateWilliamsR(indicatorSettings.williamsRPeriod || 11);
  
  // 5. VolumeOSC - (단기 거래량MA - 장기 거래량MA) / 장기 거래량MA * 100
  const calculateVolumeOSC = (shortPeriod: number, longPeriod: number): (number | null)[] => {
    const result: (number | null)[] = [];
    
    // 거래량 이동평균 계산
    const calcVolMA = (period: number): (number | null)[] => {
      const ma: (number | null)[] = [];
      for (let i = 0; i < data.length; i++) {
        if (i < period - 1) {
          ma.push(null);
          continue;
        }
        let sum = 0;
        for (let j = 0; j < period; j++) {
          sum += data[i - j].volume || 0;
        }
        ma.push(sum / period);
      }
      return ma;
    };
    
    const shortMA = calcVolMA(shortPeriod);
    const longMA = calcVolMA(longPeriod);
    
    for (let i = 0; i < data.length; i++) {
      if (shortMA[i] === null || longMA[i] === null || longMA[i] === 0) {
        result.push(null);
      } else {
        result.push(((shortMA[i]! - longMA[i]!) / longMA[i]!) * 100);
      }
    }
    return result;
  };
  
  const volumeOscData = calculateVolumeOSC(
    indicatorSettings.volumeOscShortPeriod || 10,
    indicatorSettings.volumeOscLongPeriod || 20
  );
  
  // 6. VR (Volume Ratio) - 상승일 거래량 / 하락일 거래량 * 100
  const calculateVR = (period: number): (number | null)[] => {
    const result: (number | null)[] = [];
    for (let i = 0; i < data.length; i++) {
      if (i < period) {
        result.push(null);
        continue;
      }
      let upVolume = 0;
      let downVolume = 0;
      let neutralVolume = 0;
      
      for (let j = 0; j < period; j++) {
        const idx = i - j;
        const prevIdx = idx - 1;
        if (prevIdx < 0) continue;
        
        const volume = data[idx].volume || 0;
        if (data[idx].close > data[prevIdx].close) {
          upVolume += volume;
        } else if (data[idx].close < data[prevIdx].close) {
          downVolume += volume;
        } else {
          neutralVolume += volume;
        }
      }
      
      // VR = (상승거래량 + 보합거래량/2) / (하락거래량 + 보합거래량/2) * 100
      const numerator = upVolume + neutralVolume / 2;
      const denominator = downVolume + neutralVolume / 2;
      
      if (denominator === 0) {
        result.push(null);
      } else {
        result.push((numerator / denominator) * 100);
      }
    }
    return result;
  };
  
  const vrData = calculateVR(indicatorSettings.vrPeriod || 10);
  
  // 7. RSI (Relative Strength Index) - 데이터에 없으면 직접 계산
  const calculateRSI = (period: number): (number | null)[] => {
    const result: (number | null)[] = [];
    let avgGain = 0;
    let avgLoss = 0;
    
    for (let i = 0; i < data.length; i++) {
      if (i === 0) {
        result.push(null);
        continue;
      }
      
      const change = data[i].close - data[i - 1].close;
      const gain = change > 0 ? change : 0;
      const loss = change < 0 ? -change : 0;
      
      if (i < period) {
        result.push(null);
        continue;
      }
      
      if (i === period) {
        // 첫 번째 평균 계산
        let sumGain = 0;
        let sumLoss = 0;
        for (let j = 1; j <= period; j++) {
          const c = data[j].close - data[j - 1].close;
          sumGain += c > 0 ? c : 0;
          sumLoss += c < 0 ? -c : 0;
        }
        avgGain = sumGain / period;
        avgLoss = sumLoss / period;
      } else {
        // Wilder's Smoothing
        avgGain = (avgGain * (period - 1) + gain) / period;
        avgLoss = (avgLoss * (period - 1) + loss) / period;
      }
      
      if (avgLoss === 0) {
        result.push(100);
      } else {
        const rs = avgGain / avgLoss;
        result.push(100 - (100 / (1 + rs)));
      }
    }
    
    return result;
  };
  
  const rsiData = calculateRSI(indicatorSettings.rsiPeriod || 9);
  
  // 7-1. RSI Signal (RSI의 SMA)
  const calculateRsiSignal = (signalPeriod: number): (number | null)[] => {
    const result: (number | null)[] = [];
    for (let i = 0; i < data.length; i++) {
      const rsiValue = data[i].rsi ?? rsiData[i];
      if (rsiValue === null || rsiValue === undefined || i < signalPeriod - 1) {
        result.push(null);
        continue;
      }
      
      // RSI 값들의 SMA 계산
      let sum = 0;
      let count = 0;
      for (let j = 0; j < signalPeriod; j++) {
        const rsi = data[i - j]?.rsi ?? rsiData[i - j];
        if (rsi !== null && rsi !== undefined) {
          sum += rsi;
          count++;
        }
      }
      
      if (count === signalPeriod) {
        result.push(sum / signalPeriod);
      } else {
        result.push(null);
      }
    }
    return result;
  };
  
  const rsiSignalData = calculateRsiSignal(indicatorSettings.rsiSignalPeriod || 9);
  
  // 8. OBV Signal (OBV의 SMA)
  const calculateObvSignal = (signalPeriod: number): (number | null)[] => {
    const result: (number | null)[] = [];
    for (let i = 0; i < data.length; i++) {
      const obvValue = obvData[i];
      if (obvValue === null || obvValue === undefined || i < signalPeriod - 1) {
        result.push(null);
        continue;
      }
      
      // OBV 값들의 SMA 계산
      let sum = 0;
      let count = 0;
      for (let j = 0; j < signalPeriod; j++) {
        const obv = obvData[i - j];
        if (obv !== null && obv !== undefined) {
          sum += obv;
          count++;
        }
      }
      
      if (count === signalPeriod) {
        result.push(sum / signalPeriod);
      } else {
        result.push(null);
      }
    }
    return result;
  };
  
  const obvSignalData = calculateObvSignal(indicatorSettings.obvSignalPeriod || 9);
  
  // 9. CCI Signal (CCI의 SMA)
  const calculateCciSignal = (signalPeriod: number): (number | null)[] => {
    const result: (number | null)[] = [];
    for (let i = 0; i < data.length; i++) {
      const cciValue = data[i]?.cci ?? data[i]?.cci13;
      if (cciValue === null || cciValue === undefined || i < signalPeriod - 1) {
        result.push(null);
        continue;
      }
      
      // CCI 값들의 SMA 계산
      let sum = 0;
      let count = 0;
      for (let j = 0; j < signalPeriod; j++) {
        const cci = data[i - j]?.cci ?? data[i - j]?.cci13;
        if (cci !== null && cci !== undefined) {
          sum += cci;
          count++;
        }
      }
      
      if (count === signalPeriod) {
        result.push(sum / signalPeriod);
      } else {
        result.push(null);
      }
    }
    return result;
  };
  
  const cciSignalData = calculateCciSignal(indicatorSettings.cciSignalPeriod || 10);
  
  // 10. CCI (Commodity Channel Index) - 데이터에 없으면 직접 계산
  const calculateCCI = (period: number): (number | null)[] => {
    const result: (number | null)[] = [];
    for (let i = 0; i < data.length; i++) {
      if (i < period - 1) {
        result.push(null);
        continue;
      }
      
      // Typical Price = (High + Low + Close) / 3
      const typicalPrices: number[] = [];
      for (let j = 0; j < period; j++) {
        const item = data[i - j];
        const high = item.high ?? item.close;
        const low = item.low ?? item.close;
        typicalPrices.push((high + low + item.close) / 3);
      }
      
      const currentTP = typicalPrices[0];
      const smaTP = typicalPrices.reduce((a, b) => a + b, 0) / period;
      
      // Mean Deviation
      const meanDeviation = typicalPrices.reduce((sum, tp) => sum + Math.abs(tp - smaTP), 0) / period;
      
      if (meanDeviation === 0) {
        result.push(0);
      } else {
        result.push((currentTP - smaTP) / (0.015 * meanDeviation));
      }
    }
    return result;
  };
  
  const cciData = calculateCCI(indicatorSettings.cciPeriod || 13);
  
  // 11. TRIX (Triple Exponential Average) - 데이터에 없으면 직접 계산
  const calculateTRIX = (period: number): (number | null)[] => {
    // EMA 계산 헬퍼
    const calculateEMA = (values: number[], emaPeriod: number): number[] => {
      const result: number[] = [];
      const multiplier = 2 / (emaPeriod + 1);
      
      // 첫 번째 EMA는 SMA로 시작
      let sum = 0;
      for (let i = 0; i < emaPeriod && i < values.length; i++) {
        sum += values[i];
      }
      let prevEMA = sum / Math.min(emaPeriod, values.length);
      result.push(prevEMA);
      
      for (let i = 1; i < values.length; i++) {
        const currentEMA = (values[i] - prevEMA) * multiplier + prevEMA;
        result.push(currentEMA);
        prevEMA = currentEMA;
      }
      
      return result;
    };
    
    const closePrices = data.map(d => d.close);
    
    // Triple EMA 계산
    const ema1 = calculateEMA(closePrices, period);
    const ema2 = calculateEMA(ema1, period);
    const ema3 = calculateEMA(ema2, period);
    
    // TRIX = (EMA3[today] - EMA3[yesterday]) / EMA3[yesterday] * 100
    const result: (number | null)[] = [];
    for (let i = 0; i < ema3.length; i++) {
      if (i < period * 3 - 2 || i === 0) {
        result.push(null);
      } else {
        const prevEma3 = ema3[i - 1];
        if (prevEma3 !== 0) {
          result.push(((ema3[i] - prevEma3) / prevEma3) * 100);
        } else {
          result.push(null);
        }
      }
    }
    
    return result;
  };
  
  const trixData = calculateTRIX(indicatorSettings.trixPeriod || 12);
  
  // 12. TRIX Signal (TRIX의 EMA)
  const calculateTrixSignal = (signalPeriod: number): (number | null)[] => {
    const result: (number | null)[] = [];
    const multiplier = 2 / (signalPeriod + 1);
    let prevEMA: number | null = null;
    
    for (let i = 0; i < trixData.length; i++) {
      const trixValue = trixData[i];
      if (trixValue === null) {
        result.push(null);
        continue;
      }
      
      if (prevEMA === null) {
        prevEMA = trixValue;
        result.push(trixValue);
      } else {
        const currentEMA: number = (trixValue - prevEMA) * multiplier + prevEMA;
        result.push(currentEMA);
        prevEMA = currentEMA;
      }
    }
    
    return result;
  };
  
  const trixSignalData = calculateTrixSignal(indicatorSettings.trixSignalPeriod || 9);
  
  // 13. Stochastic Oscillator - 데이터에 없으면 직접 계산
  const calculateStochastic = (kPeriod: number, dPeriod: number, slowing: number = 3): { k: (number | null)[], d: (number | null)[] } => {
    const rawK: (number | null)[] = [];
    
    // Raw %K 계산
    for (let i = 0; i < data.length; i++) {
      if (i < kPeriod - 1) {
        rawK.push(null);
        continue;
      }
      
      let highestHigh = -Infinity;
      let lowestLow = Infinity;
      
      for (let j = 0; j < kPeriod; j++) {
        const item = data[i - j];
        const high = item.high ?? item.close;
        const low = item.low ?? item.close;
        highestHigh = Math.max(highestHigh, high);
        lowestLow = Math.min(lowestLow, low);
      }
      
      const range = highestHigh - lowestLow;
      if (range === 0) {
        rawK.push(50);
      } else {
        rawK.push(((data[i].close - lowestLow) / range) * 100);
      }
    }
    
    // Slow %K (rawK의 SMA)
    const slowK: (number | null)[] = [];
    for (let i = 0; i < rawK.length; i++) {
      if (rawK[i] === null || i < slowing - 1) {
        slowK.push(null);
        continue;
      }
      
      let sum = 0;
      let count = 0;
      for (let j = 0; j < slowing; j++) {
        if (rawK[i - j] !== null) {
          sum += rawK[i - j]!;
          count++;
        }
      }
      
      if (count === slowing) {
        slowK.push(sum / slowing);
      } else {
        slowK.push(null);
      }
    }
    
    // %D (slowK의 SMA)
    const slowD: (number | null)[] = [];
    for (let i = 0; i < slowK.length; i++) {
      if (slowK[i] === null || i < dPeriod - 1) {
        slowD.push(null);
        continue;
      }
      
      let sum = 0;
      let count = 0;
      for (let j = 0; j < dPeriod; j++) {
        if (slowK[i - j] !== null) {
          sum += slowK[i - j]!;
          count++;
        }
      }
      
      if (count === dPeriod) {
        slowD.push(sum / dPeriod);
      } else {
        slowD.push(null);
      }
    }
    
    return { k: slowK, d: slowD };
  };
  
  const stochasticResult = calculateStochastic(
    indicatorSettings.stochasticKPeriod || 14,
    indicatorSettings.stochasticDPeriod || 3,
    indicatorSettings.stochasticSlowing || 3
  );
  
  // 14. ADX (Average Directional Index) - 데이터에 없으면 직접 계산
  const calculateADX = (period: number = 14): { adx: (number | null)[], plusDI: (number | null)[], minusDI: (number | null)[] } => {
    const trueRanges: number[] = [];
    const plusDMs: number[] = [];
    const minusDMs: number[] = [];
    
    for (let i = 0; i < data.length; i++) {
      const currentHigh = data[i].high ?? data[i].close;
      const currentLow = data[i].low ?? data[i].close;
      
      if (i === 0) {
        trueRanges.push(currentHigh - currentLow);
        plusDMs.push(0);
        minusDMs.push(0);
        continue;
      }
      
      const prevClose = data[i - 1].close;
      const prevHigh = data[i - 1].high ?? data[i - 1].close;
      const prevLow = data[i - 1].low ?? data[i - 1].close;
      
      // True Range
      const tr = Math.max(
        currentHigh - currentLow,
        Math.abs(currentHigh - prevClose),
        Math.abs(currentLow - prevClose)
      );
      trueRanges.push(tr);
      
      // +DM and -DM
      const upMove = currentHigh - prevHigh;
      const downMove = prevLow - currentLow;
      
      if (upMove > downMove && upMove > 0) {
        plusDMs.push(upMove);
      } else {
        plusDMs.push(0);
      }
      
      if (downMove > upMove && downMove > 0) {
        minusDMs.push(downMove);
      } else {
        minusDMs.push(0);
      }
    }
    
    // Smoothed values
    const adxResult: (number | null)[] = [];
    const plusDIResult: (number | null)[] = [];
    const minusDIResult: (number | null)[] = [];
    
    let smoothedTR = 0;
    let smoothedPlusDM = 0;
    let smoothedMinusDM = 0;
    const dxValues: number[] = [];
    
    for (let i = 0; i < data.length; i++) {
      if (i < period) {
        adxResult.push(null);
        plusDIResult.push(null);
        minusDIResult.push(null);
        
        if (i === period - 1) {
          // 첫 번째 평균
          for (let j = 0; j < period; j++) {
            smoothedTR += trueRanges[j];
            smoothedPlusDM += plusDMs[j];
            smoothedMinusDM += minusDMs[j];
          }
        }
        continue;
      }
      
      // Wilder's Smoothing
      smoothedTR = smoothedTR - (smoothedTR / period) + trueRanges[i];
      smoothedPlusDM = smoothedPlusDM - (smoothedPlusDM / period) + plusDMs[i];
      smoothedMinusDM = smoothedMinusDM - (smoothedMinusDM / period) + minusDMs[i];
      
      // +DI and -DI
      const plusDI = smoothedTR !== 0 ? (smoothedPlusDM / smoothedTR) * 100 : 0;
      const minusDI = smoothedTR !== 0 ? (smoothedMinusDM / smoothedTR) * 100 : 0;
      
      plusDIResult.push(plusDI);
      minusDIResult.push(minusDI);
      
      // DX
      const diSum = plusDI + minusDI;
      const dx = diSum !== 0 ? (Math.abs(plusDI - minusDI) / diSum) * 100 : 0;
      dxValues.push(dx);
      
      // ADX (DX의 SMA)
      if (dxValues.length < period) {
        adxResult.push(null);
      } else if (dxValues.length === period) {
        const adx = dxValues.reduce((a, b) => a + b, 0) / period;
        adxResult.push(adx);
      } else {
        // Wilder's Smoothing for ADX
        const prevADX = adxResult[i - 1] || 0;
        const adx = ((prevADX * (period - 1)) + dx) / period;
        adxResult.push(adx);
      }
    }
    
    return { adx: adxResult, plusDI: plusDIResult, minusDI: minusDIResult };
  };
  
  // ADX 보조지표용 (adxPeriod 사용)
  const adxResult = calculateADX(indicatorSettings.adxPeriod || 11);
  // DMI 보조지표용 (dmiPeriod 사용)
  const dmiResult = calculateADX(indicatorSettings.dmiPeriod || 14);
  
  // interval에 따른 X축 눈금 간격 계산
  // 데이터 개수에 따라 동적으로 간격 조정 (최대 5~6개 레이블만 표시)
  const getXAxisInterval = () => {
    const dataLength = chartData.length;
    // 화면에 최대 5~6개의 레이블만 표시하도록 간격 계산
    const maxLabels = isMobile ? 4 : 6;
    const calculatedInterval = Math.ceil(dataLength / maxLabels);
    return Math.max(calculatedInterval - 1, 0);
  };
  
  // Y축 숫자 컴팩트 포맷터 - 원화 단위 표시
  // 예: 131,228,000 → 131M원
  const formatCompactNumber = (value: number): string => {
    // BUILD VERIFICATION: 2025-12-19 11:07
    if (value === null || value === undefined) return '';
    const absValue = Math.abs(value);
    if (absValue >= 1000000000000) {
      // 1조 이상
      const formatted = value / 1000000000000;
      return (formatted % 1 === 0 ? formatted.toFixed(0) : formatted.toFixed(1)) + '조원';
    } else if (absValue >= 100000000) {
      // 1억 이상
      const formatted = value / 100000000;
      return (formatted % 1 === 0 ? formatted.toFixed(0) : formatted.toFixed(1)) + '억원';
    } else if (absValue >= 10000) {
      // 1만 이상
      const formatted = value / 10000;
      return (formatted % 1 === 0 ? formatted.toFixed(0) : formatted.toFixed(1)) + '만원';
    } else if (absValue >= 1000) {
      // 1000 이상 - 쉼표 포맷
      return Math.round(value).toLocaleString('ko-KR') + '원';
    } else if (absValue >= 1) {
      return value.toFixed(0) + '원';
    } else {
      return value.toFixed(2) + '원';
    }
  };

  // X축 레이블 포맷터 - 이미 포맷된 date 값을 그대로 사용
  // (데이터 전처리에서 이미 interval에 맞게 포맷됨)
  const formatXAxisLabel = (dateStr: string) => {
    if (!dateStr) return '';
    
    // 이미 fullChartData에서 포맷된 값이므로 그대로 반환
    // 분봉: "HH:mm" 또는 "MM/dd HH:mm"
    // 시간봉: "MM/dd HH:mm"
    // 일봉: "14, Aug"
    // 주봉: "MM/dd"
    // 월봉: "yyyy/MM"
    // 년봉: "yyyy"
    return dateStr;
  };
  
  // 데이터 전처리 및 신호 감지 - 정렬된 데이터 사용
  const fullChartData = sortedData.map((item, index) => {
    const prevItem = index > 0 ? sortedData[index - 1] : null;
    
    // 기본 전략 로직 제거됨 - 전략 규칙 관리(StrategyRule)만 사용
    // 골든/데드 크로스, MACD Custom 전략 등 프론트엔드 자체 신호 계산 삭제

    // interval에 따라 날짜 포맷 결정
    let displayDate: string;
    // timestamp가 있으면 우선 사용, 없으면 date 사용
    const hasTimestamp = item.timestamp && item.timestamp.length > 0;
    const dateObj = hasTimestamp ? parseISO(item.timestamp!) : parseISO(item.date);
    
    // 분봉/시간봉 체크 (interval 기준)
    const isMinuteOrHourInterval = selectedInterval.endsWith('m') || selectedInterval.endsWith('h');
    
    if (isMinuteOrHourInterval && hasTimestamp) {
      // 분봉/시간봉: timestamp 기반 유니크 키
      displayDate = format(dateObj, 'MM/dd HH:mm');
    } else if (selectedInterval === '1d') {
      displayDate = format(dateObj, 'MM/dd') + '일';
    } else if (selectedInterval === '1w') {
      displayDate = format(dateObj, 'MM/dd') + '일';
    } else if (selectedInterval === '1M') {
      displayDate = format(dateObj, 'yy') + '년 ' + format(dateObj, 'MM') + '월';
    } else if (selectedInterval === '1y') {
      displayDate = format(dateObj, 'yyyy') + '년';
    } else {
      // 기본: 시간 포함
      displayDate = format(dateObj, 'MM/dd HH:mm');
    }
    
    return {
      ...item,
      date: displayDate,
      originalDate: item.date,
      originalTimestamp: item.timestamp,
      open: item.open !== null && item.open !== undefined ? item.open : item.close,
      high: item.high !== null && item.high !== undefined ? item.high : item.close,
      low: item.low !== null && item.low !== undefined ? item.low : item.close,
      close: item.close,
      ma12: ma12[index],
      ma26: ma26[index],
      ma60: ma60[index],
      // 이동평균선 데이터 추가 (기존 호환용)
      sma10: ma10[index] ?? null,
      sma20: ma20[index] ?? null,
      sma50: ma50[index] ?? null,
      sma60: ma60[index] ?? null,
      sma120: ma120[index] ?? null,
      sma200: ma200[index] ?? null,
      sma300: ma300[index] ?? null,
      sma480: ma480[index] ?? null,
      // 사용자 정의 MA 데이터 (maLines 기반 - period_type 형태의 key 사용)
      ...Object.fromEntries(
        Object.entries(customMAData).map(([key, values]) => [`customMA_${key}`, values[index] ?? null])
      ),
      macdLine: item.macdLine || 0,
      signalLine: item.signalLine || 0,
      macdHistogram: item.macdHistogram || 0,
      histogramColor: (item.macdHistogram || 0) > 0 ? colors.histogramPositive : colors.histogramNegative,
      volume: item.volume || 0, // 거래량 추가
      // Context에서 가져온 색상 사용
      volumeColor: (item.close >= (item.open || item.close)) ? colors.volumeRising : colors.volumeFalling,
      // Stochastic Slow (데이터에서 가져오거나 프론트엔드에서 계산)
      stochasticK: item.stochasticK ?? stochasticResult.k[index],
      stochasticD: item.stochasticD ?? stochasticResult.d[index],
      // ADX 보조지표용 (adxPeriod 기반)
      adx: item.adx ?? adxResult.adx[index],
      plusDI: item.plusDI ?? adxResult.plusDI[index],
      minusDI: item.minusDI ?? adxResult.minusDI[index],
      // DMI 보조지표용 (dmiPeriod 기반) - 백엔드에서 제공하지 않으면 프론트엔드 계산값 사용
      dmiAdx: item.dmiAdx ?? dmiResult.adx[index],
      dmiPlusDI: item.dmiPlusDI ?? dmiResult.plusDI[index],
      dmiMinusDI: item.dmiMinusDI ?? dmiResult.minusDI[index],
      // CCI (데이터에서 가져오거나 프론트엔드에서 계산)
      cci: item.cci ?? item.cci13 ?? cciData[index],
      cciSignal: cciSignalData[index],
      // RSI (데이터에서 가져오거나 프론트엔드에서 계산)
      rsi: item.rsi ?? rsiData[index],
      rsiSignal: rsiSignalData[index],
      // TRIX (데이터에서 가져오거나 프론트엔드에서 계산)
      trix: item.trix ?? trixData[index],
      trixSignal: item.trixSignal ?? trixSignalData[index],
      // 이격도 (Disparity Index) - (현재가 / 이동평균) * 100
      disparityUltraShort: ma5[index] != null && ma5[index]! > 0 && item.close ? (item.close / ma5[index]!) * 100 : null,
      disparityShort: ma20[index] != null && ma20[index]! > 0 && item.close ? (item.close / ma20[index]!) * 100 : null,
      disparityMid: ma60[index] != null && ma60[index]! > 0 && item.close ? (item.close / ma60[index]!) * 100 : null,
      disparityLong: ma120[index] != null && ma120[index]! > 0 && item.close ? (item.close / ma120[index]!) * 100 : null,
      // 신규 보조지표 (프론트엔드에서 계산)
      newPsychological: newPsychologicalData[index],
      investPsychological: investPsychologicalData[index],
      bwi: bwiData[index],
      obv: obvData[index],
      obvSignal: obvSignalData[index],
      williamsR: williamsRData[index],
      volumeOsc: volumeOscData[index],
      vr: vrData[index],
      // 일목균형표 (Ichimoku Cloud)
      ichimokuTenkan: item.ichimokuTenkan ?? null,
      ichimokuKijun: item.ichimokuKijun ?? null,
      ichimokuChikou: item.ichimokuChikou ?? null,
      ichimokuSenkouA: item.ichimokuSenkouA ?? null,
      ichimokuSenkouB: item.ichimokuSenkouB ?? null,
      // 전략 규칙에 의한 매수/매도 마커 (backtestSignals props 또는 signal 필드 기반)
      // backtestSignals가 있으면 해당 데이터 사용, 없으면 signal 필드 사용
      backtestBuyMarker: (() => {
        // backtestSignals에서 해당 날짜/시간의 BUY 신호 찾기
        const itemDateStr = item.timestamp || item.date || '';
        // 날짜 비교 함수: 다양한 형식 지원 (분 단위까지 비교)
        const matchDate = (signalDate: string, itemDate: string): boolean => {
          if (!signalDate || !itemDate) return false;
          // 완전 일치
          if (signalDate === itemDate) return true;
          // 분 단위까지 비교 (yyyy-MM-ddTHH:mm)
          const normalizeToMinute = (d: string) => d.replace(/[:.]/g, '').substring(0, 12);
          if (normalizeToMinute(signalDate) === normalizeToMinute(itemDate)) return true;
          // ISO 형식 비교 (T 기준으로 분리 후 시간 부분 비교)
          const getDateTime = (d: string) => {
            const match = d.match(/(\d{4}-\d{2}-\d{2})[T ]?(\d{2}):?(\d{2})?/);
            return match ? `${match[1]}T${match[2]}:${match[3] || '00'}` : d;
          };
          return getDateTime(signalDate) === getDateTime(itemDate);
        };
        const backtestBuy = backtestSignals.find(s => 
          s.type === 'BUY' && matchDate(s.date, itemDateStr)
        );
        if (backtestBuy) {
          return item.low !== null && item.low !== undefined ? item.low * 0.995 : item.close * 0.995;
        }
        // backtestSignals가 있을 때는 signal 필드를 사용하지 않음 (중복 방지)
        // backtestSignals가 없을 때만 signal 필드 사용
        if (backtestSignals.length === 0 && item.signal === 'BUY') {
          return item.low !== null && item.low !== undefined ? item.low * 0.995 : item.close * 0.995;
        }
        return null;
      })(),
      backtestSellMarker: (() => {
        // backtestSignals에서 해당 날짜/시간의 SELL 신호 찾기
        const itemDateStr = item.timestamp || item.date || '';
        // 날짜 비교 함수: 다양한 형식 지원 (분 단위까지 비교)
        const matchDate = (signalDate: string, itemDate: string): boolean => {
          if (!signalDate || !itemDate) return false;
          // 완전 일치
          if (signalDate === itemDate) return true;
          // 분 단위까지 비교 (yyyy-MM-ddTHH:mm)
          const normalizeToMinute = (d: string) => d.replace(/[:.]/g, '').substring(0, 12);
          if (normalizeToMinute(signalDate) === normalizeToMinute(itemDate)) return true;
          // ISO 형식 비교 (T 기준으로 분리 후 시간 부분 비교)
          const getDateTime = (d: string) => {
            const match = d.match(/(\d{4}-\d{2}-\d{2})[T ]?(\d{2}):?(\d{2})?/);
            return match ? `${match[1]}T${match[2]}:${match[3] || '00'}` : d;
          };
          return getDateTime(signalDate) === getDateTime(itemDate);
        };
        const backtestSell = backtestSignals.find(s => 
          s.type === 'SELL' && matchDate(s.date, itemDateStr)
        );
        if (backtestSell) {
          return item.high !== null && item.high !== undefined ? item.high * 1.005 : item.close * 1.005;
        }
        // backtestSignals가 있을 때는 signal 필드를 사용하지 않음 (중복 방지)
        // backtestSignals가 없을 때만 signal 필드 사용
        if (backtestSignals.length === 0 && item.signal === 'SELL') {
          return item.high !== null && item.high !== undefined ? item.high * 1.005 : item.close * 1.005;
        }
        return null;
      })(),
      isBacktestBuy: (() => {
        const itemDateStr = item.timestamp || item.date || '';
        const matchDate = (signalDate: string, itemDate: string): boolean => {
          if (!signalDate || !itemDate) return false;
          if (signalDate === itemDate) return true;
          const normalizeToMinute = (d: string) => d.replace(/[:.]/g, '').substring(0, 12);
          if (normalizeToMinute(signalDate) === normalizeToMinute(itemDate)) return true;
          const getDateTime = (d: string) => {
            const match = d.match(/(\d{4}-\d{2}-\d{2})[T ]?(\d{2}):?(\d{2})?/);
            return match ? `${match[1]}T${match[2]}:${match[3] || '00'}` : d;
          };
          return getDateTime(signalDate) === getDateTime(itemDate);
        };
        return backtestSignals.some(s => 
          s.type === 'BUY' && matchDate(s.date, itemDateStr)
        ) || (backtestSignals.length === 0 && item.signal === 'BUY');
      })(),
      isBacktestSell: (() => {
        const itemDateStr = item.timestamp || item.date || '';
        const matchDate = (signalDate: string, itemDate: string): boolean => {
          if (!signalDate || !itemDate) return false;
          if (signalDate === itemDate) return true;
          const normalizeToMinute = (d: string) => d.replace(/[:.]/g, '').substring(0, 12);
          if (normalizeToMinute(signalDate) === normalizeToMinute(itemDate)) return true;
          const getDateTime = (d: string) => {
            const match = d.match(/(\d{4}-\d{2}-\d{2})[T ]?(\d{2}):?(\d{2})?/);
            return match ? `${match[1]}T${match[2]}:${match[3] || '00'}` : d;
          };
          return getDateTime(signalDate) === getDateTime(itemDate);
        };
        return backtestSignals.some(s => 
          s.type === 'SELL' && matchDate(s.date, itemDateStr)
        ) || (backtestSignals.length === 0 && item.signal === 'SELL');
      })(),
    };
  });
  
  // 디버깅: fullChartData에서 신호가 있는 데이터 확인
  React.useEffect(() => {
    const signalItems = fullChartData.filter(d => d.isBacktestBuy || d.isBacktestSell);
    if (signalItems.length > 0) {
      console.log('✅ fullChartData에서 신호 발견:', signalItems.length, '개');
      signalItems.forEach((item, idx) => {
        console.log(`  [${idx}] date: ${item.date}, signal: ${item.isBacktestBuy ? 'BUY' : 'SELL'}, backtestBuyMarker: ${item.backtestBuyMarker}, low: ${item.low}`);
      });
    } else {
      console.log('❌ fullChartData에 신호 없음. 원본 data 확인:', data.filter(d => d.signal === 'BUY' || d.signal === 'SELL').length, '개');
    }
  }, [fullChartData]);
  
  // 일목균형표 선행스팬용 미래 데이터 추가 (26일분)
  const fullChartDataWithFuture = React.useMemo(() => {
    // 선행스팬이 켜져 있지 않으면 원본 데이터 반환
    if (!settings.ichimokuSenkouA && !settings.ichimokuSenkouB) {
      return fullChartData;
    }
    
    if (fullChartData.length < 52) return fullChartData;
    
    const displacement = 26; // 선행스팬 이동 기간
    const result = [...fullChartData];
    const lastData = fullChartData[fullChartData.length - 1];
    
    // 시간 간격 계산 (밀리초)
    let intervalMs = 24 * 60 * 60 * 1000; // 기본 1일
    if (selectedInterval === '1m') intervalMs = 60 * 1000;
    else if (selectedInterval === '3m') intervalMs = 3 * 60 * 1000;
    else if (selectedInterval === '5m') intervalMs = 5 * 60 * 1000;
    else if (selectedInterval === '15m') intervalMs = 15 * 60 * 1000;
    else if (selectedInterval === '30m') intervalMs = 30 * 60 * 1000;
    else if (selectedInterval === '1h') intervalMs = 60 * 60 * 1000;
    else if (selectedInterval === '4h') intervalMs = 4 * 60 * 60 * 1000;
    else if (selectedInterval === '1d') intervalMs = 24 * 60 * 60 * 1000;
    else if (selectedInterval === '1w') intervalMs = 7 * 24 * 60 * 60 * 1000;
    else if (selectedInterval === '1M') intervalMs = 30 * 24 * 60 * 60 * 1000;
    
    // 마지막 데이터의 원본 날짜 파싱
    const lastDateStr = lastData.originalTimestamp || lastData.originalDate;
    const lastDate = lastDateStr ? parseISO(lastDateStr) : new Date();
    
    // 미래 26개 데이터 생성
    for (let i = 1; i <= displacement; i++) {
      const futureDate = new Date(lastDate.getTime() + i * intervalMs);
      const sourceIdx = fullChartData.length - displacement + i - 1;
      
      // 미래 날짜 포맷
      let futureDateStr: string;
      const isMinuteOrHourInterval = selectedInterval.endsWith('m') || selectedInterval.endsWith('h');
      if (isMinuteOrHourInterval) {
        futureDateStr = format(futureDate, 'MM/dd HH:mm');
      } else if (selectedInterval === '1d' || selectedInterval === '1w') {
        futureDateStr = format(futureDate, 'MM/dd') + '일';
      } else if (selectedInterval === '1M') {
        futureDateStr = format(futureDate, 'yy') + '년 ' + format(futureDate, 'MM') + '월';
      } else {
        futureDateStr = format(futureDate, 'MM/dd HH:mm');
      }
      
      // 선행스팬1 계산: (전환선 + 기준선) / 2
      // 전환선: 9일 고저 평균, 기준선: 26일 고저 평균
      let futureSenkouA: number | null = null;
      if (sourceIdx >= 0 && sourceIdx < fullChartData.length) {
        const src = fullChartData[sourceIdx];
        if (src.ichimokuTenkan != null && src.ichimokuKijun != null) {
          futureSenkouA = (src.ichimokuTenkan + src.ichimokuKijun) / 2;
        }
      }
      
      // 선행스팬2 계산: 52일간의 (최고가 + 최저가) / 2
      // 선행스팬1과 다른 계산 방식 - 52일 장기 균형점
      let futureSenkouB: number | null = null;
      if (sourceIdx >= 51 && sourceIdx < fullChartData.length) {
        let highest = -Infinity;
        let lowest = Infinity;
        for (let j = 0; j < 52; j++) {
          const idx = sourceIdx - j;
          if (idx >= 0 && idx < fullChartData.length) {
            const d = fullChartData[idx];
            if (d.high != null) highest = Math.max(highest, d.high);
            if (d.low != null) lowest = Math.min(lowest, d.low);
          }
        }
        if (highest !== -Infinity && lowest !== Infinity) {
          futureSenkouB = (highest + lowest) / 2;
        }
      }
      
      // 디버깅: 미래 데이터 첫 번째만 로그 출력
      if (i === 1) {
        console.log(`📊 일목균형표 미래 선행스팬 (${i}일 후):`, {
          sourceIdx,
          futureSenkouA: futureSenkouA?.toLocaleString(),
          futureSenkouB: futureSenkouB?.toLocaleString(),
          차이: futureSenkouA && futureSenkouB ? Math.abs(futureSenkouA - futureSenkouB).toLocaleString() : 'N/A'
        });
      }
      
      // 미래 데이터 추가 (캔들 데이터는 null, 선행스팬만 값 있음)
      result.push({
        date: futureDateStr,
        originalDate: format(futureDate, 'yyyy-MM-dd'),
        originalTimestamp: futureDate.toISOString(),
        open: null,
        high: null,
        low: null,
        close: null,
        volume: null,
        volumeColor: 'transparent',
        ma12: null,
        ma26: null,
        ma60: null,
        sma10: null,
        sma20: null,
        sma50: null,
        sma60: null,
        sma120: null,
        sma200: null,
        sma300: null,
        sma480: null,
        macdLine: null,
        signalLine: null,
        macdHistogram: null,
        histogramColor: 'transparent',
        stochasticK: null,
        stochasticD: null,
        adx: null,
        plusDI: null,
        minusDI: null,
        cci: null,
        rsi: null,
        ichimokuTenkan: null,
        ichimokuKijun: null,
        ichimokuChikou: null,
        ichimokuSenkouA: futureSenkouA,
        ichimokuSenkouB: futureSenkouB,
        isGoldenCross: false,
        isDeadCross: false,
        isZeroCrossUp: false,
        isZeroCrossDown: false,
        goldenCrossMarker: null,
        deadCrossMarker: null,
        zeroCrossUpMarker: null,
        zeroCrossDownMarker: null,
        goldenCrossMarkerPrice: null,
        deadCrossMarkerPrice: null,
        stochBuyMarkerPrice: null,
        stochSellMarkerPrice: null,
        customBuyMarker: null,
        customSellMarker: null,
        isCustomBuySignal: false,
        isCustomSellSignal: false,
        backtestBuyMarker: null,
        backtestSellMarker: null,
        isBacktestBuy: false,
        isBacktestSell: false,
        isFutureData: true, // 미래 데이터 표시
      } as any);
    }
    
    return result;
  }, [fullChartData, settings.ichimokuSenkouA, settings.ichimokuSenkouB, selectedInterval]);
  
  // viewRange에 따라 표시할 데이터 슬라이스 (줌 기능)
  const chartData = React.useMemo(() => {
    const sliced = fullChartDataWithFuture.slice(viewRange.start, viewRange.end);
    
    // 디버깅: chartData에서 신호 확인
    const signalInChartData = sliced.filter(d => d.backtestBuyMarker !== null || d.backtestSellMarker !== null);
    if (signalInChartData.length > 0) {
      console.log('🎨 chartData(렌더링용)에서 신호 발견:', signalInChartData.length, '개');
    } else {
      console.log('❌ chartData(렌더링용)에 신호 없음! viewRange:', viewRange.start, '~', viewRange.end);
    }
    
    // 데이터 범위 확인 로그
    if (sliced.length > 0) {
      const firstDate = sliced[0]?.date || sliced[0]?.originalDate;
      const lastDate = sliced[sliced.length - 1]?.date || sliced[sliced.length - 1]?.originalDate;
      console.log(`📅 MacdChart chartData 날짜 범위: ${firstDate} ~ ${lastDate}, 총 ${sliced.length}개`);
    }
    
    return sliced;
  }, [fullChartDataWithFuture, viewRange.start, viewRange.end]);
  
  // 차트 데이터를 보조지표 팝업 Context에 업데이트 (데이터 길이가 변경될 때만)
  const chartDataLengthRef = React.useRef(0);
  useEffect(() => {
    if (chartData.length > 0 && chartData.length !== chartDataLengthRef.current) {
      chartDataLengthRef.current = chartData.length;
      setChartData(chartData);
      // 차트 마진 설정 (Y축 기준좌표 동기화용)
      setChartMargin({ top: 5, right: isMobile ? 50 : 65, left: isMobile ? 5 : 55, bottom: 20 });
    }
  }, [chartData.length, setChartData, setChartMargin, isMobile]);
  
  // 외부 호버 시간에서 동기적으로 매칭된 hover 정보 계산 (useMemo)
  // ✅ 외부 동기화가 비활성화되면 계산하지 않음 (항상 null 반환)
  const externalHoverMatch = React.useMemo(() => {
    // ✅ 외부 동기화 비활성화 시 즉시 반환
    if (!isExternalSyncEnabled) {
      return { date: null, index: null };
    }
    
    if (!externalHoverTime || chartData.length === 0) {
      return { date: null, index: null };
    }
    
    // 디버깅: chartData 필드 확인 (최초 1회만)
    if (chartData.length > 0) {
      const sample = chartData[0];
      console.log(`🔍 MacdChart 매칭 시도: externalHoverTime=${externalHoverTime}, chartData.length=${chartData.length}`);
      console.log(`   chartData[0] 필드: date=${sample.date}, originalDate=${sample.originalDate}, originalTimestamp=${sample.originalTimestamp}, timestamp=${sample.timestamp}`);
    }
    
    // 1차: chartData (현재 viewRange) 내에서 정확한 문자열 매칭
    let index = chartData.findIndex(d => 
      d.originalDate === externalHoverTime || 
      d.originalTimestamp === externalHoverTime ||
      d.timestamp === externalHoverTime
    );
    
    if (index !== -1) {
      console.log(`✅ MacdChart 1차 매칭 성공: index=${index}, date=${chartData[index].date}`);
      return { date: chartData[index].date, index };
    }
    
    // 2차: 시간 기반 매칭 (60초 오차 허용)
    try {
      const externalTime = new Date(externalHoverTime).getTime();
      index = chartData.findIndex(d => {
        const itemDate = d.originalDate || d.originalTimestamp || d.timestamp;
        if (!itemDate) return false;
        try {
          const itemTime = new Date(itemDate).getTime();
          return Math.abs(externalTime - itemTime) < 60000;
        } catch {
          return false;
        }
      });
      
      if (index !== -1) {
        console.log(`✅ MacdChart 2차 매칭 성공 (60초 오차): index=${index}, date=${chartData[index].date}`);
        return { date: chartData[index].date, index };
      }
    } catch {
      // ignore
    }
    
    // 3차: 1시간 오차 허용 (시간봉의 경우)
    try {
      const externalTime = new Date(externalHoverTime).getTime();
      index = chartData.findIndex(d => {
        const itemDate = d.originalDate || d.originalTimestamp || d.timestamp;
        if (!itemDate) return false;
        try {
          const itemTime = new Date(itemDate).getTime();
          return Math.abs(externalTime - itemTime) < 3600000; // 1시간
        } catch {
          return false;
        }
      });
      
      if (index !== -1) {
        console.log(`✅ MacdChart 3차 매칭 성공 (1시간 오차): index=${index}, date=${chartData[index].date}`);
        return { date: chartData[index].date, index };
      }
    } catch {
      // ignore
    }
    
    console.log(`❌ MacdChart 매칭 실패: externalHoverTime=${externalHoverTime}`);
    return { date: null, index: null };
  }, [isExternalSyncEnabled, externalHoverTime, chartData]);
  
  // 외부 호버 시간 동기화 (캔들차트 → 보조지표) - 상태 업데이트용
  // ✅ 외부 동기화가 활성화된 경우에만 실행
  React.useEffect(() => {
    // ✅ 외부 동기화 비활성화 시 즉시 반환 - 내부 크로스헤어 상태를 건드리지 않음
    if (!isExternalSyncEnabled) {
      return;
    }
    
    // ✅ externalHoverTime이 실제로 변경되었을 때만 처리
    if (externalHoverTime === lastExternalHoverTimeRef.current) {
      return; // 동일한 값이면 무시
    }
    
    lastExternalHoverTimeRef.current = externalHoverTime;
    
    if (externalHoverTime) {
      // 외부에서 크로스헤어 위치가 설정됨 - 내부 업데이트로 인한 콜백 방지
      isExternalUpdateRef.current = true;
      
      if (externalHoverMatch.date !== null && externalHoverMatch.index !== null) {
        setGlobalHoverDate(externalHoverMatch.date);
        setGlobalHoverIndex(externalHoverMatch.index);
      }
      
      // ✅ 상태 업데이트 완료 후 플래그 리셋 (다음 이벤트 루프에서)
      setTimeout(() => {
        isExternalUpdateRef.current = false;
      }, 50); // 50ms 후 리셋 (충분한 시간 확보)
    } else if (externalHoverTime === null) {
      // externalHoverTime이 명시적으로 null일 때만 호버 해제
      // undefined일 때는 무시 (prop이 전달되지 않은 경우)
      setGlobalHoverDate(null);
      setGlobalHoverIndex(null);
      isExternalUpdateRef.current = false;
    }
  }, [isExternalSyncEnabled, externalHoverMatch, externalHoverTime]);
  
  // 내부 호버 시간 변경을 외부로 전달 (보조지표 → 캔들차트)
  // ✅ 외부 동기화가 활성화된 경우에만 실행
  React.useEffect(() => {
    // ✅ 외부 동기화 비활성화 시 실행하지 않음
    if (!isExternalSyncEnabled) {
      return;
    }
    
    console.log(`🔄 MacdChart 호버 useEffect: globalHoverIndex=${globalHoverIndex}, isExternalUpdate=${isExternalUpdateRef.current}`);
    
    if (!isExternalUpdateRef.current) {
      // 원본 ISO 타임스탬프를 전달 (캔들차트에서 매칭 가능하도록)
      if (globalHoverIndex !== null && chartData[globalHoverIndex]) {
        const item = chartData[globalHoverIndex];
        // originalTimestamp, originalDate, timestamp 순으로 확인
        const originalTime = item.originalTimestamp || item.originalDate || item.timestamp;
        console.log(`📤 MacdChart → CandlestickChart 전달: globalHoverIndex=${globalHoverIndex}, originalTime=${originalTime}`);
        onHoverTimeChange!(originalTime || null);
      } else {
        onHoverTimeChange!(null);
      }
    }
  }, [isExternalSyncEnabled, globalHoverIndex, globalHoverDate, onHoverTimeChange, chartData]);
  
  // 디버깅 로그 (최초 1회만)
  const hasLoggedRef = React.useRef(false);
  React.useEffect(() => {
    if (!hasLoggedRef.current && chartData.length > 0) {
      hasLoggedRef.current = true;
      // stochK 값 10개 샘플
      const stochValues = chartData.slice(0, 10).map(d => 
        d.stochasticK !== null ? Math.round(d.stochasticK) : 'null'
      );
      console.log('📈 StochK 값 샘플 (0-9):', stochValues.join(', '));
      
      // 연속된 값 확인 (같은 값이 몇 개 연속?)
      let sameCount = 0;
      for (let i = 1; i < chartData.length; i++) {
        if (chartData[i].stochasticK === chartData[i-1].stochasticK) {
          sameCount++;
        }
      }
      console.log(`📈 연속 동일값 개수: ${sameCount} / ${chartData.length - 1}`);
      
      // 일목균형표 선행스팬 값 상세 확인
      const senkouAValues = chartData.filter(d => d.ichimokuSenkouA !== null);
      const senkouBValues = chartData.filter(d => d.ichimokuSenkouB !== null);
      
      console.log(`📊 일목균형표 선행스팬 데이터 현황:
  - 전체 데이터: ${chartData.length}개
  - 선행1 유효값: ${senkouAValues.length}개
  - 선행2 유효값: ${senkouBValues.length}개`);
      
      // 마지막 10개 데이터에서 선행1과 선행2 비교
      const lastItems = chartData.slice(-10);
      console.log('📊 일목균형표 선행스팬 (마지막 10개):');
      lastItems.forEach((d, idx) => {
        const a = d.ichimokuSenkouA;
        const b = d.ichimokuSenkouB;
        const diff = (a !== null && b !== null) ? (a - b) : null;
        const pct = (a !== null && b !== null && b !== 0) ? ((a - b) / b * 100).toFixed(2) : 'N/A';
        console.log(`  [${chartData.length - 10 + idx}] 선행1: ${a?.toLocaleString() ?? 'null'}, 선행2: ${b?.toLocaleString() ?? 'null'}, 차이: ${diff?.toLocaleString() ?? 'N/A'} (${pct}%)`);
      });
      
      // 선행1과 선행2가 동일한 값인 경우 카운트
      let sameValueCount = 0;
      let bothValidCount = 0;
      chartData.forEach(d => {
        if (d.ichimokuSenkouA !== null && d.ichimokuSenkouB !== null) {
          bothValidCount++;
          if (d.ichimokuSenkouA === d.ichimokuSenkouB) {
            sameValueCount++;
          }
        }
      });
      console.log(`📊 선행1 == 선행2 동일값: ${sameValueCount} / ${bothValidCount} (${bothValidCount > 0 ? (sameValueCount / bothValidCount * 100).toFixed(1) : 0}%)`);
      
      // 원본 API 데이터의 선행스팬 값 확인 (프론트엔드 변환 전)
      if (data && data.length > 0) {
        const lastApiData = data.slice(-5);
        console.log('🔍 원본 API 응답 선행스팬 (마지막 5개):');
        lastApiData.forEach((d: any, idx: number) => {
          console.log(`  [${data.length - 5 + idx}] API 선행1: ${d.ichimokuSenkouA?.toLocaleString() ?? 'null'}, API 선행2: ${d.ichimokuSenkouB?.toLocaleString() ?? 'null'}`);
        });
      }
      
      // 색상 값 확인
      console.log('🎨 일목균형표 색상 설정:', {
        선행1색상: colors.ichimokuSenkouA,
        선행2색상: colors.ichimokuSenkouB,
        선행1굵기: colors.ichimokuSenkouAWidth,
        선행2굵기: colors.ichimokuSenkouBWidth,
        구름상승색: colors.ichimokuCloudBullish,
        구름하락색: colors.ichimokuCloudBearish
      });
    }
  }, [chartData]);

  // interval에 따른 Y축 범위 계산 (캔들의 high/low 고려)
  const getPriceYAxisDomain = (): [number, number] => {
    // 모든 캔들의 최고가와 최저가를 찾음
    const allLows = chartData.map(d => d.low).filter(v => v !== null && v !== undefined && !isNaN(v));
    const allHighs = chartData.map(d => d.high).filter(v => v !== null && v !== undefined && !isNaN(v));
    
    if (allLows.length === 0 || allHighs.length === 0) {
      return [0, 100];
    }
    
    const dataMin = Math.min(...allLows);
    const dataMax = Math.max(...allHighs);
    const priceRange = dataMax - dataMin;
    const midPrice = (dataMax + dataMin) / 2;
    
    // 실제 변동폭 비율 계산
    const volatilityRatio = priceRange / midPrice;
    
    // 변동폭에 따라 동적으로 여유 공간 조정
    let marginRatio = 0.05; // 기본 5%
    
    if (selectedInterval.endsWith('m')) {
      // 분봉: 변동폭에 따라 0.5%~2% 여유
      if (volatilityRatio < 0.01) {
        // 변동폭이 1% 미만이면 매우 좁게
        marginRatio = 0.005;
      } else if (volatilityRatio < 0.02) {
        marginRatio = 0.01;
      } else {
        marginRatio = 0.015;
      }
    } else if (selectedInterval.endsWith('h')) {
      // 시간봉: 1%~3% 여유
      if (volatilityRatio < 0.02) {
        marginRatio = 0.01;
      } else if (volatilityRatio < 0.05) {
        marginRatio = 0.02;
      } else {
        marginRatio = 0.03;
      }
    } else if (selectedInterval === '1d') {
      // 일봉: 2%~5% 여유
      if (volatilityRatio < 0.05) {
        marginRatio = 0.02;
      } else if (volatilityRatio < 0.1) {
        marginRatio = 0.03;
      } else {
        marginRatio = 0.05;
      }
    } else if (selectedInterval === '1w') {
      // 주봉: 5%~8% 여유
      marginRatio = volatilityRatio < 0.15 ? 0.05 : 0.08;
    } else if (selectedInterval === '1M') {
      // 월봉: 8%~10% 여유
      marginRatio = volatilityRatio < 0.2 ? 0.08 : 0.10;
    }
    
    // 여유 공간을 priceRange에 비례하여 추가
    const margin = priceRange * marginRatio;
    const yMin = dataMin - margin;
    const yMax = dataMax + margin;
    
    // 소수점 자리 수에 맞게 반올림
    const decimalPlaces = midPrice > 1000 ? 0 : midPrice > 100 ? 1 : midPrice > 10 ? 2 : 3;
    const factor = Math.pow(10, decimalPlaces);
    
    return [
      Math.floor(yMin * factor) / factor,
      Math.ceil(yMax * factor) / factor
    ];
  };

  // MACD 차트 Y축 범위 계산 (이상치 제외, 데이터 기반 스케일링)
  // ✅ useMemo로 메모이제이션하여 chartData 변경 시 자동 재계산
  const macdYAxisDomain = React.useMemo((): [number, number] => {
    const macdValues = chartData
      .map(d => d.macdLine)
      .filter(v => v !== null && v !== undefined && !isNaN(v) && isFinite(v));
    const signalValues = chartData
      .map(d => d.signalLine)
      .filter(v => v !== null && v !== undefined && !isNaN(v) && isFinite(v));
    
    const allValues = [...macdValues, ...signalValues];
    
    if (allValues.length === 0) {
      return [-100, 100];
    }
    
    // 이상치 제거를 위해 정렬 후 5%, 95% 백분위 사용
    const sorted = [...allValues].sort((a, b) => a - b);
    const lowerIndex = Math.floor(sorted.length * 0.02); // 하위 2%
    const upperIndex = Math.ceil(sorted.length * 0.98) - 1; // 상위 2%
    
    // 백분위 범위 내 최소/최대값
    const percentileMin = sorted[Math.max(0, lowerIndex)];
    const percentileMax = sorted[Math.min(sorted.length - 1, upperIndex)];
    
    // 전체 데이터의 IQR 기반 이상치 제거
    const q1Index = Math.floor(sorted.length * 0.25);
    const q3Index = Math.floor(sorted.length * 0.75);
    const q1 = sorted[q1Index];
    const q3 = sorted[q3Index];
    const iqr = q3 - q1;
    
    // IQR 기반 허용 범위 (1.5배 IQR)
    const lowerBound = q1 - 1.5 * iqr;
    const upperBound = q3 + 1.5 * iqr;
    
    // 백분위와 IQR 중 더 넓은 범위 사용
    const dataMin = Math.min(percentileMin, lowerBound);
    const dataMax = Math.max(percentileMax, upperBound);
    
    // 데이터 범위의 10% 여유 공간 추가
    const range = dataMax - dataMin;
    const margin = range * 0.1;
    
    // 0을 포함하도록 조정 (MACD는 0선 기준이 중요)
    const yMin = Math.min(dataMin - margin, 0);
    const yMax = Math.max(dataMax + margin, 0);
    
    return [yMin, yMax];
  }, [chartData]);

  // 히스토그램 차트 Y축 범위 계산
  // ✅ useMemo로 메모이제이션하여 chartData 변경 시 자동 재계산
  const histogramYAxisDomain = React.useMemo((): [number, number] => {
    const histogramValues = chartData
      .map(d => d.macdHistogram)
      .filter(v => v !== null && v !== undefined && !isNaN(v) && isFinite(v));
    
    if (histogramValues.length === 0) {
      return [-100, 100];
    }
    
    // 정렬 후 백분위 사용
    const sorted = [...histogramValues].sort((a, b) => a - b);
    const lowerIndex = Math.floor(sorted.length * 0.02);
    const upperIndex = Math.ceil(sorted.length * 0.98) - 1;
    
    const percentileMin = sorted[Math.max(0, lowerIndex)];
    const percentileMax = sorted[Math.min(sorted.length - 1, upperIndex)];
    
    // 데이터 범위의 10% 여유 공간 추가
    const range = percentileMax - percentileMin;
    const margin = range * 0.1;
    
    // 0을 포함하고 대칭으로 조정 (히스토그램은 0 기준 대칭이 좋음)
    const absMax = Math.max(Math.abs(percentileMin - margin), Math.abs(percentileMax + margin));
    
    return [-absMax, absMax];
  }, [chartData]);
  
  // ✅ 하위 호환성을 위한 함수 래퍼 (기존 코드에서 함수 호출하는 경우 대비)
  const getMacdYAxisDomain = (): [number, number] => macdYAxisDomain;
  const getHistogramYAxisDomain = (): [number, number] => histogramYAxisDomain;

  // Y축 값을 표시할 상태 (캔들차트용)
  const [hoveredYValue, setHoveredYValue] = React.useState<number | null>(null);
  const [hoveredYPosition, setHoveredYPosition] = React.useState<number | null>(null);
  
  // 다른 차트들의 Y축 값 표시 상태
  const [volumeYValue, setVolumeYValue] = React.useState<number | null>(null);
  const [volumeYPosition, setVolumeYPosition] = React.useState<number | null>(null);
  const [macdYValue, setMacdYValue] = React.useState<number | null>(null);
  const [macdYPosition, setMacdYPosition] = React.useState<number | null>(null);
  const [histogramYValue, setHistogramYValue] = React.useState<number | null>(null);
  const [histogramYPosition, setHistogramYPosition] = React.useState<number | null>(null);
  const [stochYValue, setStochYValue] = React.useState<number | null>(null);
  const [stochYPosition, setStochYPosition] = React.useState<number | null>(null);
  
  // 기본 투자 전략 및 신호 표시
  const [defaultStrategy] = React.useState<string>(loadDefaultStrategy());
  // 기본 전략 로직 제거됨 - stochSignals 상태 삭제됨
  const [strategyConfig, setStrategyConfig] = React.useState<{
    oversoldLevel: number;
    neutralLevel: number;
    overboughtLevel: number;
  }>({ oversoldLevel: 20, neutralLevel: 50, overboughtLevel: 80 });
  
  // props에서 받은 trendStrength 사용
  const trendStrength = trendStrengthProp;
  
  // 기본 전략의 설정값 로드 (전략이 실제로 변경될 때만)
  const prevDefaultStrategyRef = React.useRef<string | null>(null);
  const strategyConfigLoadedRef = React.useRef(false);
  React.useEffect(() => {
    if (!defaultStrategy) return;
    
    // 이미 로드되었거나 동일한 전략이면 스킵
    if (strategyConfigLoadedRef.current && prevDefaultStrategyRef.current === defaultStrategy) {
      return;
    }
    
    const loadStrategyConfig = async () => {
      try {
        // 전략 목록에서 기본 전략 찾기
        const strategies = await import('../services/tradingStrategyApi').then(m => m.getStrategies(false));
        const defaultStrategyData = strategies.find(s => s.strategyType === defaultStrategy);
        
        if (defaultStrategyData) {
          setStrategyConfig({
            oversoldLevel: defaultStrategyData.stochOversoldThreshold ?? 20,
            neutralLevel: defaultStrategyData.stochNeutralThreshold ?? 50,
            overboughtLevel: defaultStrategyData.stochOverboughtThreshold ?? 80,
          });
          console.log('📋 기본 전략 설정 로드:', {
            strategy: defaultStrategy,
            oversoldLevel: defaultStrategyData.stochOversoldThreshold ?? 20,
            neutralLevel: defaultStrategyData.stochNeutralThreshold ?? 50,
            overboughtLevel: defaultStrategyData.stochOverboughtThreshold ?? 80,
          });
        }
        prevDefaultStrategyRef.current = defaultStrategy;
        strategyConfigLoadedRef.current = true;
      } catch (error) {
        console.error('전략 설정 로드 실패:', error);
      }
    };
    
      loadStrategyConfig();
  }, [defaultStrategy]);
  
  // 기본 전략 로직 제거됨 - 전략 규칙 관리(StrategyRule)만 사용
  // Stochastic 신호 계산 useEffect 삭제됨
  
  const [adxYValue, setAdxYValue] = React.useState<number | null>(null);
  const [adxYPosition, setAdxYPosition] = React.useState<number | null>(null);
  const [adx11YValue, setAdx11YValue] = React.useState<number | null>(null);
  const [adx11YPosition, setAdx11YPosition] = React.useState<number | null>(null);
  const [cciYValue, setCciYValue] = React.useState<number | null>(null);
  const [cciYPosition, setCciYPosition] = React.useState<number | null>(null);
  const [rsiYValue, setRsiYValue] = React.useState<number | null>(null);
  const [rsiYPosition, setRsiYPosition] = React.useState<number | null>(null);

  // 커스텀 툴팁
  const CustomTooltip = ({ active, payload, label }: any) => {
    if (active && payload && payload.length) {
      const data = payload[0].payload;
      const priceChange = data.close - data.open;
      const priceChangePercent = ((priceChange / data.open) * 100).toFixed(2);
      const isRising = data.close >= data.open;
      
      return (
        <Paper sx={{ p: 2, backgroundColor: 'rgba(255, 255, 255, 0.95)' }}>
          <Typography variant="body2" sx={{ fontWeight: 'bold', mb: 1 }}>
            날짜: {label}
          </Typography>
          <Typography variant="body2">
            시가: {data.open?.toLocaleString()}원
          </Typography>
          <Typography variant="body2" color="error">
            고가: {data.high?.toLocaleString()}원
          </Typography>
          <Typography variant="body2" color="primary">
            저가: {data.low?.toLocaleString()}원
          </Typography>
          <Typography variant="body2" sx={{ fontWeight: 'bold' }}>
            종가: {data.close?.toLocaleString()}원
          </Typography>
          <Typography 
            variant="body2" 
            sx={{ 
              fontWeight: 'bold',
              color: isRising ? 'error.main' : 'success.main'
            }}
          >
            변동: {isRising ? '+' : ''}{priceChange.toLocaleString()}원 ({isRising ? '+' : ''}{priceChangePercent}%)
          </Typography>
          {data.volume > 0 && (
            <Typography variant="body2">
              거래량: {data.volume?.toLocaleString()}
            </Typography>
          )}
          <Typography variant="body2" color="error" sx={{ mt: 1 }}>
            MACD: {data.macdLine?.toFixed(2)}
          </Typography>
          <Typography variant="body2" color="warning.main">
            Signal: {data.signalLine?.toFixed(2)}
          </Typography>
          <Typography variant="body2" color="info.main">
            Histogram: {data.macdHistogram?.toFixed(2)}
          </Typography>
          <Typography 
            variant="body2" 
            sx={{ 
              fontWeight: 'bold',
              color: data.signal === 'BUY' ? 'success.main' : 
                     data.signal === 'SELL' ? 'error.main' : 'text.secondary'
            }}
          >
            신호: {data.signal}
          </Typography>
          {/* 기본 전략 신호 표시 제거됨 - 전략 규칙 관리만 사용 */}
        </Paper>
      );
    }
    return null;
  };

  // 커스텀 크로스헤어 (세로선 + 가로선)
  const CustomCursor = (props: any) => {
    const { x, y, width, height, top, left, stroke, offset, points } = props;
    
    // x, y가 없으면 렌더링하지 않음
    if (x === undefined || y === undefined) return null;
    
    const strokeColor = stroke || "#888888";
    
    // offset 객체에서 값을 가져오거나, 직접 전달된 props 사용, 또는 points에서 추출
    const actualTop = offset?.top ?? top ?? 0;
    const actualLeft = offset?.left ?? left ?? 0;
    const actualWidth = offset?.width ?? width ?? 0;
    const actualHeight = offset?.height ?? height ?? 0;
    
    // 마우스 Y 좌표 - points 배열에서 가져오거나 직접 전달된 y 사용
    const cursorY = points && points.length > 0 ? points[0].y : y;
    
    return (
      <g className="recharts-cursor" pointerEvents="none">
        {/* 세로선 (X축) - 차트 전체 높이 */}
        <line
          x1={x}
          y1={actualTop}
          x2={x}
          y2={actualTop + actualHeight}
          stroke={strokeColor}
          strokeWidth={1}
          opacity={0.8}
          pointerEvents="none"
        />
        {/* 가로선 (Y축) - 차트 전체 너비 */}
        {cursorY !== undefined && actualWidth > 0 && (
          <line
            x1={actualLeft}
            y1={cursorY}
            x2={actualLeft + actualWidth}
            y2={cursorY}
            stroke={strokeColor}
            strokeWidth={1}
            opacity={0.8}
            pointerEvents="none"
            strokeDasharray="4 2"
          />
        )}
      </g>
    );
  };
  
  // X축 세로선 + Y축 가로선 크로스헤어 (모든 차트에 동기화 - 보조지표 팝업과도 동기화)
  const VerticalCrosshair = (props: any) => {
    const { xAxisMap, yAxisMap, offset, chartData, showHorizontal = false, globalHoverDate: propGlobalHoverDate, globalHoverYRatio: propGlobalHoverYRatio } = props;
    
    // 우선순위 수정: 내부 globalHoverDate를 우선 사용 (보조지표 내부 호버 시)
    // externalHoverTime이 있어도 propGlobalHoverDate가 있으면 그것을 우선 사용
    const targetDate = propGlobalHoverDate || globalHoverDate || 
      (externalHoverTime ? externalHoverMatch.date : null) || 
      popupHoveredDate;
    
    console.log(`🎯 VerticalCrosshair 렌더링: targetDate=${targetDate}, propGlobalHoverDate=${propGlobalHoverDate}, globalHoverDate=${globalHoverDate}, globalHoverChartX=${globalHoverChartX}`);
    
    if (!targetDate || !xAxisMap || !chartData) {
      console.log(`❌ VerticalCrosshair 조기 종료: targetDate=${targetDate}, hasXAxisMap=${!!xAxisMap}, hasChartData=${!!chartData}`);
      return null;
    }
    
    // 현재 hover 중인 데이터의 인덱스 찾기
    let dataIndex = chartData.findIndex((item: any) => item.date === targetDate);
    if (dataIndex === -1) {
      console.log(`❌ VerticalCrosshair dataIndex 매칭 실패: targetDate=${targetDate}, chartData.length=${chartData.length}`);
      console.log(`첫 5개 데이터의 date:`, chartData.slice(0, 5).map((item: any) => item.date));
      // 대체: globalHoverIndex 사용 시도
      if (globalHoverIndex !== null && globalHoverIndex >= 0 && globalHoverIndex < chartData.length) {
        dataIndex = globalHoverIndex;
        console.log(`✅ globalHoverIndex 대체 사용: ${globalHoverIndex}`);
      } else if (globalHoverChartX !== null && globalHoverChartX !== undefined && !isNaN(globalHoverChartX)) {
        // globalHoverChartX가 있으면 dataIndex 없이도 진행 (X 좌표만 사용)
        console.log(`✅ globalHoverChartX 있음, dataIndex 없이 진행: ${globalHoverChartX}`);
        dataIndex = -1; // 유지하되 계속 진행
      } else {
        console.log(`❌ dataIndex, globalHoverIndex, globalHoverChartX 모두 없음`);
        return null;
      }
    }
    
    let x: number;
    
    // ★ 우선순위 1: globalHoverChartX가 있으면 항상 사용 (모든 차트가 동일한 X 좌표 공유)
    if (globalHoverChartX !== null && globalHoverChartX !== undefined && !isNaN(globalHoverChartX)) {
      x = globalHoverChartX;
      console.log(`✅ VerticalCrosshair X좌표 (공유 마우스 좌표): x=${x}, globalHoverChartX=${globalHoverChartX}`);
    } else {
      // ★ 우선순위 2: recharts scale을 사용하여 정확한 X 좌표 계산
      const xAxis = xAxisMap?.[0];
      
      if (xAxis && xAxis.scale && dataIndex >= 0 && dataIndex < chartData.length) {
        // dataIndex가 유효하면 해당 데이터의 date로 scale 계산
        const dateAtIndex = chartData[dataIndex].date;
        x = xAxis.scale(dateAtIndex);
        
        // scale이 band scale인 경우 중앙 위치로 조정
        if (xAxis.scale.bandwidth) {
          x += xAxis.scale.bandwidth() / 2;
        }
        
        console.log(`✅ VerticalCrosshair X좌표 (scale 사용): x=${x}, dataIndex=${dataIndex}, dateAtIndex=${dateAtIndex}`);
      } else {
        // fallback: 기존 수동 계산 방식
        const chartLeft = offset?.left || 0;
        const chartWidth = offset?.width || 0;
        const dataCount = chartData.length;
        
        const effectiveWidth = chartWidth - 20; // padding 보정
        const effectiveLeft = chartLeft + 10;
        const pointWidth = dataCount > 1 ? effectiveWidth / (dataCount - 1) : effectiveWidth;
        x = effectiveLeft + (dataIndex * pointWidth);
        
        console.log(`⚠️ VerticalCrosshair X좌표 (fallback): x=${x}, dataIndex=${dataIndex}`);
      }
    }
    
    if (x === undefined || isNaN(x)) {
      console.log(`❌ VerticalCrosshair X좌표 계산 실패: x=${x}`);
      return null;
    }
    
    const chartTop = offset?.top || 0;
    const chartHeight = offset?.height || 0;
    const y1 = chartTop;
    const y2 = chartTop + chartHeight;
    const x1 = offset?.left || 0;
    const x2 = (offset?.left || 0) + (offset?.width || 0);
    
    // 가로선 Y좌표 - 상대적 비율을 사용하여 현재 차트에 맞게 변환
    const yRatio = propGlobalHoverYRatio ?? globalHoverYRatio;
    const horizontalY = yRatio !== null ? chartTop + (yRatio * chartHeight) : null;
    console.log(`🎯 VerticalCrosshair 가로선: yRatio=${yRatio?.toFixed(3)}, propGlobalHoverYRatio=${propGlobalHoverYRatio?.toFixed(3)}, globalHoverYRatio=${globalHoverYRatio?.toFixed(3)}, horizontalY=${horizontalY?.toFixed(1)}, showHorizontal=${showHorizontal}`);
    
    // Y축 값 계산 (가로선에 표시할 값)
    let yValue: number | null = null;
    if (showHorizontal && horizontalY !== null && yAxisMap) {
      const yAxis = yAxisMap?.[0];
      if (yAxis && yAxis.scale && yAxis.scale.invert) {
        yValue = yAxis.scale.invert(horizontalY - chartTop);
      }
    }
    
    // X축 라벨 포맷 (세로선에 표시할 날짜/시간)
    const formatDateLabel = (dateStr: string) => {
      try {
        const date = new Date(dateStr);
        const hours = date.getHours().toString().padStart(2, '0');
        const minutes = date.getMinutes().toString().padStart(2, '0');
        const month = (date.getMonth() + 1).toString().padStart(2, '0');
        const day = date.getDate().toString().padStart(2, '0');
        return `${month}/${day} ${hours}:${minutes}`;
      } catch {
        return dateStr;
      }
    };
    
    const dateLabel = formatDateLabel(targetDate);
    
    return (
      <g pointerEvents="none">
        {/* 세로선 (X축 방향) */}
        <line
          x1={x}
          y1={y1}
          x2={x}
          y2={y2}
          stroke="#888888"
          strokeWidth={1}
          opacity={0.8}
        />
        
        {/* 세로선 하단에 날짜/시간 라벨 */}
        <g>
          <rect
            x={x - 30}
            y={y2 + 2}
            width={60}
            height={16}
            fill="#383838"
            opacity={0.9}
            rx={2}
          />
          <text
            x={x}
            y={y2 + 13}
            textAnchor="middle"
            fill="#ffffff"
            fontSize="10"
            fontFamily="monospace"
          >
            {dateLabel}
          </text>
        </g>
        
        {/* 가로선 (Y축 방향) - showHorizontal이 true이고 horizontalY가 있을 때만 */}
        {showHorizontal && horizontalY !== null && (
          <>
            <line
              x1={x1}
              y1={horizontalY}
              x2={x2}
              y2={horizontalY}
              stroke="#888888"
              strokeWidth={1}
              opacity={0.8}
              strokeDasharray="4 2"
            />
            
            {/* 가로선 좌측에 Y값 라벨 */}
            {yValue !== null && (
              <g>
                <rect
                  x={x1 - 45}
                  y={horizontalY - 8}
                  width={42}
                  height={16}
                  fill="#383838"
                  opacity={0.9}
                  rx={2}
                />
                <text
                  x={x1 - 24}
                  y={horizontalY + 4}
                  textAnchor="middle"
                  fill="#ffffff"
                  fontSize="10"
                  fontFamily="monospace"
                >
                  {yValue.toFixed(2)}
                </text>
              </g>
            )}
          </>
        )}
      </g>
    );
  };

  // 마우스 이동 시 Y값 추적 (전역 인덱스도 업데이트)
  const handleChartMouseMove = (state: any, chartType: string = 'candle') => {
    console.log(`🖱️ handleChartMouseMove 호출됨: chartType=${chartType}, hasPayload=${!!state?.activePayload}, index=${state?.activeTooltipIndex}`);
    
    if (!state) {
      console.log(`⚠️ handleChartMouseMove: state is null/undefined`);
      return;
    }
    
    // X, Y축 위치 계산 (크로스헤어용) - activeCoordinate 또는 chartX/chartY 사용
    const mouseX = state.activeCoordinate?.x ?? state.chartX;
    const mouseY = state.activeCoordinate?.y ?? state.chartY;
    
    console.log(`🖱️ handleChartMouseMove: chartType=${chartType}, mouseX=${mouseX}, mouseY=${mouseY}, hasPayload=${!!state?.activePayload}, externalHoverTime=${externalHoverTime}`);
    
    // ★ mouseX가 유효할 때만 globalHoverChartX 설정 및 콜백 호출
    if (mouseX !== undefined && mouseX !== null && !isNaN(mouseX)) {
      setGlobalHoverChartX(mouseX);
      console.log(`✅ globalHoverChartX 설정 성공: x=${mouseX}, chartType=${chartType}`);
      
      // ★ CandlestickChart 동기화를 위한 콜백 호출
      if (onCrosshairXChange) {
        console.log(`🚀 [MacdChart] onCrosshairXChange 호출 준비: x=${mouseX}`);
        onCrosshairXChange(mouseX);
      }
    } else {
      console.log(`⚠️ mouseX undefined - globalHoverChartX 리셋 필요`);
      // ★ mouseX가 없으면 크로스헤어 리셋 (캔들차트 overlay 제거)
      if (onCrosshairXChange) {
        console.log(`🚀 [MacdChart] onCrosshairXChange 호출 (리셋): null`);
        onCrosshairXChange(null);
      }
    }
    
    // ★ 보조지표 차트에서 마우스가 움직이면 내부 호버로 전환
    // ✅ 외부 동기화가 활성화된 경우에만 isExternalUpdateRef 조작
    if (isExternalSyncEnabled) {
      // 외부 동기화 활성화 시: externalHoverTime이 없으면 내부 호버 모드
      if (!externalHoverTime) {
        isExternalUpdateRef.current = false;
      }
    }
    // ✅ 외부 동기화 비활성화 시: isExternalUpdateRef를 건드리지 않음 (항상 false 유지)
    
    if (mouseY !== undefined) {
      setGlobalHoverChartY(mouseY);
      setHoveredYPosition(mouseY);
    }
    
    // offset 정보 저장 (크로스헤어 렌더링용)
    if (state.offset) {
      setChartOffset(state.offset);
      
      // 상대적 Y 비율 계산 (차트 영역 내에서 0~1)
      if (mouseY !== undefined) {
        const chartTop = state.offset.top || 0;
        const chartHeight = state.offset.height || 1;
        const relativeY = mouseY - chartTop;
        const yRatio = Math.max(0, Math.min(1, relativeY / chartHeight));
        setGlobalHoverYRatio(yRatio);
      }
    }
    
    // 데이터 포인트 정보 업데이트
    if (state.activePayload && state.activePayload.length > 0) {
      const payload = state.activePayload[0].payload;
      const index = state.activeTooltipIndex;
      
      // 전역 마우스 위치 업데이트 (모든 차트 동기화용)
      if (index !== undefined && payload.date) {
        setGlobalHoverIndex(index);
        setGlobalHoverLabel(payload.date);
        setGlobalHoverDate(payload.date); // ReferenceLine용
        // 보조지표 팝업과 동기화
        setPopupHoveredDate(payload.date);
        setPopupHoveredIndex(index);
        
        // 차트 상세보기를 위한 hover 상태 업데이트
        // chartData는 viewRange로 슬라이스된 데이터이므로, 절대 인덱스로 변환 필요
        const absoluteIndex = index + viewRange.start;
        // ref에 즉시 저장 (비동기 문제 없이 최신 값 접근 가능)
        hoveredDataIndexRef.current = absoluteIndex;
        hoveredChartTypeRef.current = chartType;
        // 매매 신호 확인
        const signal = payload.signal === 'BUY' ? 'BUY' : payload.signal === 'SELL' ? 'SELL' : null;
        hoveredSignalRef.current = signal;
        // state도 업데이트 (UI 렌더링용)
        setHoveredDataIndex(absoluteIndex);
        setHoveredChartType(chartType);
        setHoveredSignal(signal);
        
        // onChartHover는 즉시 호출 (차트 상세보기 팝업 즉시 갱신) - 절대 인덱스 전달
        if (onChartHover) {
          onChartHover(absoluteIndex, chartType);
        }
        
        // ★ 보조지표 → 캔들차트 크로스헤어 동기화: 직접 onHoverTimeChange 호출
        // ✅ 외부 동기화가 활성화된 경우에만 호출
        if (isExternalSyncEnabled && onHoverTimeChange) {
          const originalTime = payload.originalTimestamp || payload.originalDate || payload.timestamp || payload.date;
          if (originalTime) {
            console.log(`📤 MacdChart → CandlestickChart 직접 전달: index=${index}, originalTime=${originalTime}, chartType=${chartType}`);
            onHoverTimeChange(originalTime);
          } else {
            console.warn(`⚠️ [${chartType}] originalTime을 찾을 수 없음:`, payload);
          }
        }
      }
      
      setHoveredYValue(payload.close);
    }
  };

  const handleChartMouseLeave = () => {
    console.log(`🚪 handleChartMouseLeave 호출: externalHoverTime=${externalHoverTime}`);
    
    // ✅ 외부 동기화가 활성화된 경우에만 외부 크로스헤어 해제
    if (isExternalSyncEnabled && onHoverTimeChange) {
      console.log(`🔄 MacdChart → 외부 크로스헤어 해제: onHoverTimeChange(null)`);
      onHoverTimeChange(null);
    }
    
    // 모든 내부 호버 상태 완전히 리셋
    setGlobalHoverIndex(null);
    setGlobalHoverLabel(null);
    setGlobalHoverDate(null);
    setGlobalHoverChartX(null);
    setGlobalHoverChartY(null);
    setGlobalHoverYRatio(null);
    setChartOffset(null);
    setPopupHoveredDate(null);
    setPopupHoveredIndex(null);
    
    // 차트 상세보기 hover 상태는 유지 (버튼 클릭을 위해)
    // setHoveredDataIndex(-1);
    // setHoveredChartType('candle');
    // setHoveredSignal(null);
    
    setHoveredYValue(null);
    
    // 외부 업데이트 플래그 리셋
    isExternalUpdateRef.current = false;
  };

  // 차트 클릭 시 차트 상세보기 팝업 갱신
  const handleChartClick = (state: any, chartType: string = 'candle') => {
    if (state && state.activePayload && state.activePayload.length > 0) {
      const payload = state.activePayload[0].payload;
      
      if (payload && payload.date) {
        // payload의 date를 사용하여 chartData에서 인덱스 찾기
        const clickedDate = payload.date;
        const chartDataIndex = chartData.findIndex(item => item.date === clickedDate);
        
        // chartData는 viewRange로 슬라이스된 데이터이므로, 
        // 원본 data의 인덱스를 구하려면 viewRange.start를 더해야 함
        const absoluteIndex = chartDataIndex >= 0 
          ? chartDataIndex + viewRange.start 
          : (state.activeTooltipIndex !== undefined ? state.activeTooltipIndex + viewRange.start : -1);
        
        if (absoluteIndex >= 0 && absoluteIndex < data.length) {
          // 차트 상세보기를 위한 상태 업데이트
          setHoveredDataIndex(absoluteIndex);
          setHoveredChartType(chartType);
          // 매매 신호 확인
          const signal = payload.signal === 'BUY' ? 'BUY' : payload.signal === 'SELL' ? 'SELL' : null;
          setHoveredSignal(signal);
          
          // 외부 hover 콜백 호출 (차트 상세보기 팝업 갱신)
          if (onChartHover) {
            onChartHover(absoluteIndex, chartType);
          }
          
          // 차트 영역 클릭 콜백 호출
          if (onChartAreaClick) {
            onChartAreaClick(absoluteIndex, chartType);
          }
        }
      }
    }
    setHoveredYPosition(null);
    
    // 모든 다른 차트의 Y축 값도 초기화
    setVolumeYValue(null);
    setVolumeYPosition(null);
    setMacdYValue(null);
    setMacdYPosition(null);
    setHistogramYValue(null);
    setHistogramYPosition(null);
    setStochYValue(null);
    setStochYPosition(null);
    setAdxYValue(null);
    setAdxYPosition(null);
    setAdx11YValue(null);
    setAdx11YPosition(null);
    setCciYValue(null);
    setCciYPosition(null);
    setRsiYValue(null);
    setRsiYPosition(null);
  };

  // 차트 영역 클릭 핸들러 (Box onClick용)
  const handleChartAreaClickInternal = (chartType: string) => {
    // ref를 사용하여 즉시 최신 hover 인덱스에 접근
    if (hoveredDataIndexRef.current >= 0 && onChartAreaClick) {
      onChartAreaClick(hoveredDataIndexRef.current, chartType);
    }
  };

  // ===== 업비트 스타일 차트 레이아웃 상수 =====
  const BASE_CHART_HEIGHT = 650; // 기본 전체 차트 높이
  const INDICATOR_HEIGHT = settings.indicatorHeight || 70; // 사용자 설정 보조지표 차트 높이
  const INDICATOR_XAXIS_EXTRA = 40; // X축 표시 시 추가 높이 (그래프 영역 유지용)
  const INDICATOR_GAP = 0; // 보조지표 간 간격 (연속된 차트처럼 보이도록 0)
  const MIN_CANDLE_HEIGHT = 280; // 캔들차트 최소 높이 (너무 작아지지 않도록)
  const TOOLBAR_HEIGHT = 40; // 상단 툴바 높이
  // 모든 차트에 동일한 margin 사용 (세로선 정렬을 위해)
  // ★ left margin을 YAxis width(50) + 여유공간(10)으로 설정하여 모든 차트의 차트 영역 시작점을 동일하게 맞춤
  const CHART_MARGIN = { top: 5, right: isMobile ? 50 : 65, left: isMobile ? 5 : 60, bottom: 20 }; // 통일된 margin (X축 포함)
  const CHART_MARGIN_WITH_XAXIS = { top: 5, right: isMobile ? 50 : 65, left: isMobile ? 5 : 60, bottom: 20 }; // X축 표시용 margin
  
  // 활성화된 보조지표 목록 (순서대로 - 15가지)
  const getActiveIndicators = () => {
    const indicators: string[] = [];
    if (settings.volume) indicators.push('volume');
    if (settings.newPsychological) indicators.push('newPsychological');
    if (settings.investPsychological) indicators.push('investPsychological');
    if (settings.macdLine || settings.macdHistogram) indicators.push('macd');
    if (settings.cci) indicators.push('cci');
    if (settings.adx) indicators.push('adx');
    if (settings.bwi) indicators.push('bwi');
    if (settings.plusDi || settings.minusDi) indicators.push('dmi');
    if (settings.obv) indicators.push('obv');
    if (settings.rsi) indicators.push('rsi');
    if (settings.stochasticK || settings.stochasticD || settings.stochasticSlow) indicators.push('stochastic');
    if (settings.williamsR) indicators.push('williamsR');
    if (settings.trix || settings.trixSignal) indicators.push('trix');
    if (settings.volumeOsc) indicators.push('volumeOsc');
    if (settings.vr) indicators.push('vr');
    if (settings.disparity) indicators.push('disparity');
    return indicators;
  };
  
  // 활성화된 보조지표 개수 계산
  const getActiveIndicatorCount = () => {
    return getActiveIndicators().length;
  };
  
  // 마지막 활성화된 보조지표인지 확인
  const isLastIndicator = (indicatorName: string) => {
    const indicators = getActiveIndicators();
    return indicators.length > 0 && indicators[indicators.length - 1] === indicatorName;
  };
  
  // X축 표시 여부 확인 (마지막 보조지표에만 표시, 보조지표 없으면 캔들차트에 표시)
  const shouldShowXAxis = (indicatorName: string) => {
    const indicators = getActiveIndicators();
    if (indicators.length === 0) return false; // 캔들차트에서 처리
    return isLastIndicator(indicatorName);
  };
  
  // 캔들차트 높이 계산 (보조지표 개수에 따라 동적 조절, 최소 높이 보장)
  const getCandleChartHeight = () => {
    const activeCount = getActiveIndicatorCount();
    const indicatorsHeight = activeCount * (INDICATOR_HEIGHT + INDICATOR_XAXIS_EXTRA + INDICATOR_GAP);
    const candleHeight = BASE_CHART_HEIGHT - TOOLBAR_HEIGHT - indicatorsHeight;
    return Math.max(candleHeight, MIN_CANDLE_HEIGHT);
  };
  
  // 표시되는 차트 개수에 따라 전체 높이 계산 (캔들차트 최소 높이 보장)
  const calculateTotalHeight = () => {
    if (isFullscreen) {
      return window.innerHeight - 180;
    }
    const activeCount = getActiveIndicatorCount();
    const indicatorsHeight = activeCount * (INDICATOR_HEIGHT + INDICATOR_XAXIS_EXTRA + INDICATOR_GAP);
    const candleHeight = getCandleChartHeight();
    // 캔들차트 높이 + 보조지표 높이 + 툴바 높이
    return Math.max(BASE_CHART_HEIGHT, candleHeight + indicatorsHeight + TOOLBAR_HEIGHT);
  };

  // 차트 영역 전용 마우스/터치 추적 (돋보기용)
  React.useEffect(() => {
    if (!magnifierEnabled) {
      setChartMousePos(null);
      return;
    }

    const handleGlobalMouseMove = (e: MouseEvent) => {
      if (chartRef.current) {
        const rect = chartRef.current.getBoundingClientRect();
        // 차트 영역 내부인지 확인
        if (
          e.clientX >= rect.left &&
          e.clientX <= rect.right &&
          e.clientY >= rect.top &&
          e.clientY <= rect.bottom
        ) {
          window.requestAnimationFrame(() => {
            setChartMousePos({
              x: e.clientX,
              y: e.clientY,
            });
            setMagnifierPosition({
              x: e.clientX - rect.left,
              y: e.clientY - rect.top,
            });
          });
        } else {
          setChartMousePos(null);
          setMagnifierPosition(null);
        }
      }
    };

    // 터치 이벤트 핸들러 (모바일/태블릿)
    const handleGlobalTouchMove = (e: TouchEvent) => {
      if (chartRef.current && e.touches.length > 0) {
        const touch = e.touches[0];
        const rect = chartRef.current.getBoundingClientRect();
        // 차트 영역 내부인지 확인
        if (
          touch.clientX >= rect.left &&
          touch.clientX <= rect.right &&
          touch.clientY >= rect.top &&
          touch.clientY <= rect.bottom
        ) {
          window.requestAnimationFrame(() => {
            setChartMousePos({
              x: touch.clientX,
              y: touch.clientY,
            });
            setMagnifierPosition({
              x: touch.clientX - rect.left,
              y: touch.clientY - rect.top,
            });
          });
        } else {
          setChartMousePos(null);
          setMagnifierPosition(null);
        }
      }
    };

    const handleGlobalTouchEnd = () => {
      setChartMousePos(null);
      setMagnifierPosition(null);
    };

    window.addEventListener('mousemove', handleGlobalMouseMove, { passive: true });
    window.addEventListener('touchmove', handleGlobalTouchMove, { passive: true });
    window.addEventListener('touchend', handleGlobalTouchEnd, { passive: true });
    return () => {
      window.removeEventListener('mousemove', handleGlobalMouseMove);
      window.removeEventListener('touchmove', handleGlobalTouchMove);
      window.removeEventListener('touchend', handleGlobalTouchEnd);
    };
  }, [magnifierEnabled]);

  // 차트 영역 마우스 이동 핸들러
  // 크로스헤어용 마우스 좌표 상태 (컨테이너 크기 포함)
  const [crosshairPos, setCrosshairPos] = React.useState<{ x: number; y: number; width: number; height: number } | null>(null);
  // 캔들차트 컨테이너 크기 (보조지표에서 마우스 이동 시에도 크로스헤어 표시용)
  const [candleChartSize, setCandleChartSize] = React.useState<{ width: number; height: number } | null>(null);
  // 캔들차트에서 직접 마우스 호버 중인지 여부
  const [isHoveringCandleChart, setIsHoveringCandleChart] = React.useState(false);
  
  // 디버깅 로그 쓰로틀링을 위한 ref
  const lastMouseMoveLogRef = useRef<number>(0);
  
  const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
    if (chartRef.current) {
      const rect = chartRef.current.getBoundingClientRect();
      const x = e.clientX - rect.left;
      const y = e.clientY - rect.top;
      
      // 크로스헤어 위치 및 컨테이너 크기 저장
      setCrosshairPos({ x, y, width: rect.width, height: rect.height });
      // 캔들차트 크기 저장 (보조지표에서 사용)
      setCandleChartSize({ width: rect.width, height: rect.height });
      setIsHoveringCandleChart(true);
      
      // 돋보기 기능
      if (magnifierEnabled) {
        setMagnifierPosition({ x, y });
      }
      
      // Y 비율 계산 (전략체크 동기화용)
      const yRatio = Math.max(0, Math.min(1, y / rect.height));
      setGlobalHoverYRatio(yRatio);
      setGlobalHoverChartX(x);
      
      // X 좌표에서 캔들 데이터 찾기 (전략체크용)
      const chartAreaLeft = 50;
      const chartAreaRight = 10;
      const chartAreaWidth = rect.width - chartAreaLeft - chartAreaRight;
      
      if (x >= chartAreaLeft && x <= rect.width - chartAreaRight && chartData.length > 0) {
        const relativeX = x - chartAreaLeft;
        const ratio = relativeX / chartAreaWidth;
        const index = Math.floor(ratio * chartData.length);
        const clampedIndex = Math.max(0, Math.min(chartData.length - 1, index));
        const candleData = chartData[clampedIndex];
        
        if (candleData && candleData.date) {
          // 디버깅: 호버 위치 업데이트 확인 (쓰로틀링을 위해 500ms마다 로그)
          const now = Date.now();
          if (now - lastMouseMoveLogRef.current > 500) {
            console.log('🖱️ 캔들차트 마우스 이동:', {
              x, 
              chartDataLength: chartData.length,
              clampedIndex, 
              date: candleData.date
            });
            lastMouseMoveLogRef.current = now;
          }
          
          setGlobalHoverIndex(clampedIndex);
          setGlobalHoverLabel(candleData.date);
          setGlobalHoverDate(candleData.date);
          setPopupHoveredDate(candleData.date);
          setPopupHoveredIndex(clampedIndex);
        }
      }
    }
  };

  const handleMouseLeave = () => {
    // 캔들차트 직접 호버 해제 (크로스헤어는 보조지표에서 계속 표시될 수 있음)
    setIsHoveringCandleChart(false);
    setCrosshairPos(null);
    
    if (magnifierEnabled) {
      setMagnifierPosition(null);
    }
    
    // 전략체크용 전역 hover 상태 초기화
    setGlobalHoverYRatio(null);
    setGlobalHoverChartX(null);
    setGlobalHoverIndex(null);
    setGlobalHoverLabel(null);
    setGlobalHoverDate(null);
    setPopupHoveredDate(null);
    setPopupHoveredIndex(null);
  };

  // X 좌표에서 캔들 인덱스 및 날짜 계산 함수
  const getDataFromXPosition = (x: number, width: number) => {
    // 차트 마진 고려 (좌측 Y축 영역 60px, 우측 패딩 10px) - 보조지표 차트 YAxis width={60}과 동일
    const chartAreaLeft = 60;
    const chartAreaRight = 10;
    const chartAreaWidth = width - chartAreaLeft - chartAreaRight;
    
    // X 좌표가 차트 영역 내인지 확인
    if (x < chartAreaLeft || x > width - chartAreaRight) return null;
    
    // 차트 영역 내 상대적 X 위치
    const relativeX = x - chartAreaLeft;
    const ratio = relativeX / chartAreaWidth;
    
    // 캔들 인덱스 계산
    const index = Math.floor(ratio * chartData.length);
    const clampedIndex = Math.max(0, Math.min(chartData.length - 1, index));
    
    return chartData[clampedIndex] || null;
  };

  // 보조지표 차트에서 마우스 이동 시 Y 비율 계산 (캔들차트 크로스헤어 동기화용)
  const handleIndicatorMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
    const target = e.currentTarget;
    const rect = target.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    const width = rect.width;
    const height = rect.height;
    
    // Y 비율 계산 (0 = 상단, 1 = 하단)
    const yRatio = Math.max(0, Math.min(1, y / height));
    console.log(`📍 handleIndicatorMouseMove: y=${y.toFixed(1)}, height=${height.toFixed(1)}, yRatio=${yRatio.toFixed(3)}`);
    setGlobalHoverYRatio(yRatio);
    setGlobalHoverChartX(x);
    
    // X 좌표에서 캔들 데이터 찾기
    const data = getDataFromXPosition(x, width);
    if (data && data.date) {
      const index = chartData.findIndex(d => d.date === data.date);
      setGlobalHoverIndex(index);
      setGlobalHoverLabel(data.date);
      setGlobalHoverDate(data.date);
      setPopupHoveredDate(data.date);
      setPopupHoveredIndex(index);
    }
  };

  const handleIndicatorMouseLeave = () => {
    console.log(`🚪 handleIndicatorMouseLeave 호출: externalHoverTime=${externalHoverTime}`);
    
    // ✅ 외부 동기화가 활성화된 경우에만 외부 크로스헤어 해제
    if (isExternalSyncEnabled && onHoverTimeChange) {
      console.log(`🔄 MacdChart → 외부 크로스헤어 해제: onHoverTimeChange(null)`);
      onHoverTimeChange(null);
    }
    
    // 모든 호버 상태 완전히 리셋
    setGlobalHoverYRatio(null);
    setGlobalHoverChartX(null);
    setGlobalHoverIndex(null);
    setGlobalHoverLabel(null);
    setGlobalHoverDate(null);
    setPopupHoveredDate(null);
    setPopupHoveredIndex(null);
    
    // 외부 업데이트 플래그 리셋
    isExternalUpdateRef.current = false;
  };

  // 보조지표 터치 이벤트 핸들러
  const handleIndicatorTouchStart = (e: React.TouchEvent<HTMLDivElement>) => {
    if (e.touches.length !== 1) return;
    const touch = e.touches[0];
    const target = e.currentTarget;
    const rect = target.getBoundingClientRect();
    const x = touch.clientX - rect.left;
    const y = touch.clientY - rect.top;
    const width = rect.width;
    const height = rect.height;
    
    const yRatio = Math.max(0, Math.min(1, y / height));
    setGlobalHoverYRatio(yRatio);
    setGlobalHoverChartX(x);
    
    // X 좌표에서 캔들 데이터 찾기
    const data = getDataFromXPosition(x, width);
    if (data && data.date) {
      const index = chartData.findIndex(d => d.date === data.date);
      setGlobalHoverIndex(index);
      setGlobalHoverLabel(data.date);
      setGlobalHoverDate(data.date);
      setPopupHoveredDate(data.date);
      setPopupHoveredIndex(index);
    }
  };

  const handleIndicatorTouchMove = (e: React.TouchEvent<HTMLDivElement>) => {
    if (e.touches.length !== 1) return;
    e.preventDefault();
    const touch = e.touches[0];
    const target = e.currentTarget;
    const rect = target.getBoundingClientRect();
    const x = touch.clientX - rect.left;
    const y = touch.clientY - rect.top;
    const width = rect.width;
    const height = rect.height;
    
    const yRatio = Math.max(0, Math.min(1, y / height));
    setGlobalHoverYRatio(yRatio);
    setGlobalHoverChartX(x);
    
    // X 좌표에서 캔들 데이터 찾기
    const data = getDataFromXPosition(x, width);
    if (data && data.date) {
      const index = chartData.findIndex(d => d.date === data.date);
      setGlobalHoverIndex(index);
      setGlobalHoverLabel(data.date);
      setGlobalHoverDate(data.date);
      setPopupHoveredDate(data.date);
      setPopupHoveredIndex(index);
    }
  };

  const handleIndicatorTouchEnd = () => {
    setGlobalHoverYRatio(null);
    setGlobalHoverChartX(null);
    setGlobalHoverIndex(null);
    setGlobalHoverLabel(null);
    setGlobalHoverDate(null);
    setPopupHoveredDate(null);
    setPopupHoveredIndex(null);
  };

  // 터치 이벤트 핸들러 (모바일/태블릿 지원)
  const handleTouchMove = (e: React.TouchEvent<HTMLDivElement>) => {
    if (magnifierEnabled && chartRef.current && e.touches.length > 0) {
      const touch = e.touches[0];
      const rect = chartRef.current.getBoundingClientRect();
      setMagnifierPosition({
        x: touch.clientX - rect.left,
        y: touch.clientY - rect.top,
      });
    }
  };

  const handleTouchEnd = () => {
    if (magnifierEnabled) {
      setMagnifierPosition(null);
    }
  };

  // 터치로 차트 데이터 인덱스 계산 (크로스헤어 동기화용) - 공통 함수
  const calculateTouchChartIndex = useCallback((touchX: number, touchY: number) => {
    if (!chartRef.current || chartData.length === 0) return;
    
    const rect = chartRef.current.getBoundingClientRect();
    const x = touchX - rect.left;
    
    // 차트 영역 내 비율로 데이터 인덱스 계산
    const chartLeft = CHART_MARGIN_WITH_XAXIS.left;
    const chartRight = rect.width - CHART_MARGIN_WITH_XAXIS.right;
    const chartWidth = chartRight - chartLeft;
    
    if (x >= chartLeft && x <= chartRight) {
      const ratio = (x - chartLeft) / chartWidth;
      const index = Math.min(Math.max(0, Math.floor(ratio * chartData.length)), chartData.length - 1);
      const data = chartData[index];
      
      if (data && data.date) {
        setGlobalHoverIndex(index);
        setGlobalHoverLabel(data.date);
        setGlobalHoverDate(data.date);
        setPopupHoveredDate(data.date);
        setPopupHoveredIndex(index);
        setHoveredYValue(data.close);
        
        // Y축 위치도 계산
        const y = touchY - rect.top;
        setHoveredYPosition(y);
      }
    }
  }, [chartData, CHART_MARGIN_WITH_XAXIS, setPopupHoveredDate, setPopupHoveredIndex]);

  // 터치 시작 시 크로스헤어 표시
  const handleChartTouchStart = useCallback((e: React.TouchEvent<HTMLDivElement>) => {
    if (e.touches.length === 0) return;
    const touch = e.touches[0];
    calculateTouchChartIndex(touch.clientX, touch.clientY);
  }, [calculateTouchChartIndex]);

  // 터치 이동 시 크로스헤어 업데이트
  const handleChartTouchMove = useCallback((e: React.TouchEvent<HTMLDivElement>) => {
    if (e.touches.length === 0) return;
    const touch = e.touches[0];
    calculateTouchChartIndex(touch.clientX, touch.clientY);
  }, [calculateTouchChartIndex]);

  const handleChartTouchEnd = useCallback(() => {
    setGlobalHoverIndex(null);
    setGlobalHoverLabel(null);
    setGlobalHoverDate(null);
    setPopupHoveredDate(null);
    setPopupHoveredIndex(null);
    setHoveredYValue(null);
    setHoveredYPosition(null);
  }, [setPopupHoveredDate, setPopupHoveredIndex]);

  // ===== 핀치 줌 & 스와이프 팬 기능 (태블릿/모바일) =====
  const [touchState, setTouchState] = useState<{
    isPinching: boolean;
    isPanning: boolean;
    initialDistance: number;
    initialZoom: number;
    startX: number;
    startViewRange: { start: number; end: number };
  }>({
    isPinching: false,
    isPanning: false,
    initialDistance: 0,
    initialZoom: 1,
    startX: 0,
    startViewRange: { start: 0, end: 0 },
  });

  // 두 손가락 사이 거리 계산
  const getTouchDistance = (touches: React.TouchList) => {
    if (touches.length < 2) return 0;
    const dx = touches[0].clientX - touches[1].clientX;
    const dy = touches[0].clientY - touches[1].clientY;
    return Math.sqrt(dx * dx + dy * dy);
  };

  // 차트 터치 시작 핸들러 (핀치 줌 & 스와이프 팬 통합)
  const handleChartGestureTouchStart = useCallback((e: React.TouchEvent<HTMLDivElement>) => {
    if (e.touches.length === 2) {
      // 핀치 줌 시작
      const distance = getTouchDistance(e.touches);
      setTouchState({
        isPinching: true,
        isPanning: false,
        initialDistance: distance,
        initialZoom: zoomLevel,
        startX: 0,
        startViewRange: viewRange,
      });
    } else if (e.touches.length === 1) {
      // 스와이프 팬 시작 (단일 터치)
      setTouchState(prev => ({
        ...prev,
        isPanning: true,
        isPinching: false,
        startX: e.touches[0].clientX,
        startViewRange: viewRange,
      }));
      // 크로스헤어도 표시
      calculateTouchChartIndex(e.touches[0].clientX, e.touches[0].clientY);
    }
  }, [zoomLevel, viewRange, calculateTouchChartIndex]);

  // 차트 터치 이동 핸들러 (핀치 줌 & 스와이프 팬 통합)
  const handleChartGestureTouchMove = useCallback((e: React.TouchEvent<HTMLDivElement>) => {
    if (touchState.isPinching && e.touches.length === 2) {
      // 핀치 줌 처리
      const currentDistance = getTouchDistance(e.touches);
      const scale = currentDistance / touchState.initialDistance;
      const newZoom = Math.min(Math.max(touchState.initialZoom * scale, 0.5), 5);
      setZoomLevel(newZoom);
    } else if (touchState.isPanning && e.touches.length === 1) {
      // 스와이프 팬 처리
      const deltaX = e.touches[0].clientX - touchState.startX;
      const rect = chartRef.current?.getBoundingClientRect();
      if (!rect) return;
      
      // 드래그 거리를 데이터 포인트 개수로 변환
      const chartWidth = rect.width - CHART_MARGIN_WITH_XAXIS.left - CHART_MARGIN_WITH_XAXIS.right;
      const visiblePoints = touchState.startViewRange.end - touchState.startViewRange.start;
      const pointsToMove = Math.round((deltaX / chartWidth) * visiblePoints);
      
      // viewRange 업데이트 (드래그 방향 반대로 이동)
      const newStart = Math.max(0, Math.min(
        data.length - visiblePoints,
        touchState.startViewRange.start - pointsToMove
      ));
      const newEnd = newStart + visiblePoints;
      
      if (newStart !== viewRange.start || newEnd !== viewRange.end) {
        setViewRange({ start: newStart, end: newEnd });
      }
      
      // 크로스헤어 업데이트
      calculateTouchChartIndex(e.touches[0].clientX, e.touches[0].clientY);
    }
  }, [touchState, data.length, viewRange, CHART_MARGIN_WITH_XAXIS, calculateTouchChartIndex]);

  // 차트 터치 종료 핸들러
  const handleChartGestureTouchEnd = useCallback(() => {
    setTouchState(prev => ({
      ...prev,
      isPinching: false,
      isPanning: false,
    }));
    // 크로스헤어 숨김
    handleChartTouchEnd();
  }, [handleChartTouchEnd]);

  // 각 차트의 순서를 order 배열에서 찾아서 반환
  const getChartOrder = (settingKey: string) => {
    const index = order.findIndex(item => item.settingKey === settingKey);
    return index >= 0 ? index : 999; // 찾지 못하면 맨 뒤로
  };

  // 각 차트의 범주를 order 배열에서 찾아서 반환
  const getChartCategory = (settingKey: string) => {
    const indicator = order.find(item => item.settingKey === settingKey);
    return indicator?.category || 'other';
  };

  // 범주별 색상 및 라벨 매핑
  const getCategoryDisplay = (category: string) => {
    switch (category) {
      case 'macd':
        return { label: 'MACD', color: '#9c27b0' };
      case 'other':
        return { label: '기타 지표', color: '#ff9800' };
      default:
        return { label: '지표', color: '#757575' };
    }
  };

  // 도구 핸들러
  const handleToolChange = (tool: ChartTool) => {
    setActiveTool(tool);
    // 십자선 도구 선택 시 마우스 커서 변경
    if (tool === 'crosshair') {
      setMagnifierEnabled(false);
    }
  };

  const handleZoomIn = useCallback(() => {
    setZoomLevel(prev => {
      const newZoom = Math.min(prev * 1.5, 5); // 1.5배씩 확대, 최대 5배
      console.log('🔍 Zoom In:', prev, '->', newZoom);
      return newZoom;
    });
  }, []);

  const handleZoomOut = useCallback(() => {
    setZoomLevel(prev => {
      const newZoom = Math.max(prev / 1.5, 0.5); // 1.5배씩 축소, 최소 0.5배
      console.log('🔍 Zoom Out:', prev, '->', newZoom);
      return newZoom;
    });
  }, []);

  const handleFitScreen = useCallback(() => {
    console.log('🔍 Fit Screen: Reset to zoom level 1');
    setZoomLevel(1);
  }, []);

  const handleToggleGrid = useCallback(() => {
    setShowGrid(prev => {
      console.log('📐 Grid Toggle:', !prev);
      return !prev;
    });
  }, []);

  const handleUndo = useCallback(() => {
    if (historyIndex > 0) {
      const newIndex = historyIndex - 1;
      setHistoryIndex(newIndex);
      setDrawings(drawingHistory[newIndex]);
    }
  }, [historyIndex, drawingHistory]);

  const handleRedo = useCallback(() => {
    if (historyIndex < drawingHistory.length - 1) {
      const newIndex = historyIndex + 1;
      setHistoryIndex(newIndex);
      setDrawings(drawingHistory[newIndex]);
    }
  }, [historyIndex, drawingHistory]);

  const handleClearAll = useCallback(() => {
    if (drawings.length > 0) {
      setDrawings([]);
      saveToHistory([]);
    }
  }, [drawings, saveToHistory]);
  
  // 그리기 시작
  const handleDrawingStart = useCallback((e: React.MouseEvent<SVGSVGElement>) => {
    if (activeTool === 'crosshair' || activeTool === 'magnifier') return;
    
    const svg = drawingLayerRef.current;
    if (!svg) return;
    
    const rect = svg.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    
    // 텍스트 도구일 경우 다이얼로그 열기
    if (activeTool === 'text') {
      setPendingTextPosition({ x, y });
      setTextDialogOpen(true);
      return;
    }
    
    const newDrawing: DrawingObject = {
      id: `drawing-${Date.now()}`,
      type: activeTool as DrawingObject['type'],
      points: [{ x, y }],
      color: '#2962ff',
    };
    
    // 수평선, 수직선은 바로 완성
    if (activeTool === 'horizontalLine' || activeTool === 'verticalLine') {
      newDrawing.points = [{ x, y }, { x: activeTool === 'horizontalLine' ? x + 1000 : x, y: activeTool === 'verticalLine' ? y + 1000 : y }];
      const newDrawings = [...drawings, newDrawing];
      setDrawings(newDrawings);
      saveToHistory(newDrawings);
      return;
    }
    
    setCurrentDrawing(newDrawing);
    setIsDrawing(true);
  }, [activeTool, drawings, saveToHistory]);
  
  // 그리기 진행 중
  const handleDrawingMove = useCallback((e: React.MouseEvent<SVGSVGElement>) => {
    if (!isDrawing || !currentDrawing) return;
    
    const svg = drawingLayerRef.current;
    if (!svg) return;
    
    const rect = svg.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    
    // brush는 연속적인 포인트 추가
    if (currentDrawing.type === 'brush') {
      setCurrentDrawing(prev => prev ? {
        ...prev,
        points: [...prev.points, { x, y }]
      } : null);
    } else {
      // 다른 도구는 두 번째 포인트 업데이트
      setCurrentDrawing(prev => prev ? {
        ...prev,
        points: [prev.points[0], { x, y }]
      } : null);
    }
  }, [isDrawing, currentDrawing]);
  
  // 그리기 종료
  const handleDrawingEnd = useCallback(() => {
    if (!isDrawing || !currentDrawing) return;
    
    // 유효한 그리기인지 확인 (최소 두 포인트, 또는 brush의 경우 최소 3 포인트)
    const minPoints = currentDrawing.type === 'brush' ? 3 : 2;
    if (currentDrawing.points.length >= minPoints) {
      const newDrawings = [...drawings, currentDrawing];
      setDrawings(newDrawings);
      saveToHistory(newDrawings);
      
      // 사각형이나 측정 도구의 경우 그리기 완료 후 해당 객체 선택
      if (currentDrawing.type === 'rectangle' || currentDrawing.type === 'measure') {
        setSelectedDrawingId(currentDrawing.id);
      }
    }
    
    setIsDrawing(false);
    setCurrentDrawing(null);
  }, [isDrawing, currentDrawing, drawings, saveToHistory]);

  // 그리기 터치 핸들러 - 터치 시작 (모바일/태블릿)
  const handleDrawingTouchStart = useCallback((e: React.TouchEvent<SVGSVGElement>) => {
    // 먼저 drawing을 생성하지 않는 툴 체크
    if (activeTool === 'crosshair' || activeTool === 'magnifier') return;
    
    if (e.touches.length !== 1) return;
    
    const svg = drawingLayerRef.current;
    if (!svg) return;
    
    const touch = e.touches[0];
    const rect = svg.getBoundingClientRect();
    const x = touch.clientX - rect.left;
    const y = touch.clientY - rect.top;
    
    // 텍스트 도구일 경우 다이얼로그 열기
    if (activeTool === 'text') {
      setPendingTextPosition({ x, y });
      setTextDialogOpen(true);
      return;
    }
    
    const newDrawing: DrawingObject = {
      id: `drawing-${Date.now()}`,
      type: activeTool as DrawingObject['type'],
      points: [{ x, y }],
      color: '#2962ff',
    };
    
    // 수평선, 수직선은 바로 완성
    if (activeTool === 'horizontalLine' || activeTool === 'verticalLine') {
      const newDrawings = [...drawings, newDrawing];
      setDrawings(newDrawings);
      saveToHistory(newDrawings);
      return;
    }
    
    setCurrentDrawing(newDrawing);
    setIsDrawing(true);
  }, [activeTool, drawings, saveToHistory]);

  // 그리기 터치 핸들러 - 터치 이동 (모바일/태블릿)
  const handleDrawingTouchMove = useCallback((e: React.TouchEvent<SVGSVGElement>) => {
    if (!isDrawing || !currentDrawing) return;
    if (e.touches.length !== 1) return;
    
    e.preventDefault(); // 스크롤 방지
    
    const svg = drawingLayerRef.current;
    if (!svg) return;
    
    const touch = e.touches[0];
    const rect = svg.getBoundingClientRect();
    const x = touch.clientX - rect.left;
    const y = touch.clientY - rect.top;
    
    // brush는 연속적인 포인트 추가
    if (currentDrawing.type === 'brush') {
      setCurrentDrawing(prev => prev ? {
        ...prev,
        points: [...prev.points, { x, y }]
      } : null);
    } else {
      // 다른 도구는 두 번째 포인트 업데이트
      setCurrentDrawing(prev => prev ? {
        ...prev,
        points: [prev.points[0], { x, y }]
      } : null);
    }
  }, [isDrawing, currentDrawing]);

  // 그리기 터치 핸들러 - 터치 종료 (모바일/태블릿)
  const handleDrawingTouchEnd = useCallback(() => {
    handleDrawingEnd();
  }, [handleDrawingEnd]);
  
  // 텍스트 다이얼로그 확인
  const handleTextDialogConfirm = useCallback(() => {
    if (pendingTextPosition && textDialogValue.trim()) {
      const newDrawing: DrawingObject = {
        id: `drawing-${Date.now()}`,
        type: 'text',
        points: [pendingTextPosition],
        color: '#2962ff',
        text: textDialogValue.trim(),
      };
      const newDrawings = [...drawings, newDrawing];
      setDrawings(newDrawings);
      saveToHistory(newDrawings);
    }
    setTextDialogOpen(false);
    setTextDialogValue('');
    setPendingTextPosition(null);
  }, [pendingTextPosition, textDialogValue, drawings, saveToHistory]);
  
  // 그리기 객체 삭제 (개별)
  const handleDeleteDrawing = useCallback((id: string) => {
    const newDrawings = drawings.filter(d => d.id !== id);
    setDrawings(newDrawings);
    saveToHistory(newDrawings);
  }, [drawings, saveToHistory]);
  
  // 사각형 편집 마우스 이동 핸들러
  const handleRectangleEditMove = useCallback((e: React.MouseEvent<SVGSVGElement>) => {
    if (editingMode === 'none' || !selectedDrawingId || !editStartPos || !editStartPoints) return;
    
    const svg = drawingLayerRef.current;
    if (!svg) return;
    
    const rect = svg.getBoundingClientRect();
    const currentX = e.clientX - rect.left;
    const currentY = e.clientY - rect.top;
    const deltaX = currentX - editStartPos.x;
    const deltaY = currentY - editStartPos.y;
    
    setDrawings(prev => prev.map(drawing => {
      if (drawing.id !== selectedDrawingId || drawing.type !== 'rectangle') return drawing;
      
      let newPoints = [...editStartPoints];
      
      if (editingMode === 'move') {
        // 사각형 전체 이동
        newPoints = [
          { x: editStartPoints[0].x + deltaX, y: editStartPoints[0].y + deltaY },
          { x: editStartPoints[1].x + deltaX, y: editStartPoints[1].y + deltaY }
        ];
      } else {
        // 리사이즈 처리
        const p0 = { ...editStartPoints[0] };
        const p1 = { ...editStartPoints[1] };
        
        switch (editingMode) {
          case 'resize-n':
            newPoints = [{ x: p0.x, y: p0.y + deltaY }, p1];
            break;
          case 'resize-s':
            newPoints = [p0, { x: p1.x, y: p1.y + deltaY }];
            break;
          case 'resize-w':
            newPoints = [{ x: p0.x + deltaX, y: p0.y }, p1];
            break;
          case 'resize-e':
            newPoints = [p0, { x: p1.x + deltaX, y: p1.y }];
            break;
          case 'resize-nw':
            newPoints = [{ x: p0.x + deltaX, y: p0.y + deltaY }, p1];
            break;
          case 'resize-ne':
            newPoints = [{ x: p0.x, y: p0.y + deltaY }, { x: p1.x + deltaX, y: p1.y }];
            break;
          case 'resize-sw':
            newPoints = [{ x: p0.x + deltaX, y: p0.y }, { x: p1.x, y: p1.y + deltaY }];
            break;
          case 'resize-se':
            newPoints = [p0, { x: p1.x + deltaX, y: p1.y + deltaY }];
            break;
        }
      }
      
      return { ...drawing, points: newPoints };
    }));
  }, [editingMode, selectedDrawingId, editStartPos, editStartPoints]);
  
  // 사각형 편집 터치 이동 핸들러
  const handleRectangleEditTouchMove = useCallback((e: React.TouchEvent<SVGSVGElement>) => {
    if (editingMode === 'none' || !selectedDrawingId || !editStartPos || !editStartPoints) return;
    if (e.touches.length !== 1) return;
    
    e.preventDefault();
    
    const svg = drawingLayerRef.current;
    if (!svg) return;
    
    const touch = e.touches[0];
    const rect = svg.getBoundingClientRect();
    const currentX = touch.clientX - rect.left;
    const currentY = touch.clientY - rect.top;
    const deltaX = currentX - editStartPos.x;
    const deltaY = currentY - editStartPos.y;
    
    setDrawings(prev => prev.map(drawing => {
      if (drawing.id !== selectedDrawingId || drawing.type !== 'rectangle') return drawing;
      
      let newPoints = [...editStartPoints];
      
      if (editingMode === 'move') {
        newPoints = [
          { x: editStartPoints[0].x + deltaX, y: editStartPoints[0].y + deltaY },
          { x: editStartPoints[1].x + deltaX, y: editStartPoints[1].y + deltaY }
        ];
      } else {
        const p0 = { ...editStartPoints[0] };
        const p1 = { ...editStartPoints[1] };
        
        switch (editingMode) {
          case 'resize-n':
            newPoints = [{ x: p0.x, y: p0.y + deltaY }, p1];
            break;
          case 'resize-s':
            newPoints = [p0, { x: p1.x, y: p1.y + deltaY }];
            break;
          case 'resize-w':
            newPoints = [{ x: p0.x + deltaX, y: p0.y }, p1];
            break;
          case 'resize-e':
            newPoints = [p0, { x: p1.x + deltaX, y: p1.y }];
            break;
          case 'resize-nw':
            newPoints = [{ x: p0.x + deltaX, y: p0.y + deltaY }, p1];
            break;
          case 'resize-ne':
            newPoints = [{ x: p0.x, y: p0.y + deltaY }, { x: p1.x + deltaX, y: p1.y }];
            break;
          case 'resize-sw':
            newPoints = [{ x: p0.x + deltaX, y: p0.y }, { x: p1.x, y: p1.y + deltaY }];
            break;
          case 'resize-se':
            newPoints = [p0, { x: p1.x + deltaX, y: p1.y + deltaY }];
            break;
        }
      }
      
      return { ...drawing, points: newPoints };
    }));
  }, [editingMode, selectedDrawingId, editStartPos, editStartPoints]);
  
  // 사각형 편집 종료 핸들러
  const handleRectangleEditEnd = useCallback(() => {
    if (editingMode !== 'none') {
      // 히스토리에 저장
      saveToHistory(drawings);
    }
    setEditingMode('none');
    setEditStartPos(null);
    setEditStartPoints(null);
  }, [editingMode, drawings, saveToHistory]);
  
  // 그리기 레이어 렌더링
  const renderDrawingLayer = () => {
    const allDrawings = currentDrawing ? [...drawings, currentDrawing] : drawings;
    
    // cursor 모드에서 사각형이 있으면 pointerEvents 활성화 (편집 지원)
    const hasRectangles = drawings.some(d => d.type === 'rectangle');
    const enablePointerEvents = (activeTool !== 'crosshair' && activeTool !== 'magnifier') || hasRectangles;
    
    return (
      <svg
        ref={drawingLayerRef}
        style={{
          position: 'absolute',
          top: 0,
          left: 0,
          width: '100%',
          height: '100%',
          pointerEvents: enablePointerEvents ? 'auto' : 'none',
          zIndex: 50,
          cursor: getCursorStyle(),
          touchAction: 'none', // 터치 시 브라우저 기본 동작 방지
        }}
        onMouseDown={(e) => {
          // 빈 영역 클릭 시 선택 해제
          if (!activeTool) {
            setSelectedDrawingId(null);
          } else {
            handleDrawingStart(e);
          }
        }}
        onMouseMove={(e) => {
          if (editingMode !== 'none') {
            handleRectangleEditMove(e);
          } else {
            handleDrawingMove(e);
          }
        }}
        onMouseUp={() => {
          if (editingMode !== 'none') {
            handleRectangleEditEnd();
          } else {
            handleDrawingEnd();
          }
        }}
        onMouseLeave={() => {
          if (editingMode !== 'none') {
            handleRectangleEditEnd();
          } else {
            handleDrawingEnd();
          }
        }}
        onTouchStart={(e) => {
          if (!activeTool) {
            // 터치로 빈 영역 클릭 시 선택 해제
            setSelectedDrawingId(null);
          } else {
            handleDrawingTouchStart(e);
          }
        }}
        onTouchMove={(e) => {
          if (editingMode !== 'none') {
            handleRectangleEditTouchMove(e);
          } else {
            handleDrawingTouchMove(e);
          }
        }}
        onTouchEnd={() => {
          if (editingMode !== 'none') {
            handleRectangleEditEnd();
          } else {
            handleDrawingTouchEnd();
          }
        }}
      >
        {allDrawings.map((drawing) => renderDrawingObject(drawing))}
      </svg>
    );
  };
  
  // 커서 스타일 결정
  const getCursorStyle = () => {
    switch (activeTool) {
      case 'trendLine':
      case 'horizontalLine':
      case 'verticalLine':
      case 'rectangle':
      case 'fibonacci':
      case 'measure':
        return 'crosshair';
      case 'text':
        return 'text';
      case 'brush':
        return 'url("data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'24\' height=\'24\' viewBox=\'0 0 24 24\'%3E%3Ccircle cx=\'12\' cy=\'12\' r=\'4\' fill=\'%232962ff\'/%3E%3C/svg%3E") 12 12, auto';
      default:
        return 'default';
    }
  };
  
  // 개별 그리기 객체 렌더링
  const renderDrawingObject = (drawing: DrawingObject) => {
    const { id, type, points, color, text, strokeWidth = 1, strokeStyle = 'solid', fillColor, fillOpacity = 0.2 } = drawing;
    
    // strokeDasharray 계산
    const getStrokeDasharray = (style: string) => {
      switch (style) {
        case 'dashed': return '8,4';
        case 'dotted': return '2,4';
        default: return 'none';
      }
    };
    
    const isSelected = selectedDrawingId === id;
    
    switch (type) {
      case 'trendLine':
        if (points.length < 2) return null;
        return (
          <g key={id}>
            {/* 투명한 넓은 히트 영역 */}
            <line
              x1={points[0].x}
              y1={points[0].y}
              x2={points[1].x}
              y2={points[1].y}
              stroke="transparent"
              strokeWidth={12}
              style={{ cursor: 'pointer' }}
              onClick={() => setSelectedDrawingId(id)}
              onDoubleClick={() => handleDrawingDoubleClick(id)}
            />
            <line
              x1={points[0].x}
              y1={points[0].y}
              x2={points[1].x}
              y2={points[1].y}
              stroke={color}
              strokeWidth={strokeWidth}
              strokeDasharray={getStrokeDasharray(strokeStyle)}
              style={{ pointerEvents: 'none' }}
            />
            {isSelected && (
              <>
                <circle cx={points[0].x} cy={points[0].y} r={5} fill={color} stroke="white" strokeWidth={1} />
                <circle cx={points[1].x} cy={points[1].y} r={5} fill={color} stroke="white" strokeWidth={1} />
              </>
            )}
          </g>
        );
        
      case 'measure':
        if (points.length < 2) return null;
        
        // 업비트 스타일 기간 측정 도구
        const measureStartY = points[0].y;
        const measureEndY = points[1].y;
        const measureStartX = points[0].x;
        const measureEndX = points[1].x;
        const isGoingUp = measureEndY < measureStartY; // 위로 이동 = 상승
        const measureColor = isGoingUp ? 'rgba(41, 98, 255, 0.15)' : 'rgba(239, 83, 80, 0.15)';
        const measureBorderColor = isGoingUp ? '#2962ff' : '#ef5350';
        const measureTextBg = isGoingUp ? '#2962ff' : '#ef5350';
        
        // 사각형 좌표 계산
        const measureRectX = Math.min(measureStartX, measureEndX);
        const measureRectY = Math.min(measureStartY, measureEndY);
        const measureRectWidth = Math.abs(measureEndX - measureStartX);
        const measureRectHeight = Math.abs(measureEndY - measureStartY);
        
        // 차트 영역 정보로 가격 계산
        const chartAreaLeft = 60;
        const chartAreaWidth = (drawingLayerRef.current?.clientWidth || 800) - chartAreaLeft - 10;
        const chartAreaHeight = drawingLayerRef.current?.clientHeight || 400;
        const chartAreaTop = 40; // Legend 영역
        
        // Y 좌표를 가격으로 변환 (차트 영역 기준)
        const priceRange = chartData.length > 0 ? {
          min: Math.min(...chartData.map(d => d.low || 0)),
          max: Math.max(...chartData.map(d => d.high || 0))
        } : { min: 0, max: 0 };
        
        const yToPrice = (y: number) => {
          const ratio = (y - chartAreaTop) / (chartAreaHeight - chartAreaTop - 30);
          return priceRange.max - ratio * (priceRange.max - priceRange.min);
        };
        
        const startPrice = yToPrice(measureStartY);
        const endPrice = yToPrice(measureEndY);
        const priceDiff = endPrice - startPrice;
        const pricePercent = startPrice !== 0 ? (priceDiff / startPrice) * 100 : 0;
        
        // X 좌표를 캔들 인덱스로 변환
        const candleWidth = chartAreaWidth / Math.max(chartData.length, 1);
        const startIndex = Math.floor((measureStartX - chartAreaLeft) / candleWidth);
        const endIndex = Math.floor((measureEndX - chartAreaLeft) / candleWidth);
        const validStartIndex = Math.max(0, Math.min(startIndex, chartData.length - 1));
        const validEndIndex = Math.max(0, Math.min(endIndex, chartData.length - 1));
        const measureCandleCount = Math.abs(validEndIndex - validStartIndex) + 1;
        
        // 볼륨 합계 계산
        const minIdx = Math.min(validStartIndex, validEndIndex);
        const maxIdx = Math.max(validStartIndex, validEndIndex);
        const totalVolume = chartData.slice(minIdx, maxIdx + 1).reduce((sum, d) => sum + (d.volume || 0), 0);
        
        // 숫자 포맷팅
        const formatMeasureNumber = (num: number) => {
          if (Math.abs(num) >= 1e12) return (num / 1e12).toFixed(2) + 'T';
          if (Math.abs(num) >= 1e9) return (num / 1e9).toFixed(2) + 'B';
          if (Math.abs(num) >= 1e6) return (num / 1e6).toFixed(2) + 'M';
          if (Math.abs(num) >= 1e3) return (num / 1e3).toFixed(3) + 'K';
          return num.toFixed(2);
        };
        
        // 정보 박스 위치 (사각형 우측 하단 내부)
        const infoBoxPadding = 10;
        const infoBoxWidth = 160;
        const infoBoxHeight = 60;
        const infoBoxX = measureRectX + measureRectWidth - infoBoxWidth - infoBoxPadding;
        const infoBoxY = measureRectY + measureRectHeight - infoBoxHeight - infoBoxPadding;
        
        // 화살표 계산 (양방향)
        const arrowStartX = measureRectX;
        const arrowEndX = measureRectX + measureRectWidth;
        const arrowY = measureRectY + measureRectHeight / 2;
        const arrowSize = 8;
        
        return (
          <g key={id}>
            {/* 사각형 배경 */}
            <rect
              x={measureRectX}
              y={measureRectY}
              width={measureRectWidth}
              height={measureRectHeight}
              fill={fillColor || measureColor}
              stroke={color !== '#2962ff' ? color : measureBorderColor}
              strokeWidth={strokeWidth || 2}
              strokeDasharray={getStrokeDasharray(strokeStyle)}
              style={{ cursor: 'pointer' }}
              onClick={() => setSelectedDrawingId(id)}
              onDoubleClick={() => handleDrawingDoubleClick(id)}
            />
            
            {/* 시작점 원형 점 */}
            <circle
              cx={measureStartX}
              cy={measureStartY}
              r={6}
              fill={measureBorderColor}
              stroke="white"
              strokeWidth={2}
              style={{ cursor: 'pointer' }}
              onClick={() => setSelectedDrawingId(id)}
            />
            
            {/* 끝점 원형 점 */}
            <circle
              cx={measureEndX}
              cy={measureEndY}
              r={6}
              fill={measureBorderColor}
              stroke="white"
              strokeWidth={2}
              style={{ cursor: 'pointer' }}
              onClick={() => setSelectedDrawingId(id)}
            />
            
            {/* 수평 화살표 (중앙) - 왼쪽에서 오른쪽으로 */}
            {measureRectWidth > 60 && (
              <g>
                <line
                  x1={arrowStartX + 15}
                  y1={arrowY}
                  x2={arrowEndX - 15}
                  y2={arrowY}
                  stroke={measureBorderColor}
                  strokeWidth={2}
                />
                {/* 오른쪽 화살표 끝 */}
                <polygon
                  points={`${arrowEndX - 15},${arrowY} ${arrowEndX - 23},${arrowY - arrowSize} ${arrowEndX - 23},${arrowY + arrowSize}`}
                  fill={measureBorderColor}
                />
                {/* 왼쪽 화살표 끝 */}
                <polygon
                  points={`${arrowStartX + 15},${arrowY} ${arrowStartX + 23},${arrowY - arrowSize} ${arrowStartX + 23},${arrowY + arrowSize}`}
                  fill={measureBorderColor}
                />
              </g>
            )}
            
            {/* 정보 박스 */}
            {measureRectWidth > 80 && measureRectHeight > 50 && (
              <g>
                <rect
                  x={infoBoxX}
                  y={infoBoxY}
                  width={infoBoxWidth}
                  height={infoBoxHeight}
                  fill="white"
                  stroke={measureBorderColor}
                  strokeWidth={1}
                  rx={4}
                  opacity={0.95}
                />
                <text
                  x={infoBoxX + infoBoxWidth / 2}
                  y={infoBoxY + 22}
                  textAnchor="middle"
                  fill={measureBorderColor}
                  fontSize={13}
                  fontWeight="bold"
                >
                  {measureCandleCount} 봉, {measureCandleCount}일
                </text>
                <text
                  x={infoBoxX + infoBoxWidth / 2}
                  y={infoBoxY + 44}
                  textAnchor="middle"
                  fill="#666"
                  fontSize={12}
                >
                  볼륨 {formatMeasureNumber(totalVolume)}
                </text>
              </g>
            )}
          </g>
        );
        
      case 'horizontalLine':
        return (
          <g key={id}>
            {/* 투명한 넓은 히트 영역 */}
            <line
              x1={0}
              y1={points[0].y}
              x2="100%"
              y2={points[0].y}
              stroke="transparent"
              strokeWidth={12}
              style={{ cursor: 'pointer' }}
              onClick={() => setSelectedDrawingId(id)}
              onDoubleClick={() => handleDrawingDoubleClick(id)}
            />
            <line
              x1={0}
              y1={points[0].y}
              x2="100%"
              y2={points[0].y}
              stroke={color}
              strokeWidth={strokeWidth}
              strokeDasharray={getStrokeDasharray(strokeStyle)}
              style={{ pointerEvents: 'none' }}
            />
            {isSelected && (
              <circle cx={50} cy={points[0].y} r={5} fill={color} stroke="white" strokeWidth={1} />
            )}
          </g>
        );
        
      case 'verticalLine':
        return (
          <g key={id}>
            {/* 투명한 넓은 히트 영역 */}
            <line
              x1={points[0].x}
              y1={0}
              x2={points[0].x}
              y2="100%"
              stroke="transparent"
              strokeWidth={12}
              style={{ cursor: 'pointer' }}
              onClick={() => setSelectedDrawingId(id)}
              onDoubleClick={() => handleDrawingDoubleClick(id)}
            />
            <line
              x1={points[0].x}
              y1={0}
              x2={points[0].x}
              y2="100%"
              stroke={color}
              strokeWidth={strokeWidth}
              strokeDasharray={getStrokeDasharray(strokeStyle)}
              style={{ pointerEvents: 'none' }}
            />
            {isSelected && (
              <circle cx={points[0].x} cy={50} r={5} fill={color} stroke="white" strokeWidth={1} />
            )}
          </g>
        );
        
      case 'rectangle': {
        if (points.length < 2) return null;
        const rectX = Math.min(points[0].x, points[1].x);
        const rectY = Math.min(points[0].y, points[1].y);
        const rectWidth = Math.abs(points[1].x - points[0].x);
        const rectHeight = Math.abs(points[1].y - points[0].y);
        const rectSelected = isSelected;
        const handleSize = 8;
        
        // 배경색 계산 (기본값 또는 사용자 설정)
        const rectFillColor = fillColor || `rgba(41, 98, 255, ${fillOpacity})`;
        
        // 캔들 개수 계산 (차트 영역 기준)
        const rectChartAreaLeft = 60; // Y축 영역
        const rectChartAreaWidth = (drawingLayerRef.current?.clientWidth || 800) - rectChartAreaLeft - 10;
        const rectCandleWidth = rectChartAreaWidth / Math.max(chartData.length, 1);
        const rectStartIndex = Math.floor((rectX - rectChartAreaLeft) / rectCandleWidth);
        const rectEndIndex = Math.floor((rectX + rectWidth - rectChartAreaLeft) / rectCandleWidth);
        const candleCount = Math.max(0, Math.min(rectEndIndex, chartData.length - 1) - Math.max(rectStartIndex, 0) + 1);
        
        return (
          <g key={id}>
            {/* 사각형 영역 (클릭하여 이동) */}
            <rect
              x={rectX}
              y={rectY}
              width={rectWidth}
              height={rectHeight}
              fill={rectFillColor}
              stroke={color}
              strokeWidth={rectSelected ? strokeWidth + 1 : strokeWidth}
              strokeDasharray={getStrokeDasharray(strokeStyle)}
              style={{ cursor: 'move' }}
              onMouseDown={(e) => {
                e.stopPropagation();
                // 기존 사각형 클릭 시 선택 및 이동
                setSelectedDrawingId(id);
                setEditingMode('move');
                const svg = drawingLayerRef.current;
                if (svg) {
                  const rect = svg.getBoundingClientRect();
                  setEditStartPos({ x: e.clientX - rect.left, y: e.clientY - rect.top });
                  setEditStartPoints([...points]);
                }
              }}
              onTouchStart={(e) => {
                e.stopPropagation();
                if (e.touches.length !== 1) return;
                // 기존 사각형 터치 시 선택 및 이동
                const touch = e.touches[0];
                setSelectedDrawingId(id);
                setEditingMode('move');
                const svg = drawingLayerRef.current;
                if (svg) {
                  const rect = svg.getBoundingClientRect();
                  setEditStartPos({ x: touch.clientX - rect.left, y: touch.clientY - rect.top });
                  setEditStartPoints([...points]);
                }
              }}
              onDoubleClick={() => handleDrawingDoubleClick(id)}
            />
            
            {/* 캔들 개수 표시 */}
            {rectWidth > 30 && rectHeight > 20 && (
              <text
                x={rectX + rectWidth / 2}
                y={rectY + rectHeight / 2 + 5}
                textAnchor="middle"
                fill="rgba(41, 98, 255, 0.8)"
                fontSize={14}
                fontWeight="bold"
                style={{ pointerEvents: 'none' }}
              >
                {candleCount}개
              </text>
            )}
            
            {/* 리사이즈 핸들 (선택된 사각형에 표시) */}
            {rectSelected && (
              <>
                {/* 상단 핸들 */}
                <rect
                  x={rectX + rectWidth / 2 - handleSize / 2}
                  y={rectY - handleSize / 2}
                  width={handleSize}
                  height={handleSize}
                  fill={rectSelected ? color : 'white'}
                  stroke={color}
                  strokeWidth={1}
                  style={{ cursor: 'ns-resize' }}
                  onMouseDown={(e) => {
                    e.stopPropagation();
                    setSelectedDrawingId(id);
                    setEditingMode('resize-n');
                    const svg = drawingLayerRef.current;
                    if (svg) {
                      const rect = svg.getBoundingClientRect();
                      setEditStartPos({ x: e.clientX - rect.left, y: e.clientY - rect.top });
                      setEditStartPoints([...points]);
                    }
                  }}
                  onTouchStart={(e) => {
                    e.stopPropagation();
                    if (e.touches.length !== 1) return;
                    const touch = e.touches[0];
                    setSelectedDrawingId(id);
                    setEditingMode('resize-n');
                    const svg = drawingLayerRef.current;
                    if (svg) {
                      const rect = svg.getBoundingClientRect();
                      setEditStartPos({ x: touch.clientX - rect.left, y: touch.clientY - rect.top });
                      setEditStartPoints([...points]);
                    }
                  }}
                />
                {/* 하단 핸들 */}
                <rect
                  x={rectX + rectWidth / 2 - handleSize / 2}
                  y={rectY + rectHeight - handleSize / 2}
                  width={handleSize}
                  height={handleSize}
                  fill={rectSelected ? color : 'white'}
                  stroke={color}
                  strokeWidth={1}
                  style={{ cursor: 'ns-resize' }}
                  onMouseDown={(e) => {
                    e.stopPropagation();
                    setSelectedDrawingId(id);
                    setEditingMode('resize-s');
                    const svg = drawingLayerRef.current;
                    if (svg) {
                      const rect = svg.getBoundingClientRect();
                      setEditStartPos({ x: e.clientX - rect.left, y: e.clientY - rect.top });
                      setEditStartPoints([...points]);
                    }
                  }}
                  onTouchStart={(e) => {
                    e.stopPropagation();
                    if (e.touches.length !== 1) return;
                    const touch = e.touches[0];
                    setSelectedDrawingId(id);
                    setEditingMode('resize-s');
                    const svg = drawingLayerRef.current;
                    if (svg) {
                      const rect = svg.getBoundingClientRect();
                      setEditStartPos({ x: touch.clientX - rect.left, y: touch.clientY - rect.top });
                      setEditStartPoints([...points]);
                    }
                  }}
                />
                {/* 좌측 핸들 */}
                <rect
                  x={rectX - handleSize / 2}
                  y={rectY + rectHeight / 2 - handleSize / 2}
                  width={handleSize}
                  height={handleSize}
                  fill={rectSelected ? color : 'white'}
                  stroke={color}
                  strokeWidth={1}
                  style={{ cursor: 'ew-resize' }}
                  onMouseDown={(e) => {
                    e.stopPropagation();
                    setSelectedDrawingId(id);
                    setEditingMode('resize-w');
                    const svg = drawingLayerRef.current;
                    if (svg) {
                      const rect = svg.getBoundingClientRect();
                      setEditStartPos({ x: e.clientX - rect.left, y: e.clientY - rect.top });
                      setEditStartPoints([...points]);
                    }
                  }}
                  onTouchStart={(e) => {
                    e.stopPropagation();
                    if (e.touches.length !== 1) return;
                    const touch = e.touches[0];
                    setSelectedDrawingId(id);
                    setEditingMode('resize-w');
                    const svg = drawingLayerRef.current;
                    if (svg) {
                      const rect = svg.getBoundingClientRect();
                      setEditStartPos({ x: touch.clientX - rect.left, y: touch.clientY - rect.top });
                      setEditStartPoints([...points]);
                    }
                  }}
                />
                {/* 우측 핸들 */}
                <rect
                  x={rectX + rectWidth - handleSize / 2}
                  y={rectY + rectHeight / 2 - handleSize / 2}
                  width={handleSize}
                  height={handleSize}
                  fill={rectSelected ? color : 'white'}
                  stroke={color}
                  strokeWidth={1}
                  style={{ cursor: 'ew-resize' }}
                  onMouseDown={(e) => {
                    e.stopPropagation();
                    setSelectedDrawingId(id);
                    setEditingMode('resize-e');
                    const svg = drawingLayerRef.current;
                    if (svg) {
                      const rect = svg.getBoundingClientRect();
                      setEditStartPos({ x: e.clientX - rect.left, y: e.clientY - rect.top });
                      setEditStartPoints([...points]);
                    }
                  }}
                  onTouchStart={(e) => {
                    e.stopPropagation();
                    if (e.touches.length !== 1) return;
                    const touch = e.touches[0];
                    setSelectedDrawingId(id);
                    setEditingMode('resize-e');
                    const svg = drawingLayerRef.current;
                    if (svg) {
                      const rect = svg.getBoundingClientRect();
                      setEditStartPos({ x: touch.clientX - rect.left, y: touch.clientY - rect.top });
                      setEditStartPoints([...points]);
                    }
                  }}
                />
                {/* 좌상단 코너 핸들 */}
                <rect
                  x={rectX - handleSize / 2}
                  y={rectY - handleSize / 2}
                  width={handleSize}
                  height={handleSize}
                  fill={rectSelected ? color : 'white'}
                  stroke={color}
                  strokeWidth={1}
                  style={{ cursor: 'nwse-resize' }}
                  onMouseDown={(e) => {
                    e.stopPropagation();
                    setSelectedDrawingId(id);
                    setEditingMode('resize-nw');
                    const svg = drawingLayerRef.current;
                    if (svg) {
                      const rect = svg.getBoundingClientRect();
                      setEditStartPos({ x: e.clientX - rect.left, y: e.clientY - rect.top });
                      setEditStartPoints([...points]);
                    }
                  }}
                  onTouchStart={(e) => {
                    e.stopPropagation();
                    if (e.touches.length !== 1) return;
                    const touch = e.touches[0];
                    setSelectedDrawingId(id);
                    setEditingMode('resize-nw');
                    const svg = drawingLayerRef.current;
                    if (svg) {
                      const rect = svg.getBoundingClientRect();
                      setEditStartPos({ x: touch.clientX - rect.left, y: touch.clientY - rect.top });
                      setEditStartPoints([...points]);
                    }
                  }}
                />
                {/* 우상단 코너 핸들 */}
                <rect
                  x={rectX + rectWidth - handleSize / 2}
                  y={rectY - handleSize / 2}
                  width={handleSize}
                  height={handleSize}
                  fill={rectSelected ? color : 'white'}
                  stroke={color}
                  strokeWidth={1}
                  style={{ cursor: 'nesw-resize' }}
                  onMouseDown={(e) => {
                    e.stopPropagation();
                    setSelectedDrawingId(id);
                    setEditingMode('resize-ne');
                    const svg = drawingLayerRef.current;
                    if (svg) {
                      const rect = svg.getBoundingClientRect();
                      setEditStartPos({ x: e.clientX - rect.left, y: e.clientY - rect.top });
                      setEditStartPoints([...points]);
                    }
                  }}
                  onTouchStart={(e) => {
                    e.stopPropagation();
                    if (e.touches.length !== 1) return;
                    const touch = e.touches[0];
                    setSelectedDrawingId(id);
                    setEditingMode('resize-ne');
                    const svg = drawingLayerRef.current;
                    if (svg) {
                      const rect = svg.getBoundingClientRect();
                      setEditStartPos({ x: touch.clientX - rect.left, y: touch.clientY - rect.top });
                      setEditStartPoints([...points]);
                    }
                  }}
                />
                {/* 좌하단 코너 핸들 */}
                <rect
                  x={rectX - handleSize / 2}
                  y={rectY + rectHeight - handleSize / 2}
                  width={handleSize}
                  height={handleSize}
                  fill={rectSelected ? color : 'white'}
                  stroke={color}
                  strokeWidth={1}
                  style={{ cursor: 'nesw-resize' }}
                  onMouseDown={(e) => {
                    e.stopPropagation();
                    setSelectedDrawingId(id);
                    setEditingMode('resize-sw');
                    const svg = drawingLayerRef.current;
                    if (svg) {
                      const rect = svg.getBoundingClientRect();
                      setEditStartPos({ x: e.clientX - rect.left, y: e.clientY - rect.top });
                      setEditStartPoints([...points]);
                    }
                  }}
                  onTouchStart={(e) => {
                    e.stopPropagation();
                    if (e.touches.length !== 1) return;
                    const touch = e.touches[0];
                    setSelectedDrawingId(id);
                    setEditingMode('resize-sw');
                    const svg = drawingLayerRef.current;
                    if (svg) {
                      const rect = svg.getBoundingClientRect();
                      setEditStartPos({ x: touch.clientX - rect.left, y: touch.clientY - rect.top });
                      setEditStartPoints([...points]);
                    }
                  }}
                />
                {/* 우하단 코너 핸들 */}
                <rect
                  x={rectX + rectWidth - handleSize / 2}
                  y={rectY + rectHeight - handleSize / 2}
                  width={handleSize}
                  height={handleSize}
                  fill={rectSelected ? color : 'white'}
                  stroke={color}
                  strokeWidth={1}
                  style={{ cursor: 'nwse-resize' }}
                  onMouseDown={(e) => {
                    e.stopPropagation();
                    setSelectedDrawingId(id);
                    setEditingMode('resize-se');
                    const svg = drawingLayerRef.current;
                    if (svg) {
                      const rect = svg.getBoundingClientRect();
                      setEditStartPos({ x: e.clientX - rect.left, y: e.clientY - rect.top });
                      setEditStartPoints([...points]);
                    }
                  }}
                  onTouchStart={(e) => {
                    e.stopPropagation();
                    if (e.touches.length !== 1) return;
                    const touch = e.touches[0];
                    setSelectedDrawingId(id);
                    setEditingMode('resize-se');
                    const svg = drawingLayerRef.current;
                    if (svg) {
                      const rect = svg.getBoundingClientRect();
                      setEditStartPos({ x: touch.clientX - rect.left, y: touch.clientY - rect.top });
                      setEditStartPoints([...points]);
                    }
                  }}
                />
              </>
            )}
          </g>
        );
      }
        
      case 'fibonacci':
        if (points.length < 2) return null;
        const fibLevels = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1];
        const fibColors = ['#f44336', '#ff9800', '#ffeb3b', '#4caf50', '#2196f3', '#9c27b0', '#f44336'];
        const startY = points[0].y;
        const endY = points[1].y;
        const range = endY - startY;
        
        return (
          <g key={id}>
            {fibLevels.map((level, index) => {
              const y = startY + range * level;
              return (
                <g key={`fib-${level}`}>
                  <line
                    x1={points[0].x}
                    y1={y}
                    x2={points[1].x}
                    y2={y}
                    stroke={fibColors[index]}
                    strokeWidth={1}
                    strokeDasharray="3,3"
                  />
                  <text
                    x={points[1].x + 5}
                    y={y + 4}
                    fill={fibColors[index]}
                    fontSize={10}
                  >
                    {(level * 100).toFixed(1)}%
                  </text>
                </g>
              );
            })}
            <rect
              x={Math.min(points[0].x, points[1].x)}
              y={Math.min(startY, endY)}
              width={Math.abs(points[1].x - points[0].x)}
              height={Math.abs(range)}
              fill="rgba(41, 98, 255, 0.05)"
              stroke="none"
            />
          </g>
        );
        
      case 'text':
        return (
          <g key={id}>
            <rect
              x={points[0].x - 2}
              y={points[0].y - 16}
              width={(text?.length || 0) * 8 + 8}
              height={22}
              fill="rgba(41, 98, 255, 0.9)"
              rx={4}
            />
            <text
              x={points[0].x + 2}
              y={points[0].y}
              fill="white"
              fontSize={14}
              fontWeight="500"
            >
              {text}
            </text>
          </g>
        );
        
      case 'brush':
        if (points.length < 2) return null;
        const pathData = points.reduce((acc, point, index) => {
          if (index === 0) return `M ${point.x} ${point.y}`;
          return `${acc} L ${point.x} ${point.y}`;
        }, '');
        return (
          <path
            key={id}
            d={pathData}
            fill="none"
            stroke={color}
            strokeWidth={2}
            strokeLinecap="round"
            strokeLinejoin="round"
          />
        );
        
      default:
        return null;
    }
  };

  // 돋보기 마우스 이벤트 핸들러
  const handleMagnifierMouseMove = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
    if (magnifierActive) {
      setMagnifierPos({ x: e.clientX, y: e.clientY });
    }
  }, [magnifierActive]);

  const handleMagnifierMouseLeave = useCallback(() => {
    if (magnifierActive) {
      setMagnifierPos(null);
    }
  }, [magnifierActive]);

  return (
    <Box 
      ref={magnifierContainerRef}
      onMouseMove={handleMagnifierMouseMove}
      onMouseLeave={handleMagnifierMouseLeave}
      sx={{ 
        width: '100%', 
        minHeight: calculateTotalHeight(), 
        position: 'relative',
        cursor: magnifierActive ? 'none' : 'default',
      }}
    >
      {/* 텍스트 입력 다이얼로그 */}
      <Dialog open={textDialogOpen} onClose={() => setTextDialogOpen(false)} maxWidth="xs" fullWidth>
        <DialogTitle>텍스트 추가</DialogTitle>
        <DialogContent>
          <TextField
            autoFocus
            margin="dense"
            label="텍스트 입력"
            fullWidth
            variant="outlined"
            value={textDialogValue}
            onChange={(e) => setTextDialogValue(e.target.value)}
            onKeyDown={(e) => {
              if (e.key === 'Enter') {
                handleTextDialogConfirm();
              }
            }}
          />
        </DialogContent>
        <DialogActions>
          <Button onClick={() => setTextDialogOpen(false)}>취소</Button>
          <Button onClick={handleTextDialogConfirm} variant="contained">확인</Button>
        </DialogActions>
      </Dialog>
      
      
      {/* 차트 컨테이너 */}
      <Box sx={{ display: 'flex', flexDirection: 'column' }}>
        
      {/* 주가 캔들스틱 차트 + 거래량 (업비트 스타일 - 분리형) */}
      {!hideMainChart && (
      <Box sx={{ mb: 0.5, position: 'relative', order: 0 }}>
        
        {/* 상단 헤더: 시간봉 선택 (좌측) + 기본차트/트레이딩뷰/돋보기 (우측) */}
        <Box sx={{ 
          display: 'flex', 
          justifyContent: 'space-between', 
          alignItems: 'center', 
          mb: 0.5,
          flexWrap: 'wrap',
          gap: 1,
        }}>
          {/* 좌측: 시간봉, 보조지표, 이동평균 선택 드롭다운 그룹 */}
          <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
            {/* 시간봉 선택 */}
            <Box>
              <MuiTooltip title={`시간봉 (${selectedInterval === '1m' ? '1분' :
                 selectedInterval === '3m' ? '3분' :
                 selectedInterval === '5m' ? '5분' :
                 selectedInterval === '10m' ? '10분' :
                 selectedInterval === '15m' ? '15분' :
                 selectedInterval === '30m' ? '30분' :
                 selectedInterval === '1h' ? '1시간' :
                 selectedInterval === '4h' ? '4시간' :
                 selectedInterval === '1d' ? '일' :
                 selectedInterval === '1w' ? '주' :
                 selectedInterval === '1M' ? '월' : selectedInterval})`} arrow>
              <Button
                variant="outlined"
            size="small"
                onClick={(e) => setIntervalMenuAnchor(e.currentTarget)}
                startIcon={<AccessTime sx={{ fontSize: isMobile ? 16 : 18 }} />}
                endIcon={!isMobile ? <KeyboardArrowDown /> : undefined}
            sx={{ 
                fontSize: isMobile ? '11px' : '12px',
                  minWidth: isMobile ? 40 : 90,
                  textTransform: 'none',
                borderColor: 'divider',
                  px: isMobile ? 0.5 : 1,
                }}
              >
                {!isMobile && (selectedInterval === '1m' ? '1분' :
                 selectedInterval === '3m' ? '3분' :
                 selectedInterval === '5m' ? '5분' :
                 selectedInterval === '10m' ? '10분' :
                 selectedInterval === '15m' ? '15분' :
                 selectedInterval === '30m' ? '30분' :
                 selectedInterval === '1h' ? '1시간' :
                 selectedInterval === '4h' ? '4시간' :
                 selectedInterval === '1d' ? '일' :
                 selectedInterval === '1w' ? '주' :
                 selectedInterval === '1M' ? '월' : selectedInterval)}
              </Button>
              </MuiTooltip>
              <Menu
                anchorEl={intervalMenuAnchor}
                open={Boolean(intervalMenuAnchor)}
                onClose={() => setIntervalMenuAnchor(null)}
                anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
                transformOrigin={{ vertical: 'top', horizontal: 'left' }}
              >
                <MenuItem onClick={() => { handleIntervalClick('1m'); setIntervalMenuAnchor(null); }} selected={selectedInterval === '1m'}>1분</MenuItem>
                <MenuItem onClick={() => { handleIntervalClick('3m'); setIntervalMenuAnchor(null); }} selected={selectedInterval === '3m'}>3분</MenuItem>
                <MenuItem onClick={() => { handleIntervalClick('5m'); setIntervalMenuAnchor(null); }} selected={selectedInterval === '5m'}>5분</MenuItem>
                <MenuItem onClick={() => { handleIntervalClick('10m'); setIntervalMenuAnchor(null); }} selected={selectedInterval === '10m'}>10분</MenuItem>
                <MenuItem onClick={() => { handleIntervalClick('15m'); setIntervalMenuAnchor(null); }} selected={selectedInterval === '15m'}>15분</MenuItem>
                <MenuItem onClick={() => { handleIntervalClick('30m'); setIntervalMenuAnchor(null); }} selected={selectedInterval === '30m'}>30분</MenuItem>
                <Divider />
                <MenuItem onClick={() => { handleIntervalClick('1h'); setIntervalMenuAnchor(null); }} selected={selectedInterval === '1h'}>1시간</MenuItem>
                <MenuItem onClick={() => { handleIntervalClick('4h'); setIntervalMenuAnchor(null); }} selected={selectedInterval === '4h'}>4시간</MenuItem>
                <Divider />
                <MenuItem onClick={() => { handleIntervalClick('1d'); setIntervalMenuAnchor(null); }} selected={selectedInterval === '1d'}>일봉</MenuItem>
                <MenuItem onClick={() => { handleIntervalClick('1w'); setIntervalMenuAnchor(null); }} selected={selectedInterval === '1w'}>주봉</MenuItem>
                <MenuItem onClick={() => { handleIntervalClick('1M'); setIntervalMenuAnchor(null); }} selected={selectedInterval === '1M'}>월봉</MenuItem>
              </Menu>
            </Box>
            
            {/* 이동평균선 선택 */}
            <Box>
            <MuiTooltip title={`이동평균선 (${settings.maLines?.filter(ma => ma.enabled).length || 0}개 활성화)`} arrow>
            <Button
              variant="outlined"
              size="small"
              onClick={(e) => setMaMenuAnchor(e.currentTarget)}
              startIcon={<ShowChart sx={{ fontSize: isMobile ? 16 : 18 }} />}
              endIcon={!isMobile ? <KeyboardArrowDown /> : undefined}
              sx={{
                fontSize: isMobile ? '11px' : '12px',
                minWidth: isMobile ? 40 : 90,
                textTransform: 'none',
                borderColor: 'divider',
                px: isMobile ? 0.5 : 1,
              }}
            >
              {!isMobile && `MA(${settings.maLines?.filter(ma => ma.enabled).length || 0})`}
            </Button>
            </MuiTooltip>
            <Menu
              anchorEl={maMenuAnchor}
              open={Boolean(maMenuAnchor)}
              onClose={() => setMaMenuAnchor(null)}
              anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
              transformOrigin={{ vertical: 'top', horizontal: 'left' }}
              slotProps={{
                paper: {
                  sx: {
                    maxHeight: 400,
                    overflowY: 'auto',
                  }
                }
              }}
            >
              {/* 새로운 maLines 기반 메뉴 */}
              {settings.maLines?.map((ma, index) => (
                <MenuItem 
                  key={ma.id}
                  onClick={() => {
                    const newLines = [...settings.maLines];
                    newLines[index] = { ...newLines[index], enabled: !newLines[index].enabled };
                    updateSettings({ maLines: newLines });
                  }} 
                  sx={{ py: 0.5 }}
                >
                  <Checkbox 
                    checked={ma.enabled} 
                    size="small" 
                    sx={{ 
                      p: 0.5, 
                      color: ma.color, 
                      '&.Mui-checked': { color: ma.color } 
                    }} 
                  />
                  <ListItemText 
                    primary={`${ma.id} (${ma.period})`} 
                    primaryTypographyProps={{ fontSize: '13px' }} 
                  />
                  <Box 
                    sx={{ 
                      width: 16, 
                      height: ma.width * 2, 
                      backgroundColor: ma.color, 
                      borderRadius: 0.5,
                      ml: 1 
                    }} 
                  />
                </MenuItem>
              ))}
              <Divider sx={{ my: 0.5 }} />
              {/* 설정 버튼 */}
              <MenuItem 
                onClick={() => {
                  setMaMenuAnchor(null);
                  setMaSettingsDialogOpen(true);
                }} 
                sx={{ py: 1 }}
              >
                <ListItemText 
                  primary="⚙️ 이동평균선 설정..." 
                  primaryTypographyProps={{ fontSize: '13px', fontWeight: 'bold' }} 
                />
              </MenuItem>
            </Menu>
            </Box>
            
            {/* 일목균형표 선택 */}
            <Box>
              <MuiTooltip title={`일목균형표 (${[settings.ichimokuTenkan, settings.ichimokuKijun, settings.ichimokuChikou, settings.ichimokuSenkouA, settings.ichimokuSenkouB].filter(Boolean).length}개 선택)`} arrow>
              <Button
                variant="outlined"
                size="small"
                onClick={(e) => setIchimokuMenuAnchor(e.currentTarget)}
                startIcon={<Timeline sx={{ fontSize: isMobile ? 16 : 18 }} />}
                sx={{
                  fontSize: isMobile ? '11px' : '12px',
                  py: 0.3,
                  px: isMobile ? 0.5 : 1,
                  minWidth: isMobile ? 40 : 70,
                  textTransform: 'none',
                  borderColor: 'divider',
                }}
              >
                {!isMobile && `일목(${[settings.ichimokuTenkan, settings.ichimokuKijun, settings.ichimokuChikou, settings.ichimokuSenkouA, settings.ichimokuSenkouB].filter(Boolean).length})`}
              </Button>
              </MuiTooltip>
              <Menu
                anchorEl={ichimokuMenuAnchor}
                open={Boolean(ichimokuMenuAnchor)}
                onClose={() => setIchimokuMenuAnchor(null)}
                PaperProps={{ sx: { minWidth: 150 } }}
                slotProps={{
                  paper: {
                    sx: {
                      maxHeight: 400,
                      overflowY: 'auto',
                    }
                  }
                }}
              >
                <MenuItem onClick={(e) => { e.stopPropagation(); updateSettings({ ichimokuTenkan: !settings.ichimokuTenkan }); }} sx={{ py: 0.5 }}>
                  <Checkbox 
                    checked={settings.ichimokuTenkan} 
                    size="small" 
                    onClick={(e) => { e.stopPropagation(); updateSettings({ ichimokuTenkan: !settings.ichimokuTenkan }); }}
                    sx={{ p: 0.5, color: colors.ichimokuTenkan, '&.Mui-checked': { color: colors.ichimokuTenkan } }} 
                  />
                  <ListItemText primary={`전환선 (${settings.ichimokuTenkanPeriod || 9})`} primaryTypographyProps={{ fontSize: '13px' }} />
                  <Box sx={{ width: 12, height: 12, borderRadius: '50%', bgcolor: colors.ichimokuTenkan, ml: 1 }} />
                </MenuItem>
                <MenuItem onClick={(e) => { e.stopPropagation(); updateSettings({ ichimokuKijun: !settings.ichimokuKijun }); }} sx={{ py: 0.5 }}>
                  <Checkbox 
                    checked={settings.ichimokuKijun} 
                    size="small" 
                    onClick={(e) => { e.stopPropagation(); updateSettings({ ichimokuKijun: !settings.ichimokuKijun }); }}
                    sx={{ p: 0.5, color: colors.ichimokuKijun, '&.Mui-checked': { color: colors.ichimokuKijun } }} 
                  />
                  <ListItemText primary={`기준선 (${settings.ichimokuKijunPeriod || 26})`} primaryTypographyProps={{ fontSize: '13px' }} />
                  <Box sx={{ width: 12, height: 12, borderRadius: '50%', bgcolor: colors.ichimokuKijun, ml: 1 }} />
                </MenuItem>
                <MenuItem onClick={(e) => { e.stopPropagation(); updateSettings({ ichimokuChikou: !settings.ichimokuChikou }); }} sx={{ py: 0.5 }}>
                  <Checkbox 
                    checked={settings.ichimokuChikou} 
                    size="small" 
                    onClick={(e) => { e.stopPropagation(); updateSettings({ ichimokuChikou: !settings.ichimokuChikou }); }}
                    sx={{ p: 0.5, color: colors.ichimokuChikou, '&.Mui-checked': { color: colors.ichimokuChikou } }} 
                  />
                  <ListItemText primary={`후행스팬 (${settings.ichimokuKijunPeriod || 26})`} primaryTypographyProps={{ fontSize: '13px' }} />
                  <Box sx={{ width: 12, height: 12, borderRadius: '50%', bgcolor: colors.ichimokuChikou, ml: 1 }} />
                </MenuItem>
                <MenuItem onClick={(e) => { 
                  e.stopPropagation(); 
                  console.log('✅ [선행스팬1 클릭] 현재:', settings.ichimokuSenkouA, '→ 변경:', !settings.ichimokuSenkouA); 
                  updateSettings({ ichimokuSenkouA: !settings.ichimokuSenkouA }); 
                }} sx={{ py: 0.5 }}>
                  <Checkbox 
                    checked={settings.ichimokuSenkouA} 
                    size="small" 
                    onClick={(e) => { 
                      e.stopPropagation(); 
                      console.log('✅ [선행스팬1 체크박스 클릭] 현재:', settings.ichimokuSenkouA, '→ 변경:', !settings.ichimokuSenkouA); 
                      updateSettings({ ichimokuSenkouA: !settings.ichimokuSenkouA }); 
                    }}
                    sx={{ p: 0.5, color: colors.ichimokuSenkouA, '&.Mui-checked': { color: colors.ichimokuSenkouA } }} 
                  />
                  <ListItemText primary={`선행스팬1 (${settings.ichimokuKijunPeriod || 26})`} primaryTypographyProps={{ fontSize: '13px' }} />
                  <Box sx={{ width: 12, height: 12, borderRadius: '50%', bgcolor: colors.ichimokuSenkouA, ml: 1 }} />
                </MenuItem>
                <MenuItem onClick={(e) => { 
                  e.stopPropagation(); 
                  console.log('✅ [선행스팬2 클릭] 현재:', settings.ichimokuSenkouB, '→ 변경:', !settings.ichimokuSenkouB); 
                  updateSettings({ ichimokuSenkouB: !settings.ichimokuSenkouB }); 
                }} sx={{ py: 0.5 }}>
                  <Checkbox 
                    checked={settings.ichimokuSenkouB} 
                    size="small" 
                    onClick={(e) => { 
                      e.stopPropagation(); 
                      console.log('✅ [선행스팬2 체크박스 클릭] 현재:', settings.ichimokuSenkouB, '→ 변경:', !settings.ichimokuSenkouB); 
                      updateSettings({ ichimokuSenkouB: !settings.ichimokuSenkouB }); 
                    }}
                    sx={{ p: 0.5, color: colors.ichimokuSenkouB, '&.Mui-checked': { color: colors.ichimokuSenkouB } }} 
                  />
                  <ListItemText primary={`선행스팬2 (${settings.ichimokuSenkouBPeriod || 52})`} primaryTypographyProps={{ fontSize: '13px' }} />
                  <Box sx={{ width: 12, height: 12, borderRadius: '50%', bgcolor: colors.ichimokuSenkouB, ml: 1 }} />
                </MenuItem>
                <Divider sx={{ my: 0.5 }} />
                {/* 설정 버튼 */}
                <MenuItem 
                  onClick={() => {
                    setIchimokuMenuAnchor(null);
                    setIchimokuSettingsDialogOpen(true);
                  }} 
                  sx={{ py: 1 }}
                >
                  <ListItemText 
                    primary="⚙️ 일목균형표 설정..." 
                    primaryTypographyProps={{ fontSize: '13px', fontWeight: 'bold' }} 
                  />
                </MenuItem>
              </Menu>
            </Box>
            
            {/* 기간 선택 컨트롤 */}
            {onPreviousPeriod && onNextPeriod && (
              <Box sx={{ 
                display: 'flex', 
                alignItems: 'center', 
                bgcolor: 'action.hover',
                borderRadius: 1,
                px: 0.5,
                ml: 1,
              }}>
                <IconButton
                  size="small"
                  onClick={onPreviousPeriod}
                  disabled={isPreviousPeriodDisabled || isPeriodLoading}
                  sx={{ p: 0.25 }}
                  title="이전 기간"
                >
                  <ChevronLeft fontSize="small" />
                </IconButton>
                <Typography sx={{ fontSize: '11px', mx: 0.5, minWidth: '100px', textAlign: 'center', fontWeight: 500 }}>
                  {isPeriodLoading ? '로딩...' : periodLabel || ''}
                </Typography>
                <IconButton
                  size="small"
                  onClick={onNextPeriod}
                  disabled={isNextPeriodDisabled || isPeriodLoading}
                  sx={{ p: 0.25 }}
                  title="다음 기간"
                >
                  <ChevronRight fontSize="small" />
                </IconButton>
              </Box>
            )}
          </Box>
          
          {/* 우측: 돋보기 + 기본차트/트레이딩뷰 */}
          <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
            {/* 돋보기 버튼 */}
            <MuiTooltip title={magnifierEnabled ? "돋보기 끄기" : "돋보기 켜기"}>
              <IconButton
                size="small"
                onClick={() => {
                  setMagnifierEnabled(!magnifierEnabled);
                  setMagnifierPosition(null);
                }}
                sx={{
                  bgcolor: magnifierEnabled ? 'primary.main' : 'transparent',
                  color: magnifierEnabled ? 'primary.contrastText' : 'text.secondary',
                  border: 1,
                  borderColor: 'divider',
                  borderRadius: 1,
                  '&:hover': {
                    bgcolor: magnifierEnabled ? 'primary.dark' : 'action.hover',
                  },
                }}
              >
                {magnifierEnabled ? <ZoomOut fontSize="small" /> : <ZoomIn fontSize="small" />}
              </IconButton>
            </MuiTooltip>
            
            {/* 기본차트 / 트레이딩뷰 선택 */}
            {!isMobile && (
              <RadioGroup
                row
                value={chartMode}
                onChange={(e) => setChartMode(e.target.value as 'basic' | 'tradingView')}
                sx={{ gap: 0 }}
              >
                <FormControlLabel 
                  value="basic" 
                  control={<Radio size="small" sx={{ p: 0.5 }} />} 
                  label={<Typography variant="body2" sx={{ fontSize: '12px' }}>기본차트</Typography>}
                  sx={{ mr: 1, ml: 0 }}
                />
                <FormControlLabel 
                  value="tradingView" 
                  control={<Radio size="small" sx={{ p: 0.5, color: '#1976d2', '&.Mui-checked': { color: '#1976d2' } }} />} 
                  label={<Typography variant="body2" sx={{ fontSize: '12px', color: chartMode === 'tradingView' ? '#1976d2' : 'inherit' }}>트레이딩뷰</Typography>}
                  sx={{ mr: 0 }}
                />
              </RadioGroup>
            )}
            
            {/* 툴팁 on/off 버튼 */}
            <MuiTooltip title={settings.tooltipEnabled ? "툴팁 끄기" : "툴팁 켜기"}>
              <IconButton
                size="small"
                onClick={() => updateSettings({ tooltipEnabled: !settings.tooltipEnabled })}
                sx={{
                  bgcolor: settings.tooltipEnabled ? 'primary.main' : 'transparent',
                  color: settings.tooltipEnabled ? 'white' : 'text.secondary',
                  '&:hover': {
                    bgcolor: settings.tooltipEnabled ? 'primary.dark' : 'action.hover',
                  },
                  mr: 0.5,
                }}
              >
                {settings.tooltipEnabled ? <Info fontSize="small" /> : <InfoOutlined fontSize="small" />}
              </IconButton>
            </MuiTooltip>
            
            {/* AI 테스트 버튼 */}
            <MuiTooltip title={aiTestLoading ? "AI 분석 중..." : "AI 차트 분석 (로컬 LLM)"}>
              <Box sx={{ position: 'relative', display: 'inline-flex' }}>
                <IconButton
                  size="small"
                  onClick={handleAiTest}
                  disabled={false} // 로딩 중이어도 버튼은 활성화 (중복 요청은 함수에서 방지)
                  sx={{
                    bgcolor: aiTestLoading ? 'rgba(255, 152, 0, 0.2)' : 'transparent',
                    color: aiTestLoading ? '#ff9800' : '#9c27b0',
                    border: 1,
                    borderColor: aiTestLoading ? '#ff9800' : '#9c27b0',
                    borderRadius: 1,
                    '&:hover': {
                      bgcolor: aiTestLoading ? 'rgba(255, 152, 0, 0.3)' : 'rgba(156, 39, 176, 0.1)',
                    },
                  }}
                >
                  <Psychology fontSize="small" />
                </IconButton>
                {/* 로딩 중 버튼 주변 프로그래스 링 */}
                {aiTestLoading && (
                  <CircularProgress
                    size={32}
                    sx={{
                      color: '#ff9800',
                      position: 'absolute',
                      top: '50%',
                      left: '50%',
                      marginTop: '-16px',
                      marginLeft: '-16px',
                    }}
                  />
                )}
              </Box>
            </MuiTooltip>
            
            {/* 전체화면 토글 버튼 */}
            {onToggleFullscreen && (
              <MuiTooltip title={isFullscreen ? "기본 화면" : "전체 화면"}>
                <IconButton
                  size="small"
                  onClick={onToggleFullscreen}
                  sx={{
                    bgcolor: isFullscreen ? 'primary.main' : 'transparent',
                    color: isFullscreen ? 'primary.contrastText' : 'text.secondary',
                    border: 1,
                    borderColor: 'divider',
                    borderRadius: 1,
                    '&:hover': {
                      bgcolor: isFullscreen ? 'primary.dark' : 'action.hover',
                    },
                  }}
                >
                  {isFullscreen ? <FullscreenExit fontSize="small" /> : <Fullscreen fontSize="small" />}
                </IconButton>
              </MuiTooltip>
            )}
            
            {/* 전략체크 팝업 버튼 */}
            <MuiTooltip title="전략 체크">
              <IconButton
                size="small"
                onClick={() => setStrategyCheckPopupOpen(true)}
                sx={{
                  bgcolor: strategyCheckPopupOpen ? 'primary.main' : 'transparent',
                  color: strategyCheckPopupOpen ? 'primary.contrastText' : 'text.secondary',
                  border: 1,
                  borderColor: 'divider',
                  borderRadius: 1,
                  '&:hover': {
                    bgcolor: strategyCheckPopupOpen ? 'primary.dark' : 'action.hover',
                  },
                }}
              >
                <FactCheck fontSize="small" />
              </IconButton>
            </MuiTooltip>
            
            {/* 차트 설정 팝업 버튼 */}
              <IconButton
                size="small"
                onClick={() => setChartSettingsDialogOpen(true)}
                sx={{
                  ml: 1,
                  bgcolor: chartSettingsDialogOpen ? 'primary.main' : 'action.hover',
                  color: chartSettingsDialogOpen ? 'primary.contrastText' : 'text.secondary',
                  border: 1,
                  borderColor: 'divider',
                  borderRadius: 1,
                  '&:hover': {
                    bgcolor: 'primary.main',
                    color: 'primary.contrastText',
                  },
                }}
              >
                <SettingsIcon fontSize="small" />
              </IconButton>
            
          </Box>
        </Box>
        
        {/* 트레이딩뷰 모드일 때 상단에 도구바 표시 */}
        {!isMobile && chartMode === 'tradingView' && (
          <ChartToolbar
            activeTool={activeTool}
            onToolChange={handleToolChange}
            onZoomIn={handleZoomIn}
            onZoomOut={handleZoomOut}
            onUndo={handleUndo}
            onRedo={handleRedo}
            onClearAll={handleClearAll}
          />
        )}
        
        
        {/* 캔들차트 영역 */}
        <Box sx={{ position: 'relative' }}>
          {/* 캔들차트 영역 (동적 높이 - 보조지표 개수에 따라 조절) */}
          <Box
            ref={chartRef}
            onMouseMove={handleMouseMove}
            onMouseLeave={handleMouseLeave}
            onTouchStart={(e) => {
              // 두 손가락 터치(핀치)나 차트 내 터치 시 기본 동작 방지
              if (e.touches.length >= 2) {
                e.preventDefault();
              }
              handleTouchMove(e); // 돋보기용
              handleChartGestureTouchStart(e); // 핀치 줌 & 스와이프 팬 & 크로스헤어
            }}
            onTouchMove={(e) => {
              // 핀치 줌이나 패닝 중일 때 스크롤 방지
              if (e.touches.length >= 2 || touchState.isPanning) {
                e.preventDefault();
              }
              handleTouchMove(e); // 돋보기용
              handleChartGestureTouchMove(e); // 핀치 줌 & 스와이프 팬 & 크로스헤어
            }}
            onTouchEnd={() => {
              handleTouchEnd(); // 돋보기용
              handleChartGestureTouchEnd(); // 핀치 줌 & 스와이프 팬 종료
            }}
            sx={{ 
              width: '100%', 
              height: isFullscreen 
                ? 'calc(100vh - 180px)'  // 전체화면: 메뉴바(64) + 툴바(80) + 여백(36) 제외
                : `${getCandleChartHeight()}px`, 
              position: 'relative',
              touchAction: 'none', // 브라우저 기본 터치 제스처 비활성화
              WebkitUserSelect: 'none', // iOS 텍스트 선택 방지
              userSelect: 'none',
            }}
          >
            {/* 일목균형표 범례 */}
            {(settings.ichimokuTenkan || settings.ichimokuKijun || settings.ichimokuChikou || settings.ichimokuSenkouA || settings.ichimokuSenkouB) && (
              <Box sx={{ 
                  position: 'absolute',
                top: '50%', 
                transform: 'translateY(-50%)',
                left: isMobile ? 8 : 16, 
                  zIndex: 10,
                display: 'flex', 
                flexDirection: 'column', 
                gap: 0.5,
                bgcolor: 'rgba(0,0,0,0.5)',
                p: 0.5,
                  borderRadius: 1,
              }}>
                {settings.ichimokuTenkan && (
                  <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.75 }}>
                    <Box sx={{ width: 20, height: 2, bgcolor: colors.ichimokuTenkan }} />
                    <Typography variant="caption" sx={{ fontSize: '9px', color: colors.ichimokuTenkan, fontWeight: 'bold' }}>전환</Typography>
                  </Box>
                )}
                {settings.ichimokuKijun && (
                  <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.75 }}>
                    <Box sx={{ width: 20, height: 2, bgcolor: colors.ichimokuKijun }} />
                    <Typography variant="caption" sx={{ fontSize: '9px', color: colors.ichimokuKijun, fontWeight: 'bold' }}>기준</Typography>
                  </Box>
                )}
                {settings.ichimokuChikou && (
                  <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.75 }}>
                    <Box sx={{ width: 20, height: 2, bgcolor: colors.ichimokuChikou }} />
                    <Typography variant="caption" sx={{ fontSize: '9px', color: colors.ichimokuChikou, fontWeight: 'bold' }}>후행</Typography>
                  </Box>
                )}
                {settings.ichimokuSenkouA && (
                  <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.75 }}>
                    <Box sx={{ width: 20, height: 2, bgcolor: colors.ichimokuSenkouA || '#26A69A' }} />
                    <Typography variant="caption" sx={{ fontSize: '9px', color: colors.ichimokuSenkouA || '#26A69A', fontWeight: 'bold' }}>선행1</Typography>
                  </Box>
                )}
                {settings.ichimokuSenkouB && (
                  <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.75 }}>
                    <Box sx={{ 
                      width: 20, 
                      height: 2, 
                      background: `repeating-linear-gradient(90deg, ${colors.ichimokuSenkouB || '#EF5350'} 0px, ${colors.ichimokuSenkouB || '#EF5350'} 4px, transparent 4px, transparent 6px)` 
                    }} />
                    <Typography variant="caption" sx={{ fontSize: '9px', color: colors.ichimokuSenkouB || '#EF5350', fontWeight: 'bold' }}>선행2</Typography>
                  </Box>
                )}
              </Box>
            )}
            {/* 그리기 레이어 - 트레이딩뷰 모드일 때만 */}
            {!isMobile && chartMode === 'tradingView' && renderDrawingLayer()}
            
            {/* 크로스헤어 오버레이 (SVG) - 캔들차트 직접 호버 또는 보조지표에서 호버 시 표시 */}
            {(chartMode === 'basic' || activeTool === 'crosshair') && (crosshairPos || (candleChartSize && globalHoverYRatio !== null)) && (() => {
              // 캔들차트 직접 호버 시: crosshairPos 사용
              // 보조지표 호버 시: candleChartSize + globalHoverYRatio 사용
              const chartWidth = crosshairPos?.width ?? candleChartSize?.width ?? 0;
              const chartHeight = crosshairPos?.height ?? candleChartSize?.height ?? 0;
              
              if (chartWidth === 0 || chartHeight === 0) return null;
              
              // X 좌표: 캔들차트 직접 호버 시 마우스 X, 보조지표 호버 시 globalHoverChartX 또는 인덱스 기반 계산
              let xPos = crosshairPos?.x ?? globalHoverChartX ?? null;
              
              // Y 좌표: 캔들차트 직접 호버 시 마우스 Y, 보조지표 호버 시 비율 기반 계산
              let yPos = crosshairPos?.y ?? (globalHoverYRatio !== null ? globalHoverYRatio * chartHeight : null);
              
              // 가격 계산: Y 비율에서 가격 역계산 (차트 영역 마진 고려)
              const chartMarginTop = CHART_MARGIN_WITH_XAXIS.top || 36;
              const chartMarginBottom = CHART_MARGIN_WITH_XAXIS.bottom || 20;
              const dataAreaHeight = chartHeight - chartMarginTop - chartMarginBottom;
              
              let priceAtY: number | null = null;
              if (yPos !== null && chartData.length > 0 && dataAreaHeight > 0) {
                // 차트 데이터에서 가격 범위 계산
                const prices = chartData.flatMap(d => [d.high, d.low].filter(v => v != null));
                if (prices.length > 0) {
                  const minPrice = Math.min(...prices);
                  const maxPrice = Math.max(...prices);
                  const priceRange = maxPrice - minPrice;
                  
                  // Y 좌표에서 가격 역계산 (상단이 최고가, 하단이 최저가)
                  const yRatioInDataArea = (yPos - chartMarginTop) / dataAreaHeight;
                  const clampedRatio = Math.max(0, Math.min(1, yRatioInDataArea));
                  priceAtY = maxPrice - (clampedRatio * priceRange);
                }
              }
              
              return (
                <svg
                  width={chartWidth}
                  height={chartHeight}
                  style={{
                    position: 'absolute',
                    top: 0,
                    left: 0,
                    pointerEvents: 'none',
                    zIndex: 100,
                  }}
                >
                  {/* 세로선 (버티컬 크로스헤어) */}
                  {xPos !== null && (
                    <line
                      x1={xPos}
                      y1={0}
                      x2={xPos}
                      y2={chartHeight}
                      stroke="#888888"
                      strokeWidth={1}
                      opacity={0.8}
                    />
                  )}
                  {/* 가로선 (호리즌탈 크로스헤어) + 좌측 가격 표시 */}
                  {yPos !== null && (
                    <>
                      <line
                        x1={0}
                        y1={yPos}
                        x2={chartWidth}
                        y2={yPos}
                        stroke="#888888"
                        strokeWidth={1}
                        opacity={0.8}
                        strokeDasharray="4 2"
                      />
                      {/* 좌측 가격 라벨 (하늘색 스택) */}
                      {priceAtY !== null && (
                        <>
                          <rect
                            x={0}
                            y={yPos - 10}
                            width={55}
                            height={20}
                            fill="#0ea5e9"
                            rx={3}
                            ry={3}
                          />
                          <text
                            x={27}
                            y={yPos + 4}
                            textAnchor="middle"
                            fill="#ffffff"
                            fontSize={11}
                            fontWeight="bold"
                            fontFamily="sans-serif"
                          >
                            {formatCompactNumber(priceAtY)}
                          </text>
                        </>
                      )}
                    </>
                  )}
                </svg>
              );
            })()}
            
          <ResponsiveContainer width="100%" height="100%">
            <ComposedChart 
              data={chartData}
              syncId="upbitChart"
              margin={CHART_MARGIN}
              onMouseMove={(state) => handleChartMouseMove(state, 'candle')}
              onMouseLeave={handleChartMouseLeave}
              onClick={(state) => handleChartClick(state, 'candle')}
            >
              {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
              <XAxis 
                dataKey="date"
                type="category"
                hide={false}
                tick={{ fontSize: 9, fill: '#8b95a5' }}
                interval={getXAxisInterval()}
                tickFormatter={formatXAxisLabel}
                axisLine={{ stroke: '#252d3a' }}
                tickLine={{ stroke: '#252d3a' }}
                padding={{ left: 0, right: 0 }}
                allowDataOverflow={false}
              />
              
              {/* 가격용 Y축 (왼쪽) - 큰 숫자는 축약형으로 표시 (100M, 1B 등) */}
              <YAxis 
                yAxisId="price"
                orientation="left"
                tick={<CustomYAxisTick />}
                stroke="#252d3a"
                domain={getPriceYAxisDomain()}
                scale="linear"
                tickFormatter={formatCompactNumber}
                width={60}
                axisLine={{ stroke: '#252d3a' }}
              />
              
              {/* 돋보기 off일 때 Tooltip 표시 (크로스헤어는 Customized로 처리) */}
              {!magnifierEnabled && (
                <Tooltip 
                  content={settings.tooltipEnabled ? <CustomTooltip /> : () => null}
                  cursor={false}
                  wrapperStyle={settings.tooltipEnabled ? {} : { display: 'none' }}
                />
              )}
              <Legend 
                wrapperStyle={isMobile ? { fontSize: '10px' } : {}}
                verticalAlign="top"
                height={isMobile ? 36 : 36}
              />
                      {/* 크로스헤어 표시 (마우스 이동 시) */}
              {(chartMode === 'basic' || activeTool === 'crosshair') && (globalHoverChartX !== null || globalHoverIndex !== null) && chartData.length > 0 && (
                <Customized 
                  key={`crosshair-${globalHoverChartX ?? globalHoverIndex}-${globalHoverChartY ?? 0}`}
                  component={(props: any) => {
                    const { offset, xAxisMap } = props;
                    if (!offset || chartData.length === 0) return null;
                    
                    // X 좌표 계산: 직접 마우스 좌표 사용 또는 인덱스 기반 계산
                    let x: number | null = null;
                    
                    if (globalHoverChartX !== null) {
                      // 직접 마우스 X 좌표 사용
                      x = globalHoverChartX;
                    } else if (globalHoverIndex !== null) {
                      // 인덱스 기반 X 좌표 계산
                      const xAxis = xAxisMap?.[0];
                      if (xAxis?.scale && chartData[globalHoverIndex]?.date) {
                        x = xAxis.scale(chartData[globalHoverIndex].date);
                      } else {
                        const xAxisPadding = 10;
                        const dataAreaWidth = offset.width - (xAxisPadding * 2);
                        const barWidth = dataAreaWidth / chartData.length;
                        x = offset.left + xAxisPadding + (globalHoverIndex + 0.5) * barWidth;
                      }
                    }
                    
                    // 유효한 X 좌표인지 확인
                    if (x === null || isNaN(x)) return null;
                    
                    // Y 좌표 계산: 직접 마우스 좌표 사용 또는 비율 기반 계산
                    let y: number | null = null;
                    if (globalHoverChartY !== null) {
                      y = globalHoverChartY;
                    } else if (globalHoverYRatio !== null) {
                      y = offset.top + (globalHoverYRatio * offset.height);
                    }
                    
                    return (
                      <g pointerEvents="none">
                        {/* 세로선만 표시 (가로선은 SVG 오버레이에서 처리) */}
                        <line
                          x1={x}
                          y1={offset.top}
                          x2={x}
                          y2={offset.top + offset.height}
                          stroke="#888888"
                          strokeWidth={1}
                          opacity={0.8}
                        />
                      </g>
                    );
                  }}
                />
              )}
              
              {/* 캔들스틱 - Customized 컴포넌트 사용 */}
              <Customized 
                component={(props: any) => (
                  <Candlestick {...props} candleRising={colors.candleRising} candleFalling={colors.candleFalling} />
                )}
              />
              
              {/* 툴팁 트리거용 투명 라인 - Customized 캔들스틱은 Recharts 툴팁 시스템과 자동 연동되지 않으므로 필요 */}
              <Line
                yAxisId="price"
                type="monotone"
                dataKey="close"
                stroke="transparent"
                strokeWidth={0}
                dot={false}
                activeDot={false}
                legendType="none"
                isAnimationActive={false}
              />
              
              {/* 사용자 정의 이동평균선 (maLines 기반 - SMA/EMA 구분) */}
              {settings.maLines?.filter(ma => ma.enabled).map((ma) => (
                <Line
                  key={ma.id}
                  yAxisId="price"
                  type="monotone"
                  dataKey={`customMA_${ma.period}_${ma.type}`}
                  stroke={ma.color}
                  strokeWidth={ma.width}
                  dot={false}
                  name={`${ma.id}(${ma.type}${ma.period})`}
                  connectNulls
                  isAnimationActive={false}
                />
              ))}
              
              {/* 일목균형표 (Ichimoku Cloud) */}
              {/* 일목균형표 구름 영역 (선행스팬1과 선행스팬2 모두 켜졌을 때만 표시) */}
              {(settings.ichimokuSenkouA && settings.ichimokuSenkouB) && (
                <Customized
                  component={(props: any) => {
                    const { xAxisMap, yAxisMap, offset } = props;
                    if (!xAxisMap || !yAxisMap || !chartData || chartData.length === 0) return null;
                    
                    const xAxis = xAxisMap[0];
                    const yAxis = yAxisMap['price'];
                    if (!xAxis || !yAxis || !xAxis.scale || !yAxis.scale) return null;
                    
                    // 구름 영역을 세그먼트별로 그리기
                    const segments: { points: string; color: string }[] = [];
                    let currentSegment: { topPoints: [number, number][]; bottomPoints: [number, number][]; isBullish: boolean } | null = null;
                    
                    for (let i = 0; i < chartData.length; i++) {
                      const d = chartData[i];
                      if (d.ichimokuSenkouA == null || d.ichimokuSenkouB == null) {
                        // 세그먼트 종료
                        if (currentSegment && currentSegment.topPoints.length > 1) {
                          const topPath = currentSegment.topPoints.map(p => `${p[0]},${p[1]}`).join(' ');
                          const bottomPath = currentSegment.bottomPoints.reverse().map(p => `${p[0]},${p[1]}`).join(' ');
                          segments.push({
                            points: topPath + ' ' + bottomPath,
                            color: currentSegment.isBullish ? colors.ichimokuCloudBullish : colors.ichimokuCloudBearish
                          });
                        }
                        currentSegment = null;
                        continue;
                      }
                      
                      const x = xAxis.scale(d.date);
                      const yA = yAxis.scale(d.ichimokuSenkouA);
                      const yB = yAxis.scale(d.ichimokuSenkouB);
                      
                      if (x == null || isNaN(x) || yA == null || isNaN(yA) || yB == null || isNaN(yB)) continue;
                      
                      const isBullish = d.ichimokuSenkouA >= d.ichimokuSenkouB;
                      const top = Math.min(yA, yB);
                      const bottom = Math.max(yA, yB);
                      
                      if (!currentSegment || currentSegment.isBullish !== isBullish) {
                        // 이전 세그먼트 저장
                        if (currentSegment && currentSegment.topPoints.length > 1) {
                          const topPath = currentSegment.topPoints.map(p => `${p[0]},${p[1]}`).join(' ');
                          const bottomPath = currentSegment.bottomPoints.reverse().map(p => `${p[0]},${p[1]}`).join(' ');
                          segments.push({
                            points: topPath + ' ' + bottomPath,
                            color: currentSegment.isBullish ? colors.ichimokuCloudBullish : colors.ichimokuCloudBearish
                          });
                        }
                        // 새 세그먼트 시작
                        currentSegment = { topPoints: [], bottomPoints: [], isBullish };
                      }
                      
                      currentSegment.topPoints.push([x, top]);
                      currentSegment.bottomPoints.push([x, bottom]);
                    }
                    
                    // 마지막 세그먼트 저장
                    if (currentSegment && currentSegment.topPoints.length > 1) {
                      const topPath = currentSegment.topPoints.map(p => `${p[0]},${p[1]}`).join(' ');
                      const bottomPath = currentSegment.bottomPoints.reverse().map(p => `${p[0]},${p[1]}`).join(' ');
                      segments.push({
                        points: topPath + ' ' + bottomPath,
                        color: currentSegment.isBullish ? colors.ichimokuCloudBullish : colors.ichimokuCloudBearish
                      });
                    }
                    
                    return (
                      <g>
                        {segments.map((seg, idx) => (
                          <polygon
                            key={idx}
                            points={seg.points}
                            fill={seg.color}
                            stroke="none"
                            opacity={0.5}
                          />
                        ))}
                      </g>
                    );
                  }}
                />
              )}
              
              {/* 전환선 (Tenkan-sen) - 9일 */}
              {settings.ichimokuTenkan && (
                <Line
                  yAxisId="price"
                  type="monotone"
                  dataKey="ichimokuTenkan"
                  stroke={colors.ichimokuTenkan}
                  strokeWidth={colors.ichimokuTenkanWidth}
                  dot={false}
                  name="전환선"
                  connectNulls
                  isAnimationActive={false}
                />
              )}
              
              {/* 기준선 (Kijun-sen) - 26일 */}
              {settings.ichimokuKijun && (
                <Line
                  yAxisId="price"
                  type="monotone"
                  dataKey="ichimokuKijun"
                  stroke={colors.ichimokuKijun}
                  strokeWidth={colors.ichimokuKijunWidth}
                  dot={false}
                  name="기준선"
                  connectNulls
                  isAnimationActive={false}
                />
              )}
              
              {/* 후행스팬 (Chikou Span) */}
              {settings.ichimokuChikou && (
                <Line
                  yAxisId="price"
                  type="monotone"
                  dataKey="ichimokuChikou"
                  stroke={colors.ichimokuChikou}
                  strokeWidth={colors.ichimokuChikouWidth}
                  strokeDasharray="5 5"
                  dot={false}
                  name="후행스팬"
                  connectNulls
                  isAnimationActive={false}
                />
              )}
              
              {/* 선행스팬1 (Senkou Span A) - 구름 상단 경계 - 실선 (청록색) */}
              {settings.ichimokuSenkouA && (
                <Line
                  yAxisId="price"
                  type="monotone"
                  dataKey="ichimokuSenkouA"
                  stroke={colors.ichimokuSenkouA || '#26A69A'}
                  strokeWidth={Math.max(colors.ichimokuSenkouAWidth || 1, 2)}
                  dot={false}
                  name="선행스팬1"
                  connectNulls
                  isAnimationActive={false}
                />
              )}
              
              {/* 선행스팬2 (Senkou Span B) - 구름 하단 경계 - 점선 (빨간색) */}
              {settings.ichimokuSenkouB && (
                <Line
                  yAxisId="price"
                  type="monotone"
                  dataKey="ichimokuSenkouB"
                  stroke={colors.ichimokuSenkouB || '#EF5350'}
                  strokeWidth={Math.max(colors.ichimokuSenkouBWidth || 1, 2)}
                  strokeDasharray="8 4"
                  dot={false}
                  name="선행스팬2"
                  connectNulls
                  isAnimationActive={false}
                />
              )}
              
              {/* 볼린저 밴드 (Bollinger Bands) */}
              {/* 상한선 (Upper Band) */}
              {settings.bollingerUpper && (
                <Line
                  yAxisId="price"
                  type="monotone"
                  dataKey="bollingerUpper"
                  stroke={colors.bollingerUpperColor || '#FF9800'}
                  strokeWidth={colors.bollingerUpperWidth || 1.5}
                  strokeDasharray={colors.bollingerUpperLineStyle === 'dashed' ? '5 5' : colors.bollingerUpperLineStyle === 'dotted' ? '2 2' : ''}
                  dot={false}
                  name={`BB 상한선(${settings.bollingerPeriod},${settings.bollingerStdDev})`}
                  connectNulls
                  isAnimationActive={false}
                />
              )}
              
              {/* 중간선 (Middle Band) */}
              {settings.bollingerMiddle && (
                <Line
                  yAxisId="price"
                  type="monotone"
                  dataKey="bollingerMiddle"
                  stroke={colors.bollingerMiddleColor || '#2196F3'}
                  strokeWidth={colors.bollingerMiddleWidth || 1.5}
                  strokeDasharray={colors.bollingerMiddleLineStyle === 'dashed' ? '5 5' : colors.bollingerMiddleLineStyle === 'dotted' ? '2 2' : ''}
                  dot={false}
                  name={`BB 중간선(${settings.bollingerPeriod},${settings.bollingerStdDev})`}
                  connectNulls
                  isAnimationActive={false}
                />
              )}
              
              {/* 하한선 (Lower Band) */}
              {settings.bollingerLower && (
                <Line
                  yAxisId="price"
                  type="monotone"
                  dataKey="bollingerLower"
                  stroke={colors.bollingerLowerColor || '#4CAF50'}
                  strokeWidth={colors.bollingerLowerWidth || 1.5}
                  strokeDasharray={colors.bollingerLowerLineStyle === 'dashed' ? '5 5' : colors.bollingerLowerLineStyle === 'dotted' ? '2 2' : ''}
                  dot={false}
                  name={`BB 하한선(${settings.bollingerPeriod},${settings.bollingerStdDev})`}
                  connectNulls
                  isAnimationActive={false}
                />
              )}
              
              {/* 백테스트 매수/매도 신호 마커 (Customized 컴포넌트로 직접 렌더링) */}
              <Customized
                component={(props: any) => {
                  const { xAxisMap, yAxisMap, data } = props;
                  
                  if (!xAxisMap || !yAxisMap || !data) return null;
                  
                  const xAxis = xAxisMap[0];
                  const yAxis = yAxisMap['price'] || yAxisMap[0];
                  
                  if (!xAxis || !yAxis) return null;
                  
                  const markers: JSX.Element[] = [];
                  
                  data.forEach((item: any, index: number) => {
                    // X 좌표 계산
                    const xValue = xAxis.scale(item.date);
                    if (xValue === undefined || isNaN(xValue)) return;
                    const cx = xValue + (xAxis.bandSize || 0) / 2;
                    
                    // 매수 마커
                    if (item.backtestBuyMarker !== null && item.backtestBuyMarker !== undefined) {
                      const cy = yAxis.scale(item.backtestBuyMarker);
                      if (cy !== undefined && !isNaN(cy)) {
                        markers.push(
                          <g 
                            key={`buy-${index}`} 
                            style={{ cursor: onMarkerClick ? 'pointer' : 'default' }}
                            onClick={() => onMarkerClick?.(item.timestamp, 'BUY')}
                          >
                            <polygon
                              points={`${cx},${cy - 10} ${cx - 8},${cy + 4} ${cx + 8},${cy + 4}`}
                              fill="#2196F3"
                              stroke="#fff"
                              strokeWidth={1.5}
                            />
                            <text
                              x={cx}
                              y={cy - 14}
                              textAnchor="middle"
                              fill="#2196F3"
                              fontSize="11"
                              fontWeight="bold"
                              style={{ pointerEvents: 'none' }}
                            >
                              매수
                            </text>
                          </g>
                        );
                      }
                    }
                    
                    // 매도 마커
                    if (item.backtestSellMarker !== null && item.backtestSellMarker !== undefined) {
                      const cy = yAxis.scale(item.backtestSellMarker);
                      if (cy !== undefined && !isNaN(cy)) {
                        markers.push(
                          <g 
                            key={`sell-${index}`}
                            style={{ cursor: onMarkerClick ? 'pointer' : 'default' }}
                            onClick={() => onMarkerClick?.(item.timestamp, 'SELL')}
                          >
                            <polygon
                              points={`${cx},${cy + 10} ${cx - 8},${cy - 4} ${cx + 8},${cy - 4}`}
                              fill="#FF9800"
                              stroke="#fff"
                              strokeWidth={1.5}
                            />
                            <text
                              x={cx}
                              y={cy + 24}
                              textAnchor="middle"
                              fill="#FF9800"
                              fontSize="11"
                              fontWeight="bold"
                              style={{ pointerEvents: 'none' }}
                            >
                              매도
                            </text>
                          </g>
                        );
                      }
                    }
                  });
                  
                  return <g className="backtest-markers">{markers}</g>;
                }}
              />
            </ComposedChart>
          </ResponsiveContainer>
          
        </Box>
        
        {/* 좌우 화살표 버튼 - 캔들차트 Y축 중앙에 배치 */}
        {data.length > 0 && (
          <>
            {/* 왼쪽 화살표 버튼 (과거 데이터 로드) */}
            {showLeftButton && (
              <IconButton
                onClick={(e) => {
                  e.stopPropagation();
                  handleLoadPastData();
                }}
                disabled={isLoadingMore}
                size="small"
                sx={{
                  position: 'absolute',
                  left: 4,
                  top: '50%',
                  transform: 'translateY(-50%)',
                  zIndex: 20,
                  color: 'white',
                  bgcolor: 'rgba(0, 0, 0, 0.4)',
                  borderRadius: '50%',
                  width: 32,
                  height: 32,
                  '&:hover': {
                    bgcolor: 'rgba(0, 0, 0, 0.6)',
                  },
                  '&:disabled': {
                    color: 'rgba(255, 255, 255, 0.3)',
                    bgcolor: 'rgba(0, 0, 0, 0.2)',
                  },
                }}
              >
                {isLoadingMore ? (
                  <CircularProgress size={16} sx={{ color: 'white' }} />
                ) : (
                  <ChevronLeft fontSize="medium" />
                )}
              </IconButton>
            )}
            
            {/* 오른쪽 화살표 버튼 (미래/최신 데이터 로드) */}
            {showRightButton && (
              <IconButton
                onClick={(e) => {
                  e.stopPropagation();
                  handleLoadFutureData();
                }}
                disabled={isLoadingMore}
                size="small"
                sx={{
                  position: 'absolute',
                  right: isMobile ? 55 : 70,
                  top: '50%',
                  transform: 'translateY(-50%)',
                  zIndex: 20,
                  color: 'white',
                  bgcolor: 'rgba(0, 0, 0, 0.4)',
                  borderRadius: '50%',
                  width: 32,
                  height: 32,
                  '&:hover': {
                    bgcolor: 'rgba(0, 0, 0, 0.6)',
                  },
                  '&:disabled': {
                    color: 'rgba(255, 255, 255, 0.3)',
                    bgcolor: 'rgba(0, 0, 0, 0.2)',
                  },
                }}
              >
                {isLoadingMore ? (
                  <CircularProgress size={16} sx={{ color: 'white' }} />
                ) : (
                  <ChevronRight fontSize="medium" />
                )}
              </IconButton>
            )}
          </>
        )}
        </Box> {/* 캔들차트 영역 래퍼 닫기 */}
        
        {/* 거래량 차트 (조건부 렌더링) - 업비트 스타일로 이동됨 - 비활성화 */}
        {false && !isFullscreen && settings.volume && (
        <>
        {/* 여백 영역 */}
        <Box sx={{ height: '8px', bgcolor: 'action.hover', borderTop: 1, borderBottom: 1, borderColor: 'divider' }} />
        
        {/* 거래량 차트 영역 (하단 30%) */}
        <Box sx={{ width: '100%', height: isMobile ? '150px' : '150px', position: 'relative' }}>
          <ResponsiveContainer width="100%" height="100%">
            <BarChart 
              data={chartData}
              margin={isMobile ? 
                { top: 0, right: 15, left: 5, bottom: 5 } : 
                { top: 0, right: 65, left: 55, bottom: 5 }
              }
              onMouseMove={(state: any) => {
                if (state && state.activePayload && state.activePayload.length > 0) {
                  const data = state.activePayload[0].payload;
                  const index = state.activeTooltipIndex;
                  
                  // 전역 마우스 위치 업데이트
                  if (index !== undefined && data.date) {
                    setGlobalHoverIndex(index);
                    setGlobalHoverLabel(data.date);
                    setGlobalHoverDate(data.date);
                  }
                  
                  setVolumeYValue(data.volume);
                  if (state.chartY !== undefined) {
                    setVolumeYPosition(state.chartY);
                  }
                }
              }}
              onMouseLeave={handleChartMouseLeave}
            >
              {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} />}
              <XAxis 
                dataKey="date"
                type="category"
                tick={{ fontSize: 9, fill: '#8b95a5' }}
                interval={getXAxisInterval()}
                stroke="#252d3a"
                tickFormatter={formatXAxisLabel}
                allowDataOverflow={false}
                padding={{ left: 0, right: 0 }}
              />
              
              {/* 거래량용 Y축 (왼쪽) - 캔들차트와 정렬 */}
              <YAxis 
                orientation="left"
                tick={{ fontSize: 11, fill: '#8b95a5' }}
                stroke="#252d3a"
                tickFormatter={(value) => {
                  if (value >= 1000000) return `${(value / 1000000).toFixed(1)}M`;
                  if (value >= 1000) return `${(value / 1000).toFixed(0)}K`;
                  return value.toString();
                }}
                domain={[0, (dataMax: number) => {
                  const volumes = chartData.map(d => d.volume).filter(v => v > 0).sort((a, b) => a - b);
                  if (volumes.length === 0) return dataMax * 1.2;
                  const percentile95Index = Math.floor(volumes.length * 0.95);
                  const percentile95 = volumes[percentile95Index] || dataMax;
                  // 최댓값의 1.2배로 설정하여 상단에 여백 확보 (20% 여유 공간)
                  return Math.ceil(Math.min(dataMax, percentile95 * 1.5) * 1.2);
                }]}
              />
              
              <Tooltip 
                formatter={(value: any) => [value?.toLocaleString(), '거래량']}
                labelFormatter={(label) => `날짜: ${label}`}
                contentStyle={{
                  backgroundColor: 'rgba(255, 255, 255, 0.95)',
                  border: '1px solid #ccc',
                  borderRadius: '4px'
                }}
                cursor={false}
              />
              
              {/* X축 크로스헤어 (세로선) */}
              {(chartMode === 'basic' || activeTool === 'crosshair') && (
                <Customized 
                  component={(props: any) => (
                    <VerticalCrosshair {...props} chartData={chartData} />
                  )}
                />
              )}
              
              {/* 거래량 바 차트 */}
              <Bar 
                dataKey="volume" 
                name="거래량"
                radius={[2, 2, 0, 0]}
                maxBarSize={isMobile ? 8 : 10}
                activeBar={false}
              >
                {chartData.map((entry, index) => (
                  <Cell 
                    key={`cell-${index}`} 
                    fill={entry.volumeColor || '#42a5f5'}
                    opacity={0.7}
                  />
                ))}
              </Bar>
            </BarChart>
          </ResponsiveContainer>
          
          {/* Y축 값 표시 Chip (거래량) */}
          {volumeYValue !== null && volumeYPosition !== null && (
            <Chip
              label={(volumeYValue as number).toLocaleString()}
              size="small"
              sx={{
                position: 'absolute',
                left: isMobile ? 5 : 10,
                top: volumeYPosition,
                transform: 'translateY(-50%)',
                bgcolor: '#1976d2',
                color: 'white',
                fontWeight: 'bold',
                fontSize: '0.75rem',
                height: '24px',
                zIndex: 1000,
                pointerEvents: 'none',
              }}
            />
          )}
        </Box>
        </>
        )}
      </Box>
      )}

      {/* ===== 업비트 스타일 개별 보조지표 차트 (드래그 앤 드롭) ===== */}
      {!isFullscreen && (
      <>
        {/* 보조지표 선택 드롭다운 - 보조지표 영역 상단 */}
        <Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 0.5, mt: 0.5 }}>
          <MuiTooltip title={`보조지표 (${order.filter(item => {
              const excludeKeys = ['candleChart', 'macdHistogram', 'priceHistogram', 'volumeIndicator', 'tooltipEnabled', 'indicatorHeight'];
              const maKeys = ['ma10', 'ma20', 'ma60', 'ma120', 'ma200', 'ma300', 'ma480'];
              if (excludeKeys.includes(item.settingKey) || maKeys.includes(item.settingKey)) return false;
              return settings[item.settingKey];
            }).length}개 선택)`} arrow>
          <Button
            variant="outlined"
            size="small"
            onClick={(e) => setIndicatorMenuAnchor(e.currentTarget)}
            startIcon={<BarChartIcon sx={{ fontSize: isMobile ? 16 : 18 }} />}
            endIcon={<KeyboardArrowDown />}
            sx={{
              fontSize: isMobile ? '11px' : '12px',
              textTransform: 'none',
              borderColor: 'divider',
              px: 1,
            }}
          >
            보조지표({order.filter(item => {
              const excludeKeys = ['candleChart', 'macdHistogram', 'priceHistogram', 'volumeIndicator', 'tooltipEnabled', 'indicatorHeight'];
              const maKeys = ['ma10', 'ma20', 'ma60', 'ma120', 'ma200', 'ma300', 'ma480'];
              if (excludeKeys.includes(item.settingKey) || maKeys.includes(item.settingKey)) return false;
              return settings[item.settingKey];
            }).length})
          </Button>
          </MuiTooltip>
          <Menu
            anchorEl={indicatorMenuAnchor}
            open={Boolean(indicatorMenuAnchor)}
            onClose={() => setIndicatorMenuAnchor(null)}
            anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
            transformOrigin={{ vertical: 'top', horizontal: 'left' }}
            slotProps={{
              paper: {
                sx: {
                  maxHeight: 400,
                  overflowY: 'auto',
                }
              }
            }}
          >
            {order.filter(item => {
              const excludeKeys = ['candleChart', 'macdHistogram', 'priceHistogram', 'volumeIndicator', 'tooltipEnabled', 'indicatorHeight', 'stochasticD', 'stochasticSlow', 'trixSignal'];
              const maKeys = ['ma10', 'ma20', 'ma60', 'ma120', 'ma200', 'ma300', 'ma480'];
              return !excludeKeys.includes(item.settingKey) && !maKeys.includes(item.settingKey);
            }).map((item, index, arr) => {
              const colorMap: Record<string, 'info' | 'primary' | 'warning' | 'secondary' | 'success' | 'error'> = {
                volume: 'info', newPsychological: 'secondary', investPsychological: 'secondary',
                macdLine: 'primary', cci: 'error', adx: 'success', bwi: 'warning',
                plusDi: 'success', minusDi: 'error', obv: 'info', rsi: 'secondary',
                stochasticK: 'warning', williamsR: 'error', trix: 'primary', volumeOsc: 'info',
                vr: 'warning', disparity: 'info',
              };
              
              const handleToggle = () => {
                if (item.settingKey === 'macdLine') {
                  updateSettings({ macdLine: !settings.macdLine, macdHistogram: !settings.macdLine });
                } else if (item.settingKey === 'stochasticK') {
                  updateSettings({ stochasticK: !settings.stochasticK, stochasticD: !settings.stochasticK, stochasticSlow: !settings.stochasticK });
                } else if (item.settingKey === 'plusDi') {
                  updateSettings({ plusDi: !settings.plusDi, minusDi: !settings.plusDi });
                } else if (item.settingKey === 'trix') {
                  updateSettings({ trix: !settings.trix, trixSignal: !settings.trix });
                } else {
                  updateSettings({ [item.settingKey]: !settings[item.settingKey] });
                }
              };

              const displayLabel = item.settingKey === 'macdLine' ? 'MACD' : 
                                   item.settingKey === 'stochasticK' ? 'Stochastic Slow' :
                                   item.settingKey === 'plusDi' ? 'DMI' :
                                   item.settingKey === 'trix' ? 'TRIX' : item.label;

              return (
                <React.Fragment key={item.id}>
                  {item.settingKey === 'volume' && index > 0 && <Divider />}
                  {item.settingKey !== 'volume' && arr[index - 1]?.settingKey === 'volume' && <Divider />}
                  <MenuItem onClick={handleToggle} sx={{ py: 0.5 }}>
                    <Checkbox 
                      checked={Boolean(settings[item.settingKey])} 
                      size="small" 
                      sx={{ p: 0.5 }} 
                      color={colorMap[item.settingKey] || 'primary'} 
                    />
                    <ListItemText primary={displayLabel} primaryTypographyProps={{ fontSize: '13px' }} />
                  </MenuItem>
                </React.Fragment>
              );
            })}
            <Divider sx={{ my: 1 }} />
            <Box sx={{ px: 2, py: 1, minWidth: 180 }}>
              <Typography variant="caption" sx={{ fontSize: '11px', color: 'text.secondary', mb: 0.5, display: 'block' }}>
                차트 높이: {settings.indicatorHeight || 70}px
              </Typography>
              <Slider
                value={settings.indicatorHeight || 70}
                min={30}
                max={150}
                step={5}
                size="small"
                onChange={(_, value) => updateSettings({ indicatorHeight: value as number })}
                sx={{ 
                  width: '100%',
                  '& .MuiSlider-thumb': { width: 12, height: 12 },
                  '& .MuiSlider-track': { height: 3 },
                  '& .MuiSlider-rail': { height: 3 },
                }}
              />
              <Box sx={{ display: 'flex', justifyContent: 'space-between', mt: -0.5 }}>
                <Typography variant="caption" sx={{ fontSize: '9px', color: 'text.disabled' }}>작게</Typography>
                <Typography variant="caption" sx={{ fontSize: '9px', color: 'text.disabled' }}>크게</Typography>
              </Box>
            </Box>
            <Divider sx={{ my: 0.5 }} />
            {/* 보조지표 설정 버튼 */}
            <MenuItem 
              onClick={() => {
                setIndicatorMenuAnchor(null);
                setIndicatorSettingsDialogOpen(true);
              }} 
              sx={{ py: 1 }}
            >
              <ListItemIcon>
                <SettingsIcon fontSize="small" />
              </ListItemIcon>
              <ListItemText 
                primary="보조지표 설정..." 
                primaryTypographyProps={{ fontSize: '13px', fontWeight: 'bold' }} 
              />
            </MenuItem>
          </Menu>
        </Box>
        
      <IndicatorChartsSection
        settings={settings}
        order={order}
        updateOrder={updateOrder}
        updateSettings={updateSettings}
        isMobile={isMobile}
        chartData={chartData}
        colors={colors}
        indicatorSettings={indicatorSettings}
        showGrid={showGrid}
        shouldShowXAxis={shouldShowXAxis}
        INDICATOR_HEIGHT={INDICATOR_HEIGHT}
        INDICATOR_XAXIS_EXTRA={INDICATOR_XAXIS_EXTRA}
        CHART_MARGIN={CHART_MARGIN}
        CHART_MARGIN_WITH_XAXIS={CHART_MARGIN_WITH_XAXIS}
        getXAxisInterval={getXAxisInterval}
        formatXAxisLabel={formatXAxisLabel}
        formatCompactNumber={formatCompactNumber}
        getMacdYAxisDomain={getMacdYAxisDomain}
        handleChartMouseMove={handleChartMouseMove}
        handleChartMouseLeave={handleChartMouseLeave}
        handleChartClick={handleChartClick}
        handleChartAreaClick={handleChartAreaClickInternal}
        chartMode={chartMode}
        activeTool={activeTool}
        VerticalCrosshair={VerticalCrosshair}
        globalHoverDate={globalHoverDate}
        globalHoverYRatio={globalHoverYRatio}
        externalHoverMatchDate={externalHoverMatch.date}
        handleIndicatorMouseMove={handleIndicatorMouseMove}
        handleIndicatorMouseLeave={handleIndicatorMouseLeave}
        handleIndicatorTouchStart={handleIndicatorTouchStart}
        handleIndicatorTouchMove={handleIndicatorTouchMove}
        handleIndicatorTouchEnd={handleIndicatorTouchEnd}
      />
      </>
      )}

      {/* ===== 기존 보조지표 차트 (숨김) ===== */}
      {false && !isFullscreen && (
      <Box sx={{ order: 100 }}>
        
        {/* 보조지표 차트 영역 - 하나라도 선택된 경우에만 표시 */}
        {/* ===== 거래량 개별 차트 (업비트 스타일) ===== */}
        {settings.volume && (
          <Box sx={{ 
            height: `${INDICATOR_HEIGHT + INDICATOR_XAXIS_EXTRA}px`, 
            position: 'relative',
            mt: 0,
          }}>
            {/* 좌측상단: 타이틀 */}
            <Typography variant="caption" sx={{
              ...indicatorTitleStyle,
            }}>
              거래량
            </Typography>
            <ResponsiveContainer width="100%" height="100%">
              <BarChart 
                data={chartData}
                syncId="upbitChart"
                margin={shouldShowXAxis('volume') ? CHART_MARGIN_WITH_XAXIS : CHART_MARGIN}
                onMouseMove={(state) => handleChartMouseMove(state, 'volume')}
                onMouseLeave={handleChartMouseLeave}
                onClick={(state) => handleChartClick(state, 'volume')}
              >
                {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                <XAxis 
                  dataKey="date" 
                  hide={!shouldShowXAxis('volume')}
                  tick={{ fontSize: 9, fill: '#8b95a5' }}
                  interval={getXAxisInterval()}
                  tickFormatter={formatXAxisLabel}
                  axisLine={{ stroke: '#252d3a' }}
                  tickLine={{ stroke: '#252d3a' }}
                  padding={{ left: 0, right: 0 }}
                />
                <YAxis orientation="left" tick={<CustomYAxisTick />} width={60} tickFormatter={formatCompactNumber} stroke="#252d3a" />
                {settings.tooltipEnabled && <Tooltip formatter={(value: any) => [formatCompactNumber(value), '거래량']} />}
                <Bar dataKey="volume" radius={[1, 1, 0, 0]} maxBarSize={8}>
                  {chartData.map((entry, index) => (
                    <Cell key={`vol-${index}`} fill={entry.volumeColor || '#42a5f5'} opacity={0.7} />
                  ))}
                </Bar>
              </BarChart>
            </ResponsiveContainer>
          </Box>
        )}
        
        {/* ===== MACD 개별 차트 (업비트 스타일) ===== */}
        {(settings.macdLine || settings.macdHistogram) && (
          <Box sx={{ 
            height: `${INDICATOR_HEIGHT + INDICATOR_XAXIS_EXTRA}px`, 
            position: 'relative',
            mt: 0,
          }}>
            {/* 좌측상단: 타이틀 */}
            <Typography variant="caption" sx={{
              ...indicatorTitleStyle,
            }}>
              MACD ({indicatorSettings.macdFastPeriod},{indicatorSettings.macdSlowPeriod},{indicatorSettings.macdSignalPeriod})
            </Typography>
            <ResponsiveContainer width="100%" height="100%">
              <ComposedChart 
                data={chartData}
                syncId="upbitChart"
                margin={shouldShowXAxis('macd') ? CHART_MARGIN_WITH_XAXIS : CHART_MARGIN}
                onMouseMove={(state) => handleChartMouseMove(state, 'macd')}
                onMouseLeave={handleChartMouseLeave}
                onClick={(state) => handleChartClick(state, 'macd')}
              >
                {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                <XAxis 
                  dataKey="date" 
                  hide={!shouldShowXAxis('macd')}
                  tick={{ fontSize: 9, fill: '#8b95a5' }}
                  interval={getXAxisInterval()}
                  tickFormatter={formatXAxisLabel}
                  axisLine={{ stroke: '#252d3a' }}
                  tickLine={{ stroke: '#252d3a' }}
                  padding={{ left: 0, right: 0 }}
                />
                <YAxis orientation="left" tick={<CustomYAxisTick />} width={60} domain={getMacdYAxisDomain()} tickFormatter={formatCompactNumber} stroke="#252d3a" />
                {settings.tooltipEnabled && <Tooltip formatter={(value: any, name: string) => [value?.toFixed(2) ?? '-', name]} />}
                {indicatorSettings.macdZeroLineEnabled && <ReferenceLine y={0} stroke={colors.macdZeroLineColor || "#666"} strokeDasharray="2 2" strokeWidth={colors.macdZeroLineWidth || 0.5} />}
                {/* X축 크로스헤어 (세로선) - 모든 차트 동기화 */}
                {(chartMode === 'basic' || activeTool === 'crosshair') && (
                  <Customized 
                    component={(props: any) => (
                      <VerticalCrosshair {...props} chartData={chartData} />
                    )}
                  />
                )}
                {settings.macdHistogram && (
                  <Bar dataKey="macdHistogram" name="Histogram" radius={[1, 1, 0, 0]}>
                    {chartData.map((entry, index) => (
                      <Cell key={`hist-${index}`} fill={entry.histogramColor} />
                    ))}
                  </Bar>
                )}
                {settings.macdLine && (
                  <>
                    <Line type="monotone" dataKey="macdLine" stroke={colors.macdLine} strokeWidth={1.5} dot={false} name="MACD" connectNulls isAnimationActive={false} />
                    <Line type="monotone" dataKey="signalLine" stroke={colors.signalLine} strokeWidth={1.5} dot={false} name="Signal" connectNulls isAnimationActive={false} />
                  </>
                )}
              </ComposedChart>
            </ResponsiveContainer>
          </Box>
        )}
        
        {/* ===== Stochastic 개별 차트 (업비트 스타일) ===== */}
        {(settings.stochasticK || settings.stochasticD || settings.stochasticSlow) && (
          <Box sx={{ 
            height: `${INDICATOR_HEIGHT + INDICATOR_XAXIS_EXTRA}px`, 
            position: 'relative',
            mt: 0,
          }}>
            {/* 좌측상단: 타이틀 */}
            <Typography variant="caption" sx={{
              ...indicatorTitleStyle,
            }}>
              STOCHASTIC ({indicatorSettings.stochasticKPeriod},{indicatorSettings.stochasticDPeriod},{indicatorSettings.stochasticSlowing})
            </Typography>
            <ResponsiveContainer width="100%" height="100%">
              <LineChart 
                data={chartData}
                syncId="upbitChart"
                margin={shouldShowXAxis('stochastic') ? CHART_MARGIN_WITH_XAXIS : CHART_MARGIN}
                onMouseMove={(state) => handleChartMouseMove(state, 'stochastic')}
                onMouseLeave={handleChartMouseLeave}
                onClick={(state) => handleChartClick(state, 'stochastic')}
              >
                {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                <XAxis 
                  dataKey="date" 
                  hide={!shouldShowXAxis('stochastic')}
                  tick={{ fontSize: 9, fill: '#8b95a5' }}
                  interval={getXAxisInterval()}
                  tickFormatter={formatXAxisLabel}
                  axisLine={{ stroke: '#252d3a' }}
                  tickLine={{ stroke: '#252d3a' }}
                  padding={{ left: 0, right: 0 }}
                />
                <YAxis orientation="left" domain={[0, 100]} tick={<CustomYAxisTick />} width={60} ticks={[indicatorSettings.stochasticOversold, 50, indicatorSettings.stochasticOverbought]} stroke="#252d3a" />
                {settings.tooltipEnabled && <Tooltip formatter={(value: any, name: string) => [value?.toFixed(2) ?? '-', name]} />}
                <ReferenceLine y={indicatorSettings.stochasticOverbought} stroke={colors.stochasticOverboughtColor} strokeDasharray="2 2" strokeWidth={0.5} />
                <ReferenceLine y={indicatorSettings.stochasticNeutral} stroke={colors.stochasticNeutralColor} strokeDasharray="2 2" strokeWidth={0.5} />
                <ReferenceLine y={indicatorSettings.stochasticOversold} stroke={colors.stochasticOversoldColor} strokeDasharray="2 2" strokeWidth={0.5} />
                {/* X축 크로스헤어 (세로선) - 모든 차트 동기화 */}
                {(chartMode === 'basic' || activeTool === 'crosshair') && (
                  <Customized 
                    component={(props: any) => (
                      <VerticalCrosshair {...props} chartData={chartData} />
                    )}
                  />
                )}
                <Line type="monotone" dataKey="stochasticK" stroke={colors.stochasticK} strokeWidth={1.5} dot={false} name="%K" connectNulls isAnimationActive={false} />
                <Line type="monotone" dataKey="stochasticD" stroke={colors.stochasticD} strokeWidth={1.5} dot={false} name="%D" connectNulls isAnimationActive={false} />
              </LineChart>
            </ResponsiveContainer>
          </Box>
        )}
        
        {/* ===== RSI 개별 차트 (업비트 스타일) ===== */}
        {settings.rsi && (
          <Box sx={{ 
            height: `${INDICATOR_HEIGHT + INDICATOR_XAXIS_EXTRA}px`, 
            position: 'relative',
            mt: 0,
          }}>
            {/* 좌측상단: 타이틀 */}
            <Typography variant="caption" sx={{
              ...indicatorTitleStyle,
            }}>
              RSI ({indicatorSettings.rsiPeriod}, {indicatorSettings.rsiSignalPeriod})
            </Typography>
            <ResponsiveContainer width="100%" height="100%">
              <LineChart 
                data={chartData}
                syncId="upbitChart"
                margin={shouldShowXAxis('rsi') ? CHART_MARGIN_WITH_XAXIS : CHART_MARGIN}
                onMouseMove={(state) => handleChartMouseMove(state, 'rsi')}
                onMouseLeave={handleChartMouseLeave}
                onClick={(state) => handleChartClick(state, 'rsi')}
              >
                {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                <XAxis 
                  dataKey="date" 
                  hide={!shouldShowXAxis('rsi')}
                  tick={{ fontSize: 9, fill: '#8b95a5' }}
                  interval={getXAxisInterval()}
                  tickFormatter={formatXAxisLabel}
                  axisLine={{ stroke: '#252d3a' }}
                  tickLine={{ stroke: '#252d3a' }}
                  padding={{ left: 0, right: 0 }}
                />
                <YAxis orientation="left" domain={[0, 100]} tick={<CustomYAxisTick />} width={60} ticks={[indicatorSettings.rsiOversold, 50, indicatorSettings.rsiOverbought]} stroke="#252d3a" />
                {settings.tooltipEnabled && <Tooltip formatter={(value: any, name: string) => [value?.toFixed(2) ?? '-', name === 'rsiSignal' ? 'Signal' : 'RSI']} />}
                {indicatorSettings.rsiOverboughtEnabled && <ReferenceLine y={indicatorSettings.rsiOverbought} stroke={colors.rsiOverboughtColor || "#f44336"} strokeDasharray="2 2" strokeWidth={colors.rsiOverboughtWidth || 0.5} />}
                {indicatorSettings.rsiMiddleEnabled && <ReferenceLine y={indicatorSettings.rsiMiddle} stroke={colors.rsiMiddleColor || "#666"} strokeDasharray="2 2" strokeWidth={colors.rsiMiddleWidth || 0.5} />}
                {indicatorSettings.rsiOversoldEnabled && <ReferenceLine y={indicatorSettings.rsiOversold} stroke={colors.rsiOversoldColor || "#4caf50"} strokeDasharray="2 2" strokeWidth={colors.rsiOversoldWidth || 0.5} />}
                {/* X축 크로스헤어 (세로선) - 모든 차트 동기화 */}
                {(chartMode === 'basic' || activeTool === 'crosshair') && (
                  <Customized 
                    component={(props: any) => (
                      <VerticalCrosshair {...props} chartData={chartData} />
                    )}
                  />
                )}
                {/* RSI 라인 */}
                {indicatorSettings.rsiEnabled && (
                  <Line type="monotone" dataKey="rsi" stroke={colors.rsiColor || '#AB47BC'} strokeWidth={colors.rsiWidth || 1.5} dot={false} name="RSI" connectNulls isAnimationActive={false} />
                )}
                {/* RSI Signal 라인 */}
                {indicatorSettings.rsiSignalEnabled && (
                  <Line type="monotone" dataKey="rsiSignal" stroke={colors.rsiSignalColor || '#FF7043'} strokeWidth={colors.rsiSignalWidth || 1} dot={false} name="rsiSignal" connectNulls isAnimationActive={false}  />
                )}
              </LineChart>
            </ResponsiveContainer>
          </Box>
        )}
        
        {/* ===== ADX 개별 차트 (업비트 스타일) ===== */}
        {settings.adx && (
          <Box sx={{ 
            height: `${INDICATOR_HEIGHT + INDICATOR_XAXIS_EXTRA}px`, 
            position: 'relative',
            mt: 0,
          }}>
            {/* 좌측상단: 타이틀 */}
            <Typography variant="caption" sx={{
              ...indicatorTitleStyle,
            }}>
              ADX ({indicatorSettings.adxPeriod})
            </Typography>
            <ResponsiveContainer width="100%" height="100%">
              <LineChart 
                data={chartData}
                syncId="upbitChart"
                margin={shouldShowXAxis('adx') ? CHART_MARGIN_WITH_XAXIS : CHART_MARGIN}
                onMouseMove={(state) => handleChartMouseMove(state, 'adx')}
                onMouseLeave={handleChartMouseLeave}
                onClick={(state) => handleChartClick(state, 'adx')}
              >
                {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                <XAxis 
                  dataKey="date" 
                  hide={!shouldShowXAxis('adx')}
                  tick={{ fontSize: 9, fill: '#8b95a5' }}
                  interval={getXAxisInterval()}
                  tickFormatter={formatXAxisLabel}
                  axisLine={{ stroke: '#252d3a' }}
                  tickLine={{ stroke: '#252d3a' }}
                  padding={{ left: 0, right: 0 }}
                />
                <YAxis orientation="left" domain={[0, 100]} tick={<CustomYAxisTick />} width={60} ticks={[0, indicatorSettings.adxStrongThreshold, 50, 100]} stroke="#252d3a" />
                {settings.tooltipEnabled && <Tooltip formatter={(value: any, name: string) => [value?.toFixed(2) ?? '-', name]} />}
                {indicatorSettings.adxStrongEnabled && <ReferenceLine y={indicatorSettings.adxStrongThreshold} stroke={colors.adxStrongColor || "#00E676"} strokeDasharray="2 2" strokeWidth={colors.adxStrongWidth || 0.5} />}
                {indicatorSettings.adxWeakEnabled && <ReferenceLine y={indicatorSettings.adxWeakThreshold} stroke={colors.adxWeakColor || "#FFA726"} strokeDasharray="2 2" strokeWidth={colors.adxWeakWidth || 0.5} />}
                {/* X축 크로스헤어 (세로선) - 모든 차트 동기화 */}
                {(chartMode === 'basic' || activeTool === 'crosshair') && (
                  <Customized 
                    component={(props: any) => (
                      <VerticalCrosshair {...props} chartData={chartData} />
                    )}
                  />
                )}
                <Line type="monotone" dataKey="adx" stroke="#7C4DFF" strokeWidth={1.5} dot={false} name="ADX" connectNulls isAnimationActive={false} />
                <Line type="monotone" dataKey="plusDI" stroke="#00E676" strokeWidth={1} dot={false} name="+DI" connectNulls isAnimationActive={false} />
                <Line type="monotone" dataKey="minusDI" stroke="#FF5252" strokeWidth={1} dot={false} name="-DI" connectNulls isAnimationActive={false} />
              </LineChart>
            </ResponsiveContainer>
          </Box>
        )}
        
        {/* ===== CCI 개별 차트 (업비트 스타일) ===== */}
        {settings.cci && (
          <Box sx={{ 
            height: `${INDICATOR_HEIGHT + INDICATOR_XAXIS_EXTRA}px`, 
            position: 'relative',
            mt: 0,
          }}>
            {/* 좌측상단: 타이틀 */}
            <Typography variant="caption" sx={{
              ...indicatorTitleStyle,
            }}>
              CCI ({indicatorSettings.cciPeriod}, {indicatorSettings.cciSignalPeriod})
            </Typography>
            <ResponsiveContainer width="100%" height="100%">
              <LineChart 
                data={chartData}
                syncId="upbitChart"
                margin={shouldShowXAxis('cci') ? CHART_MARGIN_WITH_XAXIS : CHART_MARGIN}
                onMouseMove={(state) => handleChartMouseMove(state, 'cci')}
                onMouseLeave={handleChartMouseLeave}
                onClick={(state) => handleChartClick(state, 'cci')}
              >
                {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                <XAxis 
                  dataKey="date" 
                  hide={!shouldShowXAxis('cci')}
                  tick={{ fontSize: 9, fill: '#8b95a5' }}
                  interval={getXAxisInterval()}
                  tickFormatter={formatXAxisLabel}
                  axisLine={{ stroke: '#252d3a' }}
                  tickLine={{ stroke: '#252d3a' }}
                  padding={{ left: 0, right: 0 }}
                />
                <YAxis orientation="left" domain={[-200, 200]} tick={<CustomYAxisTick />} width={60} ticks={[indicatorSettings.cciOversold, 0, indicatorSettings.cciOverbought]} stroke="#252d3a" />
                {settings.tooltipEnabled && <Tooltip formatter={(value: any, name: string) => [value?.toFixed(2) ?? '-', name === 'cciSignal' ? 'Signal' : 'CCI']} />}
                {indicatorSettings.cciOverboughtEnabled && <ReferenceLine y={indicatorSettings.cciOverbought} stroke={colors.cciOverboughtColor || "#f44336"} strokeDasharray="2 2" strokeWidth={colors.cciOverboughtWidth || 0.5} />}
                {indicatorSettings.cciOversoldEnabled && <ReferenceLine y={indicatorSettings.cciOversold} stroke={colors.cciOversoldColor || "#4caf50"} strokeDasharray="2 2" strokeWidth={colors.cciOversoldWidth || 0.5} />}
                {indicatorSettings.cciMiddleEnabled && <ReferenceLine y={indicatorSettings.cciMiddle} stroke={colors.cciMiddleColor || "#666"} strokeDasharray="2 2" strokeWidth={colors.cciMiddleWidth || 0.5} />}
                {/* X축 크로스헤어 (세로선) - 모든 차트 동기화 */}
                {(chartMode === 'basic' || activeTool === 'crosshair') && (
                  <Customized 
                    component={(props: any) => (
                      <VerticalCrosshair {...props} chartData={chartData} />
                    )}
                  />
                )}
                {/* CCI 라인 */}
                {indicatorSettings.cciEnabled && (
                  <Line type="monotone" dataKey="cci" stroke={colors.cciColor || '#00BCD4'} strokeWidth={colors.cciWidth || 1.5} dot={false} name="CCI" connectNulls isAnimationActive={false} />
                )}
                {/* CCI Signal 라인 */}
                {indicatorSettings.cciSignalEnabled && (
                  <Line type="monotone" dataKey="cciSignal" stroke={colors.cciSignalColor || '#FF7043'} strokeWidth={colors.cciSignalWidth || 1} dot={false} name="cciSignal" connectNulls isAnimationActive={false}  />
                )}
              </LineChart>
            </ResponsiveContainer>
          </Box>
        )}
        
        {/* ===== TRIX 개별 차트 (업비트 스타일) ===== */}
        {settings.trix && (
          <Box sx={{ 
            height: `${INDICATOR_HEIGHT + INDICATOR_XAXIS_EXTRA}px`, 
            position: 'relative',
            mt: 0,
          }}>
            {/* 좌측상단: 타이틀 */}
            <Typography variant="caption" sx={{
              ...indicatorTitleStyle,
            }}>
              TRIX ({indicatorSettings.trixPeriod},{indicatorSettings.trixSignalPeriod})
            </Typography>
            <ResponsiveContainer width="100%" height="100%">
              <LineChart 
                data={chartData}
                syncId="upbitChart"
                margin={shouldShowXAxis('trix') ? CHART_MARGIN_WITH_XAXIS : CHART_MARGIN}
                onMouseMove={(state) => handleChartMouseMove(state, 'trix')}
                onMouseLeave={handleChartMouseLeave}
                onClick={(state) => handleChartClick(state, 'trix')}
              >
                {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} horizontal={true} vertical={true}  />}
                <XAxis 
                  dataKey="date" 
                  hide={!shouldShowXAxis('trix')}
                  tick={{ fontSize: 9, fill: '#8b95a5' }}
                  interval={getXAxisInterval()}
                  tickFormatter={formatXAxisLabel}
                  axisLine={{ stroke: '#252d3a' }}
                  tickLine={{ stroke: '#252d3a' }}
                  padding={{ left: 0, right: 0 }}
                />
                <YAxis orientation="left" tick={<CustomYAxisTick />} width={60} tickFormatter={(v) => v.toFixed(3)} tickCount={5} stroke="#252d3a" />
                {settings.tooltipEnabled && <Tooltip formatter={(value: any) => [value?.toFixed(4) ?? '-', 'TRIX']} />}
                {indicatorSettings.trixOverboughtEnabled && <ReferenceLine y={indicatorSettings.trixOverbought} stroke={colors.trixOverboughtColor || "#ef5350"} strokeDasharray="2 2" strokeWidth={colors.trixOverboughtWidth || 0.5} />}
                {indicatorSettings.trixMiddleEnabled && <ReferenceLine y={indicatorSettings.trixMiddle} stroke={colors.trixMiddleColor || "#666"} strokeDasharray="2 2" strokeWidth={colors.trixMiddleWidth || 0.5} />}
                {indicatorSettings.trixOversoldEnabled && <ReferenceLine y={indicatorSettings.trixOversold} stroke={colors.trixOversoldColor || "#26a69a"} strokeDasharray="2 2" strokeWidth={colors.trixOversoldWidth || 0.5} />}
                {/* X축 크로스헤어 (세로선) - 모든 차트 동기화 */}
                {(chartMode === 'basic' || activeTool === 'crosshair') && (
                  <Customized 
                    component={(props: any) => (
                      <VerticalCrosshair {...props} chartData={chartData} />
                    )}
                  />
                )}
                <Line type="monotone" dataKey="trix" stroke="#F50057" strokeWidth={1.5} dot={false} name="TRIX" connectNulls isAnimationActive={false} />
                <Line type="monotone" dataKey="trixSignal" stroke="#18FFFF" strokeWidth={1.5} dot={false} name="Signal" connectNulls isAnimationActive={false}  />
              </LineChart>
            </ResponsiveContainer>
          </Box>
        )}
        
        {/* === 기존 통합 차트 영역 제거됨 - 아래는 비활성화된 레거시 코드 === */}
        {false && (settings.macdLine || settings.macdHistogram || settings.stochasticSlow || 
          settings.adx || settings.cci || settings.rsi || settings.trix || settings.volumeIndicator) && (
        <Box sx={{ height: isMobile ? '320px' : '380px', position: 'relative' }}>
          
          <ResponsiveContainer width="100%" height="100%">
            <ComposedChart 
              data={chartData}
              margin={isMobile ? 
                { top: 5, right: 15, left: 5, bottom: 5 } : 
                { top: 5, right: 65, left: 55, bottom: 5 }
              }
              onMouseMove={(state: any) => {
                if (state && state.activePayload && state.activePayload.length > 0) {
                  const data = state.activePayload[0].payload;
                  const index = state.activeTooltipIndex;
                  
                  if (index !== undefined && data.date) {
                    setGlobalHoverIndex(index);
                    setGlobalHoverLabel(data.date);
                    setGlobalHoverDate(data.date);
                  }
                }
              }}
              onMouseLeave={handleChartMouseLeave}
            >
              {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} />}
              <XAxis 
                dataKey="date"
                type="category"
                tick={{ fontSize: 9, fill: '#8b95a5' }}
                interval={getXAxisInterval()}
                stroke="#252d3a"
                tickFormatter={formatXAxisLabel}
                allowDataOverflow={false}
                padding={{ left: 0, right: 0 }}
              />
              
              {/* 왼쪽1: Oscillator Y축 (0-100) - Stochastic, ADX, RSI - 항상 표시 */}
              <YAxis 
                yAxisId="oscillator"
                orientation="left"
                domain={[0, 100]}
                tick={{ fontSize: 9, fill: '#FF9500' }}
                stroke="#FF9500"
                ticks={[0, 50, 100]}
                tickCount={3}
                width={25}
              />
              
              {/* 왼쪽2: CCI Y축 (-200~200) - 항상 공간 유지 */}
              <YAxis 
                yAxisId="cci"
                orientation="left"
                domain={[-200, 200]}
                tick={settings.cci ? { fontSize: 9, fill: '#ff5722' } : false}
                stroke={settings.cci ? '#ff5722' : 'transparent'}
                ticks={[-200, 0, 200]}
                tickCount={3}
                tickFormatter={(v) => Math.round(v).toString()}
                width={28}
              />
              
              {/* 오른쪽1: MACD Y축 - 항상 공간 유지 */}
              <YAxis 
                yAxisId="macd"
                orientation="right"
                tick={(settings.macdLine || settings.macdHistogram) ? { fontSize: 9, fill: colors.macdLine } : false}
                stroke={(settings.macdLine || settings.macdHistogram) ? colors.macdLine : 'transparent'}
                domain={getMacdYAxisDomain()}
                tickFormatter={formatCompactNumber}
                width={30}
              />
              
              {/* 오른쪽2: TRIX/Volume Y축 - 항상 공간 유지 */}
              <YAxis 
                yAxisId="trix"
                orientation="right"
                tick={settings.trix ? { fontSize: 9, fill: '#E91E63' } : false}
                stroke={settings.trix ? '#E91E63' : 'transparent'}
                tickCount={3}
                tickFormatter={(v) => typeof v === 'number' ? v.toFixed(2) : v}
                width={30}
              />
              
              {/* 거래량 Y축 (숨김 처리) */}
              <YAxis 
                yAxisId="volume"
                orientation="right"
                hide={true}
              />
              
              <Tooltip 
                formatter={(value: any, name: string) => {
                  if (value === null || value === undefined) return ['-', name];
                  return [typeof value === 'number' ? value.toFixed(2) : value, name];
                }}
                labelFormatter={(label) => `날짜: ${label}`}
                contentStyle={{
                  backgroundColor: 'rgba(255, 255, 255, 0.95)',
                  border: '1px solid #ccc',
                  borderRadius: '4px'
                }}
                cursor={false}
              />
              <Legend 
                wrapperStyle={isMobile ? { fontSize: '9px' } : { fontSize: '11px' }}
                verticalAlign="top"
                height={isMobile ? 40 : 36}
              />
              
              {/* X축 크로스헤어 (세로선) */}
              {(chartMode === 'basic' || activeTool === 'crosshair') && (
                <Customized 
                  component={(props: any) => (
                    <VerticalCrosshair {...props} chartData={chartData} />
                  )}
                />
              )}
              
              {/* 거래량 Bar (배경에 표시) */}
              {settings.volumeIndicator && (
                <Bar 
                  yAxisId="volume"
                  dataKey="volume" 
                  name="거래량"
                  radius={[1, 1, 0, 0]}
                  activeBar={false}
                  opacity={0.5}
                >
                  {chartData.map((entry, index) => (
                    <Cell key={`vol-${index}`} fill={entry.volumeColor} opacity={0.5} />
                  ))}
                </Bar>
              )}
              
              {/* MACD 히스토그램 */}
              {settings.macdHistogram && (
                <Bar 
                  yAxisId="macd"
                  dataKey="macdHistogram" 
                  name="Histogram"
                  radius={[1, 1, 0, 0]}
                  activeBar={false}
                >
                  {chartData.map((entry, index) => (
                    <Cell key={`cell-${index}`} fill={entry.histogramColor} />
                  ))}
                </Bar>
              )}
              
              {/* MACD 기준선 */}
              {(settings.macdLine || settings.macdHistogram) && (
                <ReferenceLine yAxisId="macd" y={0} stroke="#666" strokeDasharray="2 2" strokeWidth={1} />
              )}
              
              {/* MACD 라인 */}
              {settings.macdLine && (
                <>
                  <Line
                    yAxisId="macd"
                    type="monotone"
                    dataKey="macdLine"
                    stroke={colors.macdLine}
                    strokeWidth={1.5}
                    dot={false}
                    name="MACD"
                    connectNulls
                    isAnimationActive={false}
                  />
                  <Line
                    yAxisId="macd"
                    type="monotone"
                    dataKey="signalLine"
                    stroke={colors.signalLine}
                    strokeWidth={1.5}
                    dot={false}
                    name="Signal"
                    connectNulls
                    isAnimationActive={false}
                  />
                </>
              )}
              
              {/* Stochastic 기준선 */}
              {settings.stochasticSlow && (
                <>
                  <ReferenceLine yAxisId="oscillator" y={80} stroke="#f44336"  strokeWidth={1} strokeOpacity={0.5} />
                  <ReferenceLine yAxisId="oscillator" y={20} stroke="#4caf50"  strokeWidth={1} strokeOpacity={0.5} />
                </>
              )}
              
              {/* Stochastic 라인 */}
              {settings.stochasticSlow && (
                <>
                  <Line
                    yAxisId="oscillator"
                    type="monotone"
                    dataKey="stochasticK"
                    stroke="#FF9500"
                    strokeWidth={1.5}
                    dot={false}
                    name="Stoch %K"
                    connectNulls
                    isAnimationActive={false}
                  />
                  <Line
                    yAxisId="oscillator"
                    type="monotone"
                    dataKey="stochasticD"
                    stroke="#FFEB3B"
                    strokeWidth={1.5}
                    dot={false}
                    name="Stoch %D"
                    connectNulls
                    isAnimationActive={false}
                  />
                </>
              )}
              
              {/* ADX 기준선 */}
              {settings.adx && (
                <ReferenceLine yAxisId="oscillator" y={25} stroke="#9C27B0" strokeDasharray="5 5" strokeWidth={1} strokeOpacity={0.4} />
              )}
              
              {/* ADX 라인 */}
              {settings.adx && (
                <>
                  <Line
                    yAxisId="oscillator"
                    type="monotone"
                    dataKey="adx"
                    stroke="#9C27B0"
                    strokeWidth={1.5}
                    dot={false}
                    name="ADX"
                    connectNulls
                    isAnimationActive={false}
                  />
                  <Line
                    yAxisId="oscillator"
                    type="monotone"
                    dataKey="plusDI"
                    stroke="#4CAF50"
                    strokeWidth={1}
                    dot={false}
                    name="+DI"
                    connectNulls
                    isAnimationActive={false}
                  />
                  <Line
                    yAxisId="oscillator"
                    type="monotone"
                    dataKey="minusDI"
                    stroke="#f44336"
                    strokeWidth={1}
                    dot={false}
                    name="-DI"
                    connectNulls
                    isAnimationActive={false}
                  />
                </>
              )}
              
              {/* CCI 기준선 */}
              {settings.cci && (
                <>
                  <ReferenceLine yAxisId="cci" y={100} stroke="#f44336"  strokeWidth={1} strokeOpacity={0.4} />
                  <ReferenceLine yAxisId="cci" y={-100} stroke="#4caf50"  strokeWidth={1} strokeOpacity={0.4} />
                  <ReferenceLine yAxisId="cci" y={0} stroke="#999" strokeDasharray="2 2" strokeWidth={1} />
                </>
              )}
              
              {/* CCI 라인 */}
              {settings.cci && (
                <Line
                  yAxisId="cci"
                  type="monotone"
                  dataKey="cci"
                  stroke="#ff5722"
                  strokeWidth={1.5}
                  dot={false}
                  name="CCI"
                  connectNulls
                  isAnimationActive={false}
                />
              )}
              
              {/* RSI 기준선 */}
              {settings.rsi && (
                <>
                  <ReferenceLine yAxisId="oscillator" y={70} stroke="#ff9800"  strokeWidth={1} strokeOpacity={0.4} />
                  <ReferenceLine yAxisId="oscillator" y={30} stroke="#2196F3"  strokeWidth={1} strokeOpacity={0.4} />
                </>
              )}
              
              {/* RSI 라인 */}
              {settings.rsi && (
                <Line
                  yAxisId="oscillator"
                  type="monotone"
                  dataKey="rsi"
                  stroke="#00BCD4"
                  strokeWidth={1.5}
                  dot={false}
                  name="RSI"
                  connectNulls
                  isAnimationActive={false}
                />
              )}
              
              {/* TRIX 기준선 */}
              {settings.trix && (
                <>
                  <ReferenceLine yAxisId="trix" y={0} stroke="#999" strokeDasharray="2 2" strokeWidth={1} />
                </>
              )}
              
              {/* TRIX 라인 */}
              {settings.trix && (
                <Line
                  yAxisId="trix"
                  type="monotone"
                  dataKey="trix"
                  stroke="#E91E63"
                  strokeWidth={1.5}
                  dot={false}
                  name="TRIX"
                  connectNulls
                  isAnimationActive={false}
                />
              )}
              
              {/* TRIX Signal 라인 */}
              {settings.trix && (
                <Line
                  yAxisId="trix"
                  type="monotone"
                  dataKey="trixSignal"
                  stroke="#9C27B0"
                  strokeWidth={1}
                  dot={false}
                  name="TRIX Signal"
                  connectNulls
                  isAnimationActive={false}
                />
              )}
            </ComposedChart>
          </ResponsiveContainer>
        </Box>
        )}
      </Box>
      )}

      {/* [기존 개별 보조지표 차트들 - 비활성화] */}
      {/* MACD 라인 차트 (독립적인 Box로 분리) - 전체화면 시 숨김 */}
      {false && !isFullscreen && settings.macdLine && (
      <Box sx={{ order: getChartOrder('macdLine') }}>
        {/* 여백 영역 */}
        <Box sx={{ height: '8px', bgcolor: 'action.hover', borderTop: 1, borderBottom: 1, borderColor: 'divider' }} />
        
        {/* MACD 라인 차트 */}
        <Box sx={{ width: '100%', height: isMobile ? '220px' : '230px', position: 'relative' }}>
          {/* 범주 표시 - 차트 내부 우측 상단 */}
          <Box sx={{ 
            position: 'absolute', 
            top: 8, 
            right: isMobile ? 10 : 35, 
            zIndex: 10,
            display: 'flex',
            alignItems: 'center',
            bgcolor: 'rgba(255, 255, 255, 0.85)',
            p: 0.5,
            borderRadius: 1,
            boxShadow: '0 1px 3px rgba(0,0,0,0.1)'
          }}>
            <Chip 
              label={getCategoryDisplay(getChartCategory('macdLine')).label + ' 라인'}
              size="small"
              sx={{ 
                bgcolor: getCategoryDisplay(getChartCategory('macdLine')).color,
                color: 'white',
                fontWeight: 'bold',
                fontSize: '0.65rem',
                height: '18px',
              }}
            />
            {/* <Typography variant="caption" sx={{ ml: 0.5, color: 'text.primary', fontSize: '0.7rem', fontWeight: 500 }}>
              MACD 라인 (크로스 신호 포함)
            </Typography> */}
          </Box>
          
          <ResponsiveContainer width="100%" height="100%">
            <ComposedChart 
              data={chartData}
              margin={isMobile ? 
                { top: 5, right: 15, left: 5, bottom: 5 } : 
                { top: 5, right: 35, left: 25, bottom: 5 }
              }
              onMouseMove={(state: any) => {
                if (state && state.activePayload && state.activePayload.length > 0) {
                  const data = state.activePayload[0].payload;
                  const index = state.activeTooltipIndex;
                  
                  // 전역 마우스 위치 업데이트
                  if (index !== undefined && data.date) {
                    setGlobalHoverIndex(index);
                    setGlobalHoverLabel(data.date);
                    setGlobalHoverDate(data.date);
                  }
                  
                  setMacdYValue(data.macdLine);
                  if (state.chartY !== undefined) {
                    setMacdYPosition(state.chartY);
                  }
                }
              }}
              onMouseLeave={handleChartMouseLeave}
            >
              {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} />}
              <XAxis 
                dataKey="date"
                type="category"
                tick={{ fontSize: 9, fill: '#8b95a5' }}
                interval={getXAxisInterval()}
                stroke="#252d3a"
                tickFormatter={formatXAxisLabel}
                allowDataOverflow={false}
                padding={{ left: 0, right: 0 }}
              />
              
              {/* MACD용 Y축 (왼쪽) - 데이터 기반 스케일링 */}
              <YAxis 
                orientation="left"
                tick={{ fontSize: 11, fill: '#8b95a5' }}
                stroke="#252d3a"
                domain={getMacdYAxisDomain()}
                tickFormatter={(value) => {
                  const absValue = Math.abs(value);
                  if (absValue >= 1000000) return `${(value / 1000000).toFixed(1)}M`;
                  if (absValue >= 1000) return `${(value / 1000).toFixed(1)}K`;
                  return value.toFixed(0);
                }}
              />
              
              <Tooltip 
                content={<CustomTooltip />}
                cursor={false}
              />
              <Legend 
                wrapperStyle={isMobile ? { fontSize: '10px', lineHeight: '1.4' } : {}}
                iconSize={isMobile ? 8 : 14}
                verticalAlign="top"
                height={isMobile ? 60 : 36}
              />
              <ReferenceLine y={0} stroke="#666" strokeDasharray="2 2" />
              
              {/* X축 크로스헤어 (세로선) */}
              {(chartMode === 'basic' || activeTool === 'crosshair') && (
                <Customized 
                  component={(props: any) => (
                    <VerticalCrosshair {...props} chartData={chartData} />
                  )}
                />
              )}
              
              {/* MACD 및 Signal 라인 */}
              <Line
                type="monotone"
                dataKey="macdLine"
                stroke={colors.macdLine}
                strokeWidth={1.5}
                dot={false}
                name="MACD"
                connectNulls
                isAnimationActive={false}
              />
              <Line
                type="monotone"
                dataKey="signalLine"
                stroke={colors.signalLine}
                strokeWidth={1.5}
                dot={false}
                name="Signal"
                connectNulls
                isAnimationActive={false}
              />
              
              {/* 기본 전략 마커들 제거됨 - 전략 규칙 관리(StrategyRule)만 사용 */}
              {/* 골든/데드 크로스, 제로라인 돌파 마커 삭제 */}
              <Scatter
                dataKey="_removed_placeholder"
                fill="transparent"
                shape={() => <g />}
                name={isMobile ? "하향돌파" : "제로라인 하향돌파"}
              />
            </ComposedChart>
          </ResponsiveContainer>
          
          {/* Y축 값 표시 Chip (MACD) */}
          {macdYValue !== null && macdYPosition !== null && (
            <Chip
              label={macdYValue?.toFixed(2) ?? ''}
              size="small"
              sx={{
                position: 'absolute',
                left: isMobile ? 5 : 10,
                top: macdYPosition,
                transform: 'translateY(-50%)',
                bgcolor: '#1976d2',
                color: 'white',
                fontWeight: 'bold',
                fontSize: '0.75rem',
                height: '24px',
                zIndex: 1000,
                pointerEvents: 'none',
              }}
            />
          )}
        </Box>
      </Box>
      )}

      {/* MACD 히스토그램 - 비활성화 (통합 차트로 이동) */}
      {false && !isFullscreen && settings.macdHistogram && (
      <Box sx={{ order: getChartOrder('macdHistogram') }}>
        {/* 여백 영역 */}
        <Box sx={{ height: '8px', bgcolor: 'action.hover', borderTop: 1, borderBottom: 1, borderColor: 'divider' }} />
        
        <Box sx={{ height: isMobile ? '220px' : '230px', position: 'relative' }}>
          {/* 범주 표시 - 차트 내부 우측 상단 */}
          <Box sx={{ 
            position: 'absolute', 
            top: 8, 
            right: isMobile ? 10 : 35, 
            zIndex: 10,
            display: 'flex',
            alignItems: 'center',
            bgcolor: 'rgba(255, 255, 255, 0.85)',
            p: 0.5,
            borderRadius: 1,
            boxShadow: '0 1px 3px rgba(0,0,0,0.1)'
          }}>
            <Chip 
              label={getCategoryDisplay(getChartCategory('macdHistogram')).label + ' 히스토그램'}
              size="small"
              sx={{ 
                bgcolor: getCategoryDisplay(getChartCategory('macdHistogram')).color,
                color: 'white',
                fontWeight: 'bold',
                fontSize: '0.65rem',
                height: '18px',
              }}
            />
            {/* <Typography variant="caption" sx={{ ml: 0.5, color: 'text.primary', fontSize: '0.7rem', fontWeight: 500 }}>
              MACD 히스토그램
            </Typography> */}
          </Box>
          
          <ResponsiveContainer width="100%" height="100%">
            <BarChart 
              data={chartData}
              margin={isMobile ? 
                { top: 5, right: 15, left: 5, bottom: 5 } : 
                { top: 5, right: 35, left: 25, bottom: 5 }
              }
              onMouseMove={(state: any) => {
                if (state && state.activePayload && state.activePayload.length > 0) {
                  const data = state.activePayload[0].payload;
                  const index = state.activeTooltipIndex;
                  
                  if (index !== undefined && data.date) {
                    setGlobalHoverIndex(index);
                    setGlobalHoverLabel(data.date);
                    setGlobalHoverDate(data.date);
                  }
                  
                  setHistogramYValue(data.macdHistogram);
                  if (state.chartY !== undefined) {
                    setHistogramYPosition(state.chartY);
                  }
                }
              }}
              onMouseLeave={handleChartMouseLeave}
            >
              {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} />}
              <XAxis 
                dataKey="date"
                type="category"
                tick={{ fontSize: 9, fill: '#8b95a5' }}
                interval={getXAxisInterval()}
                stroke="#252d3a"
                tickFormatter={formatXAxisLabel}
                allowDataOverflow={false}
                padding={{ left: 0, right: 0 }}
              />
              <YAxis 
                orientation="left"
                tick={{ fontSize: 11, fill: '#8b95a5' }}
                stroke="#252d3a"
                domain={getHistogramYAxisDomain()}
                tickFormatter={(value) => {
                  const absValue = Math.abs(value);
                  if (absValue >= 1000000) return `${(value / 1000000).toFixed(1)}M`;
                  if (absValue >= 1000) return `${(value / 1000).toFixed(1)}K`;
                  return value.toFixed(0);
                }}
              />
              <Tooltip 
                content={<CustomTooltip />}
                cursor={false}
              />
              <Legend 
                wrapperStyle={isMobile ? { fontSize: '10px' } : {}}
                verticalAlign="top"
                height={isMobile ? 36 : 36}
              />
              <ReferenceLine y={0} stroke="#666" strokeWidth={2} />
              
              {/* X축 크로스헤어 (세로선) */}
              {(chartMode === 'basic' || activeTool === 'crosshair') && (
                <Customized 
                  component={(props: any) => (
                    <VerticalCrosshair {...props} chartData={chartData} />
                  )}
                />
              )}
              
              <Bar 
                dataKey="macdHistogram" 
                name="Histogram"
                radius={[2, 2, 0, 0]}
                activeBar={false}
              >
                {chartData.map((entry, index) => (
                  <Cell 
                    key={`cell-${index}`} 
                    fill={entry.histogramColor}
                  />
                ))}
              </Bar>
            </BarChart>
          </ResponsiveContainer>
          
          {/* Y축 값 표시 Chip (히스토그램) */}
          {histogramYValue !== null && histogramYPosition !== null && (
            <Chip
              label={histogramYValue?.toFixed(2) ?? ''}
              size="small"
              sx={{
                position: 'absolute',
                left: isMobile ? 5 : 10,
                top: histogramYPosition,
                transform: 'translateY(-50%)',
                bgcolor: '#1976d2',
                color: 'white',
                fontWeight: 'bold',
                fontSize: '0.75rem',
                height: '24px',
                zIndex: 1000,
                pointerEvents: 'none',
              }}
            />
          )}
        </Box>
      </Box>
      )}

      {/* MACD Custom (이동평균선 전략) - 제거됨 */}
      {false && !isFullscreen && (
      <Box sx={{ order: getChartOrder('macdCustom') }}>
        {/* 여백 영역 */}
        <Box sx={{ height: '8px', bgcolor: 'action.hover', borderTop: 1, borderBottom: 1, borderColor: 'divider' }} />
        
        <Box sx={{ height: isMobile ? '220px' : '230px', position: 'relative' }}>
          {/* 범주 표시 - 차트 내부 우측 상단 */}
          <Box sx={{ 
            position: 'absolute', 
            top: 8, 
            right: isMobile ? 10 : 35, 
            zIndex: 10,
            display: 'flex',
            alignItems: 'center',
            bgcolor: 'rgba(255, 255, 255, 0.85)',
            p: 0.5,
            borderRadius: 1,
            boxShadow: '0 1px 3px rgba(0,0,0,0.1)'
          }}>
            <Chip 
              label={getCategoryDisplay(getChartCategory('macdCustom')).label + ' Custom'}
              size="small"
              sx={{ 
                bgcolor: getCategoryDisplay(getChartCategory('macdCustom')).color,
                color: 'white',
                fontWeight: 'bold',
                fontSize: '0.65rem',
                height: '18px',
              }}
            />
          </Box>
          
          <ResponsiveContainer width="100%" height="100%">
            <ComposedChart 
              data={chartData}
              margin={isMobile ? 
                { top: 5, right: 15, left: 5, bottom: 5 } : 
                { top: 5, right: 35, left: 25, bottom: 5 }
              }
              onMouseMove={(state: any) => {
                if (state && state.activePayload && state.activePayload.length > 0) {
                  const data = state.activePayload[0].payload;
                  const index = state.activeTooltipIndex;
                  
                  if (index !== undefined && data.date) {
                    setGlobalHoverIndex(index);
                    setGlobalHoverLabel(data.date);
                    setGlobalHoverDate(data.date);
                  }
                }
              }}
              onMouseLeave={handleChartMouseLeave}
            >
              {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} />}
              <XAxis 
                dataKey="date"
                type="category"
                tick={{ fontSize: 9, fill: '#8b95a5' }}
                interval={getXAxisInterval()}
                stroke="#252d3a"
                tickFormatter={formatXAxisLabel}
                allowDataOverflow={false}
                padding={{ left: 0, right: 0 }}
              />
              <YAxis 
                orientation="left"
                tick={{ fontSize: 11, fill: '#8b95a5' }}
                stroke="#252d3a"
                domain={(() => {
                  // MA 데이터에서 min/max 계산
                  const maValues: number[] = [];
                  chartData.forEach(d => {
                    if (d.ma12 !== null && d.ma12 !== undefined && !isNaN(d.ma12)) maValues.push(d.ma12);
                    if (d.ma26 !== null && d.ma26 !== undefined && !isNaN(d.ma26)) maValues.push(d.ma26);
                    if (d.ma60 !== null && d.ma60 !== undefined && !isNaN(d.ma60)) maValues.push(d.ma60);
                    if (d.close !== null && d.close !== undefined && !isNaN(d.close)) maValues.push(d.close);
                  });
                  if (maValues.length === 0) return ['auto', 'auto'];
                  const min = Math.min(...maValues);
                  const max = Math.max(...maValues);
                  const padding = (max - min) * 0.05;
                  return [min - padding, max + padding];
                })()}
                tickFormatter={formatCompactNumber}
              />
              <Tooltip 
                content={<CustomTooltip />}
                cursor={false}
              />
              <Legend 
                wrapperStyle={isMobile ? { fontSize: '10px' } : {}}
                verticalAlign="top"
                height={isMobile ? 36 : 36}
              />
              
              {/* X축 크로스헤어 (세로선) */}
              {(chartMode === 'basic' || activeTool === 'crosshair') && (
                <Customized 
                  component={(props: any) => (
                    <VerticalCrosshair {...props} chartData={chartData} />
                  )}
                />
              )}
              
              {/* 12일 이동평균선 */}
              <Line 
                type="monotone"
                dataKey="ma12"
                stroke={colors.ma12}
                strokeWidth={2}
                dot={false}
                activeDot={{ r: 2 }}
                name="MA12"
                connectNulls
              />
              
              {/* 26일 이동평균선 */}
              <Line 
                type="monotone"
                dataKey="ma26"
                stroke={colors.ma26}
                strokeWidth={2}
                dot={false}
                activeDot={{ r: 2 }}
                name="MA26"
                connectNulls
              />
              
              {/* 60일 이동평균선 */}
              <Line 
                type="monotone"
                dataKey="ma60"
                stroke={colors.ma60}
                strokeWidth={2}
                dot={false}
                activeDot={{ r: 2 }}
                name="MA60"
                connectNulls
              />
              
              {/* 현재가 라인 */}
              <Line 
                type="monotone"
                dataKey="close"
                stroke="#666"
                strokeWidth={1}
                dot={false}
                name="현재가"
                connectNulls
              />
              
              {/* 매수 신호 마커 (초록색 상향 삼각형) */}
              <Scatter
                dataKey="customBuyMarker"
                fill="#4caf50"
                shape="triangle"
                name={isMobile ? "매수" : "매수 신호"}
              />
              
              {/* 매도 신호 마커 (빨간색 하향 삼각형) */}
              <Scatter
                dataKey="customSellMarker"
                fill="#f44336"
                shape={(props: any) => {
                  const { cx, cy } = props;
                  // 하향 삼각형 (역삼각형)
                  return (
                    <g>
                      <polygon
                        points={`${cx},${cy + 6} ${cx - 6},${cy - 6} ${cx + 6},${cy - 6}`}
                        fill="#f44336"
                        stroke="#fff"
                        strokeWidth={1}
                      />
                    </g>
                  );
                }}
                name={isMobile ? "매도" : "매도 신호"}
              />
              
            </ComposedChart>
          </ResponsiveContainer>
        </Box>
      </Box>
      )}

      {/* Stochastic Slow 차트 - 비활성화 (통합 차트로 이동) */}
      {false && !isFullscreen && settings.stochasticSlow && (
      <Box sx={{ order: getChartOrder('stochasticSlow') }}>
        {/* 여백 영역 */}
        <Box sx={{ height: '8px', bgcolor: 'action.hover', borderTop: 1, borderBottom: 1, borderColor: 'divider' }} />
        
        <Box sx={{ height: isMobile ? '220px' : '230px', position: 'relative' }}>
          {/* 범주 표시 - 차트 내부 우측 상단 */}
          <Box sx={{ 
            position: 'absolute', 
            top: 8, 
            right: isMobile ? 10 : 35, 
            zIndex: 10,
            display: 'flex',
            alignItems: 'center',
            gap: 0.5,
            bgcolor: 'rgba(255, 255, 255, 0.85)',
            p: 0.5,
            borderRadius: 1,
            boxShadow: '0 1px 3px rgba(0,0,0,0.1)'
          }}>
            <Chip 
              label={`Stochastic Slow (${indicatorSettings.stochasticKPeriod}, ${indicatorSettings.stochasticDPeriod}, ${indicatorSettings.stochasticSlowing})`}
              size="small"
              sx={{ 
                bgcolor: getCategoryDisplay(getChartCategory('stochasticSlow')).color,
                color: 'white',
                fontWeight: 'bold',
                fontSize: '0.65rem',
                height: '18px',
              }}
            />
            {/* 추세 상태 및 신호 개수 표시 */}
            {defaultStrategy === 'TREND_STOCHASTIC' && (
              <>
                <Chip 
                  label={trendStrength >= 0 ? '정배열' : '역배열'}
                  size="small"
                  sx={{ 
                    bgcolor: trendStrength >= 0 ? '#4caf50' : '#f44336',
                    color: 'white',
                    fontWeight: 'bold',
                    fontSize: '0.6rem',
                    height: '18px',
                  }}
                />
                {/* 기본 전략 신호 Chip 제거됨 - 전략 규칙 관리만 사용 */}
              </>
            )}
          </Box>
          
          <ResponsiveContainer width="100%" height="100%">
            <LineChart 
              data={chartData}
              margin={isMobile ? 
                { top: 5, right: 15, left: 5, bottom: 5 } : 
                { top: 5, right: 35, left: 25, bottom: 5 }
              }
              onMouseMove={(state: any) => {
                if (state && state.activePayload && state.activePayload.length > 0) {
                  const data = state.activePayload[0].payload;
                  const index = state.activeTooltipIndex;
                  
                  if (index !== undefined && data.date) {
                    setGlobalHoverIndex(index);
                    setGlobalHoverLabel(data.date);
                    setGlobalHoverDate(data.date);
                  }
                  
                  setStochYValue(data.stochasticK);
                  if (state.chartY !== undefined) {
                    setStochYPosition(state.chartY);
                  }
                }
              }}
              onMouseLeave={handleChartMouseLeave}
            >
              {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} />}
              <XAxis 
                dataKey="date"
                type="category"
                tick={{ fontSize: 9, fill: '#8b95a5' }}
                interval={getXAxisInterval()}
                stroke="#252d3a"
                tickFormatter={formatXAxisLabel}
                allowDataOverflow={false}
                padding={{ left: 0, right: 0 }}
              />
              <YAxis 
                orientation="left"
                tick={{ fontSize: 11, fill: '#8b95a5' }}
                stroke="#252d3a"
                domain={[0, 100]}
                ticks={[0, 20, 50, 80, 100]}
                tickCount={5}
                allowDecimals={false}
                interval={0}
              />
              <Tooltip 
                formatter={(value: any, name: string) => {
                  if (value === null || value === undefined) return ['-', name];
                  return [value.toFixed(2), name];
                }}
                labelFormatter={(label) => `날짜: ${label}`}
                contentStyle={{
                  backgroundColor: 'rgba(255, 255, 255, 0.95)',
                  border: '1px solid #ccc',
                  borderRadius: '4px'
                }}
                cursor={false}
              />
              <Legend 
                wrapperStyle={isMobile ? { fontSize: '10px' } : {}}
                verticalAlign="top"
                height={isMobile ? 36 : 36}
              />
              
              {/* X축 크로스헤어 (세로선) */}
              {(chartMode === 'basic' || activeTool === 'crosshair') && (
                <Customized 
                  component={(props: any) => (
                    <VerticalCrosshair {...props} chartData={chartData} />
                  )}
                />
              )}
              
              {/* 과매수/과매도 기준선 */}
              <ReferenceLine y={indicatorSettings.stochasticOverbought} stroke={colors.stochasticOverboughtColor}  strokeWidth={1} label={{ value: `과매수(${indicatorSettings.stochasticOverbought})`, position: 'right', fontSize: 10 }} />
              <ReferenceLine y={indicatorSettings.stochasticOversold} stroke={colors.stochasticOversoldColor}  strokeWidth={1} label={{ value: `과매도(${indicatorSettings.stochasticOversold})`, position: 'right', fontSize: 10 }} />
              <ReferenceLine y={indicatorSettings.stochasticNeutral} stroke={colors.stochasticNeutralColor} strokeDasharray="2 2" strokeWidth={1} />
              
              {/* %K 라인 */}
              <Line
                type="monotone"
                dataKey="stochasticK"
                stroke={colors.stochasticK}
                strokeWidth={1.5}
                dot={false}
                name="%K"
                connectNulls
                isAnimationActive={false}
              />
              
              {/* %D 라인 */}
              <Line
                type="monotone"
                dataKey="stochasticD"
                stroke={colors.stochasticD}
                strokeWidth={1.5}
                dot={false}
                name="%D"
                connectNulls
                isAnimationActive={false}
              />
              
              {/* 기본 전략 신호 마커 제거됨 - 전략 규칙 관리만 사용 */}
            </LineChart>
          </ResponsiveContainer>
          
          {/* Y축 값 표시 Chip (Stochastic) */}
          {stochYValue !== null && stochYPosition !== null && (
            <Chip
              label={stochYValue?.toFixed(2) ?? ''}
              size="small"
              sx={{
                position: 'absolute',
                left: isMobile ? 5 : 10,
                top: stochYPosition,
                transform: 'translateY(-50%)',
                bgcolor: '#1976d2',
                color: 'white',
                fontWeight: 'bold',
                fontSize: '0.75rem',
                height: '24px',
                zIndex: 1000,
                pointerEvents: 'none',
              }}
            />
          )}
        </Box>
      </Box>
      )}

      {/* ADX 차트 - 비활성화 (통합 차트로 이동) */}
      {false && !isFullscreen && settings.adx && (
      <Box sx={{ order: getChartOrder('adx') }}>
        {/* 여백 영역 */}
        <Box sx={{ height: '8px', bgcolor: 'action.hover', borderTop: 1, borderBottom: 1, borderColor: 'divider' }} />
        
        <Box sx={{ height: isMobile ? '220px' : '230px', position: 'relative' }}>
          {/* 범주 표시 - 차트 내부 우측 상단 */}
          <Box sx={{ 
            position: 'absolute', 
            top: 8, 
            right: isMobile ? 10 : 35, 
            zIndex: 10,
            display: 'flex',
            alignItems: 'center',
            bgcolor: 'rgba(255, 255, 255, 0.85)',
            p: 0.5,
            borderRadius: 1,
            boxShadow: '0 1px 3px rgba(0,0,0,0.1)'
          }}>
            <Chip 
              // label={getCategoryDisplay(getChartCategory('adx')).label}
              label="ADX (14) - 추세 강도"
              size="small"
              sx={{ 
                bgcolor: getCategoryDisplay(getChartCategory('adx')).color,
                color: 'white',
                fontWeight: 'bold',
                fontSize: '0.65rem',
                height: '18px',
              }}
            />
            {/* <Typography variant="caption" sx={{ ml: 0.5, color: 'text.primary', fontSize: '0.7rem', fontWeight: 500 }}>
              ADX (14) - 추세 강도
            </Typography> */}
          </Box>
          
          <ResponsiveContainer width="100%" height="100%">
            <LineChart 
              data={chartData}
              margin={isMobile ? 
                { top: 5, right: 15, left: 5, bottom: 5 } : 
                { top: 5, right: 35, left: 25, bottom: 5 }
              }
              onMouseMove={(state: any) => {
                if (state && state.activePayload && state.activePayload.length > 0) {
                  const data = state.activePayload[0].payload;
                  const index = state.activeTooltipIndex;
                  
                  if (index !== undefined && data.date) {
                    setGlobalHoverIndex(index);
                    setGlobalHoverLabel(data.date);
                    setGlobalHoverDate(data.date);
                  }
                  
                  setAdxYValue(data.adx);
                  if (state.chartY !== undefined) {
                    setAdxYPosition(state.chartY);
                  }
                }
              }}
              onMouseLeave={handleChartMouseLeave}
            >
              {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} />}
              <XAxis 
                dataKey="date"
                type="category"
                tick={{ fontSize: 9, fill: '#8b95a5' }}
                interval={getXAxisInterval()}
                stroke="#252d3a"
                tickFormatter={formatXAxisLabel}
                allowDataOverflow={false}
                padding={{ left: 0, right: 0 }}
              />
              <YAxis 
                orientation="left"
                domain={[0, 100]}
                tick={{ fontSize: 11, fill: '#8b95a5' }}
                stroke="#252d3a"
              />
              <Tooltip 
                formatter={(value: any) => {
                  if (value === null || value === undefined) return ['N/A', ''];
                  return [Number(value).toFixed(2), ''];
                }}
                cursor={false}
              />
              <Legend 
                wrapperStyle={{ fontSize: isMobile ? '10px' : '13px' }}
                verticalAlign="top"
                height={isMobile ? 36 : 36}
              />
              
              {/* X축 크로스헤어 (세로선) */}
              {(chartMode === 'basic' || activeTool === 'crosshair') && (
                <Customized 
                  component={(props: any) => (
                    <VerticalCrosshair {...props} chartData={chartData} />
                  )}
                />
              )}
              
              {/* 추세 강도 기준선 */}
              {indicatorSettings.adxStrongEnabled && <ReferenceLine y={indicatorSettings.adxStrongThreshold} stroke={colors.adxStrongColor || "#4caf50"} strokeWidth={colors.adxStrongWidth || 1} label={{ value: `강한 추세(${indicatorSettings.adxStrongThreshold})`, position: 'right', fontSize: 10 }} />}
              {indicatorSettings.adxWeakEnabled && <ReferenceLine y={indicatorSettings.adxWeakThreshold} stroke={colors.adxWeakColor || "#ff9800"} strokeWidth={colors.adxWeakWidth || 1} label={{ value: `약한 추세(${indicatorSettings.adxWeakThreshold})`, position: 'right', fontSize: 10 }} />}
              
              {/* ADX 라인 */}
              <Line
                type="monotone"
                dataKey="adx"
                stroke="#9c27b0"
                strokeWidth={2}
                dot={false}
                name="ADX"
                connectNulls
              />
              
              {/* +DI 라인 */}
              <Line
                type="monotone"
                dataKey="plusDI"
                stroke="#4caf50"
                strokeWidth={1.5}
                dot={false}
                name="+DI"
                connectNulls
              />
              
              {/* -DI 라인 */}
              <Line
                type="monotone"
                dataKey="minusDI"
                stroke="#f44336"
                strokeWidth={1.5}
                dot={false}
                name="-DI"
                connectNulls
              />
            </LineChart>
          </ResponsiveContainer>
          
          {/* Y축 값 표시 Chip (ADX) */}
          {adxYValue !== null && adxYPosition !== null && (
            <Chip
              label={adxYValue?.toFixed(2) ?? ''}
              size="small"
              sx={{
                position: 'absolute',
                left: isMobile ? 5 : 10,
                top: adxYPosition,
                transform: 'translateY(-50%)',
                bgcolor: '#1976d2',
                color: 'white',
                fontWeight: 'bold',
                fontSize: '0.75rem',
                height: '24px',
                zIndex: 1000,
                pointerEvents: 'none',
              }}
            />
          )}
        </Box>
      </Box>
      )}

      {/* ADX11 차트 - 제거됨 */}
      {false && !isFullscreen && (
      <Box sx={{ order: getChartOrder('adx11') }}>
        {/* 여백 영역 */}
        <Box sx={{ height: '8px', bgcolor: 'action.hover', borderTop: 1, borderBottom: 1, borderColor: 'divider' }} />
        
        <Box sx={{ height: isMobile ? '220px' : '230px', position: 'relative' }}>
          {/* 범주 표시 - 차트 내부 우측 상단 */}
          <Box sx={{ 
            position: 'absolute', 
            top: 8, 
            right: isMobile ? 10 : 35, 
            zIndex: 10,
            display: 'flex',
            alignItems: 'center',
            bgcolor: 'rgba(255, 255, 255, 0.85)',
            p: 0.5,
            borderRadius: 1,
            boxShadow: '0 1px 3px rgba(0,0,0,0.1)'
          }}>
            <Chip 
              // label={getCategoryDisplay(getChartCategory('adx11')).label}
              label="ADX11 (11) - 단기 추세"
              size="small"
              sx={{ 
                bgcolor: getCategoryDisplay(getChartCategory('adx11')).color,
                color: 'white',
                fontWeight: 'bold',
                fontSize: '0.65rem',
                height: '18px',
              }}
            />
            {/* <Typography variant="caption" sx={{ ml: 0.5, color: 'text.primary', fontSize: '0.7rem', fontWeight: 500 }}>
              ADX11 (11) - 단기 추세
            </Typography> */}
          </Box>
          
          <ResponsiveContainer width="100%" height="100%">
            <LineChart 
              data={chartData}
              margin={isMobile ? 
                { top: 5, right: 15, left: 5, bottom: 5 } : 
                { top: 5, right: 35, left: 25, bottom: 5 }
              }
              onMouseMove={(state: any) => {
                if (state && state.activePayload && state.activePayload.length > 0) {
                  const data = state.activePayload[0].payload;
                  const index = state.activeTooltipIndex;
                  
                  if (index !== undefined && data.date) {
                    setGlobalHoverIndex(index);
                    setGlobalHoverLabel(data.date);
                    setGlobalHoverDate(data.date);
                  }
                  
                  setAdx11YValue(data.adx11);
                  if (state.chartY !== undefined) {
                    setAdx11YPosition(state.chartY);
                  }
                }
              }}
              onMouseLeave={handleChartMouseLeave}
            >
              {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} />}
              <XAxis 
                dataKey="date"
                type="category"
                tick={{ fontSize: 9, fill: '#8b95a5' }}
                interval={getXAxisInterval()}
                stroke="#252d3a"
                tickFormatter={formatXAxisLabel}
                allowDataOverflow={false}
                padding={{ left: 0, right: 0 }}
              />
              <YAxis 
                orientation="left"
                domain={[0, 100]}
                tick={{ fontSize: 11, fill: '#8b95a5' }}
                stroke="#252d3a"
              />
              <Tooltip 
                formatter={(value: any) => {
                  if (value === null || value === undefined) return ['N/A', ''];
                  return [Number(value).toFixed(2), ''];
                }}
                cursor={false}
              />
              <Legend 
                wrapperStyle={{ fontSize: isMobile ? '10px' : '13px' }}
                verticalAlign="top"
                height={isMobile ? 36 : 36}
              />
              
              {/* X축 크로스헤어 (세로선) */}
              {(chartMode === 'basic' || activeTool === 'crosshair') && (
                <Customized 
                  component={(props: any) => (
                    <VerticalCrosshair {...props} chartData={chartData} />
                  )}
                />
              )}
              
              {/* 추세 강도 기준선 */}
              {indicatorSettings.adxStrongEnabled && <ReferenceLine y={indicatorSettings.adxStrongThreshold} stroke={colors.adxStrongColor || "#4caf50"} strokeWidth={colors.adxStrongWidth || 1} label={{ value: `강한 추세(${indicatorSettings.adxStrongThreshold})`, position: 'right', fontSize: 10 }} />}
              {indicatorSettings.adxWeakEnabled && <ReferenceLine y={indicatorSettings.adxWeakThreshold} stroke={colors.adxWeakColor || "#ff9800"} strokeWidth={colors.adxWeakWidth || 1} label={{ value: `약한 추세(${indicatorSettings.adxWeakThreshold})`, position: 'right', fontSize: 10 }} />}

              {/* ADX11 라인 */}
              <Line
                type="monotone"
                dataKey="adx11"
                stroke="#673ab7"
                strokeWidth={2}
                dot={false}
                name="ADX11"
                connectNulls
              />
            </LineChart>
          </ResponsiveContainer>
          
          {/* Y축 값 표시 Chip (ADX11) */}
          {adx11YValue !== null && adx11YPosition !== null && (
            <Chip
              label={adx11YValue?.toFixed(2) ?? ''}
              size="small"
              sx={{
                position: 'absolute',
                left: isMobile ? 5 : 10,
                top: adx11YPosition,
                transform: 'translateY(-50%)',
                bgcolor: '#1976d2',
                color: 'white',
                fontWeight: 'bold',
                fontSize: '0.75rem',
                height: '24px',
                zIndex: 1000,
                pointerEvents: 'none',
              }}
            />
          )}
        </Box>
      </Box>
      )}

      {/* CCI13 차트 - 비활성화 (통합 차트로 이동) */}
      {false && !isFullscreen && settings.cci && (
      <Box sx={{ order: getChartOrder('cci') }}>
        {/* 여백 영역 */}
        <Box sx={{ height: '8px', bgcolor: 'action.hover', borderTop: 1, borderBottom: 1, borderColor: 'divider' }} />
        
        <Box sx={{ height: isMobile ? '220px' : '230px', position: 'relative' }}>
          {/* 범주 표시 - 차트 내부 우측 상단 */}
          <Box sx={{ 
            position: 'absolute', 
            top: 8, 
            right: isMobile ? 10 : 35, 
            zIndex: 10,
            display: 'flex',
            alignItems: 'center',
            bgcolor: 'rgba(255, 255, 255, 0.85)',
            p: 0.5,
            borderRadius: 1,
            boxShadow: '0 1px 3px rgba(0,0,0,0.1)'
          }}>
            <Chip 
              // label={getCategoryDisplay(getChartCategory('cci')).label}
              label="CCI13 (13) - Commodity Channel Index"
              size="small"
              sx={{ 
                bgcolor: getCategoryDisplay(getChartCategory('cci')).color,
                color: 'white',
                fontWeight: 'bold',
                fontSize: '0.65rem',
                height: '18px',
              }}
            />
            {/* <Typography variant="caption" sx={{ ml: 0.5, color: 'text.primary', fontSize: '0.7rem', fontWeight: 500 }}>
              CCI13 (13) - Commodity Channel Index
            </Typography> */}
          </Box>
          
          <ResponsiveContainer width="100%" height="100%">
            <LineChart 
              data={chartData}
              margin={isMobile ? 
                { top: 5, right: 15, left: 5, bottom: 5 } : 
                { top: 5, right: 35, left: 25, bottom: 5 }
              }
              onMouseMove={(state: any) => {
                if (state && state.activePayload && state.activePayload.length > 0) {
                  const data = state.activePayload[0].payload;
                  const index = state.activeTooltipIndex;
                  
                  if (index !== undefined && data.date) {
                    setGlobalHoverIndex(index);
                    setGlobalHoverLabel(data.date);
                    setGlobalHoverDate(data.date);
                  }
                  
                  setCciYValue(data.cci);
                  if (state.chartY !== undefined) {
                    setCciYPosition(state.chartY);
                  }
                }
              }}
              onMouseLeave={handleChartMouseLeave}
            >
              {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} />}
              <XAxis 
                dataKey="date"
                type="category"
                tick={{ fontSize: 9, fill: '#8b95a5' }}
                interval={getXAxisInterval()}
                stroke="#252d3a"
                tickFormatter={formatXAxisLabel}
                allowDataOverflow={false}
                padding={{ left: 0, right: 0 }}
              />
              <YAxis 
                orientation="left"
                domain={[-300, 300]}
                tick={{ fontSize: 11, fill: '#8b95a5' }}
                stroke="#252d3a"
              />
              <Tooltip 
                formatter={(value: any) => {
                  if (value === null || value === undefined) return ['N/A', ''];
                  return [Number(value).toFixed(2), ''];
                }}
                cursor={false}
              />
              <Legend 
                wrapperStyle={{ fontSize: isMobile ? '10px' : '13px' }}
                verticalAlign="top"
                height={isMobile ? 36 : 36}
              />
              
              {/* X축 크로스헤어 (세로선) */}
              {(chartMode === 'basic' || activeTool === 'crosshair') && (
                <Customized 
                  component={(props: any) => (
                    <VerticalCrosshair {...props} chartData={chartData} />
                  )}
                />
              )}
              
              {/* 과매수/과매도 기준선 */}
              <ReferenceLine y={indicatorSettings.cciOverbought} stroke="#f44336"  strokeWidth={1} label={{ value: `과매수(${indicatorSettings.cciOverbought > 0 ? '+' : ''}${indicatorSettings.cciOverbought})`, position: 'right', fontSize: 10 }} />
              <ReferenceLine y={indicatorSettings.cciOversold} stroke="#4caf50"  strokeWidth={1} label={{ value: `과매도(${indicatorSettings.cciOversold})`, position: 'right', fontSize: 10 }} />
              <ReferenceLine y={0} stroke="#999" strokeDasharray="2 2" strokeWidth={1} />
              
              {/* CCI13 라인 */}
              <Line
                type="monotone"
                dataKey="cci"
                stroke="#ff5722"
                strokeWidth={2}
                dot={false}
                name="CCI13"
                connectNulls
              />
            </LineChart>
          </ResponsiveContainer>
          
          {/* Y축 값 표시 Chip (CCI13) */}
          {cciYValue !== null && cciYPosition !== null && (
            <Chip
              label={cciYValue?.toFixed(2) ?? ''}
              size="small"
              sx={{
                position: 'absolute',
                left: isMobile ? 5 : 10,
                top: cciYPosition,
                transform: 'translateY(-50%)',
                bgcolor: '#1976d2',
                color: 'white',
                fontWeight: 'bold',
                fontSize: '0.75rem',
                height: '24px',
                zIndex: 1000,
                pointerEvents: 'none',
              }}
            />
          )}
        </Box>
      </Box>
      )}

      {/* RSI 차트 - 비활성화 (통합 차트로 이동) */}
      {false && !isFullscreen && settings.rsi && (
      <Box sx={{ order: getChartOrder('rsi') }}>
        {/* 여백 영역 */}
        <Box sx={{ height: '8px', bgcolor: 'action.hover', borderTop: 1, borderBottom: 1, borderColor: 'divider' }} />
        
        <Box sx={{ height: isMobile ? '220px' : '230px', position: 'relative' }}>
          {/* 범주 표시 - 차트 내부 우측 상단 */}
          <Box sx={{ 
            position: 'absolute', 
            top: 8, 
            right: isMobile ? 10 : 35, 
            zIndex: 10,
            display: 'flex',
            alignItems: 'center',
            bgcolor: 'rgba(255, 255, 255, 0.85)',
            p: 0.5,
            borderRadius: 1,
            boxShadow: '0 1px 3px rgba(0,0,0,0.1)'
          }}>
            <Chip 
              label="RSI (14) - Relative Strength Index"
              size="small"
              sx={{ 
                bgcolor: getCategoryDisplay(getChartCategory('rsi')).color,
                color: 'white',
                fontWeight: 'bold',
                fontSize: '0.65rem',
                height: '18px',
              }}
            />
          </Box>
          
          <ResponsiveContainer width="100%" height="100%">
            <LineChart 
              data={chartData}
              margin={isMobile ? 
                { top: 5, right: 15, left: 5, bottom: 5 } : 
                { top: 5, right: 35, left: 25, bottom: 5 }
              }
              onMouseMove={(state: any) => {
                if (state && state.activePayload && state.activePayload.length > 0) {
                  const data = state.activePayload[0].payload;
                  const index = state.activeTooltipIndex;
                  
                  if (index !== undefined && data.date) {
                    setGlobalHoverIndex(index);
                    setGlobalHoverLabel(data.date);
                    setGlobalHoverDate(data.date);
                  }
                  
                  setRsiYValue(data.rsi);
                  if (state.chartY !== undefined) {
                    setRsiYPosition(state.chartY);
                  }
                }
              }}
              onMouseLeave={handleChartMouseLeave}
            >
              {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} />}
              <XAxis 
                dataKey="date"
                type="category"
                tick={{ fontSize: 9, fill: '#8b95a5' }}
                interval={getXAxisInterval()}
                stroke="#252d3a"
                tickFormatter={formatXAxisLabel}
                allowDataOverflow={false}
                padding={{ left: 0, right: 0 }}
              />
              <YAxis 
                orientation="left"
                domain={[0, 100]}
                tick={{ fontSize: 11, fill: '#8b95a5' }}
                stroke="#252d3a"
              />
              <Tooltip 
                formatter={(value: any) => {
                  if (value === null || value === undefined) return ['N/A', ''];
                  return [Number(value).toFixed(2), ''];
                }}
                cursor={false}
              />
              <Legend 
                wrapperStyle={{ fontSize: isMobile ? '10px' : '13px' }}
                verticalAlign="top"
                height={isMobile ? 36 : 36}
              />
              
              {/* X축 크로스헤어 (세로선) */}
              {(chartMode === 'basic' || activeTool === 'crosshair') && (
                <Customized 
                  component={(props: any) => (
                    <VerticalCrosshair {...props} chartData={chartData} />
                  )}
                />
              )}
              
              {/* 과매수/과매도 기준선 */}
              {indicatorSettings.rsiOverboughtEnabled && <ReferenceLine y={indicatorSettings.rsiOverbought} stroke={colors.rsiOverboughtColor || "#f44336"} strokeWidth={colors.rsiOverboughtWidth || 1} label={{ value: `과매수(${indicatorSettings.rsiOverbought})`, position: 'right', fontSize: 10 }} />}
              {indicatorSettings.rsiOversoldEnabled && <ReferenceLine y={indicatorSettings.rsiOversold} stroke={colors.rsiOversoldColor || "#4caf50"} strokeWidth={colors.rsiOversoldWidth || 1} label={{ value: `과매도(${indicatorSettings.rsiOversold})`, position: 'right', fontSize: 10 }} />}
              {indicatorSettings.rsiMiddleEnabled && <ReferenceLine y={indicatorSettings.rsiMiddle} stroke={colors.rsiMiddleColor || "#999"} strokeDasharray="2 2" strokeWidth={colors.rsiMiddleWidth || 1} />}
              
              {/* RSI 라인 */}
              <Line
                type="monotone"
                dataKey="rsi"
                stroke="#4CAF50"
                strokeWidth={2}
                dot={false}
                name="RSI"
                connectNulls
              />
            </LineChart>
          </ResponsiveContainer>
          
          {/* Y축 값 표시 Chip (RSI) */}
          {rsiYValue !== null && rsiYPosition !== null && (
            <Chip
              label={rsiYValue?.toFixed(2) ?? ''}
              size="small"
              sx={{
                position: 'absolute',
                left: isMobile ? 5 : 10,
                top: rsiYPosition,
                transform: 'translateY(-50%)',
                bgcolor: '#4CAF50',
                color: 'white',
                fontWeight: 'bold',
                fontSize: '0.75rem',
                height: '24px',
                zIndex: 1000,
                pointerEvents: 'none',
              }}
            />
          )}
        </Box>
      </Box>
      )}
      
      </Box> {/* 차트 컨테이너 종료 */}
      
      {/* 차트 영역 전용 돋보기 (Portal) */}
      {magnifierEnabled && chartMousePos && magnifierPosition && chartRef.current && (() => {
        const rect = chartRef.current.getBoundingClientRect();
        const magnifierSize = 300;
        const zoomLevel = 4;
        
        // 마우스 위치가 돋보기 중앙에 오도록 계산
        const magnifierLeft = chartMousePos.x - magnifierSize / 2;
        const magnifierTop = chartMousePos.y - magnifierSize / 2;
        
        // 차트 내 상대 위치에서 확대할 영역 계산
        const sourceX = magnifierPosition.x - (magnifierSize / 2 / zoomLevel);
        const sourceY = magnifierPosition.y - (magnifierSize / 2 / zoomLevel);
        
        return ReactDOM.createPortal(
          <>
            {/* 전체 화면 반투명 오버레이 */}
            <Box
              sx={{
                position: 'fixed',
                top: 0,
                left: 0,
                width: '100vw',
                height: '100vh',
                bgcolor: 'rgba(0, 0, 0, 0.1)',
                pointerEvents: 'none',
                zIndex: 9998,
              }}
            />
            
            {/* 돋보기 렌즈 */}
            <Box
              sx={{
                position: 'fixed',
                left: magnifierLeft,
                top: magnifierTop,
                width: magnifierSize,
                height: magnifierSize,
                borderRadius: '50%',
                border: '5px solid #1976d2',
                boxShadow: '0 12px 32px rgba(0, 0, 0, 0.5)',
                pointerEvents: 'none',
                overflow: 'hidden',
                zIndex: 9999,
                bgcolor: 'background.paper',
              }}
            >
              {/* 확대된 차트 영역 */}
              <Box
                sx={{
                  position: 'absolute',
                  width: rect.width,
                  height: rect.height,
                  transform: `scale(${zoomLevel})`,
                  transformOrigin: '0 0',
                  left: -sourceX * zoomLevel + magnifierSize / 2,
                  top: -sourceY * zoomLevel + magnifierSize / 2,
                }}
                dangerouslySetInnerHTML={{
                  __html: (() => {
                    if (chartRef.current) {
                      const clone = chartRef.current.cloneNode(true) as HTMLElement;
                      // 스타일 복제
                      const styles = Array.from(document.querySelectorAll('style'))
                        .map(s => s.outerHTML)
                        .join('');
                      return styles + clone.innerHTML;
                    }
                    return '';
                  })()
                }}
              />
              
              {/* 십자선 표시 */}
              <Box
                sx={{
                  position: 'absolute',
                  left: '50%',
                  top: 0,
                  bottom: 0,
                  width: 2,
                  bgcolor: 'rgba(25, 118, 210, 0.8)',
                  transform: 'translateX(-50%)',
                  zIndex: 10000,
                }}
              />
              <Box
                sx={{
                  position: 'absolute',
                  top: '50%',
                  left: 0,
                  right: 0,
                  height: 2,
                  bgcolor: 'rgba(25, 118, 210, 0.8)',
                  transform: 'translateY(-50%)',
                  zIndex: 10000,
                }}
              />
            </Box>
          </>,
          document.body
        );
      })()}
      
      {/* 보조지표 팝업 다이얼로그 */}
      <Dialog
        open={indicatorPopupOpen}
        onClose={(_, reason) => {
          // 배경 클릭이나 ESC 키로는 닫지 않음 - 닫기 버튼으로만 닫기
          if (reason === 'backdropClick' || reason === 'escapeKeyDown') {
            return;
          }
          setIndicatorPopupOpen(false);
        }}
        maxWidth={false}
        disableEscapeKeyDown
        hideBackdrop // 백드롭 완전 숨김 - 아래 화면 조작 가능
        sx={{
          pointerEvents: 'none', // Dialog 컨테이너는 클릭 통과
          '& .MuiDialog-container': {
            pointerEvents: 'none',
          }
        }}
        PaperProps={{
          ref: popupRef,
          sx: {
            width: popupSize.width,
            height: popupSize.height,
            minWidth: 400,
            minHeight: 300,
            bgcolor: `rgba(${theme.palette.mode === 'dark' ? '18, 18, 26' : '255, 255, 255'}, ${popupTransparency / 100})`,
            backdropFilter: popupTransparency > 50 ? 'blur(10px)' : 'none',
            borderRadius: 2,
            overflow: 'visible', // 리사이즈 핸들이 보이도록
            position: 'absolute',
            transform: `translate(${popupPosition.x}px, ${popupPosition.y}px)`,
            transition: (isDragging || isResizing) ? 'none' : 'transform 0.1s ease-out',
            boxShadow: '0 8px 32px rgba(0,0,0,0.3)',
            border: '2px solid',
            borderColor: theme.palette.mode === 'dark' ? 'primary.main' : 'primary.light',
            pointerEvents: 'auto', // Paper는 클릭 가능
          }
        }}
      >
        {/* 리사이즈 핸들들 */}
        {/* 상단 */}
        <Box
          onMouseDown={(e) => handleResizeMouseDown(e, 'n')}
          onTouchStart={(e) => handleResizeTouchStart(e, 'n')}
          sx={{
            position: 'absolute',
            top: -4,
            left: 10,
            right: 10,
            height: 8,
            cursor: 'ns-resize',
            zIndex: 10,
            touchAction: 'none',
          }}
        />
        {/* 하단 */}
        <Box
          onMouseDown={(e) => handleResizeMouseDown(e, 's')}
          onTouchStart={(e) => handleResizeTouchStart(e, 's')}
          sx={{
            position: 'absolute',
            bottom: -4,
            left: 10,
            right: 10,
            height: 8,
            cursor: 'ns-resize',
            zIndex: 10,
            touchAction: 'none',
          }}
        />
        {/* 좌측 */}
        <Box
          onMouseDown={(e) => handleResizeMouseDown(e, 'w')}
          onTouchStart={(e) => handleResizeTouchStart(e, 'w')}
          sx={{
            position: 'absolute',
            left: -4,
            top: 10,
            bottom: 10,
            width: 8,
            cursor: 'ew-resize',
            zIndex: 10,
            touchAction: 'none',
          }}
        />
        {/* 우측 */}
        <Box
          onMouseDown={(e) => handleResizeMouseDown(e, 'e')}
          onTouchStart={(e) => handleResizeTouchStart(e, 'e')}
          sx={{
            position: 'absolute',
            right: -4,
            top: 10,
            bottom: 10,
            width: 8,
            cursor: 'ew-resize',
            zIndex: 10,
            touchAction: 'none',
          }}
        />
        {/* 좌상단 코너 */}
        <Box
          onMouseDown={(e) => handleResizeMouseDown(e, 'nw')}
          onTouchStart={(e) => handleResizeTouchStart(e, 'nw')}
          sx={{
            position: 'absolute',
            top: -6,
            left: -6,
            width: 14,
            height: 14,
            cursor: 'nwse-resize',
            zIndex: 11,
            touchAction: 'none',
          }}
        />
        {/* 우상단 코너 */}
        <Box
          onMouseDown={(e) => handleResizeMouseDown(e, 'ne')}
          onTouchStart={(e) => handleResizeTouchStart(e, 'ne')}
          sx={{
            position: 'absolute',
            top: -6,
            right: -6,
            width: 14,
            height: 14,
            cursor: 'nesw-resize',
            zIndex: 11,
            touchAction: 'none',
          }}
        />
        {/* 좌하단 코너 */}
        <Box
          onMouseDown={(e) => handleResizeMouseDown(e, 'sw')}
          onTouchStart={(e) => handleResizeTouchStart(e, 'sw')}
          sx={{
            position: 'absolute',
            bottom: -6,
            left: -6,
            width: 14,
            height: 14,
            cursor: 'nesw-resize',
            zIndex: 11,
            touchAction: 'none',
          }}
        />
        {/* 우하단 코너 */}
        <Box
          onMouseDown={(e) => handleResizeMouseDown(e, 'se')}
          onTouchStart={(e) => handleResizeTouchStart(e, 'se')}
          sx={{
            position: 'absolute',
            bottom: -6,
            right: -6,
            width: 14,
            height: 14,
            cursor: 'nwse-resize',
            zIndex: 11,
            touchAction: 'none',
            // 리사이즈 그립 표시
            '&::after': {
              content: '""',
              position: 'absolute',
              bottom: 4,
              right: 4,
              width: 8,
              height: 8,
              borderRight: '2px solid',
              borderBottom: '2px solid',
              borderColor: 'primary.main',
              opacity: 0.6,
            }
          }}
        />
        {/* 팝업 헤더 - 드래그 핸들 */}
        <DialogTitle 
          onMouseDown={handlePopupMouseDown}
          onTouchStart={handlePopupTouchStart}
          sx={{ 
            display: 'flex', 
            alignItems: 'center', 
            justifyContent: 'space-between',
            py: 1,
            px: 2,
            borderBottom: 1,
            borderColor: 'divider',
            bgcolor: `rgba(${theme.palette.mode === 'dark' ? '30, 30, 46' : '245, 245, 245'}, ${Math.max(popupTransparency / 100, 0.3)})`,
            cursor: 'move',
            userSelect: 'none',
            touchAction: 'none', // 터치 시 스크롤 방지
          }}>
          <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
            <Typography variant="h6" sx={{ fontSize: '1rem', fontWeight: 'bold' }}>
              보조지표 차트
            </Typography>
            {/* 팝업 내 보조지표 토글 */}
            <Box sx={{ display: 'flex', gap: 0.5, ml: 2 }}>
              <Chip
                label="MACD"
                size="small"
                variant={settings.macdLine ? "filled" : "outlined"}
                color={settings.macdLine ? "primary" : "default"}
                onClick={() => updateSettings({ macdLine: !settings.macdLine, macdHistogram: !settings.macdLine })}
                sx={{ height: '22px', fontSize: '10px', cursor: 'pointer' }}
              />
              <Chip
                label="Stoch"
                size="small"
                variant={settings.stochasticSlow ? "filled" : "outlined"}
                color={settings.stochasticSlow ? "primary" : "default"}
                onClick={() => updateSettings({ stochasticSlow: !settings.stochasticSlow })}
                sx={{ height: '22px', fontSize: '10px', cursor: 'pointer' }}
              />
              <Chip
                label="Vol"
                size="small"
                variant={settings.volumeIndicator ? "filled" : "outlined"}
                color={settings.volumeIndicator ? "primary" : "default"}
                onClick={() => updateSettings({ volumeIndicator: !settings.volumeIndicator })}
                sx={{ height: '22px', fontSize: '10px', cursor: 'pointer' }}
              />
              <Chip
                label="ADX"
                size="small"
                variant={settings.adx ? "filled" : "outlined"}
                color={settings.adx ? "primary" : "default"}
                onClick={() => updateSettings({ adx: !settings.adx })}
                sx={{ height: '22px', fontSize: '10px', cursor: 'pointer' }}
              />
              <Chip
                label="CCI"
                size="small"
                variant={settings.cci ? "filled" : "outlined"}
                color={settings.cci ? "primary" : "default"}
                onClick={() => updateSettings({ cci: !settings.cci })}
                sx={{ height: '22px', fontSize: '10px', cursor: 'pointer' }}
              />
              <Chip
                label="RSI"
                size="small"
                variant={settings.rsi ? "filled" : "outlined"}
                color={settings.rsi ? "primary" : "default"}
                onClick={() => updateSettings({ rsi: !settings.rsi })}
                sx={{ height: '22px', fontSize: '10px', cursor: 'pointer' }}
              />
              <Chip
                label="TRIX"
                size="small"
                variant={settings.trix ? "filled" : "outlined"}
                color={settings.trix ? "primary" : "default"}
                onClick={() => updateSettings({ trix: !settings.trix })}
                sx={{ height: '22px', fontSize: '10px', cursor: 'pointer' }}
              />
            </Box>
          </Box>
          
          <Box 
            sx={{ display: 'flex', alignItems: 'center', gap: 2 }}
            onMouseDown={(e) => e.stopPropagation()} // 드래그 이벤트 전파 방지
          >
            {/* 투명도 조절 */}
            <Box sx={{ display: 'flex', alignItems: 'center', gap: 1, minWidth: 200 }}>
              <Opacity sx={{ fontSize: 18, color: 'text.secondary' }} />
              <Typography variant="caption" sx={{ color: 'text.secondary', minWidth: 45 }}>
                투명도
              </Typography>
              <Slider
                value={popupTransparency}
                onChange={(_, value) => setPopupTransparency(value as number)}
                min={0}
                max={100}
                step={5}
                size="small"
                sx={{ width: 120 }}
              />
              <Typography variant="caption" sx={{ color: 'text.secondary', minWidth: 40 }}>
                {popupTransparency}%
              </Typography>
            </Box>
            
            {/* 닫기 버튼 */}
            <IconButton
              size="small"
              onClick={() => setIndicatorPopupOpen(false)}
              sx={{
                bgcolor: 'action.hover',
                '&:hover': { bgcolor: 'error.main', color: 'error.contrastText' },
              }}
            >
              <Close fontSize="small" />
            </IconButton>
          </Box>
        </DialogTitle>
        
        {/* 팝업 차트 영역 */}
        <DialogContent sx={{ p: 0, height: 'calc(100% - 56px)', overflow: 'hidden' }}>
          {(settings.macdLine || settings.macdHistogram || settings.stochasticSlow || 
            settings.adx || settings.cci || settings.rsi || settings.trix || settings.volumeIndicator) ? (
          <Box sx={{ width: '100%', height: '100%' }}>
            <ResponsiveContainer width="100%" height="100%">
              <ComposedChart 
                data={chartData}
                syncId="upbitChart"
                margin={CHART_MARGIN_WITH_XAXIS}
                onMouseMove={(state: any) => {
                  if (state && state.activePayload && state.activePayload.length > 0) {
                    const data = state.activePayload[0].payload;
                    const index = state.activeTooltipIndex;
                    
                    if (index !== undefined && data.date) {
                      setGlobalHoverIndex(index);
                      setGlobalHoverLabel(data.date);
                      setGlobalHoverDate(data.date);
                    }
                  }
                }}
                onMouseLeave={handleChartMouseLeave}
              >
                {showGrid && <CartesianGrid stroke="#252d3a" strokeWidth={0.5} />}
                <XAxis 
                  dataKey="date"
                  type="category"
                  tick={{ fontSize: 11, fill: '#8b95a5' }}
                  interval={getXAxisInterval()}
                  stroke="#252d3a"
                  tickFormatter={formatXAxisLabel}
                  allowDataOverflow={false}
                  padding={{ left: 0, right: 0 }}
                />
                
                {/* 캔들차트와 X축 위치를 맞추기 위해 모든 Y축 숨김, 단일 축만 공간 확보 */}
                {/* 왼쪽: 가격 Y축과 동일한 width (50) - 라벨 숨김 */}
                <YAxis 
                  yAxisId="oscillator"
                  orientation="left"
                  domain={[0, 100]}
                  tick={false}
                  stroke="transparent"
                  width={60}
                />
                
                {/* CCI, MACD, TRIX, Volume Y축은 완전히 숨김 */}
                <YAxis 
                  yAxisId="cci"
                  orientation="left"
                  domain={[-200, 200]}
                  hide={true}
                  width={0}
                />
                
                <YAxis 
                  yAxisId="macd"
                  orientation="left"
                  domain={getMacdYAxisDomain()}
                  hide={true}
                  width={0}
                />
                
                <YAxis 
                  yAxisId="trix"
                  orientation="left"
                  hide={true}
                  width={0}
                />
                
                <YAxis 
                  yAxisId="volume"
                  orientation="left"
                  hide={true}
                  width={0}
                />
                
                <Tooltip 
                  formatter={(value: any, name: string) => {
                    if (value === null || value === undefined) return ['-', name];
                    return [typeof value === 'number' ? value.toFixed(2) : value, name];
                  }}
                  labelFormatter={(label) => `날짜: ${label}`}
                  contentStyle={{
                    backgroundColor: 'rgba(255, 255, 255, 0.95)',
                    border: '1px solid #ccc',
                    borderRadius: '4px'
                  }}
                />
                {/* Legend 제거 - 캔들차트와 X축 위치 일치를 위해 */}
                
                {/* 거래량 Bar */}
                {settings.volumeIndicator && (
                  <Bar 
                    yAxisId="volume"
                    dataKey="volume" 
                    name="거래량"
                    radius={[1, 1, 0, 0]}
                    activeBar={false}
                    opacity={0.5}
                  >
                    {chartData.map((entry, index) => (
                      <Cell key={`pop-vol-${index}`} fill={entry.volumeColor} opacity={0.5} />
                    ))}
                  </Bar>
                )}
                
                {/* MACD 히스토그램 */}
                {settings.macdHistogram && (
                  <Bar 
                    yAxisId="macd"
                    dataKey="macdHistogram" 
                    name="Histogram"
                    radius={[1, 1, 0, 0]}
                    activeBar={false}
                  >
                    {chartData.map((entry, index) => (
                      <Cell key={`pop-cell-${index}`} fill={entry.histogramColor} />
                    ))}
                  </Bar>
                )}
                
                {/* MACD 기준선 */}
                {(settings.macdLine || settings.macdHistogram) && (
                  <ReferenceLine yAxisId="macd" y={0} stroke="#666" strokeDasharray="2 2" strokeWidth={1} />
                )}
                
                {/* MACD 라인 */}
                {settings.macdLine && (
                  <>
                    <Line
                      yAxisId="macd"
                      type="monotone"
                      dataKey="macdLine"
                      stroke={colors.macdLine}
                      strokeWidth={2}
                      dot={false}
                      name="MACD"
                      connectNulls
                      isAnimationActive={false}
                    />
                    <Line
                      yAxisId="macd"
                      type="monotone"
                      dataKey="signalLine"
                      stroke={colors.signalLine}
                      strokeWidth={2}
                      dot={false}
                      name="Signal"
                      connectNulls
                      isAnimationActive={false}
                    />
                  </>
                )}
                
                {/* Stochastic 기준선 */}
                {settings.stochasticSlow && (
                  <>
                    <ReferenceLine yAxisId="oscillator" y={80} stroke="#f44336"  strokeWidth={1} strokeOpacity={0.5} />
                    <ReferenceLine yAxisId="oscillator" y={20} stroke="#4caf50"  strokeWidth={1} strokeOpacity={0.5} />
                  </>
                )}
                
                {/* Stochastic 라인 */}
                {settings.stochasticSlow && (
                  <>
                    <Line
                      yAxisId="oscillator"
                      type="monotone"
                      dataKey="stochasticK"
                      stroke="#FF9500"
                      strokeWidth={2}
                      dot={false}
                      name="Stoch %K"
                      connectNulls
                      isAnimationActive={false}
                    />
                    <Line
                      yAxisId="oscillator"
                      type="monotone"
                      dataKey="stochasticD"
                      stroke="#FFEB3B"
                      strokeWidth={2}
                      dot={false}
                      name="Stoch %D"
                      connectNulls
                      isAnimationActive={false}
                    />
                  </>
                )}
                
                {/* ADX 기준선 */}
                {settings.adx && (
                  <ReferenceLine yAxisId="oscillator" y={25} stroke="#9C27B0" strokeDasharray="5 5" strokeWidth={1} strokeOpacity={0.4} />
                )}
                
                {/* ADX 라인 */}
                {settings.adx && (
                  <>
                    <Line
                      yAxisId="oscillator"
                      type="monotone"
                      dataKey="adx"
                      stroke="#9C27B0"
                      strokeWidth={2}
                      dot={false}
                      name="ADX"
                      connectNulls
                      isAnimationActive={false}
                    />
                    <Line
                      yAxisId="oscillator"
                      type="monotone"
                      dataKey="plusDI"
                      stroke="#4CAF50"
                      strokeWidth={1.5}
                      dot={false}
                      name="+DI"
                      connectNulls
                      isAnimationActive={false}
                    />
                    <Line
                      yAxisId="oscillator"
                      type="monotone"
                      dataKey="minusDI"
                      stroke="#f44336"
                      strokeWidth={1.5}
                      dot={false}
                      name="-DI"
                      connectNulls
                      isAnimationActive={false}
                    />
                  </>
                )}
                
                {/* CCI 기준선 */}
                {settings.cci && (
                  <>
                    <ReferenceLine yAxisId="cci" y={100} stroke="#f44336"  strokeWidth={1} strokeOpacity={0.4} />
                    <ReferenceLine yAxisId="cci" y={-100} stroke="#4caf50"  strokeWidth={1} strokeOpacity={0.4} />
                    <ReferenceLine yAxisId="cci" y={0} stroke="#999" strokeDasharray="2 2" strokeWidth={1} />
                  </>
                )}
                
                {/* CCI 라인 */}
                {settings.cci && (
                  <Line
                    yAxisId="cci"
                    type="monotone"
                    dataKey="cci"
                    stroke="#ff5722"
                    strokeWidth={2}
                    dot={false}
                    name="CCI"
                    connectNulls
                    isAnimationActive={false}
                  />
                )}
                
                {/* RSI 기준선 */}
                {settings.rsi && (
                  <>
                    <ReferenceLine yAxisId="oscillator" y={70} stroke="#ff9800"  strokeWidth={1} strokeOpacity={0.4} />
                    <ReferenceLine yAxisId="oscillator" y={30} stroke="#2196F3"  strokeWidth={1} strokeOpacity={0.4} />
                  </>
                )}
                
                {/* RSI 라인 */}
                {settings.rsi && (
                  <Line
                    yAxisId="oscillator"
                    type="monotone"
                    dataKey="rsi"
                    stroke="#00BCD4"
                    strokeWidth={2}
                    dot={false}
                    name="RSI"
                    connectNulls
                    isAnimationActive={false}
                  />
                )}
                
                {/* TRIX 기준선 */}
                {settings.trix && (
                  <>
                    <ReferenceLine yAxisId="trix" y={0} stroke="#999" strokeDasharray="2 2" strokeWidth={1} />
                  </>
                )}
                
                {/* TRIX 라인 */}
                {settings.trix && (
                  <Line
                    yAxisId="trix"
                    type="monotone"
                    dataKey="trix"
                    stroke="#E91E63"
                    strokeWidth={2}
                    dot={false}
                    name="TRIX"
                    connectNulls
                    isAnimationActive={false}
                  />
                )}
                
                {/* TRIX Signal 라인 */}
                {settings.trix && (
                  <Line
                    yAxisId="trix"
                    type="monotone"
                    dataKey="trixSignal"
                    stroke="#9C27B0"
                    strokeWidth={1.5}
                    dot={false}
                    name="TRIX Signal"
                    connectNulls
                    isAnimationActive={false}
                  />
                )}
              </ComposedChart>
            </ResponsiveContainer>
          </Box>
          ) : (
            <Box sx={{ 
              display: 'flex', 
              alignItems: 'center', 
              justifyContent: 'center', 
              height: '100%',
              color: 'text.secondary'
            }}>
              <Typography>표시할 보조지표를 선택하세요</Typography>
            </Box>
          )}
        </DialogContent>
      </Dialog>
      
      {/* 이동평균선 설정 다이얼로그 */}
      <MASettingsDialog 
        open={maSettingsDialogOpen} 
        onClose={() => setMaSettingsDialogOpen(false)} 
      />
      
      {/* 일목균형표 설정 다이얼로그 */}
      <IchimokuSettingsDialog
        open={ichimokuSettingsDialogOpen}
        onClose={() => setIchimokuSettingsDialogOpen(false)}
      />
      
      {/* 보조지표 설정 다이얼로그 */}
      <IndicatorParamsSettingsDialog
        open={indicatorSettingsDialogOpen}
        onClose={() => setIndicatorSettingsDialogOpen(false)}
      />
      
      {/* 통합 차트 설정 다이얼로그 */}
      <ChartSettingsDialog
        open={chartSettingsDialogOpen}
        onClose={() => setChartSettingsDialogOpen(false)}
      />
      
      {/* 그리기 객체 스타일 편집 다이얼로그 */}
      <Dialog
        open={styleEditDialogOpen}
        onClose={() => setStyleEditDialogOpen(false)}
        maxWidth="xs"
        fullWidth
      >
        <DialogTitle sx={{ pb: 1 }}>
          도형 스타일 편집
        </DialogTitle>
        <DialogContent>
          <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2.5, pt: 1 }}>
            {/* 선 색상 */}
            <Box>
              <Typography variant="body2" fontWeight="bold" sx={{ mb: 1 }}>
                선 색상
              </Typography>
              <Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
                {['#2962ff', '#ef5350', '#26a69a', '#ff9800', '#9c27b0', '#00bcd4', '#ffeb3b', '#ffffff', '#8b95a5'].map(color => (
                  <Box
                    key={color}
                    onClick={() => setEditingStyle(prev => ({ ...prev, color }))}
                    sx={{
                      width: 28,
                      height: 28,
                      borderRadius: '50%',
                      bgcolor: color,
                      cursor: 'pointer',
                      border: editingStyle.color === color ? '3px solid #2962ff' : '2px solid #444',
                      boxShadow: editingStyle.color === color ? '0 0 8px rgba(41, 98, 255, 0.5)' : 'none',
                      '&:hover': { transform: 'scale(1.1)' },
                      transition: 'all 0.15s',
                    }}
                  />
                ))}
              </Box>
            </Box>
            
            {/* 선 굵기 */}
            <Box>
              <Typography variant="body2" fontWeight="bold" sx={{ mb: 1 }}>
                선 굵기: {editingStyle.strokeWidth}px
              </Typography>
              <Slider
                value={editingStyle.strokeWidth}
                onChange={(_, value) => setEditingStyle(prev => ({ ...prev, strokeWidth: value as number }))}
                min={1}
                max={5}
                step={0.5}
                marks={[
                  { value: 1, label: '1' },
                  { value: 2, label: '2' },
                  { value: 3, label: '3' },
                  { value: 4, label: '4' },
                  { value: 5, label: '5' },
                ]}
                sx={{ width: '100%' }}
              />
            </Box>
            
            {/* 선 종류 */}
            <Box>
              <Typography variant="body2" fontWeight="bold" sx={{ mb: 1 }}>
                선 종류
              </Typography>
              <ToggleButtonGroup
                value={editingStyle.strokeStyle}
                exclusive
                onChange={(_, value) => value && setEditingStyle(prev => ({ ...prev, strokeStyle: value }))}
                size="small"
                fullWidth
              >
                <ToggleButton value="solid">
                  <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
                    <Box sx={{ width: 30, height: 2, bgcolor: 'currentColor' }} />
                    실선
                  </Box>
                </ToggleButton>
                <ToggleButton value="dashed">
                  <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
                    <Box sx={{ width: 30, height: 2, borderTop: '2px dashed currentColor' }} />
                    점선
                  </Box>
                </ToggleButton>
                <ToggleButton value="dotted">
                  <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
                    <Box sx={{ width: 30, height: 2, borderTop: '2px dotted currentColor' }} />
                    점점선
                  </Box>
                </ToggleButton>
              </ToggleButtonGroup>
            </Box>
            
            {/* 배경색 (사각형용) */}
            {(() => {
              const editingDrawing = drawings.find(d => d.id === editingDrawingId);
              if (editingDrawing?.type === 'rectangle' || editingDrawing?.type === 'measure') {
                return (
                  <>
                    <Box>
                      <Typography variant="body2" fontWeight="bold" sx={{ mb: 1 }}>
                        배경 색상
                      </Typography>
                      <Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
                        {['rgba(41, 98, 255, 0.2)', 'rgba(239, 83, 80, 0.2)', 'rgba(38, 166, 154, 0.2)', 'rgba(255, 152, 0, 0.2)', 'rgba(156, 39, 176, 0.2)', 'transparent'].map(color => (
                          <Box
                            key={color}
                            onClick={() => setEditingStyle(prev => ({ ...prev, fillColor: color }))}
                            sx={{
                              width: 28,
                              height: 28,
                              borderRadius: 1,
                              bgcolor: color === 'transparent' ? 'transparent' : color,
                              cursor: 'pointer',
                              border: editingStyle.fillColor === color ? '3px solid #2962ff' : '2px solid #444',
                              display: 'flex',
                              alignItems: 'center',
                              justifyContent: 'center',
                              '&:hover': { transform: 'scale(1.1)' },
                              transition: 'all 0.15s',
                            }}
                          >
                            {color === 'transparent' && (
                              <Close fontSize="small" sx={{ color: '#888' }} />
                            )}
                          </Box>
                        ))}
                      </Box>
                    </Box>
                    
                    <Box>
                      <Typography variant="body2" fontWeight="bold" sx={{ mb: 1 }}>
                        배경 투명도: {Math.round(editingStyle.fillOpacity * 100)}%
                      </Typography>
                      <Slider
                        value={editingStyle.fillOpacity}
                        onChange={(_, value) => setEditingStyle(prev => ({ ...prev, fillOpacity: value as number }))}
                        min={0}
                        max={1}
                        step={0.1}
                        sx={{ width: '100%' }}
                      />
                    </Box>
                  </>
                );
              }
              return null;
            })()}
            
            {/* 미리보기 */}
            <Box sx={{ mt: 1, p: 2, bgcolor: 'action.hover', borderRadius: 1, textAlign: 'center' }}>
              <Typography variant="caption" color="text.secondary" sx={{ mb: 1, display: 'block' }}>
                미리보기
              </Typography>
              <svg width="100%" height="40">
                <line
                  x1="20"
                  y1="20"
                  x2="200"
                  y2="20"
                  stroke={editingStyle.color}
                  strokeWidth={editingStyle.strokeWidth}
                  strokeDasharray={
                    editingStyle.strokeStyle === 'dashed' ? '8,4' :
                    editingStyle.strokeStyle === 'dotted' ? '2,4' : 'none'
                  }
                />
              </svg>
            </Box>
          </Box>
        </DialogContent>
        <DialogActions sx={{ px: 3, pb: 2, gap: 1 }}>
          <Button
            onClick={() => editingDrawingId && deleteDrawing(editingDrawingId)}
            color="error"
            variant="outlined"
          >
            삭제
          </Button>
          <Box sx={{ flex: 1 }} />
          <Button onClick={() => setStyleEditDialogOpen(false)} color="inherit">
            취소
          </Button>
          <Button onClick={applyDrawingStyle} variant="contained" color="primary">
            적용
          </Button>
        </DialogActions>
      </Dialog>
      
      {/* 전략 체크 팝업 */}
      {strategyCheckPopupOpen && (
        <Paper
          elevation={8}
          sx={{
            position: 'fixed',
            left: strategyCheckPosition.x,
            top: strategyCheckPosition.y,
            width: strategyPopupSize.width,
            height: strategyPopupSize.height,
            zIndex: 1500,
            bgcolor: `rgba(${theme.palette.mode === 'dark' ? '30, 30, 46' : '255, 255, 255'}, ${1 - strategyCheckTransparency / 100})`,
            backdropFilter: strategyCheckTransparency < 50 ? 'blur(8px)' : 'none',
            borderRadius: 2,
            border: '2px solid',
            borderColor: 'primary.main',
            minWidth: 300,
            minHeight: 200,
            overflow: 'hidden',
            display: 'flex',
            flexDirection: 'column',
          }}
          onMouseDown={(e) => {
            // 헤더 영역에서만 드래그 시작 (슬라이더, 체크박스, 버튼 영역 제외)
            const target = e.target as HTMLElement;
            const isInteractiveElement = target.closest('.MuiSlider-root') || 
                                         target.closest('.MuiCheckbox-root') ||
                                         target.closest('.MuiIconButton-root') ||
                                         target.closest('input');
            if (target.closest('.strategy-popup-header') && !isInteractiveElement) {
              e.preventDefault();
              setStrategyCheckDragging(true);
              setStrategyCheckDragStart({
                mouseX: e.clientX,
                mouseY: e.clientY,
                posX: strategyCheckPosition.x,
                posY: strategyCheckPosition.y,
              });
            }
          }}
          onTouchStart={(e) => {
            const target = e.target as HTMLElement;
            const isInteractiveElement = target.closest('.MuiSlider-root') || 
                                         target.closest('.MuiCheckbox-root') ||
                                         target.closest('.MuiIconButton-root') ||
                                         target.closest('input');
            if (target.closest('.strategy-popup-header') && !isInteractiveElement && e.touches.length === 1) {
              const touch = e.touches[0];
              setStrategyCheckDragging(true);
              setStrategyCheckDragStart({
                mouseX: touch.clientX,
                mouseY: touch.clientY,
                posX: strategyCheckPosition.x,
                posY: strategyCheckPosition.y,
              });
            }
          }}
        >
          {/* 헤더 */}
          <Box
            className="strategy-popup-header"
            sx={{
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'space-between',
              px: 2,
              py: 1,
              borderBottom: 1,
              borderColor: 'divider',
              bgcolor: `rgba(${theme.palette.mode === 'dark' ? '40, 40, 56' : '245, 245, 245'}, ${Math.max(1 - strategyCheckTransparency / 100, 0.5)})`,
              cursor: 'move',
              userSelect: 'none',
            }}
          >
            <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
              <FactCheck sx={{ fontSize: 20, color: 'primary.main' }} />
              <Typography variant="subtitle2" fontWeight="bold">
                전략 체크
              </Typography>
              {workerReferenceTime && (
                <Chip 
                  label={workerReferenceTime} 
                  size="small" 
                  sx={{ 
                    fontSize: '10px', 
                    height: 20,
                    bgcolor: 'primary.dark',
                    color: 'primary.contrastText',
                  }} 
                />
              )}
            </Box>
            
            <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
              {/* 현재시점 체크박스 */}
              <FormControlLabel
                control={
                  <Checkbox
                    size="small"
                    checked={strategyCheckCurrentTime}
                    onChange={(e) => setStrategyCheckCurrentTime(e.target.checked)}
                    sx={{ p: 0.5 }}
                  />
                }
                label={<Typography variant="caption">현재시점</Typography>}
                sx={{ mr: 2, ml: 0 }}
              />
              
              {/* 투명도 조절 */}
              <Opacity sx={{ fontSize: 16, color: 'text.secondary' }} />
              <Slider
                value={strategyCheckTransparency}
                onChange={(_, value) => setStrategyCheckTransparency(value as number)}
                min={0}
                max={80}
                step={5}
                size="small"
                sx={{ width: 80 }}
              />
              <Typography variant="caption" sx={{ color: 'text.secondary', minWidth: 35 }}>
                {strategyCheckTransparency}%
              </Typography>
              
              {/* 닫기 버튼 */}
              <IconButton
                size="small"
                onClick={() => setStrategyCheckPopupOpen(false)}
                sx={{ ml: 1 }}
              >
                <Close fontSize="small" />
              </IconButton>
            </Box>
          </Box>
          
          {/* 전략 목록 - 마우스 이벤트를 차트로 통과시킴 */}
          <Box sx={{ p: 2, flex: 1, overflowY: 'auto', overflowX: 'hidden', pointerEvents: 'none' }}>
            {isLoadingStrategyData ? (
              <Box sx={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', height: '100%', gap: 2, pointerEvents: 'auto' }}>
                <CircularProgress size={32} />
                <Typography variant="body2" color="text.secondary">
                  전략 분석용 데이터 로드 중...
                </Typography>
                <Typography variant="caption" color="text.secondary">
                  보조지표 계산에 필요한 충분한 데이터를 가져오고 있습니다.
                </Typography>
              </Box>
            ) : strategyRules.length === 0 ? (
              <Typography variant="body2" color="text.secondary" textAlign="center" sx={{ pointerEvents: 'auto' }}>
                등록된 전략 규칙이 없습니다.
              </Typography>
            ) : isEvaluating && workerEvaluationResults.length === 0 ? (
              <Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100%', pointerEvents: 'auto' }}>
                <CircularProgress size={24} />
                <Typography variant="body2" color="text.secondary" sx={{ ml: 1 }}>
                  전략 평가 중...
                </Typography>
              </Box>
            ) : (
              <Box 
                sx={{ 
                  display: 'grid', 
                  gridTemplateColumns: `repeat(auto-fill, minmax(180px, 1fr))`,
                  gap: 1.5,
                  justifyItems: 'stretch',
                  pointerEvents: 'auto', // 그리드 영역은 상호작용 가능
                }}
              >
                {workerEvaluationResults.map((result) => {
                  // Worker에서 받은 평가 결과 사용
                  const evaluation = result.evaluation;
                  const rule = strategyRules.find(r => r.id === result.strategyId) || { 
                    id: result.strategyId, 
                    name: result.strategyName, 
                    enabled: result.enabled,
                    totalConditionCount: 0,
                    conditionSummary: ''
                  };
                  
                  const buyPercent = evaluation.buy.percentage;
                  const sellPercent = evaluation.sell.percentage;
                  
                  // 신호등 색상 결정 (100%: 파란, 50%+: 주황, 50%-: 빨강)
                  const getSignalColor = (percent: number) => {
                    if (percent === 100) return '#2196f3'; // 파란색
                    if (percent >= 50) return '#ff9800'; // 주황색
                    return '#ef5350'; // 빨간색
                  };
                  
                  const getSignalGlow = (percent: number) => {
                    if (percent === 100) return '0 0 10px #2196f3';
                    if (percent >= 50) return '0 0 8px #ff9800';
                    return '0 0 6px #ef5350';
                  };
                  
                  // 백엔드에서 받은 조건 개수
                  const totalCount = rule.totalConditionCount || 0;
                  
                  return (
                    <Box
                      key={rule.id || rule.name}
                      sx={{
                        display: 'flex',
                        flexDirection: 'column',
                        p: 1,
                        borderRadius: 1.5,
                        bgcolor: 'action.hover',
                        border: 1,
                        borderColor: 'divider',
                        minWidth: 0,
                      }}
                    >
                      {/* 전략 이름 */}
                      <Typography
                        variant="caption"
                        fontWeight="bold"
                        sx={{
                          mb: 1.5,
                          textAlign: 'center',
                          fontSize: '11px',
                          overflow: 'hidden',
                          textOverflow: 'ellipsis',
                          whiteSpace: 'nowrap',
                        }}
                      >
                        {rule.name}
                      </Typography>
                      
                      {/* 매수/매도 섹션 */}
                      <Box sx={{ display: 'flex', gap: 1.5, justifyContent: 'center' }}>
                        {/* 매수 섹션 */}
                        {evaluation.buy.total > 0 && (
                          <Box sx={{ 
                            display: 'flex', 
                            flexDirection: 'column', 
                            alignItems: 'center',
                            p: 1,
                            borderRadius: 1,
                            bgcolor: 'rgba(33, 150, 243, 0.2)',
                            border: '1px solid rgba(33, 150, 243, 0.5)',
                            flex: 1,
                            boxShadow: 'inset 0 0 10px rgba(33, 150, 243, 0.15)',
                          }}>
                            <Typography variant="caption" sx={{ color: '#42a5f5', fontWeight: 'bold', mb: 0.5, textShadow: '0 0 4px rgba(33, 150, 243, 0.5)' }}>
                              매수
                            </Typography>
                            
                            {/* 신호등 */}
                            <Box
                              sx={{
                                width: 24,
                                height: 24,
                                borderRadius: '50%',
                                bgcolor: getSignalColor(buyPercent),
                                boxShadow: getSignalGlow(buyPercent),
                                mb: 0.5,
                                transition: 'all 0.3s',
                              }}
                            />
                            
                            {/* 비율 표시 */}
                            <Typography
                              variant="caption"
                              fontWeight="bold"
                              sx={{ color: getSignalColor(buyPercent), fontSize: '12px' }}
                            >
                              {buyPercent}%
                            </Typography>
                            
                            {/* 조건 갯수 */}
                            <Typography variant="caption" sx={{ color: 'text.secondary', fontSize: '9px' }}>
                              {evaluation.buy.matched}/{evaluation.buy.total}
                            </Typography>
                            
                            {/* 매수 조건 상세 - 파란색 계열 */}
                            <Box sx={{ mt: 0.5, width: '100%' }}>
                              {evaluation.buy.conditions.slice(0, 5).map((cond, idx) => (
                                <Box
                                  key={idx}
                                  sx={{
                                    display: 'flex',
                                    alignItems: 'center',
                                    gap: 0.5,
                                    py: 0.25,
                                  }}
                                >
                                  <Box
                                    sx={{
                                      width: 6,
                                      height: 6,
                                      borderRadius: '50%',
                                      bgcolor: cond.matched ? '#2196f3' : '#64b5f6',
                                      opacity: cond.matched ? 1 : 0.5,
                                      flexShrink: 0,
                                    }}
                                  />
                                  <Typography
                                    variant="caption"
                                    sx={{
                                      fontSize: '8px',
                                      color: cond.matched ? '#2196f3' : '#90caf9',
                                      fontWeight: cond.matched ? 600 : 400,
                                      overflow: 'hidden',
                                      textOverflow: 'ellipsis',
                                      whiteSpace: 'nowrap',
                                    }}
                                  >
                                    {cond.description}
                                  </Typography>
                                </Box>
                              ))}
                              {evaluation.buy.conditions.length > 5 && (
                                <Typography variant="caption" sx={{ fontSize: '8px', color: '#64b5f6' }}>
                                  +{evaluation.buy.conditions.length - 5}개 더...
                                </Typography>
                              )}
                            </Box>
                          </Box>
                        )}
                        
                        {/* 매도 섹션 */}
                        {evaluation.sell.total > 0 && (
                          <Box sx={{ 
                            display: 'flex', 
                            flexDirection: 'column', 
                            alignItems: 'center',
                            p: 1,
                            borderRadius: 1,
                            bgcolor: 'rgba(255, 87, 34, 0.2)',
                            border: '1px solid rgba(255, 87, 34, 0.5)',
                            flex: 1,
                            boxShadow: 'inset 0 0 10px rgba(255, 87, 34, 0.15)',
                          }}>
                            <Typography variant="caption" sx={{ color: '#ff7043', fontWeight: 'bold', mb: 0.5, textShadow: '0 0 4px rgba(255, 87, 34, 0.5)' }}>
                              매도
                            </Typography>
                            
                            {/* 신호등 */}
                            <Box
                              sx={{
                                width: 24,
                                height: 24,
                                borderRadius: '50%',
                                bgcolor: getSignalColor(sellPercent),
                                boxShadow: getSignalGlow(sellPercent),
                                mb: 0.5,
                                transition: 'all 0.3s',
                              }}
                            />
                            
                            {/* 비율 표시 */}
                            <Typography
                              variant="caption"
                              fontWeight="bold"
                              sx={{ color: getSignalColor(sellPercent), fontSize: '12px' }}
                            >
                              {sellPercent}%
                            </Typography>
                            
                            {/* 조건 갯수 */}
                            <Typography variant="caption" sx={{ color: 'text.secondary', fontSize: '9px' }}>
                              {evaluation.sell.matched}/{evaluation.sell.total}
                            </Typography>
                            
                            {/* 매도 조건 상세 - 빨간색/주황색 계열 */}
                            <Box sx={{ mt: 0.5, width: '100%' }}>
                              {evaluation.sell.conditions.slice(0, 5).map((cond, idx) => (
                                <Box
                                  key={idx}
                                  sx={{
                                    display: 'flex',
                                    alignItems: 'center',
                                    gap: 0.5,
                                    py: 0.25,
                                  }}
                                >
                                  <Box
                                    sx={{
                                      width: 6,
                                      height: 6,
                                      borderRadius: '50%',
                                      bgcolor: cond.matched ? '#ff5722' : '#ffab91',
                                      opacity: cond.matched ? 1 : 0.5,
                                      flexShrink: 0,
                                    }}
                                  />
                                  <Typography
                                    variant="caption"
                                    sx={{
                                      fontSize: '8px',
                                      color: cond.matched ? '#ff5722' : '#ffab91',
                                      fontWeight: cond.matched ? 600 : 400,
                                      overflow: 'hidden',
                                      textOverflow: 'ellipsis',
                                      whiteSpace: 'nowrap',
                                    }}
                                  >
                                    {cond.description}
                                  </Typography>
                                </Box>
                              ))}
                              {evaluation.sell.conditions.length > 5 && (
                                <Typography variant="caption" sx={{ fontSize: '8px', color: '#ffab91' }}>
                                  +{evaluation.sell.conditions.length - 5}개 더...
                                </Typography>
                              )}
                            </Box>
                          </Box>
                        )}
                        
                        {/* 조건이 없는 경우 - 백엔드 정보로 대체 표시 */}
                        {evaluation.buy.total === 0 && evaluation.sell.total === 0 && totalCount > 0 && (
                          <Box sx={{ 
                            display: 'flex', 
                            flexDirection: 'column', 
                            alignItems: 'center',
                            p: 1,
                            borderRadius: 1,
                            bgcolor: 'rgba(156, 39, 176, 0.1)',
                            border: '1px solid rgba(156, 39, 176, 0.3)',
                            flex: 1,
                          }}>
                            <Typography variant="caption" sx={{ color: '#9c27b0', fontWeight: 'bold', mb: 0.5 }}>
                              조건 {totalCount}개
                            </Typography>
                            
                            {/* 신호등 - 데이터 없음 상태 */}
                            <Box
                              sx={{
                                width: 24,
                                height: 24,
                                borderRadius: '50%',
                                bgcolor: '#757575',
                                mb: 0.5,
                              }}
                            />
                            
                            {rule.conditionSummary && (
                              <Typography variant="caption" sx={{ color: 'text.secondary', fontSize: '9px', textAlign: 'center' }}>
                                {rule.conditionSummary}
                              </Typography>
                            )}
                            
                            <Typography variant="caption" sx={{ color: 'warning.main', fontSize: '8px', mt: 0.5 }}>
                              차트 데이터 필요
                            </Typography>
                          </Box>
                        )}
                        
                        {/* 조건도 없고 totalCount도 0인 경우 */}
                        {evaluation.buy.total === 0 && evaluation.sell.total === 0 && totalCount === 0 && (
                          <Typography variant="caption" color="text.secondary" sx={{ py: 2 }}>
                            조건 없음
                          </Typography>
                        )}
                      </Box>
                    </Box>
                  );
                })}
              </Box>
            )}
            
            {/* 현재 기준 시점 표시 */}
            <Box sx={{ mt: 2, textAlign: 'center', p: 1, bgcolor: 'action.selected', borderRadius: 1 }}>
              <Typography variant="caption" color="text.secondary">
                기준 시점:{' '}
                <Typography component="span" variant="caption" fontWeight="bold" color="primary.main">
                  {workerReferenceTime || '-'}
                </Typography>
                {isEvaluating && (
                  <CircularProgress size={10} sx={{ ml: 1, verticalAlign: 'middle' }} />
                )}
              </Typography>
              <Typography variant="caption" display="block" color="text.secondary" sx={{ mt: 0.5 }}>
                데이터: {strategyCheckChartData.length}개
                {strategyDataLoadError && (
                  <Typography component="span" variant="caption" color="warning.main" sx={{ ml: 1 }}>
                    ({strategyDataLoadError})
                  </Typography>
                )}
              </Typography>
              {!strategyCheckCurrentTime && (
                <Typography variant="caption" color="warning.main" sx={{ display: 'block', mt: 0.5, fontSize: '9px' }}>
                  마우스 위치: {globalHoverIndex !== null ? `인덱스 ${globalHoverIndex}` : '없음'}
                </Typography>
              )}
            </Box>
            
            {/* 안내 문구 */}
            <Typography
              variant="caption"
              color="text.secondary"
              sx={{ display: 'block', mt: 1, textAlign: 'center' }}
            >
              {strategyCheckCurrentTime 
                ? '현재시점 모드: 최신 캔들 기준으로 자동 갱신됩니다'
                : '차트에 마우스/터치로 이동하면 해당 지점의 전략 일치율이 갱신됩니다'}
            </Typography>
          </Box>
          
          {/* 리사이즈 핸들 (우하단) */}
          <Box
            className="strategy-popup-resize-handle"
            sx={{
              position: 'absolute',
              right: 0,
              bottom: 0,
              width: 20,
              height: 20,
              cursor: 'nwse-resize',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
              '&::before': {
                content: '""',
                width: 10,
                height: 10,
                borderRight: '2px solid',
                borderBottom: '2px solid',
                borderColor: 'text.secondary',
                opacity: 0.5,
              },
              '&:hover::before': {
                opacity: 1,
              },
            }}
            onMouseDown={(e) => {
              e.stopPropagation();
              setStrategyPopupResizing(true);
              setStrategyPopupResizeStart({
                mouseX: e.clientX,
                mouseY: e.clientY,
                width: strategyPopupSize.width,
                height: strategyPopupSize.height,
              });
            }}
            onTouchStart={(e) => {
              if (e.touches.length === 1) {
                e.stopPropagation();
                const touch = e.touches[0];
                setStrategyPopupResizing(true);
                setStrategyPopupResizeStart({
                  mouseX: touch.clientX,
                  mouseY: touch.clientY,
                  width: strategyPopupSize.width,
                  height: strategyPopupSize.height,
                });
              }
            }}
          />
        </Paper>
      )}
      
      {/* AI 테스트 결과 팝업 */}
      <Dialog
        open={aiTestPopupOpen}
        onClose={() => setAiTestPopupOpen(false)}
        maxWidth="md"
        fullWidth
        PaperProps={{
          sx: {
            bgcolor: theme.palette.mode === 'dark' ? '#1e1e2e' : '#ffffff',
            backgroundImage: 'none',
            maxHeight: '80vh',
          }
        }}
      >
        <DialogTitle sx={{ 
          display: 'flex', 
          alignItems: 'center', 
          gap: 1,
          bgcolor: theme.palette.mode === 'dark' ? '#2d2d44' : '#f5f5f5',
          borderBottom: 1,
          borderColor: 'divider',
        }}>
          <Psychology sx={{ color: '#9c27b0' }} />
          <Typography variant="h6" sx={{ flexGrow: 1 }}>
            AI 차트 분석 결과
          </Typography>
          <Chip 
            label={`${title || symbolProp || 'Unknown'}`} 
            size="small" 
            color="primary" 
            variant="outlined"
          />
        </DialogTitle>
        <DialogContent sx={{ p: 0 }}>
          {aiTestLoading ? (
            <Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', py: 8 }}>
              <CircularProgress size={48} sx={{ color: '#9c27b0', mb: 2 }} />
              <Typography variant="body1" color="text.secondary">
                AI가 차트를 분석하고 있습니다...
              </Typography>
              <Typography variant="caption" color="text.secondary" sx={{ mt: 1 }}>
                Qwen3 4B 모델로 분석 중
              </Typography>
            </Box>
          ) : aiTestError ? (
            <Box sx={{ p: 3 }}>
              <Box sx={{ 
                p: 2, 
                bgcolor: 'error.dark', 
                borderRadius: 1,
                border: '1px solid',
                borderColor: 'error.main',
              }}>
                <Typography variant="body1" color="error.contrastText" fontWeight="bold" gutterBottom>
                  오류 발생
                </Typography>
                <Typography variant="body2" color="error.contrastText">
                  {aiTestError}
                </Typography>
              </Box>
              <Typography variant="caption" color="text.secondary" sx={{ display: 'block', mt: 2 }}>
                💡 로컬 LLM 서버(Ollama)가 실행 중인지 확인하세요.
                <br />
                실행 명령: <code>ollama run qwen3:4b</code>
              </Typography>
            </Box>
          ) : (
            <Box sx={{ p: 3 }}>
              <Box sx={{ 
                p: 2, 
                bgcolor: theme.palette.mode === 'dark' ? 'rgba(156, 39, 176, 0.1)' : 'rgba(156, 39, 176, 0.05)',
                borderRadius: 1,
                border: '1px solid',
                borderColor: 'rgba(156, 39, 176, 0.3)',
                maxHeight: '50vh',
                overflow: 'auto',
              }}>
                <Typography 
                  variant="body2" 
                  sx={{ 
                    whiteSpace: 'pre-wrap',
                    lineHeight: 1.8,
                    '& strong': { color: '#9c27b0' },
                  }}
                >
                  {aiTestResult || '분석 결과가 없습니다.'}
                </Typography>
              </Box>
            </Box>
          )}
        </DialogContent>
        <DialogActions sx={{ 
          p: 2, 
          bgcolor: theme.palette.mode === 'dark' ? '#2d2d44' : '#f5f5f5',
          borderTop: 1,
          borderColor: 'divider',
        }}>
          <Button 
            onClick={handleAiTest} 
            disabled={aiTestLoading}
            startIcon={<Psychology />}
            variant="outlined"
            sx={{ borderColor: '#9c27b0', color: '#9c27b0' }}
          >
            다시 분석
          </Button>
          <Button 
            onClick={() => setAiTestPopupOpen(false)} 
            variant="contained"
            sx={{ bgcolor: '#9c27b0', '&:hover': { bgcolor: '#7b1fa2' } }}
          >
            닫기
          </Button>
        </DialogActions>
      </Dialog>

      {/* 돋보기 기능 */}
      {magnifierActive && magnifierPos && (
        <Box
          sx={{
            position: 'fixed',
            left: magnifierPos.x - 200,
            top: magnifierPos.y - 200,
            width: 400,
            height: 400,
            borderRadius: '50%',
            border: '4px solid',
            borderColor: 'primary.main',
            boxShadow: '0 8px 32px rgba(0,0,0,0.3)',
            overflow: 'hidden',
            pointerEvents: 'none',
            zIndex: 9999,
            bgcolor: theme.palette.mode === 'dark' ? 'rgba(30, 30, 30, 0.95)' : 'rgba(255, 255, 255, 0.95)',
          }}
        >
          <Box
            sx={{
              width: '200%',
              height: '200%',
              transform: 'scale(2)',
              transformOrigin: '50% 50%',
              position: 'absolute',
              left: '-50%',
              top: '-50%',
            }}
          >
            {/* 돋보기 내부 안내 텍스트 */}
            <Box
              sx={{
                position: 'absolute',
                top: '50%',
                left: '50%',
                transform: 'translate(-50%, -50%) scale(0.5)',
                textAlign: 'center',
                color: 'text.secondary',
                fontSize: '16px',
                fontWeight: 600,
                zIndex: 10000,
              }}
            >
              <Box>🔍</Box>
              <Box sx={{ mt: 1 }}>2배 확대</Box>
            </Box>
          </Box>
        </Box>
      )}
    </Box>
  );
};

// React.memo로 감싸서 불필요한 리렌더링 방지
export default React.memo(MacdChart, (prevProps, nextProps) => {
  // externalHoverTime이 변경되면 반드시 리렌더링 (크로스헤어 동기화)
  if (prevProps.externalHoverTime !== nextProps.externalHoverTime) {
    return false; // 리렌더링 필요
  }
  
  // externalViewRange가 변경되면 리렌더링
  if (JSON.stringify(prevProps.externalViewRange) !== JSON.stringify(nextProps.externalViewRange)) {
    return false;
  }
  
  // 데이터 길이와 마지막 항목의 timestamp를 비교하여 변경 여부 판단
  if (prevProps.data.length !== nextProps.data.length) {
    return false; // 길이가 다르면 리렌더링
  }
  
  const prevLast = prevProps.data[prevProps.data.length - 1];
  const nextLast = nextProps.data[nextProps.data.length - 1];
  
  // 마지막 데이터의 timestamp와 close 값이 같으면 리렌더링 스킵
  if (prevLast?.timestamp === nextLast?.timestamp && 
      prevLast?.close === nextLast?.close) {
    return true; // 동일하면 리렌더링 스킵
  }
  
  // interval이 변경되면 리렌더링
  if (prevProps.interval !== nextProps.interval) {
    return false;
  }
  
  return false; // 기본적으로 리렌더링
});
