Files
Alberto Balbo d25b4443c0 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.
2025-12-12 23:27:28 +01:00

342 lines
15 KiB
Plaintext

@page "/assets"
@using TradingBot.Models
@using TradingBot.Services
@inject TradingBotService BotService
@implements IDisposable
@rendermode InteractiveServer
<PageTitle>Asset - TradingBot</PageTitle>
<div class="assets-page">
<div class="page-header">
<div>
<h1>Gestione Asset</h1>
<p class="subtitle">Visualizza, configura e assegna strategie ai tuoi asset di trading</p>
</div>
<div class="header-controls">
<div class="view-toggle">
<button class="toggle-btn @(viewMode == "grid" ? "active" : "")" @onclick="@(() => viewMode = "grid")">
<span class="bi bi-grid-3x3-gap"></span>
</button>
<button class="toggle-btn @(viewMode == "list" ? "active" : "")" @onclick="@(() => viewMode = "list")">
<span class="bi bi-list-ul"></span>
</button>
</div>
<select class="filter-select" @bind="filterStatus">
<option value="all">Tutti gli Asset</option>
<option value="active">Solo Attivi</option>
<option value="inactive">Solo Inattivi</option>
</select>
</div>
</div>
<!-- Summary Stats -->
<div class="assets-summary">
<div class="summary-stat">
<div class="stat-icon">
<span class="bi bi-coin"></span>
</div>
<div class="stat-content">
<span class="stat-label">Totale Asset</span>
<span class="stat-value">@totalAssets</span>
</div>
</div>
<div class="summary-stat success">
<div class="stat-icon">
<span class="bi bi-check-circle"></span>
</div>
<div class="stat-content">
<span class="stat-label">Asset Attivi</span>
<span class="stat-value">@activeAssets</span>
</div>
</div>
<div class="summary-stat warning">
<div class="stat-icon">
<span class="bi bi-diagram-3"></span>
</div>
<div class="stat-content">
<span class="stat-label">Strategie Assegnate</span>
<span class="stat-value">@assignedStrategies</span>
</div>
</div>
<div class="summary-stat info">
<div class="stat-icon">
<span class="bi bi-currency-dollar"></span>
</div>
<div class="stat-content">
<span class="stat-label">Valore Totale</span>
<span class="stat-value">$@totalValue.ToString("N0")</span>
</div>
</div>
</div>
<!-- Assets Grid/List -->
@if (viewMode == "grid")
{
<div class="assets-grid">
@foreach (var config in GetFilteredAssets())
{
var price = BotService.GetLatestPrice(config.Symbol);
var stats = BotService.AssetStatistics.TryGetValue(config.Symbol, out var s) ? s : null;
<div class="asset-card @(config.IsEnabled ? "enabled" : "disabled")">
<div class="asset-card-header">
<div class="asset-info">
<div class="asset-icon">@config.Symbol.Substring(0, 1)</div>
<div class="asset-title">
<h3>@config.Name</h3>
<span class="asset-symbol">@config.Symbol</span>
</div>
</div>
<label class="toggle-switch">
<input type="checkbox"
checked="@config.IsEnabled"
@onchange="@((e) => ToggleAsset(config.Symbol, (bool)e.Value!))" />
<span class="toggle-slider"></span>
</label>
</div>
<div class="asset-card-body">
@if (price != null)
{
<div class="price-section">
<div class="current-price">$@price.Price.ToString("N2")</div>
<div class="price-change @(price.Change24h >= 0 ? "positive" : "negative")">
<span class="bi @(price.Change24h >= 0 ? "bi-arrow-up" : "bi-arrow-down")"></span>
@Math.Abs(price.Change24h).ToString("F2")% (24h)
</div>
</div>
}
<div class="asset-metrics">
<div class="metric">
<span class="metric-label">Holdings</span>
<span class="metric-value">@config.CurrentHoldings.ToString("F6")</span>
</div>
<div class="metric">
<span class="metric-label">Valore</span>
<span class="metric-value">$@((config.CurrentBalance + config.CurrentHoldings * (price?.Price ?? 0)).ToString("N2"))</span>
</div>
<div class="metric">
<span class="metric-label">Profitto</span>
<span class="metric-value @(config.TotalProfit >= 0 ? "profit" : "loss")">
$@config.TotalProfit.ToString("N2")
</span>
</div>
<div class="metric">
<span class="metric-label">Trades</span>
<span class="metric-value">@(stats?.TotalTrades ?? 0)</span>
</div>
</div>
<div class="strategy-section">
<label class="strategy-label">Strategia Assegnata</label>
<select class="strategy-select"
value="@config.StrategyName"
@onchange="@((e) => AssignStrategy(config.Symbol, e.Value?.ToString() ?? ""))">
<option value="">Nessuna strategia</option>
<option value="RSI + MACD Cross">RSI + MACD Cross</option>
<option value="Media Mobile Semplice">Media Mobile Semplice</option>
<option value="Scalping Veloce">Scalping Veloce</option>
<option value="Trend Following">Trend Following</option>
<option value="Mean Reversion">Mean Reversion</option>
<option value="Conservative">Conservative</option>
</select>
</div>
</div>
<div class="asset-card-footer">
<button class="btn-secondary btn-sm" @onclick="@(() => OpenAssetDetails(config.Symbol))">
<span class="bi bi-gear"></span>
Configura
</button>
<button class="btn-secondary btn-sm" @onclick="@(() => ViewChart(config.Symbol))">
<span class="bi bi-graph-up"></span>
Grafico
</button>
</div>
</div>
}
</div>
}
else
{
<!-- List View -->
<div class="assets-table">
<div class="table-header">
<div class="th">Asset</div>
<div class="th">Prezzo</div>
<div class="th">Var. 24h</div>
<div class="th">Holdings</div>
<div class="th">Valore</div>
<div class="th">Profitto</div>
<div class="th">Strategia</div>
<div class="th">Stato</div>
<div class="th">Azioni</div>
</div>
@foreach (var config in GetFilteredAssets())
{
var price = BotService.GetLatestPrice(config.Symbol);
var stats = BotService.AssetStatistics.TryGetValue(config.Symbol, out var s) ? s : null;
<div class="table-row @(config.IsEnabled ? "enabled" : "disabled")">
<div class="cell-asset">
<div class="asset-icon-small">@config.Symbol.Substring(0, 1)</div>
<div>
<div class="asset-name">@config.Name</div>
<div class="asset-symbol-small">@config.Symbol</div>
</div>
</div>
<div class="cell">
@if (price != null)
{
<span class="price-value">$@price.Price.ToString("N2")</span>
}
else
{
<span class="text-muted">-</span>
}
</div>
<div class="cell">
@if (price != null)
{
<span class="change-badge @(price.Change24h >= 0 ? "positive" : "negative")">
@(price.Change24h >= 0 ? "+" : "")@price.Change24h.ToString("F2")%
</span>
}
else
{
<span class="text-muted">-</span>
}
</div>
<div class="cell">
<span class="mono-value">@config.CurrentHoldings.ToString("F6")</span>
</div>
<div class="cell">
<span class="mono-value">$@((config.CurrentBalance + config.CurrentHoldings * (price?.Price ?? 0)).ToString("N2"))</span>
</div>
<div class="cell">
<span class="mono-value @(config.TotalProfit >= 0 ? "profit" : "loss")">
$@config.TotalProfit.ToString("N2")
</span>
</div>
<div class="cell-strategy">
<select class="strategy-select-small"
value="@config.StrategyName"
@onchange="@((e) => AssignStrategy(config.Symbol, e.Value?.ToString() ?? ""))">
<option value="">Nessuna</option>
<option value="RSI + MACD Cross">RSI + MACD</option>
<option value="Media Mobile Semplice">SMA</option>
<option value="Scalping Veloce">Scalping</option>
<option value="Trend Following">Trend</option>
<option value="Mean Reversion">Mean Rev.</option>
<option value="Conservative">Conserv.</option>
</select>
</div>
<div class="cell">
<label class="toggle-switch-small">
<input type="checkbox"
checked="@config.IsEnabled"
@onchange="@((e) => ToggleAsset(config.Symbol, (bool)e.Value!))" />
<span class="toggle-slider-small"></span>
</label>
</div>
<div class="cell-actions">
<button class="btn-icon-small" title="Configura" @onclick="@(() => OpenAssetDetails(config.Symbol))">
<span class="bi bi-gear"></span>
</button>
<button class="btn-icon-small" title="Grafico" @onclick="@(() => ViewChart(config.Symbol))">
<span class="bi bi-graph-up"></span>
</button>
</div>
</div>
}
</div>
}
</div>
@code {
private string viewMode = "grid";
private string filterStatus = "all";
private int totalAssets = 0;
private int activeAssets = 0;
private int assignedStrategies = 0;
private decimal totalValue = 0;
protected override void OnInitialized()
{
BotService.OnStatusChanged += HandleUpdate;
BotService.OnTradeExecuted += HandleTradeExecuted;
BotService.OnPriceUpdated += HandlePriceUpdate;
RefreshData();
}
private void RefreshData()
{
totalAssets = BotService.AssetConfigurations.Count;
activeAssets = BotService.AssetConfigurations.Values.Count(c => c.IsEnabled);
assignedStrategies = BotService.AssetConfigurations.Values.Count(c => !string.IsNullOrEmpty(c.StrategyName));
totalValue = BotService.AssetConfigurations.Values.Sum(c =>
{
var price = BotService.GetLatestPrice(c.Symbol);
return c.CurrentBalance + (c.CurrentHoldings * (price?.Price ?? 0));
});
StateHasChanged();
}
private IEnumerable<AssetConfiguration> GetFilteredAssets()
{
var assets = BotService.AssetConfigurations.Values.OrderBy(c => c.Symbol);
return filterStatus switch
{
"active" => assets.Where(c => c.IsEnabled),
"inactive" => assets.Where(c => !c.IsEnabled),
_ => assets
};
}
private void ToggleAsset(string symbol, bool enabled)
{
BotService.ToggleAsset(symbol, enabled);
RefreshData();
}
private void AssignStrategy(string symbol, string strategyName)
{
if (BotService.AssetConfigurations.TryGetValue(symbol, out var config))
{
config.StrategyName = strategyName;
RefreshData();
}
}
private void OpenAssetDetails(string symbol)
{
// TODO: Open modal or navigate to asset detail page
}
private void ViewChart(string symbol)
{
var navManager = Navigation;
navManager?.NavigateTo($"/market?symbol={symbol}");
}
private void HandleUpdate() => InvokeAsync(RefreshData);
private void HandleTradeExecuted(Trade trade) => InvokeAsync(RefreshData);
private void HandlePriceUpdate(string symbol, MarketPrice price) => InvokeAsync(RefreshData);
[Inject] private NavigationManager? Navigation { get; set; }
public void Dispose()
{
BotService.OnStatusChanged -= HandleUpdate;
BotService.OnTradeExecuted -= HandleTradeExecuted;
BotService.OnPriceUpdated -= HandlePriceUpdate;
}
}