98 lines
3.3 KiB
TypeScript
98 lines
3.3 KiB
TypeScript
/** 전략편집기 — 사용자 저장 템플릿 (DB ui_preferences) */
|
|
import type { LogicNode } from './strategyTypes';
|
|
import type { EditorConditionState } from './strategyConditionSerde';
|
|
import { getUiPreferences, patchUiPreferences } from './uiPreferencesDb';
|
|
|
|
export type UserStrategyTemplate = {
|
|
id: string;
|
|
label: string;
|
|
description?: string;
|
|
signal: 'buy' | 'sell';
|
|
editorState: EditorConditionState;
|
|
orphans: LogicNode[];
|
|
createdAt: number;
|
|
};
|
|
|
|
function cloneJson<T>(value: T): T {
|
|
return JSON.parse(JSON.stringify(value)) as T;
|
|
}
|
|
|
|
export function createUserTemplateId(): string {
|
|
return `tmpl-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
}
|
|
|
|
function parseStored(raw: unknown): UserStrategyTemplate[] {
|
|
if (!Array.isArray(raw)) return [];
|
|
return raw.filter(
|
|
(t): t is UserStrategyTemplate =>
|
|
t != null
|
|
&& typeof t === 'object'
|
|
&& typeof (t as UserStrategyTemplate).id === 'string'
|
|
&& typeof (t as UserStrategyTemplate).label === 'string'
|
|
&& ((t as UserStrategyTemplate).signal === 'buy' || (t as UserStrategyTemplate).signal === 'sell')
|
|
&& typeof (t as UserStrategyTemplate).editorState === 'object'
|
|
&& Array.isArray((t as UserStrategyTemplate).orphans),
|
|
);
|
|
}
|
|
|
|
export function loadUserStrategyTemplates(): UserStrategyTemplate[] {
|
|
const raw = getUiPreferences().strategyEditor?.userTemplates;
|
|
return parseStored(raw);
|
|
}
|
|
|
|
export function saveUserStrategyTemplates(templates: UserStrategyTemplate[]): void {
|
|
patchUiPreferences({ strategyEditor: { userTemplates: templates } });
|
|
}
|
|
|
|
export function addUserStrategyTemplate(
|
|
input: Omit<UserStrategyTemplate, 'id' | 'createdAt'>,
|
|
): UserStrategyTemplate {
|
|
const next: UserStrategyTemplate = {
|
|
...input,
|
|
id: createUserTemplateId(),
|
|
editorState: cloneJson(input.editorState),
|
|
orphans: cloneJson(input.orphans),
|
|
createdAt: Date.now(),
|
|
};
|
|
const templates = [...loadUserStrategyTemplates(), next];
|
|
saveUserStrategyTemplates(templates);
|
|
return next;
|
|
}
|
|
|
|
export function deleteUserStrategyTemplate(id: string): UserStrategyTemplate[] {
|
|
const templates = loadUserStrategyTemplates().filter(t => t.id !== id);
|
|
saveUserStrategyTemplates(templates);
|
|
return templates;
|
|
}
|
|
|
|
export function updateUserStrategyTemplate(
|
|
id: string,
|
|
patch: Partial<Pick<UserStrategyTemplate, 'label' | 'description' | 'signal' | 'editorState' | 'orphans'>>,
|
|
): UserStrategyTemplate[] {
|
|
const templates = loadUserStrategyTemplates().map(t => {
|
|
if (t.id !== id) return t;
|
|
return {
|
|
...t,
|
|
...patch,
|
|
...(patch.editorState != null ? { editorState: cloneJson(patch.editorState) } : null),
|
|
...(patch.orphans != null ? { orphans: cloneJson(patch.orphans) } : null),
|
|
};
|
|
});
|
|
saveUserStrategyTemplates(templates);
|
|
return templates;
|
|
}
|
|
|
|
export function loadHiddenBuiltinTemplateIds(): string[] {
|
|
const raw = getUiPreferences().strategyEditor?.hiddenBuiltinTemplateIds;
|
|
if (!Array.isArray(raw)) return [];
|
|
return raw.filter((id): id is string => typeof id === 'string' && id.length > 0);
|
|
}
|
|
|
|
export function hideBuiltinTemplate(id: string): string[] {
|
|
const hidden = loadHiddenBuiltinTemplateIds();
|
|
if (hidden.includes(id)) return hidden;
|
|
const next = [...hidden, id];
|
|
patchUiPreferences({ strategyEditor: { hiddenBuiltinTemplateIds: next } });
|
|
return next;
|
|
}
|