Development of a Crypto Bot Dashboard with Real-Time Analytics
A trading bot is a black box until you have a dashboard. You only see the final PnL, but don't know how the strategy behaves intraday. A 35% drawdown can go unnoticed if you just look at win rate. A crypto bot statistics dashboard provides transparency: equity curve, drawdown, real Sharpe ratio. Without it, you risk losing your deposit. Our dashboards are built on real-time WebSocket and REST API, updating every 5 seconds. The result is instant reaction to market changes and timely stop of unprofitable strategies.
Problems the Dashboard Solves
The first problem is hidden drawdowns. A bot can show 70% win rate, but rare large losses eat all profit. Equity curve visualizes the real account state. Second, lack of context. Without risk metrics (Sharpe, drawdown) you cannot objectively compare two strategies. Third, monitoring delays. REST polling once a minute misses important signals: slippage or MEV attacks on DEXes. Real-time WebSocket solves this.
Why Equity Curve Is the Main Chart for a Crypto Bot?
An 80% win rate can hide rare large losses. Equity curve shows real capital dynamics. A good curve: steady growth with controlled drawdowns. A bad one: sharp 30-40% drops after which the bot never recovers. Equity curve reveals the true drawdown hidden in summary metrics. Additionally, we plot the curve accounting for slippage and fees — this gives an honest picture.
What Metrics Are Mandatory in the Dashboard?
We highlight five key ones:
- Profit factor — ratio of profitable to losing trades. A value >2 means $2 profit for every $1 risk.
- Sharpe ratio — risk-adjusted return. Higher means more stable strategy.
- Max drawdown — maximum drop from peak to trough. Assesses deposit risk.
- Average trade duration — mean holding time per position. Important for high-frequency strategies.
- Consecutive losses — streak of losing trades. Reveals periods of instability.
These metrics help you notice early when a bot starts degrading — e.g., increasing max drawdown or falling profit factor. Contact us for an engineer consultation to choose the optimal set for your strategy.
Implementation in Python with Decimal
For precise metric calculation we use Decimal. Below are key methods from our PerformanceCalculator class:
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:
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
def sharpe_ratio(self, risk_free_rate: float = 0.05) -> float:
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:
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
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 with FastAPI
FastAPI endpoints for statistics and paginated trade list.
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 with React
Components for equity curve chart and KPI cards.
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>
);
};
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 via WebSocket
Every 5 seconds we update the current bot 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)
Comparison of Data Retrieval Methods
| Characteristic | WebSocket | REST polling (every minute) |
|---|---|---|
| Data latency | 5 seconds | 60 seconds |
| Server load | Low (persistent connection) | High (frequent requests) |
| Implementation complexity | Medium | Low |
Our WebSocket dashboard updates 10 times faster than REST polling once per minute. This is critical for high-frequency strategies where every second affects PnL.
Dashboard Development Stages
| Stage | Duration | Result |
|---|---|---|
| Consultation and requirements gathering | 1-2 days | Technical specification |
| Interface prototyping | 3-5 days | Dashboard mockup |
| Backend (API, aggregation, WebSocket) | 5-10 days | Working endpoints |
| Frontend (charts, cards, filters) | 5-10 days | Dashboard interface |
| Integration and testing | 3-5 days | Acceptance, training |
After implementing the dashboard, the average client increases profit by 15% due to timely stopping of unprofitable strategies. We have over 5 years in blockchain and developed dashboards for 50+ trading bots. We guarantee metric correctness and 99.9% uptime.
Example calculation on real data
Consider a bot on Binance with a $10,000 deposit. In one month, 200 trades: 120 profitable (average profit $50) and 80 losing (average loss $30). Profit factor = (12050)/(8030) = 6000/2400 = 2.5. Max drawdown = -8% (peak $11,200, bottom $10,304). Sharpe ratio = 1.8. These metrics indicate a stable strategy.
What Is Included in the Work
- Analysis of your current data and metrics.
- API design (REST + WebSocket).
- Backend development in Python (FastAPI, PostgreSQL, Redis).
- Frontend in React (Recharts, Tailwind).
- Real-time monitoring integration.
- Documentation, team training, 30-day support.
Order a dashboard for your crypto bot — get a free engineer consultation. Contact us for a bot metric audit. Savings on commissions and timely problem detection pay for the implementation within the first month.







