import React, { useState, useEffect } from 'react' import useSWR from 'swr' import { api } from '../lib/api' import type { TraderInfo, CreateTraderRequest, AIModel, Exchange, } from '../types' import { useLanguage } from '../contexts/LanguageContext' import { t, type Language } from '../i18n/translations' import { useAuth } from '../contexts/AuthContext' import { getExchangeIcon } from './ExchangeIcons' import { getModelIcon } from './ModelIcons' import { TraderConfigModal } from './TraderConfigModal' import { Bot, Brain, Landmark, BarChart3, Trash2, Plus, Users, AlertTriangle, BookOpen, HelpCircle, } from 'lucide-react' // 获取友好的AI模型名称 function getModelDisplayName(modelId: string): string { switch (modelId.toLowerCase()) { case 'deepseek': return 'DeepSeek' case 'qwen': return 'Qwen' case 'claude': return 'Claude' default: return modelId.toUpperCase() } } // 提取下划线后面的名称部分 function getShortName(fullName: string): string { const parts = fullName.split('_') return parts.length > 1 ? parts[parts.length - 1] : fullName } interface AITradersPageProps { onTraderSelect?: (traderId: string) => void } export function AITradersPage({ onTraderSelect }: AITradersPageProps) { const { language } = useLanguage() const { user, token } = useAuth() const [showCreateModal, setShowCreateModal] = useState(false) const [showEditModal, setShowEditModal] = useState(false) const [showModelModal, setShowModelModal] = useState(false) const [showExchangeModal, setShowExchangeModal] = useState(false) const [showSignalSourceModal, setShowSignalSourceModal] = useState(false) const [editingModel, setEditingModel] = useState(null) const [editingExchange, setEditingExchange] = useState(null) const [editingTrader, setEditingTrader] = useState(null) const [allModels, setAllModels] = useState([]) const [allExchanges, setAllExchanges] = useState([]) const [supportedModels, setSupportedModels] = useState([]) const [supportedExchanges, setSupportedExchanges] = useState([]) const [userSignalSource, setUserSignalSource] = useState<{ coinPoolUrl: string oiTopUrl: string }>({ coinPoolUrl: '', oiTopUrl: '', }) const { data: traders, mutate: mutateTraders } = useSWR( user && token ? 'traders' : null, api.getTraders, { refreshInterval: 5000 } ) // 加载AI模型和交易所配置 useEffect(() => { const loadConfigs = async () => { if (!user || !token) { // 未登录时只加载公开的支持模型和交易所 try { const [supportedModels, supportedExchanges] = await Promise.all([ api.getSupportedModels(), api.getSupportedExchanges(), ]) setSupportedModels(supportedModels) setSupportedExchanges(supportedExchanges) } catch (err) { console.error('Failed to load supported configs:', err) } return } try { const [ modelConfigs, exchangeConfigs, supportedModels, supportedExchanges, ] = await Promise.all([ api.getModelConfigs(), api.getExchangeConfigs(), api.getSupportedModels(), api.getSupportedExchanges(), ]) setAllModels(modelConfigs) setAllExchanges(exchangeConfigs) setSupportedModels(supportedModels) setSupportedExchanges(supportedExchanges) // 加载用户信号源配置 try { const signalSource = await api.getUserSignalSource() setUserSignalSource({ coinPoolUrl: signalSource.coin_pool_url || '', oiTopUrl: signalSource.oi_top_url || '', }) } catch (error) { console.log('📡 用户信号源配置暂未设置') } } catch (error) { console.error('Failed to load configs:', error) } } loadConfigs() }, [user, token]) // 显示所有用户的模型和交易所配置(用于调试) const configuredModels = allModels || [] const configuredExchanges = allExchanges || [] // 只在创建交易员时使用已启用且配置完整的 const enabledModels = allModels?.filter((m) => m.enabled && m.apiKey) || [] const enabledExchanges = allExchanges?.filter((e) => { if (!e.enabled) return false // Aster 交易所需要特殊字段 if (e.id === 'aster') { return ( e.asterUser && e.asterUser.trim() !== '' && e.asterSigner && e.asterSigner.trim() !== '' && e.asterPrivateKey && e.asterPrivateKey.trim() !== '' ) } // Hyperliquid 只需要私钥(作为apiKey),钱包地址会自动从私钥生成 if (e.id === 'hyperliquid') { return e.apiKey && e.apiKey.trim() !== '' } // Binance 等其他交易所需要 apiKey 和 secretKey return ( e.apiKey && e.apiKey.trim() !== '' && e.secretKey && e.secretKey.trim() !== '' ) }) || [] // 检查模型是否正在被运行中的交易员使用 const isModelInUse = (modelId: string) => { return traders?.some((t) => t.ai_model === modelId && t.is_running) || false } // 检查交易所是否正在被运行中的交易员使用 const isExchangeInUse = (exchangeId: string) => { return ( traders?.some((t) => t.exchange_id === exchangeId && t.is_running) || false ) } const handleCreateTrader = async (data: CreateTraderRequest) => { try { const model = allModels?.find((m) => m.id === data.ai_model_id) const exchange = allExchanges?.find((e) => e.id === data.exchange_id) if (!model?.enabled) { alert(t('modelNotConfigured', language)) return } if (!exchange?.enabled) { alert(t('exchangeNotConfigured', language)) return } await api.createTrader(data) setShowCreateModal(false) mutateTraders() } catch (error) { console.error('Failed to create trader:', error) alert(t('createTraderFailed', language)) } } const handleEditTrader = async (traderId: string) => { try { const traderConfig = await api.getTraderConfig(traderId) setEditingTrader(traderConfig) setShowEditModal(true) } catch (error) { console.error('Failed to fetch trader config:', error) alert(t('getTraderConfigFailed', language)) } } const handleSaveEditTrader = async (data: CreateTraderRequest) => { if (!editingTrader) return try { const model = enabledModels?.find((m) => m.id === data.ai_model_id) const exchange = enabledExchanges?.find((e) => e.id === data.exchange_id) if (!model) { alert(t('modelConfigNotExist', language)) return } if (!exchange) { alert(t('exchangeConfigNotExist', language)) return } const request = { name: data.name, ai_model_id: data.ai_model_id, exchange_id: data.exchange_id, initial_balance: data.initial_balance, scan_interval_minutes: data.scan_interval_minutes, btc_eth_leverage: data.btc_eth_leverage, altcoin_leverage: data.altcoin_leverage, trading_symbols: data.trading_symbols, custom_prompt: data.custom_prompt, override_base_prompt: data.override_base_prompt, is_cross_margin: data.is_cross_margin, use_coin_pool: data.use_coin_pool, use_oi_top: data.use_oi_top, } await api.updateTrader(editingTrader.trader_id, request) setShowEditModal(false) setEditingTrader(null) mutateTraders() } catch (error) { console.error('Failed to update trader:', error) alert(t('updateTraderFailed', language)) } } const handleDeleteTrader = async (traderId: string) => { if (!confirm(t('confirmDeleteTrader', language))) return try { await api.deleteTrader(traderId) mutateTraders() } catch (error) { console.error('Failed to delete trader:', error) alert(t('deleteTraderFailed', language)) } } const handleToggleTrader = async (traderId: string, running: boolean) => { try { if (running) { await api.stopTrader(traderId) } else { await api.startTrader(traderId) } mutateTraders() } catch (error) { console.error('Failed to toggle trader:', error) alert(t('operationFailed', language)) } } const handleModelClick = (modelId: string) => { if (!isModelInUse(modelId)) { setEditingModel(modelId) setShowModelModal(true) } } const handleExchangeClick = (exchangeId: string) => { if (!isExchangeInUse(exchangeId)) { setEditingExchange(exchangeId) setShowExchangeModal(true) } } const handleDeleteModelConfig = async (modelId: string) => { if (!confirm(t('confirmDeleteModel', language))) return try { const updatedModels = allModels?.map((m) => m.id === modelId ? { ...m, apiKey: '', customApiUrl: '', customModelName: '', enabled: false, } : m ) || [] const request = { models: Object.fromEntries( updatedModels.map((model) => [ model.provider, // 使用 provider 而不是 id { enabled: model.enabled, api_key: model.apiKey || '', custom_api_url: model.customApiUrl || '', custom_model_name: model.customModelName || '', }, ]) ), } await api.updateModelConfigs(request) setAllModels(updatedModels) setShowModelModal(false) setEditingModel(null) } catch (error) { console.error('Failed to delete model config:', error) alert(t('deleteConfigFailed', language)) } } const handleSaveModelConfig = async ( modelId: string, apiKey: string, customApiUrl?: string, customModelName?: string ) => { try { // 创建或更新用户的模型配置 const existingModel = allModels?.find((m) => m.id === modelId) let updatedModels // 找到要配置的模型(优先从已配置列表,其次从支持列表) const modelToUpdate = existingModel || supportedModels?.find((m) => m.id === modelId) if (!modelToUpdate) { alert(t('modelNotExist', language)) return } if (existingModel) { // 更新现有配置 updatedModels = allModels?.map((m) => m.id === modelId ? { ...m, apiKey, customApiUrl: customApiUrl || '', customModelName: customModelName || '', enabled: true, } : m ) || [] } else { // 添加新配置 const newModel = { ...modelToUpdate, apiKey, customApiUrl: customApiUrl || '', customModelName: customModelName || '', enabled: true, } updatedModels = [...(allModels || []), newModel] } const request = { models: Object.fromEntries( updatedModels.map((model) => [ model.provider, // 使用 provider 而不是 id { enabled: model.enabled, api_key: model.apiKey || '', custom_api_url: model.customApiUrl || '', custom_model_name: model.customModelName || '', }, ]) ), } await api.updateModelConfigs(request) // 重新获取用户配置以确保数据同步 const refreshedModels = await api.getModelConfigs() setAllModels(refreshedModels) setShowModelModal(false) setEditingModel(null) } catch (error) { console.error('Failed to save model config:', error) alert(t('saveConfigFailed', language)) } } const handleDeleteExchangeConfig = async (exchangeId: string) => { if (!confirm(t('confirmDeleteExchange', language))) return try { const updatedExchanges = allExchanges?.map((e) => e.id === exchangeId ? { ...e, apiKey: '', secretKey: '', enabled: false } : e ) || [] const request = { exchanges: Object.fromEntries( updatedExchanges.map((exchange) => [ exchange.id, { enabled: exchange.enabled, api_key: exchange.apiKey || '', secret_key: exchange.secretKey || '', testnet: exchange.testnet || false, }, ]) ), } await api.updateExchangeConfigs(request) setAllExchanges(updatedExchanges) setShowExchangeModal(false) setEditingExchange(null) } catch (error) { console.error('Failed to delete exchange config:', error) alert(t('deleteExchangeConfigFailed', language)) } } const handleSaveExchangeConfig = async ( exchangeId: string, apiKey: string, secretKey?: string, testnet?: boolean, hyperliquidWalletAddr?: string, asterUser?: string, asterSigner?: string, asterPrivateKey?: string ) => { try { // 找到要配置的交易所(从supportedExchanges中) const exchangeToUpdate = supportedExchanges?.find( (e) => e.id === exchangeId ) if (!exchangeToUpdate) { alert(t('exchangeNotExist', language)) return } // 创建或更新用户的交易所配置 const existingExchange = allExchanges?.find((e) => e.id === exchangeId) let updatedExchanges if (existingExchange) { // 更新现有配置 updatedExchanges = allExchanges?.map((e) => e.id === exchangeId ? { ...e, apiKey, secretKey, testnet, hyperliquidWalletAddr, asterUser, asterSigner, asterPrivateKey, enabled: true, } : e ) || [] } else { // 添加新配置 const newExchange = { ...exchangeToUpdate, apiKey, secretKey, testnet, hyperliquidWalletAddr, asterUser, asterSigner, asterPrivateKey, enabled: true, } updatedExchanges = [...(allExchanges || []), newExchange] } const request = { exchanges: Object.fromEntries( updatedExchanges.map((exchange) => [ exchange.id, { enabled: exchange.enabled, api_key: exchange.apiKey || '', secret_key: exchange.secretKey || '', testnet: exchange.testnet || false, hyperliquid_wallet_addr: exchange.hyperliquidWalletAddr || '', aster_user: exchange.asterUser || '', aster_signer: exchange.asterSigner || '', aster_private_key: exchange.asterPrivateKey || '', }, ]) ), } await api.updateExchangeConfigs(request) // 重新获取用户配置以确保数据同步 const refreshedExchanges = await api.getExchangeConfigs() setAllExchanges(refreshedExchanges) setShowExchangeModal(false) setEditingExchange(null) } catch (error) { console.error('Failed to save exchange config:', error) alert(t('saveConfigFailed', language)) } } const handleAddModel = () => { setEditingModel(null) setShowModelModal(true) } const handleAddExchange = () => { setEditingExchange(null) setShowExchangeModal(true) } const handleSaveSignalSource = async ( coinPoolUrl: string, oiTopUrl: string ) => { try { await api.saveUserSignalSource(coinPoolUrl, oiTopUrl) setUserSignalSource({ coinPoolUrl, oiTopUrl }) setShowSignalSourceModal(false) } catch (error) { console.error('Failed to save signal source:', error) alert(t('saveSignalSourceFailed', language)) } } return (
{/* Header */}

