diff --git a/trader/aster_trader.go b/trader/aster_trader.go index d9ba82a6..15128e13 100644 --- a/trader/aster_trader.go +++ b/trader/aster_trader.go @@ -438,13 +438,23 @@ func (t *AsterTrader) GetBalance() (map[string]interface{}, error) { return nil, err } + // 🔍 调试:打印原始API响应 + log.Printf("🔍 Aster API原始响应: %s", string(body)) + // 查找USDT余额 totalBalance := 0.0 availableBalance := 0.0 crossUnPnl := 0.0 for _, bal := range balances { + // 🔍 调试:打印每条余额记录 + log.Printf("🔍 余额记录: %+v", bal) + if asset, ok := bal["asset"].(string); ok && asset == "USDT" { + // 🔍 调试:打印USDT余额详情 + log.Printf("🔍 USDT余额详情: balance=%v, availableBalance=%v, crossUnPnl=%v", + bal["balance"], bal["availableBalance"], bal["crossUnPnl"]) + if wb, ok := bal["balance"].(string); ok { totalBalance, _ = strconv.ParseFloat(wb, 64) } @@ -458,11 +468,25 @@ func (t *AsterTrader) GetBalance() (map[string]interface{}, error) { } } + // ✅ Aster API完全兼容Binance API格式 + // balance字段 = wallet balance(不包含未实现盈亏) + // crossUnPnl = unrealized profit(未实现盈亏) + // crossWalletBalance = balance + crossUnPnl(全仓钱包余额,包含盈亏) + // + // 参考Binance官方文档: + // - Account Information V2: marginBalance = walletBalance + unrealizedProfit + // - Balance V3: crossWalletBalance = balance + crossUnPnl + + log.Printf("✓ Aster API返回: 钱包余额=%.2f, 未实现盈亏=%.2f, 可用余额=%.2f", + totalBalance, + crossUnPnl, + availableBalance) + // 返回与Binance相同的字段名,确保AutoTrader能正确解析 return map[string]interface{}{ - "totalWalletBalance": totalBalance, + "totalWalletBalance": totalBalance, // 钱包余额(不含未实现盈亏) "availableBalance": availableBalance, - "totalUnrealizedProfit": crossUnPnl, + "totalUnrealizedProfit": crossUnPnl, // 未实现盈亏 }, nil } diff --git a/web/src/components/AITradersPage.tsx b/web/src/components/AITradersPage.tsx index e1c73699..a19e7097 100644 --- a/web/src/components/AITradersPage.tsx +++ b/web/src/components/AITradersPage.tsx @@ -23,6 +23,7 @@ import { Users, AlertTriangle, BookOpen, + HelpCircle, } from 'lucide-react' // 获取友好的AI模型名称 @@ -1064,6 +1065,51 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) { ) } +// Tooltip Helper Component +function Tooltip({ + content, + children, +}: { + content: string + children: React.ReactNode +}) { + const [show, setShow] = useState(false) + + return ( +
+
setShow(true)} + onMouseLeave={() => setShow(false)} + onClick={() => setShow(!show)} + > + {children} +
+ {show && ( +
+ {content} +
+
+ )} +
+ ) +} + // Signal Source Configuration Modal Component function SignalSourceModal({ coinPoolUrl, @@ -1772,10 +1818,16 @@ function ExchangeConfigModal({ <>
+ {selectedExchange.id === 'aster' && ( +
{t('asterUsdtWarning', language)}
+ )}
{t('exchangeConfigWarning1', language)}
{t('exchangeConfigWarning2', language)}
{t('exchangeConfigWarning3', language)}
diff --git a/web/src/components/TraderConfigModal.tsx b/web/src/components/TraderConfigModal.tsx index f96c9070..e0c7c0bf 100644 --- a/web/src/components/TraderConfigModal.tsx +++ b/web/src/components/TraderConfigModal.tsx @@ -68,6 +68,8 @@ export function TraderConfigModal({ const [selectedCoins, setSelectedCoins] = useState([]) const [showCoinSelector, setShowCoinSelector] = useState(false) const [promptTemplates, setPromptTemplates] = useState<{ name: string }[]>([]) + const [isFetchingBalance, setIsFetchingBalance] = useState(false) + const [balanceFetchError, setBalanceFetchError] = useState('') useEffect(() => { if (traderData) { @@ -182,6 +184,45 @@ export function TraderConfigModal({ }) } + const handleFetchCurrentBalance = async () => { + if (!isEditMode || !traderData?.trader_id) { + setBalanceFetchError('只有在编辑模式下才能获取当前余额'); + return; + } + + setIsFetchingBalance(true); + setBalanceFetchError(''); + + try { + const token = localStorage.getItem('token'); + const response = await fetch(`/api/account?trader_id=${traderData.trader_id}`, { + headers: { + 'Authorization': `Bearer ${token}` + } + }); + + if (!response.ok) { + throw new Error('获取账户余额失败'); + } + + const data = await response.json(); + + // total_equity = 当前账户净值(包含未实现盈亏) + // 这应该作为新的初始余额 + const currentBalance = data.total_equity || data.balance || 0; + + setFormData(prev => ({ ...prev, initial_balance: currentBalance })); + + // 显示成功提示 + console.log('已获取当前余额:', currentBalance); + } catch (error) { + console.error('获取余额失败:', error); + setBalanceFetchError('获取余额失败,请检查网络连接'); + } finally { + setIsFetchingBalance(false); + } + }; + const handleSave = async () => { if (!onSave) return @@ -346,9 +387,22 @@ export function TraderConfigModal({
- +
+ + {isEditMode && ( + + )} +
+ {!isEditMode && ( +

+ + 请输入您交易所账户的当前实际余额。如果输入不准确,P&L统计将会错误。 +

+ )} + {isEditMode && ( +

+ 点击"获取当前余额"按钮可自动获取您交易所账户的当前净值 +

+ )} + {balanceFetchError && ( +

{balanceFetchError}

+ )}
diff --git a/web/src/i18n/translations.ts b/web/src/i18n/translations.ts index ae5c8cca..a48b96b1 100644 --- a/web/src/i18n/translations.ts +++ b/web/src/i18n/translations.ts @@ -197,6 +197,14 @@ export const translations = { 'Hyperliquid uses private key for trading authentication', hyperliquidWalletAddressDesc: 'Wallet address corresponding to the private key', + asterUserDesc: + 'Main wallet address - The EVM wallet address you use to log in to Aster', + asterSignerDesc: + 'API wallet address - Generate from https://www.asterdex.com/en/api-wallet', + asterPrivateKeyDesc: + 'API wallet private key - Get from https://www.asterdex.com/en/api-wallet (only used locally for signing, never transmitted)', + asterUsdtWarning: + 'Important: Aster only tracks USDT balance. Please ensure you use USDT as margin currency to avoid P&L calculation errors caused by price fluctuations of other assets (BNB, ETH, etc.)', testnetDescription: 'Enable to connect to exchange test environment for simulated trading', securityWarning: 'Security Warning', @@ -658,7 +666,15 @@ export const translations = { enterPassphrase: '输入Passphrase (OKX必填)', hyperliquidPrivateKeyDesc: 'Hyperliquid 使用私钥进行交易认证', hyperliquidWalletAddressDesc: '与私钥对应的钱包地址', - testnetDescription: '启用后将连接到交易所测试环境,用于模拟交易', + asterUserDesc: + '主钱包地址 - 您用于登录 Aster 的 EVM 钱包地址', + asterSignerDesc: + 'API 钱包地址 - 从 https://www.asterdex.com/zh-CN/api-wallet 生成', + asterPrivateKeyDesc: + 'API 钱包私钥 - 从 https://www.asterdex.com/zh-CN/api-wallet 获取(仅在本地用于签名,不会被传输)', + asterUsdtWarning: + '重要提示:Aster 仅统计 USDT 余额。请确保您使用 USDT 作为保证金币种,避免其他资产(BNB、ETH等)的价格波动导致盈亏统计错误', + testnetDescription: '启用后将连接到交易所测试环境,用于模拟交易', securityWarning: '安全提示', saveConfiguration: '保存配置',