Skip to content

Commit 917a073

Browse files
the-dev-zclaude
andcommitted
refactor(hyperliquid): align with upstream - auto-generation only, no manual wallet input
## 對齊上游策略 ### Backend 修復 1. ❌ 移除重複計算:刪除 `accountValue += spotUSDCBalance` (Line 139-140) - 原問題:Spot 被加了兩次(Line 140 和 Line 183) - 正確做法:只在最後計算 totalWalletBalance 時加一次 2. ✅ 對齊 Withdrawable 處理: - 改為 `if err == nil && withdrawable > 0` - 與上游保持一致 3. ✅ 對齊註釋風格: - 使用 "Step 4", "Step 5" 標記 - 簡化日誌輸出 ### Frontend 修復 1. ✅ 移除 wallet address 驗證: - Line 111: 只要求 `apiKey`,不要求 `hyperliquidWalletAddr` - Line 1209: 提交時傳空字符串 `''` - Line 1528: 按鈕禁用邏輯只檢查 apiKey 2. ✅ 簡化用戶體驗: - 用戶只需提供私鑰 - Backend 自動從私鑰生成錢包地址 - 降低配置門檻 ## 技術細節 **問題**: 之前 Spot 餘額被重複計算 ```go // ❌ 錯誤(加了兩次) accountValue += spotUSDCBalance // Line 140 totalWalletBalance := walletBalanceWithoutUnrealized + spotUSDCBalance // Line 183 // ✅ 正確(只加一次) totalWalletBalance := walletBalanceWithoutUnrealized + spotUSDCBalance // Line 177 ``` **結果**: 完全對齊 NoFxAiOS/nofx dev branch ## 相關分支 - ✅ PR NoFxAiOS#471 已合併到上游 - ❌ 刪除本地 fix/hyperliquid-complete-balance-fix 分支(任務完成) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 436de71 commit 917a073

File tree

2 files changed

+21
-28
lines changed

2 files changed

+21
-28
lines changed

