Files
Encelado/DesktopBot/ViewModels/WalletViewModel.cs
T
2026-06-09 18:29:41 +02:00

147 lines
4.6 KiB
C#

using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
using DesktopBot.Services;
namespace DesktopBot.ViewModels
{
/// <summary>
/// ViewModel per il Wallet e le posizioni aperte
/// </summary>
public class WalletViewModel : BaseViewModel
{
private readonly ITradingService _tradingService;
private decimal _equity;
private bool _isLoading;
private string _errorMessage;
private bool _hasError;
private ObservableCollection<PositionViewModel> _positions = new ObservableCollection<PositionViewModel>();
public decimal Equity
{
get => _equity;
set => SetProperty(ref _equity, value);
}
public bool IsLoading
{
get => _isLoading;
set => SetProperty(ref _isLoading, value);
}
public string ErrorMessage
{
get => _errorMessage;
set => SetProperty(ref _errorMessage, value);
}
public bool HasError
{
get => _hasError;
set => SetProperty(ref _hasError, value);
}
public ObservableCollection<PositionViewModel> Positions
{
get => _positions;
set => SetProperty(ref _positions, value);
}
public ICommand RefreshCommand { get; }
public ICommand CloseAllCommand { get; }
public WalletViewModel(ITradingService tradingService)
{
_tradingService = tradingService;
RefreshCommand = new RelayCommand(async _ => await RefreshAsync());
CloseAllCommand = new RelayCommand(async _ => await CloseAllPositionsAsync());
}
private async System.Threading.Tasks.Task RefreshAsync()
{
await LoadAsync();
}
/// <summary>
/// Carica equity e posizioni aperte da Alpaca
/// </summary>
public async System.Threading.Tasks.Task LoadAsync()
{
IsLoading = true;
HasError = false;
try
{
var accountTask = _tradingService.GetAccountAsync();
var positionsTask = _tradingService.GetAllPositionsAsync();
await System.Threading.Tasks.Task.WhenAll(accountTask, positionsTask);
var account = accountTask.Result;
var positions = positionsTask.Result;
Application.Current?.Dispatcher.Invoke(() =>
{
Equity = account.Equity ?? 0m;
Positions.Clear();
foreach (var pos in positions)
Positions.Add(new PositionViewModel(pos));
});
}
catch (Exception ex)
{
Application.Current?.Dispatcher.Invoke(() =>
{
HasError = true;
ErrorMessage = "Errore: " + ex.Message;
});
}
finally
{
Application.Current?.Dispatcher.Invoke(() => IsLoading = false);
}
}
private async System.Threading.Tasks.Task CloseAllPositionsAsync()
{
try
{
await _tradingService.CloseAllPositionsAsync();
await RefreshAsync();
}
catch { /* Gestito silenziosamente */ }
}
}
/// <summary>
/// ViewModel per una singola posizione aperta
/// </summary>
public class PositionViewModel : BaseViewModel
{
public string Symbol { get; }
public decimal Quantity { get; }
public string Side { get; }
public decimal EntryPrice { get; }
public decimal CurrentPrice { get; }
public decimal MarketValue { get; }
public decimal UnrealizedPnL { get; }
public decimal UnrealizedPnLPercent { get; }
public bool IsProfit { get; }
public PositionViewModel(Alpaca.Markets.IPosition position)
{
Symbol = position.Symbol;
Quantity = Math.Abs(position.Quantity);
Side = position.Quantity >= 0 ? "LONG" : "SHORT";
EntryPrice = position.AverageEntryPrice;
CurrentPrice = position.AssetCurrentPrice ?? 0m;
MarketValue = position.MarketValue ?? 0m;
UnrealizedPnL = position.UnrealizedProfitLoss ?? 0m;
var cost = position.CostBasis != 0 ? position.CostBasis : 1m;
UnrealizedPnLPercent = (cost != 0) ? (UnrealizedPnL / Math.Abs(cost)) * 100m : 0m;
IsProfit = UnrealizedPnL >= 0;
}
}
}