Fix ROI calculation for Strategy

This commit is contained in:
2025-10-08 19:37:24 +07:00
parent 76b087a6e4
commit 1a99224d18
5 changed files with 41 additions and 36 deletions

View File

@@ -182,8 +182,8 @@ public class AgentGrain : Grain, IAgentGrain
_state.State.TotalFees = totalFees; _state.State.TotalFees = totalFees;
// Calculate wins/losses from position PnL // Calculate wins/losses from position PnL
var totalWins = 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?.Realized ?? 0) <= 0); var totalLosses = positions.Count(p => (p.ProfitAndLoss?.Net ?? 0) <= 0);
// Calculate ROI based on PnL minus fees // Calculate ROI based on PnL minus fees
var netPnL = totalPnL - totalFees; var netPnL = totalPnL - totalFees;

View File

@@ -791,12 +791,18 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
} }
} }
var positionForMetrics = _tradingBot.Positions.Where(p => p.Value.IsValidForMetrics()) var positionForMetrics = await ServiceScopeHelpers.WithScopedService<ITradingService, List<Position>>(
.Select(p => p.Value).ToList(); _scopeFactory,
async tradingService =>
{
return (await tradingService.GetPositionsByInitiatorIdentifierAsync(this.GetPrimaryKey()))
.Where(p => p.IsValidForMetrics()).ToList();
});
// Calculate statistics using TradingBox helpers // Calculate statistics using TradingBox helpers
var (tradeWins, tradeLosses) = TradingBox.GetWinLossCount(positionForMetrics); var (tradeWins, tradeLosses) = TradingBox.GetWinLossCount(positionForMetrics);
var pnl = _tradingBot.GetProfitAndLoss(); var pnl = positionForMetrics.Sum(p => p.ProfitAndLoss.Realized);
var fees = _tradingBot.GetTotalFees(); var fees = positionForMetrics.Sum(p => p.CalculateTotalFees());
var netPnl = pnl - fees; // Net PnL after fees var netPnl = pnl - fees; // Net PnL after fees
var volume = TradingBox.GetTotalVolumeTraded(positionForMetrics); var volume = TradingBox.GetTotalVolumeTraded(positionForMetrics);

View File

@@ -400,24 +400,27 @@ public class TradingBotBase : ITradingBot
} }
Position internalPosition = null; Position internalPosition = null;
List<Position> brokerPositions = null; var brokerPositions = await ServiceScopeHelpers.WithScopedService<ITradingService, List<Position>>(
await ServiceScopeHelpers.WithScopedService<ITradingService>(_scopeFactory, async tradingService => _scopeFactory, async tradingService =>
{ {
internalPosition = Config.IsForBacktest internalPosition = Config.IsForBacktest
? positionForSignal ? positionForSignal
: await tradingService.GetPositionByIdentifierAsync(positionForSignal.Identifier); : await tradingService.GetPositionByIdentifierAsync(positionForSignal.Identifier);
if (Config.IsForBacktest) if (Config.IsForBacktest)
{ {
brokerPositions = new List<Position> { internalPosition }; return new List<Position> { internalPosition };
} }
else else
{ {
brokerPositions = await ServiceScopeHelpers.WithScopedService<IExchangeService, List<Position>>( return await ServiceScopeHelpers.WithScopedService<IExchangeService, List<Position>>(
_scopeFactory, _scopeFactory,
async exchangeService => { return [.. await exchangeService.GetBrokerPositions(Account)]; }); async exchangeService =>
} {
}); return [.. await exchangeService.GetBrokerPositions(Account)];
});
}
});
if (!Config.IsForBacktest) if (!Config.IsForBacktest)
{ {
@@ -625,11 +628,11 @@ public class TradingBotBase : ITradingBot
lastCandle = Config.IsForBacktest lastCandle = Config.IsForBacktest
? LastCandle ? LastCandle
: await exchangeService.GetCandle(Account, Config.Ticker, : await exchangeService.GetCandle(Account, Config.Ticker,
DateTime.UtcNow); DateTime.UtcNow);
}); });
var currentTime = Config.IsForBacktest ? lastCandle.Date : 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 var pnlPercentage = positionForSignal.Open.Price * positionForSignal.Open.Quantity != 0
? Math.Round((currentPnl / (positionForSignal.Open.Price * positionForSignal.Open.Quantity)) * 100, ? Math.Round((currentPnl / (positionForSignal.Open.Price * positionForSignal.Open.Quantity)) * 100,
2) 2)
@@ -1592,11 +1595,11 @@ public class TradingBotBase : ITradingBot
if (position.Open?.Status == TradeStatus.Filled) if (position.Open?.Status == TradeStatus.Filled)
{ {
Logger.LogInformation( 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) if (position.ProfitAndLoss != null)
{ {
Config.BotTradingBalance += position.ProfitAndLoss.Realized; Config.BotTradingBalance += position.ProfitAndLoss.Net;
Logger.LogInformation( Logger.LogInformation(
string.Format("💰 Balance Updated\nNew bot trading balance: `${0:F2}`", string.Format("💰 Balance Updated\nNew bot trading balance: `${0:F2}`",

View File

@@ -142,13 +142,6 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable
var positionVolume = TradingHelpers.GetVolumeForPosition(position); var positionVolume = TradingHelpers.GetVolumeForPosition(position);
totalVolume += positionVolume; 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 // Calculate fees and PnL for all positions
totalFees += position.CalculateTotalFees(); totalFees += position.CalculateTotalFees();
totalPnL += position.ProfitAndLoss?.Realized ?? 0; totalPnL += position.ProfitAndLoss?.Realized ?? 0;
@@ -177,8 +170,11 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable
_state.State.PositionCountByAsset[ticker]++; _state.State.PositionCountByAsset[ticker]++;
// Position count breakdown by direction - only count finished positions // 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)) if (!_state.State.PositionCountByDirection.ContainsKey(direction))
{ {
_state.State.PositionCountByDirection[direction] = 0; _state.State.PositionCountByDirection[direction] = 0;

View File

@@ -4,7 +4,7 @@ import {closeGmxPositionImpl, getClientForAddress} from '../../src/plugins/custo
import {TradeDirection} from '../../src/generated/ManagingApiTypes' import {TradeDirection} from '../../src/generated/ManagingApiTypes'
test('GMX Position Closing', async (t) => { 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 sdk = await getClientForAddress('0x932167388dD9aad41149b3cA23eBD489E2E2DD78')
const result = await closeGmxPositionImpl( const result = await closeGmxPositionImpl(