feat: Add category performance, update WebUI and OpenRouter integration, fix bugs

This commit is contained in:
Richard
2026-07-05 14:13:16 +02:00
parent d102af2965
commit e9e9ce0d5a
30 changed files with 2302 additions and 54 deletions
+44 -12
View File
@@ -213,12 +213,12 @@ async function loadDashboard() {
const tbody = document.getElementById('topTradersBody');
tbody.innerHTML = data.topTraders.map((t, i) => `
<tr>
<td onclick="viewTrader(${t.id})" style="cursor:pointer">${i + 1}</td>
<td><strong>${t.platform === 'Polymarket' ? `<a href="https://polymarket.com/profile/${t.platformUserId}" target="_blank" style="color:var(--text);text-decoration:underline;">${t.displayName}</a>` : t.displayName}</strong></td>
<td onclick="viewTrader(${t.id})" style="cursor:pointer">${t.platform}</td>
<td onclick="viewTrader(${t.id})" style="cursor:pointer"><strong>${Number(t.combinedScore).toFixed(1)}</strong></td>
<td onclick="viewTrader(${t.id})" style="cursor:pointer">${fmt.pct(t.winRate)}</td>
<td onclick="viewTrader(${t.id})" style="cursor:pointer">${fmt.pnl(t.totalPnl)}</td>
<td onclick="viewTrader(${t.id})" style="cursor:pointer">${i + 1}</td>
<td><strong><a href="#" onclick="viewTrader(${t.id}); return false;" style="color:var(--primary);text-decoration:none;">${t.displayName}</a></strong></td>
<td onclick="viewTrader(${t.id})" style="cursor:pointer">${t.platform}</td>
<td onclick="viewTrader(${t.id})" style="cursor:pointer"><strong>${Number(t.combinedScore).toFixed(1)}</strong></td>
<td onclick="viewTrader(${t.id})" style="cursor:pointer">${fmt.pct(t.winRate)}</td>
<td onclick="viewTrader(${t.id})" style="cursor:pointer">${fmt.pnl(t.totalPnl)}</td>
<td onclick="viewTrader(${t.id})" style="cursor:pointer">${fmt.tier(t.tier)}</td>
<td onclick="viewTrader(${t.id})" style="cursor:pointer">${fmt.num(t.totalTrades)}</td>
</tr>
@@ -231,7 +231,7 @@ async function loadDashboard() {
<td>${fmt.time(t.executedAt)}</td>
<td onclick="viewTrader(${t.traderId})" style="cursor:pointer; color:var(--primary)">${t.traderName}</td>
<td onclick="${t.dbMarketId ? `viewMarket(${t.dbMarketId})` : `''`}" style="cursor:${t.dbMarketId ? 'pointer' : 'default'}" title="Market ID: ${t.marketId}">
${t.dbMarketId ? `<span style="color:var(--primary)">Market #${t.dbMarketId}</span>` : (t.marketId ? t.marketId.substring(0, 12) + '...' : '—')}
${t.marketName || (t.marketId.length > 20 ? t.marketId.substring(0,20)+'...' : t.marketId)}
</td>
<td>${fmt.side(t.side)}</td>
<td>${Number(t.price).toFixed(2)}</td>
@@ -287,7 +287,7 @@ async function loadTraders() {
tbody.innerHTML = data.map((t, i) => `
<tr>
<td>${i + 1}</td>
<td><strong>${t.platform === 'Polymarket' ? `<a href="https://polymarket.com/profile/${t.platformUserId}" target="_blank" style="color:var(--text);text-decoration:underline;">${t.displayName}</a>` : t.displayName}</strong></td>
<td><strong><a href="#" onclick="viewTrader(${t.id}); return false;" style="color:var(--primary);text-decoration:none;">${t.displayName}</a></strong></td>
<td>${t.platform}</td>
<td><strong>${Number(t.combinedScore).toFixed(1)}</strong></td>
<td>${fmt.pct(t.winRate)}</td>
@@ -351,7 +351,7 @@ async function viewTrader(id) {
document.getElementById('td-platformId').textContent = t.platformUserId;
document.getElementById('td-tier').innerHTML = fmt.tier(t.tier);
document.getElementById('td-strategy').textContent = t.strategy;
document.getElementById('td-winrate').textContent = fmt.pct(t.winRate);
document.getElementById('td-winrate').innerHTML = fmt.pct(t.winRate);
document.getElementById('td-pnl').innerHTML = fmt.pnl(t.totalPnl);
document.getElementById('td-trades').textContent = fmt.num(t.totalTrades);
document.getElementById('td-score').textContent = Number(t.combinedScore).toFixed(1);
@@ -363,13 +363,45 @@ async function viewTrader(id) {
const aiBtn = document.getElementById('btn-ai-analysis');
aiBtn.onclick = () => triggerAiAnalysis(id, true);
const wlBtn = document.getElementById('btn-toggle-watchlist');
wlBtn.textContent = t.isOnWatchlist ? '★ Watchlist (Remove)' : '☆ Watchlist (Add)';
wlBtn.onclick = async () => {
const method = t.isOnWatchlist ? 'DELETE' : 'POST';
await api(`/api/traders/${id}/watchlist`, { method });
viewTrader(id); // Reload to update UI
};
const openBtn = document.getElementById('btn-open-platform');
if (t.platform === 'Polymarket') {
openBtn.style.display = 'inline-block';
openBtn.textContent = 'Open on Polymarket';
openBtn.onclick = () => window.open(`https://polymarket.com/profile/${t.platformUserId}`, '_blank');
} else {
openBtn.style.display = 'none';
}
const catBody = document.getElementById('td-categoryBody');
if (t.categoryPerformances && t.categoryPerformances.length > 0) {
catBody.innerHTML = t.categoryPerformances.map(c => `
<tr>
<td>${c.category}</td>
<td>${fmt.pct(c.winRate)}</td>
<td>${fmt.pnl(c.totalPnL)}</td>
<td>${fmt.usd(c.totalVolume)}</td>
<td>${fmt.num(c.totalTrades)}</td>
</tr>
`).join('');
} else {
catBody.innerHTML = '<tr><td colspan="5" style="text-align:center; padding:16px; color:var(--text-muted)">No category data available</td></tr>';
}
const tbody = document.getElementById('td-tradesBody');
tbody.innerHTML = t.recentTrades.map(tr => `
<tr>
<td>${fmt.time(tr.executedAt)}</td>
<td onclick="${tr.dbMarketId ? `viewMarket(${tr.dbMarketId})` : `''`}" style="cursor:${tr.dbMarketId ? 'pointer' : 'default'}; color:${tr.dbMarketId ? 'var(--primary)' : 'inherit'}" title="Market ID: ${tr.marketId}">
${tr.dbMarketId ? `Market #${tr.dbMarketId}` : (tr.marketId ? tr.marketId.substring(0, 16) + '...' : '—')}
</td>
<td onclick="${tr.dbMarketId ? `viewMarket(${tr.dbMarketId})` : `''`}" style="cursor:${tr.dbMarketId ? 'pointer' : 'default'}; color:${tr.dbMarketId ? 'var(--primary)' : 'inherit'}" title="Market ID: ${tr.marketId}">
${tr.marketName || (tr.marketId.length > 20 ? tr.marketId.substring(0,20)+'...' : tr.marketId)}
</td>
<td>${fmt.side(tr.side)}</td>
<td>${Number(tr.price).toFixed(2)}</td>
<td>${fmt.num(tr.size)}</td>