185 lines
6.9 KiB
C#
185 lines
6.9 KiB
C#
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Input;
|
|
using DesktopBot.Engine;
|
|
using DesktopBot.Models;
|
|
using DesktopBot.Services;
|
|
namespace DesktopBot.ViewModels
|
|
{
|
|
public class MainViewModel : BaseViewModel
|
|
{
|
|
private readonly ITradingService _tradingService;
|
|
private readonly AutomatedBotEngine _botEngine;
|
|
private readonly AlpacaPingService _pingService;
|
|
|
|
public PingViewModel PingVM { get; } = new PingViewModel();
|
|
|
|
// ── Auto-refresh periodico ─────────────────────────────────────────
|
|
/// <summary>Intervallo (secondi) per l'aggiornamento frequente: posizioni e ordini.</summary>
|
|
private const int FastRefreshSeconds = 30;
|
|
/// <summary>Intervallo (secondi) per l'aggiornamento lento: balance, wallet, dashboard.</summary>
|
|
private const int SlowRefreshSeconds = 60;
|
|
|
|
private CancellationTokenSource _autoRefreshCts;
|
|
private int _refreshCycle; // contatore cicli per sfalsare il refresh lento
|
|
|
|
private BaseViewModel _currentViewModel;
|
|
private string _selectedTab;
|
|
|
|
public DashboardViewModel DashboardVM { get; }
|
|
public BotsManagerViewModel BotsVM { get; }
|
|
public LiveLogViewModel LiveLogVM { get; }
|
|
public WalletViewModel WalletVM { get; }
|
|
public SettingsViewModel SettingsVM { get; }
|
|
public BalanceViewModel BalanceVM { get; }
|
|
public PositionsViewModel PositionsVM { get; }
|
|
public OrdersViewModel OrdersVM { get; }
|
|
|
|
public BaseViewModel CurrentViewModel
|
|
{
|
|
get => _currentViewModel;
|
|
set => SetProperty(ref _currentViewModel, value);
|
|
}
|
|
|
|
public string SelectedTab
|
|
{
|
|
get => _selectedTab;
|
|
set
|
|
{
|
|
if (SetProperty(ref _selectedTab, value))
|
|
NavigateToTab(value);
|
|
}
|
|
}
|
|
|
|
public ICommand NavigateCommand { get; }
|
|
|
|
public MainViewModel()
|
|
{
|
|
_tradingService = new AlpacaTradingService();
|
|
_botEngine = new AutomatedBotEngine(_tradingService);
|
|
_pingService = new AlpacaPingService();
|
|
_pingService.PingCompleted += (_, r) => PingVM.Update(r);
|
|
|
|
DashboardVM = new DashboardViewModel(_tradingService, _botEngine);
|
|
BotsVM = new BotsManagerViewModel(_tradingService);
|
|
LiveLogVM = new LiveLogViewModel(_botEngine);
|
|
WalletVM = new WalletViewModel(_tradingService);
|
|
SettingsVM = new SettingsViewModel(_tradingService, OnCredentialsSaved);
|
|
BalanceVM = new BalanceViewModel(_tradingService);
|
|
PositionsVM = new PositionsViewModel(_tradingService);
|
|
OrdersVM = new OrdersViewModel(_tradingService);
|
|
|
|
NavigateCommand = new RelayCommand(param => SelectedTab = param?.ToString());
|
|
SubscribeToBotEvents();
|
|
|
|
var saved = CredentialService.LoadCredentials();
|
|
if (saved != null)
|
|
{
|
|
_tradingService.Initialize(saved.ApiKey, saved.ApiSecret, saved.IsPaper);
|
|
_pingService.SetEnvironment(saved.IsPaper);
|
|
_pingService.Start(10);
|
|
CurrentViewModel = DashboardVM;
|
|
SelectedTab = "Dashboard";
|
|
_ = LoadInitialDataAsync();
|
|
}
|
|
else
|
|
{
|
|
CurrentViewModel = SettingsVM;
|
|
SelectedTab = "Settings";
|
|
}
|
|
}
|
|
|
|
private void NavigateToTab(string tabName)
|
|
{
|
|
CurrentViewModel = tabName switch
|
|
{
|
|
"Dashboard" => (BaseViewModel)DashboardVM,
|
|
"Bot" => BotsVM,
|
|
"LiveLog" => LiveLogVM,
|
|
"Wallet" => WalletVM,
|
|
"Settings" => SettingsVM,
|
|
"Balance" => BalanceVM,
|
|
"Positions" => PositionsVM,
|
|
"Orders" => OrdersVM,
|
|
_ => DashboardVM
|
|
};
|
|
}
|
|
|
|
private void SubscribeToBotEvents()
|
|
{
|
|
_botEngine.LogGenerated += (sender, log) => LiveLogVM.AddLog(log);
|
|
_botEngine.EquityUpdated += (sender, equity) => DashboardVM.UpdateEquity(equity);
|
|
}
|
|
|
|
private async Task LoadInitialDataAsync()
|
|
{
|
|
await Task.WhenAll(
|
|
DashboardVM.LoadAsync(),
|
|
WalletVM.LoadAsync(),
|
|
BalanceVM.LoadAsync(),
|
|
PositionsVM.LoadAsync(),
|
|
OrdersVM.LoadAsync()
|
|
);
|
|
StartAutoRefresh();
|
|
}
|
|
|
|
// ── Auto-refresh periodico ─────────────────────────────────────────
|
|
private void StartAutoRefresh()
|
|
{
|
|
_autoRefreshCts?.Cancel();
|
|
_autoRefreshCts = new CancellationTokenSource();
|
|
_refreshCycle = 0;
|
|
_ = AutoRefreshLoopAsync(_autoRefreshCts.Token);
|
|
}
|
|
|
|
private async Task AutoRefreshLoopAsync(CancellationToken ct)
|
|
{
|
|
try
|
|
{
|
|
while (!ct.IsCancellationRequested)
|
|
{
|
|
await Task.Delay(TimeSpan.FromSeconds(FastRefreshSeconds), ct);
|
|
if (ct.IsCancellationRequested) break;
|
|
|
|
_refreshCycle++;
|
|
|
|
// Refresh frequente: posizioni aperte e ordini recenti
|
|
var fastTasks = new System.Collections.Generic.List<Task>
|
|
{
|
|
SafeRefresh(PositionsVM.LoadAsync),
|
|
SafeRefresh(OrdersVM.LoadAsync)
|
|
};
|
|
|
|
// Refresh lento (ogni 2 cicli = 60s): balance, wallet, dashboard
|
|
if (_refreshCycle % 2 == 0)
|
|
{
|
|
fastTasks.Add(SafeRefresh(BalanceVM.LoadAsync));
|
|
fastTasks.Add(SafeRefresh(WalletVM.LoadAsync));
|
|
fastTasks.Add(SafeRefresh(DashboardVM.LoadAsync));
|
|
}
|
|
|
|
await Task.WhenAll(fastTasks);
|
|
}
|
|
}
|
|
catch (OperationCanceledException) { /* atteso */ }
|
|
}
|
|
|
|
private static async Task SafeRefresh(Func<Task> loader)
|
|
{
|
|
try { await loader(); }
|
|
catch { /* non bloccare il ciclo per errori singoli di rete */ }
|
|
}
|
|
|
|
private void OnCredentialsSaved(string apiKey, string apiSecret, bool isPaper)
|
|
{
|
|
SelectedTab = "Dashboard";
|
|
_ = LoadInitialDataAsync();
|
|
}
|
|
public void InitializeWithCredentials(string apiKey, string apiSecret, bool isPaper)
|
|
{
|
|
_tradingService.Initialize(apiKey, apiSecret, isPaper);
|
|
}
|
|
}
|
|
}
|