""" 配置管理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 # 计算使用最小仓位百分比时,实际能得到的保证金 min_position_value = available_balance * min_position_percent actual_min_margin = min_position_value / leverage if leverage > 0 else min_position_value # 检查是否可行: # 1. 需要的仓位百分比不能超过最大允许的仓位百分比 # 2. 使用最小仓位百分比时,实际保证金必须 >= 最小保证金要求 is_feasible = (required_position_percent <= max_position_percent) and (actual_min_margin >= min_margin_usdt) suggestions = [] if not is_feasible: # 不可行,给出建议 # 判断是哪个条件不满足 if required_position_percent > max_position_percent: # 条件1不满足:需要的仓位百分比超过最大允许 # 方案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}%" }) if actual_min_margin < min_margin_usdt: # 条件2不满足:使用最小仓位时,实际保证金不足 # 方案1:降低最小保证金到实际能支持的值 suggestions.append({ "type": "reduce_min_margin_to_supported", "title": "降低最小保证金到可支持值", "description": f"将 MIN_MARGIN_USDT 调整为 {actual_min_margin:.2f} USDT(当前: {min_margin_usdt:.2f} USDT,实际只支持 {actual_min_margin:.2f} USDT)", "config_key": "MIN_MARGIN_USDT", "suggested_value": round(actual_min_margin, 2), "reason": f"当前账户余额和配置下,使用最小仓位 {min_position_percent*100:.1f}% 时,实际保证金只有 {actual_min_margin:.2f} USDT,无法满足 {min_margin_usdt:.2f} USDT 的要求" }) # 方案2:提高最小仓位百分比 required_min_position_percent = (min_margin_usdt * leverage) / available_balance if available_balance > 0 else 0 if required_min_position_percent <= max_position_percent: suggestions.append({ "type": "increase_min_position", "title": "提高最小仓位百分比", "description": f"将 MIN_POSITION_PERCENT 调整为 {required_min_position_percent*100:.2f}%(当前: {min_position_percent*100:.1f}%)", "config_key": "MIN_POSITION_PERCENT", "suggested_value": round(required_min_position_percent, 4), "reason": f"需要至少 {required_min_position_percent*100:.2f}% 的仓位才能满足 {min_margin_usdt:.2f} USDT 的保证金要求" }) # 方案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, "min_position_value": min_position_value, "actual_min_margin": actual_min_margin, "min_position_percent": min_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))