- 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à
319 lines
12 KiB
Plaintext
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;
|
|
}
|
|
}
|