using Managing.Application.Abstractions; using Managing.Application.Abstractions.Services; using Managing.Application.Hubs; using Managing.Application.ManageBot.Commands; using Managing.Application.Workers.Abstractions; using Managing.Api.Models.Responses; using Managing.Domain.Candles; using Managing.Domain.Statistics; using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.SignalR; using static Managing.Common.Enums; namespace Managing.Api.Controllers; /// /// Controller for handling data-related operations such as retrieving tickers, spotlight data, and candles. /// Requires authorization for access. /// [ApiController] [Route("[controller]")] public class DataController : ControllerBase { private readonly IExchangeService _exchangeService; private readonly IAccountService _accountService; private readonly ICacheService _cacheService; private readonly IStatisticService _statisticService; private readonly IHubContext _hubContext; private readonly IMediator _mediator; /// /// Initializes a new instance of the class. /// /// Service for interacting with exchanges. /// Service for account management. /// Service for caching data. /// Service for statistical analysis. /// SignalR hub context for real-time communication. /// Mediator for handling commands and queries. public DataController( IExchangeService exchangeService, IAccountService accountService, ICacheService cacheService, IStatisticService statisticService, IHubContext hubContext, IMediator mediator) { _exchangeService = exchangeService; _accountService = accountService; _cacheService = cacheService; _statisticService = statisticService; _hubContext = hubContext; _mediator = mediator; } /// /// Retrieves tickers for a given account and timeframe, utilizing caching to improve performance. /// /// The timeframe for which to retrieve tickers. /// An array of tickers. [HttpPost("GetTickers")] public async Task> GetTickers(Timeframe timeframe) { var cacheKey = string.Concat(timeframe.ToString()); var tickers = _cacheService.GetValue>(cacheKey); if (tickers == null || tickers.Count == 0) { tickers = await _exchangeService.GetTickers(timeframe); _cacheService.SaveValue(cacheKey, tickers, TimeSpan.FromHours(2)); } return Ok(tickers); } /// /// Retrieves the latest spotlight overview, using caching to enhance response times. /// /// A object containing spotlight data. [Authorize] [HttpGet("Spotlight")] public ActionResult GetSpotlight() { var overview = _cacheService.GetOrSave(nameof(SpotlightOverview), () => { return _statisticService.GetLastSpotlight(DateTime.Now.AddDays(-2)); }, TimeSpan.FromMinutes(2)); if (overview?.Spotlights.Count < overview?.ScenarioCount || overview == null) { overview = _statisticService.GetLastSpotlight(DateTime.Now.AddDays(-2)); } return Ok(overview); } /// /// Retrieves candle data for a given exchange, ticker, start date, and timeframe. /// /// The exchange to retrieve candles from. /// The ticker symbol to retrieve candles for. /// The start date for the candle data. /// The timeframe for the candle data. /// A list of objects. [Authorize] [HttpGet("GetCandles")] public async Task>> GetCandles(TradingExchanges exchange, Ticker ticker, DateTime startDate, Timeframe timeframe) { return Ok(await _exchangeService.GetCandlesInflux(exchange, ticker, startDate, timeframe)); } /// /// Retrieves statistics about currently running bots and their change in the last 24 hours. /// /// A containing bot statistics. [HttpGet("GetStrategiesStatistics")] public async Task> GetStrategiesStatistics() { const string cacheKey = "StrategiesStatistics"; const string previousCountKey = "PreviousBotsCount"; // Check if the statistics are already cached var cachedStats = _cacheService.GetValue(cacheKey); if (cachedStats != null) { return Ok(cachedStats); } // Get active bots var activeBots = await _mediator.Send(new GetActiveBotsCommand()); var currentCount = activeBots.Count; // Get previous count from cache var previousCount = _cacheService.GetValue(previousCountKey); // Calculate change - if no previous value, set current count as the change (all are new) int change; if (previousCount == 0) { // First time running - assume all bots are new (positive change) change = currentCount; } else { // Calculate actual difference between current and previous count change = currentCount - previousCount; } // Create the response var botsStatistics = new StrategiesStatisticsViewModel { TotalStrategiesRunning = currentCount, ChangeInLast24Hours = change }; // Store current count for future comparison (with 24 hour expiration) _cacheService.SaveValue(previousCountKey, currentCount, TimeSpan.FromHours(24)); // Cache the statistics for 5 minutes _cacheService.SaveValue(cacheKey, botsStatistics, TimeSpan.FromMinutes(5)); return Ok(botsStatistics); } /// /// Retrieves the top 3 performing strategies based on ROI. /// /// A containing the top performing strategies. [HttpGet("GetTopStrategies")] public async Task> GetTopStrategies() { const string cacheKey = "TopStrategies"; // Check if the top strategies are already cached var cachedStrategies = _cacheService.GetValue(cacheKey); if (cachedStrategies != null) { return Ok(cachedStrategies); } // Get active bots var activeBots = await _mediator.Send(new GetActiveBotsCommand()); // Calculate PnL for each bot once and store in a list of tuples var botsWithPnL = activeBots .Select(bot => new { Bot = bot, PnL = bot.GetProfitAndLoss() }) .OrderByDescending(item => item.PnL) .Take(3) .ToList(); // Map to view model var topStrategies = new TopStrategiesViewModel { TopStrategies = botsWithPnL .Select(item => new StrategyPerformance { StrategyName = item.Bot.Name, PnL = item.PnL }) .ToList() }; // Cache the result for 10 minutes _cacheService.SaveValue(cacheKey, topStrategies, TimeSpan.FromMinutes(10)); return Ok(topStrategies); } }