직접입력값 평가오류 수정
This commit is contained in:
@@ -1,94 +1,68 @@
|
||||
/** 정규화 로직 회귀 검증 — node scripts/test-threshold-normalize.mjs */
|
||||
function isThresholdOverridden(cond) {
|
||||
return cond.thresholdOverride === true;
|
||||
}
|
||||
function parseThresholdField(field) {
|
||||
if (!field?.startsWith('K_')) return null;
|
||||
const n = parseFloat(field.slice(2));
|
||||
return Number.isFinite(n) ? n : null;
|
||||
}
|
||||
const DEFAULT = { RSI: { over: 70, mid: 50, under: 30 } };
|
||||
function inferThresholdRoleForValue(value, indicatorType) {
|
||||
const key = indicatorType?.toUpperCase?.() ?? indicatorType;
|
||||
const th = DEFAULT[key] ?? {};
|
||||
const eps = 0.0001;
|
||||
if (th.over != null && Math.abs(th.over - value) < eps) return 'HL_OVER';
|
||||
if (th.mid != null && Math.abs(th.mid - value) < eps) return 'HL_MID';
|
||||
if (th.under != null && Math.abs(th.under - value) < eps) return 'HL_UNDER';
|
||||
return null;
|
||||
}
|
||||
function inferFromK(field, indicatorType) {
|
||||
if (!field?.startsWith('K_')) return null;
|
||||
const val = parseFloat(field.slice(2));
|
||||
return inferThresholdRoleForValue(val, indicatorType);
|
||||
}
|
||||
function canonicalizeCondition(cond) {
|
||||
let next = { ...cond, indicatorType: cond.indicatorType?.toUpperCase?.() ?? cond.indicatorType };
|
||||
const targetNum = typeof next.targetValue === 'number' && Number.isFinite(next.targetValue)
|
||||
? next.targetValue
|
||||
: null;
|
||||
const fromField = parseThresholdField(next.rightField);
|
||||
const val = targetNum ?? fromField;
|
||||
if (val != null && Number.isFinite(val)) {
|
||||
const role = inferThresholdRoleForValue(val, next.indicatorType);
|
||||
if (role) {
|
||||
const { targetValue, ...rest } = next;
|
||||
return { ...rest, rightField: role, thresholdOverride: false };
|
||||
|
||||
function ensureDirectThresholdSnapshot(cond) {
|
||||
const indicatorType = cond.indicatorType?.toUpperCase?.() ?? cond.indicatorType;
|
||||
let next = { ...cond, indicatorType };
|
||||
for (const key of ['rightField', 'leftField']) {
|
||||
const f = next[key];
|
||||
if (!f?.startsWith('K_')) continue;
|
||||
const parsed = parseThresholdField(f);
|
||||
if (parsed == null) continue;
|
||||
if (next.targetValue == null || !Number.isFinite(next.targetValue)) {
|
||||
next = { ...next, targetValue: parsed, thresholdOverride: true };
|
||||
} else if (next.thresholdOverride !== true) {
|
||||
next = { ...next, thresholdOverride: true };
|
||||
}
|
||||
}
|
||||
if (next.rightField?.startsWith('K_')) {
|
||||
const role = inferFromK(next.rightField, next.indicatorType);
|
||||
if (role) {
|
||||
const { targetValue, ...rest } = next;
|
||||
return { ...rest, rightField: role, thresholdOverride: false };
|
||||
}
|
||||
}
|
||||
return next;
|
||||
}
|
||||
function isConditionCarrier(node) {
|
||||
return !!node.condition && (node.type === 'CONDITION' || node.type == null);
|
||||
}
|
||||
function normalizeNode(node) {
|
||||
let next = { ...node };
|
||||
if (isConditionCarrier(node)) {
|
||||
next = { ...node, type: node.type ?? 'CONDITION', condition: canonicalizeCondition(node.condition) };
|
||||
}
|
||||
if (next.children?.length) {
|
||||
next.children = next.children.map(normalizeNode);
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
/** 차트 hline mid=48 이어도 직접입력 50 → HL_MID (Ta4j 기본값 기준) */
|
||||
function setConditionThreshold(cond, value) {
|
||||
const role = inferThresholdRoleForValue(value, cond.indicatorType);
|
||||
if (role) {
|
||||
const { targetValue, ...rest } = cond;
|
||||
return { ...rest, rightField: role, thresholdOverride: false };
|
||||
}
|
||||
return { ...cond, rightField: `K_${value}`, targetValue: value, thresholdOverride: true };
|
||||
function setDirectThreshold(cond, side, value) {
|
||||
const fieldKey = side === 'left' ? 'leftField' : 'rightField';
|
||||
return {
|
||||
...cond,
|
||||
[fieldKey]: `K_${value}`,
|
||||
targetValue: value,
|
||||
thresholdOverride: true,
|
||||
};
|
||||
}
|
||||
|
||||
function setConditionThreshold(cond, value, directInput) {
|
||||
if (directInput) return setDirectThreshold(cond, 'right', value);
|
||||
return setDirectThreshold(cond, 'right', value);
|
||||
}
|
||||
|
||||
let failed = 0;
|
||||
const cases = [
|
||||
{ type: 'CONDITION', condition: { indicatorType: 'RSI', rightField: 'K_50', thresholdOverride: true, targetValue: 50 } },
|
||||
{ condition: { indicatorType: 'RSI', rightField: 'K_50', thresholdOverride: true, targetValue: 50 } },
|
||||
{ type: 'TIMEFRAME', children: [{ condition: { indicatorType: 'RSI', rightField: 'K_50', thresholdOverride: true, targetValue: 50 } }] },
|
||||
];
|
||||
for (const c of cases) {
|
||||
const r = normalizeNode(c);
|
||||
const cond = r.condition ?? r.children?.[0]?.condition;
|
||||
const ok = cond?.rightField === 'HL_MID' && cond?.thresholdOverride === false;
|
||||
function assertOk(label, ok) {
|
||||
if (!ok) failed++;
|
||||
console.log(ok ? 'PASS' : 'FAIL', JSON.stringify(c).slice(0, 60), '=>', cond?.rightField, cond?.thresholdOverride);
|
||||
console.log(ok ? 'PASS' : 'FAIL', label);
|
||||
}
|
||||
|
||||
const direct = setConditionThreshold(
|
||||
{ indicatorType: 'RSI', rightField: 'HL_OVER', thresholdOverride: false },
|
||||
50,
|
||||
);
|
||||
const directOk = direct.rightField === 'HL_MID' && direct.thresholdOverride === false;
|
||||
if (!directOk) failed++;
|
||||
console.log(directOk ? 'PASS' : 'FAIL', 'setConditionThreshold(50) with chart mid≠50 =>', direct.rightField);
|
||||
const k55 = ensureDirectThresholdSnapshot({
|
||||
indicatorType: 'RSI',
|
||||
rightField: 'K_55',
|
||||
thresholdOverride: true,
|
||||
targetValue: 55,
|
||||
});
|
||||
assertOk('K_55 preserved', k55.rightField === 'K_55' && k55.targetValue === 55 && k55.thresholdOverride === true);
|
||||
|
||||
const k50 = ensureDirectThresholdSnapshot({
|
||||
indicatorType: 'RSI',
|
||||
rightField: 'K_50',
|
||||
thresholdOverride: true,
|
||||
targetValue: 50,
|
||||
});
|
||||
assertOk('K_50 preserved (not HL_MID)', k50.rightField === 'K_50' && k50.thresholdOverride === true);
|
||||
|
||||
const direct55 = setConditionThreshold({ indicatorType: 'RSI', rightField: 'HL_MID', thresholdOverride: false }, 55, true);
|
||||
assertOk('directInput 55 => K_55', direct55.rightField === 'K_55' && direct55.targetValue === 55);
|
||||
|
||||
const filled = ensureDirectThresholdSnapshot({ indicatorType: 'RSI', rightField: 'K_55', thresholdOverride: false });
|
||||
assertOk('K_55 fills targetValue', filled.targetValue === 55 && filled.thresholdOverride === true);
|
||||
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
|
||||
Reference in New Issue
Block a user