From 7abf7db2df450b95fe289dc59090b86c9a1c05da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=87=E8=96=87=E5=AE=89?= Date: Sun, 15 Feb 2026 08:46:20 +0800 Subject: [PATCH] 1 --- frontend/src/components/GlobalConfig.jsx | 49 ++++++++++++++---------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/frontend/src/components/GlobalConfig.jsx b/frontend/src/components/GlobalConfig.jsx index b64e43a..5135218 100644 --- a/frontend/src/components/GlobalConfig.jsx +++ b/frontend/src/components/GlobalConfig.jsx @@ -229,6 +229,8 @@ const GlobalConfig = () => { altcoin: { name: '山寨币策略(推荐)', desc: '【安全推荐】总仓65%降低回撤,大盘-1%再屏蔽多单。3%风险/单,30%第一止盈/55%第二止盈,30%激活移动止损、10%保护。', + // 只比这几项与数据库一致即视为选中,避免因缺项或格式差异永远不中 + signatureKeys: ['AUTO_TRADE_ONLY_TRENDING', 'RSI_EXTREME_REVERSE_ENABLED', 'USE_MARGIN_CAP_FOR_TP', 'USE_MARGIN_CAP_FOR_SL', 'MAX_POSITION_PERCENT', 'MAX_DAILY_ENTRIES', 'TOP_N_SYMBOLS', 'FIXED_RISK_PERCENT'], configs: { // 风险与止盈止损 ATR_STOP_LOSS_MULTIPLIER: 3.0, @@ -908,17 +910,24 @@ const GlobalConfig = () => { // 管理员全局配置页面:不依赖任何 account,直接管理全局配置表 const isGlobalStrategyAccount = isAdmin - // 当前预设检测:与后端返回值比较,支持类型标准化(后端可能返回 string "8" / "true") + // 预设检测:只比「特征键」且与数据库一致(宽松:数字容差、布尔兼容字符串、百分比支持 0.2 与 20 两种存法) + const toNum = (v) => (typeof v === 'number' ? v : parseFloat(v)) + const toBool = (v) => (v === true || v === false ? v : (v === 'true' || String(v).toLowerCase() === 'true' || v === 1 || v === '1')) + const toRatio = (v, key) => { + const n = toNum(v) + if (Number.isNaN(n)) return null + if (key && (key.includes('PERCENT') || key.includes('PCT')) && n > 1) return n / 100 + return n + } const valueMatches = (cur, exp, key) => { - if (exp === true || exp === false) { - const b = cur === true || cur === false ? cur : (cur === 'true' || String(cur).toLowerCase() === 'true') - return b === exp - } + if (exp === true || exp === false) return toBool(cur) === exp if (typeof exp === 'number') { - const n = typeof cur === 'number' ? cur : parseFloat(cur) - if (Number.isNaN(n)) return false - const tolerance = (key && (key.includes('PERCENT') || key.includes('PCT'))) ? 0.001 : 0.0001 - return Math.abs(n - exp) <= tolerance + const r = key && (key.includes('PERCENT') || key.includes('PCT')) + const a = r ? toRatio(cur, key) : toNum(cur) + let b = exp + if (r && exp > 1) b = exp / 100 + if (a == null || Number.isNaN(a)) return false + return Math.abs(a - b) <= 0.002 } return cur === exp } @@ -926,21 +935,19 @@ const GlobalConfig = () => { if (configs && Object.keys(configs).length > 0 && presets) { try { for (const [presetKey, preset] of Object.entries(presets)) { + const keysToCheck = preset.signatureKeys && preset.signatureKeys.length > 0 + ? preset.signatureKeys + : Object.keys(preset.configs) let match = true - for (const [key, expectedValue] of Object.entries(preset.configs)) { + for (const key of keysToCheck) { const currentConfig = configs[key] - if (!currentConfig) continue // 后端未返回的 key 不参与匹配,避免因缺项导致永远不选中 - let cur = currentConfig.value - let exp = expectedValue - if (key.includes('PERCENT') || key.includes('PCT')) { - if (typeof exp === 'number' && exp > 1) exp = exp / 100 - } - if (!valueMatches(cur, exp, key)) { - match = false - break - } + if (!currentConfig) { match = false; break } + const exp = preset.configs[key] + if (exp === undefined) continue + const cur = currentConfig.value + if (!valueMatches(cur, exp, key)) { match = false; break } } - if (match) { + if (match && keysToCheck.length > 0) { currentPreset = presetKey break }