using System; using System.Collections.ObjectModel; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using Alpaca.Markets; using DesktopBot.Services; namespace DesktopBot.ViewModels { // ─── BalanceViewModel ──────────────────────────────────────────────────────── public class BalanceViewModel : BaseViewModel { private readonly ITradingService _tradingService; private bool _isLoading; private string _errorMessage; private bool _hasError; 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 BalanceRows { get; } = new ObservableCollection(); public ICommand RefreshCommand { get; } public BalanceViewModel(ITradingService tradingService) { _tradingService = tradingService; RefreshCommand = new RelayCommand(async _ => await LoadAsync()); } public async Task LoadAsync() { IsLoading = true; HasError = false; try { var account = await _tradingService.GetAccountAsync(); Application.Current.Dispatcher.Invoke(() => { BalanceRows.Clear(); AddSection("Buying Power"); AddRow("RegT Buying Power", account.RegulationBuyingPower, account.RegulationBuyingPower); AddRow("Day Trading Buying Power", account.DayTradingBuyingPower, account.DayTradingBuyingPower); AddRow("Effective Buying Power", account.BuyingPower, account.BuyingPower); AddRow("Non-Marginable Buying Power", account.NonMarginableBuyingPower, account.NonMarginableBuyingPower); AddSection("Margin"); AddRow("Initial Margin", account.InitialMargin, account.InitialMargin); AddRow("Maintenance Margin", account.MaintenanceMargin, account.MaintenanceMargin); AddSection("Cash"); AddRow("Cash", account.TradableCash, account.TradableCash); AddSection("Positions"); AddRow("Equity", account.LastEquity, account.Equity ?? 0m, bold: true); AddRow("Long Market Value", account.LongMarketValue, account.LongMarketValue); AddRow("Short Market Value", account.ShortMarketValue, account.ShortMarketValue); }); } catch (Exception ex) { Application.Current.Dispatcher.Invoke(() => { HasError = true; ErrorMessage = "Errore: " + ex.Message; }); } finally { Application.Current.Dispatcher.Invoke(() => IsLoading = false); } } private void AddSection(string title) => BalanceRows.Add(new BalanceRowViewModel { Label = title, IsSection = true }); private void AddRow(string label, decimal? last, decimal? curr, bool bold = false) => BalanceRows.Add(new BalanceRowViewModel { Label = label, LastClose = last.HasValue ? "$" + last.Value.ToString("N2") : "-", Current = curr.HasValue ? "$" + curr.Value.ToString("N2") : "-", IsBold = bold }); private void AddRow(string label, decimal last, decimal curr, bool bold = false) => BalanceRows.Add(new BalanceRowViewModel { Label = label, LastClose = "$" + last.ToString("N2"), Current = "$" + curr.ToString("N2"), IsBold = bold }); } // ─── PositionsViewModel ────────────────────────────────────────────────────── public class PositionsViewModel : BaseViewModel { private readonly ITradingService _tradingService; private bool _isLoading; private string _errorMessage; private bool _hasError; 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; } = new ObservableCollection(); public ICommand RefreshCommand { get; } public ICommand CloseAllCommand { get; } public ICommand ClosePositionCommand { get; } public PositionsViewModel(ITradingService tradingService) { _tradingService = tradingService; RefreshCommand = new RelayCommand(async _ => await LoadAsync()); CloseAllCommand = new RelayCommand(async _ => { await _tradingService.CloseAllPositionsAsync(); await LoadAsync(); }); ClosePositionCommand = new RelayCommand(async p => { if (p is string symbol) { await _tradingService.ClosePositionAsync(symbol); await LoadAsync(); } }); } public async Task LoadAsync() { IsLoading = true; HasError = false; try { var positions = await _tradingService.GetAllPositionsAsync(); Application.Current.Dispatcher.Invoke(() => { Positions.Clear(); foreach (var p in positions) Positions.Add(new PositionViewModel(p)); }); } catch (Exception ex) { Application.Current.Dispatcher.Invoke(() => { HasError = true; ErrorMessage = "Errore: " + ex.Message; }); } finally { Application.Current.Dispatcher.Invoke(() => IsLoading = false); } } } // ─── OrdersViewModel ──────────────────────────────────────────────────────── public class OrdersViewModel : BaseViewModel { private readonly ITradingService _tradingService; private bool _isLoading; private string _errorMessage; private bool _hasError; 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 Orders { get; } = new ObservableCollection(); public ICommand RefreshCommand { get; } public ICommand CancelOrderCommand { get; } public OrdersViewModel(ITradingService tradingService) { _tradingService = tradingService; RefreshCommand = new RelayCommand(async _ => await LoadAsync()); CancelOrderCommand = new RelayCommand(async p => { if (p is Guid id) { await _tradingService.CancelOrderAsync(id); await LoadAsync(); } }); } public async Task LoadAsync() { IsLoading = true; HasError = false; try { var orders = await _tradingService.GetOrdersAsync(OrderStatusFilter.All, 100); Application.Current.Dispatcher.Invoke(() => { Orders.Clear(); foreach (var o in orders) Orders.Add(new OrderViewModel(o)); }); } catch (Exception ex) { Application.Current.Dispatcher.Invoke(() => { HasError = true; ErrorMessage = "Errore: " + ex.Message; }); } finally { Application.Current.Dispatcher.Invoke(() => IsLoading = false); } } } }