Skip to main content

Overview

Create a comprehensive trading dashboard that displays user trading statistics, leaderboards, and performance metrics using ZKScore trading data.

Basic Trading Dashboard

Trading Statistics

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

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

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

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

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

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

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

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

  1. Real-time Updates: Use polling or WebSocket connections for live data
  2. Performance: Implement pagination for large datasets
  3. Caching: Cache frequently accessed data
  4. Error Handling: Handle API failures gracefully
  5. User Experience: Show loading states and provide feedback