280 lines
9.3 KiB
TypeScript
280 lines
9.3 KiB
TypeScript
/**
|
|
* 지표 추가 팝업 Main 탭 — 설정 초기화·차트 인스턴스 병합
|
|
*/
|
|
import type { IndicatorConfig } from '../types';
|
|
import type { PlotDef, HLineDef } from './indicatorRegistry';
|
|
import { getIndicatorDef, enrichIndicatorConfig } from './indicatorRegistry';
|
|
import { MAIN_INDICATOR_TYPES } from './indicatorMainTab';
|
|
import { getSettingsIndicatorTypes } from './indicatorSettingsList';
|
|
import { initializeIndicatorConfigForEditor } from './indicatorSettingsEditor';
|
|
import {
|
|
normalizeSmaConfig,
|
|
createDefaultSmaPlotVisibility,
|
|
} from './smaConfig';
|
|
import { normalizeIchimokuConfig } from './ichimokuConfig';
|
|
import type { IchimokuCloudColors } from './ichimokuConfig';
|
|
import { isMainIndicatorType } from './indicatorMainTab';
|
|
import { indicatorDefaultsFingerprint } from './indicatorDefaultsFingerprint';
|
|
|
|
export type MainIndicatorDraftMap = Record<string, IndicatorConfig>;
|
|
|
|
/** 지표 추가 팝업에서 차트 미적용 지표 설정용 modal id 접두사 */
|
|
export const INDICATOR_SETTINGS_TEMPLATE_PREFIX = 'template:';
|
|
|
|
export function indicatorSettingsTemplateId(type: string): string {
|
|
return `${INDICATOR_SETTINGS_TEMPLATE_PREFIX}${type}`;
|
|
}
|
|
|
|
export function isIndicatorSettingsTemplateId(id: string): boolean {
|
|
return id.startsWith(INDICATOR_SETTINGS_TEMPLATE_PREFIX);
|
|
}
|
|
|
|
export function indicatorTypeFromSettingsId(id: string): string | null {
|
|
if (!isIndicatorSettingsTemplateId(id)) return null;
|
|
return id.slice(INDICATOR_SETTINGS_TEMPLATE_PREFIX.length);
|
|
}
|
|
|
|
export function buildMainIndicatorConfig(
|
|
type: string,
|
|
activeIndicators: 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 },
|
|
): IndicatorConfig | null {
|
|
const def = getIndicatorDef(type);
|
|
if (!def) return null;
|
|
const active = activeIndicators.find(i => i.type === type);
|
|
const base: IndicatorConfig = active ?? {
|
|
id: `template_${type}`,
|
|
type,
|
|
params: {},
|
|
plots: [],
|
|
hlines: [],
|
|
};
|
|
return initializeIndicatorConfigForEditor(base, getParams, getVisualConfig);
|
|
}
|
|
|
|
/** Main 탭 16종 전체 설정 맵 (차트에 없어도 DB 기본값 편집용) */
|
|
export function buildMainIndicatorDraftMap(
|
|
activeIndicators: 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 },
|
|
): MainIndicatorDraftMap {
|
|
const map: MainIndicatorDraftMap = {};
|
|
for (const type of MAIN_INDICATOR_TYPES) {
|
|
const cfg = buildMainIndicatorConfig(type, activeIndicators, getParams, getVisualConfig);
|
|
if (cfg) map[type] = cfg;
|
|
}
|
|
return map;
|
|
}
|
|
|
|
export function newMainIndicatorId(): string {
|
|
return `ind_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
}
|
|
|
|
function finalizeMainIndicatorConfig(cfg: IndicatorConfig, isNew: boolean): IndicatorConfig {
|
|
let out = enrichIndicatorConfig(cfg);
|
|
if (out.type === 'SMA') {
|
|
out = normalizeSmaConfig({
|
|
...out,
|
|
plotVisibility: isNew
|
|
? (out.plotVisibility ?? createDefaultSmaPlotVisibility())
|
|
: out.plotVisibility,
|
|
});
|
|
} else if (out.type === 'IchimokuCloud') {
|
|
out = normalizeIchimokuConfig(out);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
/** DB에 저장된 전역 기본값 → 차트 인스턴스에 반영 (id·hidden·pane 병합 상태 유지) */
|
|
export function mergeGlobalDefaultsOntoChartIndicators(
|
|
activeIndicators: 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 },
|
|
): IndicatorConfig[] {
|
|
return activeIndicators.map(ind => {
|
|
const cfg = buildMainIndicatorConfig(ind.type, [ind], getParams, getVisualConfig);
|
|
if (!cfg) return enrichIndicatorConfig(ind);
|
|
return finalizeMainIndicatorConfig(
|
|
{
|
|
...cfg,
|
|
id: ind.id,
|
|
hidden: ind.hidden,
|
|
mergedWith: ind.mergedWith,
|
|
},
|
|
false,
|
|
);
|
|
});
|
|
}
|
|
|
|
/** 전역 기본값 병합 후 실제 렌더 설정이 바뀌었는지 */
|
|
export function chartIndicatorsNeedDefaultsRefresh(
|
|
before: IndicatorConfig[],
|
|
after: IndicatorConfig[],
|
|
): boolean {
|
|
if (before.length !== after.length) return true;
|
|
return before.some((ind, i) =>
|
|
indicatorDefaultsFingerprint(ind) !== indicatorDefaultsFingerprint(after[i]),
|
|
);
|
|
}
|
|
|
|
/** Main 탭 설정 → 현재 차트에 올라간 동일 type 인스턴스에 반영 (id·hidden 유지) */
|
|
export function mergeMainDraftOntoChart(
|
|
activeIndicators: IndicatorConfig[],
|
|
draftByType: MainIndicatorDraftMap,
|
|
): IndicatorConfig[] {
|
|
return activeIndicators.map(ind => {
|
|
const tpl = draftByType[ind.type];
|
|
if (!tpl) return enrichIndicatorConfig(ind);
|
|
return finalizeMainIndicatorConfig(
|
|
{ ...tpl, id: ind.id, hidden: ind.hidden },
|
|
false,
|
|
);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Main 탭 일괄/설정 적용 — enabledTypes 에 포함된 지표만 차트에 남기고,
|
|
* 새로 켠 type 은 draft 설정으로 추가, 꺼진 type 은 제거.
|
|
* Main 탭이 아닌 지표는 그대로 유지.
|
|
*/
|
|
export function applyMainDraftToChart(
|
|
activeIndicators: IndicatorConfig[],
|
|
draftByType: MainIndicatorDraftMap,
|
|
enabledTypes: ReadonlySet<string>,
|
|
): IndicatorConfig[] {
|
|
const result: IndicatorConfig[] = activeIndicators
|
|
.filter(ind => !isMainIndicatorType(ind.type))
|
|
.map(ind => enrichIndicatorConfig(ind));
|
|
|
|
const seen = new Set<string>();
|
|
|
|
for (const ind of activeIndicators) {
|
|
if (!isMainIndicatorType(ind.type)) continue;
|
|
if (!enabledTypes.has(ind.type)) continue;
|
|
seen.add(ind.type);
|
|
const tpl = draftByType[ind.type];
|
|
if (!tpl) {
|
|
result.push(enrichIndicatorConfig(ind));
|
|
continue;
|
|
}
|
|
result.push(finalizeMainIndicatorConfig(
|
|
{ ...tpl, id: ind.id, hidden: ind.hidden },
|
|
false,
|
|
));
|
|
}
|
|
|
|
for (const type of MAIN_INDICATOR_TYPES) {
|
|
if (!enabledTypes.has(type) || seen.has(type)) continue;
|
|
const tpl = draftByType[type];
|
|
if (!tpl) continue;
|
|
result.push(finalizeMainIndicatorConfig(
|
|
{ ...tpl, id: newMainIndicatorId() },
|
|
true,
|
|
));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/** 차트에 올라간 Main 탭 type 집합 */
|
|
export function mainTypesOnChart(indicators: IndicatorConfig[]): Set<string> {
|
|
return new Set(
|
|
indicators.filter(i => isMainIndicatorType(i.type)).map(i => i.type),
|
|
);
|
|
}
|
|
|
|
export function mainDraftListFromMap(map: MainIndicatorDraftMap): IndicatorConfig[] {
|
|
return MAIN_INDICATOR_TYPES
|
|
.map(type => map[type])
|
|
.filter((c): c is IndicatorConfig => c != null);
|
|
}
|
|
|
|
/** 설정 UI 전체 지표 draft 맵 */
|
|
export function buildAllIndicatorDraftMap(
|
|
activeIndicators: 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 },
|
|
): MainIndicatorDraftMap {
|
|
const map: MainIndicatorDraftMap = {};
|
|
for (const type of getSettingsIndicatorTypes()) {
|
|
const cfg = buildMainIndicatorConfig(type, activeIndicators, getParams, getVisualConfig);
|
|
if (cfg) map[type] = cfg;
|
|
}
|
|
return map;
|
|
}
|
|
|
|
/** draft 맵 → DB 저장용 config 배열 (순서 유지) */
|
|
export function allDraftListFromMap(map: MainIndicatorDraftMap): IndicatorConfig[] {
|
|
return getSettingsIndicatorTypes()
|
|
.map(type => map[type])
|
|
.filter((c): c is IndicatorConfig => c != null);
|
|
}
|
|
|
|
/** 차트에 올라간 보조지표 type 집합 (Main 외 포함) */
|
|
export function indicatorTypesOnChart(indicators: IndicatorConfig[]): Set<string> {
|
|
return new Set(indicators.map(i => i.type));
|
|
}
|
|
|
|
const MANAGED_TYPES = getSettingsIndicatorTypes();
|
|
const MANAGED_SET = new Set(MANAGED_TYPES);
|
|
|
|
/**
|
|
* 설정 UI에서 관리하는 모든 type — enabledTypes 기준으로 차트 병합.
|
|
* 관리 대상이 아닌 type 은 변경 없이 유지.
|
|
*/
|
|
export function applyAllDraftToChart(
|
|
activeIndicators: IndicatorConfig[],
|
|
draftByType: MainIndicatorDraftMap,
|
|
enabledTypes: ReadonlySet<string>,
|
|
): IndicatorConfig[] {
|
|
const result: IndicatorConfig[] = activeIndicators
|
|
.filter(ind => !MANAGED_SET.has(ind.type))
|
|
.map(ind => enrichIndicatorConfig(ind));
|
|
|
|
const seen = new Set<string>();
|
|
|
|
for (const ind of activeIndicators) {
|
|
if (!MANAGED_SET.has(ind.type)) continue;
|
|
if (!enabledTypes.has(ind.type)) continue;
|
|
seen.add(ind.type);
|
|
const tpl = draftByType[ind.type];
|
|
if (!tpl) {
|
|
result.push(enrichIndicatorConfig(ind));
|
|
continue;
|
|
}
|
|
result.push(finalizeMainIndicatorConfig(
|
|
{ ...tpl, id: ind.id, hidden: ind.hidden },
|
|
false,
|
|
));
|
|
}
|
|
|
|
for (const type of MANAGED_TYPES) {
|
|
if (!enabledTypes.has(type) || seen.has(type)) continue;
|
|
const tpl = draftByType[type];
|
|
if (!tpl) continue;
|
|
result.push(finalizeMainIndicatorConfig(
|
|
{ ...tpl, id: newMainIndicatorId() },
|
|
true,
|
|
));
|
|
}
|
|
|
|
return result;
|
|
}
|