trader/hyperliquid_trader.go

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,9 @@ func (t *HyperliquidTrader) GetBalance() (map[string]interface{}, error) {
133133

134134
// 🔍 调试:打印API返回的完整摘要结构
135135
summaryJSON, _ := json.MarshalIndent(summary, " ", " ")
136-
log.Printf("🔍 [DEBUG] Hyperliquid Perpetuals %s 完整数据:", summaryType)
136+
log.Printf("🔍 [DEBUG] Hyperliquid API %s 完整数据:", summaryType)
137137
log.Printf("%s", string(summaryJSON))
138138

139-
// ⚠️ 关键修复:将 Spot 现货余额加入总余额
140-
accountValue += spotUSDCBalance
141-
142139
// ⚠️ 关键修复:从所有持仓中累加真正的未实现盈亏
143140
totalUnrealizedPnl := 0.0
144141
for _, assetPos := range accountState.AssetPositions {
@@ -154,40 +151,37 @@ func (t *HyperliquidTrader) GetBalance() (map[string]interface{}, error) {
154151
// 需要返回"不包含未实现盈亏的钱包余额"
155152
walletBalanceWithoutUnrealized := accountValue - totalUnrealizedPnl
156153

157-
// ⚠️ 优先使用Withdrawable字段(Hyperliquid API返回的真实可用余额)
154+
// ✅ Step 4: 使用 Withdrawable 欄位(PR #443)
155+
// Withdrawable 是官方提供的真实可提现余额,比简单计算更可靠
158156
availableBalance := 0.0
159157
if accountState.Withdrawable != "" {
160158
withdrawable, err := strconv.ParseFloat(accountState.Withdrawable, 64)
161-
if err == nil {
159+
if err == nil && withdrawable > 0 {
162160
availableBalance = withdrawable
163-
log.Printf("✓ 使用Hyperliquid API的Withdrawable字段: %.2f USDT", availableBalance)
164-
} else {
165-
log.Printf("⚠️ 解析Withdrawable字段失败: %v,将使用计算值", err)
161+
log.Printf("✓ 使用 Withdrawable 作为可用余额: %.2f", availableBalance)
166162
}
167163
}
168164

169-
// 后备方案:如果Withdrawable不可用,使用计算值(确保不为负数)
165+
// 降级方案:如果没有 Withdrawable,使用简单计算
170166
if availableBalance == 0 && accountState.Withdrawable == "" {
171167
availableBalance = accountValue - totalMarginUsed
172168
if availableBalance < 0 {
173-
log.Printf("⚠️ [Hyperliquid] 计算的可用余额为负 (%.2f - %.2f = %.2f),已调整为0。",
174-
accountValue, totalMarginUsed, availableBalance)
175-
log.Printf(" 提示:这可能是因为Hyperliquid的TotalMarginUsed计算方式不同,或持仓处于高风险状态")
169+
log.Printf("⚠️ 计算出的可用余额为负数 (%.2f),重置为 0", availableBalance)
176170
availableBalance = 0
177171
}
178172
}
179173

180-
// ✅ 正確邏輯:Spot 只加到總資產,不加到可用餘額
181-
// 原因:Spot 和 Perpetuals 是獨立帳戶,Spot 的錢不能直接用於開倉
182-
// 需要手動調用 ClassTransfer 才能轉帳
174+
// ✅ Step 5: 正確處理 Spot + Perpetuals 余额
175+
// 重要:Spot 只加到總資產,不加到可用餘額
176+
// 原因:Spot 和 Perpetuals 是獨立帳戶,需手動 ClassTransfer 才能轉帳
183177
totalWalletBalance := walletBalanceWithoutUnrealized + spotUSDCBalance
184178

185-
result["totalWalletBalance"] = totalWalletBalance // 總資產(Perp錢包 + Spot)
186-
result["availableBalance"] = availableBalance // 可用餘額(僅 Perpetuals,不含 Spot)
187-
result["totalUnrealizedProfit"] = totalUnrealizedPnl // 未實現盈虧
188-
result["spotBalance"] = spotUSDCBalance // Spot 現貨餘額(單獨返回)
179+
result["totalWalletBalance"] = totalWalletBalance // 總資產(Perp + Spot)
180+
result["availableBalance"] = availableBalance // 可用餘額(僅 Perpetuals,不含 Spot)
181+
result["totalUnrealizedProfit"] = totalUnrealizedPnl // 未實現盈虧(僅來自 Perpetuals)
182+
result["spotBalance"] = spotUSDCBalance // Spot 現貨餘額(單獨返回)
189183

190-
log.Printf("✓ Hyperliquid 账户总览:")
184+
log.Printf("✓ Hyperliquid 完整账户:")
191185
log.Printf(" • Spot 现货余额: %.2f USDC (需手动转账到 Perpetuals 才能开仓)", spotUSDCBalance)
192186
log.Printf(" • Perpetuals 合约净值: %.2f USDC (钱包%.2f + 未实现%.2f)",
193187
accountValue,

web/src/components/AITradersPage.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,9 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
106106
e.asterPrivateKey && e.asterPrivateKey.trim() !== '';
107107
}
108108

109-
// Hyperliquid 只需要私钥(作为apiKey)和钱包地址
109+
// Hyperliquid 只需要私钥(作为apiKey),钱包地址可自动生成
110110
if (e.id === 'hyperliquid') {
111-
return e.apiKey && e.apiKey.trim() !== '' &&
112-
e.hyperliquidWalletAddr && e.hyperliquidWalletAddr.trim() !== '';
111+
return e.apiKey && e.apiKey.trim() !== '';
113112
}
114113

115114
// Binance 等其他交易所需要 apiKey 和 secretKey
@@ -1207,8 +1206,8 @@ function ExchangeConfigModal({
12071206
if (!apiKey.trim() || !secretKey.trim()) return;
12081207
await onSave(selectedExchangeId, apiKey.trim(), secretKey.trim(), testnet);
12091208
} else if (selectedExchange?.id === 'hyperliquid') {
1210-
if (!apiKey.trim() || !hyperliquidWalletAddr.trim()) return;
1211-
await onSave(selectedExchangeId, apiKey.trim(), '', testnet, hyperliquidWalletAddr.trim());
1209+
if (!apiKey.trim()) return // 只验证私钥,钱包地址自动从私钥生成
1210+
await onSave(selectedExchangeId, apiKey.trim(), '', testnet, '') // 传空字符串,后端自动生成地址
12121211
} else if (selectedExchange?.id === 'aster') {
12131212
if (!asterUser.trim() || !asterSigner.trim() || !asterPrivateKey.trim()) return;
12141213
await onSave(selectedExchangeId, '', '', testnet, undefined, asterUser.trim(), asterSigner.trim(), asterPrivateKey.trim());
@@ -1523,10 +1522,10 @@ function ExchangeConfigModal({
15231522
<button
15241523
type="submit"
15251524
disabled={
1526-
!selectedExchange ||
1525+
!selectedExchange ||
15271526
(selectedExchange.id === 'binance' && (!apiKey.trim() || !secretKey.trim())) ||
15281527
(selectedExchange.id === 'okx' && (!apiKey.trim() || !secretKey.trim() || !passphrase.trim())) ||
1529-
(selectedExchange.id === 'hyperliquid' && (!apiKey.trim() || !hyperliquidWalletAddr.trim())) ||
1528+
(selectedExchange.id === 'hyperliquid' && !apiKey.trim()) ||
15301529
(selectedExchange.id === 'aster' && (!asterUser.trim() || !asterSigner.trim() || !asterPrivateKey.trim())) ||
15311530
(selectedExchange.type === 'cex' && selectedExchange.id !== 'hyperliquid' && selectedExchange.id !== 'aster' && selectedExchange.id !== 'binance' && selectedExchange.id !== 'okx' && (!apiKey.trim() || !secretKey.trim()))
15321531
}

0 commit comments

Comments
 (0)