Files
goldenChart/frontend/src/utils/maDslField.ts
T

114 lines
3.7 KiB
TypeScript

/**
* MA/EMA DSL 필드 — MA1~MA11 슬롯 ↔ SMA period1~11 (보조지표 설정과 동일)
*
* - MA3 → period3 (예: 20일)
* - MA20 → 레거시: 숫자>11 이면 기간 20일 그대로
*/
import { SMA_DEFAULT_PERIODS, SMA_MA_COUNT, formatSmaPeriodDisplayLabel } from './smaConfig';
export const MA_DSL_SLOT_MAX = SMA_MA_COUNT;
export function buildMaLinesFromSmaParams(
params?: Record<string, number | string | boolean>,
): number[] {
const lines: number[] = [];
for (let slot = 1; slot <= SMA_MA_COUNT; slot++) {
const raw = params?.[`period${slot}`];
const p = typeof raw === 'number' && raw > 0
? Math.floor(raw)
: SMA_DEFAULT_PERIODS[slot - 1];
lines.push(p);
}
return lines;
}
/** DSL MA/EMA 필드 → 실제 SMA/EMA 계산 기간 */
export function resolveMaDslFieldPeriod(
field: string | undefined,
smaParams?: Record<string, number | string | boolean>,
): number | null {
if (!field) return null;
const ma = /^MA(\d+)$/.exec(field);
if (ma && !field.startsWith('MACD')) {
const n = parseInt(ma[1], 10);
if (!Number.isFinite(n) || n <= 0) return null;
if (n <= MA_DSL_SLOT_MAX) return buildMaLinesFromSmaParams(smaParams)[n - 1];
return n;
}
const ema = /^EMA(\d+)$/.exec(field);
if (ema) {
const n = parseInt(ema[1], 10);
if (!Number.isFinite(n) || n <= 0) return null;
if (n <= MA_DSL_SLOT_MAX) return buildMaLinesFromSmaParams(smaParams)[n - 1];
return n;
}
return null;
}
/** EMA 설정 기간 → EMA5일 */
export function formatEmaPeriodDisplayLabel(period: number | string | boolean | undefined): string {
const raw = typeof period === 'number' ? period : Number(period);
const p = Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 1;
return `EMA${p}일`;
}
/** UI·해석용 라벨 — MA20일 */
export function formatMaDslFieldLabel(
field: string | undefined,
smaParams?: Record<string, number | string | boolean>,
): string {
if (!field) return '';
const period = resolveMaDslFieldPeriod(field, smaParams);
if (period == null) return field;
if (field.startsWith('EMA')) return formatEmaPeriodDisplayLabel(period);
if (field.startsWith('MA') && !field.startsWith('MACD')) return formatSmaPeriodDisplayLabel(period);
return field;
}
export function buildMaFieldOptions(
maLines: number[],
): { value: string; label: string }[] {
return Array.from({ length: SMA_MA_COUNT }, (_, i) => {
const slot = i + 1;
const p = maLines[i] ?? SMA_DEFAULT_PERIODS[i];
return { value: `MA${slot}`, label: formatSmaPeriodDisplayLabel(p) };
});
}
export function buildEmaFieldOptions(
maLines: number[],
maxSlots = 4,
): { value: string; label: string }[] {
const n = Math.min(maxSlots, SMA_MA_COUNT);
return Array.from({ length: n }, (_, i) => {
const slot = i + 1;
const p = maLines[i] ?? SMA_DEFAULT_PERIODS[i];
return { value: `EMA${slot}`, label: formatEmaPeriodDisplayLabel(p) };
});
}
/** 레거시 MA20·MA60(기간 인코딩) 옵션 유지 */
export function appendLegacyMaFieldOption(
opts: { value: string; label: string }[],
field: string | undefined,
): { value: string; label: string }[] {
if (!field || opts.some(o => o.value === field)) return opts;
const period = resolveMaDslFieldPeriod(field);
if (period == null) return opts;
const ma = /^MA(\d+)$/.exec(field);
if (ma && !field.startsWith('MACD')) {
const n = parseInt(ma[1], 10);
if (n > MA_DSL_SLOT_MAX) {
return [...opts, { value: field, label: formatSmaPeriodDisplayLabel(period) }];
}
}
const ema = /^EMA(\d+)$/.exec(field);
if (ema) {
const n = parseInt(ema[1], 10);
if (n > MA_DSL_SLOT_MAX) {
return [...opts, { value: field, label: formatEmaPeriodDisplayLabel(period) }];
}
}
return opts;
}