실시간 차트 신고가,신저가 표시

This commit is contained in:
Macbook
2026-06-12 21:00:52 +09:00
parent d54dc6e2ff
commit 046bcc6379
15 changed files with 509 additions and 39 deletions
+37 -9
View File
@@ -52,6 +52,10 @@ import {
loadPaletteItems,
type PaletteItem,
} from '../utils/strategyPaletteStorage';
import {
buildCompositePaletteTemplateRows,
buildSidewaysPaletteTemplateRows,
} from '../utils/strategyEditorTemplatePaletteRows';
import {
buildPriceExtremeBreakoutTree,
isPriceExtremeBreakoutPairPaletteValue,
@@ -1307,22 +1311,22 @@ export default function StrategyEditorPage({ theme, onNavigateToBacktest }: Prop
const templates = useMemo(() => getStrategyTemplates(DEF), [DEF]);
const templateRows = useMemo(() => {
const userRows = userTemplates.map(t => ({
const userRows: TemplatePaletteRow[] = userTemplates.map(t => ({
key: t.id,
source: 'user' as const,
source: 'user',
label: t.label,
description: t.description,
signal: t.signal,
user: t,
}));
const hidden = new Set(hiddenBuiltinTemplateIds);
const builtinRows = templates
const builtinRows: TemplatePaletteRow[] = templates
.filter(t => !hidden.has(getBuiltinTemplateId(t)))
.map(t => {
const builtinId = getBuiltinTemplateId(t);
return {
key: builtinId,
source: 'builtin' as const,
source: 'builtin',
label: t.label,
description: 'description' in t ? t.description : undefined,
signal: t.signal,
@@ -1330,19 +1334,26 @@ export default function StrategyEditorPage({ theme, onNavigateToBacktest }: Prop
builtin: t,
};
});
return [...userRows, ...builtinRows];
}, [userTemplates, templates, hiddenBuiltinTemplateIds]);
const compositeRows: TemplatePaletteRow[] = buildCompositePaletteTemplateRows(compositePalette, hidden);
const sidewaysRows: TemplatePaletteRow[] = buildSidewaysPaletteTemplateRows(sidewaysPalette, hidden);
return [...userRows, ...builtinRows, ...compositeRows, ...sidewaysRows];
}, [userTemplates, templates, hiddenBuiltinTemplateIds, compositePalette, sidewaysPalette]);
const filteredTemplateRows = useMemo(() => {
const q = templateSearch.trim().toLowerCase();
if (!q) return templateRows;
return templateRows.filter(row => {
const signalLabel = row.signal === 'buy' ? '매수' : '매도';
const badge = row.source === 'user' ? '내 템플릿'
: row.source === 'composite-palette' ? '복합지표'
: row.source === 'sideways-palette' ? '횡보지표'
: '';
return (
row.label.toLowerCase().includes(q)
|| row.description?.toLowerCase().includes(q)
|| signalLabel.includes(q)
|| row.signal.includes(q)
|| badge.includes(q)
);
});
}, [templateRows, templateSearch]);
@@ -1468,14 +1479,31 @@ export default function StrategyEditorPage({ theme, onNavigateToBacktest }: Prop
handleDeleteUserTemplate(row.user.id, row.label);
} else if (row.source === 'builtin' && row.builtinId) {
handleDeleteBuiltinTemplate(row.builtinId, row.label);
} else if (row.source === 'composite-palette' || row.source === 'sideways-palette') {
handleDeleteBuiltinTemplate(row.key, row.label);
}
setSelectedTemplateKey(null);
}, [selectedTemplateKey, templateRows, handleDeleteUserTemplate, handleDeleteBuiltinTemplate]);
const handleTemplateApply = useCallback((row: TemplatePaletteRow) => {
if (row.source === 'user' && row.user) applyUserTemplate(row.user);
else if (row.builtin) handleTemplate(row.builtin);
}, [applyUserTemplate, handleTemplate]);
if (row.source === 'user' && row.user) {
applyUserTemplate(row.user);
return;
}
if (row.source === 'builtin' && row.builtin) {
handleTemplate(row.builtin);
return;
}
if (row.source === 'composite-palette' && row.paletteItem) {
applyPaletteItem(row.paletteItem);
showSnack(`"${row.label}" 조건이 추가됐습니다`);
return;
}
if (row.source === 'sideways-palette' && row.sidewaysFilterId) {
applySidewaysFilter(row.sidewaysFilterId);
showSnack(`"${row.label}" 필터가 추가됐습니다`);
}
}, [applyUserTemplate, handleTemplate, applyPaletteItem, applySidewaysFilter]);
const handleTemplateSaveConfirm = useCallback(() => {
const label = templateSaveLabel.trim();