{t('aiTraders', language)} {traders?.length || 0} {t('active', language)}

{t('manageAITraders', language)}

{/* Configuration Status */}
{/* AI Models */}

{t('aiModels', language)}

{configuredModels.map((model) => { const inUse = isModelInUse(model.id) return (
handleModelClick(model.id)} >
{getModelIcon(model.provider || model.id, { width: 28, height: 28, }) || (
{getShortName(model.name)[0]}
)}
{getShortName(model.name)}
{inUse ? t('inUse', language) : model.enabled ? t('enabled', language) : t('configured', language)}
) })} {configuredModels.length === 0 && (
{t('noModelsConfigured', language)}
)}
{/* Exchanges */}

{t('exchanges', language)}

{configuredExchanges.map((exchange) => { const inUse = isExchangeInUse(exchange.id) return (
handleExchangeClick(exchange.id)} >
{getExchangeIcon(exchange.id, { width: 28, height: 28 })}
{getShortName(exchange.name)}
{exchange.type.toUpperCase()} •{' '} {inUse ? t('inUse', language) : exchange.enabled ? t('enabled', language) : t('configured', language)}
) })} {configuredExchanges.length === 0 && (
{t('noExchangesConfigured', language)}
)}
{/* Traders List */}

{t('currentTraders', language)}

