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 } from '../i18n/translations'; import { getExchangeIcon } from './ExchangeIcons'; import { getModelIcon } from './ModelIcons'; // 获取友好的AI模型名称 function getModelDisplayName(modelId: string): string { switch (modelId.toLowerCase()) { case 'deepseek': return 'DeepSeek'; case 'qwen': return 'Qwen'; case 'claude': return 'Claude'; case 'gpt4': case 'gpt-4': return 'GPT-4'; case 'gpt3.5': case 'gpt-3.5': return 'GPT-3.5'; default: return modelId.toUpperCase(); } } interface AITradersPageProps { onTraderSelect?: (traderId: string) => void; } export function AITradersPage({ onTraderSelect }: AITradersPageProps) { const { language } = useLanguage(); const [showCreateModal, setShowCreateModal] = useState(false); const [showModelModal, setShowModelModal] = useState(false); const [showExchangeModal, setShowExchangeModal] = useState(false); const [editingModel, setEditingModel] = useState(null); const [editingExchange, setEditingExchange] = useState(null); const [allModels, setAllModels] = useState([]); const [allExchanges, setAllExchanges] = useState([]); const [supportedModels, setSupportedModels] = useState([]); const [supportedExchanges, setSupportedExchanges] = useState([]); const { data: traders, mutate: mutateTraders } = useSWR( 'traders', api.getTraders, { refreshInterval: 5000 } ); // 加载AI模型和交易所配置 useEffect(() => { const loadConfigs = async () => { 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); } catch (error) { console.error('Failed to load configs:', error); } }; loadConfigs(); }, []); // 显示所有用户的模型和交易所配置(用于调试) 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.asterSigner && e.asterPrivateKey; } // Hyperliquid 只需要私钥(作为apiKey),不需要secretKey if (e.id === 'hyperliquid') { return e.apiKey && e.hyperliquidWalletAddr; } // 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 (modelId: string, exchangeId: string, name: string, initialBalance: number, customPrompt?: string, overrideBase?: boolean, isCrossMargin?: boolean) => { try { const model = allModels?.find(m => m.id === modelId); const exchange = allExchanges?.find(e => e.id === exchangeId); if (!model?.enabled) { alert(t('modelNotConfigured', language)); return; } if (!exchange?.enabled) { alert(t('exchangeNotConfigured', language)); return; } const request: CreateTraderRequest = { name, ai_model_id: modelId, exchange_id: exchangeId, initial_balance: initialBalance, custom_prompt: customPrompt, override_base_prompt: overrideBase, is_cross_margin: isCrossMargin }; await api.createTrader(request); setShowCreateModal(false); mutateTraders(); } catch (error) { console.error('Failed to create trader:', error); alert('创建交易员失败'); } }; 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('删除交易员失败'); } }; 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('操作失败'); } }; 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('确定要删除此AI模型配置吗?')) return; try { const updatedModels = allModels?.map(m => m.id === modelId ? { ...m, apiKey: '', enabled: false } : m ) || []; const request = { models: Object.fromEntries( updatedModels.map(model => [ model.id, { enabled: model.enabled, api_key: model.apiKey || '' } ]) ) }; await api.updateModelConfigs(request); setAllModels(updatedModels); setShowModelModal(false); setEditingModel(null); } catch (error) { console.error('Failed to delete model config:', error); alert('删除配置失败'); } }; const handleSaveModelConfig = async (modelId: string, apiKey: string) => { try { // 找到要配置的模型(从supportedModels中) const modelToUpdate = supportedModels?.find(m => m.id === modelId); if (!modelToUpdate) { alert('模型不存在'); return; } // 创建或更新用户的模型配置 const existingModel = allModels?.find(m => m.id === modelId); let updatedModels; if (existingModel) { // 更新现有配置 updatedModels = allModels?.map(m => m.id === modelId ? { ...m, apiKey, enabled: true } : m ) || []; } else { // 添加新配置 const newModel = { ...modelToUpdate, apiKey, enabled: true }; updatedModels = [...(allModels || []), newModel]; } const request = { models: Object.fromEntries( updatedModels.map(model => [ model.id, { enabled: model.enabled, api_key: model.apiKey || '' } ]) ) }; 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('保存配置失败'); } }; const handleDeleteExchangeConfig = async (exchangeId: string) => { if (!confirm('确定要删除此交易所配置吗?')) 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('删除交易所配置失败'); } }; 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('交易所不存在'); 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('保存交易所配置失败'); } }; const handleAddModel = () => { setEditingModel(null); setShowModelModal(true); }; const handleAddExchange = () => { setEditingExchange(null); setShowExchangeModal(true); }; 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: 32, height: 32 }) || (
{model.name[0]}
)}
{model.name}
{inUse ? '正在使用' : model.enabled ? '已启用' : '已配置'}
); })} {configuredModels.length === 0 && (
🧠
暂无已配置的AI模型
)}
{/* Exchanges */}

