Crypto Bot Statistics Dashboard Development

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Crypto Bot Statistics Dashboard Development
Medium
~3-5 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1214
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Crypto Bot Statistics Dashboard Development

A trading bot without statistics is like flying blind. A statistics dashboard transforms raw trade data into meaningful metrics: how profitable is the strategy, where does it lose money, what is the drawdown, is the equity curve stable enough. A good dashboard combines analytical metrics with real-time monitoring.

Key Trading Metrics

Performance Metrics

from decimal import Decimal
from typing import List
import statistics
import math

class PerformanceCalculator:
    def __init__(self, trades: list[ClosedTrade], initial_capital: Decimal):
        self.trades = sorted(trades, key=lambda t: t.closed_at)
        self.initial_capital = initial_capital

    def total_pnl(self) -> Decimal:
        return sum(t.pnl for t in self.trades)

    def total_roi(self) -> float:
        return float(self.total_pnl() / self.initial_capital * 100)

    def win_rate(self) -> float:
        if not self.trades:
            return 0
        wins = sum(1 for t in self.trades if t.pnl > 0)
        return wins / len(self.trades) * 100

    def profit_factor(self) -> float:
        gross_profit = sum(float(t.pnl) for t in self.trades if t.pnl > 0)
        gross_loss = abs(sum(float(t.pnl) for t in self.trades if t.pnl < 0))
        return gross_profit / gross_loss if gross_loss > 0 else float('inf')

    def max_drawdown(self) -> float:
        """Maximum drawdown from peak to trough of equity curve"""
        equity = float(self.initial_capital)
        peak = equity
        max_dd = 0

        for trade in self.trades:
            equity += float(trade.pnl)
            if equity > peak:
                peak = equity
            dd = (peak - equity) / peak
            max_dd = max(max_dd, dd)

        return max_dd * 100  # in percentage

    def sharpe_ratio(self, risk_free_rate: float = 0.05) -> float:
        """Annualized Sharpe Ratio"""
        if len(self.trades) < 2:
            return 0

        daily_returns = self.build_daily_returns()
        if not daily_returns:
            return 0

        avg_daily_return = statistics.mean(daily_returns)
        std_daily_return = statistics.stdev(daily_returns)

        if std_daily_return == 0:
            return 0

        daily_rf = risk_free_rate / 365
        sharpe = (avg_daily_return - daily_rf) / std_daily_return * math.sqrt(365)
        return round(sharpe, 2)

    def avg_trade_duration_hours(self) -> float:
        if not self.trades:
            return 0
        durations = [
            (t.closed_at - t.opened_at).total_seconds() / 3600
            for t in self.trades
        ]
        return statistics.mean(durations)

    def consecutive_losses(self) -> int:
        """Maximum streak of losing trades"""
        max_streak = 0
        current_streak = 0
        for trade in self.trades:
            if trade.pnl < 0:
                current_streak += 1
                max_streak = max(max_streak, current_streak)
            else:
                current_streak = 0
        return max_streak

Equity Curve

def build_equity_curve(self) -> list[dict]:
    equity = float(self.initial_capital)
    curve = [{'date': self.trades[0].opened_at, 'equity': equity}]

    for trade in self.trades:
        equity += float(trade.pnl)
        curve.append({
            'date': trade.closed_at,
            'equity': equity,
            'pnl': float(trade.pnl),
            'cumulative_roi': (equity / float(self.initial_capital) - 1) * 100
        })

    return curve

Backend API

from fastapi import FastAPI, Query
from datetime import datetime, timedelta

app = FastAPI()

@app.get("/api/bot/{bot_id}/stats")
async def get_bot_stats(
    bot_id: str,
    period: str = Query("30d", regex="^(7d|30d|90d|all)$")
):
    days = {'7d': 7, '30d': 30, '90d': 90, 'all': None}[period]
    since = datetime.utcnow() - timedelta(days=days) if days else None

    trades = await db.get_closed_trades(bot_id, since=since)
    initial_capital = await db.get_initial_capital(bot_id)
    open_positions = await db.get_open_positions(bot_id)

    calc = PerformanceCalculator(trades, initial_capital)

    return {
        "period": period,
        "summary": {
            "total_pnl_usdt": str(calc.total_pnl()),
            "total_roi_percent": round(calc.total_roi(), 2),
            "win_rate_percent": round(calc.win_rate(), 1),
            "profit_factor": round(calc.profit_factor(), 2),
            "sharpe_ratio": calc.sharpe_ratio(),
            "max_drawdown_percent": round(calc.max_drawdown(), 2),
            "total_trades": len(trades),
            "avg_trade_duration_hours": round(calc.avg_trade_duration_hours(), 1),
            "max_consecutive_losses": calc.consecutive_losses(),
        },
        "equity_curve": calc.build_equity_curve(),
        "open_positions": [p.to_dict() for p in open_positions],
        "current_status": await get_bot_status(bot_id)
    }

@app.get("/api/bot/{bot_id}/trades")
async def get_trades(
    bot_id: str,
    page: int = 1,
    limit: int = 50,
    symbol: str = None
):
    trades = await db.get_trades_paginated(bot_id, page, limit, symbol)
    return {
        "trades": [t.to_dict() for t in trades.items],
        "total": trades.total,
        "page": page,
        "pages": math.ceil(trades.total / limit)
    }

Frontend Components

Equity Curve Chart

import { LineChart, Line, XAxis, YAxis, Tooltip, ReferenceLine } from 'recharts';

const EquityCurveChart: React.FC<{data: EquityPoint[]}> = ({ data }) => {
  const initialEquity = data[0]?.equity || 0;

  return (
    <LineChart width={800} height={300} data={data}>
      <XAxis dataKey="date" tickFormatter={d => format(new Date(d), 'MM/dd')} />
      <YAxis tickFormatter={v => `$${(v/1000).toFixed(1)}k`} />
      <Tooltip
        formatter={(value: number) => [`$${value.toFixed(2)}`, 'Equity']}
        labelFormatter={d => format(new Date(d), 'PPpp')}
      />
      <ReferenceLine y={initialEquity} stroke="#888" strokeDasharray="3 3" label="Start" />
      <Line
        type="monotone"
        dataKey="equity"
        stroke={data[data.length-1]?.equity >= initialEquity ? '#22c55e' : '#ef4444'}
        dot={false}
        strokeWidth={2}
      />
    </LineChart>
  );
};

Summary KPI Cards

const StatCard: React.FC<{label: string; value: string; positive?: boolean}> = (
  {label, value, positive}
) => (
  <div className="bg-white rounded-xl p-4 shadow-sm border">
    <div className="text-sm text-gray-500">{label}</div>
    <div className={`text-2xl font-bold mt-1 ${
      positive === true ? 'text-green-600' :
      positive === false ? 'text-red-600' : 'text-gray-900'
    }`}>{value}</div>
  </div>
);

Real-time Monitoring

WebSocket updates of bot current state:

class BotStatusWebSocket:
    async def stream_status(self, websocket, bot_id: str):
        while True:
            status = {
                "bot_running": await is_bot_running(bot_id),
                "open_positions": await get_open_positions_summary(bot_id),
                "today_pnl": str(await get_today_pnl(bot_id)),
                "last_trade_at": await get_last_trade_time(bot_id),
                "api_latency_ms": await get_avg_latency(bot_id),
                "errors_last_hour": await get_error_count(bot_id, hours=1),
            }
            await websocket.send_json(status)
            await asyncio.sleep(5)  # update every 5 seconds

A dashboard with good analytics helps make decisions: continue the strategy or stop the bot, change parameters, add capital. Without numbers — it's trading blind.