Merge pull request #414 from zhouyongyou/feat/query-actual-balance-v2

feat(api): query actual exchange balance when creating trader
This commit is contained in:
Icyoung
2025-11-05 15:45:09 +08:00
committed by GitHub
+70 -2
View File
@@ -9,6 +9,7 @@ import (
"nofx/config"
"nofx/decision"
"nofx/manager"
"nofx/trader"
"strconv"
"strings"
"time"
@@ -347,6 +348,73 @@ func (s *Server) handleCreateTrader(c *gin.Context) {
scanIntervalMinutes = 3 // 默认3分钟
}
// ✨ 查询交易所实际余额,覆盖用户输入
actualBalance := req.InitialBalance // 默认使用用户输入
exchanges, err := s.database.GetExchanges(userID)
if err != nil {
log.Printf("⚠️ 获取交易所配置失败,使用用户输入的初始资金: %v", err)
}
// 查找匹配的交易所配置
var exchangeCfg *config.ExchangeConfig
for _, ex := range exchanges {
if ex.ID == req.ExchangeID {
exchangeCfg = ex
break
}
}
if exchangeCfg == nil {
log.Printf("⚠️ 未找到交易所 %s 的配置,使用用户输入的初始资金", req.ExchangeID)
} else if !exchangeCfg.Enabled {
log.Printf("⚠️ 交易所 %s 未启用,使用用户输入的初始资金", req.ExchangeID)
} else {
// 根据交易所类型创建临时 trader 查询余额
var tempTrader trader.Trader
var createErr error
switch req.ExchangeID {
case "binance":
tempTrader = trader.NewFuturesTrader(exchangeCfg.APIKey, exchangeCfg.SecretKey)
case "hyperliquid":
tempTrader, createErr = trader.NewHyperliquidTrader(
exchangeCfg.APIKey, // private key
exchangeCfg.HyperliquidWalletAddr,
exchangeCfg.Testnet,
)
case "aster":
tempTrader, createErr = trader.NewAsterTrader(
exchangeCfg.AsterUser,
exchangeCfg.AsterSigner,
exchangeCfg.AsterPrivateKey,
)
default:
log.Printf("⚠️ 不支持的交易所类型: %s,使用用户输入的初始资金", req.ExchangeID)
}
if createErr != nil {
log.Printf("⚠️ 创建临时 trader 失败,使用用户输入的初始资金: %v", createErr)
} else if tempTrader != nil {
// 查询实际余额
balanceInfo, balanceErr := tempTrader.GetBalance()
if balanceErr != nil {
log.Printf("⚠️ 查询交易所余额失败,使用用户输入的初始资金: %v", balanceErr)
} else {
// 提取可用余额
if availableBalance, ok := balanceInfo["available_balance"].(float64); ok && availableBalance > 0 {
actualBalance = availableBalance
log.Printf("✓ 查询到交易所实际余额: %.2f USDT (用户输入: %.2f USDT)", actualBalance, req.InitialBalance)
} else if totalBalance, ok := balanceInfo["balance"].(float64); ok && totalBalance > 0 {
// 有些交易所可能只返回 balance 字段
actualBalance = totalBalance
log.Printf("✓ 查询到交易所实际余额: %.2f USDT (用户输入: %.2f USDT)", actualBalance, req.InitialBalance)
} else {
log.Printf("⚠️ 无法从余额信息中提取可用余额,使用用户输入的初始资金")
}
}
}
}
// 创建交易员配置(数据库实体)
trader := &config.TraderRecord{
ID: traderID,
@@ -354,7 +422,7 @@ func (s *Server) handleCreateTrader(c *gin.Context) {
Name: req.Name,
AIModelID: req.AIModelID,
ExchangeID: req.ExchangeID,
InitialBalance: req.InitialBalance,
InitialBalance: actualBalance, // 使用实际查询的余额
BTCETHLeverage: btcEthLeverage,
AltcoinLeverage: altcoinLeverage,
TradingSymbols: req.TradingSymbols,
@@ -369,7 +437,7 @@ func (s *Server) handleCreateTrader(c *gin.Context) {
}
// 保存到数据库
err := s.database.CreateTrader(trader)
err = s.database.CreateTrader(trader)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("创建交易员失败: %v", err)})
return