This commit is contained in:
薇薇安 2026-01-17 19:35:27 +08:00
parent c778afdcb6
commit 26b2674c86
3 changed files with 272 additions and 99 deletions

View File

@ -69,116 +69,189 @@ async def check_config_feasibility():
min_margin_usdt = TradingConfig.get_value('MIN_MARGIN_USDT', 5.0) min_margin_usdt = TradingConfig.get_value('MIN_MARGIN_USDT', 5.0)
min_position_percent = TradingConfig.get_value('MIN_POSITION_PERCENT', 0.02) min_position_percent = TradingConfig.get_value('MIN_POSITION_PERCENT', 0.02)
max_position_percent = TradingConfig.get_value('MAX_POSITION_PERCENT', 0.08) 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 leverage_to_check = [base_leverage]
required_position_percent = required_position_value / available_balance if available_balance > 0 else 0 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)
# 计算使用最小仓位百分比时,实际能得到的保证金 leverage_to_check = sorted(set(leverage_to_check))
min_position_value = available_balance * min_position_percent
actual_min_margin = min_position_value / leverage if leverage > 0 else min_position_value
# 检查是否可行: # 检查每个杠杆倍数下的可行性
# 1. 需要的仓位百分比不能超过最大允许的仓位百分比 leverage_results = []
# 2. 使用最小仓位百分比时,实际保证金必须 >= 最小保证金要求 all_feasible = True
is_feasible = (required_position_percent <= max_position_percent) and (actual_min_margin >= min_margin_usdt) 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 = [] suggestions = []
if not is_feasible: if not is_feasible:
# 不可行,给出建议 # 不可行,给出建议
# 判断是哪个条件不满足 # 找出不可行的杠杆倍数
if required_position_percent > max_position_percent: infeasible_leverages = [r for r in leverage_results if not r['feasible']]
# 条件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: if infeasible_leverages:
# 条件2不满足使用最小仓位时实际保证金不足 # 找出最坏情况(实际保证金最小的)
# 方案1降低最小保证金到实际能支持的值 worst = min(infeasible_leverages, key=lambda x: x['actual_min_margin'])
suggestions.append({ worst_leverage = worst['leverage']
"type": "reduce_min_margin_to_supported", worst_margin = worst['actual_min_margin']
"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提高最小仓位百分比 # 方案1基于最坏情况最大杠杆降低最小保证金
required_min_position_percent = (min_margin_usdt * leverage) / available_balance if available_balance > 0 else 0 if not worst['condition2_ok']:
if required_min_position_percent <= max_position_percent:
suggestions.append({ suggestions.append({
"type": "increase_min_position", "type": "reduce_min_margin_to_supported",
"title": "提高最小仓位百分比", "title": f"降低最小保证金(考虑{worst_leverage}x杠杆",
"description": f"将 MIN_POSITION_PERCENT 调整为 {required_min_position_percent*100:.2f}%(当前: {min_position_percent*100:.1f}%", "description": f"将 MIN_MARGIN_USDT 调整为 {worst_margin:.2f} USDT当前: {min_margin_usdt:.2f} USDT。在{worst_leverage}x杠杆下实际只支持 {worst_margin:.2f} USDT",
"config_key": "MIN_POSITION_PERCENT", "config_key": "MIN_MARGIN_USDT",
"suggested_value": round(required_min_position_percent, 4), "suggested_value": round(worst_margin, 2),
"reason": f"需要至少 {required_min_position_percent*100:.2f}% 的仓位才能满足 {min_margin_usdt:.2f} USDT 的保证金要求" "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({ suggestions.append({
"type": "increase_balance", "type": "leverage_analysis",
"title": "增加账户余额", "title": "各杠杆倍数检查结果",
"description": f"将账户余额增加到至少 {required_balance:.2f} USDT当前: {available_balance:.2f} USDT", "description": "详细检查结果见下方",
"config_key": None, "config_key": None,
"suggested_value": round(required_balance, 2), "suggested_value": None,
"reason": f"当前余额不足以满足最小保证金 {min_margin_usdt:.2f} USDT 的要求" "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: else:
# 可行,显示当前配置信息 # 可行,显示当前配置信息
actual_min_position_value = available_balance * min_position_percent 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({ suggestions.append({
"type": "info", "type": "info",
"title": "配置可行", "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, "config_key": None,
"suggested_value": None, "suggested_value": None,
"reason": 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 { return {
"feasible": is_feasible, "feasible": is_feasible,
"account_balance": available_balance, "account_balance": available_balance,
"leverage": leverage, "base_leverage": base_leverage,
"max_leverage": max_leverage,
"use_dynamic_leverage": use_dynamic_leverage,
"current_config": { "current_config": {
"min_margin_usdt": min_margin_usdt, "min_margin_usdt": min_margin_usdt,
"min_position_percent": min_position_percent, "min_position_percent": min_position_percent,
"max_position_percent": max_position_percent "max_position_percent": max_position_percent
}, },
"calculated_values": { "calculated_values": {
"required_position_value": required_position_value, "required_position_value": base_result['required_position_value'],
"required_position_percent": required_position_percent * 100, "required_position_percent": base_result['required_position_percent'],
"max_allowed_position_percent": max_position_percent * 100, "max_allowed_position_percent": max_position_percent * 100,
"min_position_value": min_position_value, "min_position_value": available_balance * min_position_percent,
"actual_min_margin": actual_min_margin, "actual_min_margin": base_result['actual_min_margin'],
"min_position_percent": min_position_percent * 100 "min_position_percent": min_position_percent * 100
}, },
"leverage_analysis": {
"leverages_checked": leverage_to_check,
"all_feasible": all_feasible,
"results": leverage_results
},
"suggestions": suggestions "suggestions": suggestions
} }
except Exception as e: except Exception as e:

View File

@ -600,3 +600,58 @@
.apply-suggestion-btn:active { .apply-suggestion-btn:active {
transform: translateY(0); 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;
}

View File

@ -297,7 +297,10 @@ const ConfigPanel = () => {
<div className="feasibility-info"> <div className="feasibility-info">
<p> <p>
账户余额: <strong>{feasibilityCheck.account_balance?.toFixed(2) || 'N/A'}</strong> USDT | 账户余额: <strong>{feasibilityCheck.account_balance?.toFixed(2) || 'N/A'}</strong> USDT |
杠杆: <strong>{feasibilityCheck.leverage}x</strong> | 基础杠杆: <strong>{feasibilityCheck.base_leverage || feasibilityCheck.leverage || 'N/A'}x</strong>
{feasibilityCheck.use_dynamic_leverage && feasibilityCheck.max_leverage && (
<> | 最大杠杆: <strong>{feasibilityCheck.max_leverage}x</strong></>
)} |
最小保证金: <strong>{feasibilityCheck.current_config?.min_margin_usdt?.toFixed(2) || 'N/A'}</strong> USDT 最小保证金: <strong>{feasibilityCheck.current_config?.min_margin_usdt?.toFixed(2) || 'N/A'}</strong> USDT
</p> </p>
{!feasibilityCheck.feasible && ( {!feasibilityCheck.feasible && (
@ -319,35 +322,77 @@ const ConfigPanel = () => {
{feasibilityCheck.suggestions && feasibilityCheck.suggestions.length > 0 && ( {feasibilityCheck.suggestions && feasibilityCheck.suggestions.length > 0 && (
<div className="feasibility-suggestions"> <div className="feasibility-suggestions">
<h5>建议方案</h5> <h5>建议方案</h5>
{feasibilityCheck.suggestions.map((suggestion, index) => ( {feasibilityCheck.suggestions.map((suggestion, index) => {
<div key={index} className="suggestion-item"> //
<div className="suggestion-header"> if (suggestion.type === 'leverage_analysis' && suggestion.leverage_results) {
<strong>{suggestion.title}</strong> return (
<div key={index} className="suggestion-item leverage-analysis">
<div className="suggestion-header">
<strong>{suggestion.title}</strong>
</div>
<div className="leverage-table">
<table>
<thead>
<tr>
<th>杠杆倍数</th>
<th>需要仓位价值</th>
<th>需要仓位%</th>
<th>实际最小保证金</th>
<th>状态</th>
</tr>
</thead>
<tbody>
{suggestion.leverage_results.map((result, idx) => (
<tr key={idx} className={result.feasible ? 'feasible-row' : 'infeasible-row'}>
<td><strong>{result.leverage}x</strong></td>
<td>{result.required_position_value.toFixed(2)} USDT</td>
<td>{result.required_position_percent.toFixed(1)}%</td>
<td>{result.actual_min_margin.toFixed(2)} USDT</td>
<td>
<span className={result.feasible ? 'status-ok' : 'status-error'}>
{result.feasible ? '✓ 可行' : '✗ 不可行'}
</span>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)
}
//
return (
<div key={index} className="suggestion-item">
<div className="suggestion-header">
<strong>{suggestion.title}</strong>
</div>
<p className="suggestion-desc">{suggestion.description}</p>
{suggestion.config_key && suggestion.suggested_value !== null && (
<button
className="apply-suggestion-btn"
onClick={async () => {
try {
await api.updateConfig(suggestion.config_key, {
value: suggestion.suggested_value,
type: 'number',
category: 'position'
})
setMessage(`已应用建议: ${suggestion.title}`)
await loadConfigs()
await checkFeasibility()
} catch (error) {
setMessage('应用建议失败: ' + error.message)
}
}}
>
应用此建议
</button>
)}
</div> </div>
<p className="suggestion-desc">{suggestion.description}</p> )
{suggestion.config_key && suggestion.suggested_value !== null && ( })}
<button
className="apply-suggestion-btn"
onClick={async () => {
try {
await api.updateConfig(suggestion.config_key, {
value: suggestion.suggested_value,
type: 'number',
category: 'position'
})
setMessage(`已应用建议: ${suggestion.title}`)
await loadConfigs()
await checkFeasibility()
} catch (error) {
setMessage('应用建议失败: ' + error.message)
}
}}
>
应用此建议
</button>
)}
</div>
))}
</div> </div>
)} )}
</div> </div>