1
This commit is contained in:
parent
ce493b622f
commit
8e025adba9
@ -145,8 +145,81 @@
|
|||||||
color: var(--error);
|
color: var(--error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Real-time Prices */
|
||||||
|
.price-section {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
padding: 16px;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-item .symbol {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-item .price {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Core Metrics Grid */
|
||||||
|
.core-metrics-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.core-metric-card {
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
padding: 24px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.core-metric-label {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.core-metric-value {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.core-metric-value.positive {
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.core-metric-value.negative {
|
||||||
|
color: var(--error);
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.metrics-grid, .stat-grid, .grade-stats {
|
.metrics-grid, .stat-grid, .grade-stats, .core-metrics-grid, .price-list {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -273,6 +346,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Real-time Prices -->
|
||||||
|
<div v-if="Object.keys(latestPrices).length > 0" class="price-section">
|
||||||
|
<div class="stat-label" style="margin-bottom: 8px;">实时价格</div>
|
||||||
|
<div class="price-list">
|
||||||
|
<div class="price-item" v-for="(price, symbol) in latestPrices" :key="symbol">
|
||||||
|
<span class="symbol">{{ symbol }}</span>
|
||||||
|
<span class="price">${{ price.toLocaleString() }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Tabs -->
|
<!-- Tabs -->
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<button class="tab" :class="{ active: currentTab === 'positions' }" @click="switchTab('positions')">
|
<button class="tab" :class="{ active: currentTab === 'positions' }" @click="switchTab('positions')">
|
||||||
@ -284,6 +368,9 @@
|
|||||||
<button class="tab" :class="{ active: currentTab === 'history' }" @click="switchTab('history')">
|
<button class="tab" :class="{ active: currentTab === 'history' }" @click="switchTab('history')">
|
||||||
历史订单 ({{ orderHistory.length }})
|
历史订单 ({{ orderHistory.length }})
|
||||||
</button>
|
</button>
|
||||||
|
<button class="tab" :class="{ active: currentTab === 'stats' }" @click="switchTab('stats')">
|
||||||
|
详细统计
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Content -->
|
<!-- Content -->
|
||||||
@ -483,6 +570,94 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Detailed Statistics Tab -->
|
||||||
|
<div v-if="currentTab === 'stats'" class="table-container">
|
||||||
|
<!-- Core Metrics -->
|
||||||
|
<div class="core-metrics-grid">
|
||||||
|
<div class="core-metric-card">
|
||||||
|
<div class="core-metric-label">累计收益率</div>
|
||||||
|
<div class="core-metric-value" :class="stats.total_pnl_percent >= 0 ? 'positive' : 'negative'">
|
||||||
|
{{ stats.total_pnl_percent >= 0 ? '+' : '' }}{{ stats.total_pnl_percent ? stats.total_pnl_percent.toFixed(2) : '0.00' }}%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="core-metric-card">
|
||||||
|
<div class="core-metric-label">总盈亏</div>
|
||||||
|
<div class="core-metric-value" :class="stats.total_pnl >= 0 ? 'positive' : 'negative'">
|
||||||
|
{{ stats.total_pnl >= 0 ? '+' : '' }}${{ stats.total_pnl ? stats.total_pnl.toFixed(2) : '0.00' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="core-metric-card">
|
||||||
|
<div class="core-metric-label">胜率</div>
|
||||||
|
<div class="core-metric-value positive">
|
||||||
|
{{ stats.win_rate ? stats.win_rate.toFixed(1) : '0.0' }}%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="core-metric-card">
|
||||||
|
<div class="core-metric-label">盈亏比</div>
|
||||||
|
<div class="core-metric-value">
|
||||||
|
{{ stats.profit_factor === Infinity ? '∞' : (stats.profit_factor ? stats.profit_factor.toFixed(2) : '0.00') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Grade Stats -->
|
||||||
|
<div class="grade-stats" style="margin-top: 24px;">
|
||||||
|
<div class="grade-card">
|
||||||
|
<div class="grade-card-header">
|
||||||
|
<span class="grade-card-title">交易详情</span>
|
||||||
|
</div>
|
||||||
|
<div class="grade-card-stats">
|
||||||
|
<div class="grade-stat-row">
|
||||||
|
<span class="grade-stat-label">总交易数</span>
|
||||||
|
<span class="grade-stat-value">{{ stats.total_trades || 0 }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="grade-stat-row">
|
||||||
|
<span class="grade-stat-label">盈利交易</span>
|
||||||
|
<span class="grade-stat-value positive">{{ stats.winning_trades || 0 }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="grade-stat-row">
|
||||||
|
<span class="grade-stat-label">亏损交易</span>
|
||||||
|
<span class="grade-stat-value negative">{{ stats.losing_trades || 0 }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="grade-stat-row">
|
||||||
|
<span class="grade-stat-label">最佳交易</span>
|
||||||
|
<span class="grade-stat-value positive">{{ stats.best_trade ? stats.best_trade.toFixed(2) : '0.00' }}%</span>
|
||||||
|
</div>
|
||||||
|
<div class="grade-stat-row">
|
||||||
|
<span class="grade-stat-label">最差交易</span>
|
||||||
|
<span class="grade-stat-value negative">{{ stats.worst_trade ? stats.worst_trade.toFixed(2) : '0.00' }}%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grade-card">
|
||||||
|
<div class="grade-card-header">
|
||||||
|
<span class="grade-card-title">收益分析</span>
|
||||||
|
</div>
|
||||||
|
<div class="grade-card-stats">
|
||||||
|
<div class="grade-stat-row">
|
||||||
|
<span class="grade-stat-label">平均盈利</span>
|
||||||
|
<span class="grade-stat-value positive">${{ stats.average_win ? stats.average_win.toFixed(2) : '0.00' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="grade-stat-row">
|
||||||
|
<span class="grade-stat-label">平均亏损</span>
|
||||||
|
<span class="grade-stat-value negative">${{ stats.average_loss ? stats.average_loss.toFixed(2) : '0.00' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="grade-stat-row">
|
||||||
|
<span class="grade-stat-label">最大回撤</span>
|
||||||
|
<span class="grade-stat-value negative">{{ stats.max_drawdown ? stats.max_drawdown.toFixed(2) : '0.00' }}%</span>
|
||||||
|
</div>
|
||||||
|
<div class="grade-stat-row">
|
||||||
|
<span class="grade-stat-label">收益率</span>
|
||||||
|
<span class="grade-stat-value" :class="stats.return_percent >= 0 ? 'positive' : 'negative'">
|
||||||
|
{{ stats.return_percent >= 0 ? '+' : '' }}{{ stats.return_percent ? stats.return_percent.toFixed(2) : '0.00' }}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -534,7 +709,9 @@
|
|||||||
showAdminMenu: false,
|
showAdminMenu: false,
|
||||||
adminPassword: '223388',
|
adminPassword: '223388',
|
||||||
titleClickCount: 0,
|
titleClickCount: 0,
|
||||||
titleClickTimer: null
|
titleClickTimer: null,
|
||||||
|
refreshInterval: null,
|
||||||
|
latestPrices: {}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -571,6 +748,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async silentRefresh() {
|
||||||
|
try {
|
||||||
|
await Promise.all([
|
||||||
|
this.fetchAccountStatus(),
|
||||||
|
this.fetchStatistics(),
|
||||||
|
this.fetchOrders()
|
||||||
|
]);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('静默刷新失败:', e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
async fetchAccountStatus() {
|
async fetchAccountStatus() {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('/api/trading/account');
|
const response = await axios.get('/api/trading/account');
|
||||||
@ -599,6 +788,10 @@
|
|||||||
console.log('API Response:', response.data);
|
console.log('API Response:', response.data);
|
||||||
if (response.data.success) {
|
if (response.data.success) {
|
||||||
this.orders = response.data.orders || [];
|
this.orders = response.data.orders || [];
|
||||||
|
// 获取实时价格
|
||||||
|
if (response.data.latest_prices) {
|
||||||
|
this.latestPrices = response.data.latest_prices;
|
||||||
|
}
|
||||||
console.log('Orders loaded:', this.orders.length);
|
console.log('Orders loaded:', this.orders.length);
|
||||||
console.log('Orders data:', this.orders);
|
console.log('Orders data:', this.orders);
|
||||||
console.log('Open positions:', this.orders.filter(o => o.status === 'open'));
|
console.log('Open positions:', this.orders.filter(o => o.status === 'open'));
|
||||||
@ -768,12 +961,22 @@
|
|||||||
mounted() {
|
mounted() {
|
||||||
this.refreshData();
|
this.refreshData();
|
||||||
|
|
||||||
|
// 每3秒自动刷新(静默刷新,不显示 loading)
|
||||||
|
this.refreshInterval = setInterval(() => {
|
||||||
|
this.silentRefresh();
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
// 点击外部关闭管理菜单
|
// 点击外部关闭管理菜单
|
||||||
document.addEventListener('click', (e) => {
|
document.addEventListener('click', (e) => {
|
||||||
if (this.showAdminMenu && !e.target.closest('.admin-dropdown')) {
|
if (this.showAdminMenu && !e.target.closest('.admin-dropdown')) {
|
||||||
this.showAdminMenu = false;
|
this.showAdminMenu = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
if (this.refreshInterval) {
|
||||||
|
clearInterval(this.refreshInterval);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}).mount('#app');
|
}).mount('#app');
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user