diff --git a/backend/api/routes/config.py b/backend/api/routes/config.py index e01c0fc..e3e5cf3 100644 --- a/backend/api/routes/config.py +++ b/backend/api/routes/config.py @@ -356,6 +356,18 @@ async def get_global_configs( # 添加更多核心策略配置的默认值(确保前端能显示所有重要配置) ADDITIONAL_STRATEGY_DEFAULTS = { + "BETA_FILTER_ENABLED": { + "value": True, + "type": "boolean", + "category": "strategy", + "description": "大盘共振过滤:BTC/ETH 下跌时屏蔽多单", + }, + "BETA_FILTER_THRESHOLD": { + "value": -0.005, + "type": "number", + "category": "strategy", + "description": "大盘共振阈值(比例,如 -0.005 表示 -0.5%)", + }, "USE_ATR_STOP_LOSS": { "value": True, "type": "boolean", diff --git a/frontend/src/components/GlobalConfig.jsx b/frontend/src/components/GlobalConfig.jsx index 59c0d4b..3cab685 100644 --- a/frontend/src/components/GlobalConfig.jsx +++ b/frontend/src/components/GlobalConfig.jsx @@ -7,6 +7,26 @@ import './GlobalConfig.css' import './ConfigPanel.css' // 复用 ConfigPanel 的样式 // 复用 ConfigPanel 的 ConfigItem 组件 +// 部分配置项使用“数值原样”(如 RSI 0–100、24h 涨跌幅 25 表示 25%),不做 0–1 比例转换 +const NUMBER_AS_IS_KEYS = new Set([ + 'MAX_RSI_FOR_LONG', + 'MIN_RSI_FOR_SHORT', + 'MAX_CHANGE_PERCENT_FOR_LONG', + 'MAX_CHANGE_PERCENT_FOR_SHORT', +]) +// 配置项中文标签(便于识别) +const KEY_LABELS = { + MAX_RSI_FOR_LONG: '做多 RSI 上限', + MIN_RSI_FOR_SHORT: '做空 RSI 下限', + MAX_CHANGE_PERCENT_FOR_LONG: '做多 24h 涨跌幅上限(%)', + MAX_CHANGE_PERCENT_FOR_SHORT: '做空 24h 涨跌幅上限(%)', + TAKE_PROFIT_1_PERCENT: '第一目标止盈(%)', + SCAN_EXTRA_SYMBOLS_FOR_SUPPLEMENT: '智能补单候选数', + SYMBOL_LOSS_COOLDOWN_ENABLED: '连续亏损冷却', + SYMBOL_MAX_CONSECUTIVE_LOSSES: '连续亏损次数阈值', + SYMBOL_LOSS_COOLDOWN_SEC: '冷却时间(秒)', +} + const ConfigItem = ({ label, config, onUpdate, disabled }) => { const isPercentKey = label.includes('PERCENT') || label.includes('PCT') const PCT_LIKE_KEYS = new Set([ @@ -16,7 +36,8 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => { 'FIXED_RISK_PERCENT', // 固定风险百分比,已经是小数形式(0.02 = 2%) ]) const isPctLike = PCT_LIKE_KEYS.has(label) - const isRatioPercentKey = isPercentKey && !isPctLike + const isNumberAsIs = NUMBER_AS_IS_KEYS.has(label) + const displayLabel = KEY_LABELS[label] || label const formatPercent = (n) => { if (typeof n !== 'number' || isNaN(n)) return '' @@ -24,6 +45,11 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => { } const getInitialDisplayValue = (val) => { + if (config.type === 'number' && isNumberAsIs) { + if (val === null || val === undefined || val === '') return '' + const numVal = typeof val === 'string' ? parseFloat(val) : val + return isNaN(numVal) ? '' : String(numVal) + } if (config.type === 'number' && isPercentKey) { if (val === null || val === undefined || val === '') { return '' @@ -32,13 +58,9 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => { if (isNaN(numVal)) { return '' } - // ⚠️ 简化:直接显示数据库中的值(0.30),用户直接输入小数(0.30) - // 如果值>1,说明可能是旧数据(百分比形式),需要转换;否则直接显示 if (isPctLike) { - // pct-like:值本身就是"百分比数值"(<=1表示<=1%),直接显示 return formatPercent(numVal <= 0.05 ? numVal * 100 : numVal) } - // 常规比例型:直接显示数据库中的值(0.30),如果>1则可能是旧数据 return formatPercent(numVal <= 1 ? numVal : numVal / 100) } return val === null || val === undefined ? '' : val @@ -63,21 +85,26 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => { if (!isEditing) return let finalValue = localValue if (config.type === 'number') { - if (isPercentKey) { + if (isNumberAsIs) { + const numVal = parseFloat(localValue) + if (isNaN(numVal)) { + setLocalValue(getInitialDisplayValue(config.value)) + setIsEditing(false) + return + } + finalValue = numVal + } else if (isPercentKey) { const numVal = parseFloat(localValue) if (isNaN(numVal)) { setLocalValue(getInitialDisplayValue(config.value)) setIsEditing(false) return } - // ⚠️ 简化:用户直接输入小数(0.30),直接存储,不做转换 - // 验证范围:0-1之间(比例形式) if (numVal < 0 || numVal > 1) { setLocalValue(getInitialDisplayValue(config.value)) setIsEditing(false) return } - // 直接使用输入的值(0.30),不做转换 finalValue = numVal } else { finalValue = parseFloat(localValue) @@ -101,7 +128,7 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => { return (
- +
{config.type === 'boolean' ? ( @@ -124,10 +151,9 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => { value={displayValue === '' ? '' : String(displayValue)} onChange={(e) => { let newValue = e.target.value - if (config.type === 'number') { + if (config.type === 'number' && !isNumberAsIs) { if (isPercentKey) { const numValue = parseFloat(newValue) - // ⚠️ 简化:验证范围改为0-1(比例形式),用户直接输入小数 if (newValue !== '' && !isNaN(numValue) && (numValue < 0 || numValue > 1)) { return }