goldenChat base source add
This commit is contained in:
@@ -0,0 +1,228 @@
|
||||
/**
|
||||
* useIndicatorSettings
|
||||
*
|
||||
* 전역 지표 파라미터 및 시각 설정(색상·선굵기·수평선)을 DB와 동기화하는 훅.
|
||||
*
|
||||
* - 앱 시작 시 DB에서 로드 → indicatorRegistry.ts 하드코딩 기본값을 완전 대체
|
||||
* - 지표 파라미터 또는 시각 설정이 변경되면 DB에 저장
|
||||
* - 백엔드 Ta4j 계산 시에도 동일한 파라미터 값 사용
|
||||
*
|
||||
* 사용 예:
|
||||
* ```tsx
|
||||
* const { getParams, saveParams, getVisualConfig, saveVisual } = useIndicatorSettings();
|
||||
*
|
||||
* // 지표 추가 시: DB 저장값 우선 적용 (파라미터 + 색상)
|
||||
* const params = getParams(type, def.defaultParams);
|
||||
* const { plots, hlines } = getVisualConfig(type, def.plots, def.hlines);
|
||||
*
|
||||
* // 지표 설정 저장 시: 파라미터 + 시각 설정 모두 DB에 반영
|
||||
* saveParams(updated.type, updated.params);
|
||||
* saveVisual(updated.type, updated.plots, updated.hlines);
|
||||
* ```
|
||||
*/
|
||||
|
||||
import { useEffect, useRef, useCallback } from 'react';
|
||||
import { getIndicatorDef, mergePlotDefs, PlotDef, HLineDef } from '../utils/indicatorRegistry';
|
||||
import { createDefaultSmaParams, createDefaultSmaPlots, normalizeSmaConfig } from '../utils/smaConfig';
|
||||
import { normalizeIchimokuConfig, mergeIchimokuPlots } from '../utils/ichimokuConfig';
|
||||
import {
|
||||
DEFAULT_ICHIMOKU_CLOUD_COLORS,
|
||||
type IchimokuCloudColors,
|
||||
resolveIchimokuCloudColors,
|
||||
} from '../utils/ichimokuConfig';
|
||||
import {
|
||||
loadIndicatorSettings,
|
||||
saveIndicatorSettings,
|
||||
saveAllIndicatorSettings,
|
||||
loadIndicatorVisualSettings,
|
||||
saveIndicatorVisualSettings,
|
||||
} from '../utils/backendApi';
|
||||
|
||||
type ParamMap = Record<string, unknown>;
|
||||
type AllSettings = Record<string, ParamMap>;
|
||||
|
||||
export interface IndicatorVisual {
|
||||
plots?: PlotDef[];
|
||||
hlines?: HLineDef[];
|
||||
cloudColors?: IchimokuCloudColors;
|
||||
}
|
||||
type VisualCache = Record<string, IndicatorVisual>;
|
||||
|
||||
// ── 전역 싱글톤 캐시 — 여러 슬롯에서 공유하여 중복 네트워크 요청 방지 ──
|
||||
let _cache: AllSettings | null = null;
|
||||
let _loadPromise: Promise<AllSettings> | null = null;
|
||||
|
||||
let _visualCache: VisualCache | null = null;
|
||||
let _visualLoadPromise: Promise<VisualCache> | null = null;
|
||||
|
||||
function ensureLoaded(): Promise<AllSettings> {
|
||||
if (_cache !== null) return Promise.resolve(_cache);
|
||||
if (_loadPromise) return _loadPromise;
|
||||
_loadPromise = loadIndicatorSettings().then(data => {
|
||||
_cache = (data as AllSettings) ?? {};
|
||||
_loadPromise = null;
|
||||
return _cache;
|
||||
});
|
||||
return _loadPromise;
|
||||
}
|
||||
|
||||
function ensureVisualLoaded(): Promise<VisualCache> {
|
||||
if (_visualCache !== null) return Promise.resolve(_visualCache);
|
||||
if (_visualLoadPromise) return _visualLoadPromise;
|
||||
_visualLoadPromise = loadIndicatorVisualSettings().then(data => {
|
||||
_visualCache = (data as VisualCache) ?? {};
|
||||
_visualLoadPromise = null;
|
||||
return _visualCache;
|
||||
});
|
||||
return _visualLoadPromise;
|
||||
}
|
||||
|
||||
/** 캐시를 강제로 무효화 (테스트 또는 로그아웃 시) */
|
||||
export function invalidateIndicatorSettingsCache() {
|
||||
_cache = null;
|
||||
_loadPromise = null;
|
||||
_visualCache = null;
|
||||
_visualLoadPromise = null;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
export function useIndicatorSettings(sessionKey = 0) {
|
||||
// 로그인/로그아웃 시 지표 설정 재로드
|
||||
useEffect(() => {
|
||||
invalidateIndicatorSettingsCache();
|
||||
ensureLoaded().catch(err =>
|
||||
console.error('[useIndicatorSettings] params load failed', err)
|
||||
);
|
||||
ensureVisualLoaded().catch(err =>
|
||||
console.error('[useIndicatorSettings] visual load failed', err)
|
||||
);
|
||||
}, [sessionKey]);
|
||||
|
||||
// ── 파라미터 ────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* 지표 타입에 대한 파라미터를 반환.
|
||||
* DB 저장값 → defaultParams 순으로 우선순위 적용.
|
||||
*/
|
||||
const getParams = useCallback(
|
||||
(
|
||||
type: string,
|
||||
defaults?: Record<string, number | string | boolean>
|
||||
): Record<string, number | string | boolean> => {
|
||||
const dbParams = _cache?.[type] ?? {};
|
||||
const fallback = defaults ?? getIndicatorDef(type)?.defaultParams ?? {};
|
||||
const merged = { ...fallback, ...dbParams } as Record<string, number | string | boolean>;
|
||||
if (type === 'SMA') {
|
||||
return normalizeSmaConfig({ id: '', type: 'SMA', params: merged }).params;
|
||||
}
|
||||
if (type === 'IchimokuCloud') {
|
||||
return normalizeIchimokuConfig({ id: '', type: 'IchimokuCloud', params: merged }).params;
|
||||
}
|
||||
return merged;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
/** 특정 지표 파라미터를 DB에 저장하고 캐시를 업데이트 */
|
||||
const saveParams = useCallback(
|
||||
(type: string, params: Record<string, number | string | boolean>) => {
|
||||
if (_cache) {
|
||||
_cache = { ..._cache, [type]: params as ParamMap };
|
||||
} else {
|
||||
_cache = { [type]: params as ParamMap };
|
||||
}
|
||||
saveIndicatorSettings(type, params as ParamMap).catch(err =>
|
||||
console.error(`[useIndicatorSettings] saveParams failed for ${type}`, err)
|
||||
);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
/** 현재 활성 지표 목록 전체를 DB에 저장 */
|
||||
const saveAll = useCallback(
|
||||
(allParams: AllSettings) => {
|
||||
_cache = { ...allParams };
|
||||
saveAllIndicatorSettings(allParams).catch(err =>
|
||||
console.error('[useIndicatorSettings] saveAll failed', err)
|
||||
);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
// ── 시각 설정 (색상·선굵기·수평선) ──────────────────────────────────────
|
||||
|
||||
/**
|
||||
* 지표의 시각 설정을 반환.
|
||||
* DB 저장값 → registry defaults 순으로 우선순위 적용.
|
||||
*
|
||||
* @param type 지표 타입 (e.g. "RSI")
|
||||
* @param defaultPlots 없을 경우 사용할 기본 플롯 (indicatorRegistry.plots)
|
||||
* @param defaultHlines 없을 경우 사용할 기본 수평선 (indicatorRegistry.hlines)
|
||||
*/
|
||||
const getVisualConfig = useCallback(
|
||||
(
|
||||
type: string,
|
||||
defaultPlots?: PlotDef[],
|
||||
defaultHlines?: HLineDef[]
|
||||
): { plots: PlotDef[]; hlines: HLineDef[]; cloudColors?: IchimokuCloudColors } => {
|
||||
const saved = _visualCache?.[type];
|
||||
const def = getIndicatorDef(type);
|
||||
const registryPlots = defaultPlots ?? def?.plots ?? [];
|
||||
let plots = mergePlotDefs(saved?.plots as PlotDef[] | undefined, registryPlots);
|
||||
const hlines = (saved?.hlines as HLineDef[] | undefined) ?? defaultHlines ?? def?.hlines ?? [];
|
||||
|
||||
if (type === 'SMA') {
|
||||
plots = normalizeSmaConfig({
|
||||
id: '',
|
||||
type: 'SMA',
|
||||
params: createDefaultSmaParams(),
|
||||
plots,
|
||||
}).plots ?? createDefaultSmaPlots();
|
||||
}
|
||||
|
||||
if (type === 'IchimokuCloud') {
|
||||
plots = mergeIchimokuPlots(plots, registryPlots);
|
||||
}
|
||||
|
||||
const cloudColors = type === 'IchimokuCloud'
|
||||
? resolveIchimokuCloudColors(saved?.cloudColors ?? DEFAULT_ICHIMOKU_CLOUD_COLORS)
|
||||
: undefined;
|
||||
|
||||
// 깊은 복사: 여러 슬롯이 같은 참조를 공유하지 않도록
|
||||
return {
|
||||
plots: plots.map(p => ({ ...p })),
|
||||
hlines: hlines.map(h => ({ ...h })),
|
||||
cloudColors: cloudColors ? { ...cloudColors } : undefined,
|
||||
};
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
/**
|
||||
* 지표의 시각 설정을 DB에 저장하고 캐시를 업데이트.
|
||||
* IndicatorSettingsModal 저장 콜백에서 호출.
|
||||
*/
|
||||
const saveVisual = useCallback(
|
||||
(
|
||||
type: string,
|
||||
plots?: PlotDef[],
|
||||
hlines?: HLineDef[],
|
||||
cloudColors?: IchimokuCloudColors,
|
||||
) => {
|
||||
const visual: IndicatorVisual = { plots, hlines };
|
||||
if (cloudColors) visual.cloudColors = cloudColors;
|
||||
if (_visualCache) {
|
||||
_visualCache = { ..._visualCache, [type]: visual };
|
||||
} else {
|
||||
_visualCache = { [type]: visual };
|
||||
}
|
||||
saveIndicatorVisualSettings(type, visual as { plots?: unknown[]; hlines?: unknown[]; cloudColors?: IchimokuCloudColors }).catch(err =>
|
||||
console.error(`[useIndicatorSettings] saveVisual failed for ${type}`, err)
|
||||
);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return { getParams, saveParams, saveAll, getVisualConfig, saveVisual };
|
||||
}
|
||||
Reference in New Issue
Block a user