diff --git a/src/Managing.Api/Controllers/DataController.cs b/src/Managing.Api/Controllers/DataController.cs index e267c0e..0326478 100644 --- a/src/Managing.Api/Controllers/DataController.cs +++ b/src/Managing.Api/Controllers/DataController.cs @@ -1,9 +1,12 @@ 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; @@ -16,7 +19,6 @@ namespace Managing.Api.Controllers; /// Requires authorization for access. /// [ApiController] -[Authorize] [Route("[controller]")] public class DataController : ControllerBase { @@ -25,6 +27,7 @@ public class DataController : ControllerBase private readonly ICacheService _cacheService; private readonly IStatisticService _statisticService; private readonly IHubContext _hubContext; + private readonly IMediator _mediator; /// /// Initializes a new instance of the class. @@ -34,18 +37,21 @@ public class DataController : ControllerBase /// 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) + IHubContext hubContext, + IMediator mediator) { _exchangeService = exchangeService; _accountService = accountService; _cacheService = cacheService; _statisticService = statisticService; _hubContext = hubContext; + _mediator = mediator; } /// @@ -72,6 +78,7 @@ public class DataController : ControllerBase /// Retrieves the latest spotlight overview, using caching to enhance response times. /// /// A object containing spotlight data. + [Authorize] [HttpGet("Spotlight")] public ActionResult GetSpotlight() { @@ -94,10 +101,110 @@ public class DataController : ControllerBase /// 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); + } } \ No newline at end of file diff --git a/src/Managing.Api/Models/Responses/StrategiesStatisticsViewModel.cs b/src/Managing.Api/Models/Responses/StrategiesStatisticsViewModel.cs new file mode 100644 index 0000000..2f4fd39 --- /dev/null +++ b/src/Managing.Api/Models/Responses/StrategiesStatisticsViewModel.cs @@ -0,0 +1,18 @@ +namespace Managing.Api.Models.Responses +{ + /// + /// View model representing statistics about active bots + /// + public class StrategiesStatisticsViewModel + { + /// + /// Total number of bots currently running + /// + public int TotalStrategiesRunning { get; set; } + + /// + /// Change in bot count over the last 24 hours + /// + public int ChangeInLast24Hours { get; set; } + } +} \ No newline at end of file diff --git a/src/Managing.Api/Models/Responses/TopStrategiesViewModel.cs b/src/Managing.Api/Models/Responses/TopStrategiesViewModel.cs new file mode 100644 index 0000000..590b149 --- /dev/null +++ b/src/Managing.Api/Models/Responses/TopStrategiesViewModel.cs @@ -0,0 +1,29 @@ +namespace Managing.Api.Models.Responses +{ + /// + /// Represents a high-performing strategy with its name and PnL value + /// + public class StrategyPerformance + { + /// + /// Name of the strategy bot + /// + public string StrategyName { get; set; } + + /// + /// Profit and Loss value of the strategy + /// + public decimal PnL { get; set; } + } + + /// + /// View model containing the top performing strategies by ROI + /// + public class TopStrategiesViewModel + { + /// + /// List of the top performing strategies by ROI + /// + public List TopStrategies { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/src/Managing.Web3Proxy/test/plugins/open-position.test.ts b/src/Managing.Web3Proxy/test/plugins/open-position.test.ts index b067ca8..cdcd8c8 100644 --- a/src/Managing.Web3Proxy/test/plugins/open-position.test.ts +++ b/src/Managing.Web3Proxy/test/plugins/open-position.test.ts @@ -15,7 +15,7 @@ test('GMX Position Opening', async (t) => { 2, 50003, 96001, - 35002 + 85002 ) console.log('Position opening result:', result) assert.ok(result, 'Position opening result should be defined')