auto_trade_sys/backend/api/routes/config.py
薇薇安 004472bbd4 a
2026-01-17 18:58:53 +08:00

286 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
配置管理API
"""
from fastapi import APIRouter, HTTPException
from api.models.config import ConfigItem, ConfigUpdate
import sys
from pathlib import Path
import logging
# 添加项目根目录到路径
project_root = Path(__file__).parent.parent.parent.parent
sys.path.insert(0, str(project_root))
sys.path.insert(0, str(project_root / 'backend'))
sys.path.insert(0, str(project_root / 'trading_system'))
from database.models import TradingConfig
logger = logging.getLogger(__name__)
router = APIRouter()
@router.get("")
@router.get("/")
async def get_all_configs():
"""获取所有配置"""
try:
configs = TradingConfig.get_all()
result = {}
for config in configs:
result[config['config_key']] = {
'value': TradingConfig._convert_value(
config['config_value'],
config['config_type']
),
'type': config['config_type'],
'category': config['category'],
'description': config['description']
}
return result
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/feasibility-check")
async def check_config_feasibility():
"""
检查配置可行性,基于当前账户余额和杠杆倍数计算可行的配置建议
"""
try:
# 获取账户余额
try:
from api.routes.account import get_realtime_account_data
account_data = await get_realtime_account_data()
available_balance = account_data.get('available_balance', 0)
total_balance = account_data.get('total_balance', 0)
except Exception as e:
logger.warning(f"获取账户余额失败: {e},使用默认值")
available_balance = 0
total_balance = 0
if available_balance <= 0:
return {
"feasible": False,
"error": "无法获取账户余额请检查API配置",
"suggestions": []
}
# 获取当前配置
min_margin_usdt = TradingConfig.get_value('MIN_MARGIN_USDT', 5.0)
min_position_percent = TradingConfig.get_value('MIN_POSITION_PERCENT', 0.02)
max_position_percent = TradingConfig.get_value('MAX_POSITION_PERCENT', 0.08)
leverage = TradingConfig.get_value('LEVERAGE', 10)
# 计算最小保证金要求对应的最小仓位价值
required_position_value = min_margin_usdt * leverage
required_position_percent = required_position_value / available_balance if available_balance > 0 else 0
# 检查是否可行
is_feasible = required_position_percent <= max_position_percent
suggestions = []
if not is_feasible:
# 不可行,给出建议
# 方案1降低最小保证金
suggested_min_margin = (available_balance * max_position_percent) / leverage
suggestions.append({
"type": "reduce_min_margin",
"title": "降低最小保证金",
"description": f"将 MIN_MARGIN_USDT 调整为 {suggested_min_margin:.2f} USDT当前: {min_margin_usdt:.2f} USDT",
"config_key": "MIN_MARGIN_USDT",
"suggested_value": round(suggested_min_margin, 2),
"reason": f"当前配置需要 {required_position_percent*100:.1f}% 的仓位价值,但最大允许 {max_position_percent*100:.1f}%"
})
# 方案2增加账户余额
required_balance = required_position_value / max_position_percent
suggestions.append({
"type": "increase_balance",
"title": "增加账户余额",
"description": f"将账户余额增加到至少 {required_balance:.2f} USDT当前: {available_balance:.2f} USDT",
"config_key": None,
"suggested_value": round(required_balance, 2),
"reason": f"当前余额不足以满足最小保证金 {min_margin_usdt:.2f} USDT 的要求"
})
# 方案3降低杠杆倍数
suggested_leverage = int((available_balance * max_position_percent) / min_margin_usdt)
if suggested_leverage >= 1:
suggestions.append({
"type": "reduce_leverage",
"title": "降低杠杆倍数",
"description": f"将 LEVERAGE 调整为 {suggested_leverage}x当前: {leverage}x",
"config_key": "LEVERAGE",
"suggested_value": suggested_leverage,
"reason": f"降低杠杆可以减少所需仓位价值"
})
else:
# 可行,显示当前配置信息
actual_min_position_value = available_balance * min_position_percent
actual_min_margin = actual_min_position_value / leverage if leverage > 0 else actual_min_position_value
suggestions.append({
"type": "info",
"title": "配置可行",
"description": f"当前配置可以正常下单。最小仓位价值: {actual_min_position_value:.2f} USDT对应保证金: {actual_min_margin:.2f} USDT",
"config_key": None,
"suggested_value": None,
"reason": None
})
return {
"feasible": is_feasible,
"account_balance": available_balance,
"leverage": leverage,
"current_config": {
"min_margin_usdt": min_margin_usdt,
"min_position_percent": min_position_percent,
"max_position_percent": max_position_percent
},
"calculated_values": {
"required_position_value": required_position_value,
"required_position_percent": required_position_percent * 100,
"max_allowed_position_percent": max_position_percent * 100
},
"suggestions": suggestions
}
except Exception as e:
logger.error(f"检查配置可行性失败: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"检查配置可行性失败: {str(e)}")
@router.get("/{key}")
async def get_config(key: str):
"""获取单个配置"""
try:
config = TradingConfig.get(key)
if not config:
raise HTTPException(status_code=404, detail="Config not found")
return {
'key': config['config_key'],
'value': TradingConfig._convert_value(
config['config_value'],
config['config_type']
),
'type': config['config_type'],
'category': config['category'],
'description': config['description']
}
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.put("/{key}")
async def update_config(key: str, item: ConfigUpdate):
"""更新配置"""
try:
# 获取现有配置以确定类型和分类
existing = TradingConfig.get(key)
if not existing:
raise HTTPException(status_code=404, detail="Config not found")
config_type = item.type or existing['config_type']
category = item.category or existing['category']
description = item.description or existing['description']
# 验证配置值
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 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%)"
)
# 更新配置
TradingConfig.set(key, item.value, config_type, category, description)
# 清除配置缓存,确保立即生效
try:
from config_manager import ConfigManager
# 如果存在全局配置管理器实例,清除其缓存
import config_manager
if hasattr(config_manager, '_config_manager') and config_manager._config_manager:
config_manager._config_manager.reload()
except Exception as e:
logger.debug(f"清除配置缓存失败: {e}")
return {
"message": "配置已更新",
"key": key,
"value": item.value,
"note": "交易系统将在下次扫描时自动使用新配置"
}
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.post("/batch")
async def update_configs_batch(configs: list[ConfigItem]):
"""批量更新配置"""
try:
updated_count = 0
errors = []
for item in configs:
try:
# 验证配置值
if item.type == 'number':
try:
float(item.value)
except (ValueError, TypeError):
errors.append(f"{item.key}: Invalid number value")
continue
# 特殊验证:百分比配置
if 'PERCENT' in item.key and item.type == 'number':
if not (0 <= float(item.value) <= 1):
errors.append(f"{item.key}: Must be between 0 and 1")
continue
TradingConfig.set(
item.key,
item.value,
item.type,
item.category,
item.description
)
updated_count += 1
except Exception as e:
errors.append(f"{item.key}: {str(e)}")
if errors:
return {
"message": f"部分配置更新成功: {updated_count}/{len(configs)}",
"updated": updated_count,
"errors": errors,
"note": "交易系统将在下次扫描时自动使用新配置"
}
return {
"message": f"成功更新 {updated_count} 个配置",
"updated": updated_count,
"note": "交易系统将在下次扫描时自动使用新配置"
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))