지표탭 추가

This commit is contained in:
Macbook
2026-05-27 09:32:26 +09:00
parent d0985f8f0f
commit 8876dbd752
12 changed files with 595 additions and 73 deletions
+89 -17
View File
@@ -31,7 +31,7 @@ export interface IndicatorPeriodDef {
investPsy: number;
}
const VALUE_FIELD_PREFIX: Record<string, string> = {
export const VALUE_FIELD_PREFIX: Record<string, string> = {
RSI: 'RSI_VALUE',
CCI: 'CCI_VALUE',
ADX: 'ADX_VALUE',
@@ -69,33 +69,70 @@ function parseFieldPeriod(field?: string): number | null {
return Number.isFinite(n) && n > 0 ? n : null;
}
/** 조건 노드의 RSI 등 값(좌측) 기간 — 오버라이드 시 DSL, 아니면 보조지표 설정(DEF) */
export function isValuePeriodOverridden(cond: ConditionDSL): boolean {
if (cond.valuePeriodOverride === true) return true;
if (cond.valuePeriodOverride === false) return false;
if (cond.period != null && cond.period > 0) return true;
if (cond.leftPeriod != null && cond.leftPeriod > 0) return true;
if (isValuePeriodFieldStored(cond.indicatorType, cond.leftField)) return true;
return false;
}
export function isRightPeriodOverridden(cond: ConditionDSL): boolean {
if (cond.rightPeriodOverride === true) return true;
if (cond.rightPeriodOverride === false) return false;
if (cond.rightPeriod != null && cond.rightPeriod > 0) return true;
return false;
}
export function isThresholdOverridden(cond: ConditionDSL): boolean {
if (cond.thresholdOverride === true) return true;
if (cond.thresholdOverride === false) return false;
return false;
}
/** 조건 노드의 RSI 등 값(좌측) 기간 */
export function getConditionValuePeriod(cond: ConditionDSL, def: IndicatorPeriodDef): number {
if (isPriceExtremeIndicator(cond.indicatorType)) {
if (cond.period && cond.period > 0) return cond.period;
const fromRight = parsePriorExtremePeriod(cond.rightField);
if (fromRight) return fromRight;
if (isValuePeriodOverridden(cond)) {
if (cond.period && cond.period > 0) return cond.period;
const fromRight = parsePriorExtremePeriod(cond.rightField);
if (fromRight) return fromRight;
}
return getDefaultIndicatorPeriod(cond.indicatorType, def);
}
if (cond.composite) {
if (cond.leftPeriod && cond.leftPeriod > 0) return cond.leftPeriod;
const fromField = parsePeriodFromCompositeField(cond.indicatorType, cond.leftField);
if (isValuePeriodOverridden(cond)) {
if (cond.leftPeriod && cond.leftPeriod > 0) return cond.leftPeriod;
const fromField = parsePeriodFromCompositeField(cond.indicatorType, cond.leftField);
if (fromField) return fromField;
}
const { short } = getCompositeDefaultPeriods(cond.indicatorType, def as CompositePeriodDef);
return short;
}
if (isValuePeriodOverridden(cond)) {
if (cond.period && cond.period > 0) return cond.period;
const fromField = parseFieldPeriod(cond.leftField);
if (fromField) return fromField;
}
if (cond.period && cond.period > 0) return cond.period;
const fromField = parseFieldPeriod(cond.leftField);
if (fromField) return fromField;
return getDefaultIndicatorPeriod(cond.indicatorType, def);
}
export function getConditionRightPeriod(cond: ConditionDSL, def: IndicatorPeriodDef): number {
if (cond.composite) {
if (cond.rightPeriod && cond.rightPeriod > 0) return cond.rightPeriod;
const fromField = parsePeriodFromCompositeField(cond.indicatorType, cond.rightField);
if (isRightPeriodOverridden(cond)) {
if (cond.rightPeriod && cond.rightPeriod > 0) return cond.rightPeriod;
const fromField = parsePeriodFromCompositeField(cond.indicatorType, cond.rightField);
if (fromField) return fromField;
}
const { long } = getCompositeDefaultPeriods(cond.indicatorType, def as CompositePeriodDef);
return long;
}
if (isValuePeriodOverridden(cond)) {
const fromField = parseFieldPeriod(cond.rightField);
if (fromField) return fromField;
}
const fromField = parseFieldPeriod(cond.rightField);
if (fromField) return fromField;
return getDefaultIndicatorPeriod(cond.indicatorType, def);
}
@@ -142,6 +179,9 @@ export function hasEditableThreshold(cond: ConditionDSL): boolean {
}
export function getConditionThreshold(cond: ConditionDSL, side: 'left' | 'right'): number | null {
if (side === 'right' && !isThresholdOverridden(cond)) {
return parseThresholdField(cond.rightField);
}
const field = side === 'left' ? cond.leftField : cond.rightField;
return parseThresholdField(field);
}
@@ -153,13 +193,13 @@ export function setConditionValuePeriod(
): ConditionDSL {
const p = Math.max(1, Math.min(500, Math.round(period)));
if (isPriceExtremeIndicator(cond.indicatorType)) {
return syncPriceExtremeSimpleFields({ ...cond, period: p });
return syncPriceExtremeSimpleFields({ ...cond, period: p, valuePeriodOverride: true });
}
if (cond.composite) {
return syncCompositeFields({ ...cond, composite: true, leftPeriod: p });
return syncCompositeFields({ ...cond, composite: true, leftPeriod: p, valuePeriodOverride: true });
}
const prefix = VALUE_FIELD_PREFIX[cond.indicatorType];
const next: ConditionDSL = { ...cond, period: p };
const next: ConditionDSL = { ...cond, period: p, valuePeriodOverride: true };
if (prefix && (cond.leftField === prefix || cond.leftField?.startsWith(`${prefix}_`))) {
next.leftField = `${prefix}_${p}`;
}
@@ -169,7 +209,7 @@ export function setConditionValuePeriod(
export function setConditionRightPeriod(cond: ConditionDSL, period: number): ConditionDSL {
const p = Math.max(1, Math.min(500, Math.round(period)));
if (!cond.composite) return cond;
return syncCompositeFields({ ...cond, composite: true, rightPeriod: p });
return syncCompositeFields({ ...cond, composite: true, rightPeriod: p, rightPeriodOverride: true });
}
export function setConditionThreshold(
@@ -184,9 +224,41 @@ export function setConditionThreshold(
...cond,
[fieldKey]: thresholdField(value),
targetValue: value,
thresholdOverride: true,
};
}
/** 신규 조건 노드 — 보조지표 설정 기본값 상속(오버라이드 없음) */
export function initConditionPeriodsInherit(
indicatorType: string,
condition: ConditionDSL,
def: IndicatorPeriodDef,
): ConditionDSL {
const base = {
...condition,
valuePeriodOverride: false as const,
thresholdOverride: false as const,
rightPeriodOverride: false as const,
};
if (isPriceExtremeIndicator(indicatorType)) {
const period = getDefaultIndicatorPeriod(indicatorType, def);
return syncPriceExtremeSimpleFields({ ...base, period });
}
if (condition.composite) return base;
const prefix = VALUE_FIELD_PREFIX[indicatorType];
if (!prefix) {
const { period: _p, ...rest } = base;
return rest;
}
const lf = condition.leftField ?? '';
if (lf === prefix || lf.startsWith(`${prefix}_`)) {
const { period: _p, ...rest } = base;
return { ...rest, leftField: prefix };
}
const { period: _p, ...rest } = base;
return rest;
}
export function initConditionPeriods(
indicatorType: string,
condition: ConditionDSL,