{traders && traders.length > 0 ? (
{traders.map((trader) => (
{trader.trader_name}
{getModelDisplayName( trader.ai_model.split('_').pop() || trader.ai_model )}{' '} Model • {trader.exchange_id?.toUpperCase()}
{/* Status */}
{t('status', language)}
{trader.is_running ? t('running', language) : t('stopped', language)}
{/* Actions */}
))}
) : (
{t('noTraders', language)}
{t('createFirstTrader', language)}
{(configuredModels.length === 0 || configuredExchanges.length === 0) && (
{configuredModels.length === 0 && configuredExchanges.length === 0 ? t('configureModelsAndExchangesFirst', language) : configuredModels.length === 0 ? t('configureModelsFirst', language) : t('configureExchangesFirst', language)}
)}
)}
{/* Create Trader Modal */} {showCreateModal && ( setShowCreateModal(false)} /> )} {/* Edit Trader Modal */} {showEditModal && editingTrader && ( { setShowEditModal(false) setEditingTrader(null) }} /> )} {/* Model Configuration Modal */} {showModelModal && ( { setShowModelModal(false) setEditingModel(null) }} language={language} /> )} {/* Exchange Configuration Modal */} {showExchangeModal && ( { setShowExchangeModal(false) setEditingExchange(null) }} language={language} /> )} {/* Signal Source Configuration Modal */} {showSignalSourceModal && ( setShowSignalSourceModal(false)} language={language} /> )}
) } // 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, oiTopUrl, onSave, onClose, language, }: { coinPoolUrl: string oiTopUrl: string onSave: (coinPoolUrl: string, oiTopUrl: string) => void onClose: () => void language: Language }) { const [coinPool, setCoinPool] = useState(coinPoolUrl || '') const [oiTop, setOiTop] = useState(oiTopUrl || '') const handleSubmit = (e: React.FormEvent) => { e.preventDefault() onSave(coinPool.trim(), oiTop.trim()) } return (

