11 KiB
11 KiB
配置值格式统一方案
🎯 问题分析
当前问题
数据库中的配置值(百分比形式):
TRAILING_STOP_ACTIVATION= 30(表示30%)TRAILING_STOP_PROTECT= 15(表示15%)MIN_VOLATILITY= 3(表示3%)
代码期望的格式(比例形式):
TRAILING_STOP_ACTIVATION= 0.30(表示30%)TRAILING_STOP_PROTECT= 0.15(表示15%)MIN_VOLATILITY= 0.03(表示3%)
临时修复:
- 在代码中添加格式转换逻辑(如果值>1,除以100)
- 问题:代码逻辑复杂,容易出错,维护困难
✅ 更好的解决方案
方案:统一数据格式(推荐)
原则:
- 数据库存储:统一使用比例形式(0.30表示30%)
- 前端输入:用户输入30,前端转换为0.30存储
- 前端显示:从数据库读取0.30,显示为30
- 代码逻辑:直接使用比例形式,不需要格式转换
优势:
- ✅ 数据格式统一:避免混乱
- ✅ 代码逻辑清晰:不需要到处做格式转换
- ✅ 减少出错:避免格式不一致导致的bug
- ✅ 易于维护:统一的格式,更容易理解和维护
🔧 实施步骤
步骤1:检查前端格式转换逻辑
当前前端逻辑(frontend/src/components/ConfigPanel.jsx:1588-1594):
// 常规比例型:前端按"百分比"输入(0~100),存储为 0~1
if (processedValue < 0 || processedValue > 100) {
setLocalValue(getInitialDisplayValue(value))
return
}
processedValue = processedValue / 100 // ✅ 前端已经转换为比例形式
结论:✅ 前端已经正确转换,用户输入30,会转换为0.30存储
步骤2:检查后端验证逻辑
当前后端验证(backend/api/routes/config.py:843-848):
# 特殊验证:百分比配置应该在0-1之间
if ('PERCENT' in key or 'PCT' in key) and config_type == 'number':
if not (0 <= float(item.value) <= 1):
raise HTTPException(
status_code=400,
detail=f"{key} must be between 0 and 1 (0% to 100%)"
)
结论:✅ 后端已经正确验证,只接受0-1之间的值
步骤3:数据迁移(修复现有数据)
问题:数据库中可能已经有旧数据是百分比形式(30而不是0.30)
解决方案:创建数据迁移脚本,将百分比形式转换为比例形式
需要迁移的配置项:
TRAILING_STOP_ACTIVATION(如果>1,除以100)TRAILING_STOP_PROTECT(如果>1,除以100)MIN_VOLATILITY(如果>1,除以100)TAKE_PROFIT_PERCENT(如果>1,除以100)STOP_LOSS_PERCENT(如果>1,除以100)- 其他百分比配置项
步骤4:移除代码中的格式转换(可选)
当前代码(backend/config_manager.py:735-758):
# ⚠️ 关键修复:百分比配置值格式转换
if isinstance(value, (int, float)) and value is not None:
percent_keys = [...]
if key in percent_keys:
if value > 1:
value = value / 100.0 # 格式转换
建议:
- 数据迁移完成后,可以移除格式转换逻辑
- 或者保留作为兼容性处理(标记为临时方案)
📝 数据迁移脚本
SQL迁移脚本
-- 迁移百分比配置项:将百分比形式(>1)转换为比例形式(除以100)
-- 1. 迁移 trading_config 表
UPDATE trading_config
SET config_value = CAST(config_value AS DECIMAL(10, 4)) / 100.0
WHERE config_key IN (
'TRAILING_STOP_ACTIVATION',
'TRAILING_STOP_PROTECT',
'MIN_VOLATILITY',
'TAKE_PROFIT_PERCENT',
'STOP_LOSS_PERCENT',
'MIN_STOP_LOSS_PRICE_PCT',
'MIN_TAKE_PROFIT_PRICE_PCT',
'FIXED_RISK_PERCENT',
'MAX_POSITION_PERCENT',
'MAX_TOTAL_POSITION_PERCENT',
'MIN_POSITION_PERCENT'
)
AND config_type = 'number'
AND CAST(config_value AS DECIMAL(10, 4)) > 1;
-- 2. 迁移 global_strategy_config 表
UPDATE global_strategy_config
SET config_value = CAST(config_value AS DECIMAL(10, 4)) / 100.0
WHERE config_key IN (
'TRAILING_STOP_ACTIVATION',
'TRAILING_STOP_PROTECT',
'MIN_VOLATILITY',
'TAKE_PROFIT_PERCENT',
'STOP_LOSS_PERCENT',
'MIN_STOP_LOSS_PRICE_PCT',
'MIN_TAKE_PROFIT_PRICE_PCT',
'FIXED_RISK_PERCENT',
'MAX_POSITION_PERCENT',
'MAX_TOTAL_POSITION_PERCENT',
'MIN_POSITION_PERCENT'
)
AND config_type = 'number'
AND CAST(config_value AS DECIMAL(10, 4)) > 1;
🔍 验证步骤
1. 检查数据库中的配置值
-- 检查 trading_config 表中的百分比配置项
SELECT config_key, config_value, config_type
FROM trading_config
WHERE config_key IN (
'TRAILING_STOP_ACTIVATION',
'TRAILING_STOP_PROTECT',
'MIN_VOLATILITY',
'TAKE_PROFIT_PERCENT',
'STOP_LOSS_PERCENT'
)
AND config_type = 'number';
-- 检查 global_strategy_config 表中的百分比配置项
SELECT config_key, config_value, config_type
FROM global_strategy_config
WHERE config_key IN (
'TRAILING_STOP_ACTIVATION',
'TRAILING_STOP_PROTECT',
'MIN_VOLATILITY',
'TAKE_PROFIT_PERCENT',
'STOP_LOSS_PERCENT'
)
AND config_type = 'number';
2. 执行数据迁移
# 执行SQL迁移脚本
mysql -u username -p database_name < migrate_percent_configs.sql
3. 验证迁移结果
-- 验证:所有百分比配置项应该在0-1之间
SELECT config_key, config_value
FROM trading_config
WHERE config_key IN (
'TRAILING_STOP_ACTIVATION',
'TRAILING_STOP_PROTECT',
'MIN_VOLATILITY',
'TAKE_PROFIT_PERCENT',
'STOP_LOSS_PERCENT'
)
AND config_type = 'number'
AND (CAST(config_value AS DECIMAL(10, 4)) < 0 OR CAST(config_value AS DECIMAL(10, 4)) > 1);
-- 应该返回0行
4. 清除Redis缓存
# 清除Redis缓存,让系统重新加载配置
redis-cli FLUSHDB
# 或者只清除配置相关的key
redis-cli DEL "config:trading_config:*"
redis-cli DEL "config:global_strategy_config:*"
🎯 实施建议
方案A:立即迁移数据(推荐)
步骤:
- ✅ 创建数据迁移脚本
- ✅ 执行数据迁移
- ✅ 验证迁移结果
- ✅ 清除Redis缓存
- ⚠️ 保留代码中的格式转换作为兼容性处理(标记为临时方案)
优势:
- 数据格式统一,避免混乱
- 代码逻辑清晰
- 减少出错
风险:
- 需要确保迁移脚本正确
- 需要备份数据库
方案B:保留格式转换(临时方案)
步骤:
- ✅ 保留代码中的格式转换逻辑
- ✅ 标记为临时兼容性处理
- ⚠️ 后续逐步迁移数据
优势:
- 不需要立即迁移数据
- 系统可以继续运行
劣势:
- 代码逻辑复杂
- 容易出错
- 维护困难
📊 对比分析
当前方案(代码中格式转换)
优点:
- ✅ 不需要立即迁移数据
- ✅ 系统可以继续运行
缺点:
- ❌ 代码逻辑复杂,需要到处做格式转换
- ❌ 容易出错(忘记转换)
- ❌ 维护困难
- ❌ 数据格式不统一
统一数据格式方案
优点:
- ✅ 数据格式统一,避免混乱
- ✅ 代码逻辑清晰,不需要格式转换
- ✅ 减少出错,易于维护
- ✅ 前端和后端验证逻辑一致
缺点:
- ⚠️ 需要数据迁移
- ⚠️ 需要备份数据库
✅ 最终建议
推荐方案:统一数据格式
理由:
- 架构更清晰:数据格式统一,代码逻辑简单
- 易于维护:不需要到处做格式转换
- 减少出错:避免格式不一致导致的bug
- 长期收益:虽然需要一次性迁移,但长期收益更大
实施步骤:
- 创建数据迁移脚本
- 备份数据库
- 执行数据迁移
- 验证迁移结果
- 清除Redis缓存
- 保留代码中的格式转换作为兼容性处理(标记为临时方案,后续可以移除)
📝 数据迁移脚本(完整版)
-- ============================================================
-- 配置值格式统一迁移脚本
-- 将百分比形式(>1)转换为比例形式(除以100)
-- ============================================================
-- 1. 备份表(可选,但强烈推荐)
CREATE TABLE trading_config_backup AS SELECT * FROM trading_config;
CREATE TABLE global_strategy_config_backup AS SELECT * FROM global_strategy_config;
-- 2. 迁移 trading_config 表
UPDATE trading_config
SET config_value = CAST(config_value AS DECIMAL(10, 4)) / 100.0
WHERE config_key IN (
'TRAILING_STOP_ACTIVATION',
'TRAILING_STOP_PROTECT',
'MIN_VOLATILITY',
'TAKE_PROFIT_PERCENT',
'STOP_LOSS_PERCENT',
'MIN_STOP_LOSS_PRICE_PCT',
'MIN_TAKE_PROFIT_PRICE_PCT',
'FIXED_RISK_PERCENT',
'MAX_POSITION_PERCENT',
'MAX_TOTAL_POSITION_PERCENT',
'MIN_POSITION_PERCENT'
)
AND config_type = 'number'
AND CAST(config_value AS DECIMAL(10, 4)) > 1;
-- 3. 迁移 global_strategy_config 表
UPDATE global_strategy_config
SET config_value = CAST(config_value AS DECIMAL(10, 4)) / 100.0
WHERE config_key IN (
'TRAILING_STOP_ACTIVATION',
'TRAILING_STOP_PROTECT',
'MIN_VOLATILITY',
'TAKE_PROFIT_PERCENT',
'STOP_LOSS_PERCENT',
'MIN_STOP_LOSS_PRICE_PCT',
'MIN_TAKE_PROFIT_PRICE_PCT',
'FIXED_RISK_PERCENT',
'MAX_POSITION_PERCENT',
'MAX_TOTAL_POSITION_PERCENT',
'MIN_POSITION_PERCENT'
)
AND config_type = 'number'
AND CAST(config_value AS DECIMAL(10, 4)) > 1;
-- 4. 验证迁移结果
-- 检查是否还有>1的百分比配置项
SELECT 'trading_config' as table_name, config_key, config_value
FROM trading_config
WHERE config_key IN (
'TRAILING_STOP_ACTIVATION',
'TRAILING_STOP_PROTECT',
'MIN_VOLATILITY',
'TAKE_PROFIT_PERCENT',
'STOP_LOSS_PERCENT'
)
AND config_type = 'number'
AND CAST(config_value AS DECIMAL(10, 4)) > 1
UNION ALL
SELECT 'global_strategy_config' as table_name, config_key, config_value
FROM global_strategy_config
WHERE config_key IN (
'TRAILING_STOP_ACTIVATION',
'TRAILING_STOP_PROTECT',
'MIN_VOLATILITY',
'TAKE_PROFIT_PERCENT',
'STOP_LOSS_PERCENT'
)
AND config_type = 'number'
AND CAST(config_value AS DECIMAL(10, 4)) > 1;
-- 应该返回0行
🎯 总结
当前方案的问题
代码中格式转换:
- ❌ 代码逻辑复杂
- ❌ 需要到处做格式转换
- ❌ 容易出错
- ❌ 维护困难
更好的方案
统一数据格式:
- ✅ 数据库存储比例形式(0.30表示30%)
- ✅ 前端输入时转换为比例形式
- ✅ 前端显示时转换为百分比形式
- ✅ 代码逻辑直接使用比例形式
实施建议
- 立即执行数据迁移:统一数据格式
- 保留格式转换作为兼容性处理:标记为临时方案
- 后续可以移除格式转换:数据格式统一后,可以移除