Add best agent by pnl

This commit is contained in:
2025-08-15 22:35:29 +07:00
parent e73af1dd3a
commit 137444a045
6 changed files with 200 additions and 4 deletions

View File

@@ -400,6 +400,49 @@ public class DataController : ControllerBase
return Ok(topStrategiesByRoi); return Ok(topStrategiesByRoi);
} }
/// <summary>
/// Retrieves the top 3 performing agents based on PnL.
/// </summary>
/// <returns>A <see cref="TopAgentsByPnLViewModel"/> containing the top performing agents by PnL.</returns>
[HttpGet("GetTopAgentsByPnL")]
public async Task<ActionResult<TopAgentsByPnLViewModel>> GetTopAgentsByPnL()
{
try
{
// Get all agent summaries
var allAgentSummaries = await _mediator.Send(new GetAllAgentSummariesCommand("Total"));
// Filter agents with valid PnL data and order by PnL
var agentsWithPnL = allAgentSummaries
.Where(agent => agent.TotalPnL != 0) // Only include agents with actual PnL
.OrderByDescending(agent => agent.TotalPnL)
.Take(3)
.ToList();
// Map to view model
var topAgentsByPnL = new TopAgentsByPnLViewModel
{
TopAgentsByPnL = agentsWithPnL
.Select(agent => new AgentPerformance
{
AgentName = agent.AgentName,
PnL = agent.TotalPnL,
TotalROI = agent.TotalROI,
TotalVolume = agent.TotalVolume,
ActiveStrategiesCount = agent.ActiveStrategiesCount,
TotalBalance = agent.TotalBalance
})
.ToList()
};
return Ok(topAgentsByPnL);
}
catch (Exception ex)
{
return StatusCode(500, $"Error retrieving top agents by PnL: {ex.Message}");
}
}
/// <summary> /// <summary>
/// Retrieves list of the active strategies for a user with detailed information /// Retrieves list of the active strategies for a user with detailed information
/// </summary> /// </summary>

View File

@@ -65,4 +65,51 @@ namespace Managing.Api.Models.Responses
/// </summary> /// </summary>
public List<StrategyRoiPerformance> TopStrategiesByRoi { get; set; } = new List<StrategyRoiPerformance>(); public List<StrategyRoiPerformance> TopStrategiesByRoi { get; set; } = new List<StrategyRoiPerformance>();
} }
/// <summary>
/// Represents a high-performing agent with its name and PnL value
/// </summary>
public class AgentPerformance
{
/// <summary>
/// Name of the agent
/// </summary>
public string AgentName { get; set; }
/// <summary>
/// Profit and Loss value of the agent
/// </summary>
public decimal PnL { get; set; }
/// <summary>
/// Total ROI percentage of the agent
/// </summary>
public decimal TotalROI { get; set; }
/// <summary>
/// Total volume traded by the agent
/// </summary>
public decimal TotalVolume { get; set; }
/// <summary>
/// Number of active strategies for this agent
/// </summary>
public int ActiveStrategiesCount { get; set; }
/// <summary>
/// Total balance including USDC and open position values
/// </summary>
public decimal TotalBalance { get; set; }
}
/// <summary>
/// View model containing the top performing agents by PnL
/// </summary>
public class TopAgentsByPnLViewModel
{
/// <summary>
/// List of the top performing agents by PnL
/// </summary>
public List<AgentPerformance> TopAgentsByPnL { get; set; } = new List<AgentPerformance>();
}
} }

View File

