diff --git a/web/package-lock.json b/web/package-lock.json
index c7990faa..32e3c01b 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -10,6 +10,7 @@
"dependencies": {
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
+ "lucide-react": "^0.552.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"recharts": "^2.15.2",
@@ -2156,6 +2157,15 @@
"yallist": "^3.0.2"
}
},
+ "node_modules/lucide-react": {
+ "version": "0.552.0",
+ "resolved": "https://registry.npmmirror.com/lucide-react/-/lucide-react-0.552.0.tgz",
+ "integrity": "sha512-g9WCjmfwqbexSnZE+2cl21PCfXOcqnGeWeMTNAOGEfpPbm/ZF4YIq77Z8qWrxbu660EKuLB4nSLggoKnCb+isw==",
+ "license": "ISC",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
diff --git a/web/package.json b/web/package.json
index a126d761..dfe57495 100644
--- a/web/package.json
+++ b/web/package.json
@@ -8,22 +8,23 @@
"preview": "vite preview"
},
"dependencies": {
+ "clsx": "^2.1.1",
+ "date-fns": "^4.1.0",
+ "lucide-react": "^0.552.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
- "zustand": "^5.0.2",
- "swr": "^2.2.5",
"recharts": "^2.15.2",
- "date-fns": "^4.1.0",
- "clsx": "^2.1.1"
+ "swr": "^2.2.5",
+ "zustand": "^5.0.2"
},
"devDependencies": {
"@types/react": "^18.3.17",
"@types/react-dom": "^18.3.5",
"@vitejs/plugin-react": "^4.3.4",
- "typescript": "^5.8.3",
- "vite": "^6.0.7",
- "tailwindcss": "^3.4.17",
+ "autoprefixer": "^10.4.20",
"postcss": "^8.4.49",
- "autoprefixer": "^10.4.20"
+ "tailwindcss": "^3.4.17",
+ "typescript": "^5.8.3",
+ "vite": "^6.0.7"
}
}
diff --git a/web/public/images/logo.png b/web/public/images/logo.png
new file mode 100644
index 00000000..28ec8c71
Binary files /dev/null and b/web/public/images/logo.png differ
diff --git a/web/src/App.tsx b/web/src/App.tsx
index ddc6d028..7c84ba9d 100644
--- a/web/src/App.tsx
+++ b/web/src/App.tsx
@@ -11,6 +11,7 @@ import { LanguageProvider, useLanguage } from './contexts/LanguageContext';
import { AuthProvider, useAuth } from './contexts/AuthContext';
import { t, type Language } from './i18n/translations';
import { useSystemConfig } from './hooks/useSystemConfig';
+import { Bot, RefreshCw, TrendingUp, BarChart3, Brain, Download, Upload, Check, X, AlertCircle, Zap, TrendingUp as ArrowUp, TrendingDown as ArrowDown } from 'lucide-react';
import type {
SystemStatus,
AccountInfo,
@@ -175,10 +176,7 @@ function App() {
return (
-
- ⚡
-
+
加载中...
@@ -201,9 +199,7 @@ function App() {
{/* Left - Logo and Title */}
-
- ⚡
-
+
{t('appTitle', language)}
@@ -264,7 +260,8 @@ function App() {
{/* Admin Mode Indicator */}
{systemConfig?.admin_mode && (
- ⚡ 管理员模式
+
+ 管理员模式
)}
@@ -429,9 +426,9 @@ function TraderDetailsPage({
-
- 🤖
-
+
+
+
{selectedTrader.trader_name}
@@ -470,8 +467,9 @@ function TraderDetailsPage({
{/* Debug Info */}
{account && (
-
- 🔄 Last Update: {lastUpdate} | Total Equity: {account?.total_equity?.toFixed(2) || '0.00'} |
+
+
+ Last Update: {lastUpdate} | Total Equity: {account?.total_equity?.toFixed(2) || '0.00'} |
Available: {account?.available_balance?.toFixed(2) || '0.00'} | P&L: {account?.total_pnl?.toFixed(2) || '0.00'}{' '}
({account?.total_pnl_pct?.toFixed(2) || '0.00'}%)
@@ -517,7 +515,8 @@ function TraderDetailsPage({
- 📈 {t('currentPositions', language)}
+
+ {t('currentPositions', language)}
{positions && positions.length > 0 && (
@@ -581,7 +580,9 @@ function TraderDetailsPage({
) : (
-
📊
+
+
+
{t('noPositions', language)}
{t('noActivePositions', language)}
@@ -594,11 +595,11 @@ function TraderDetailsPage({
{/* 标题 */}
-
- 🧠
+
{t('recentDecisions', language)}
@@ -618,7 +619,9 @@ function TraderDetailsPage({
))
) : (
-
🧠
+
+
+
{t('noDecisionsYet', language)}
{t('aiDecisionsWillAppear', language)}
@@ -657,10 +660,11 @@ function StatCard({
{change !== undefined && (
- {positive ? '▲' : '▼'} {positive ? '+' : ''}
+ {positive ?
:
}
+ {positive ? '+' : ''}
{change.toFixed(2)}%
@@ -704,7 +708,8 @@ function DecisionCard({ decision, language }: { decision: DecisionRecord; langua
className="flex items-center gap-2 text-sm transition-colors"
style={{ color: '#60a5fa' }}
>
-
📥 {t('inputPrompt', language)}
+
+
{t('inputPrompt', language)}
{showInputPrompt ? t('collapse', language) : t('expand', language)}
{showInputPrompt && (
@@ -723,7 +728,8 @@ function DecisionCard({ decision, language }: { decision: DecisionRecord; langua
className="flex items-center gap-2 text-sm transition-colors"
style={{ color: '#F0B90B' }}
>
-
📤 {t('aiThinking', language)}
+
+
{t('aiThinking', language)}
{showCoT ? t('collapse', language) : t('expand', language)}
{showCoT && (
@@ -753,9 +759,11 @@ function DecisionCard({ decision, language }: { decision: DecisionRecord; langua
{action.price > 0 && (
@{action.price.toFixed(4)}
)}
-
- {action.success ? '✓' : '✗'}
-
+ {action.success ? (
+
+ ) : (
+
+ )}
{action.error &&
{action.error}}
))}
@@ -789,8 +797,9 @@ function DecisionCard({ decision, language }: { decision: DecisionRecord; langua
{/* Error Message */}
{decision.error_message && (
-
- ❌ {decision.error_message}
+
+
+ {decision.error_message}
)}
diff --git a/web/src/components/AILearning.tsx b/web/src/components/AILearning.tsx
index 20da61d2..f44baf27 100644
--- a/web/src/components/AILearning.tsx
+++ b/web/src/components/AILearning.tsx
@@ -2,6 +2,7 @@ import useSWR from 'swr';
import { useLanguage } from '../contexts/LanguageContext';
import { t } from '../i18n/translations';
import { api } from '../lib/api';
+import { Brain, BarChart3, TrendingUp, TrendingDown, Sparkles, Coins, Trophy, ScrollText, Lightbulb } from 'lucide-react';
interface TradeOutcome {
symbol: string;
@@ -72,7 +73,9 @@ export default function AILearning({ traderId }: AILearningProps) {
if (!performance) {
return (
-
📊 {t('loading', language)}
+
+ {t('loading', language)}
+
);
}
@@ -81,7 +84,7 @@ export default function AILearning({ traderId }: AILearningProps) {
return (
- 🧠
+
{t('aiLearning', language)}
@@ -109,12 +112,12 @@ export default function AILearning({ traderId }: AILearningProps) {
filter: 'blur(60px)'
}} />
-
- 🧠
+
{performance.total_trades}
-
📊 Trades
+
+ Trades
+
@@ -199,7 +204,9 @@ export default function AILearning({ traderId }: AILearningProps) {
+{(performance.avg_win || 0).toFixed(2)}
-
📈 USDT Average
+
+ USDT Average
+
@@ -220,7 +227,9 @@ export default function AILearning({ traderId }: AILearningProps) {
{(performance.avg_loss || 0).toFixed(2)}
-
📉 USDT Average
+
+ USDT Average
+
@@ -239,11 +248,11 @@ export default function AILearning({ traderId }: AILearningProps) {
}} />
-
- 🧬
+
夏普比率
@@ -307,11 +316,11 @@ export default function AILearning({ traderId }: AILearningProps) {
}} />
-
- 💰
+
@@ -373,7 +382,7 @@ export default function AILearning({ traderId }: AILearningProps) {
boxShadow: '0 4px 16px rgba(16, 185, 129, 0.1)'
}}>
- 🏆
+
{t('bestPerformer', language)}
@@ -395,7 +404,7 @@ export default function AILearning({ traderId }: AILearningProps) {
boxShadow: '0 4px 16px rgba(248, 113, 113, 0.1)'
}}>
- 📉
+
{t('worstPerformer', language)}
@@ -428,7 +437,7 @@ export default function AILearning({ traderId }: AILearningProps) {
backdropFilter: 'blur(10px)'
}}>
- 📊 {t('symbolPerformance', language)}
+ {t('symbolPerformance', language)}
@@ -488,7 +497,7 @@ export default function AILearning({ traderId }: AILearningProps) {
backdropFilter: 'blur(10px)'
}}>
-
📜
+
{t('tradeHistory', language)}
@@ -631,7 +640,9 @@ export default function AILearning({ traderId }: AILearningProps) {
})
) : (
-
📜
+
+
+
{t('noCompletedTrades', language)}
)}
@@ -646,11 +657,11 @@ export default function AILearning({ traderId }: AILearningProps) {
boxShadow: '0 4px 16px rgba(240, 185, 11, 0.1)'
}}>
-
- 💡
+
{t('howAILearns', language)}
diff --git a/web/src/components/AITradersPage.tsx b/web/src/components/AITradersPage.tsx
index d76d45f7..25763608 100644
--- a/web/src/components/AITradersPage.tsx
+++ b/web/src/components/AITradersPage.tsx
@@ -6,6 +6,7 @@ import { useLanguage } from '../contexts/LanguageContext';
import { t } from '../i18n/translations';
import { getExchangeIcon } from './ExchangeIcons';
import { getModelIcon } from './ModelIcons';
+import { Bot, Brain, Landmark, BarChart3, Trash2, Plus, Users, AlertTriangle } from 'lucide-react';
// 获取友好的AI模型名称
function getModelDisplayName(modelId: string): string {
@@ -360,11 +361,11 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
{/* Header */}
-
- 🤖
+
@@ -385,38 +386,41 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
-
+
-
+
@@ -425,8 +429,9 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
{/* AI Models */}
-
- 🧠 {t('aiModels', language)}
+
+
+ {t('aiModels', language)}
{configuredModels.map(model => {
@@ -465,7 +470,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
})}
{configuredModels.length === 0 && (
)}
@@ -474,8 +479,9 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
{/* Exchanges */}
-
- 🏦 {t('exchanges', language)}
+
+
+ {t('exchanges', language)}
{configuredExchanges.map(exchange => {
@@ -506,7 +512,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
})}
{configuredExchanges.length === 0 && (
)}
@@ -518,23 +524,24 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
- 👥 {t('currentTraders', language)}
+
+ {t('currentTraders', language)}
{traders && traders.length > 0 ? (
{traders.map(trader => (
-
-
- 🤖
+
@@ -566,29 +573,30 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
-
+
-
+
@@ -597,7 +605,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
) : (
-
🤖
+
{t('noTraders', language)}
{t('createFirstTrader', language)}
{(configuredModels.length === 0 || configuredExchanges.length === 0) && (
@@ -855,11 +863,12 @@ function CreateTraderModal({
style={{ accentColor: '#F6465D' }}
/>
-
+
- ⚠️ 警告:勾选后将完全使用您的自定义策略,不再使用系统默认的风控和交易逻辑。
+ 警告:勾选后将完全使用您的自定义策略,不再使用系统默认的风控和交易逻辑。
这可能导致交易风险增加。仅在您完全理解交易逻辑时使用此选项。
@@ -950,7 +959,7 @@ function ModelConfigModal({
style={{ background: 'rgba(246, 70, 93, 0.1)', color: '#F6465D' }}
title="删除配置"
>
- 🗑️
+
)}
@@ -1122,7 +1131,7 @@ function ExchangeConfigModal({
style={{ background: 'rgba(246, 70, 93, 0.1)', color: '#F6465D' }}
title="删除配置"
>
- 🗑️
+
)}
diff --git a/web/src/components/ComparisonChart.tsx b/web/src/components/ComparisonChart.tsx
index b333c04f..b0dec3f0 100644
--- a/web/src/components/ComparisonChart.tsx
+++ b/web/src/components/ComparisonChart.tsx
@@ -14,6 +14,7 @@ import useSWR from 'swr';
import { api } from '../lib/api';
import type { CompetitionTraderData } from '../types';
import { getTraderColor } from '../utils/traderColors';
+import { BarChart3 } from 'lucide-react';
interface ComparisonChartProps {
traders: CompetitionTraderData[];
@@ -133,7 +134,9 @@ export function ComparisonChart({ traders }: ComparisonChartProps) {
if (combinedData.length === 0) {
return (
-
📊
+
+
+
暂无历史数据
运行几个周期后将显示对比曲线
diff --git a/web/src/components/CompetitionPage.tsx b/web/src/components/CompetitionPage.tsx
index 2a638b83..59e9be88 100644
--- a/web/src/components/CompetitionPage.tsx
+++ b/web/src/components/CompetitionPage.tsx
@@ -3,6 +3,7 @@ import { api } from '../lib/api';
import type { CompetitionData } from '../types';
import { ComparisonChart } from './ComparisonChart';
import { getTraderColor } from '../utils/traderColors';
+import { Trophy, Medal, Circle, CircleDot } from 'lucide-react';
export function CompetitionPage() {
const { data: competition } = useSWR
(
@@ -51,11 +52,11 @@ export function CompetitionPage() {
{/* Competition Header - 精简版 */}
-
- 🏆
+
@@ -121,8 +122,12 @@ export function CompetitionPage() {
{/* Rank & Name */}
-
- {index === 0 ? '🥇' : index === 1 ? '🥈' : '🥉'}
+
+
{trader.trader_name}
@@ -171,13 +176,17 @@ export function CompetitionPage() {
{/* Status */}
- {trader.is_running ? '●' : '○'}
+ {trader.is_running ? (
+
+ ) : (
+
+ )}
diff --git a/web/src/components/EquityChart.tsx b/web/src/components/EquityChart.tsx
index b8b846cf..bfa89617 100644
--- a/web/src/components/EquityChart.tsx
+++ b/web/src/components/EquityChart.tsx
@@ -13,6 +13,7 @@ import useSWR from 'swr';
import { api } from '../lib/api';
import { useLanguage } from '../contexts/LanguageContext';
import { t } from '../i18n/translations';
+import { AlertTriangle, BarChart3, DollarSign, Percent, TrendingUp as ArrowUp, TrendingDown as ArrowDown } from 'lucide-react'
interface EquityPoint {
timestamp: string;
@@ -52,16 +53,26 @@ export function EquityChart({ traderId }: EquityChartProps) {
if (error) {
return (
-
-
-
⚠️
+
+
+
-
{t('loadingError', language)}
-
{error.message}
+
+ {t('loadingError', language)}
+
+
+ {error.message}
+
- );
+ )
}
// 过滤掉无效数据:total_equity为0或小于1的数据点(API失败导致)
@@ -69,15 +80,21 @@ export function EquityChart({ traderId }: EquityChartProps) {
if (!validHistory || validHistory.length === 0) {
return (
-
-
{t('accountEquityCurve', language)}
-
-
📊
-
{t('noHistoricalData', language)}
-
{t('dataWillAppear', language)}
+
+
+ {t('accountEquityCurve', language)}
+
+
+
+
+
+
+ {t('noHistoricalData', language)}
+
+
{t('dataWillAppear', language)}
- );
+ )
}
// 限制显示最近的数据点(性能优化)
@@ -161,142 +178,238 @@ export function EquityChart({ traderId }: EquityChartProps) {
};
return (
-
+
{/* Header */}
-
-
-
{t('accountEquityCurve', language)}
-
-
+
+
+
+ {t('accountEquityCurve', language)}
+
+
+
{account?.total_equity.toFixed(2) || '0.00'}
- USDT
-
-
+ USDT
+
+
+
+
- {isProfit ? '▲' : '▼'} {isProfit ? '+' : ''}
+ {isProfit ? : }
+ {isProfit ? '+' : ''}
{currentValue.raw_pnl_pct}%
-
- ({isProfit ? '+' : ''}{currentValue.raw_pnl.toFixed(2)} USDT)
+
+ ({isProfit ? '+' : ''}
+ {currentValue.raw_pnl.toFixed(2)} USDT)
{/* Display Mode Toggle */}
-
+
{/* Chart */}
-
-
-
-
-
-
-
-
-
-
-
-
- displayMode === 'dollar' ? `$${value.toFixed(0)}` : `${value}%`
- }
- />
- } />
-
- 50 ? false : { fill: '#F0B90B', r: 3 }}
- activeDot={{ r: 6, fill: '#FCD535', stroke: '#F0B90B', strokeWidth: 2 }}
- connectNulls={true}
- />
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ displayMode === 'dollar' ? `$${value.toFixed(0)}` : `${value}%`
+ }
+ />
+ } />
+
+ 50 ? false : { fill: '#F0B90B', r: 3 }}
+ activeDot={{
+ r: 6,
+ fill: '#FCD535',
+ stroke: '#F0B90B',
+ strokeWidth: 2,
+ }}
+ connectNulls={true}
+ />
+
+
{/* Footer Stats */}
-
-
-
{t('initialBalance', language)}
-
+
+
+
+ {t('initialBalance', language)}
+
+
{initialBalance.toFixed(2)} USDT
-
-
{t('currentEquity', language)}
-
+
+
+ {t('currentEquity', language)}
+
+
{currentValue.raw_equity.toFixed(2)} USDT
-
-
{t('historicalCycles', language)}
-
{validHistory.length} {t('cycles', language)}
+
+
+ {t('historicalCycles', language)}
+
+
+ {validHistory.length} {t('cycles', language)}
+
-
-
{t('displayRange', language)}
-
+
+
+ {t('displayRange', language)}
+
+
{validHistory.length > MAX_DISPLAY_POINTS
? `${t('recent', language)} ${MAX_DISPLAY_POINTS}`
- : t('allData', language)
- }
+ : t('allData', language)}
- );
+ )
}
diff --git a/web/src/components/Header.tsx b/web/src/components/Header.tsx
index 3fb1c693..af141d85 100644
--- a/web/src/components/Header.tsx
+++ b/web/src/components/Header.tsx
@@ -14,9 +14,8 @@ export function Header({ simple = false }: HeaderProps) {
{/* Left - Logo and Title */}
-
- ⚡
+
+
diff --git a/web/src/components/LoginPage.tsx b/web/src/components/LoginPage.tsx
index bf4dd9e3..8923ebe2 100644
--- a/web/src/components/LoginPage.tsx
+++ b/web/src/components/LoginPage.tsx
@@ -3,6 +3,7 @@ import { useAuth } from '../contexts/AuthContext';
import { useLanguage } from '../contexts/LanguageContext';
import { t } from '../i18n/translations';
import { Header } from './Header';
+import { Smartphone } from 'lucide-react';
export function LoginPage() {
const { language } = useLanguage();
@@ -57,9 +58,8 @@ export function LoginPage() {
{/* Logo */}
-
- ⚡
+
+
{t('loginTitle', language)}
@@ -121,7 +121,9 @@ export function LoginPage() {
) : (