📡 {t('signalSourceConfig', language)}

setCoinPool(e.target.value)} placeholder="https://api.example.com/coinpool" className="w-full px-3 py-2 rounded" style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF', }} />
{t('coinPoolDescription', language)}
setOiTop(e.target.value)} placeholder="https://api.example.com/oitop" className="w-full px-3 py-2 rounded" style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF', }} />
{t('oiTopDescription', language)}
ℹ️ {t('information', language)}
{t('signalSourceInfo1', language)}
{t('signalSourceInfo2', language)}
{t('signalSourceInfo3', language)}
) } // Model Configuration Modal Component function ModelConfigModal({ allModels, configuredModels, editingModelId, onSave, onDelete, onClose, language, }: { allModels: AIModel[] configuredModels: AIModel[] editingModelId: string | null onSave: ( modelId: string, apiKey: string, baseUrl?: string, modelName?: string ) => void onDelete: (modelId: string) => void onClose: () => void language: Language }) { const [selectedModelId, setSelectedModelId] = useState(editingModelId || '') const [apiKey, setApiKey] = useState('') const [baseUrl, setBaseUrl] = useState('') const [modelName, setModelName] = useState('') // 获取当前编辑的模型信息 - 编辑时从已配置的模型中查找,新建时从所有支持的模型中查找 const selectedModel = editingModelId ? configuredModels?.find((m) => m.id === selectedModelId) : allModels?.find((m) => m.id === selectedModelId) // 如果是编辑现有模型,初始化API Key、Base URL和Model Name useEffect(() => { if (editingModelId && selectedModel) { setApiKey(selectedModel.apiKey || '') setBaseUrl(selectedModel.customApiUrl || '') setModelName(selectedModel.customModelName || '') } }, [editingModelId, selectedModel]) const handleSubmit = (e: React.FormEvent) => { e.preventDefault() if (!selectedModelId || !apiKey.trim()) return onSave( selectedModelId, apiKey.trim(), baseUrl.trim() || undefined, modelName.trim() || undefined ) } // 可选择的模型列表(所有支持的模型) const availableModels = allModels || [] return (

