Aggiunta Bootstrap 5.3.3 (CSS, JS, RTL, mappe) al progetto

Sono stati aggiunti tutti i file principali di Bootstrap 5.3.3, inclusi CSS, JavaScript (bundle, ESM, UMD, minificati), versioni RTL, utility, reboot, griglia e relative mappe delle sorgenti. Questi file abilitano un sistema di design moderno, responsive e accessibile, con supporto per layout LTR e RTL, debugging avanzato tramite source map e tutte le funzionalità di Bootstrap per lo sviluppo dell’interfaccia utente. Nessuna modifica ai file esistenti.
This commit is contained in:
2025-12-12 23:27:28 +01:00
parent d50cb1f7b4
commit d25b4443c0
103 changed files with 69677 additions and 0 deletions

View File

@@ -0,0 +1,460 @@
@page "/statistics"
@using TradingBot.Models
@using TradingBot.Services
@inject TradingBotService BotService
@inject NavigationManager Navigation
@implements IDisposable
@rendermode InteractiveServer
<PageTitle>Statistiche - TradingBot</PageTitle>
<div class="statistics-page">
<!-- Header -->
<header class="stats-header">
<div class="header-content">
<div class="page-title">
<h1><span class="bi bi-graph-up"></span> Statistiche Avanzate</h1>
<p class="subtitle">Analisi dettagliata delle performance e metriche di trading</p>
</div>
<div class="header-filters">
<select class="filter-select" @bind="selectedSymbol" @bind:after="OnSymbolChanged">
<option value="">Tutti gli Asset</option>
@foreach (var symbol in BotService.AssetConfigurations.Keys.OrderBy(s => s))
{
<option value="@symbol">@symbol</option>
}
</select>
</div>
</div>
</header>
<div class="stats-content">
@if (string.IsNullOrEmpty(selectedSymbol))
{
<!-- Portfolio Overview -->
<div class="overview-section">
<h2 class="section-title">
<span class="bi bi-pie-chart-fill"></span> Panoramica Portfolio
</h2>
<div class="stats-grid">
<div class="stat-card primary">
<div class="stat-header">
<span class="stat-icon"><span class="bi bi-wallet2"></span></span>
<span class="stat-label">Valore Totale</span>
</div>
<div class="stat-value">$@portfolioStats.TotalBalance.ToString("N2")</div>
<div class="stat-footer">
<span class="stat-change @(portfolioStats.TotalProfitPercentage >= 0 ? "positive" : "negative")">
<span class="bi @(portfolioStats.TotalProfitPercentage >= 0 ? "bi-arrow-up" : "bi-arrow-down")"></span>
@Math.Abs(portfolioStats.TotalProfitPercentage).ToString("F2")%
</span>
</div>
</div>
<div class="stat-card">
<div class="stat-header">
<span class="stat-icon success"><span class="bi bi-trophy"></span></span>
<span class="stat-label">Profitto Netto</span>
</div>
<div class="stat-value @(portfolioStats.TotalProfit >= 0 ? "profit" : "loss")">
$@portfolioStats.TotalProfit.ToString("N2")
</div>
<div class="stat-footer">
<span class="stat-meta">Da $@portfolioStats.InitialBalance.ToString("N2")</span>
</div>
</div>
<div class="stat-card">
<div class="stat-header">
<span class="stat-icon info"><span class="bi bi-arrow-left-right"></span></span>
<span class="stat-label">Totale Operazioni</span>
</div>
<div class="stat-value">@portfolioStats.TotalTrades</div>
<div class="stat-footer">
<span class="stat-meta">@portfolioStats.ActiveAssets asset attivi</span>
</div>
</div>
<div class="stat-card">
<div class="stat-header">
<span class="stat-icon warning"><span class="bi bi-percent"></span></span>
<span class="stat-label">Win Rate</span>
</div>
<div class="stat-value">@portfolioStats.WinRate.ToString("F1")%</div>
<div class="stat-footer">
<span class="stat-meta">Tasso di successo</span>
</div>
</div>
</div>
<!-- Best/Worst Performers -->
<div class="performers-section">
<div class="performer-card best">
<div class="performer-header">
<span class="bi bi-trophy-fill"></span>
<span>Miglior Performer</span>
</div>
@if (!string.IsNullOrEmpty(portfolioStats.BestPerformingAssetSymbol))
{
<div class="performer-content">
<div class="performer-symbol">@portfolioStats.BestPerformingAssetSymbol</div>
<div class="performer-value profit">+$@portfolioStats.BestPerformingAssetProfit.ToString("N2")</div>
</div>
}
else
{
<div class="empty-performer">Nessun dato</div>
}
</div>
<div class="performer-card worst">
<div class="performer-header">
<span class="bi bi-graph-down"></span>
<span>Peggior Performer</span>
</div>
@if (!string.IsNullOrEmpty(portfolioStats.WorstPerformingAssetSymbol))
{
<div class="performer-content">
<div class="performer-symbol">@portfolioStats.WorstPerformingAssetSymbol</div>
<div class="performer-value @(portfolioStats.WorstPerformingAssetProfit >= 0 ? "profit" : "loss")">
$@portfolioStats.WorstPerformingAssetProfit.ToString("N2")
</div>
</div>
}
else
{
<div class="empty-performer">Nessun dato</div>
}
</div>
</div>
</div>
<!-- Asset Breakdown -->
<div class="breakdown-section">
<h2 class="section-title">
<span class="bi bi-list-columns-reverse"></span> Breakdown per Asset
</h2>
<div class="breakdown-table">
<div class="table-header">
<div class="th">Asset</div>
<div class="th">Valore</div>
<div class="th">Profitto</div>
<div class="th">% Profitto</div>
<div class="th">Trades</div>
<div class="th">Win Rate</div>
<div class="th">Azioni</div>
</div>
@foreach (var assetStat in portfolioStats.AssetStatistics.OrderByDescending(a => a.NetProfit))
{
var config = BotService.AssetConfigurations.TryGetValue(assetStat.Symbol, out var c) ? c : null;
if (config == null) continue;
var currentValue = config.CurrentBalance + (config.CurrentHoldings * assetStat.CurrentPrice);
<div class="table-row">
<div class="td asset-cell">
<span class="asset-symbol">@assetStat.Symbol</span>
<span class="asset-name">@assetStat.Name</span>
</div>
<div class="td">$@currentValue.ToString("N2")</div>
<div class="td @(assetStat.NetProfit >= 0 ? "profit" : "loss")">
$@assetStat.NetProfit.ToString("N2")
</div>
<div class="td @(config.ProfitPercentage >= 0 ? "profit" : "loss")">
@config.ProfitPercentage.ToString("F2")%
</div>
<div class="td">@assetStat.TotalTrades</div>
<div class="td">@assetStat.WinRate.ToString("F1")%</div>
<div class="td">
<button class="btn-details" @onclick="() => ViewAssetDetails(assetStat.Symbol)">
<span class="bi bi-eye"></span> Dettagli
</button>
</div>
</div>
}
</div>
</div>
}
else
{
<!-- Single Asset Statistics -->
var assetStats = BotService.AssetStatistics.TryGetValue(selectedSymbol, out var stats) ? stats : null;
var assetConfig = BotService.AssetConfigurations.TryGetValue(selectedSymbol, out var config) ? config : null;
@if (assetStats != null && assetConfig != null)
{
<div class="asset-details-section">
<div class="asset-details-header">
<div class="asset-title-section">
<h2>@assetStats.Name (@assetStats.Symbol)</h2>
<span class="status-badge @(assetConfig.IsEnabled ? "active" : "inactive")">
@(assetConfig.IsEnabled ? "Attivo" : "Inattivo")
</span>
</div>
<button class="btn-back" @onclick="ClearSelection">
<span class="bi bi-arrow-left"></span> Torna alla panoramica
</button>
</div>
<!-- Key Metrics -->
<div class="metrics-grid">
<div class="metric-card">
<div class="metric-icon"><span class="bi bi-cash-stack"></span></div>
<div class="metric-content">
<div class="metric-label">Prezzo Corrente</div>
<div class="metric-value">$@assetStats.CurrentPrice.ToString("N2")</div>
</div>
</div>
<div class="metric-card">
<div class="metric-icon success"><span class="bi bi-bar-chart-line"></span></div>
<div class="metric-content">
<div class="metric-label">Holdings</div>
<div class="metric-value">@assetConfig.CurrentHoldings.ToString("F6")</div>
</div>
</div>
<div class="metric-card">
<div class="metric-icon @(assetStats.NetProfit >= 0 ? "success" : "danger")">
<span class="bi bi-graph-up-arrow"></span>
</div>
<div class="metric-content">
<div class="metric-label">Profitto Netto</div>
<div class="metric-value @(assetStats.NetProfit >= 0 ? "profit" : "loss")">
$@assetStats.NetProfit.ToString("N2")
</div>
</div>
</div>
<div class="metric-card">
<div class="metric-icon info"><span class="bi bi-percent"></span></div>
<div class="metric-content">
<div class="metric-label">ROI</div>
<div class="metric-value @(assetConfig.ProfitPercentage >= 0 ? "profit" : "loss")">
@assetConfig.ProfitPercentage.ToString("F2")%
</div>
</div>
</div>
</div>
<!-- Trading Performance -->
<div class="performance-section">
<h3 class="subsection-title">Performance Trading</h3>
<div class="performance-grid">
<div class="performance-item">
<span class="perf-label">Totale Operazioni</span>
<span class="perf-value">@assetStats.TotalTrades</span>
</div>
<div class="performance-item">
<span class="perf-label">Operazioni Vincenti</span>
<span class="perf-value profit">@assetStats.WinningTrades</span>
</div>
<div class="performance-item">
<span class="perf-label">Operazioni Perdenti</span>
<span class="perf-value loss">@assetStats.LosingTrades</span>
</div>
<div class="performance-item">
<span class="perf-label">Win Rate</span>
<span class="perf-value">@assetStats.WinRate.ToString("F1")%</span>
</div>
<div class="performance-item">
<span class="perf-label">Profit Factor</span>
<span class="perf-value">@(assetStats.ProfitFactor > 1000 ? ">1000" : assetStats.ProfitFactor.ToString("F2"))</span>
</div>
<div class="performance-item">
<span class="perf-label">Vittorie Consecutive</span>
<span class="perf-value">@assetStats.MaxConsecutiveWins</span>
</div>
</div>
</div>
<!-- Profit/Loss Analysis -->
<div class="pnl-section">
<h3 class="subsection-title">Analisi Profitti/Perdite</h3>
<div class="pnl-grid">
<div class="pnl-card profit-card">
<div class="pnl-header">
<span class="bi bi-arrow-up-circle-fill"></span>
<span>Profitti</span>
</div>
<div class="pnl-amount profit">$@assetStats.TotalProfit.ToString("N2")</div>
<div class="pnl-meta">
Media per trade: $@assetStats.AverageProfit.ToString("N2")
</div>
<div class="pnl-meta">
Profitto massimo: $@assetStats.LargestWin.ToString("N2")
</div>
</div>
<div class="pnl-card loss-card">
<div class="pnl-header">
<span class="bi bi-arrow-down-circle-fill"></span>
<span>Perdite</span>
</div>
<div class="pnl-amount loss">$@assetStats.TotalLoss.ToString("N2")</div>
<div class="pnl-meta">
Media per trade: $@assetStats.AverageLoss.ToString("N2")
</div>
<div class="pnl-meta">
Perdita massima: $@assetStats.LargestLoss.ToString("N2")
</div>
</div>
@if (assetStats.UnrealizedPnL != 0)
{
<div class="pnl-card unrealized-card">
<div class="pnl-header">
<span class="bi bi-hourglass-split"></span>
<span>P/L Non Realizzato</span>
</div>
<div class="pnl-amount @(assetStats.UnrealizedPnL >= 0 ? "profit" : "loss")">
$@assetStats.UnrealizedPnL.ToString("N2")
</div>
<div class="pnl-meta">
@assetStats.UnrealizedPnLPercentage.ToString("F2")% sulla posizione corrente
</div>
</div>
}
</div>
</div>
<!-- Recent Trades -->
@if (assetStats.RecentTrades.Count > 0)
{
<div class="trades-section">
<h3 class="subsection-title">Operazioni Recenti</h3>
<div class="trades-list">
@foreach (var trade in assetStats.RecentTrades.Take(20))
{
<div class="trade-item @(trade.IsBot ? "bot-trade" : "")">
<div class="trade-icon @(trade.Type == TradeType.Buy ? "buy" : "sell")">
<span class="bi @(trade.Type == TradeType.Buy ? "bi-arrow-down-circle-fill" : "bi-arrow-up-circle-fill")"></span>
</div>
<div class="trade-details">
<div class="trade-type">
@(trade.Type == TradeType.Buy ? "ACQUISTO" : "VENDITA")
@if (trade.IsBot)
{
<span class="bot-label">
<span class="bi bi-robot"></span> BOT
</span>
}
</div>
<div class="trade-meta">
@trade.Timestamp.ToLocalTime().ToString("dd/MM/yyyy HH:mm:ss")
</div>
</div>
<div class="trade-amounts">
<div class="trade-quantity">@trade.Amount.ToString("F6") @trade.Symbol</div>
<div class="trade-price">&#64; $@trade.Price.ToString("N2")</div>
</div>
<div class="trade-value">
$@((trade.Amount * trade.Price).ToString("N2"))
</div>
</div>
}
</div>
</div>
}
else
{
<div class="empty-trades">
<span class="bi bi-inbox"></span>
<p>Nessuna operazione eseguita per questo asset</p>
</div>
}
</div>
}
else
{
<div class="empty-state">
<span class="bi bi-exclamation-circle"></span>
<p>Asset non trovato o dati non disponibili</p>
</div>
}
}
</div>
</div>
@code {
[SupplyParameterFromQuery(Name = "symbol")]
private string? QuerySymbol { get; set; }
private string selectedSymbol = "";
private PortfolioStatistics portfolioStats = new();
protected override void OnInitialized()
{
BotService.OnStatusChanged += HandleUpdate;
BotService.OnTradeExecuted += HandleTradeExecuted;
BotService.OnStatisticsUpdated += HandleUpdate;
BotService.OnPriceUpdated += HandlePriceUpdate;
if (!string.IsNullOrEmpty(QuerySymbol))
{
selectedSymbol = QuerySymbol;
}
RefreshData();
}
protected override void OnParametersSet()
{
if (!string.IsNullOrEmpty(QuerySymbol) && QuerySymbol != selectedSymbol)
{
selectedSymbol = QuerySymbol;
RefreshData();
}
}
private void RefreshData()
{
portfolioStats = BotService.GetPortfolioStatistics();
StateHasChanged();
}
private void OnSymbolChanged()
{
if (string.IsNullOrEmpty(selectedSymbol))
{
Navigation.NavigateTo("/statistics");
}
else
{
Navigation.NavigateTo($"/statistics?symbol={selectedSymbol}");
}
RefreshData();
}
private void ViewAssetDetails(string symbol)
{
selectedSymbol = symbol;
Navigation.NavigateTo($"/statistics?symbol={symbol}");
RefreshData();
}
private void ClearSelection()
{
selectedSymbol = "";
Navigation.NavigateTo("/statistics");
RefreshData();
}
private void HandleUpdate() => InvokeAsync(RefreshData);
private void HandleTradeExecuted(Trade trade) => InvokeAsync(RefreshData);
private void HandlePriceUpdate(string symbol, MarketPrice price) => InvokeAsync(RefreshData);
public void Dispose()
{
BotService.OnStatusChanged -= HandleUpdate;
BotService.OnTradeExecuted -= HandleTradeExecuted;
BotService.OnStatisticsUpdated -= HandleUpdate;
BotService.OnPriceUpdated -= HandlePriceUpdate;
}
}