Overview
Create a comprehensive trading dashboard that displays user trading statistics, leaderboards, and performance metrics using ZKScore trading data.Basic Trading Dashboard
Trading Statistics
Copy
import { ZKScoreClient } from '@zkscore/sdk';
const client = new ZKScoreClient({
apiKey: process.env.ZKSCORE_API_KEY
});
class TradingDashboard {
constructor() {
this.client = client;
}
async getTradingStats(userAddress) {
try {
const stats = await this.client.getTradingStats(userAddress);
return {
totalVolume: stats.totalVolume,
totalTrades: stats.totalTrades,
winRate: stats.winRate,
averageTradeSize: stats.averageTradeSize,
profitLoss: stats.profitLoss,
bestTrade: stats.bestTrade,
worstTrade: stats.worstTrade
};
} catch (error) {
console.error('Error getting trading stats:', error);
return null;
}
}
async getTradingHistory(userAddress, limit = 50) {
try {
const history = await this.client.getTradingHistory(userAddress, { limit });
return history.map(trade => ({
id: trade.id,
timestamp: new Date(trade.timestamp),
type: trade.type,
amount: trade.amount,
price: trade.price,
profitLoss: trade.profitLoss,
protocol: trade.protocol
}));
} catch (error) {
console.error('Error getting trading history:', error);
return [];
}
}
}
Trading Leaderboard
Copy
class TradingLeaderboard {
constructor(client) {
this.client = client;
}
async getLeaderboard(period = 'all', limit = 100) {
try {
const leaderboard = await this.client.getTradingLeaderboard({
period,
limit,
sortBy: 'volume'
});
return leaderboard.map((entry, index) => ({
rank: index + 1,
address: entry.address,
zksId: entry.zksId,
volume: entry.volume,
trades: entry.trades,
winRate: entry.winRate,
score: entry.score
}));
} catch (error) {
console.error('Error getting leaderboard:', error);
return [];
}
}
async getUserRank(userAddress) {
try {
const leaderboard = await this.getLeaderboard();
const userRank = leaderboard.findIndex(entry => entry.address === userAddress);
return userRank >= 0 ? userRank + 1 : null;
} catch (error) {
console.error('Error getting user rank:', error);
return null;
}
}
}
React Trading Dashboard
Main Dashboard Component
Copy
import React, { useState, useEffect } from 'react';
import { ZKScoreClient } from '@zkscore/sdk';
const TradingDashboard = ({ userAddress }) => {
const [stats, setStats] = useState(null);
const [history, setHistory] = useState([]);
const [leaderboard, setLeaderboard] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
loadDashboardData();
}, [userAddress]);
const loadDashboardData = async () => {
try {
setLoading(true);
const client = new ZKScoreClient({
apiKey: process.env.REACT_APP_ZKSCORE_API_KEY
});
const [statsData, historyData, leaderboardData] = await Promise.all([
client.getTradingStats(userAddress),
client.getTradingHistory(userAddress),
client.getTradingLeaderboard({ limit: 10 })
]);
setStats(statsData);
setHistory(historyData);
setLeaderboard(leaderboardData);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
if (loading) return <div>Loading trading dashboard...</div>;
if (error) return <div>Error: {error}</div>;
return (
<div className="trading-dashboard">
<h1>Trading Dashboard</h1>
<div className="dashboard-grid">
<TradingStats stats={stats} />
<TradingHistory history={history} />
<TradingLeaderboard leaderboard={leaderboard} />
</div>
</div>
);
};
Trading Stats Component
Copy
const TradingStats = ({ stats }) => {
if (!stats) return <div>No trading data available</div>;
return (
<div className="trading-stats">
<h2>Trading Statistics</h2>
<div className="stats-grid">
<StatCard
title="Total Volume"
value={`$${stats.totalVolume.toLocaleString()}`}
icon="💰"
/>
<StatCard
title="Total Trades"
value={stats.totalTrades.toLocaleString()}
icon="📊"
/>
<StatCard
title="Win Rate"
value={`${(stats.winRate * 100).toFixed(1)}%`}
icon="🎯"
/>
<StatCard
title="P&L"
value={`$${stats.profitLoss.toLocaleString()}`}
icon="📈"
positive={stats.profitLoss >= 0}
/>
</div>
</div>
);
};
const StatCard = ({ title, value, icon, positive }) => (
<div className={`stat-card ${positive !== undefined ? (positive ? 'positive' : 'negative') : ''}`}>
<div className="stat-icon">{icon}</div>
<div className="stat-content">
<h3>{title}</h3>
<p className="stat-value">{value}</p>
</div>
</div>
);
Trading History Component
Copy
const TradingHistory = ({ history }) => {
return (
<div className="trading-history">
<h2>Recent Trades</h2>
<div className="history-table">
<table>
<thead>
<tr>
<th>Time</th>
<th>Type</th>
<th>Amount</th>
<th>Price</th>
<th>P&L</th>
<th>Protocol</th>
</tr>
</thead>
<tbody>
{history.map(trade => (
<TradeRow key={trade.id} trade={trade} />
))}
</tbody>
</table>
</div>
</div>
);
};
const TradeRow = ({ trade }) => {
const formatTime = (timestamp) => {
return new Date(timestamp).toLocaleString();
};
const formatAmount = (amount) => {
return `$${amount.toLocaleString()}`;
};
const formatP&L = (profitLoss) => {
const isPositive = profitLoss >= 0;
return (
<span className={isPositive ? 'positive' : 'negative'}>
{isPositive ? '+' : ''}${profitLoss.toLocaleString()}
</span>
);
};
return (
<tr>
<td>{formatTime(trade.timestamp)}</td>
<td>
<span className={`trade-type ${trade.type.toLowerCase()}`}>
{trade.type}
</span>
</td>
<td>{formatAmount(trade.amount)}</td>
<td>${trade.price.toLocaleString()}</td>
<td>{formatP&L(trade.profitLoss)}</td>
<td>{trade.protocol}</td>
</tr>
);
};
Trading Leaderboard Component
Copy
const TradingLeaderboard = ({ leaderboard }) => {
return (
<div className="trading-leaderboard">
<h2>Top Traders</h2>
<div className="leaderboard-list">
{leaderboard.map((entry, index) => (
<LeaderboardEntry
key={entry.address}
entry={entry}
rank={index + 1}
/>
))}
</div>
</div>
);
};
const LeaderboardEntry = ({ entry, rank }) => {
const getRankIcon = (rank) => {
if (rank === 1) return '🥇';
if (rank === 2) return '🥈';
if (rank === 3) return '🥉';
return `#${rank}`;
};
return (
<div className="leaderboard-entry">
<div className="rank">{getRankIcon(rank)}</div>
<div className="user-info">
<div className="zks-id">{entry.zksId || entry.address}</div>
<div className="stats">
<span>Volume: ${entry.volume.toLocaleString()}</span>
<span>Trades: {entry.trades}</span>
<span>Win Rate: {(entry.winRate * 100).toFixed(1)}%</span>
</div>
</div>
<div className="score">{entry.score}</div>
</div>
);
};
Advanced Features
Performance Analytics
Copy
class TradingAnalytics {
constructor(client) {
this.client = client;
}
async getPerformanceMetrics(userAddress, period = '30d') {
try {
const history = await this.client.getTradingHistory(userAddress, {
period,
limit: 1000
});
const metrics = this.calculateMetrics(history);
return metrics;
} catch (error) {
console.error('Error calculating metrics:', error);
return null;
}
}
calculateMetrics(history) {
const totalTrades = history.length;
const winningTrades = history.filter(trade => trade.profitLoss > 0);
const losingTrades = history.filter(trade => trade.profitLoss < 0);
const totalP&L = history.reduce((sum, trade) => sum + trade.profitLoss, 0);
const winRate = totalTrades > 0 ? winningTrades.length / totalTrades : 0;
const averageWin = winningTrades.length > 0
? winningTrades.reduce((sum, trade) => sum + trade.profitLoss, 0) / winningTrades.length
: 0;
const averageLoss = losingTrades.length > 0
? losingTrades.reduce((sum, trade) => sum + trade.profitLoss, 0) / losingTrades.length
: 0;
return {
totalTrades,
winRate,
totalP&L,
averageWin,
averageLoss,
profitFactor: averageLoss !== 0 ? Math.abs(averageWin / averageLoss) : 0
};
}
}
Real-time Updates
Copy
class TradingUpdates {
constructor(client) {
this.client = client;
this.listeners = [];
}
subscribe(callback) {
this.listeners.push(callback);
}
async startRealTimeUpdates(userAddress) {
// Poll for updates every 30 seconds
setInterval(async () => {
try {
const latestStats = await this.client.getTradingStats(userAddress);
this.notifyListeners('stats', latestStats);
const latestHistory = await this.client.getTradingHistory(userAddress, { limit: 1 });
if (latestHistory.length > 0) {
this.notifyListeners('newTrade', latestHistory[0]);
}
} catch (error) {
console.error('Error updating trading data:', error);
}
}, 30000);
}
notifyListeners(type, data) {
this.listeners.forEach(callback => callback({ type, data }));
}
}
Best Practices
- Real-time Updates: Use polling or WebSocket connections for live data
- Performance: Implement pagination for large datasets
- Caching: Cache frequently accessed data
- Error Handling: Handle API failures gracefully
- User Experience: Show loading states and provide feedback