From 3128e3e9d910e1514c974616780423784876499c Mon Sep 17 00:00:00 2001 From: cryptooda Date: Fri, 10 Oct 2025 02:48:50 +0700 Subject: [PATCH] Add cache for position --- .../Controllers/DataController.cs | 19 ++++++++++-- .../PostgreSql/PostgreSqlTradingRepository.cs | 30 ++++++++++++++----- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/Managing.Api/Controllers/DataController.cs b/src/Managing.Api/Controllers/DataController.cs index bbfda255..cec89f33 100644 --- a/src/Managing.Api/Controllers/DataController.cs +++ b/src/Managing.Api/Controllers/DataController.cs @@ -480,8 +480,23 @@ public class DataController : ControllerBase decimal totalVolume = strategy.Volume; decimal volumeLast24h = strategy.Volume; - // Fetch positions associated with this bot using the provided trading service - var positions = await tradingService.GetPositionsByInitiatorIdentifierAsync(strategy.Identifier); + // Use caching for position data in UI context (not critical trading operations) + var cacheKey = $"positions_{strategy.Identifier}"; + var cachedPositions = _cacheService.GetValue>(cacheKey); + + List positions; + if (cachedPositions != null) + { + positions = cachedPositions; + } + else + { + // Fetch positions associated with this bot using the provided trading service + positions = (await tradingService.GetPositionsByInitiatorIdentifierAsync(strategy.Identifier)).ToList(); + + // Cache positions for 2 minutes for UI display purposes + _cacheService.SaveValue(cacheKey, positions, TimeSpan.FromMinutes(2)); + } // Calculate win/loss statistics from actual positions (including open positions) int wins = positions.Count(p => p.ProfitAndLoss != null && p.ProfitAndLoss.Realized > 0); diff --git a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlTradingRepository.cs b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlTradingRepository.cs index e73be6cd..9a850b7d 100644 --- a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlTradingRepository.cs +++ b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlTradingRepository.cs @@ -273,17 +273,33 @@ public class PostgreSqlTradingRepository : BaseRepositoryWithLogging, ITradingRe { await PostgreSqlConnectionHelper.EnsureConnectionOpenAsync(_context); + // Optimized query with explicit JOINs to avoid N+1 queries var position = await _context.Positions .AsNoTracking() - .Include(p => p.User) - .Include(p => p.OpenTrade) - .Include(p => p.StopLossTrade) - .Include(p => p.TakeProfit1Trade) - .Include(p => p.TakeProfit2Trade) - .FirstOrDefaultAsync(p => p.Identifier == identifier) + .Where(p => p.Identifier == identifier) + .Select(p => new + { + Position = p, + User = p.User, + OpenTrade = p.OpenTrade, + StopLossTrade = p.StopLossTrade, + TakeProfit1Trade = p.TakeProfit1Trade, + TakeProfit2Trade = p.TakeProfit2Trade + }) + .FirstOrDefaultAsync() .ConfigureAwait(false); - return PostgreSqlMappers.Map(position ?? throw new InvalidOperationException("Position not found")); + if (position == null) + throw new InvalidOperationException("Position not found"); + + // Manually assign navigation properties to avoid lazy loading + position.Position.User = position.User; + position.Position.OpenTrade = position.OpenTrade; + position.Position.StopLossTrade = position.StopLossTrade; + position.Position.TakeProfit1Trade = position.TakeProfit1Trade; + position.Position.TakeProfit2Trade = position.TakeProfit2Trade; + + return PostgreSqlMappers.Map(position.Position); } finally {