지표탭 추가
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user