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à
This commit is contained in:
2026-01-06 17:49:07 +01:00
parent c229c50f1d
commit 64f3511695
18 changed files with 4266 additions and 41 deletions

View File

@@ -41,6 +41,22 @@
}
</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)
@@ -49,6 +65,14 @@
}
</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)
@@ -103,16 +127,51 @@
{
<div class="sidebar-summary">
<div class="summary-card">
<div class="summary-row">
<span class="summary-title">Portfolio</span>
<span class="summary-amount">$@portfolioValue.ToString("N0")</span>
<div class="summary-header">
<span class="summary-section-title">Portfolio</span>
</div>
<div class="summary-row">
<span class="summary-title">Profitto</span>
<span class="summary-amount @(totalProfit >= 0 ? "profit" : "loss")">
$@totalProfit.ToString("N2")
<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>
}
@@ -148,6 +207,11 @@
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()
{
@@ -155,6 +219,7 @@
sidebarCollapsed = settings.SidebarCollapsed;
BotService.OnStatusChanged += HandleUpdate;
BotService.OnTradeExecuted += HandleTradeExecuted;
BotService.OnPriceUpdated += HandlePriceUpdate;
SettingsService.OnSettingsChanged += HandleSettingsChanged;
@@ -184,34 +249,69 @@
private void UpdateStats()
{
portfolioValue = BotService.AssetConfigurations.Values.Sum(c =>
c.CurrentBalance + (c.CurrentHoldings * (BotService.GetLatestPrice(c.Symbol)?.Price ?? 0)));
totalProfit = BotService.AssetConfigurations.Values.Sum(c => c.TotalProfit);
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()
{
UpdateStats();
InvokeAsync(StateHasChanged);
}
private void HandlePriceUpdate(string symbol, MarketPrice price)
{
UpdateStats();
InvokeAsync(StateHasChanged);
}
private void HandleSettingsChanged()
{
var settings = SettingsService.GetSettings();
sidebarCollapsed = settings.SidebarCollapsed;
InvokeAsync(StateHasChanged);
}
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;
}