전략평가 전략목록 수정
This commit is contained in:
@@ -94,6 +94,7 @@ import {
|
||||
} from '../utils/strategyTemplateList';
|
||||
import type { TemplatePaletteRow } from './strategyEditor/TemplatePaletteTab';
|
||||
import { buildStrategyEditorDefFromSettings } from '../utils/strategyEditorShared';
|
||||
import { isEvaluationTemplateStrategy } from '../utils/strategyEvaluationTemplateApply';
|
||||
|
||||
const LEFT_KEY = 'seval-left-width';
|
||||
const LEFT_OPEN_KEY = 'seval-left-open';
|
||||
@@ -261,6 +262,21 @@ function StrategyEvaluationPageInner({ theme = 'dark' }: Props) {
|
||||
[templateRows, templateSearch],
|
||||
);
|
||||
|
||||
/** 전략 탭·일괄평가용 — `[템플릿]` 접두 전략 제외 (템플릿 탭 전용) */
|
||||
const listStrategies = useMemo(
|
||||
() => strategies.filter(s => !isEvaluationTemplateStrategy(s.name)),
|
||||
[strategies],
|
||||
);
|
||||
|
||||
/** 차트 전략 선택 — 목록 제외, 현재 선택된 템플릿 평가 전략만 예외 표시 */
|
||||
const chartStrategies = useMemo(() => {
|
||||
if (!selectedStrategy?.id || !isEvaluationTemplateStrategy(selectedStrategy.name)) {
|
||||
return listStrategies;
|
||||
}
|
||||
if (listStrategies.some(s => s.id === selectedStrategy.id)) return listStrategies;
|
||||
return [selectedStrategy, ...listStrategies];
|
||||
}, [listStrategies, selectedStrategy]);
|
||||
|
||||
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));
|
||||
@@ -443,12 +459,12 @@ function StrategyEvaluationPageInner({ theme = 'dark' }: Props) {
|
||||
|
||||
const filteredStrategies = useMemo(() => {
|
||||
const q = strategySearch.trim().toLowerCase();
|
||||
if (!q) return strategies;
|
||||
return strategies.filter(s =>
|
||||
if (!q) return listStrategies;
|
||||
return listStrategies.filter(s =>
|
||||
(s.name ?? '').toLowerCase().includes(q)
|
||||
|| String(s.id ?? '').includes(q),
|
||||
);
|
||||
}, [strategies, strategySearch]);
|
||||
}, [listStrategies, strategySearch]);
|
||||
|
||||
const sortedFilteredStrategies = useMemo(
|
||||
() => sortStrategies(filteredStrategies, strategyListSort),
|
||||
@@ -533,7 +549,7 @@ function StrategyEvaluationPageInner({ theme = 'dark' }: Props) {
|
||||
]);
|
||||
|
||||
const bulkEval = useStrategyBulkEvaluation({
|
||||
strategies,
|
||||
strategies: listStrategies,
|
||||
market,
|
||||
timeframe: chartTimeframe,
|
||||
bars,
|
||||
@@ -547,7 +563,7 @@ function StrategyEvaluationPageInner({ theme = 'dark' }: Props) {
|
||||
|
||||
const autoBulkEvalTriggerKey = useMemo(() => {
|
||||
const lastBarTime = bars[bars.length - 1]?.time ?? 0;
|
||||
const strategyIds = strategies.map(s => s.id).filter(Boolean).join(',');
|
||||
const strategyIds = listStrategies.map(s => s.id).filter(Boolean).join(',');
|
||||
return [
|
||||
market,
|
||||
chartTimeframe,
|
||||
@@ -567,7 +583,7 @@ function StrategyEvaluationPageInner({ theme = 'dark' }: Props) {
|
||||
evalWindowMeta.evaluationBarCount,
|
||||
bars,
|
||||
paramsRevision,
|
||||
strategies,
|
||||
listStrategies,
|
||||
]);
|
||||
|
||||
const lastAutoBulkEvalKeyRef = useRef<string | null>(null);
|
||||
@@ -581,7 +597,7 @@ function StrategyEvaluationPageInner({ theme = 'dark' }: Props) {
|
||||
useEffect(() => {
|
||||
const waitingForSelectedSignals = selectedStrategyId != null && signalScanRunning;
|
||||
if (chartLoading || waitingForSelectedSignals) return;
|
||||
if (bars.length < 10 || !strategies.some(s => s.id != null)) return;
|
||||
if (bars.length < 10 || !listStrategies.some(s => s.id != null)) return;
|
||||
|
||||
const key = autoBulkEvalTriggerKey;
|
||||
if (lastAutoBulkEvalKeyRef.current === key) return;
|
||||
@@ -594,7 +610,7 @@ function StrategyEvaluationPageInner({ theme = 'dark' }: Props) {
|
||||
signalScanRunning,
|
||||
selectedStrategyId,
|
||||
bars.length,
|
||||
strategies,
|
||||
listStrategies,
|
||||
bulkEval.startBulkEvaluation,
|
||||
]);
|
||||
|
||||
@@ -1128,7 +1144,7 @@ function StrategyEvaluationPageInner({ theme = 'dark' }: Props) {
|
||||
timeframe={chartTimeframe}
|
||||
timeframeChoice={timeframeChoice}
|
||||
tfOptions={tfOptions}
|
||||
strategies={strategies}
|
||||
strategies={chartStrategies}
|
||||
selectedStrategyId={selectedStrategyId}
|
||||
onStrategySelect={handleSelectStrategyById}
|
||||
onMarketChange={setMarket}
|
||||
|
||||
@@ -44,6 +44,12 @@ import {
|
||||
type StrategyFlowLayoutStore,
|
||||
} from './strategyEditorLayoutStorage';
|
||||
|
||||
export const EVALUATION_TEMPLATE_STRATEGY_PREFIX = '[템플릿]';
|
||||
|
||||
export function isEvaluationTemplateStrategy(name: string | null | undefined): boolean {
|
||||
return (name ?? '').trimStart().startsWith(EVALUATION_TEMPLATE_STRATEGY_PREFIX);
|
||||
}
|
||||
|
||||
export type TemplateEvaluationPayload = {
|
||||
buy: EditorConditionState;
|
||||
sell: EditorConditionState;
|
||||
@@ -118,7 +124,7 @@ function payloadForSide(
|
||||
sellOrphans,
|
||||
flowLayout: buildStrategyFlowLayoutStore(buyLayout, sellLayout, buyOrphans, sellOrphans),
|
||||
preferredTab: signal,
|
||||
strategyName: `[템플릿] ${row.label}`,
|
||||
strategyName: `${EVALUATION_TEMPLATE_STRATEGY_PREFIX} ${row.label}`,
|
||||
strategyDescription: row.description?.trim() || `전략 템플릿: ${row.label}`,
|
||||
};
|
||||
}
|
||||
@@ -168,7 +174,7 @@ export function suggestTemplateEvaluationStrategyName(
|
||||
baseLabel: string,
|
||||
existingNames: ReadonlySet<string>,
|
||||
): string {
|
||||
const base = `[템플릿] ${baseLabel.trim() || '새 전략'}`;
|
||||
const base = `${EVALUATION_TEMPLATE_STRATEGY_PREFIX} ${baseLabel.trim() || '새 전략'}`;
|
||||
if (!existingNames.has(base)) return base;
|
||||
let n = 2;
|
||||
while (existingNames.has(`${base} (${n})`)) n += 1;
|
||||
|
||||
Reference in New Issue
Block a user