From 8d93a8a095b061a3a16443e6a3d3d636e2f2e756 Mon Sep 17 00:00:00 2001 From: icy Date: Sat, 1 Nov 2025 18:58:32 +0800 Subject: [PATCH] Fixed health check; Fixed dex config; Add rank trader info view; --- DOCKER_DEPLOY.en.md | 4 +- DOCKER_DEPLOY.md | 4 +- PM2_DEPLOYMENT.md | 2 +- README.md | 4 +- README.ru.md | 2 +- README.uk.md | 2 +- README.zh-CN.md | 4 +- api/server.go | 8 +- config/database.go | 9 +- docker-compose.yml | 2 +- docker/Dockerfile.backend | 2 +- start.sh | 2 +- web/src/components/AITradersPage.tsx | 277 +++++++++++++++---- web/src/components/CompetitionPage.tsx | 6 +- web/src/components/TraderConfigViewModal.tsx | 211 ++++++++++++++ web/src/i18n/translations.ts | 44 +++ web/src/types.ts | 18 ++ 17 files changed, 518 insertions(+), 83 deletions(-) create mode 100644 web/src/components/TraderConfigViewModal.tsx 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 b02a497e..ede2ab31 100644 --- a/README.md +++ b/README.md @@ -859,7 +859,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"}` @@ -1109,7 +1109,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 53f9b82c..7c1d7aed 100644 --- a/README.ru.md +++ b/README.ru.md @@ -768,7 +768,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 27620409..0412e8dc 100644 --- a/README.uk.md +++ b/README.uk.md @@ -768,7 +768,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 a5fc01c9..4d479bd6 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -835,7 +835,7 @@ VITE v5.x.x ready in xxx ms ```bash # 在新终端窗口中 -curl http://localhost:8080/health +curl http://localhost:8080/api/health ``` 应返回:`{"status":"ok"}` @@ -1042,7 +1042,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 e65a8e90..388184d5 100644 --- a/api/server.go +++ b/api/server.go @@ -65,12 +65,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) @@ -1352,7 +1352,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 70da76b6..9e788665 100644 --- a/config/database.go +++ b/config/database.go @@ -811,7 +811,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, @@ -826,6 +831,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 b049f95d..2fcc40fb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,7 +18,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 bc2516cf..ea0b2d2a 100644 --- a/web/src/components/AITradersPage.tsx +++ b/web/src/components/AITradersPage.tsx @@ -3,7 +3,7 @@ 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 { t, Language } from '../i18n/translations'; import { getExchangeIcon } from './ExchangeIcons'; import { getModelIcon } from './ModelIcons'; import { TraderConfigModal } from './TraderConfigModal'; @@ -107,12 +107,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 @@ -375,11 +378,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]; } @@ -780,6 +803,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) { setShowExchangeModal(false); setEditingExchange(null); }} + language={language} /> )} @@ -1083,19 +1107,29 @@ function ExchangeConfigModal({ editingExchangeId, onSave, onDelete, - onClose + 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); + + // 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); @@ -1107,6 +1141,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]); @@ -1117,11 +1159,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); }; // 可选择的交易所列表(所有支持的交易所) @@ -1192,51 +1244,147 @@ function ExchangeConfigModal({ {selectedExchange && ( <> -
- - setApiKey(e.target.value)} - placeholder="输入API密钥" - 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="输入密钥" - 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="输入Passphrase (OKX必填)" - 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 + /> +
+ )}
@@ -1248,21 +1396,21 @@ function ExchangeConfigModal({ className="form-checkbox rounded" style={{ accentColor: '#F0B90B' }} /> - 使用测试网 + {t('useTestnet', language)}
- 启用后将连接到交易所测试环境,用于模拟交易 + {t('testnetDescription', language)}
- ⚠️ 安全提示 + {t('securityWarning', language)}
-
• API密钥将被加密存储,建议使用只读或期货交易权限
-
• 不要授予提现权限,确保资金安全
-
• 删除配置后,相关交易员将无法正常交易
+
{t('securityTip1', language)}
+
{t('securityTip2', language)}
+
{t('securityTip3', language)}
@@ -1275,15 +1423,22 @@ function ExchangeConfigModal({ className="flex-1 px-4 py-2 rounded text-sm font-semibold" style={{ background: '#2B3139', color: '#848E9C' }} > - 取消 + {t('cancel', language)} diff --git a/web/src/components/CompetitionPage.tsx b/web/src/components/CompetitionPage.tsx index 05f58580..193b4cce 100644 --- a/web/src/components/CompetitionPage.tsx +++ b/web/src/components/CompetitionPage.tsx @@ -3,7 +3,7 @@ import useSWR from 'swr'; import { api } from '../lib/api'; import type { CompetitionData } from '../types'; import { ComparisonChart } from './ComparisonChart'; -import { TraderConfigModal } from './TraderConfigModal'; +import { TraderConfigViewModal } from './TraderConfigViewModal'; import { getTraderColor } from '../utils/traderColors'; import { useLanguage } from '../contexts/LanguageContext'; import { t } from '../i18n/translations'; @@ -273,8 +273,8 @@ export function CompetitionPage() { )} - {/* Trader Config Modal */} - 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 9fc1a854..2498131e 100644 --- a/web/src/i18n/translations.ts +++ b/web/src/i18n/translations.ts @@ -164,6 +164,28 @@ 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', + enterSecretKey: 'Enter Secret Key', + enterPrivateKey: 'Enter Private Key', + enterWalletAddress: 'Enter Wallet Address', + enterUser: 'Enter User', + enterSigner: 'Enter Signer Address', + enterPassphrase: 'Enter Passphrase (Required for OKX)', + hyperliquidPrivateKeyDesc: 'Hyperliquid uses private key for trading authentication', + hyperliquidWalletAddressDesc: 'Wallet address corresponding to the private key', + securityWarning: '⚠️ Security Notice', + securityTip1: '• API keys will be encrypted and stored. Recommend using read-only or futures trading permissions', + securityTip2: '• Do not grant withdrawal permissions to ensure fund safety', + securityTip3: '• After deleting configuration, related traders will not be able to trade normally', + testnetDescription: 'Enable to connect to exchange testnet environment for simulation trading', + saveConfiguration: 'Save Configuration', + // Trader Configuration positionMode: 'Position Mode', crossMarginMode: 'Cross Margin', @@ -406,6 +428,28 @@ 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: '与私钥对应的钱包地址', + securityWarning: '⚠️ 安全提示', + securityTip1: '• API密钥将被加密存储,建议使用只读或期货交易权限', + securityTip2: '• 不要授予提现权限,确保资金安全', + securityTip3: '• 删除配置后,相关交易员将无法正常交易', + testnetDescription: '启用后将连接到交易所测试环境,用于模拟交易', + saveConfiguration: '保存配置', + // Trader Configuration positionMode: '仓位模式', crossMarginMode: '全仓模式', diff --git a/web/src/types.ts b/web/src/types.ts index 9554a3b3..7058f9c5 100644 --- a/web/src/types.ts +++ b/web/src/types.ts @@ -179,3 +179,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; +}