diff --git a/backend/api/routes/config.py b/backend/api/routes/config.py index 0b2ef5a..4d615d0 100644 --- a/backend/api/routes/config.py +++ b/backend/api/routes/config.py @@ -69,116 +69,189 @@ async def check_config_feasibility(): 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) + base_leverage = TradingConfig.get_value('LEVERAGE', 10) + max_leverage = TradingConfig.get_value('MAX_LEVERAGE', 15) + use_dynamic_leverage = TradingConfig.get_value('USE_DYNAMIC_LEVERAGE', True) - # 计算最小保证金要求对应的最小仓位价值 - required_position_value = min_margin_usdt * leverage - required_position_percent = required_position_value / available_balance if available_balance > 0 else 0 + # 检查所有可能的杠杆倍数(考虑动态杠杆) + leverage_to_check = [base_leverage] + if use_dynamic_leverage and max_leverage > base_leverage: + # 检查基础杠杆、最大杠杆,以及中间的关键值(如15x, 20x, 30x, 40x) + leverage_to_check.append(max_leverage) + # 添加常见的杠杆倍数 + for lev in [15, 20, 25, 30, 40, 50]: + if base_leverage < lev <= max_leverage and lev not in leverage_to_check: + leverage_to_check.append(lev) - # 计算使用最小仓位百分比时,实际能得到的保证金 - min_position_value = available_balance * min_position_percent - actual_min_margin = min_position_value / leverage if leverage > 0 else min_position_value + leverage_to_check = sorted(set(leverage_to_check)) - # 检查是否可行: - # 1. 需要的仓位百分比不能超过最大允许的仓位百分比 - # 2. 使用最小仓位百分比时,实际保证金必须 >= 最小保证金要求 - is_feasible = (required_position_percent <= max_position_percent) and (actual_min_margin >= min_margin_usdt) + # 检查每个杠杆倍数下的可行性 + leverage_results = [] + all_feasible = True + worst_case_leverage = None + worst_case_margin = None + + for leverage in leverage_to_check: + # 计算最小保证金要求对应的最小仓位价值 + 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. 使用最小仓位百分比时,实际保证金必须 >= 最小保证金要求 + condition1_ok = required_position_percent <= max_position_percent + condition2_ok = actual_min_margin >= min_margin_usdt + is_feasible_at_leverage = condition1_ok and condition2_ok + + leverage_results.append({ + 'leverage': leverage, + 'required_position_value': required_position_value, + 'required_position_percent': required_position_percent * 100, + 'actual_min_margin': actual_min_margin, + 'feasible': is_feasible_at_leverage, + 'condition1_ok': condition1_ok, + 'condition2_ok': condition2_ok + }) + + if not is_feasible_at_leverage: + all_feasible = False + # 记录最坏情况(实际保证金最小的) + if worst_case_margin is None or actual_min_margin < worst_case_margin: + worst_case_leverage = leverage + worst_case_margin = actual_min_margin + + # 使用基础杠杆的结果作为主要判断 + base_result = next((r for r in leverage_results if r['leverage'] == base_leverage), leverage_results[0]) + is_feasible = all_feasible 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}%" - }) + # 找出不可行的杠杆倍数 + infeasible_leverages = [r for r in leverage_results if not r['feasible']] - 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 的要求" - }) + if infeasible_leverages: + # 找出最坏情况(实际保证金最小的) + worst = min(infeasible_leverages, key=lambda x: x['actual_min_margin']) + worst_leverage = worst['leverage'] + worst_margin = worst['actual_min_margin'] - # 方案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: + # 方案1:基于最坏情况(最大杠杆)降低最小保证金 + if not worst['condition2_ok']: 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 的保证金要求" + "type": "reduce_min_margin_to_supported", + "title": f"降低最小保证金(考虑{worst_leverage}x杠杆)", + "description": f"将 MIN_MARGIN_USDT 调整为 {worst_margin:.2f} USDT(当前: {min_margin_usdt:.2f} USDT)。在{worst_leverage}x杠杆下,实际只支持 {worst_margin:.2f} USDT", + "config_key": "MIN_MARGIN_USDT", + "suggested_value": round(worst_margin, 2), + "reason": f"在{worst_leverage}x杠杆下,使用最小仓位 {min_position_percent*100:.1f}% 时,实际保证金只有 {worst_margin:.2f} USDT,无法满足 {min_margin_usdt:.2f} USDT 的要求" + }) + + # 方案2:基于最大杠杆计算需要的最小保证金 + max_leverage_result = next((r for r in leverage_results if r['leverage'] == max_leverage), None) + if max_leverage_result and not max_leverage_result['feasible']: + if max_leverage_result['required_position_percent'] > max_position_percent: + suggested_min_margin_max_lev = (available_balance * max_position_percent) / max_leverage + suggestions.append({ + "type": "reduce_min_margin_for_max_leverage", + "title": f"降低最小保证金(支持{max_leverage}x杠杆)", + "description": f"将 MIN_MARGIN_USDT 调整为 {suggested_min_margin_max_lev:.2f} USDT(当前: {min_margin_usdt:.2f} USDT),以支持{max_leverage}x杠杆", + "config_key": "MIN_MARGIN_USDT", + "suggested_value": round(suggested_min_margin_max_lev, 2), + "reason": f"在{max_leverage}x杠杆下,需要 {max_leverage_result['required_position_percent']:.1f}% 的仓位价值,但最大允许 {max_position_percent*100:.1f}%" + }) + + # 方案3:降低最大杠杆 + if use_dynamic_leverage: + # 计算能支持的最大杠杆 + max_supported_leverage = int((available_balance * max_position_percent) / min_margin_usdt) + if max_supported_leverage < max_leverage and max_supported_leverage >= base_leverage: + suggestions.append({ + "type": "reduce_max_leverage", + "title": "降低最大杠杆倍数", + "description": f"将 MAX_LEVERAGE 调整为 {max_supported_leverage}x(当前: {max_leverage}x),以支持当前配置", + "config_key": "MAX_LEVERAGE", + "suggested_value": max_supported_leverage, + "reason": f"当前配置下,最大只能支持 {max_supported_leverage}x 杠杆才能满足最小保证金要求" + }) + + # 方案4:增加账户余额 + if worst['required_position_percent'] > max_position_percent: + required_balance = worst['required_position_value'] / max_position_percent + suggestions.append({ + "type": "increase_balance", + "title": "增加账户余额", + "description": f"将账户余额增加到至少 {required_balance:.2f} USDT(当前: {available_balance:.2f} USDT),以支持{worst_leverage}x杠杆", + "config_key": None, + "suggested_value": round(required_balance, 2), + "reason": f"在{worst_leverage}x杠杆下,当前余额不足以满足最小保证金 {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)", + "type": "leverage_analysis", + "title": "各杠杆倍数检查结果", + "description": "详细检查结果见下方", "config_key": None, - "suggested_value": round(required_balance, 2), - "reason": f"当前余额不足以满足最小保证金 {min_margin_usdt:.2f} USDT 的要求" + "suggested_value": None, + "reason": None, + "leverage_results": leverage_results }) - - # 方案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 + actual_min_margin = base_result['actual_min_margin'] suggestions.append({ "type": "info", "title": "配置可行", - "description": f"当前配置可以正常下单。最小仓位价值: {actual_min_position_value:.2f} USDT,对应保证金: {actual_min_margin:.2f} USDT", + "description": f"当前配置可以正常下单。最小仓位价值: {actual_min_position_value:.2f} USDT,对应保证金: {actual_min_margin:.2f} USDT({base_leverage}x杠杆)", "config_key": None, "suggested_value": None, "reason": None }) + + # 如果启用了动态杠杆,显示所有杠杆倍数的检查结果 + if use_dynamic_leverage and len(leverage_to_check) > 1: + suggestions.append({ + "type": "leverage_analysis", + "title": "各杠杆倍数检查结果(全部可行)", + "description": "详细检查结果见下方", + "config_key": None, + "suggested_value": None, + "reason": None, + "leverage_results": leverage_results + }) return { "feasible": is_feasible, "account_balance": available_balance, - "leverage": leverage, + "base_leverage": base_leverage, + "max_leverage": max_leverage, + "use_dynamic_leverage": use_dynamic_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, + "required_position_value": base_result['required_position_value'], + "required_position_percent": base_result['required_position_percent'], "max_allowed_position_percent": max_position_percent * 100, - "min_position_value": min_position_value, - "actual_min_margin": actual_min_margin, + "min_position_value": available_balance * min_position_percent, + "actual_min_margin": base_result['actual_min_margin'], "min_position_percent": min_position_percent * 100 }, + "leverage_analysis": { + "leverages_checked": leverage_to_check, + "all_feasible": all_feasible, + "results": leverage_results + }, "suggestions": suggestions } except Exception as e: diff --git a/frontend/src/components/ConfigPanel.css b/frontend/src/components/ConfigPanel.css index 3090c5a..6fb00ea 100644 --- a/frontend/src/components/ConfigPanel.css +++ b/frontend/src/components/ConfigPanel.css @@ -600,3 +600,58 @@ .apply-suggestion-btn:active { transform: translateY(0); } + +/* 杠杆分析表格样式 */ +.leverage-analysis { + margin-top: 1rem; +} + +.leverage-table { + margin-top: 0.75rem; + overflow-x: auto; +} + +.leverage-table table { + width: 100%; + border-collapse: collapse; + font-size: 0.85rem; + background: white; + border-radius: 4px; + overflow: hidden; +} + +.leverage-table th { + background: #f5f5f5; + padding: 0.5rem; + text-align: left; + font-weight: 600; + color: #555; + border-bottom: 2px solid #ddd; +} + +.leverage-table td { + padding: 0.5rem; + border-bottom: 1px solid #eee; +} + +.leverage-table tr:last-child td { + border-bottom: none; +} + +.leverage-table .feasible-row { + background: #f0f9f0; +} + +.leverage-table .infeasible-row { + background: #fff5f5; +} + +.leverage-table .status-ok { + color: #4CAF50; + font-weight: 600; +} + +.leverage-table .status-error { + color: #f44336; + font-weight: 600; +} diff --git a/frontend/src/components/ConfigPanel.jsx b/frontend/src/components/ConfigPanel.jsx index 73c3a3c..77b311b 100644 --- a/frontend/src/components/ConfigPanel.jsx +++ b/frontend/src/components/ConfigPanel.jsx @@ -297,7 +297,10 @@ const ConfigPanel = () => {
账户余额: {feasibilityCheck.account_balance?.toFixed(2) || 'N/A'} USDT | - 杠杆: {feasibilityCheck.leverage}x | + 基础杠杆: {feasibilityCheck.base_leverage || feasibilityCheck.leverage || 'N/A'}x + {feasibilityCheck.use_dynamic_leverage && feasibilityCheck.max_leverage && ( + <> | 最大杠杆: {feasibilityCheck.max_leverage}x> + )} | 最小保证金: {feasibilityCheck.current_config?.min_margin_usdt?.toFixed(2) || 'N/A'} USDT
{!feasibilityCheck.feasible && ( @@ -319,35 +322,77 @@ const ConfigPanel = () => { {feasibilityCheck.suggestions && feasibilityCheck.suggestions.length > 0 && (| 杠杆倍数 | +需要仓位价值 | +需要仓位% | +实际最小保证金 | +状态 | +
|---|---|---|---|---|
| {result.leverage}x | +{result.required_position_value.toFixed(2)} USDT | +{result.required_position_percent.toFixed(1)}% | +{result.actual_min_margin.toFixed(2)} USDT | ++ + {result.feasible ? '✓ 可行' : '✗ 不可行'} + + | +
{suggestion.description}
+ {suggestion.config_key && suggestion.suggested_value !== null && ( + + )}{suggestion.description}
- {suggestion.config_key && suggestion.suggested_value !== null && ( - - )} -