Fix realized pnl on backtest save + add tests (not all passing)
This commit is contained in:
@@ -21,9 +21,6 @@ namespace Managing.Application.Abstractions
|
||||
|
||||
Task Run();
|
||||
Task StopBot(string reason = null);
|
||||
int GetWinRate();
|
||||
decimal GetProfitAndLoss();
|
||||
decimal GetTotalFees();
|
||||
Task LoadAccount();
|
||||
Task LoadLastCandle();
|
||||
Task<LightSignal> CreateManualSignal(TradeDirection direction);
|
||||
|
||||
@@ -339,15 +339,16 @@ public class BacktestExecutor
|
||||
// Start result calculation timing
|
||||
var resultCalculationStart = Stopwatch.GetTimestamp();
|
||||
|
||||
// Calculate final results (using existing optimized methods)
|
||||
var netPnl = tradingBot.GetProfitAndLoss(); // This returns Net PnL (after fees)
|
||||
var winRate = tradingBot.GetWinRate();
|
||||
var stats = TradingHelpers.GetStatistics(tradingBot.WalletBalances);
|
||||
// Calculate final results using static methods from TradingBox
|
||||
var realizedPnl = TradingBox.GetTotalRealizedPnL(tradingBot.Positions); // PnL before fees
|
||||
var netPnl = TradingBox.GetTotalNetPnL(tradingBot.Positions); // PnL after fees
|
||||
var winRate = TradingBox.GetWinRate(tradingBot.Positions);
|
||||
var stats = TradingBox.GetStatistics(tradingBot.WalletBalances);
|
||||
var growthPercentage =
|
||||
TradingHelpers.GetGrowthFromInitalBalance(tradingBot.WalletBalances.FirstOrDefault().Value, netPnl);
|
||||
var hodlPercentage = TradingHelpers.GetHodlPercentage(candles.First(), candles.Last());
|
||||
TradingBox.GetGrowthFromInitalBalance(tradingBot.WalletBalances.FirstOrDefault().Value, netPnl);
|
||||
var hodlPercentage = TradingBox.GetHodlPercentage(candles.First(), candles.Last());
|
||||
|
||||
var fees = tradingBot.GetTotalFees();
|
||||
var fees = TradingBox.GetTotalFees(tradingBot.Positions);
|
||||
var scoringParams = new BacktestScoringParams(
|
||||
sharpeRatio: (double)stats.SharpeRatio,
|
||||
growthPercentage: (double)growthPercentage,
|
||||
@@ -383,7 +384,7 @@ public class BacktestExecutor
|
||||
var result = new Backtest(config, tradingBot.Positions, tradingBot.Signals,
|
||||
withCandles ? candles : new HashSet<Candle>())
|
||||
{
|
||||
FinalPnl = netPnl, // Net PnL (after fees)
|
||||
FinalPnl = realizedPnl, // Realized PnL before fees
|
||||
WinRate = winRate,
|
||||
GrowthPercentage = growthPercentage,
|
||||
HodlPercentage = hodlPercentage,
|
||||
@@ -398,7 +399,7 @@ public class BacktestExecutor
|
||||
StartDate = candles.FirstOrDefault()!.OpenTime,
|
||||
EndDate = candles.LastOrDefault()!.OpenTime,
|
||||
InitialBalance = initialBalance,
|
||||
NetPnl = netPnl, // Already net of fees
|
||||
NetPnl = netPnl, // Net PnL after fees
|
||||
};
|
||||
|
||||
if (save && user != null)
|
||||
|
||||
@@ -132,14 +132,14 @@ public class BacktestTradingBotGrain : Grain, IBacktestTradingBotGrain
|
||||
|
||||
_logger.LogInformation("Backtest processing completed. Calculating final results...");
|
||||
|
||||
var finalPnl = tradingBot.GetProfitAndLoss();
|
||||
var winRate = tradingBot.GetWinRate();
|
||||
var stats = TradingHelpers.GetStatistics(tradingBot.WalletBalances);
|
||||
var finalPnl = TradingBox.GetTotalNetPnL(tradingBot.Positions);
|
||||
var winRate = TradingBox.GetWinRate(tradingBot.Positions);
|
||||
var stats = TradingBox.GetStatistics(tradingBot.WalletBalances);
|
||||
var growthPercentage =
|
||||
TradingHelpers.GetGrowthFromInitalBalance(tradingBot.WalletBalances.FirstOrDefault().Value, finalPnl);
|
||||
var hodlPercentage = TradingHelpers.GetHodlPercentage(candles.First(), candles.Last());
|
||||
TradingBox.GetGrowthFromInitalBalance(tradingBot.WalletBalances.FirstOrDefault().Value, finalPnl);
|
||||
var hodlPercentage = TradingBox.GetHodlPercentage(candles.First(), candles.Last());
|
||||
|
||||
var fees = tradingBot.GetTotalFees();
|
||||
var fees = TradingBox.GetTotalFees(tradingBot.Positions);
|
||||
var scoringParams = new BacktestScoringParams(
|
||||
sharpeRatio: (double)stats.SharpeRatio,
|
||||
growthPercentage: (double)growthPercentage,
|
||||
|
||||
@@ -557,8 +557,8 @@ public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
|
||||
Positions = _tradingBot.Positions,
|
||||
Signals = _tradingBot.Signals,
|
||||
WalletBalances = _tradingBot.WalletBalances,
|
||||
ProfitAndLoss = _tradingBot.GetProfitAndLoss(),
|
||||
WinRate = _tradingBot.GetWinRate(),
|
||||
ProfitAndLoss = TradingBox.GetTotalNetPnL(_tradingBot.Positions),
|
||||
WinRate = TradingBox.GetWinRate(_tradingBot.Positions),
|
||||
ExecutionCount = _state.State.ExecutionCount,
|
||||
StartupTime = _state.State.StartupTime,
|
||||
CreateDate = _state.State.CreateDate
|
||||
|
||||
@@ -460,7 +460,7 @@ public class TradingBotBase : ITradingBot
|
||||
if (!WalletBalances.ContainsKey(date))
|
||||
{
|
||||
var previousBalance = WalletBalances.First().Value;
|
||||
WalletBalances[date] = previousBalance + GetProfitAndLoss();
|
||||
WalletBalances[date] = previousBalance + TradingBox.GetTotalNetPnL(Positions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1501,7 +1501,7 @@ public class TradingBotBase : ITradingBot
|
||||
var closingVolume = brokerPosition.Open.Price * position.Open.Quantity *
|
||||
position.Open.Leverage;
|
||||
var totalBotFees = position.GasFees + position.UiFees +
|
||||
TradingHelpers.CalculateClosingUiFees(closingVolume);
|
||||
TradingBox.CalculateClosingUiFees(closingVolume);
|
||||
var gmxNetPnl = brokerPosition.ProfitAndLoss.Realized; // This is already after GMX fees
|
||||
|
||||
position.ProfitAndLoss = new ProfitAndLoss
|
||||
@@ -2099,67 +2099,6 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
}
|
||||
|
||||
public int GetWinRate()
|
||||
{
|
||||
// Optimized: Single iteration instead of multiple LINQ queries
|
||||
int succeededPositions = 0;
|
||||
int totalPositions = 0;
|
||||
|
||||
foreach (var position in Positions.Values)
|
||||
{
|
||||
if (position.IsValidForMetrics())
|
||||
{
|
||||
totalPositions++;
|
||||
if (position.IsInProfit())
|
||||
{
|
||||
succeededPositions++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (totalPositions == 0)
|
||||
return 0;
|
||||
|
||||
return (succeededPositions * 100) / totalPositions;
|
||||
}
|
||||
|
||||
public decimal GetProfitAndLoss()
|
||||
{
|
||||
// Optimized: Single iteration instead of LINQ chaining
|
||||
decimal netPnl = 0;
|
||||
|
||||
foreach (var position in Positions.Values)
|
||||
{
|
||||
if (position.IsValidForMetrics() && position.ProfitAndLoss != null)
|
||||
{
|
||||
netPnl += position.ProfitAndLoss.Net;
|
||||
}
|
||||
}
|
||||
|
||||
return netPnl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the total fees paid by the trading bot for each position.
|
||||
/// Includes UI fees (0.1% of position size) and network fees ($0.15 for opening).
|
||||
/// Closing fees are handled by oracle, so no network fee for closing.
|
||||
/// </summary>
|
||||
/// <returns>Returns the total fees paid as a decimal value.</returns>
|
||||
public decimal GetTotalFees()
|
||||
{
|
||||
// Optimized: Avoid LINQ Where overhead, inline the check
|
||||
decimal totalFees = 0;
|
||||
|
||||
foreach (var position in Positions.Values)
|
||||
{
|
||||
if (position.IsValidForMetrics())
|
||||
{
|
||||
totalFees += TradingHelpers.CalculatePositionFees(position);
|
||||
}
|
||||
}
|
||||
|
||||
return totalFees;
|
||||
}
|
||||
|
||||
public async Task ToggleIsForWatchOnly()
|
||||
{
|
||||
|
||||
@@ -57,7 +57,7 @@ public class ClosePositionCommandHandler(
|
||||
// Add UI fees for closing the position (broker closed it)
|
||||
var closingPositionSizeUsd =
|
||||
(lastPrice * request.Position.Open.Quantity) * request.Position.Open.Leverage;
|
||||
var closingUiFees = TradingHelpers.CalculateClosingUiFees(closingPositionSizeUsd);
|
||||
var closingUiFees = TradingBox.CalculateClosingUiFees(closingPositionSizeUsd);
|
||||
request.Position.AddUiFees(closingUiFees);
|
||||
request.Position.AddGasFees(Constants.GMX.Config.GasFeePerTransaction);
|
||||
|
||||
@@ -83,7 +83,7 @@ public class ClosePositionCommandHandler(
|
||||
|
||||
// Add UI fees for closing the position
|
||||
var closingPositionSizeUsd = (lastPrice * closedPosition.Quantity) * request.Position.Open.Leverage;
|
||||
var closingUiFees = TradingHelpers.CalculateClosingUiFees(closingPositionSizeUsd);
|
||||
var closingUiFees = TradingBox.CalculateClosingUiFees(closingPositionSizeUsd);
|
||||
request.Position.AddUiFees(closingUiFees);
|
||||
request.Position.AddGasFees(Constants.GMX.Config.GasFeePerTransaction);
|
||||
|
||||
|
||||
@@ -89,11 +89,11 @@ namespace Managing.Application.Trading.Handlers
|
||||
|
||||
// Calculate and set fees for the position
|
||||
|
||||
position.GasFees = TradingHelpers.CalculateOpeningGasFees();
|
||||
position.GasFees = TradingBox.CalculateOpeningGasFees();
|
||||
|
||||
// Set UI fees for opening
|
||||
var positionSizeUsd = TradingHelpers.GetVolumeForPosition(position);
|
||||
position.UiFees = TradingHelpers.CalculateOpeningUiFees(positionSizeUsd);
|
||||
var positionSizeUsd = TradingBox.GetVolumeForPosition(position);
|
||||
position.UiFees = TradingBox.CalculateOpeningUiFees(positionSizeUsd);
|
||||
|
||||
var closeDirection = request.Direction == TradeDirection.Long
|
||||
? TradeDirection.Short
|
||||
|
||||
Reference in New Issue
Block a user