Fix get Balance

This commit is contained in:
2025-10-20 16:20:36 +07:00
parent f24938114d
commit 79f07af899
4 changed files with 193 additions and 119 deletions

View File

@@ -361,7 +361,7 @@ public class AgentGrain : Grain, IAgentGrain
// Get or refresh cached balance data
var balanceData = await GetOrRefreshBalanceDataAsync(accountName);
if (balanceData == null)
if (balanceData == null || balanceData.IsValid == false)
{
_logger.LogError("Failed to get balance data for account {AccountName}, user {UserId}",
accountName, this.GetPrimaryKeyLong());
@@ -645,7 +645,7 @@ public class AgentGrain : Grain, IAgentGrain
{
_logger.LogError(ex, "Error fetching balance data for account {AccountName}, user {UserId}",
accountName, this.GetPrimaryKeyLong());
return null;
throw;
}
}

View File

@@ -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;
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)
{
// For open interest, only count the opening volume (not closing trades)
var openingVolume = position.Open.Price * position.Open.Quantity * position.Open.Leverage;
hourlyOI += openingVolume;
}
}
hourlyOpenInterest.Add(hourlyOI);
}
// Find the maximum open interest during the day
maxOpenInterest = hourlyOpenInterest.Max();
_logger.LogInformation("Calculating snapshot for {TargetDate} with {PositionCount} positions",
targetDate, positions.Count);
foreach (var position in positions)
{
// Calculate CUMULATIVE volume up to this point in time
// Include all positions that were opened on or before the target date
// Only include positions that were OPENED on or before the target date
if (position.Date.Date > targetDate)
{
_logger.LogDebug("Position {PositionId} opened after target date ({PositionDate} > {TargetDate}), skipping",
position.Identifier, position.Date.Date, targetDate);
continue;
}
// Count this position in the cumulative count
totalPositionCount++;
// 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;
// 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)
{
// 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;
}
// Add closing volume if position was closed on or before this day
if (position.IsValidForMetrics())
else if (position.Status == PositionStatus.Finished || position.Status == PositionStatus.Flipped)
{
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);
}
// 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);
if (position.TakeProfit1.Status == TradeStatus.Filled && position.TakeProfit1.Date.Date <= targetDate)
if (closedAfterTargetDate)
{
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)
);
if (wasClosedOnOrBeforeThisDay)
// Calculate fees and PnL for FINISHED positions (only if closed on or before target date)
if (position.Status == PositionStatus.Finished || position.Status == PositionStatus.Flipped)
{
totalFees += position.CalculateTotalFees();
totalPnL += position.ProfitAndLoss?.Realized ?? 0;
}
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);
// Count positions that were created on or before this day (CUMULATIVE position count)
if (position.Date.Date <= targetDate)
{
totalPositionCount++;
if (wasClosedByTargetDate)
{
totalFees += position.CalculateTotalFees();
totalPnL += position.ProfitAndLoss?.Realized ?? 0;
}
}
}
@@ -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,
};

View File

@@ -26,7 +26,7 @@ public static class ChainService
var chains = new List<Chain>()
{
GetArbitrum(),
GetEthereum(),
// GetEthereum(),
//GetArbitrumGoerli(),
//GetGoerli()
};

View File

@@ -80,7 +80,8 @@ public static class TokenService
Ticker.AAVE,
Ticker.XRP,
Ticker.PENDLE,
Ticker.BNB
Ticker.BNB,
Ticker.USDC
};
}
}