feat: Implement copytrading analytics and update WebUI
- Add v4 Tape-based CopytradingEstimator (Clustering, MAE, Tape Fills) - Add CopytradingBacktestHarness (Walk-Forward testing, Shrinkage, LCB ranking) - Add EF Core Migrations for FollowerFillPrice and Scoring fields - Update WebUI: Fix Trader Detail tab layout blowout - Update WebUI: Redesign Trader Detail menubar - Update WebUI: Display separate Quality and Copyability metric cards - Update WebUI: Add 'Highly Copyable' filter to Trader List
This commit is contained in:
@@ -275,9 +275,15 @@ async function loadDashboard() {
|
||||
async function loadTraders() {
|
||||
let url = '/api/traders?skip=0&take=100';
|
||||
if (currentPlatform !== 'All') url += `&platform=${currentPlatform}`;
|
||||
|
||||
const hcCheckbox = document.getElementById('chk-highly-copyable');
|
||||
if (hcCheckbox && hcCheckbox.checked) {
|
||||
url += `&highlyCopyable=true`;
|
||||
}
|
||||
|
||||
let data = await api(url);
|
||||
const tbody = document.getElementById('allTradersBody');
|
||||
if (!data || !data.length) { tbody.innerHTML = '<tr><td colspan="9"><div class="empty-state"><p>No traders tracked yet.</p></div></td></tr>'; return; }
|
||||
if (!data || !data.length) { tbody.innerHTML = '<tr><td colspan="11"><div class="empty-state"><p>No traders tracked yet.</p></div></td></tr>'; return; }
|
||||
|
||||
// Sorting
|
||||
if (currentSort === 'score') data.sort((a, b) => b.combinedScore - a.combinedScore);
|
||||
@@ -287,9 +293,11 @@ async function loadTraders() {
|
||||
tbody.innerHTML = data.map((t, i) => `
|
||||
<tr>
|
||||
<td>${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><strong><a href="#" onclick="viewTrader(${t.id}); return false;" style="color:var(--primary);text-decoration:none;">${t.displayName}</a></strong>${t.isSuspectedBot ? ' 🤖' : ''}</td>
|
||||
<td>${t.platform}</td>
|
||||
<td><strong>${Number(t.combinedScore).toFixed(1)}</strong></td>
|
||||
<td>${Number(t.copytradingQualityScore || 0).toFixed(1)}</td>
|
||||
<td>${Number(t.copytradingCopyabilityScore || 0).toFixed(1)}</td>
|
||||
<td>${fmt.pct(t.winRate)}</td>
|
||||
<td>${fmt.pnl(t.totalPnl)}</td>
|
||||
<td>${fmt.tier(t.tier)}</td>
|
||||
@@ -358,6 +366,8 @@ async function viewTrader(id) {
|
||||
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);
|
||||
document.getElementById('td-quality-score').textContent = Number(t.copytradingQualityScore || 0).toFixed(1);
|
||||
document.getElementById('td-copyability-score').textContent = Number(t.copytradingCopyabilityScore || 0).toFixed(1);
|
||||
document.getElementById('td-ai-summary').textContent = t.aiStrategySummary || 'Not analyzed yet.';
|
||||
|
||||
const refreshBtn = document.getElementById('btn-refresh-trader');
|
||||
|
||||
Reference in New Issue
Block a user