diff --git a/DOCKER_DEPLOY.en.md b/DOCKER_DEPLOY.en.md index 06464b32..bd909273 100644 --- a/DOCKER_DEPLOY.en.md +++ b/DOCKER_DEPLOY.en.md @@ -97,7 +97,7 @@ docker compose up -d Once deployed, open your browser and visit: - **Web Interface**: http://localhost:3000 -- **API Health Check**: http://localhost:8080/health +- **API Health Check**: http://localhost:8080/api/health ## 📊 Service Management @@ -280,7 +280,7 @@ docker inspect nofx-backend | jq '.[0].State.Health' docker inspect nofx-frontend | jq '.[0].State.Health' # Manually test health endpoints -curl http://localhost:8080/health +curl http://localhost:8080/api/health curl http://localhost:3000/health ``` diff --git a/DOCKER_DEPLOY.md b/DOCKER_DEPLOY.md index cc268261..49667356 100644 --- a/DOCKER_DEPLOY.md +++ b/DOCKER_DEPLOY.md @@ -100,7 +100,7 @@ docker compose up -d 部署成功后,打开浏览器访问: - **Web 界面**: http://localhost:3000 -- **API 文档**: http://localhost:8080/health +- **API 文档**: http://localhost:8080/api/health ## 📊 服务管理 @@ -281,7 +281,7 @@ docker inspect nofx-backend | jq '.[0].State.Health' docker inspect nofx-frontend | jq '.[0].State.Health' # 手动测试健康端点 -curl http://localhost:8080/health +curl http://localhost:8080/api/health curl http://localhost:3000/health ``` diff --git a/PM2_DEPLOYMENT.md b/PM2_DEPLOYMENT.md index c9806ea3..3668a29e 100644 --- a/PM2_DEPLOYMENT.md +++ b/PM2_DEPLOYMENT.md @@ -79,7 +79,7 @@ npm install -g pm2 - **前端 Web 界面**: http://localhost:3000 - **后端 API**: http://localhost:8080 -- **健康检查**: http://localhost:8080/health +- **健康检查**: http://localhost:8080/api/health --- diff --git a/README.md b/README.md index ec3432d9..7ac4f409 100644 --- a/README.md +++ b/README.md @@ -887,7 +887,7 @@ Open your web browser and visit: ```bash # In a new terminal window -curl http://localhost:8080/health +curl http://localhost:8080/api/health ``` Should return: `{"status":"ok"}` @@ -1137,7 +1137,7 @@ GET /api/performance?trader_id=xxx # AI performance analysis ### System Endpoints ```bash -GET /health # Health check +GET /api/health # Health check ``` --- diff --git a/README.ru.md b/README.ru.md index b93a3e14..2d2e6ff3 100644 --- a/README.ru.md +++ b/README.ru.md @@ -796,7 +796,7 @@ VITE v5.x.x ready in xxx ms ```bash # В новом окне терминала -curl http://localhost:8080/health +curl http://localhost:8080/api/health ``` Должно вернуть: `{"status":"ok"}` diff --git a/README.uk.md b/README.uk.md index ce751669..0ec6d5df 100644 --- a/README.uk.md +++ b/README.uk.md @@ -796,7 +796,7 @@ VITE v5.x.x ready in xxx ms ```bash # У новому вікні терміналу -curl http://localhost:8080/health +curl http://localhost:8080/api/health ``` Повинно повернути: `{"status":"ok"}` diff --git a/README.zh-CN.md b/README.zh-CN.md index e4be410d..75be2f64 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -869,7 +869,7 @@ VITE v5.x.x ready in xxx ms ```bash # 在新终端窗口中 -curl http://localhost:8080/health +curl http://localhost:8080/api/health ``` 应返回:`{"status":"ok"}` @@ -1076,7 +1076,7 @@ GET /api/statistics?trader_id=xxx # 统计信息 ### 系统接口 ```bash -GET /health # 健康检查 +GET /api/health # 健康检查 GET /api/config # 系统配置 ``` diff --git a/api/server.go b/api/server.go index 110f0a9c..ad16470a 100644 --- a/api/server.go +++ b/api/server.go @@ -66,12 +66,12 @@ func corsMiddleware() gin.HandlerFunc { // setupRoutes 设置路由 func (s *Server) setupRoutes() { - // 健康检查 - s.router.Any("/health", s.handleHealth) - // API路由组 api := s.router.Group("/api") { + // 健康检查 + api.Any("/health", s.handleHealth) + // 认证相关路由(无需认证) api.POST("/register", s.handleRegister) api.POST("/login", s.handleLogin) @@ -84,6 +84,10 @@ func (s *Server) setupRoutes() { // 系统配置(无需认证) api.GET("/config", s.handleGetSystemConfig) + + // 系统提示词模板管理(无需认证) + api.GET("/prompt-templates", s.handleGetPromptTemplates) + api.GET("/prompt-templates/:name", s.handleGetPromptTemplate) // 需要认证的路由 protected := api.Group("/", s.authMiddleware()) @@ -110,9 +114,6 @@ func (s *Server) setupRoutes() { protected.GET("/user/signal-sources", s.handleGetUserSignalSource) protected.POST("/user/signal-sources", s.handleSaveUserSignalSource) - // 系统提示词模板管理 - protected.GET("/prompt-templates", s.handleGetPromptTemplates) - protected.GET("/prompt-templates/:name", s.handleGetPromptTemplate) // 竞赛总览 protected.GET("/competition", s.handleCompetition) @@ -1398,7 +1399,7 @@ func (s *Server) Start() error { addr := fmt.Sprintf(":%d", s.port) log.Printf("🌐 API服务器启动在 http://localhost%s", addr) log.Printf("📊 API文档:") - log.Printf(" • GET /health - 健康检查") + log.Printf(" • GET /api/health - 健康检查") log.Printf(" • GET /api/traders - AI交易员列表") log.Printf(" • POST /api/traders - 创建新的AI交易员") log.Printf(" • DELETE /api/traders/:id - 删除AI交易员") diff --git a/config/database.go b/config/database.go index 624433bc..c5eef755 100644 --- a/config/database.go +++ b/config/database.go @@ -853,7 +853,12 @@ func (d *Database) GetTraderConfig(userID, traderID string) (*TraderRecord, *AIM err := d.db.QueryRow(` SELECT - t.id, t.user_id, t.name, t.ai_model_id, t.exchange_id, t.initial_balance, t.scan_interval_minutes, t.is_running, t.created_at, t.updated_at, + t.id, t.user_id, t.name, t.ai_model_id, t.exchange_id, t.initial_balance, t.scan_interval_minutes, t.is_running, + COALESCE(t.btc_eth_leverage, 5) as btc_eth_leverage, COALESCE(t.altcoin_leverage, 5) as altcoin_leverage, + COALESCE(t.trading_symbols, '') as trading_symbols, COALESCE(t.use_coin_pool, 0) as use_coin_pool, + COALESCE(t.use_oi_top, 0) as use_oi_top, COALESCE(t.custom_prompt, '') as custom_prompt, + COALESCE(t.override_base_prompt, 0) as override_base_prompt, COALESCE(t.is_cross_margin, 1) as is_cross_margin, + t.created_at, t.updated_at, a.id, a.user_id, a.name, a.provider, a.enabled, a.api_key, a.created_at, a.updated_at, e.id, e.user_id, e.name, e.type, e.enabled, e.api_key, e.secret_key, e.testnet, COALESCE(e.hyperliquid_wallet_addr, '') as hyperliquid_wallet_addr, @@ -868,6 +873,8 @@ func (d *Database) GetTraderConfig(userID, traderID string) (*TraderRecord, *AIM `, traderID, userID).Scan( &trader.ID, &trader.UserID, &trader.Name, &trader.AIModelID, &trader.ExchangeID, &trader.InitialBalance, &trader.ScanIntervalMinutes, &trader.IsRunning, + &trader.BTCETHLeverage, &trader.AltcoinLeverage, &trader.TradingSymbols, &trader.UseCoinPool, + &trader.UseOITop, &trader.CustomPrompt, &trader.OverrideBasePrompt, &trader.IsCrossMargin, &trader.CreatedAt, &trader.UpdatedAt, &aiModel.ID, &aiModel.UserID, &aiModel.Name, &aiModel.Provider, &aiModel.Enabled, &aiModel.APIKey, &aiModel.CreatedAt, &aiModel.UpdatedAt, diff --git a/docker-compose.yml b/docker-compose.yml index 612f1466..e2f6c905 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,7 +19,7 @@ services: networks: - nofx-network healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8080/health"] + test: ["CMD", "curl", "-f", "http://localhost:8080/api/health"] interval: 30s timeout: 10s retries: 3 diff --git a/docker/Dockerfile.backend b/docker/Dockerfile.backend index 18387f67..c25700f2 100644 --- a/docker/Dockerfile.backend +++ b/docker/Dockerfile.backend @@ -63,6 +63,6 @@ COPY --from=backend-builder /app/nofx . EXPOSE 8080 HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ - CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1 + CMD wget --no-verbose --tries=1 --spider http://localhost:8080/api/health || exit 1 CMD ["./nofx"] diff --git a/start.sh b/start.sh index 04f0478c..47cb2536 100755 --- a/start.sh +++ b/start.sh @@ -227,7 +227,7 @@ status() { $COMPOSE_CMD ps echo "" print_info "健康检查:" - curl -s "http://localhost:${NOFX_BACKEND_PORT}/health" | jq '.' || echo "后端未响应" + curl -s "http://localhost:${NOFX_BACKEND_PORT}/api/health" | jq '.' || echo "后端未响应" } # ------------------------------------------------------------------------ diff --git a/web/src/components/AITradersPage.tsx b/web/src/components/AITradersPage.tsx index b031340c..d5937fb0 100644 --- a/web/src/components/AITradersPage.tsx +++ b/web/src/components/AITradersPage.tsx @@ -101,12 +101,15 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) { // Aster 交易所需要特殊字段 if (e.id === 'aster') { - return e.asterUser && e.asterSigner && e.asterPrivateKey; + return e.asterUser && e.asterUser.trim() !== '' && + e.asterSigner && e.asterSigner.trim() !== '' && + e.asterPrivateKey && e.asterPrivateKey.trim() !== ''; } - // Hyperliquid 只需要私钥(作为apiKey),不需要secretKey + // Hyperliquid 只需要私钥(作为apiKey)和钱包地址 if (e.id === 'hyperliquid') { - return e.apiKey && e.hyperliquidWalletAddr; + return e.apiKey && e.apiKey.trim() !== '' && + e.hyperliquidWalletAddr && e.hyperliquidWalletAddr.trim() !== ''; } // Binance 等其他交易所需要 apiKey 和 secretKey @@ -372,11 +375,31 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) { if (existingExchange) { // 更新现有配置 updatedExchanges = allExchanges?.map(e => - e.id === exchangeId ? { ...e, apiKey, secretKey, testnet, hyperliquidWalletAddr, asterUser, asterSigner, asterPrivateKey, enabled: true } : 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 }; + const newExchange = { + ...exchangeToUpdate, + apiKey, + secretKey, + testnet, + hyperliquidWalletAddr, + asterUser, + asterSigner, + asterPrivateKey, + enabled: true + }; updatedExchanges = [...(allExchanges || []), newExchange]; } @@ -1121,6 +1144,14 @@ function ExchangeConfigModal({ const [secretKey, setSecretKey] = useState(''); const [passphrase, setPassphrase] = useState(''); const [testnet, setTestnet] = useState(false); + + // Hyperliquid 特定字段 + const [hyperliquidWalletAddr, setHyperliquidWalletAddr] = useState(''); + + // Aster 特定字段 + const [asterUser, setAsterUser] = useState(''); + const [asterSigner, setAsterSigner] = useState(''); + const [asterPrivateKey, setAsterPrivateKey] = useState(''); // 获取当前编辑的交易所信息 const selectedExchange = allExchanges?.find(e => e.id === selectedExchangeId); @@ -1132,6 +1163,14 @@ function ExchangeConfigModal({ setSecretKey(selectedExchange.secretKey || ''); setPassphrase(''); // Don't load existing passphrase for security setTestnet(selectedExchange.testnet || false); + + // Hyperliquid 字段 + setHyperliquidWalletAddr(selectedExchange.hyperliquidWalletAddr || ''); + + // Aster 字段 + setAsterUser(selectedExchange.asterUser || ''); + setAsterSigner(selectedExchange.asterSigner || ''); + setAsterPrivateKey(''); // Don't load existing private key for security } }, [editingExchangeId, selectedExchange]); @@ -1142,11 +1181,21 @@ function ExchangeConfigModal({ // 根据交易所类型验证不同字段 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() || !hyperliquidWalletAddr.trim()) return; + await onSave(selectedExchangeId, apiKey.trim(), '', testnet, hyperliquidWalletAddr.trim()); + } 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); } - - await onSave(selectedExchangeId, apiKey.trim(), secretKey.trim(), testnet, undefined, undefined, undefined, undefined); }; // 可选择的交易所列表(所有支持的交易所) @@ -1217,51 +1266,147 @@ function ExchangeConfigModal({ {selectedExchange && ( <> -
- - 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 - /> -
+ {/* 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 - /> -
+
+ + 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 - /> -
+ {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)} +
+
+ +
+ + setHyperliquidWalletAddr(e.target.value)} + placeholder={t('enterWalletAddress', language)} + className="w-full px-3 py-2 rounded" + style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }} + required + /> +
+ {t('hyperliquidWalletAddressDesc', 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 + /> +
+ )}
@@ -1304,7 +1449,14 @@ function ExchangeConfigModal({
)} - {/* Trader Config Modal */} - { const fetchPromptTemplates = async () => { try { - const token = localStorage.getItem('token'); - const response = await fetch('/api/prompt-templates', { - headers: { - 'Authorization': `Bearer ${token}` - } - }); + const response = await fetch('/api/prompt-templates'); const data = await response.json(); if (data.templates) { setPromptTemplates(data.templates); diff --git a/web/src/components/TraderConfigViewModal.tsx b/web/src/components/TraderConfigViewModal.tsx new file mode 100644 index 00000000..553b5b52 --- /dev/null +++ b/web/src/components/TraderConfigViewModal.tsx @@ -0,0 +1,211 @@ +import { useState } from 'react'; +import type { TraderConfigData } from '../types'; + +// 提取下划线后面的名称部分 +function getShortName(fullName: string): string { + const parts = fullName.split('_'); + return parts.length > 1 ? parts[parts.length - 1] : fullName; +} + + +interface TraderConfigViewModalProps { + isOpen: boolean; + onClose: () => void; + traderData?: TraderConfigData | null; +} + +export function TraderConfigViewModal({ + isOpen, + onClose, + traderData +}: TraderConfigViewModalProps) { + const [copiedField, setCopiedField] = useState(null); + + if (!isOpen || !traderData) return null; + + const copyToClipboard = async (text: string, fieldName: string) => { + try { + await navigator.clipboard.writeText(text); + setCopiedField(fieldName); + setTimeout(() => setCopiedField(null), 2000); + } catch (error) { + console.error('Failed to copy:', error); + } + }; + + const CopyButton = ({ text, fieldName }: { text: string; fieldName: string }) => ( + + ); + + const InfoRow = ({ label, value, copyable = false, fieldName = '' }: { + label: string; + value: string | number | boolean; + copyable?: boolean; + fieldName?: string; + }) => ( +
+ {label} +
+ + {typeof value === 'boolean' ? (value ? '是' : '否') : value} + + {copyable && typeof value === 'string' && value && ( + + )} +
+
+ ); + + return ( +
+
e.stopPropagation()} + > + {/* Header */} +
+
+
+ 👁️ +
+
+

+ 交易员配置 +

+

+ {traderData.trader_name} 的配置信息 +

+
+
+
+ {/* Running Status */} +
+ {traderData.is_running ? '●' : '○'} + {traderData.is_running ? '运行中' : '已停止'} +
+ +
+
+ + {/* Content */} +
+ {/* Basic Info */} +
+

+ 🤖 基础信息 +

+
+ + + + + +
+
+ + {/* Trading Configuration */} +
+

+ ⚖️ 交易配置 +

+
+ + + + +
+
+ + {/* Signal Sources */} +
+

+ 📡 信号源配置 +

+
+ + +
+
+ + {/* Custom Prompt */} +
+
+

+ 💬 交易策略提示词 +

+ {traderData.custom_prompt && ( + + )} +
+
+ + {traderData.custom_prompt ? ( +
+
+ {traderData.override_base_prompt ? '自定义提示词' : '附加提示词'}: +
+
+ {traderData.custom_prompt} +
+
+ ) : ( +
+ 未设置自定义提示词,使用系统默认策略 +
+ )} +
+
+
+ + {/* Footer */} +
+ + +
+
+
+ ); +} \ No newline at end of file diff --git a/web/src/i18n/translations.ts b/web/src/i18n/translations.ts index 305d0a20..ed5caaad 100644 --- a/web/src/i18n/translations.ts +++ b/web/src/i18n/translations.ts @@ -172,6 +172,25 @@ export const translations = { useOfficialAPI: 'Use official API service', useCustomAPI: 'Use custom API endpoint', + // Exchange Configuration + secretKey: 'Secret Key', + privateKey: 'Private Key', + walletAddress: 'Wallet Address', + user: 'User', + signer: 'Signer', + passphrase: 'Passphrase', + enterPrivateKey: 'Enter Private Key', + enterWalletAddress: 'Enter Wallet Address', + enterUser: 'Enter User', + enterSigner: 'Enter Signer Address', + enterSecretKey: 'Enter Secret Key', + enterPassphrase: 'Enter Passphrase (Required for OKX)', + hyperliquidPrivateKeyDesc: 'Hyperliquid uses private key for trading authentication', + hyperliquidWalletAddressDesc: 'Wallet address corresponding to the private key', + testnetDescription: 'Enable to connect to exchange test environment for simulated trading', + securityWarning: 'Security Warning', + saveConfiguration: 'Save Configuration', + // Trader Configuration positionMode: 'Position Mode', crossMarginMode: 'Cross Margin', @@ -227,10 +246,6 @@ export const translations = { addExchange: 'Add Exchange', confirmDeleteExchange: 'Are you sure you want to delete this exchange configuration?', pleaseSelectExchange: 'Please select an exchange', - enterSecretKey: 'Enter secret key', - enterPassphrase: 'Enter Passphrase (Required for OKX)', - testnetDescription: 'Enable to connect to exchange test environment for simulated trading', - securityWarning: 'Security Warning', exchangeConfigWarning1: '• API keys will be encrypted, recommend using read-only or futures trading permissions', exchangeConfigWarning2: '• Do not grant withdrawal permissions to ensure fund security', exchangeConfigWarning3: '• After deleting configuration, related traders will not be able to trade', @@ -474,6 +489,25 @@ export const translations = { useOfficialAPI: '使用官方API服务', useCustomAPI: '使用自定义API端点', + // Exchange Configuration + secretKey: '密钥', + privateKey: '私钥', + walletAddress: '钱包地址', + user: '用户名', + signer: '签名者', + passphrase: '口令', + enterSecretKey: '输入密钥', + enterPrivateKey: '输入私钥', + enterWalletAddress: '输入钱包地址', + enterUser: '输入用户名', + enterSigner: '输入签名者地址', + enterPassphrase: '输入Passphrase (OKX必填)', + hyperliquidPrivateKeyDesc: 'Hyperliquid 使用私钥进行交易认证', + hyperliquidWalletAddressDesc: '与私钥对应的钱包地址', + testnetDescription: '启用后将连接到交易所测试环境,用于模拟交易', + securityWarning: '安全提示', + saveConfiguration: '保存配置', + // Trader Configuration positionMode: '仓位模式', crossMarginMode: '全仓模式', @@ -529,10 +563,6 @@ export const translations = { addExchange: '添加交易所', confirmDeleteExchange: '确定要删除此交易所配置吗?', pleaseSelectExchange: '请选择交易所', - enterSecretKey: '输入密钥', - enterPassphrase: '输入Passphrase (OKX必填)', - testnetDescription: '启用后将连接到交易所测试环境,用于模拟交易', - securityWarning: '安全提示', exchangeConfigWarning1: '• API密钥将被加密存储,建议使用只读或期货交易权限', exchangeConfigWarning2: '• 不要授予提现权限,确保资金安全', exchangeConfigWarning3: '• 删除配置后,相关交易员将无法正常交易', diff --git a/web/src/types.ts b/web/src/types.ts index 24ad061b..e4ba1199 100644 --- a/web/src/types.ts +++ b/web/src/types.ts @@ -182,3 +182,21 @@ export interface CompetitionData { traders: CompetitionTraderData[]; count: number; } + +// Trader Configuration Data for View Modal +export 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; + is_cross_margin: boolean; + use_coin_pool: boolean; + use_oi_top: boolean; + initial_balance: number; + is_running: boolean; +}