|
|
|
|
@@ -64,6 +64,9 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable
|
|
|
|
|
_logger.LogInformation("Daily reminder scheduled - Next daily: {NextDaily}, Due time: {DueTime}",
|
|
|
|
|
nextDailyTime, timeUntilNextDay);
|
|
|
|
|
|
|
|
|
|
// Fix the first snapshot date if needed (should be day before first position)
|
|
|
|
|
await FixFirstSnapshotDateAsync();
|
|
|
|
|
|
|
|
|
|
// Wipe daily snapshots except for the first day
|
|
|
|
|
await WipeDailySnapshotsExceptFirstAsync();
|
|
|
|
|
|
|
|
|
|
@@ -76,10 +79,28 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable
|
|
|
|
|
// Create initial empty daily snapshot if none exist
|
|
|
|
|
if (!_state.State.DailySnapshots.Any())
|
|
|
|
|
{
|
|
|
|
|
var today = DateTime.UtcNow.Date.AddSeconds(1); // Today at 00:00:01 UTC
|
|
|
|
|
// Find the first position date to determine when to create the initial snapshot
|
|
|
|
|
var positions = await _tradingService.GetAllDatabasePositionsAsync();
|
|
|
|
|
DateTime initialSnapshotDate;
|
|
|
|
|
|
|
|
|
|
if (positions.Any())
|
|
|
|
|
{
|
|
|
|
|
// Create initial snapshot on the day BEFORE the first position
|
|
|
|
|
var firstPositionDate = positions.Min(p => p.Date).Date;
|
|
|
|
|
initialSnapshotDate = firstPositionDate.AddDays(-1);
|
|
|
|
|
_logger.LogInformation("First position found on {FirstPositionDate}, creating initial snapshot on {InitialSnapshotDate}",
|
|
|
|
|
firstPositionDate, initialSnapshotDate);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// No positions yet, create snapshot for today
|
|
|
|
|
initialSnapshotDate = DateTime.UtcNow.Date;
|
|
|
|
|
_logger.LogInformation("No positions found, creating initial snapshot for today: {InitialSnapshotDate}", initialSnapshotDate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var initialSnapshot = new DailySnapshot
|
|
|
|
|
{
|
|
|
|
|
Date = today,
|
|
|
|
|
Date = initialSnapshotDate,
|
|
|
|
|
TotalAgents = 0,
|
|
|
|
|
TotalStrategies = 0,
|
|
|
|
|
TotalVolume = 0,
|
|
|
|
|
@@ -94,7 +115,7 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable
|
|
|
|
|
_state.State.LastSnapshot = initialSnapshot.Date;
|
|
|
|
|
_state.State.LastUpdated = initialSnapshot.Date;
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("Created initial empty daily snapshot for {Date}", today);
|
|
|
|
|
_logger.LogInformation("Created initial empty daily snapshot for {Date}", initialSnapshotDate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await RefreshDataAsync();
|
|
|
|
|
@@ -138,6 +159,7 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable
|
|
|
|
|
|
|
|
|
|
// Calculate all metrics from positions in a single loop
|
|
|
|
|
var newVolume = 0m; // Volume from positions after last snapshot
|
|
|
|
|
var totalVolumeFromAllPositions = 0m; // Total volume calculated from ALL positions (for comparison)
|
|
|
|
|
var totalFees = 0m;
|
|
|
|
|
var totalPnL = 0m;
|
|
|
|
|
var totalOpenInterest = 0m;
|
|
|
|
|
@@ -155,15 +177,24 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable
|
|
|
|
|
// Calculate volume using the dedicated method
|
|
|
|
|
var positionVolume = TradingHelpers.GetVolumeForPosition(position);
|
|
|
|
|
|
|
|
|
|
// Track total volume from ALL positions for debugging
|
|
|
|
|
totalVolumeFromAllPositions += positionVolume;
|
|
|
|
|
|
|
|
|
|
// For cumulative volume: only add volume from positions created AFTER last snapshot
|
|
|
|
|
// For volume breakdown: include all positions
|
|
|
|
|
if (position.Date.Date > lastSnapshotDate)
|
|
|
|
|
{
|
|
|
|
|
newVolume += positionVolume;
|
|
|
|
|
_logger.LogDebug(
|
|
|
|
|
_logger.LogInformation(
|
|
|
|
|
"Position {PositionId} created after last snapshot ({PositionDate} > {LastSnapshotDate}), adding volume: {Volume}",
|
|
|
|
|
position.Identifier, position.Date.Date, lastSnapshotDate, positionVolume);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_logger.LogDebug(
|
|
|
|
|
"Position {PositionId} created before/on last snapshot ({PositionDate} <= {LastSnapshotDate}), skipping volume: {Volume}",
|
|
|
|
|
position.Identifier, position.Date.Date, lastSnapshotDate, positionVolume);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Calculate breakdown metrics from ALL positions (for current state)
|
|
|
|
|
var ticker = position.Ticker;
|
|
|
|
|
@@ -210,19 +241,30 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable
|
|
|
|
|
// CUMULATIVE volume: baseline + new volume since last snapshot
|
|
|
|
|
var updatedCumulativeVolume = cumulativeVolume + newVolume;
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("Volume calculation: Base={BaseVolume}, New={NewVolume}, Total={TotalVolume}",
|
|
|
|
|
cumulativeVolume, newVolume, updatedCumulativeVolume);
|
|
|
|
|
_logger.LogWarning(
|
|
|
|
|
"Volume calculation DEBUG: TotalFromAllPositions={TotalFromAll}, LastSnapshotVolume={LastSnapshot}, NewVolumeSinceSnapshot={NewVolume}, CalculatedCumulative={Cumulative}, CurrentState={CurrentState}",
|
|
|
|
|
totalVolumeFromAllPositions, cumulativeVolume, newVolume, updatedCumulativeVolume,
|
|
|
|
|
_state.State.TotalPlatformVolume);
|
|
|
|
|
|
|
|
|
|
// 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 (updatedCumulativeVolume < _state.State.TotalPlatformVolume)
|
|
|
|
|
if (correctCumulativeVolume < _state.State.TotalPlatformVolume)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning(
|
|
|
|
|
"Calculated cumulative volume ({Calculated}) is less than current volume ({Current}). Keeping current value.",
|
|
|
|
|
updatedCumulativeVolume, _state.State.TotalPlatformVolume);
|
|
|
|
|
updatedCumulativeVolume = _state.State.TotalPlatformVolume;
|
|
|
|
|
"Calculated volume ({Calculated}) is less than current volume ({Current}). Keeping current value to maintain cumulative nature.",
|
|
|
|
|
correctCumulativeVolume, _state.State.TotalPlatformVolume);
|
|
|
|
|
correctCumulativeVolume = _state.State.TotalPlatformVolume;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_state.State.TotalPlatformVolume = updatedCumulativeVolume;
|
|
|
|
|
_state.State.TotalPlatformVolume = correctCumulativeVolume;
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("Final volume set to: {FinalVolume}", correctCumulativeVolume);
|
|
|
|
|
_state.State.TotalPlatformFees = totalFees;
|
|
|
|
|
_state.State.TotalPlatformPnL = totalPnL;
|
|
|
|
|
_state.State.NetPnL = totalPnL - totalFees; // Calculate NetPnL
|
|
|
|
|
@@ -368,6 +410,67 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable
|
|
|
|
|
return timeSinceLastUpdate > TimeSpan.FromMinutes(5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Fixes the first snapshot date to ensure it's the day before the first position
|
|
|
|
|
/// </summary>
|
|
|
|
|
private async Task FixFirstSnapshotDateAsync()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (!_state.State.DailySnapshots.Any())
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("No daily snapshots to fix");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var positions = await _tradingService.GetAllDatabasePositionsAsync();
|
|
|
|
|
if (!positions.Any())
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("No positions found, cannot determine correct first snapshot date");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var firstPositionDate = positions.Min(p => p.Date).Date;
|
|
|
|
|
var correctFirstSnapshotDate = firstPositionDate.AddDays(-1);
|
|
|
|
|
var currentFirstSnapshot = _state.State.DailySnapshots.OrderBy(s => s.Date).First();
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation(
|
|
|
|
|
"First position date: {FirstPositionDate}, Current first snapshot: {CurrentFirstSnapshot}, Correct first snapshot should be: {CorrectFirstSnapshot}",
|
|
|
|
|
firstPositionDate, currentFirstSnapshot.Date, correctFirstSnapshotDate);
|
|
|
|
|
|
|
|
|
|
// If the first snapshot date is wrong, fix it
|
|
|
|
|
if (currentFirstSnapshot.Date.Date != correctFirstSnapshotDate.Date)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning(
|
|
|
|
|
"First snapshot date {CurrentDate} is incorrect. Should be {CorrectDate} (day before first position). Fixing...",
|
|
|
|
|
currentFirstSnapshot.Date, correctFirstSnapshotDate);
|
|
|
|
|
|
|
|
|
|
// Update the first snapshot date
|
|
|
|
|
currentFirstSnapshot.Date = correctFirstSnapshotDate;
|
|
|
|
|
|
|
|
|
|
// Reset all metrics to 0 for the first snapshot (it's before any activity)
|
|
|
|
|
currentFirstSnapshot.TotalVolume = 0;
|
|
|
|
|
currentFirstSnapshot.TotalPnL = 0;
|
|
|
|
|
currentFirstSnapshot.NetPnL = 0;
|
|
|
|
|
currentFirstSnapshot.TotalOpenInterest = 0;
|
|
|
|
|
currentFirstSnapshot.TotalLifetimePositionCount = 0;
|
|
|
|
|
currentFirstSnapshot.TotalPlatformFees = 0;
|
|
|
|
|
|
|
|
|
|
await _state.WriteStateAsync();
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("Fixed first snapshot date to {CorrectDate}", correctFirstSnapshotDate);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("First snapshot date is correct");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogError(ex, "Error fixing first snapshot date");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Wipes all daily snapshots except for the first day
|
|
|
|
|
/// </summary>
|
|
|
|
|
@@ -485,6 +588,7 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Calculates a CUMULATIVE daily snapshot from positions up to a specific date
|
|
|
|
|
/// Based on SQL query logic: OpenVolume + ClosingVolume per position
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="positions">All positions to analyze</param>
|
|
|
|
|
/// <param name="targetDate">The date to calculate the snapshot up to</param>
|
|
|
|
|
@@ -492,127 +596,96 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable
|
|
|
|
|
private async Task<DailySnapshot> CalculateDailySnapshotFromPositionsAsync(List<Position> positions,
|
|
|
|
|
DateTime targetDate)
|
|
|
|
|
{
|
|
|
|
|
var dayStart = targetDate;
|
|
|
|
|
var dayEnd = targetDate.AddDays(1);
|
|
|
|
|
|
|
|
|
|
// For CUMULATIVE daily snapshots, we need to consider ALL positions to calculate:
|
|
|
|
|
// 1. TOTAL volume from all trades that occurred on or before this day
|
|
|
|
|
// 2. Open interest from positions that were active during this day
|
|
|
|
|
// 3. Cumulative position count up to this date
|
|
|
|
|
// So we'll process all positions and include relevant data cumulatively
|
|
|
|
|
|
|
|
|
|
// Calculate metrics for this specific day
|
|
|
|
|
// Calculate CUMULATIVE metrics: sum of ALL volume/PnL from positions opened on or before target date
|
|
|
|
|
var totalVolume = 0m;
|
|
|
|
|
var totalFees = 0m;
|
|
|
|
|
var totalPnL = 0m;
|
|
|
|
|
var maxOpenInterest = 0m;
|
|
|
|
|
var currentOpenInterest = 0m;
|
|
|
|
|
var totalPositionCount = 0;
|
|
|
|
|
|
|
|
|
|
// Calculate open interest at different points during the day to find the maximum
|
|
|
|
|
var hourlyOpenInterest = new List<decimal>();
|
|
|
|
|
|
|
|
|
|
// Check open interest at each hour of the day (0-23)
|
|
|
|
|
for (int hour = 0; hour < 24; hour++)
|
|
|
|
|
{
|
|
|
|
|
var hourDateTime = targetDate.AddHours(hour);
|
|
|
|
|
var hourlyOI = 0m;
|
|
|
|
|
_logger.LogInformation("Calculating snapshot for {TargetDate} with {PositionCount} positions",
|
|
|
|
|
targetDate, positions.Count);
|
|
|
|
|
|
|
|
|
|
foreach (var position in positions)
|
|
|
|
|
{
|
|
|
|
|
// Check if position was active at this hour
|
|
|
|
|
var wasActiveAtThisHour = position.Date <= hourDateTime &&
|
|
|
|
|
(position.IsOpen() ||
|
|
|
|
|
(position.StopLoss.Status == TradeStatus.Filled &&
|
|
|
|
|
position.StopLoss.Date > hourDateTime) ||
|
|
|
|
|
(position.TakeProfit1.Status == TradeStatus.Filled &&
|
|
|
|
|
position.TakeProfit1.Date > hourDateTime) ||
|
|
|
|
|
(position.TakeProfit2 != null &&
|
|
|
|
|
position.TakeProfit2.Status == TradeStatus.Filled &&
|
|
|
|
|
position.TakeProfit2.Date > hourDateTime));
|
|
|
|
|
|
|
|
|
|
if (wasActiveAtThisHour)
|
|
|
|
|
// Only include positions that were OPENED on or before the target date
|
|
|
|
|
if (position.Date.Date > targetDate)
|
|
|
|
|
{
|
|
|
|
|
// For open interest, only count the opening volume (not closing trades)
|
|
|
|
|
var openingVolume = position.Open.Price * position.Open.Quantity * position.Open.Leverage;
|
|
|
|
|
hourlyOI += openingVolume;
|
|
|
|
|
}
|
|
|
|
|
_logger.LogDebug("Position {PositionId} opened after target date ({PositionDate} > {TargetDate}), skipping",
|
|
|
|
|
position.Identifier, position.Date.Date, targetDate);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hourlyOpenInterest.Add(hourlyOI);
|
|
|
|
|
}
|
|
|
|
|
// Count this position in the cumulative count
|
|
|
|
|
totalPositionCount++;
|
|
|
|
|
|
|
|
|
|
// Find the maximum open interest during the day
|
|
|
|
|
maxOpenInterest = hourlyOpenInterest.Max();
|
|
|
|
|
// Calculate volume for this position using SQL logic:
|
|
|
|
|
// OpenVolume = Price * Quantity * Leverage (from opening trade)
|
|
|
|
|
var openVolume = position.Open.Price * position.Open.Quantity * position.Open.Leverage;
|
|
|
|
|
var closingVolume = 0m;
|
|
|
|
|
|
|
|
|
|
foreach (var position in positions)
|
|
|
|
|
// ClosingVolume = Sum of all filled closing trades (SL, TP1, TP2) that happened on or before target date
|
|
|
|
|
if (position.Status == PositionStatus.Finished || position.Status == PositionStatus.Flipped)
|
|
|
|
|
{
|
|
|
|
|
// Calculate CUMULATIVE volume up to this point in time
|
|
|
|
|
// Include all positions that were opened on or before the target date
|
|
|
|
|
// Stop Loss volume (if filled and on or before target date)
|
|
|
|
|
if (position.StopLoss?.Status == TradeStatus.Filled && position.StopLoss.Date.Date <= targetDate)
|
|
|
|
|
{
|
|
|
|
|
closingVolume += position.StopLoss.Price * position.StopLoss.Quantity * position.StopLoss.Leverage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Take Profit 1 volume (if filled and on or before target date)
|
|
|
|
|
if (position.TakeProfit1?.Status == TradeStatus.Filled && position.TakeProfit1.Date.Date <= targetDate)
|
|
|
|
|
{
|
|
|
|
|
closingVolume += position.TakeProfit1.Price * position.TakeProfit1.Quantity * position.TakeProfit1.Leverage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Take Profit 2 volume (if filled and on or before target date)
|
|
|
|
|
if (position.TakeProfit2?.Status == TradeStatus.Filled && position.TakeProfit2.Date.Date <= targetDate)
|
|
|
|
|
{
|
|
|
|
|
closingVolume += position.TakeProfit2.Price * position.TakeProfit2.Quantity * position.TakeProfit2.Leverage;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Total volume for this position = opening + closing
|
|
|
|
|
var positionVolume = openVolume + closingVolume;
|
|
|
|
|
totalVolume += positionVolume;
|
|
|
|
|
|
|
|
|
|
_logger.LogDebug(
|
|
|
|
|
"Checking position {PositionId}: Position.Date={PositionDate}, TargetDate={TargetDate}, Position.Date.Date={PositionDateOnly}",
|
|
|
|
|
position.Identifier, position.Date, targetDate, position.Date.Date);
|
|
|
|
|
"Position {PositionId} (opened {OpenDate}): OpenVolume={OpenVol}, ClosingVolume={CloseVol}, Total={Total}",
|
|
|
|
|
position.Identifier, position.Date.Date, openVolume, closingVolume, positionVolume);
|
|
|
|
|
|
|
|
|
|
// Add opening volume if position was opened on or before this day
|
|
|
|
|
// Use more flexible date comparison to handle timezone differences
|
|
|
|
|
if (position.Date.Date <= targetDate)
|
|
|
|
|
// Calculate open interest (only for positions that are still open on target date)
|
|
|
|
|
if (position.IsOpen())
|
|
|
|
|
{
|
|
|
|
|
var openingVolume = position.Open.Price * position.Open.Quantity * position.Open.Leverage;
|
|
|
|
|
totalVolume += openingVolume;
|
|
|
|
|
_logger.LogDebug(
|
|
|
|
|
"Position {PositionId} opened on/before {TargetDate}: Opening volume = {OpeningVolume}",
|
|
|
|
|
position.Identifier, targetDate, openingVolume);
|
|
|
|
|
currentOpenInterest += openVolume;
|
|
|
|
|
}
|
|
|
|
|
else if (position.Status == PositionStatus.Finished || position.Status == PositionStatus.Flipped)
|
|
|
|
|
{
|
|
|
|
|
// Check if position was still open on the target date (closed after target date)
|
|
|
|
|
var closedAfterTargetDate =
|
|
|
|
|
(position.StopLoss?.Status == TradeStatus.Filled && position.StopLoss.Date.Date > targetDate) ||
|
|
|
|
|
(position.TakeProfit1?.Status == TradeStatus.Filled && position.TakeProfit1.Date.Date > targetDate) ||
|
|
|
|
|
(position.TakeProfit2?.Status == TradeStatus.Filled && position.TakeProfit2.Date.Date > targetDate);
|
|
|
|
|
|
|
|
|
|
// Add closing volume if position was closed on or before this day
|
|
|
|
|
if (position.IsValidForMetrics())
|
|
|
|
|
if (closedAfterTargetDate)
|
|
|
|
|
{
|
|
|
|
|
if (position.StopLoss.Status == TradeStatus.Filled && position.StopLoss.Date.Date <= targetDate)
|
|
|
|
|
{
|
|
|
|
|
var closingVolume = position.StopLoss.Price * position.StopLoss.Quantity *
|
|
|
|
|
position.StopLoss.Leverage;
|
|
|
|
|
totalVolume += closingVolume;
|
|
|
|
|
_logger.LogDebug(
|
|
|
|
|
"Position {PositionId} closed on/before {TargetDate} via StopLoss: Closing volume = {ClosingVolume}",
|
|
|
|
|
position.Identifier, targetDate, closingVolume);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (position.TakeProfit1.Status == TradeStatus.Filled && position.TakeProfit1.Date.Date <= targetDate)
|
|
|
|
|
{
|
|
|
|
|
var closingVolume = position.TakeProfit1.Price * position.TakeProfit1.Quantity *
|
|
|
|
|
position.TakeProfit1.Leverage;
|
|
|
|
|
totalVolume += closingVolume;
|
|
|
|
|
_logger.LogDebug(
|
|
|
|
|
"Position {PositionId} closed on/before {TargetDate} via TakeProfit1: Closing volume = {ClosingVolume}",
|
|
|
|
|
position.Identifier, targetDate, closingVolume);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (position.TakeProfit2 != null && position.TakeProfit2.Status == TradeStatus.Filled &&
|
|
|
|
|
position.TakeProfit2.Date.Date <= targetDate)
|
|
|
|
|
{
|
|
|
|
|
var closingVolume = position.TakeProfit2.Price * position.TakeProfit2.Quantity *
|
|
|
|
|
position.TakeProfit2.Leverage;
|
|
|
|
|
totalVolume += closingVolume;
|
|
|
|
|
currentOpenInterest += openVolume;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Calculate CUMULATIVE fees and PnL for positions closed on or before this day
|
|
|
|
|
var wasClosedOnOrBeforeThisDay = position.IsValidForMetrics() && (
|
|
|
|
|
(position.StopLoss.Status == TradeStatus.Filled && position.StopLoss.Date.Date <= targetDate) ||
|
|
|
|
|
(position.TakeProfit1.Status == TradeStatus.Filled && position.TakeProfit1.Date.Date <= targetDate) ||
|
|
|
|
|
(position.TakeProfit2 != null && position.TakeProfit2.Status == TradeStatus.Filled &&
|
|
|
|
|
position.TakeProfit2.Date.Date <= targetDate)
|
|
|
|
|
);
|
|
|
|
|
// Calculate fees and PnL for FINISHED positions (only if closed on or before target date)
|
|
|
|
|
if (position.Status == PositionStatus.Finished || position.Status == PositionStatus.Flipped)
|
|
|
|
|
{
|
|
|
|
|
var wasClosedByTargetDate =
|
|
|
|
|
(position.StopLoss?.Status == TradeStatus.Filled && position.StopLoss.Date.Date <= targetDate) ||
|
|
|
|
|
(position.TakeProfit1?.Status == TradeStatus.Filled && position.TakeProfit1.Date.Date <= targetDate) ||
|
|
|
|
|
(position.TakeProfit2?.Status == TradeStatus.Filled && position.TakeProfit2.Date.Date <= targetDate);
|
|
|
|
|
|
|
|
|
|
if (wasClosedOnOrBeforeThisDay)
|
|
|
|
|
if (wasClosedByTargetDate)
|
|
|
|
|
{
|
|
|
|
|
totalFees += position.CalculateTotalFees();
|
|
|
|
|
totalPnL += position.ProfitAndLoss?.Realized ?? 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Count positions that were created on or before this day (CUMULATIVE position count)
|
|
|
|
|
if (position.Date.Date <= targetDate)
|
|
|
|
|
{
|
|
|
|
|
totalPositionCount++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -621,8 +694,8 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable
|
|
|
|
|
var totalStrategies = _state.State.TotalActiveStrategies;
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation(
|
|
|
|
|
"Calculated CUMULATIVE daily snapshot for {TargetDate}: CumVolume={TotalVolume}, MaxOpenInterest={MaxOpenInterest}, CumPositionCount={TotalPositionCount}",
|
|
|
|
|
targetDate, totalVolume, maxOpenInterest, totalPositionCount);
|
|
|
|
|
"Calculated CUMULATIVE snapshot for {TargetDate}: CumVolume={TotalVolume}, OpenInterest={OpenInterest}, CumPositionCount={TotalPositionCount}, Fees={Fees}, PnL={PnL}",
|
|
|
|
|
targetDate, totalVolume, currentOpenInterest, totalPositionCount, totalFees, totalPnL);
|
|
|
|
|
|
|
|
|
|
return new DailySnapshot
|
|
|
|
|
{
|
|
|
|
|
@@ -632,7 +705,7 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable
|
|
|
|
|
TotalVolume = totalVolume,
|
|
|
|
|
TotalPnL = totalPnL,
|
|
|
|
|
NetPnL = totalPnL - totalFees,
|
|
|
|
|
TotalOpenInterest = maxOpenInterest,
|
|
|
|
|
TotalOpenInterest = currentOpenInterest,
|
|
|
|
|
TotalLifetimePositionCount = totalPositionCount,
|
|
|
|
|
TotalPlatformFees = (int)totalFees,
|
|
|
|
|
};
|
|
|
|
|
|