{editingModelId ? t('editAIModel', language) : t('addAIModel', language)}

{editingModelId && ( )}
{!editingModelId && (
)} {selectedModel && (
{getModelIcon(selectedModel.provider || selectedModel.id, { width: 32, height: 32, }) || (
{selectedModel.name[0]}
)}
{getShortName(selectedModel.name)}
{selectedModel.provider} • {selectedModel.id}
)} {selectedModel && ( <>
setApiKey(e.target.value)} placeholder={t('enterAPIKey', language)} className="w-full px-3 py-2 rounded" style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF', }} required />
setBaseUrl(e.target.value)} placeholder={t('customBaseURLPlaceholder', language)} className="w-full px-3 py-2 rounded" style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF', }} />
{t('leaveBlankForDefault', language)}
setModelName(e.target.value)} placeholder="例如: deepseek-chat, qwen3-max, gpt-5" className="w-full px-3 py-2 rounded" style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF', }} />
留空使用默认模型名称
ℹ️ {t('information', language)}
{t('modelConfigInfo1', language)}
{t('modelConfigInfo2', language)}
{t('modelConfigInfo3', language)}
)}
) } // Exchange Configuration Modal Component function ExchangeConfigModal({ allExchanges, editingExchangeId, onSave, onDelete, onClose, language, }: { allExchanges: Exchange[] editingExchangeId: string | null onSave: ( exchangeId: string, apiKey: string, secretKey?: string, testnet?: boolean, hyperliquidWalletAddr?: string, asterUser?: string, asterSigner?: string, asterPrivateKey?: string ) => Promise onDelete: (exchangeId: string) => void onClose: () => void language: Language }) { const [selectedExchangeId, setSelectedExchangeId] = useState( editingExchangeId || '' ) const [apiKey, setApiKey] = useState('') const [secretKey, setSecretKey] = useState('') const [passphrase, setPassphrase] = useState('') const [testnet, setTestnet] = useState(false) const [showGuide, setShowGuide] = useState(false) // Aster 特定字段 const [asterUser, setAsterUser] = useState('') const [asterSigner, setAsterSigner] = useState('') const [asterPrivateKey, setAsterPrivateKey] = useState('') // 获取当前编辑的交易所信息 const selectedExchange = allExchanges?.find( (e) => e.id === selectedExchangeId ) // 如果是编辑现有交易所,初始化表单数据 useEffect(() => { if (editingExchangeId && selectedExchange) { setApiKey(selectedExchange.apiKey || '') setSecretKey(selectedExchange.secretKey || '') setPassphrase('') // Don't load existing passphrase for security setTestnet(selectedExchange.testnet || false) // Aster 字段 setAsterUser(selectedExchange.asterUser || '') setAsterSigner(selectedExchange.asterSigner || '') setAsterPrivateKey('') // Don't load existing private key for security } }, [editingExchangeId, selectedExchange]) const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() if (!selectedExchangeId) return // 根据交易所类型验证不同字段 if (selectedExchange?.id === 'binance') { if (!apiKey.trim() || !secretKey.trim()) return await onSave(selectedExchangeId, apiKey.trim(), secretKey.trim(), testnet) } else if (selectedExchange?.id === 'hyperliquid') { if (!apiKey.trim()) return // 只验证私钥,钱包地址自动从私钥生成 await onSave(selectedExchangeId, apiKey.trim(), '', testnet, '') // 传空字符串,后端自动生成地址 } else if (selectedExchange?.id === 'aster') { if (!asterUser.trim() || !asterSigner.trim() || !asterPrivateKey.trim()) return await onSave( selectedExchangeId, '', '', testnet, undefined, asterUser.trim(), asterSigner.trim(), asterPrivateKey.trim() ) } else if (selectedExchange?.id === 'okx') { if (!apiKey.trim() || !secretKey.trim() || !passphrase.trim()) return await onSave(selectedExchangeId, apiKey.trim(), secretKey.trim(), testnet) } else { // 默认情况(其他CEX交易所) if (!apiKey.trim() || !secretKey.trim()) return await onSave(selectedExchangeId, apiKey.trim(), secretKey.trim(), testnet) } } // 可选择的交易所列表(所有支持的交易所) const availableExchanges = allExchanges || [] return (