@@ -1939,6 +1939,41 @@ export class DataClient extends AuthorizedApiBase {
return Promise.resolve<TopStrategiesByRoiViewModel>(null as any); return Promise.resolve<TopStrategiesByRoiViewModel>(null as any);
} }
data_GetTopAgentsByPnL(): Promise<TopAgentsByPnLViewModel> {
let url_ = this.baseUrl + "/Data/GetTopAgentsByPnL";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "GET",
headers: {
"Accept": "application/json"
}
};
return this.transformOptions(options_).then(transformedOptions_ => {
return this.http.fetch(url_, transformedOptions_);
}).then((_response: Response) => {
return this.processData_GetTopAgentsByPnL(_response);
});
}
protected processData_GetTopAgentsByPnL(response: Response): Promise<TopAgentsByPnLViewModel> {
const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200) {
return response.text().then((_responseText) => {
let result200: any = null;
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as TopAgentsByPnLViewModel;
return result200;
});
} else if (status !== 200 && status !== 204) {
return response.text().then((_responseText) => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
});
}
return Promise.resolve<TopAgentsByPnLViewModel>(null as any);
}
data_GetUserStrategies(agentName: string | null | undefined): Promise<UserStrategyDetailsViewModel[]> { data_GetUserStrategies(agentName: string | null | undefined): Promise<UserStrategyDetailsViewModel[]> {
let url_ = this.baseUrl + "/Data/GetUserStrategies?"; let url_ = this.baseUrl + "/Data/GetUserStrategies?";
if (agentName !== undefined && agentName !== null) if (agentName !== undefined && agentName !== null)
@@ -4602,6 +4637,19 @@ export interface StrategyRoiPerformance {
volume?: number; volume?: number;
} }
export interface TopAgentsByPnLViewModel {
topAgentsByPnL?: AgentPerformance[] | null;
}
export interface AgentPerformance {
agentName?: string | null;
pnL?: number;
totalROI?: number;
totalVolume?: number;
activeStrategiesCount?: number;
totalBalance?: number;
}
export interface UserStrategyDetailsViewModel { export interface UserStrategyDetailsViewModel {
name?: string | null; name?: string | null;
state?: BotStatus; state?: BotStatus;

View File

@@ -957,6 +957,19 @@ export interface StrategyRoiPerformance {
volume?: number; volume?: number;
} }
export interface TopAgentsByPnLViewModel {
topAgentsByPnL?: AgentPerformance[] | null;
}
export interface AgentPerformance {
agentName?: string | null;
pnL?: number;
totalROI?: number;
totalVolume?: number;
activeStrategiesCount?: number;
totalBalance?: number;
}
export interface UserStrategyDetailsViewModel { export interface UserStrategyDetailsViewModel {
name?: string | null; name?: string | null;
state?: BotStatus; state?: BotStatus;

View File

@@ -24,6 +24,7 @@ function PlatformSummary({index}: { index: number }) {
const platformData = data?.platform const platformData = data?.platform
const topStrategies = data?.topStrategies const topStrategies = data?.topStrategies
const topStrategiesByRoi = data?.topStrategiesByRoi const topStrategiesByRoi = data?.topStrategiesByRoi
const topAgentsByPnL = data?.topAgentsByPnL
const formatCurrency = (value: number) => { const formatCurrency = (value: number) => {
if (value >= 1000000) { if (value >= 1000000) {
@@ -210,6 +211,46 @@ function PlatformSummary({index}: { index: number }) {
)} )}
</div> </div>
</div> </div>
{/* Top 3 Agents by PnL */}
<div className="bg-base-200 rounded-lg p-6">
<div className="flex items-center gap-2 mb-4">
<span className="text-2xl">👥</span>
<h3 className="text-lg font-semibold text-gray-400">Top 3 Agents by PnL</h3>
</div>
<div className="space-y-3">
{topAgentsByPnL?.topAgentsByPnL?.slice(0, 3).map((agent, index) => (
<div key={index} className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-purple-500 rounded-full flex items-center justify-center">
<span className="text-xs font-bold text-white">
{agent.agentName?.charAt(0) || 'A'}
</span>
</div>
<div>
<div className="text-sm text-white font-medium">
{agent.agentName || '[Agent Name]'}
</div>
<div className="text-xs text-gray-400">
{agent.activeStrategiesCount || 0} strategies
</div>
</div>
</div>
<div className="text-right">
<div
className={`text-sm font-bold ${(agent.pnL || 0) >= 0 ? 'text-green-500' : 'text-red-500'}`}>
{(agent.pnL || 0) >= 0 ? '+' : ''}{formatCurrency(agent.pnL || 0)}
</div>
<div className="text-xs text-gray-400">
{(agent.totalROI || 0).toFixed(2)}% ROI
</div>
</div>
</div>
)) || (
<div className="text-gray-500 text-sm">No agent data available</div>
)}
</div>
</div>
</div> </div>
{/* Platform Summary Stats */} {/* Platform Summary Stats */}

View File

@@ -2,28 +2,32 @@ import {
DataClient, DataClient,
type PlatformSummaryViewModel, type PlatformSummaryViewModel,
type TopStrategiesByRoiViewModel, type TopStrategiesByRoiViewModel,
type TopStrategiesViewModel type TopStrategiesViewModel,
type TopAgentsByPnLViewModel
} from '../generated/ManagingApi' } from '../generated/ManagingApi'
export interface PlatformData { export interface PlatformData {
platform: PlatformSummaryViewModel platform: PlatformSummaryViewModel
topStrategies: TopStrategiesViewModel topStrategies: TopStrategiesViewModel
topStrategiesByRoi: TopStrategiesByRoiViewModel topStrategiesByRoi: TopStrategiesByRoiViewModel
topAgentsByPnL: TopAgentsByPnLViewModel
} }
export const fetchPlatformData = async (apiUrl: string): Promise<PlatformData> => { export const fetchPlatformData = async (apiUrl: string): Promise<PlatformData> => {
const client = new DataClient({}, apiUrl) const client = new DataClient({}, apiUrl)
// Fetch all platform data in parallel // Fetch all platform data in parallel
const [platform, topStrategies, topStrategiesByRoi] = await Promise.all([ const [platform, topStrategies, topStrategiesByRoi, topAgentsByPnL] = await Promise.all([
client.data_GetPlatformSummary(), client.data_GetPlatformSummary(),
client.data_GetTopStrategies(), client.data_GetTopStrategies(),
client.data_GetTopStrategiesByRoi() client.data_GetTopStrategiesByRoi(),
client.data_GetTopAgentsByPnL()
]) ])
return { return {
platform, platform,
topStrategies, topStrategies,
topStrategiesByRoi topStrategiesByRoi,
topAgentsByPnL
} }
} }