Files
goldenChart/frontend/src/utils/strategyStartNodes.ts
T
2026-06-12 21:32:19 +09:00

111 lines
3.3 KiB
TypeScript

import { generateNodeId } from './strategyTypes';
export const START_NODE_ID = '__strategy_start__';
export const DEFAULT_START_CANDLE = '1m';
/** 새 전략 START 노드 기본 평가 분봉 */
export const DEFAULT_NEW_STRATEGY_START_CANDLE = '3m';
export const STRATEGY_CANDLE_TYPES = [
'1m', '3m', '5m', '10m', '15m', '30m', '1h', '4h', '1w', '1d',
] as const;
/** 그래프 START 노드 — 시간봉 체크박스 2행 배치 */
export const START_TIMEFRAME_GRAPH_ROWS: readonly (readonly StrategyCandleType[])[] = [
['1m', '3m', '5m', '10m', '15m'],
['30m', '1h', '4h', '1w', '1d'],
] as const;
export type StrategyCandleType = (typeof STRATEGY_CANDLE_TYPES)[number];
export type StartNodeMeta = {
/** @deprecated 단일 분봉 — candleTypes 우선 */
candleType?: string;
/** 선택된 평가 시간봉 (마감봉마다 독립 체크) */
candleTypes?: string[];
};
/** START가 2개 이상일 때 분기 간 결합 연산 */
export type StartCombineOp = 'AND' | 'OR';
export const DEFAULT_START_COMBINE_OP: StartCombineOp = 'AND';
export function isStartNodeId(id: string): boolean {
return id === START_NODE_ID || id.startsWith('__strategy_start_');
}
export function createStartNodeId(): string {
return `__strategy_start_${generateNodeId()}__`;
}
export function defaultStartMeta(): Record<string, StartNodeMeta> {
return {
[START_NODE_ID]: {
candleTypes: [DEFAULT_NEW_STRATEGY_START_CANDLE],
candleType: DEFAULT_NEW_STRATEGY_START_CANDLE,
},
};
}
/** START 메타에서 정렬된 평가 분봉 목록 (최소 1개) */
export function getStartCandleTypes(meta?: StartNodeMeta | null): string[] {
const raw = meta?.candleTypes?.length
? meta.candleTypes
: meta?.candleType
? [meta.candleType]
: [DEFAULT_START_CANDLE];
const seen = new Set<string>();
const out: string[] = [];
for (const ct of STRATEGY_CANDLE_TYPES) {
if (raw.some(r => normalizeStartCandleType(r) === ct) && !seen.has(ct)) {
seen.add(ct);
out.push(ct);
}
}
for (const r of raw) {
const n = normalizeStartCandleType(r);
if (!seen.has(n)) {
seen.add(n);
out.push(n);
}
}
return out.length > 0 ? out : [DEFAULT_START_CANDLE];
}
export function formatStartCandleTypesLabel(types: string[]): string {
const list = getStartCandleTypes({ candleTypes: types });
return list.map(formatStrategyCandleLabel).join(' · ');
}
export function normalizeStartCandleType(value: string | undefined | null): string {
const raw = (value ?? DEFAULT_START_CANDLE).trim().toLowerCase();
if (raw === '1d' || raw === '1w') return raw;
return (STRATEGY_CANDLE_TYPES as readonly string[]).includes(raw) ? raw : DEFAULT_START_CANDLE;
}
export function formatStartCandleLabel(candleType: string): string {
return normalizeStartCandleType(candleType);
}
const CANDLE_TYPE_LABELS: Record<string, string> = {
'1m': '1분봉',
'3m': '3분봉',
'5m': '5분봉',
'10m': '10분봉',
'15m': '15분봉',
'30m': '30분봉',
'1h': '1시간봉',
'4h': '4시간봉',
'1w': '주간봉',
'1d': '일봉',
};
export function formatStrategyCandleLabel(candleType: string): string {
const ct = normalizeStartCandleType(candleType);
return CANDLE_TYPE_LABELS[ct] ?? ct;
}
export const STRATEGY_CANDLE_TYPE_OPTIONS = STRATEGY_CANDLE_TYPES.map(ct => ({
value: ct,
label: CANDLE_TYPE_LABELS[ct] ?? ct,
}));