using System; using System.Collections.ObjectModel; using System.Windows; using System.Windows.Input; using DesktopBot.Services; namespace DesktopBot.ViewModels { /// /// ViewModel per il Wallet e le posizioni aperte /// public class WalletViewModel : BaseViewModel { private readonly ITradingService _tradingService; private decimal _equity; private bool _isLoading; private string _errorMessage; private bool _hasError; private ObservableCollection _positions = new ObservableCollection(); 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 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(); } /// /// Carica equity e posizioni aperte da Alpaca /// 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 */ } } } /// /// ViewModel per una singola posizione aperta /// 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; } } }