Add test for platform summary calculation
This commit is contained in:
@@ -571,6 +571,17 @@ public static class TradingBox
|
||||
decimal TotalFees,
|
||||
decimal Collateral);
|
||||
|
||||
public record PlatformSummaryMetrics(
|
||||
decimal TotalPlatformVolume,
|
||||
decimal TotalPlatformFees,
|
||||
decimal TotalPlatformPnL,
|
||||
decimal NetPnL,
|
||||
decimal OpenInterest,
|
||||
int TotalLifetimePositionCount,
|
||||
Dictionary<string, decimal> VolumeByAsset,
|
||||
Dictionary<string, int> PositionCountByAsset,
|
||||
Dictionary<TradeDirection, int> PositionCountByDirection);
|
||||
|
||||
public static AgentSummaryMetrics CalculateAgentSummaryMetrics(List<Position> positions)
|
||||
{
|
||||
var validPositions = positions?
|
||||
@@ -595,6 +606,141 @@ public static class TradingBox
|
||||
collateral);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates comprehensive platform summary metrics from a list of positions.
|
||||
/// This includes volume, PnL, fees, open interest, and breakdowns by asset/direction.
|
||||
/// </summary>
|
||||
/// <param name="positions">List of all positions to analyze</param>
|
||||
/// <param name="previousTotalVolume">Previous total volume to ensure cumulative volume never decreases</param>
|
||||
/// <returns>PlatformSummaryMetrics with all calculated values</returns>
|
||||
public static PlatformSummaryMetrics CalculatePlatformSummaryMetrics(List<Position> positions, decimal previousTotalVolume = 0m)
|
||||
{
|
||||
if (positions == null || !positions.Any())
|
||||
{
|
||||
return new PlatformSummaryMetrics(
|
||||
TotalPlatformVolume: 0m,
|
||||
TotalPlatformFees: 0m,
|
||||
TotalPlatformPnL: 0m,
|
||||
NetPnL: 0m,
|
||||
OpenInterest: 0m,
|
||||
TotalLifetimePositionCount: 0,
|
||||
VolumeByAsset: new Dictionary<string, decimal>(),
|
||||
PositionCountByAsset: new Dictionary<string, int>(),
|
||||
PositionCountByDirection: new Dictionary<TradeDirection, int>()
|
||||
);
|
||||
}
|
||||
|
||||
// Initialize result variables
|
||||
var volumeByAsset = new Dictionary<string, decimal>();
|
||||
var positionCountByAsset = new Dictionary<string, int>();
|
||||
var positionCountByDirection = new Dictionary<TradeDirection, int>();
|
||||
|
||||
decimal totalVolumeFromAllPositions = 0m;
|
||||
decimal totalFees = 0m;
|
||||
decimal totalPnL = 0m;
|
||||
decimal totalOpenInterest = 0m;
|
||||
int totalPositionCount = 0;
|
||||
|
||||
foreach (var position in positions)
|
||||
{
|
||||
if (!position.IsValidForMetrics()) continue;
|
||||
|
||||
// Calculate volume using the same logic as daily snapshots for consistency
|
||||
// Opening volume is always counted (for positions opened on or before today)
|
||||
var openVolume = position.Open.Price * position.Open.Quantity * position.Open.Leverage;
|
||||
var closingVolume = 0m;
|
||||
|
||||
// Only include closing volume from trades that are filled
|
||||
if (position.Status == PositionStatus.Finished || position.Status == PositionStatus.Flipped)
|
||||
{
|
||||
if (position.StopLoss?.Status == TradeStatus.Filled)
|
||||
{
|
||||
closingVolume += position.StopLoss.Price * position.StopLoss.Quantity * position.StopLoss.Leverage;
|
||||
}
|
||||
|
||||
if (position.TakeProfit1?.Status == TradeStatus.Filled)
|
||||
{
|
||||
closingVolume += position.TakeProfit1.Price * position.TakeProfit1.Quantity * position.TakeProfit1.Leverage;
|
||||
}
|
||||
|
||||
if (position.TakeProfit2?.Status == TradeStatus.Filled)
|
||||
{
|
||||
closingVolume += position.TakeProfit2.Price * position.TakeProfit2.Quantity * position.TakeProfit2.Leverage;
|
||||
}
|
||||
}
|
||||
|
||||
var positionVolume = openVolume + closingVolume;
|
||||
|
||||
// Track total volume from ALL positions (this is the true cumulative volume)
|
||||
totalVolumeFromAllPositions += positionVolume;
|
||||
|
||||
// Calculate breakdown metrics from ALL positions (for current state)
|
||||
var ticker = position.Ticker.ToString();
|
||||
var direction = position.OriginDirection;
|
||||
|
||||
// Volume breakdown by asset - update state directly
|
||||
if (!volumeByAsset.ContainsKey(ticker))
|
||||
{
|
||||
volumeByAsset[ticker] = 0;
|
||||
}
|
||||
volumeByAsset[ticker] += positionVolume;
|
||||
|
||||
// Position count breakdown by asset - update state directly
|
||||
if (!positionCountByAsset.ContainsKey(ticker))
|
||||
{
|
||||
positionCountByAsset[ticker] = 0;
|
||||
}
|
||||
positionCountByAsset[ticker]++;
|
||||
|
||||
// Calculate fees and PnL for all positions
|
||||
totalFees += position.CalculateTotalFees();
|
||||
totalPnL += position.ProfitAndLoss?.Realized ?? 0;
|
||||
|
||||
// Count all positions
|
||||
totalPositionCount++;
|
||||
|
||||
// Position count breakdown by direction - only count open positions
|
||||
if (position.IsOpen())
|
||||
{
|
||||
var openingVolume = position.Open.Price * position.Open.Quantity * position.Open.Leverage;
|
||||
totalOpenInterest += openingVolume;
|
||||
|
||||
if (!positionCountByDirection.ContainsKey(direction))
|
||||
{
|
||||
positionCountByDirection[direction] = 0;
|
||||
}
|
||||
positionCountByDirection[direction]++;
|
||||
}
|
||||
}
|
||||
|
||||
// ISSUE FIX: The correct cumulative volume should be calculated from ALL positions
|
||||
// not just by adding new positions to the last snapshot, because:
|
||||
// 1. Positions might be closed/updated which affects their volume
|
||||
// 2. The daily snapshot might be outdated
|
||||
// We should use the total from all positions, but never let it decrease
|
||||
var correctCumulativeVolume = totalVolumeFromAllPositions;
|
||||
|
||||
// Ensure volume never decreases
|
||||
if (correctCumulativeVolume < previousTotalVolume)
|
||||
{
|
||||
correctCumulativeVolume = previousTotalVolume;
|
||||
}
|
||||
|
||||
var netPnL = totalPnL - totalFees;
|
||||
|
||||
return new PlatformSummaryMetrics(
|
||||
TotalPlatformVolume: correctCumulativeVolume,
|
||||
TotalPlatformFees: totalFees,
|
||||
TotalPlatformPnL: totalPnL,
|
||||
NetPnL: netPnL,
|
||||
OpenInterest: totalOpenInterest,
|
||||
TotalLifetimePositionCount: totalPositionCount,
|
||||
VolumeByAsset: volumeByAsset,
|
||||
PositionCountByAsset: positionCountByAsset,
|
||||
PositionCountByDirection: positionCountByDirection
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the volume traded in the last 24 hours
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user