diff --git a/frontend/src/components/StrategyEvaluationPage.tsx b/frontend/src/components/StrategyEvaluationPage.tsx
index 04bc16e..6fc76d7 100644
--- a/frontend/src/components/StrategyEvaluationPage.tsx
+++ b/frontend/src/components/StrategyEvaluationPage.tsx
@@ -23,7 +23,8 @@ import { resolveStrategyPrimaryTimeframe } from '../utils/strategyToChartIndicat
import { fetchEvaluationSnapshotAtBar } from '../utils/strategyEvaluationSnapshot';
import StrategyEvaluationSignalPanel from './strategyEvaluation/StrategyEvaluationSignalPanel';
import StrategyEvaluationChart from './strategyEvaluation/StrategyEvaluationChart';
-import StrategyEvaluationConditionPanel from './strategyEvaluation/StrategyEvaluationConditionPanel';
+import StrategyEvaluationStrategyPanel from './strategyEvaluation/StrategyEvaluationStrategyPanel';
+import StrategyEvaluationIndicatorPalettePanel from './strategyEvaluation/StrategyEvaluationIndicatorPalettePanel';
import StrategyEvaluationSettingsTab from './strategyEvaluation/StrategyEvaluationSettingsTab';
import StrategyEvaluationMarketTab from './strategyEvaluation/StrategyEvaluationMarketTab';
import StrategyEditorModal from './strategyEvaluation/StrategyEditorModal';
@@ -51,6 +52,9 @@ import { buildChartBacktestReportModel } from '../utils/backtestReportModel';
import { BACKTEST_DISPLAY_BAR_COUNT } from '../utils/backtestWarmup';
import StrategyEvaluationAiVerifyPanel from './strategyEvaluation/StrategyEvaluationAiVerifyPanel';
import StrategyEvaluationChartSettingsTab from './strategyEvaluation/StrategyEvaluationChartSettingsTab';
+import PaletteDragOverlay from './strategyEditor/PaletteDragOverlay';
+import { needsPointerPaletteDrag } from '../utils/paletteDragSession';
+import { useStrategyEvaluationEditor } from '../hooks/useStrategyEvaluationEditor';
import {
buildStrategyEvaluationAiVerifyContext,
requestStrategyEvaluationAiVerify,
@@ -66,7 +70,7 @@ const LEFT_DEFAULT = 380;
const RIGHT_DEFAULT = 440;
type LeftTab = 'strategy' | 'market' | 'settings';
-type RightTab = 'signal' | 'chart' | 'ai';
+type RightTab = 'signal' | 'indicators' | 'chart' | 'ai';
function readMinWidth(key: string, fallback: number): number {
return Math.max(readStoredSize(key, fallback), fallback);
@@ -139,6 +143,34 @@ export default function StrategyEvaluationPage({ theme = 'dark' }: Props) {
[baseGetParams, appliedIndicatorParams],
);
+ const handleStrategyEditorSaved = useCallback((saved: StrategyDto, meta?: { created?: boolean }) => {
+ const hydrated = hydrateStrategyDto(saved);
+ setSelectedStrategy(hydrated);
+ setStrategies(prev => {
+ const exists = prev.some(s => s.id === hydrated.id);
+ if (exists) return prev.map(s => (s.id === hydrated.id ? hydrated : s));
+ return [...prev, hydrated];
+ });
+ if (meta?.created && hydrated.id != null) {
+ setSelectedStrategyId(hydrated.id);
+ }
+ evalSessionRef.current += 1;
+ setSnapshot(undefined);
+ setEvalError(null);
+ setEvalLoading(true);
+ reportCacheKeyRef.current = null;
+ setReportModel(null);
+ }, []);
+
+ const strategyEditor = useStrategyEvaluationEditor({
+ strategy: selectedStrategy,
+ strategies,
+ getParams: baseGetParams,
+ getVisualConfig,
+ settingsRevision,
+ onStrategySaved: handleStrategyEditorSaved,
+ });
+
const [leftWidth, setLeftWidth] = useState(() => readMinWidth(LEFT_KEY, LEFT_DEFAULT));
const [leftOpen, setLeftOpen] = useState(() => readStoredBool(LEFT_OPEN_KEY, true));
const [rightWidth, setRightWidth] = useState(() => readMinWidth(RIGHT_KEY, RIGHT_DEFAULT));
@@ -784,10 +816,9 @@ export default function StrategyEvaluationPage({ theme = 'dark' }: Props) {
refreshMarketTickers={marketFeed.refreshAllTickers}
onMarketSearchOpenChange={setMarketSearchOpen}
/>
-
@@ -800,7 +831,13 @@ export default function StrategyEvaluationPage({ theme = 'dark' }: Props) {
side="right"
open={rightOpen}
onToggle={toggleRightPanel}
- label={rightTab === 'chart' ? '차트설정 패널' : '시그널 패널'}
+ label={
+ rightTab === 'chart'
+ ? '차트설정 패널'
+ : rightTab === 'indicators'
+ ? '전략지표 패널'
+ : '시그널 패널'
+ }
/>
{rightOpen && (
시그널
+