a
This commit is contained in:
parent
6e23c924b2
commit
aaca165f55
|
|
@ -356,6 +356,18 @@ async def get_global_configs(
|
||||||
|
|
||||||
# 添加更多核心策略配置的默认值(确保前端能显示所有重要配置)
|
# 添加更多核心策略配置的默认值(确保前端能显示所有重要配置)
|
||||||
ADDITIONAL_STRATEGY_DEFAULTS = {
|
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": {
|
"USE_ATR_STOP_LOSS": {
|
||||||
"value": True,
|
"value": True,
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,26 @@ import './GlobalConfig.css'
|
||||||
import './ConfigPanel.css' // 复用 ConfigPanel 的样式
|
import './ConfigPanel.css' // 复用 ConfigPanel 的样式
|
||||||
|
|
||||||
// 复用 ConfigPanel 的 ConfigItem 组件
|
// 复用 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 ConfigItem = ({ label, config, onUpdate, disabled }) => {
|
||||||
const isPercentKey = label.includes('PERCENT') || label.includes('PCT')
|
const isPercentKey = label.includes('PERCENT') || label.includes('PCT')
|
||||||
const PCT_LIKE_KEYS = new Set([
|
const PCT_LIKE_KEYS = new Set([
|
||||||
|
|
@ -16,7 +36,8 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => {
|
||||||
'FIXED_RISK_PERCENT', // 固定风险百分比,已经是小数形式(0.02 = 2%)
|
'FIXED_RISK_PERCENT', // 固定风险百分比,已经是小数形式(0.02 = 2%)
|
||||||
])
|
])
|
||||||
const isPctLike = PCT_LIKE_KEYS.has(label)
|
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) => {
|
const formatPercent = (n) => {
|
||||||
if (typeof n !== 'number' || isNaN(n)) return ''
|
if (typeof n !== 'number' || isNaN(n)) return ''
|
||||||
|
|
@ -24,6 +45,11 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const getInitialDisplayValue = (val) => {
|
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 (config.type === 'number' && isPercentKey) {
|
||||||
if (val === null || val === undefined || val === '') {
|
if (val === null || val === undefined || val === '') {
|
||||||
return ''
|
return ''
|
||||||
|
|
@ -32,13 +58,9 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => {
|
||||||
if (isNaN(numVal)) {
|
if (isNaN(numVal)) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
// ⚠️ 简化:直接显示数据库中的值(0.30),用户直接输入小数(0.30)
|
|
||||||
// 如果值>1,说明可能是旧数据(百分比形式),需要转换;否则直接显示
|
|
||||||
if (isPctLike) {
|
if (isPctLike) {
|
||||||
// pct-like:值本身就是"百分比数值"(<=1表示<=1%),直接显示
|
|
||||||
return formatPercent(numVal <= 0.05 ? numVal * 100 : numVal)
|
return formatPercent(numVal <= 0.05 ? numVal * 100 : numVal)
|
||||||
}
|
}
|
||||||
// 常规比例型:直接显示数据库中的值(0.30),如果>1则可能是旧数据
|
|
||||||
return formatPercent(numVal <= 1 ? numVal : numVal / 100)
|
return formatPercent(numVal <= 1 ? numVal : numVal / 100)
|
||||||
}
|
}
|
||||||
return val === null || val === undefined ? '' : val
|
return val === null || val === undefined ? '' : val
|
||||||
|
|
@ -63,21 +85,26 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => {
|
||||||
if (!isEditing) return
|
if (!isEditing) return
|
||||||
let finalValue = localValue
|
let finalValue = localValue
|
||||||
if (config.type === 'number') {
|
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)
|
const numVal = parseFloat(localValue)
|
||||||
if (isNaN(numVal)) {
|
if (isNaN(numVal)) {
|
||||||
setLocalValue(getInitialDisplayValue(config.value))
|
setLocalValue(getInitialDisplayValue(config.value))
|
||||||
setIsEditing(false)
|
setIsEditing(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// ⚠️ 简化:用户直接输入小数(0.30),直接存储,不做转换
|
|
||||||
// 验证范围:0-1之间(比例形式)
|
|
||||||
if (numVal < 0 || numVal > 1) {
|
if (numVal < 0 || numVal > 1) {
|
||||||
setLocalValue(getInitialDisplayValue(config.value))
|
setLocalValue(getInitialDisplayValue(config.value))
|
||||||
setIsEditing(false)
|
setIsEditing(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 直接使用输入的值(0.30),不做转换
|
|
||||||
finalValue = numVal
|
finalValue = numVal
|
||||||
} else {
|
} else {
|
||||||
finalValue = parseFloat(localValue)
|
finalValue = parseFloat(localValue)
|
||||||
|
|
@ -101,7 +128,7 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => {
|
||||||
return (
|
return (
|
||||||
<div className="config-item">
|
<div className="config-item">
|
||||||
<div className="config-item-header">
|
<div className="config-item-header">
|
||||||
<label>{label}</label>
|
<label>{displayLabel}</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="config-input-wrapper">
|
<div className="config-input-wrapper">
|
||||||
{config.type === 'boolean' ? (
|
{config.type === 'boolean' ? (
|
||||||
|
|
@ -124,10 +151,9 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => {
|
||||||
value={displayValue === '' ? '' : String(displayValue)}
|
value={displayValue === '' ? '' : String(displayValue)}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
let newValue = e.target.value
|
let newValue = e.target.value
|
||||||
if (config.type === 'number') {
|
if (config.type === 'number' && !isNumberAsIs) {
|
||||||
if (isPercentKey) {
|
if (isPercentKey) {
|
||||||
const numValue = parseFloat(newValue)
|
const numValue = parseFloat(newValue)
|
||||||
// ⚠️ 简化:验证范围改为0-1(比例形式),用户直接输入小数
|
|
||||||
if (newValue !== '' && !isNaN(numValue) && (numValue < 0 || numValue > 1)) {
|
if (newValue !== '' && !isNaN(numValue) && (numValue < 0 || numValue > 1)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user