🏦 {t('exchanges', language)}

{configuredExchanges.map(exchange => { const inUse = isExchangeInUse(exchange.id); return (
handleExchangeClick(exchange.id)} >
{getExchangeIcon(exchange.id, { width: 32, height: 32 })}
{exchange.name}
{exchange.type.toUpperCase()} • {inUse ? '正在使用' : exchange.enabled ? '已启用' : '已配置'}
); })} {configuredExchanges.length === 0 && (
🏦
暂无已配置的交易所
)}
{/* 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)} language={language} /> )} {/* Model Configuration Modal */} {showModelModal && ( { setShowModelModal(false); setEditingModel(null); }} language={language} /> )} {/* Exchange Configuration Modal */} {showExchangeModal && ( { setShowExchangeModal(false); setEditingExchange(null); }} language={language} /> )}
); } // Create Trader Modal Component function CreateTraderModal({ enabledModels, enabledExchanges, onCreate, onClose, language }: { enabledModels: AIModel[]; enabledExchanges: Exchange[]; onCreate: (modelId: string, exchangeId: string, name: string, initialBalance: number, customPrompt?: string, overrideBase?: boolean, isCrossMargin?: boolean) => void; onClose: () => void; language: any; }) { // 默认选择DeepSeek模型,如果没有启用则选择第一个 const defaultModel = enabledModels.find(m => m.id === 'deepseek') || enabledModels[0]; // 默认选择Binance交易所,如果没有启用则选择第一个 const defaultExchange = enabledExchanges.find(e => e.id === 'binance') || enabledExchanges[0]; const [selectedModel, setSelectedModel] = useState(defaultModel?.id || ''); const [selectedExchange, setSelectedExchange] = useState(defaultExchange?.id || ''); const [traderName, setTraderName] = useState(''); const [initialBalance, setInitialBalance] = useState(1000); const [customPrompt, setCustomPrompt] = useState(''); const [showAdvanced, setShowAdvanced] = useState(false); const [overrideBase, setOverrideBase] = useState(false); const [isCrossMargin, setIsCrossMargin] = useState(true); // 默认为全仓模式 const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!selectedModel || !selectedExchange || !traderName.trim()) return; onCreate(selectedModel, selectedExchange, traderName.trim(), initialBalance, customPrompt.trim() || undefined, overrideBase, isCrossMargin); }; return (

{t('createNewTrader', language)}

setTraderName(e.target.value)} placeholder={t('enterTraderName', language)} className="w-full px-3 py-2 rounded" style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }} required />
setInitialBalance(Number(e.target.value))} min="50" className="w-full px-3 py-2 rounded" style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }} required />
{/* Margin Mode Selection */}
{isCrossMargin ? '全仓模式:所有仓位共享账户余额作为保证金' : '逐仓模式:每个仓位独立管理保证金,风险隔离'}
{/* Advanced Settings Toggle */}
{/* Custom Prompt Field - Show when advanced is toggled */} {showAdvanced && (