Add platform grain

This commit is contained in:
2025-08-14 19:44:33 +07:00
parent 345d76e06f
commit 4a45d6c970
13 changed files with 2324 additions and 59 deletions

View File

@@ -1,5 +1,7 @@
using Managing.Api.Models.Requests;
using Managing.Api.Extensions;
using Managing.Api.Models.Requests;
using Managing.Api.Models.Responses;
using Managing.Application.Abstractions.Grains;
using Managing.Application.Abstractions.Services;
using Managing.Application.Hubs;
using Managing.Application.ManageBot.Commands;
@@ -36,6 +38,7 @@ public class DataController : ControllerBase
private readonly IHubContext<CandleHub> _hubContext;
private readonly IMediator _mediator;
private readonly ITradingService _tradingService;
private readonly IGrainFactory _grainFactory;
/// <summary>
/// Initializes a new instance of the <see cref="DataController"/> class.
@@ -47,6 +50,7 @@ public class DataController : ControllerBase
/// <param name="hubContext">SignalR hub context for real-time communication.</param>
/// <param name="mediator">Mediator for handling commands and queries.</param>
/// <param name="tradingService">Service for trading operations.</param>
/// <param name="grainFactory">Orleans grain factory for accessing grains.</param>
public DataController(
IExchangeService exchangeService,
IAccountService accountService,
@@ -55,7 +59,8 @@ public class DataController : ControllerBase
IAgentService agentService,
IHubContext<CandleHub> hubContext,
IMediator mediator,
ITradingService tradingService)
ITradingService tradingService,
IGrainFactory grainFactory)
{
_exchangeService = exchangeService;
_accountService = accountService;
@@ -65,6 +70,7 @@ public class DataController : ControllerBase
_hubContext = hubContext;
_mediator = mediator;
_tradingService = tradingService;
_grainFactory = grainFactory;
}
/// <summary>
@@ -458,68 +464,31 @@ public class DataController : ControllerBase
/// <summary>
/// Retrieves a summary of platform activity across all agents (platform-level data only)
/// Uses Orleans grain for efficient caching and real-time updates
/// </summary>
/// <returns>A summary of platform activity without individual agent details</returns>
[HttpGet("GetPlatformSummary")]
public async Task<ActionResult<PlatformSummaryViewModel>> GetPlatformSummary()
{
const string cacheKey = "PlatformSummary";
// Check if the platform summary is already cached
var cachedSummary = _cacheService.GetValue<PlatformSummaryViewModel>(cacheKey);
if (cachedSummary != null)
try
{
return Ok(cachedSummary);
// Get the platform summary grain
var platformSummaryGrain = _grainFactory.GetGrain<IPlatformSummaryGrain>("platform-summary");
// Get the platform summary from the grain (handles caching and real-time updates)
var abstractionsSummary = await platformSummaryGrain.GetPlatformSummaryAsync();
// Convert to API ViewModel
var summary = abstractionsSummary.ToApiViewModel();
return Ok(summary);
}
// Get all agents and their strategies (without time filter)
var agentsWithStrategies = await _mediator.Send(new GetAllAgentsCommand());
// Create the platform summary
var summary = new PlatformSummaryViewModel
catch (Exception ex)
{
TotalAgents = agentsWithStrategies.Count,
TotalActiveStrategies = agentsWithStrategies.Values.Sum(list => list.Count)
};
// Calculate total platform metrics
decimal totalPlatformPnL = 0;
decimal totalPlatformVolume = 0;
decimal totalPlatformVolumeLast24h = 0;
// Calculate totals from all agents
foreach (var agent in agentsWithStrategies)
{
var strategies = agent.Value;
if (strategies.Count == 0)
{
continue; // Skip agents with no strategies
}
// TODO: Add this calculation into repository for better performance
var globalPnL = strategies.Sum(s => s.Pnl);
var globalVolume = strategies.Sum(s => s.Volume);
var globalVolumeLast24h = strategies.Sum(s => s.Volume);
// Calculate agent metrics for platform totals
// Add to platform totals
totalPlatformPnL += globalPnL;
totalPlatformVolume += globalVolume;
totalPlatformVolumeLast24h += globalVolumeLast24h;
// Log the error and return a fallback response
// In production, you might want to return cached data or partial data
return StatusCode(500, $"Error retrieving platform summary: {ex.Message}");
}
// Set the platform totals
summary.TotalPlatformPnL = totalPlatformPnL;
summary.TotalPlatformVolume = totalPlatformVolume;
summary.TotalPlatformVolumeLast24h = totalPlatformVolumeLast24h;
// Cache the results for 5 minutes
_cacheService.SaveValue(cacheKey, summary, TimeSpan.FromMinutes(5));
return Ok(summary);
}

