147 lines
4.6 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|