import { useState, useEffect } from 'react'; import type { AIModel, Exchange, CreateTraderRequest } from '../types'; // 提取下划线后面的名称部分 function getShortName(fullName: string): string { const parts = fullName.split('_'); return parts.length > 1 ? parts[parts.length - 1] : fullName; } interface TraderConfigData { trader_id?: string; trader_name: string; ai_model: string; exchange_id: string; btc_eth_leverage: number; altcoin_leverage: number; trading_symbols: string; custom_prompt: string; override_base_prompt: boolean; system_prompt_template: string; is_cross_margin: boolean; use_coin_pool: boolean; use_oi_top: boolean; initial_balance: number; } interface TraderConfigModalProps { isOpen: boolean; onClose: () => void; traderData?: TraderConfigData | null; isEditMode?: boolean; availableModels?: AIModel[]; availableExchanges?: Exchange[]; onSave?: (data: CreateTraderRequest) => Promise; } export function TraderConfigModal({ isOpen, onClose, traderData, isEditMode = false, availableModels = [], availableExchanges = [], onSave }: TraderConfigModalProps) { const [formData, setFormData] = useState({ trader_name: '', ai_model: '', exchange_id: '', btc_eth_leverage: 5, altcoin_leverage: 3, trading_symbols: '', custom_prompt: '', override_base_prompt: false, system_prompt_template: 'default', is_cross_margin: true, use_coin_pool: false, use_oi_top: false, initial_balance: 1000, }); const [isSaving, setIsSaving] = useState(false); const [availableCoins, setAvailableCoins] = useState([]); const [selectedCoins, setSelectedCoins] = useState([]); const [showCoinSelector, setShowCoinSelector] = useState(false); const [promptTemplates, setPromptTemplates] = useState<{name: string}[]>([]); useEffect(() => { if (traderData) { setFormData(traderData); // 设置已选择的币种 if (traderData.trading_symbols) { const coins = traderData.trading_symbols.split(',').map(s => s.trim()).filter(s => s); setSelectedCoins(coins); } } else if (!isEditMode) { setFormData({ trader_name: '', ai_model: availableModels[0]?.id || '', exchange_id: availableExchanges[0]?.id || '', btc_eth_leverage: 5, altcoin_leverage: 3, trading_symbols: '', custom_prompt: '', override_base_prompt: false, system_prompt_template: 'default', is_cross_margin: true, use_coin_pool: false, use_oi_top: false, initial_balance: 1000, }); } // 确保旧数据也有默认的 system_prompt_template if (traderData && !traderData.system_prompt_template) { setFormData(prev => ({ ...prev, system_prompt_template: 'default' })); } }, [traderData, isEditMode, availableModels, availableExchanges]); // 获取系统配置中的币种列表 useEffect(() => { const fetchConfig = async () => { try { const response = await fetch('/api/config'); const config = await response.json(); if (config.default_coins) { setAvailableCoins(config.default_coins); } } catch (error) { console.error('Failed to fetch config:', error); // 使用默认币种列表 setAvailableCoins(['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT', 'DOGEUSDT', 'ADAUSDT']); } }; fetchConfig(); }, []); // 获取系统提示词模板列表 useEffect(() => { const fetchPromptTemplates = async () => { try { const response = await fetch('/api/prompt-templates'); const data = await response.json(); if (data.templates) { setPromptTemplates(data.templates); } } catch (error) { console.error('Failed to fetch prompt templates:', error); // 使用默认模板列表 setPromptTemplates([{name: 'default'}, {name: 'aggressive'}]); } }; fetchPromptTemplates(); }, []); // 当选择的币种改变时,更新输入框 useEffect(() => { const symbolsString = selectedCoins.join(','); setFormData(prev => ({ ...prev, trading_symbols: symbolsString })); }, [selectedCoins]); if (!isOpen) return null; const handleInputChange = (field: keyof TraderConfigData, value: any) => { setFormData(prev => ({ ...prev, [field]: value })); // 如果是直接编辑trading_symbols,同步更新selectedCoins if (field === 'trading_symbols') { const coins = value.split(',').map((s: string) => s.trim()).filter((s: string) => s); setSelectedCoins(coins); } }; const handleCoinToggle = (coin: string) => { setSelectedCoins(prev => { if (prev.includes(coin)) { return prev.filter(c => c !== coin); } else { return [...prev, coin]; } }); }; const handleSave = async () => { if (!onSave) return; setIsSaving(true); try { const saveData: CreateTraderRequest = { name: formData.trader_name, ai_model_id: formData.ai_model, exchange_id: formData.exchange_id, btc_eth_leverage: formData.btc_eth_leverage, altcoin_leverage: formData.altcoin_leverage, trading_symbols: formData.trading_symbols, custom_prompt: formData.custom_prompt, override_base_prompt: formData.override_base_prompt, system_prompt_template: formData.system_prompt_template, is_cross_margin: formData.is_cross_margin, use_coin_pool: formData.use_coin_pool, use_oi_top: formData.use_oi_top, initial_balance: formData.initial_balance, }; await onSave(saveData); onClose(); } catch (error) { console.error('保存失败:', error); } finally { setIsSaving(false); } }; return (
e.stopPropagation()} > {/* Header */}
{isEditMode ? '✏️' : '➕'}

{isEditMode ? '修改交易员' : '创建交易员'}

{isEditMode ? '修改交易员配置参数' : '配置新的AI交易员'}

{/* Content */}
{/* Basic Info */}

🤖 基础配置

handleInputChange('trader_name', e.target.value)} className="w-full px-3 py-2 bg-[#0B0E11] border border-[#2B3139] rounded text-[#EAECEF] focus:border-[#F0B90B] focus:outline-none" placeholder="请输入交易员名称" />
{/* Trading Configuration */}

⚖️ 交易配置

{/* 第一行:保证金模式和初始余额 */}
handleInputChange('initial_balance', Number(e.target.value))} className="w-full px-3 py-2 bg-[#0B0E11] border border-[#2B3139] rounded text-[#EAECEF] focus:border-[#F0B90B] focus:outline-none" min="100" step="100" />
{/* 第二行:杠杆设置 */}
handleInputChange('btc_eth_leverage', Number(e.target.value))} className="w-full px-3 py-2 bg-[#0B0E11] border border-[#2B3139] rounded text-[#EAECEF] focus:border-[#F0B90B] focus:outline-none" min="1" max="125" />
handleInputChange('altcoin_leverage', Number(e.target.value))} className="w-full px-3 py-2 bg-[#0B0E11] border border-[#2B3139] rounded text-[#EAECEF] focus:border-[#F0B90B] focus:outline-none" min="1" max="75" />
{/* 第三行:交易币种 */}
handleInputChange('trading_symbols', e.target.value)} className="w-full px-3 py-2 bg-[#0B0E11] border border-[#2B3139] rounded text-[#EAECEF] focus:border-[#F0B90B] focus:outline-none" placeholder="例如: BTCUSDT,ETHUSDT,ADAUSDT" /> {/* 币种选择器 */} {showCoinSelector && (
点击选择币种:
{availableCoins.map(coin => ( ))}
)}
{/* Signal Sources */}

📡 信号源配置

handleInputChange('use_coin_pool', e.target.checked)} className="w-4 h-4" />
handleInputChange('use_oi_top', e.target.checked)} className="w-4 h-4" />
{/* Trading Prompt */}

💬 交易策略提示词

{/* 系统提示词模板选择 */}

选择预设的交易策略模板(包含交易哲学、风控原则等)

handleInputChange('override_base_prompt', e.target.checked)} className="w-4 h-4" /> ⚠️ 启用后将完全替换默认策略