+
{/* Header */}
-
-
-
+
+
-
-
-
+
-
+
+
{t('aiTraders', language)}
-
{traders?.length || 0} {t('active', language)}
@@ -500,37 +500,37 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
+
+
-
+
{/* 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 }) || (
-
+
+ {getModelIcon(model.provider || model.id, { width: 28, height: 28 }) || (
+
@@ -587,63 +587,63 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
)}
-
- {getShortName(model.name)}
+
+ {getShortName(model.name)}
{inUse ? t('inUse', language) : model.enabled ? t('enabled', language) : t('configured', language)}
-
+
);
})}
{configuredModels.length === 0 && (
-
-
- {t('noModelsConfigured', language)}
+
+
+ {t('noModelsConfigured', language)}
)}
{/* Exchanges */}
-
-
-
+
+
+
{t('exchanges', language)}
-
+
{configuredExchanges.map(exchange => {
const inUse = isExchangeInUse(exchange.id);
return (
- handleExchangeClick(exchange.id)}
>
-
-
- {getExchangeIcon(exchange.id, { width: 32, height: 32 })}
+
+
+ {getExchangeIcon(exchange.id, { width: 28, height: 28 })}
-
- {getShortName(exchange.name)}
+
+ {getShortName(exchange.name)}
{exchange.type.toUpperCase()} • {inUse ? t('inUse', language) : exchange.enabled ? t('enabled', language) : t('configured', language)}
-
+
);
})}
{configuredExchanges.length === 0 && (
-
-
- {t('noExchangesConfigured', language)}
+
+
+ {t('noExchangesConfigured', language)}
)}
@@ -651,47 +651,47 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
{/* 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)}
-
@@ -700,20 +700,20 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
{/* Actions */}
-
+
-
+
@@ -746,15 +746,15 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
))}
) : (
-
-
- {t('noTraders', language)}
- {t('createFirstTrader', language)}
+
+
+ {t('noTraders', language)}
+ {t('createFirstTrader', language)}
{(configuredModels.length === 0 || configuredExchanges.length === 0) && (
-
- {configuredModels.length === 0 && configuredExchanges.length === 0
+
+ {configuredModels.length === 0 && configuredExchanges.length === 0
? t('configureModelsAndExchangesFirst', language)
- : configuredModels.length === 0
+ : configuredModels.length === 0
? t('configureModelsFirst', language)
: t('configureExchangesFirst', language)
}
diff --git a/web/src/components/ComparisonChart.tsx b/web/src/components/ComparisonChart.tsx
index 1050562e..56810e0e 100644
--- a/web/src/components/ComparisonChart.tsx
+++ b/web/src/components/ComparisonChart.tsx
@@ -316,24 +316,24 @@ export function ComparisonChart({ traders }: ComparisonChartProps) {
{/* Stats */}
-
-
+
+
{t('comparisonMode', language)}
- PnL %
+ PnL %
-
+
{t('dataPoints', language)}
- {t('count', language, {count: combinedData.length})}
+ {t('count', language, {count: combinedData.length})}
-
+
{t('currentGap', language)}
- 1 ? '#F0B90B' : '#EAECEF' }}>
+ 1 ? '#F0B90B' : '#EAECEF' }}>
{currentGap.toFixed(2)}%
-
+
{t('displayRange', language)}
-
+
{combinedData.length > MAX_DISPLAY_POINTS
? `${t('recent', language)} ${MAX_DISPLAY_POINTS}`
: t('allData', language)}
diff --git a/web/src/components/CompetitionPage.tsx b/web/src/components/CompetitionPage.tsx
index 2e3d90f6..b123a75b 100644
--- a/web/src/components/CompetitionPage.tsx
+++ b/web/src/components/CompetitionPage.tsx
@@ -75,13 +75,16 @@ export function CompetitionPage() {
return (
{/* Competition Header - 精简版 */}
-
-
-
-
+
+
+
+
-
+
{t('aiCompetition', language)}
{competition.count} {t('traders', language)}
@@ -92,9 +95,9 @@ export function CompetitionPage() {
-
+
{t('leader', language)}
- {leader?.trader_name}
+ {leader?.trader_name}
= 0 ? '#0ECB81' : '#F6465D' }}>
{(leader?.total_pnl ?? 0) >= 0 ? '+' : ''}{leader?.total_pnl_pct?.toFixed(2) || '0.00'}%
@@ -157,20 +160,20 @@ export function CompetitionPage() {
{/* Stats */}
-
+
{/* Total Equity */}
{t('equity', language)}
-
+
{trader.total_equity?.toFixed(2) || '0.00'}
{/* P&L */}
-
+
{t('pnl', language)}
= 0 ? '#0ECB81' : '#F6465D' }}
>
{(trader.total_pnl ?? 0) >= 0 ? '+' : ''}
@@ -184,7 +187,7 @@ export function CompetitionPage() {
{/* Positions */}
{t('pos', language)}
-
+
{trader.position_count}
@@ -244,15 +247,12 @@ export function CompetitionPage() {
>
{trader.trader_name}
-
- {trader.ai_model.toUpperCase()} + {trader.exchange.toUpperCase()}
-
- = 0 ? '#0ECB81' : '#F6465D' }}>
+ = 0 ? '#0ECB81' : '#F6465D' }}>
{(trader.total_pnl ?? 0) >= 0 ? '+' : ''}{trader.total_pnl_pct?.toFixed(2) || '0.00'}%
{isWinning && gap > 0 && (
+
{t('aiModels', language)}
-
+
{configuredModels.map(model => {
const inUse = isModelInUse(model.id);
return (
-
handleModelClick(model.id)}
>
-
-
- {getModelIcon(model.provider || model.id, { width: 32, height: 32 }) || (
-
{/* Exchanges */}
-
+
);
})}
{configuredModels.length === 0 && (
-
+ {getModelIcon(model.provider || model.id, { width: 28, height: 28 }) || (
+
-
@@ -587,63 +587,63 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
)}
-
-
+
{getShortName(model.name)}
+
+
{getShortName(model.name)}
{inUse ? t('inUse', language) : model.enabled ? t('enabled', language) : t('configured', language)}
-
-
{t('noModelsConfigured', language)}
+
+
+
)}
{t('noModelsConfigured', language)}
-
-
-
+
+
+
{t('exchanges', language)}
-
+
{configuredExchanges.map(exchange => {
const inUse = isExchangeInUse(exchange.id);
return (
- handleExchangeClick(exchange.id)}
>
-
-
- {getExchangeIcon(exchange.id, { width: 32, height: 32 })}
+
+
+ {getExchangeIcon(exchange.id, { width: 28, height: 28 })}
-
- {getShortName(exchange.name)}
+
+ {getShortName(exchange.name)}
{exchange.type.toUpperCase()} • {inUse ? t('inUse', language) : exchange.enabled ? t('enabled', language) : t('configured', language)}
-
+
);
})}
{configuredExchanges.length === 0 && (
-
-
- {t('noExchangesConfigured', language)}
+
+
+ {t('noExchangesConfigured', language)}
)}
@@ -651,47 +651,47 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
{/* 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)}
-
@@ -700,20 +700,20 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
{/* Actions */}
-
+
-
+
@@ -746,15 +746,15 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
))}
) : (
-
-
- {t('noTraders', language)}
- {t('createFirstTrader', language)}
+
+
+ {t('noTraders', language)}
+ {t('createFirstTrader', language)}
{(configuredModels.length === 0 || configuredExchanges.length === 0) && (
-
- {configuredModels.length === 0 && configuredExchanges.length === 0
+
+ {configuredModels.length === 0 && configuredExchanges.length === 0
? t('configureModelsAndExchangesFirst', language)
- : configuredModels.length === 0
+ : configuredModels.length === 0
? t('configureModelsFirst', language)
: t('configureExchangesFirst', language)
}
diff --git a/web/src/components/ComparisonChart.tsx b/web/src/components/ComparisonChart.tsx
index 1050562e..56810e0e 100644
--- a/web/src/components/ComparisonChart.tsx
+++ b/web/src/components/ComparisonChart.tsx
@@ -316,24 +316,24 @@ export function ComparisonChart({ traders }: ComparisonChartProps) {
{/* Stats */}
-
-
+
+
{t('comparisonMode', language)}
- PnL %
+ PnL %
-
+
{t('dataPoints', language)}
- {t('count', language, {count: combinedData.length})}
+ {t('count', language, {count: combinedData.length})}
-
+
{t('currentGap', language)}
- 1 ? '#F0B90B' : '#EAECEF' }}>
+ 1 ? '#F0B90B' : '#EAECEF' }}>
{currentGap.toFixed(2)}%
-
+
{t('displayRange', language)}
-
+
{combinedData.length > MAX_DISPLAY_POINTS
? `${t('recent', language)} ${MAX_DISPLAY_POINTS}`
: t('allData', language)}
diff --git a/web/src/components/CompetitionPage.tsx b/web/src/components/CompetitionPage.tsx
index 2e3d90f6..b123a75b 100644
--- a/web/src/components/CompetitionPage.tsx
+++ b/web/src/components/CompetitionPage.tsx
@@ -75,13 +75,16 @@ export function CompetitionPage() {
return (
{/* Competition Header - 精简版 */}
-
-
-
-
+
+
+
+
-
+
{t('aiCompetition', language)}
{competition.count} {t('traders', language)}
@@ -92,9 +95,9 @@ export function CompetitionPage() {
-
+
{t('leader', language)}
- {leader?.trader_name}
+ {leader?.trader_name}
= 0 ? '#0ECB81' : '#F6465D' }}>
{(leader?.total_pnl ?? 0) >= 0 ? '+' : ''}{leader?.total_pnl_pct?.toFixed(2) || '0.00'}%
@@ -157,20 +160,20 @@ export function CompetitionPage() {
{/* Stats */}
-
+
{/* Total Equity */}
{t('equity', language)}
-
+
{trader.total_equity?.toFixed(2) || '0.00'}
{/* P&L */}
-
+
{t('pnl', language)}
= 0 ? '#0ECB81' : '#F6465D' }}
>
{(trader.total_pnl ?? 0) >= 0 ? '+' : ''}
@@ -184,7 +187,7 @@ export function CompetitionPage() {
{/* Positions */}
{t('pos', language)}
-
+
{trader.position_count}
@@ -244,15 +247,12 @@ export function CompetitionPage() {
>
{trader.trader_name}
-
- {trader.ai_model.toUpperCase()} + {trader.exchange.toUpperCase()}
-
- = 0 ? '#0ECB81' : '#F6465D' }}>
+ = 0 ? '#0ECB81' : '#F6465D' }}>
{(trader.total_pnl ?? 0) >= 0 ? '+' : ''}{trader.total_pnl_pct?.toFixed(2) || '0.00'}%
{isWinning && gap > 0 && (
+
{t('exchanges', language)}
-
+
{configuredExchanges.map(exchange => {
const inUse = isExchangeInUse(exchange.id);
return (
-
handleExchangeClick(exchange.id)}
>
-
-
- {getExchangeIcon(exchange.id, { width: 32, height: 32 })}
+
{/* Traders List */}
-
+
);
})}
{configuredExchanges.length === 0 && (
-
+ {getExchangeIcon(exchange.id, { width: 28, height: 28 })}
-
-
-
+
{getShortName(exchange.name)}
+
+
{getShortName(exchange.name)}
{exchange.type.toUpperCase()} • {inUse ? t('inUse', language) : exchange.enabled ? t('enabled', language) : t('configured', language)}
-
-
@@ -651,47 +651,47 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
{t('noExchangesConfigured', language)}
+
+
+
)}
{t('noExchangesConfigured', language)}
-
-
-
-
+
+
+
+
{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)}
-
@@ -700,20 +700,20 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
{/* Actions */}
-
+
-
+
@@ -746,15 +746,15 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
))}
) : (
-
-
- {t('noTraders', language)}
- {t('createFirstTrader', language)}
+
+
+ {t('noTraders', language)}
+ {t('createFirstTrader', language)}
{(configuredModels.length === 0 || configuredExchanges.length === 0) && (
-
- {configuredModels.length === 0 && configuredExchanges.length === 0
+
+ {configuredModels.length === 0 && configuredExchanges.length === 0
? t('configureModelsAndExchangesFirst', language)
- : configuredModels.length === 0
+ : configuredModels.length === 0
? t('configureModelsFirst', language)
: t('configureExchangesFirst', language)
}
diff --git a/web/src/components/ComparisonChart.tsx b/web/src/components/ComparisonChart.tsx
index 1050562e..56810e0e 100644
--- a/web/src/components/ComparisonChart.tsx
+++ b/web/src/components/ComparisonChart.tsx
@@ -316,24 +316,24 @@ export function ComparisonChart({ traders }: ComparisonChartProps) {
{/* Stats */}
-
-
+
+
{t('comparisonMode', language)}
- PnL %
+ PnL %
-
+
{t('dataPoints', language)}
- {t('count', language, {count: combinedData.length})}
+ {t('count', language, {count: combinedData.length})}
-
+
{t('currentGap', language)}
- 1 ? '#F0B90B' : '#EAECEF' }}>
+ 1 ? '#F0B90B' : '#EAECEF' }}>
{currentGap.toFixed(2)}%
-
+
{t('displayRange', language)}
-
+
{combinedData.length > MAX_DISPLAY_POINTS
? `${t('recent', language)} ${MAX_DISPLAY_POINTS}`
: t('allData', language)}
diff --git a/web/src/components/CompetitionPage.tsx b/web/src/components/CompetitionPage.tsx
index 2e3d90f6..b123a75b 100644
--- a/web/src/components/CompetitionPage.tsx
+++ b/web/src/components/CompetitionPage.tsx
@@ -75,13 +75,16 @@ export function CompetitionPage() {
return (
{/* Competition Header - 精简版 */}
-
-
-
-
+
+
+
+
-
+
{t('aiCompetition', language)}
{competition.count} {t('traders', language)}
@@ -92,9 +95,9 @@ export function CompetitionPage() {
-
+
{t('leader', language)}
- {leader?.trader_name}
+ {leader?.trader_name}
= 0 ? '#0ECB81' : '#F6465D' }}>
{(leader?.total_pnl ?? 0) >= 0 ? '+' : ''}{leader?.total_pnl_pct?.toFixed(2) || '0.00'}%
@@ -157,20 +160,20 @@ export function CompetitionPage() {
{/* Stats */}
-
+
{/* Total Equity */}
{t('equity', language)}
-
+
{trader.total_equity?.toFixed(2) || '0.00'}
{/* P&L */}
-
+
{t('pnl', language)}
= 0 ? '#0ECB81' : '#F6465D' }}
>
{(trader.total_pnl ?? 0) >= 0 ? '+' : ''}
@@ -184,7 +187,7 @@ export function CompetitionPage() {
{/* Positions */}
{t('pos', language)}
-
+
{trader.position_count}
@@ -244,15 +247,12 @@ export function CompetitionPage() {
>
{trader.trader_name}
-
- {trader.ai_model.toUpperCase()} + {trader.exchange.toUpperCase()}
-
- = 0 ? '#0ECB81' : '#F6465D' }}>
+ = 0 ? '#0ECB81' : '#F6465D' }}>
{(trader.total_pnl ?? 0) >= 0 ? '+' : ''}{trader.total_pnl_pct?.toFixed(2) || '0.00'}%
{isWinning && gap > 0 && (
+
+
{traders && traders.length > 0 ? (
-
+
{t('currentTraders', language)}
+
{traders.map(trader => (
-
-
+
-
+
-
-
+
-
+
{trader.trader_name}
-
{getModelDisplayName(trader.ai_model.split('_').pop() || trader.ai_model)} Model • {trader.exchange_id?.toUpperCase()}
+
{/* Status */}
) : (
-
{t('status', language)}
-
@@ -700,20 +700,20 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
{/* Actions */}
-
+
@@ -746,15 +746,15 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
))}
-
+
-
-
{t('noTraders', language)}
- {t('createFirstTrader', language)}
+
+
+
{t('noTraders', language)}
+ {t('createFirstTrader', language)}
{(configuredModels.length === 0 || configuredExchanges.length === 0) && (
-
- {configuredModels.length === 0 && configuredExchanges.length === 0
+
+ {configuredModels.length === 0 && configuredExchanges.length === 0
? t('configureModelsAndExchangesFirst', language)
- : configuredModels.length === 0
+ : configuredModels.length === 0
? t('configureModelsFirst', language)
: t('configureExchangesFirst', language)
}
diff --git a/web/src/components/ComparisonChart.tsx b/web/src/components/ComparisonChart.tsx
index 1050562e..56810e0e 100644
--- a/web/src/components/ComparisonChart.tsx
+++ b/web/src/components/ComparisonChart.tsx
@@ -316,24 +316,24 @@ export function ComparisonChart({ traders }: ComparisonChartProps) {
{/* Stats */}
-
-
+
+
-
{t('comparisonMode', language)}
- PnL %
+ PnL %
+
-
{t('dataPoints', language)}
- {t('count', language, {count: combinedData.length})}
+ {t('count', language, {count: combinedData.length})}
+
{t('currentGap', language)}
- 1 ? '#F0B90B' : '#EAECEF' }}>
+
- 1 ? '#F0B90B' : '#EAECEF' }}>
{currentGap.toFixed(2)}%
+
{t('displayRange', language)}
-
+
{combinedData.length > MAX_DISPLAY_POINTS
? `${t('recent', language)} ${MAX_DISPLAY_POINTS}`
: t('allData', language)}
diff --git a/web/src/components/CompetitionPage.tsx b/web/src/components/CompetitionPage.tsx
index 2e3d90f6..b123a75b 100644
--- a/web/src/components/CompetitionPage.tsx
+++ b/web/src/components/CompetitionPage.tsx
@@ -75,13 +75,16 @@ export function CompetitionPage() {
return (
{/* Competition Header - 精简版 */}
-
-
-
-
+
+
+
-
+
-
+
+
{t('aiCompetition', language)}
{competition.count} {t('traders', language)}
@@ -92,9 +95,9 @@ export function CompetitionPage() {
+
{/* Stats */}
-
{t('leader', language)}
- {leader?.trader_name}
+ {leader?.trader_name}
= 0 ? '#0ECB81' : '#F6465D' }}>
{(leader?.total_pnl ?? 0) >= 0 ? '+' : ''}{leader?.total_pnl_pct?.toFixed(2) || '0.00'}%
@@ -157,20 +160,20 @@ export function CompetitionPage() {
+
{/* Total Equity */}
{t('equity', language)}
-
+
{/* P&L */}
-
{trader.total_equity?.toFixed(2) || '0.00'}
+
{t('pnl', language)}
= 0 ? '#0ECB81' : '#F6465D' }}
>
{(trader.total_pnl ?? 0) >= 0 ? '+' : ''}
@@ -184,7 +187,7 @@ export function CompetitionPage() {
{/* Positions */}
{t('pos', language)}
-
+
{trader.position_count}
@@ -244,15 +247,12 @@ export function CompetitionPage() {
>
{trader.trader_name}
-
- {trader.ai_model.toUpperCase()} + {trader.exchange.toUpperCase()}
-
- = 0 ? '#0ECB81' : '#F6465D' }}>
+
= 0 ? '#0ECB81' : '#F6465D' }}>
{(trader.total_pnl ?? 0) >= 0 ? '+' : ''}{trader.total_pnl_pct?.toFixed(2) || '0.00'}%
{isWinning && gap > 0 && (