229 lines
7.7 KiB
TypeScript
229 lines
7.7 KiB
TypeScript
/**
|
|
* 보조지표 설정 편집 — 개별 모달·일괄 설정 팝업 공통 직렬화/초기화
|
|
*/
|
|
import type { IndicatorConfig, HlinesBackground, Timeframe } from '../types';
|
|
import type { PlotDef, HLineDef } from './indicatorRegistry';
|
|
import {
|
|
getIndicatorDef,
|
|
mergePlotDefs,
|
|
normalizeHLines,
|
|
normalizeObvParams,
|
|
enrichIndicatorConfig,
|
|
} from './indicatorRegistry';
|
|
import { isStrategyChartIndicatorId } from './strategyToChartIndicators';
|
|
import {
|
|
normalizeBollingerParams,
|
|
DEFAULT_BB_BAND_BACKGROUND,
|
|
resolveBbBandBackground,
|
|
} from './bollingerConfig';
|
|
import { normalizeDmiParams } from './indicatorRegistry';
|
|
import {
|
|
normalizeSmaConfig,
|
|
createDefaultSmaParams,
|
|
createDefaultSmaPlots,
|
|
createDefaultSmaPlotVisibility,
|
|
} from './smaConfig';
|
|
import {
|
|
normalizeIchimokuConfig,
|
|
createDefaultIchimokuParams,
|
|
createDefaultIchimokuPlotVisibility,
|
|
mergeIchimokuPlots,
|
|
DEFAULT_ICHIMOKU_CLOUD_COLORS,
|
|
resolveIchimokuCloudColors,
|
|
type IchimokuCloudColors,
|
|
} from './ichimokuConfig';
|
|
|
|
export const DEFAULT_HLINES_BG: HlinesBackground = { visible: true, color: '#787B8640' };
|
|
|
|
export interface IndicatorSettingsEditorSnapshot {
|
|
params: Record<string, number | string | boolean>;
|
|
plots: PlotDef[];
|
|
plotVis: Record<string, boolean>;
|
|
hlines: HLineDef[];
|
|
hlinesBg: HlinesBackground;
|
|
lastValueVisible: boolean;
|
|
timeframeVis: Partial<Record<Timeframe, boolean>>;
|
|
cloudColors: IchimokuCloudColors;
|
|
bandBg: HlinesBackground;
|
|
}
|
|
|
|
/** 차트·DB 값으로 편집기 초기 config (개별 모달·일괄 팝업 동일) */
|
|
export function initializeIndicatorConfigForEditor(
|
|
raw: IndicatorConfig,
|
|
getParams?: (type: string, defaults: Record<string, number | string | boolean>) => Record<string, number | string | boolean>,
|
|
getVisualConfig?: (
|
|
type: string,
|
|
defaultPlots: PlotDef[],
|
|
defaultHlines?: HLineDef[],
|
|
) => { plots: PlotDef[]; hlines: HLineDef[]; cloudColors?: IchimokuCloudColors; bandBackground?: HlinesBackground },
|
|
): IndicatorConfig {
|
|
const def = getIndicatorDef(raw.type);
|
|
if (!def) return enrichIndicatorConfig(raw);
|
|
|
|
const params = getParams
|
|
? { ...getParams(raw.type, def.defaultParams), ...raw.params }
|
|
: { ...raw.params };
|
|
const visual = getVisualConfig?.(raw.type, def.plots, def.hlines);
|
|
|
|
const mergedPlots = isStrategyChartIndicatorId(raw.id)
|
|
? mergePlotDefs(undefined, mergePlotDefs(visual?.plots, def.plots))
|
|
: mergePlotDefs(
|
|
raw.plots?.length ? raw.plots : undefined,
|
|
mergePlotDefs(visual?.plots, def.plots),
|
|
);
|
|
|
|
return enrichIndicatorConfig({
|
|
...raw,
|
|
params,
|
|
plots: mergedPlots,
|
|
hlines: raw.hlines?.length ? raw.hlines : visual?.hlines,
|
|
cloudColors: raw.cloudColors ?? visual?.cloudColors,
|
|
bandBackground: raw.bandBackground ?? visual?.bandBackground,
|
|
});
|
|
}
|
|
|
|
export function createEditorSnapshot(config: IndicatorConfig): IndicatorSettingsEditorSnapshot {
|
|
const normalized = config.type === 'SMA'
|
|
? normalizeSmaConfig(config)
|
|
: config.type === 'IchimokuCloud'
|
|
? normalizeIchimokuConfig(config)
|
|
: config;
|
|
const def = getIndicatorDef(config.type);
|
|
|
|
const baseParams = config.type === 'SMA' || config.type === 'IchimokuCloud'
|
|
? { ...normalized.params }
|
|
: { ...(def?.defaultParams ?? {}), ...normalized.params };
|
|
|
|
let params = baseParams;
|
|
if (config.type === 'OBV') params = normalizeObvParams(baseParams);
|
|
if (config.type === 'BollingerBands') params = normalizeBollingerParams(baseParams);
|
|
if (config.type === 'DMI') params = normalizeDmiParams(baseParams);
|
|
|
|
const basePlots: PlotDef[] = config.type === 'IchimokuCloud'
|
|
? mergeIchimokuPlots(normalized.plots, def?.plots ?? [])
|
|
: mergePlotDefs(normalized.plots, def?.plots ?? []);
|
|
|
|
const plotVis: Record<string, boolean> = { ...normalized.plotVisibility };
|
|
const mergedParams = { ...(def?.defaultParams ?? {}), ...normalized.params };
|
|
for (const p of basePlots) {
|
|
if (plotVis[p.id] === undefined) {
|
|
plotVis[p.id] = normalized.plotVisibility?.[p.id] !== false;
|
|
}
|
|
}
|
|
if ((config.type === 'CCI' || config.type === 'RSI') && mergedParams.maType === 'None') {
|
|
plotVis.plot1 = false;
|
|
}
|
|
|
|
const baseHLines: HLineDef[] = config.hlines ?? def?.hlines ?? [];
|
|
|
|
return {
|
|
params,
|
|
plots: basePlots.map(p => ({ ...p })),
|
|
plotVis,
|
|
hlines: normalizeHLines(
|
|
baseHLines.map(h => ({ ...h, visible: h.visible !== false })),
|
|
config.type,
|
|
def?.hlines,
|
|
),
|
|
hlinesBg: normalized.hlinesBackground ?? { ...DEFAULT_HLINES_BG },
|
|
lastValueVisible: normalized.lastValueVisible !== false,
|
|
timeframeVis: { ...normalized.timeframeVisibility },
|
|
cloudColors: resolveIchimokuCloudColors(normalized.cloudColors),
|
|
bandBg: resolveBbBandBackground(normalized.bandBackground),
|
|
};
|
|
}
|
|
|
|
/** 편집 스냅샷 → IndicatorConfig (저장·일괄 적용·실시간 onChange 공통) */
|
|
export function configFromEditorSnapshot(
|
|
base: IndicatorConfig,
|
|
snap: IndicatorSettingsEditorSnapshot,
|
|
): IndicatorConfig {
|
|
const def = getIndicatorDef(base.type);
|
|
const hasHLines = (def?.hlines ?? []).length > 0 || snap.hlines.length > 0;
|
|
const tfVis = Object.values(snap.timeframeVis).some(v => v === false)
|
|
? snap.timeframeVis
|
|
: undefined;
|
|
|
|
const draft: IndicatorConfig = {
|
|
...base,
|
|
params: snap.params,
|
|
plots: snap.plots,
|
|
plotVisibility: snap.plotVis,
|
|
hlines: snap.hlines,
|
|
hlinesBackground: hasHLines ? snap.hlinesBg : undefined,
|
|
lastValueVisible: snap.lastValueVisible,
|
|
timeframeVisibility: tfVis,
|
|
...(base.type === 'IchimokuCloud' ? { cloudColors: snap.cloudColors } : {}),
|
|
};
|
|
|
|
if (base.type === 'SMA') return normalizeSmaConfig(draft);
|
|
if (base.type === 'IchimokuCloud') return normalizeIchimokuConfig(draft);
|
|
if (base.type === 'BollingerBands') {
|
|
return enrichIndicatorConfig({
|
|
...draft,
|
|
params: normalizeBollingerParams(draft.params),
|
|
bandBackground: snap.bandBg,
|
|
});
|
|
}
|
|
return enrichIndicatorConfig(draft);
|
|
}
|
|
|
|
/** 지표별 기본값 (id·hidden 유지) */
|
|
export function resetConfigToDefaults(config: IndicatorConfig): IndicatorConfig {
|
|
const def = getIndicatorDef(config.type);
|
|
if (!def) return config;
|
|
|
|
if (config.type === 'SMA') {
|
|
return normalizeSmaConfig({
|
|
...config,
|
|
params: createDefaultSmaParams(),
|
|
plots: createDefaultSmaPlots(),
|
|
plotVisibility: createDefaultSmaPlotVisibility(),
|
|
hlines: [],
|
|
hlinesBackground: undefined,
|
|
lastValueVisible: true,
|
|
timeframeVisibility: undefined,
|
|
});
|
|
}
|
|
if (config.type === 'IchimokuCloud') {
|
|
return normalizeIchimokuConfig({
|
|
...config,
|
|
params: createDefaultIchimokuParams(),
|
|
plots: def.plots.map(p => ({ ...p })),
|
|
plotVisibility: createDefaultIchimokuPlotVisibility(),
|
|
cloudColors: DEFAULT_ICHIMOKU_CLOUD_COLORS,
|
|
hlines: [],
|
|
hlinesBackground: undefined,
|
|
lastValueVisible: true,
|
|
timeframeVisibility: undefined,
|
|
});
|
|
}
|
|
|
|
const baseParams = { ...(def.defaultParams as Record<string, number | string | boolean>) };
|
|
const plots = def.plots.map(p => ({ ...p }));
|
|
const plotVis: Record<string, boolean> = {};
|
|
for (const p of def.plots) plotVis[p.id] = true;
|
|
|
|
let params = config.type === 'BollingerBands'
|
|
? normalizeBollingerParams(baseParams)
|
|
: baseParams;
|
|
|
|
const draft: IndicatorConfig = {
|
|
...config,
|
|
params,
|
|
plots,
|
|
plotVisibility: plotVis,
|
|
hlines: normalizeHLines(
|
|
(def.hlines ?? []).map(h => ({ ...h, visible: h.visible !== false })),
|
|
config.type,
|
|
def.hlines,
|
|
),
|
|
hlinesBackground: { ...DEFAULT_HLINES_BG },
|
|
lastValueVisible: true,
|
|
timeframeVisibility: undefined,
|
|
cloudColors: undefined,
|
|
bandBackground: config.type === 'BollingerBands' ? { ...DEFAULT_BB_BAND_BACKGROUND } : undefined,
|
|
};
|
|
return enrichIndicatorConfig(draft);
|
|
}
|