goldenChat base source add
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* 보조지표 설정 화면 — 플롯 행·전역 파라미터 배치 (일목/SMA 단일 화면 컨셉)
|
||||
*/
|
||||
import type { PlotDef } from './indicatorRegistry';
|
||||
|
||||
/** 플롯 행에 붙지 않고 상단에만 표시할 키 (우선 순서) */
|
||||
const GLOBAL_FIRST_KEYS: string[] = [
|
||||
'src', 'maType', 'mult', 'multiplier', 'percent', 'bbMult', 'offset', 'sigma',
|
||||
];
|
||||
|
||||
const GLOBAL_FIRST_SET = new Set(GLOBAL_FIRST_KEYS);
|
||||
|
||||
/** 지표별 플롯 ID → 파라미터 키 (빈 배열 = 자동/공유) */
|
||||
export const INDICATOR_PLOT_PARAMS: Record<string, Record<string, string[]>> = {
|
||||
MACross: { plot0: ['fastLength'], plot1: ['slowLength'] },
|
||||
Stochastic: { plot0: ['kLength', 'smooth'], plot1: ['dSmoothing'] },
|
||||
StochRSI: { plot0: ['lengthRSI', 'lengthStoch', 'smoothK'], plot1: ['smoothD'] },
|
||||
CCI: { plot0: ['length'], plot1: ['maLength'] },
|
||||
RSI: { plot0: ['length'], plot1: ['maLength'] },
|
||||
/** 업비트: OBV 행=이동평균 종류, 신호선 행=기간 (색상 컨트롤 좌측) */
|
||||
OBV: { plot0: ['maType'], plot1: ['maLength'] },
|
||||
/** 업비트: 길이·곱·오프셋은 상단 전역 행, 플롯 행은 스타일만 */
|
||||
BollingerBands: { plot0: [], plot1: [], plot2: [] },
|
||||
/** 업비트: DI 길이·ADX 스무딩은 상단 전역, 플롯 행은 스타일만 */
|
||||
DMI: { plot0: [], plot1: [], plot2: [], plot3: [], plot4: [] },
|
||||
KeltnerChannels: { plot0: ['length', 'multiplier'], plot1: [], plot2: [] },
|
||||
DonchianChannels: { plot0: ['length'], plot1: [], plot2: [] },
|
||||
Envelope: { plot0: ['length', 'percent'], plot1: [], plot2: [] },
|
||||
RVI: { plot0: ['length'], plot1: [] },
|
||||
FisherTransform: { plot0: ['length'], plot1: [] },
|
||||
MACD: { plot0: ['fastLength', 'slowLength', 'signalLength'], plot1: [], plot2: [] },
|
||||
TSI: { plot0: ['longLength', 'shortLength'], plot1: ['signalLength'] },
|
||||
TRIX: { plot0: ['length'], plot1: ['signalLength'] },
|
||||
SMIErgodic: { plot0: ['shortLength', 'longLength'], plot1: ['signalLength'] },
|
||||
SMIErgodicOscillator: { plot0: ['shortLength', 'longLength'], plot1: ['signalLength'] },
|
||||
UltimateOscillator: { plot0: ['length1', 'length2', 'length3'] },
|
||||
ConnorsRSI: { plot0: ['rsiLength', 'upDownLength', 'rocLength'] },
|
||||
Psychological: { plot0: ['length'] },
|
||||
InvestPsychological: { plot0: ['length'] },
|
||||
Disparity: {
|
||||
plot0: ['ultraLength'],
|
||||
plot1: ['shortLength'],
|
||||
plot2: ['midLength'],
|
||||
plot3: ['longLength'],
|
||||
},
|
||||
IchimokuCloud: {
|
||||
plot0: ['conversionPeriods'],
|
||||
plot1: ['basePeriods'],
|
||||
/** 선행스팬1 — 전환·기준 평균선의 미래 이동(shift) 기간 */
|
||||
plot2: ['displacement'],
|
||||
plot3: ['laggingSpan2Periods'],
|
||||
/** 후행스팬 — chikou 이동(선행스팬1과 동일 displacement) */
|
||||
plot4: ['displacement'],
|
||||
},
|
||||
};
|
||||
|
||||
const PLOT_INDEX_FALLBACK: Record<number, string[]> = {
|
||||
0: [
|
||||
'length', 'len', 'kLength', 'fastLength', 'length1', 'lengthRSI', 'rsiLength',
|
||||
'shortLength', 'ultraLength', 'conversionPeriods', 'turbolen',
|
||||
],
|
||||
1: ['dSmoothing', 'slowLength', 'length2', 'smoothD', 'maLength', 'signalLength', 'midLength', 'basePeriods'],
|
||||
2: ['smooth', 'length3', 'longLength', 'laggingSpan2Periods'],
|
||||
3: ['longLength', 'displacement'],
|
||||
4: ['displacement'],
|
||||
};
|
||||
|
||||
function keysInParams(keys: string[], params: Record<string, unknown>): string[] {
|
||||
return keys.filter(k => k in params);
|
||||
}
|
||||
|
||||
/** 플롯 한 줄에 표시할 숫자·선택 파라미터 키 */
|
||||
export function getPlotParamKeys(
|
||||
indicatorType: string,
|
||||
plotId: string,
|
||||
plotIndex: number,
|
||||
plots: PlotDef[],
|
||||
params: Record<string, number | string | boolean>,
|
||||
): string[] {
|
||||
const mapped = INDICATOR_PLOT_PARAMS[indicatorType]?.[plotId];
|
||||
if (mapped !== undefined) {
|
||||
return keysInParams(mapped, params);
|
||||
}
|
||||
|
||||
if (plots.length === 1) {
|
||||
return Object.keys(params).filter(
|
||||
k => !GLOBAL_FIRST_SET.has(k) && typeof params[k] === 'number',
|
||||
);
|
||||
}
|
||||
|
||||
const fallback = keysInParams(PLOT_INDEX_FALLBACK[plotIndex] ?? [], params);
|
||||
if (fallback.length) return fallback;
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/** 업비트 BB 인풋 탭 순서 */
|
||||
const BOLLINGER_GLOBAL_KEYS = ['length', 'mult', 'offset'] as const;
|
||||
|
||||
/** 플롯에 할당되지 않은 전역 파라미터 (상단 행) */
|
||||
export function getGlobalParamKeys(
|
||||
indicatorType: string,
|
||||
params: Record<string, number | string | boolean>,
|
||||
plots: PlotDef[],
|
||||
): string[] {
|
||||
if (indicatorType === 'BollingerBands') {
|
||||
return BOLLINGER_GLOBAL_KEYS.filter(k => k in params);
|
||||
}
|
||||
|
||||
const assigned = new Set<string>();
|
||||
for (let i = 0; i < plots.length; i++) {
|
||||
for (const k of getPlotParamKeys(indicatorType, plots[i].id, i, plots, params)) {
|
||||
assigned.add(k);
|
||||
}
|
||||
}
|
||||
|
||||
const remaining = Object.keys(params).filter(
|
||||
k => !assigned.has(k) && k !== 'symbolMode' && k !== 'refSymbol' && k !== 'src' && k !== 'maType',
|
||||
);
|
||||
const ordered = GLOBAL_FIRST_KEYS.filter(k => remaining.includes(k));
|
||||
const rest = remaining.filter(k => !ordered.includes(k));
|
||||
return [...ordered, ...rest];
|
||||
}
|
||||
|
||||
/** 플롯 행 입력란에 표시할 내용 없음 */
|
||||
export function plotRowHasNoInputs(
|
||||
indicatorType: string,
|
||||
plotId: string,
|
||||
plotIndex: number,
|
||||
plots: PlotDef[],
|
||||
params: Record<string, number | string | boolean>,
|
||||
): boolean {
|
||||
return getPlotParamKeys(indicatorType, plotId, plotIndex, plots, params).length === 0;
|
||||
}
|
||||
|
||||
/** 파라미터가 상단 전역 행에만 있음 (OBV: maType·maLength) — 플롯 행 '자동' 미표시 */
|
||||
export function plotParamsOnGlobalRowOnly(indicatorType: string, plotId: string): boolean {
|
||||
const mapped = INDICATOR_PLOT_PARAMS[indicatorType]?.[plotId];
|
||||
return mapped !== undefined && mapped.length === 0;
|
||||
}
|
||||
Reference in New Issue
Block a user