1
This commit is contained in:
parent
d16bb53e60
commit
78d1c3ac37
|
|
@ -831,6 +831,130 @@ async def check_config_feasibility(
|
|||
raise HTTPException(status_code=500, detail=f"检查配置可行性失败: {str(e)}")
|
||||
|
||||
|
||||
@router.put("/global/{key}")
|
||||
async def update_global_config(
|
||||
key: str,
|
||||
item: ConfigUpdate,
|
||||
user: Dict[str, Any] = Depends(get_current_user),
|
||||
):
|
||||
"""更新单个全局策略配置(仅管理员)"""
|
||||
if (user.get("role") or "user") != "admin":
|
||||
raise HTTPException(status_code=403, detail="仅管理员可修改全局策略配置")
|
||||
|
||||
try:
|
||||
from database.models import GlobalStrategyConfig
|
||||
|
||||
# 验证配置值
|
||||
config_type = item.type or "string"
|
||||
if config_type == 'number':
|
||||
try:
|
||||
float(item.value)
|
||||
except (ValueError, TypeError):
|
||||
raise HTTPException(status_code=400, detail=f"Invalid number value for {key}")
|
||||
elif config_type == 'boolean':
|
||||
if not isinstance(item.value, bool):
|
||||
if isinstance(item.value, str):
|
||||
item.value = item.value.lower() in ('true', '1', 'yes', 'on')
|
||||
else:
|
||||
item.value = bool(item.value)
|
||||
|
||||
# 特殊验证:百分比配置应该在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%)"
|
||||
)
|
||||
|
||||
# 更新数据库
|
||||
GlobalStrategyConfig.set(
|
||||
key,
|
||||
item.value,
|
||||
config_type,
|
||||
item.category or "strategy",
|
||||
item.description,
|
||||
updated_by=user.get("username")
|
||||
)
|
||||
|
||||
# 更新缓存 (ConfigManager)
|
||||
try:
|
||||
import config_manager
|
||||
# 更新 GlobalStrategyConfigManager 的缓存
|
||||
if hasattr(config_manager, 'GlobalStrategyConfigManager'):
|
||||
mgr = config_manager.GlobalStrategyConfigManager()
|
||||
# 写入Redis (会更新 redis 和 _cache)
|
||||
mgr._set_to_redis(key, item.value)
|
||||
# 手动更新本地缓存,确保当前进程也生效
|
||||
mgr._cache[key] = item.value
|
||||
logger.info(f"全局配置已更新到Redis缓存: {key} = {item.value}")
|
||||
except Exception as e:
|
||||
logger.warning(f"更新全局配置缓存失败: {e}")
|
||||
|
||||
return {
|
||||
"message": "全局配置已更新",
|
||||
"key": key,
|
||||
"value": item.value
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/global/batch")
|
||||
async def update_global_configs_batch(
|
||||
configs: list[ConfigItem],
|
||||
user: Dict[str, Any] = Depends(get_current_user),
|
||||
):
|
||||
"""批量更新全局策略配置(仅管理员)"""
|
||||
if (user.get("role") or "user") != "admin":
|
||||
raise HTTPException(status_code=403, detail="仅管理员可修改全局策略配置")
|
||||
|
||||
try:
|
||||
from database.models import GlobalStrategyConfig
|
||||
import config_manager
|
||||
|
||||
updated_count = 0
|
||||
mgr = None
|
||||
if hasattr(config_manager, 'GlobalStrategyConfigManager'):
|
||||
mgr = config_manager.GlobalStrategyConfigManager()
|
||||
|
||||
for item in configs:
|
||||
# 简单验证
|
||||
config_type = item.type
|
||||
val = item.value
|
||||
if config_type == 'number':
|
||||
try:
|
||||
val = float(val)
|
||||
except:
|
||||
continue
|
||||
elif config_type == 'boolean':
|
||||
if not isinstance(val, bool):
|
||||
val = str(val).lower() in ('true', '1', 'yes', 'on')
|
||||
|
||||
# 更新DB
|
||||
GlobalStrategyConfig.set(
|
||||
item.key,
|
||||
val,
|
||||
config_type,
|
||||
item.category,
|
||||
item.description,
|
||||
updated_by=user.get("username")
|
||||
)
|
||||
|
||||
# 更新缓存
|
||||
if mgr:
|
||||
mgr._set_to_redis(item.key, val)
|
||||
mgr._cache[item.key] = val
|
||||
|
||||
updated_count += 1
|
||||
|
||||
return {"message": f"成功更新 {updated_count} 个全局配置项"}
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ const ConfigItem = ({ label, config, onUpdate, disabled }) => {
|
|||
'LIMIT_ORDER_OFFSET_PCT',
|
||||
'ENTRY_MAX_DRIFT_PCT_TRENDING',
|
||||
'ENTRY_MAX_DRIFT_PCT_RANGING',
|
||||
'FIXED_RISK_PERCENT', // 固定风险百分比,已经是小数形式(0.02 = 2%)
|
||||
// FIXED_RISK_PERCENT 已移除,直接显示小数
|
||||
])
|
||||
const isPctLike = PCT_LIKE_KEYS.has(label)
|
||||
const isNumberAsIs = NUMBER_AS_IS_KEYS.has(label)
|
||||
|
|
@ -429,6 +429,24 @@ const GlobalConfig = () => {
|
|||
MIN_HOLD_TIME_SEC: 1800,
|
||||
USE_DYNAMIC_ATR_MULTIPLIER: false, // 关闭动态调整,使用固定2.5倍
|
||||
}
|
||||
},
|
||||
script_v1: {
|
||||
name: '高收益趋势 (脚本配置)',
|
||||
desc: '原 update_global_config.py 脚本中的配置。高止盈(60%) + 紧止损(10%) + 移动止损保护。适合捕捉大单边行情。',
|
||||
configs: {
|
||||
TAKE_PROFIT_PERCENT: 60.0,
|
||||
TAKE_PROFIT_1_PERCENT: 30.0,
|
||||
STOP_LOSS_PERCENT: 10.0,
|
||||
TRAILING_STOP_ACTIVATION: 30.0,
|
||||
TRAILING_STOP_PROTECT: 5.0,
|
||||
LEVERAGE: 4,
|
||||
RISK_REWARD_RATIO: 3.0,
|
||||
ATR_TAKE_PROFIT_MULTIPLIER: 4.5,
|
||||
ATR_STOP_LOSS_MULTIPLIER: 1.5,
|
||||
USE_FIXED_RISK_SIZING: true,
|
||||
FIXED_RISK_PERCENT: 1.0, // 1%
|
||||
MAX_LEVERAGE_SMALL_CAP: 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -447,6 +465,17 @@ const GlobalConfig = () => {
|
|||
BETA_FILTER_THRESHOLD: { value: -0.005, type: 'number', category: 'strategy', description: '大盘共振阈值(比例,如 -0.005 表示 -0.5%)。' },
|
||||
RSI_EXTREME_REVERSE_ENABLED: { value: false, type: 'boolean', category: 'strategy', description: '开启后:原信号做多但 RSI 超买(≥做多上限)时改为做空;原信号做空但 RSI 超卖(≤做空下限)时改为做多。属均值回归思路,可填补超买超卖时不下单的空置;默认关闭。' },
|
||||
RSI_EXTREME_REVERSE_ONLY_NEUTRAL_4H: { value: true, type: 'boolean', category: 'strategy', description: '建议开启:仅在 4H 趋势为中性时允许 RSI 反向单,避免在强趋势里逆势抄底/摸顶,降低风险。关闭则反向可与 4H 同向(仍受“禁止逆4H趋势”约束)。' },
|
||||
TAKE_PROFIT_PERCENT: { value: 0.25, type: 'number', category: 'risk', description: '止盈百分比(保证金比例,如 25.0=25%)。' },
|
||||
STOP_LOSS_PERCENT: { value: 0.08, type: 'number', category: 'risk', description: '止损百分比(保证金比例,如 8.0=8%)。' },
|
||||
TRAILING_STOP_ACTIVATION: { value: 0.20, type: 'number', category: 'risk', description: '移动止损激活阈值(盈利达到此比例后激活,如 20.0=20%)。' },
|
||||
TRAILING_STOP_PROTECT: { value: 0.10, type: 'number', category: 'risk', description: '移动止损保护阈值(回撤至此比例时触发止损,如 10.0=10%)。' },
|
||||
LEVERAGE: { value: 5, type: 'number', category: 'risk', description: '基础杠杆倍数。' },
|
||||
RISK_REWARD_RATIO: { value: 1.5, type: 'number', category: 'risk', description: '盈亏比目标(用于计算动态止盈止损)。' },
|
||||
ATR_TAKE_PROFIT_MULTIPLIER: { value: 1.5, type: 'number', category: 'risk', description: 'ATR 止盈倍数。' },
|
||||
ATR_STOP_LOSS_MULTIPLIER: { value: 2.5, type: 'number', category: 'risk', description: 'ATR 止损倍数。' },
|
||||
USE_FIXED_RISK_SIZING: { value: false, type: 'boolean', category: 'risk', description: '是否使用固定风险仓位计算(基于止损距离和账户余额)。' },
|
||||
FIXED_RISK_PERCENT: { value: 0.01, type: 'number', category: 'risk', description: '每笔交易固定风险百分比(如 0.01=1%)。' },
|
||||
MAX_LEVERAGE_SMALL_CAP: { value: 3, type: 'number', category: 'risk', description: '小市值/山寨币最大允许杠杆。' },
|
||||
}
|
||||
|
||||
const loadConfigs = async () => {
|
||||
|
|
@ -950,6 +979,7 @@ const GlobalConfig = () => {
|
|||
conservative: { group: 'legacy', tag: '传统' },
|
||||
balanced: { group: 'legacy', tag: '传统' },
|
||||
aggressive: { group: 'legacy', tag: '高频实验' },
|
||||
script_v1: { group: 'legacy', tag: '脚本迁移' },
|
||||
}
|
||||
|
||||
const presetGroups = [
|
||||
|
|
@ -975,7 +1005,7 @@ const GlobalConfig = () => {
|
|||
key: 'legacy',
|
||||
title: 'C. 传统 / 实验(不建议长期)',
|
||||
desc: '这组更多用于对比或临时实验(频率更高/更容易过度交易),建议在稳定盈利前谨慎使用。',
|
||||
presetKeys: ['conservative', 'balanced', 'aggressive'],
|
||||
presetKeys: ['conservative', 'balanced', 'aggressive', 'script_v1'],
|
||||
},
|
||||
]
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user