{editingExchangeId ? t('editExchange', language) : t('addExchange', language)}

{selectedExchange?.id === 'binance' && ( )} {editingExchangeId && ( )}
{!editingExchangeId && (
)} {selectedExchange && (
{getExchangeIcon(selectedExchange.id, { width: 32, height: 32, })}
{getShortName(selectedExchange.name)}
{selectedExchange.type.toUpperCase()} •{' '} {selectedExchange.id}
)} {selectedExchange && ( <> {/* Binance 和其他 CEX 交易所的字段 */} {(selectedExchange.id === 'binance' || selectedExchange.type === 'cex') && selectedExchange.id !== 'hyperliquid' && selectedExchange.id !== 'aster' && ( <>
setApiKey(e.target.value)} placeholder={t('enterAPIKey', language)} className="w-full px-3 py-2 rounded" style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF', }} required />
setSecretKey(e.target.value)} placeholder={t('enterSecretKey', language)} className="w-full px-3 py-2 rounded" style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF', }} required />
{selectedExchange.id === 'okx' && (
setPassphrase(e.target.value)} placeholder={t('enterPassphrase', language)} className="w-full px-3 py-2 rounded" style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF', }} required />
)} )} {/* Hyperliquid 交易所的字段 */} {selectedExchange.id === 'hyperliquid' && ( <>
setApiKey(e.target.value)} placeholder={t('enterPrivateKey', language)} className="w-full px-3 py-2 rounded" style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF', }} required />
{t('hyperliquidPrivateKeyDesc', language)}
)} {/* Aster 交易所的字段 */} {selectedExchange.id === 'aster' && ( <>
setAsterUser(e.target.value)} placeholder={t('enterUser', language)} className="w-full px-3 py-2 rounded" style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF', }} required />
setAsterSigner(e.target.value)} placeholder={t('enterSigner', language)} className="w-full px-3 py-2 rounded" style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF', }} required />
setAsterPrivateKey(e.target.value)} placeholder={t('enterPrivateKey', language)} className="w-full px-3 py-2 rounded" style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF', }} required />
)}
{t('testnetDescription', language)}
{' '} {t('securityWarning', language)}
{selectedExchange.id === 'aster' && (
{t('asterUsdtWarning', language)}
)}
{t('exchangeConfigWarning1', language)}
{t('exchangeConfigWarning2', language)}
{t('exchangeConfigWarning3', language)}
)}
{/* Binance Setup Guide Modal */} {showGuide && (
setShowGuide(false)} >
e.stopPropagation()} >

{t('binanceSetupGuide', language)}

{t('binanceSetupGuide',
)}
) }