Files
Encelado/TradingBot/Components/Layout/MainLayout.razor
Alberto Balbo 64f3511695 Nuove: multi-strategy, indicatori avanzati, posizioni
- Sidebar portfolio con metriche dettagliate (Totale, Investito, Disponibile, P&L, ROI) e aggiornamento real-time
- Sistema multi-strategia: 8 strategie assegnabili per asset, voting decisionale, pagina Trading Control
- Nuova pagina Posizioni: gestione, chiusura manuale, P&L non realizzato, notifiche
- Sistema indicatori tecnici: 7+ indicatori configurabili, segnali real-time, raccomandazioni, storico segnali
- Refactoring TradingBotService per capitale, P&L, ROI, eventi
- Nuovi modelli e servizi per strategie/indicatori, persistenza configurazioni
- UI/UX: navigazione aggiornata, widget, modali, responsive
- Aggiornamento README e CHANGELOG con tutte le novità
2026-01-06 17:49:07 +01:00

319 lines
12 KiB
Plaintext

@inherits LayoutComponentBase
@using TradingBot.Services
@using TradingBot.Models
@inject TradingBotService BotService
@inject SettingsService SettingsService
@inject NavigationManager Navigation
@implements IDisposable
<div class="trading-bot-layout @(sidebarCollapsed ? "collapsed" : "expanded")">
<!-- Modern Vertical Sidebar -->
<aside class="modern-sidebar">
<!-- Brand Section -->
<div class="sidebar-brand">
<div class="brand-container @(sidebarCollapsed ? "minimized" : "")">
<div class="brand-logo">
<span class="logo-icon bi bi-graph-up-arrow"></span>
</div>
@if (!sidebarCollapsed)
{
<div class="brand-info">
<h1 class="brand-title">Trading<span class="accent">Bot</span></h1>
<div class="status-badge @(isRunning ? "online" : "offline")">
<span class="status-indicator"></span>
<span class="status-text">@(isRunning ? "ATTIVO" : "OFFLINE")</span>
</div>
</div>
}
</div>
<button class="collapse-btn" @onclick="ToggleSidebar" title="@(sidebarCollapsed ? "Espandi" : "Minimizza")">
<span class="bi bi-chevron-@(sidebarCollapsed ? "right" : "left")"></span>
</button>
</div>
<!-- Navigation Menu -->
<nav class="sidebar-menu">
<NavLink class="menu-item" href="/" Match="NavLinkMatch.All" title="Dashboard">
<span class="item-icon bi bi-speedometer2"></span>
@if (!sidebarCollapsed)
{
<span class="item-text">Dashboard</span>
}
</NavLink>
<NavLink class="menu-item" href="/trading-control" title="Trading Control">
<span class="item-icon bi bi-sliders"></span>
@if (!sidebarCollapsed)
{
<span class="item-text">Trading Control</span>
}
</NavLink>
<NavLink class="menu-item" href="/positions" title="Posizioni">
<span class="item-icon bi bi-wallet2"></span>
@if (!sidebarCollapsed)
{
<span class="item-text">Posizioni</span>
}
</NavLink>
<NavLink class="menu-item" href="/strategies" title="Strategie">
<span class="item-icon bi bi-diagram-3"></span>
@if (!sidebarCollapsed)
{
<span class="item-text">Strategie</span>
}
</NavLink>
<NavLink class="menu-item" href="/indicators" title="Indicatori">
<span class="item-icon bi bi-graph-down-arrow"></span>
@if (!sidebarCollapsed)
{
<span class="item-text">Indicatori</span>
}
</NavLink>
<NavLink class="menu-item" href="/assets" title="Asset">
<span class="item-icon bi bi-coin"></span>
@if (!sidebarCollapsed)
{
<span class="item-text">Asset</span>
}
</NavLink>
<NavLink class="menu-item" href="/trading" title="Trading">
<span class="item-icon bi bi-graph-up-arrow"></span>
@if (!sidebarCollapsed)
{
<span class="item-text">Trading</span>
}
</NavLink>
<NavLink class="menu-item" href="/market" title="Analisi Mercato">
<span class="item-icon bi bi-bar-chart-line"></span>
@if (!sidebarCollapsed)
{
<span class="item-text">Analisi Mercato</span>
}
</NavLink>
<NavLink class="menu-item" href="/statistics" title="Statistiche">
<span class="item-icon bi bi-graph-up"></span>
@if (!sidebarCollapsed)
{
<span class="item-text">Statistiche</span>
}
</NavLink>
<NavLink class="menu-item" href="/logs" title="Logs">
<span class="item-icon bi bi-terminal"></span>
@if (!sidebarCollapsed)
{
<span class="item-text">Logs</span>
}
</NavLink>
<NavLink class="menu-item" href="/settings" title="Impostazioni">
<span class="item-icon bi bi-gear"></span>
@if (!sidebarCollapsed)
{
<span class="item-text">Impostazioni</span>
}
</NavLink>
</nav>
<!-- Portfolio Summary (quando espanso) -->
@if (!sidebarCollapsed)
{
<div class="sidebar-summary">
<div class="summary-card">
<div class="summary-header">
<span class="summary-section-title">Portfolio</span>
</div>
<div class="summary-row">
<span class="summary-title">Capitale Totale</span>
<span class="summary-amount">$@totalCapital.ToString("N2")</span>
</div>
<div class="summary-row">
<span class="summary-title">Investito</span>
<span class="summary-amount invested">$@investedCapital.ToString("N2")</span>
</div>
<div class="summary-row">
<span class="summary-title">Disponibile</span>
<span class="summary-amount available">$@availableCapital.ToString("N2")</span>
</div>
<div class="summary-divider"></div>
<div class="summary-row highlight">
<span class="summary-title">P&L Corrente</span>
<span class="summary-amount @(currentPL >= 0 ? "profit" : "loss")">
@(currentPL >= 0 ? "+" : "")$@currentPL.ToString("N2")
</span>
</div>
<div class="summary-row">
<span class="summary-title">ROI</span>
<span class="summary-amount @(roiPercentage >= 0 ? "profit" : "loss")">
@(roiPercentage >= 0 ? "+" : "")@roiPercentage.ToString("F2")%
</span>
</div>
</div>
</div>
<!-- Version Footer -->
<div class="sidebar-footer">
<div class="version-info">
<span class="version-label">TradingBot</span>
<span class="version-number">v1.5.2</span>
</div>
<div class="build-info">
<span class="build-date">Build: @DateTime.Now.ToString("yyyy-MM-dd")</span>
</div>
</div>
}
</aside>
<!-- Main Content Area -->
<div class="main-area">
<!-- Top Header Bar -->
<header class="content-header">
<div class="header-left">
<!-- Placeholder for page title -->
</div>
<div class="header-right">
<button class="header-btn notifications" title="Notifiche">
<span class="bi bi-bell"></span>
</button>
<button class="header-btn bot-control @(isRunning ? "running" : "stopped")" @onclick="ToggleBot">
<span class="bi bi-@(isRunning ? "pause" : "play")-circle-fill"></span>
<span class="btn-label">@(isRunning ? "Stop" : "Avvia")</span>
</button>
</div>
</header>
<!-- Page Content -->
<main class="page-content">
@Body
</main>
</div>
</div>
@code {
private bool sidebarCollapsed = false;
private bool isRunning => BotService.Status.IsRunning;
private decimal portfolioValue = 0;
private decimal totalProfit = 0;
private decimal totalCapital = 0;
private decimal investedCapital = 0;
private decimal availableCapital = 0;
private decimal currentPL = 0;
private decimal roiPercentage = 0;
protected override void OnInitialized()
{
var settings = SettingsService.GetSettings();
sidebarCollapsed = settings.SidebarCollapsed;
BotService.OnStatusChanged += HandleUpdate;
BotService.OnTradeExecuted += HandleTradeExecuted;
BotService.OnPriceUpdated += HandlePriceUpdate;
SettingsService.OnSettingsChanged += HandleSettingsChanged;
UpdateStats();
if (settings.AutoStartBot && !BotService.Status.IsRunning)
{
BotService.Start();
}
}
private void ToggleSidebar()
{
sidebarCollapsed = !sidebarCollapsed;
SettingsService.UpdateSetting(nameof(AppSettings.SidebarCollapsed), sidebarCollapsed);
StateHasChanged(); // Force immediate UI update
Console.WriteLine($"Sidebar toggled: collapsed={sidebarCollapsed}"); // Debug log
}
private void ToggleBot()
{
if (isRunning)
BotService.Stop();
else
BotService.Start();
}
private void UpdateStats()
{
totalCapital = 0;
investedCapital = 0;
availableCapital = 0;
currentPL = 0;
totalProfit = 0;
foreach (var config in BotService.AssetConfigurations.Values)
{
if (!config.IsEnabled) continue;
// Capitale disponibile (cash)
availableCapital += config.CurrentBalance;
// Capitale investito in posizioni aperte
var currentPrice = BotService.GetLatestPrice(config.Symbol)?.Price ?? 0;
var positionValue = config.CurrentHoldings * currentPrice;
investedCapital += positionValue;
// P&L non realizzato sulle posizioni aperte
if (config.CurrentHoldings > 0 && config.AverageEntryPrice > 0)
{
var entryValue = config.CurrentHoldings * config.AverageEntryPrice;
currentPL += (positionValue - entryValue);
}
// Totale profitti/perdite realizzati
totalProfit += config.TotalProfit;
}
// Capitale totale = Disponibile + Investito
totalCapital = availableCapital + investedCapital;
// Portfolio value include anche i profitti realizzati
portfolioValue = totalCapital + totalProfit;
// ROI basato sul capitale iniziale
var initialCapital = BotService.AssetConfigurations.Values
.Where(c => c.IsEnabled)
.Sum(c => c.InitialBalance);
if (initialCapital > 0)
{
var totalGain = currentPL + totalProfit;
roiPercentage = (totalGain / initialCapital) * 100;
}
else
{
roiPercentage = 0;
}
}
private void HandleUpdate() => InvokeAsync(() => { UpdateStats(); StateHasChanged(); });
private void HandleTradeExecuted(Trade trade) => InvokeAsync(() => { UpdateStats(); StateHasChanged(); });
private void HandlePriceUpdate(string symbol, MarketPrice price) => InvokeAsync(() => { UpdateStats(); StateHasChanged(); });
private void HandleSettingsChanged() => InvokeAsync(StateHasChanged);
public void Dispose()
{
BotService.OnStatusChanged -= HandleUpdate;
BotService.OnTradeExecuted -= HandleTradeExecuted;
BotService.OnPriceUpdated -= HandlePriceUpdate;
SettingsService.OnSettingsChanged -= HandleSettingsChanged;
}
}