From 1a99224d180e0990b148e7372f4503a3dffa82bb Mon Sep 17 00:00:00 2001 From: cryptooda Date: Wed, 8 Oct 2025 19:37:24 +0700 Subject: [PATCH] Fix ROI calculation for Strategy --- .../Bots/Grains/AgentGrain.cs | 4 +- .../Bots/Grains/LiveTradingBotGrain.cs | 14 ++++-- .../Bots/TradingBotBase.cs | 45 ++++++++++--------- .../Grains/PlatformSummaryGrain.cs | 12 ++--- .../test/plugins/close-position.test.ts | 2 +- 5 files changed, 41 insertions(+), 36 deletions(-) diff --git a/src/Managing.Application/Bots/Grains/AgentGrain.cs b/src/Managing.Application/Bots/Grains/AgentGrain.cs index 6831c78b..9cceb919 100644 --- a/src/Managing.Application/Bots/Grains/AgentGrain.cs +++ b/src/Managing.Application/Bots/Grains/AgentGrain.cs @@ -182,8 +182,8 @@ public class AgentGrain : Grain, IAgentGrain _state.State.TotalFees = totalFees; // Calculate wins/losses from position PnL - var totalWins = positions.Count(p => (p.ProfitAndLoss?.Realized ?? 0) > 0); - var totalLosses = positions.Count(p => (p.ProfitAndLoss?.Realized ?? 0) <= 0); + var totalWins = positions.Count(p => (p.ProfitAndLoss?.Net ?? 0) > 0); + var totalLosses = positions.Count(p => (p.ProfitAndLoss?.Net ?? 0) <= 0); // Calculate ROI based on PnL minus fees var netPnL = totalPnL - totalFees; diff --git a/src/Managing.Application/Bots/Grains/LiveTradingBotGrain.cs b/src/Managing.Application/Bots/Grains/LiveTradingBotGrain.cs index b6d64ac1..306e80a1 100644 --- a/src/Managing.Application/Bots/Grains/LiveTradingBotGrain.cs +++ b/src/Managing.Application/Bots/Grains/LiveTradingBotGrain.cs @@ -791,12 +791,18 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable } } - var positionForMetrics = _tradingBot.Positions.Where(p => p.Value.IsValidForMetrics()) - .Select(p => p.Value).ToList(); + var positionForMetrics = await ServiceScopeHelpers.WithScopedService>( + _scopeFactory, + async tradingService => + { + return (await tradingService.GetPositionsByInitiatorIdentifierAsync(this.GetPrimaryKey())) + .Where(p => p.IsValidForMetrics()).ToList(); + }); + // Calculate statistics using TradingBox helpers var (tradeWins, tradeLosses) = TradingBox.GetWinLossCount(positionForMetrics); - var pnl = _tradingBot.GetProfitAndLoss(); - var fees = _tradingBot.GetTotalFees(); + var pnl = positionForMetrics.Sum(p => p.ProfitAndLoss.Realized); + var fees = positionForMetrics.Sum(p => p.CalculateTotalFees()); var netPnl = pnl - fees; // Net PnL after fees var volume = TradingBox.GetTotalVolumeTraded(positionForMetrics); diff --git a/src/Managing.Application/Bots/TradingBotBase.cs b/src/Managing.Application/Bots/TradingBotBase.cs index 1fa962fa..3a740c09 100644 --- a/src/Managing.Application/Bots/TradingBotBase.cs +++ b/src/Managing.Application/Bots/TradingBotBase.cs @@ -400,24 +400,27 @@ public class TradingBotBase : ITradingBot } Position internalPosition = null; - List brokerPositions = null; - await ServiceScopeHelpers.WithScopedService(_scopeFactory, async tradingService => - { - internalPosition = Config.IsForBacktest - ? positionForSignal - : await tradingService.GetPositionByIdentifierAsync(positionForSignal.Identifier); + var brokerPositions = await ServiceScopeHelpers.WithScopedService>( + _scopeFactory, async tradingService => + { + internalPosition = Config.IsForBacktest + ? positionForSignal + : await tradingService.GetPositionByIdentifierAsync(positionForSignal.Identifier); - if (Config.IsForBacktest) - { - brokerPositions = new List { internalPosition }; - } - else - { - brokerPositions = await ServiceScopeHelpers.WithScopedService>( - _scopeFactory, - async exchangeService => { return [.. await exchangeService.GetBrokerPositions(Account)]; }); - } - }); + if (Config.IsForBacktest) + { + return new List { internalPosition }; + } + else + { + return await ServiceScopeHelpers.WithScopedService>( + _scopeFactory, + async exchangeService => + { + return [.. await exchangeService.GetBrokerPositions(Account)]; + }); + } + }); if (!Config.IsForBacktest) { @@ -625,11 +628,11 @@ public class TradingBotBase : ITradingBot lastCandle = Config.IsForBacktest ? LastCandle : await exchangeService.GetCandle(Account, Config.Ticker, - DateTime.UtcNow); + DateTime.UtcNow); }); var currentTime = Config.IsForBacktest ? lastCandle.Date : DateTime.UtcNow; - var currentPnl = positionForSignal.ProfitAndLoss?.Realized ?? 0; + var currentPnl = positionForSignal.ProfitAndLoss?.Net ?? 0; var pnlPercentage = positionForSignal.Open.Price * positionForSignal.Open.Quantity != 0 ? Math.Round((currentPnl / (positionForSignal.Open.Price * positionForSignal.Open.Quantity)) * 100, 2) @@ -1592,11 +1595,11 @@ public class TradingBotBase : ITradingBot if (position.Open?.Status == TradeStatus.Filled) { Logger.LogInformation( - $"✅ Position Closed Successfully\nPosition: `{position.SignalIdentifier}`\nPnL: `${position.ProfitAndLoss?.Realized:F2}`"); + $"✅ Position Closed Successfully\nPosition: `{position.SignalIdentifier}`\nPnL: `${position.ProfitAndLoss?.Net:F2}`"); if (position.ProfitAndLoss != null) { - Config.BotTradingBalance += position.ProfitAndLoss.Realized; + Config.BotTradingBalance += position.ProfitAndLoss.Net; Logger.LogInformation( string.Format("💰 Balance Updated\nNew bot trading balance: `${0:F2}`", diff --git a/src/Managing.Application/Grains/PlatformSummaryGrain.cs b/src/Managing.Application/Grains/PlatformSummaryGrain.cs index c91e872b..a27bfc80 100644 --- a/src/Managing.Application/Grains/PlatformSummaryGrain.cs +++ b/src/Managing.Application/Grains/PlatformSummaryGrain.cs @@ -142,13 +142,6 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable var positionVolume = TradingHelpers.GetVolumeForPosition(position); totalVolume += positionVolume; - // Add to open interest for active positions only (only opening volume) - if (position.Status.Equals(PositionStatus.Filled)) - { - var openingVolume = position.Open.Price * position.Open.Quantity * position.Open.Leverage; - totalOpenInterest += openingVolume; - } - // Calculate fees and PnL for all positions totalFees += position.CalculateTotalFees(); totalPnL += position.ProfitAndLoss?.Realized ?? 0; @@ -177,8 +170,11 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable _state.State.PositionCountByAsset[ticker]++; // Position count breakdown by direction - only count finished positions - if (position.IsValidForMetrics()) + if (position.IsOpen()) { + var openingVolume = position.Open.Price * position.Open.Quantity * position.Open.Leverage; + totalOpenInterest += openingVolume; + if (!_state.State.PositionCountByDirection.ContainsKey(direction)) { _state.State.PositionCountByDirection[direction] = 0; diff --git a/src/Managing.Web3Proxy/test/plugins/close-position.test.ts b/src/Managing.Web3Proxy/test/plugins/close-position.test.ts index 653e167e..4338a017 100644 --- a/src/Managing.Web3Proxy/test/plugins/close-position.test.ts +++ b/src/Managing.Web3Proxy/test/plugins/close-position.test.ts @@ -4,7 +4,7 @@ import {closeGmxPositionImpl, getClientForAddress} from '../../src/plugins/custo import {TradeDirection} from '../../src/generated/ManagingApiTypes' test('GMX Position Closing', async (t) => { - await t.test('should close a long position for BTC', async () => { + await t.test('should close position', async () => { const sdk = await getClientForAddress('0x932167388dD9aad41149b3cA23eBD489E2E2DD78') const result = await closeGmxPositionImpl(