fix(hyperliquid): use Withdrawable field as primary source for available balance

## 改进分析

之前的修复V1只是防止负数(设为0),但不够彻底:
- 场景:用户有1000 USDC,totalMarginUsed=1050
- V1结果:availableBalance = 0
- 问题:用户想开100 USDT仓位仍会失败

## 根本原因

Hyperliquid的TotalMarginUsed计算方式与Binance不同:
- 可能包含维持保证金要求
- 不能简单用 accountValue - totalMarginUsed

## 正确解决方案

使用Hyperliquid API提供的Withdrawable字段:
- 这是官方计算的真实可提现余额
- 已考虑所有持仓风险和保证金要求
- 准确反映用户真实可用资金

## 实现

1. **优先策略**:使用accountState.Withdrawable字段
2. **后备策略**:Withdrawable不可用时,使用计算值(防负数)
3. **完整日志**:清晰显示使用哪个数据源

## 参考

- Hyperliquid API文档:withdrawable字段表示可提现余额
- CCXT issues #23997, #26671:类似问题讨论
- 推荐做法:free = withdrawable

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
ZhouYongyou
2025-11-04 22:20:59 +08:00
parent 9e42aa153c
commit 590bd5eddc

View File

@@ -109,17 +109,31 @@ func (t *HyperliquidTrader) GetBalance() (map[string]interface{}, error) {
// 需要返回"不包含未实现盈亏的钱包余额"
walletBalanceWithoutUnrealized := accountValue - totalUnrealizedPnl
// ⚠️ 计算可用余额,确保不为负数(防止"没钱"误报
availableBalance := accountValue - totalMarginUsed
if availableBalance < 0 {
log.Printf("⚠️ [Hyperliquid] 计算的可用余额为负 (%.2f - %.2f = %.2f)已调整为0。",
accountValue, totalMarginUsed, availableBalance)
log.Printf(" 提示这可能是因为Hyperliquid的TotalMarginUsed计算方式不同或持仓处于高风险状态")
availableBalance = 0
// ⚠️ 优先使用Withdrawable字段Hyperliquid API返回的真实可用余额
availableBalance := 0.0
if accountState.Withdrawable != "" {
withdrawable, err := strconv.ParseFloat(accountState.Withdrawable, 64)
if err == nil {
availableBalance = withdrawable
log.Printf("✓ 使用Hyperliquid API的Withdrawable字段: %.2f USDT", availableBalance)
} else {
log.Printf("⚠️ 解析Withdrawable字段失败: %v将使用计算值", err)
}
}
// 后备方案如果Withdrawable不可用使用计算值确保不为负数
if availableBalance == 0 && accountState.Withdrawable == "" {
availableBalance = accountValue - totalMarginUsed
if availableBalance < 0 {
log.Printf("⚠️ [Hyperliquid] 计算的可用余额为负 (%.2f - %.2f = %.2f)已调整为0。",
accountValue, totalMarginUsed, availableBalance)
log.Printf(" 提示这可能是因为Hyperliquid的TotalMarginUsed计算方式不同或持仓处于高风险状态")
availableBalance = 0
}
}
result["totalWalletBalance"] = walletBalanceWithoutUnrealized // 钱包余额(不含未实现盈亏)
result["availableBalance"] = availableBalance // 可用余额(总净值 - 占用保证金最小为0
result["availableBalance"] = availableBalance // 可用余额(优先使用Withdrawable最小为0
result["totalUnrealizedProfit"] = totalUnrealizedPnl // 未实现盈亏
log.Printf("✓ Hyperliquid 账户: 总净值=%.2f (钱包%.2f+未实现%.2f), 可用=%.2f, 保证金占用=%.2f",