View File

@@ -0,0 +1,40 @@
using Managing.Api.Models.Responses;
using AbstractionsPlatformSummaryViewModel = Managing.Application.Abstractions.Models.PlatformSummaryViewModel;
namespace Managing.Api.Extensions;
/// <summary>
/// Extension methods for converting between Platform Summary ViewModels
/// </summary>
public static class PlatformSummaryExtensions
{
/// <summary>
/// Converts from the Abstractions PlatformSummaryViewModel to the API PlatformSummaryViewModel
/// </summary>
public static PlatformSummaryViewModel ToApiViewModel(this AbstractionsPlatformSummaryViewModel abstractionsModel)
{
return new PlatformSummaryViewModel
{
TotalAgents = abstractionsModel.TotalAgents,
TotalActiveStrategies = abstractionsModel.TotalActiveStrategies,
TotalPlatformPnL = abstractionsModel.TotalPlatformPnL,
TotalPlatformVolume = abstractionsModel.TotalPlatformVolume,
TotalPlatformVolumeLast24h = abstractionsModel.TotalPlatformVolumeLast24h,
TotalOpenInterest = abstractionsModel.TotalOpenInterest,
TotalPositionCount = abstractionsModel.TotalPositionCount,
AgentsChange24h = abstractionsModel.AgentsChange24h,
StrategiesChange24h = abstractionsModel.StrategiesChange24h,
PnLChange24h = abstractionsModel.PnLChange24h,
VolumeChange24h = abstractionsModel.VolumeChange24h,
OpenInterestChange24h = abstractionsModel.OpenInterestChange24h,
PositionCountChange24h = abstractionsModel.PositionCountChange24h,
VolumeByAsset = abstractionsModel.VolumeByAsset,
PositionCountByAsset = abstractionsModel.PositionCountByAsset,
PositionCountByDirection = abstractionsModel.PositionCountByDirection.ToDictionary(
kvp => kvp.Key.ToString(),
kvp => kvp.Value),
LastUpdated = abstractionsModel.LastUpdated,
Last24HourSnapshot = abstractionsModel.Last24HourSnapshot
};
}
}

View File

@@ -71,6 +71,74 @@ namespace Managing.Api.Models.Responses
/// Total volume traded across all agents in the last 24 hours in USD
/// </summary>
public decimal TotalPlatformVolumeLast24h { get; set; }
/// <summary>
/// Total open interest across all positions in USD
/// </summary>
public decimal TotalOpenInterest { get; set; }
/// <summary>
/// Total number of open positions across all strategies
/// </summary>
public int TotalPositionCount { get; set; }
// 24-hour changes
/// <summary>
/// Change in agent count over the last 24 hours
/// </summary>
public int AgentsChange24h { get; set; }
/// <summary>
/// Change in strategy count over the last 24 hours
/// </summary>
public int StrategiesChange24h { get; set; }
/// <summary>
/// Change in PnL over the last 24 hours
/// </summary>
public decimal PnLChange24h { get; set; }
/// <summary>
/// Change in volume over the last 24 hours
/// </summary>
public decimal VolumeChange24h { get; set; }
/// <summary>
/// Change in open interest over the last 24 hours
/// </summary>
public decimal OpenInterestChange24h { get; set; }
/// <summary>
/// Change in position count over the last 24 hours
/// </summary>
public int PositionCountChange24h { get; set; }
// Breakdowns
/// <summary>
/// Volume breakdown by asset/ticker
/// </summary>
public Dictionary<string, decimal> VolumeByAsset { get; set; } = new();
/// <summary>
/// Position count breakdown by asset/ticker
/// </summary>
public Dictionary<string, int> PositionCountByAsset { get; set; } = new();
/// <summary>
/// Position count breakdown by direction (Long/Short)
/// </summary>
public Dictionary<string, int> PositionCountByDirection { get; set; } = new();
// Metadata
/// <summary>
/// When the data was last updated
/// </summary>
public DateTime LastUpdated { get; set; }
/// <summary>
/// When the last 24-hour snapshot was taken
/// </summary>
public DateTime Last24HourSnapshot { get; set; }
}
/// <summary>