Add more tests + Log pnl for each backtest
This commit is contained in:
@@ -188,27 +188,10 @@ public class AgentGrain : Grain, IAgentGrain
|
||||
var positions = (await _tradingService.GetPositionByUserIdAsync((int)this.GetPrimaryKeyLong()))
|
||||
.Where(p => p.IsValidForMetrics()).ToList();
|
||||
|
||||
// Calculate aggregated statistics from position data
|
||||
var totalPnL = positions.Sum(p => p.ProfitAndLoss?.Realized ?? 0);
|
||||
var totalVolume = TradingBox.GetTotalVolumeTraded(positions);
|
||||
var collateral = positions.Sum(p => p.Open.Price * p.Open.Quantity);
|
||||
var totalFees = positions.Sum(p => p.CalculateTotalFees());
|
||||
var metrics = TradingBox.CalculateAgentSummaryMetrics(positions);
|
||||
|
||||
// Store total fees in grain state for caching
|
||||
_state.State.TotalFees = totalFees;
|
||||
|
||||
// Calculate wins/losses from position PnL
|
||||
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;
|
||||
var totalROI = collateral switch
|
||||
{
|
||||
> 0 => (netPnL / collateral) * 100,
|
||||
>= 0 => 0,
|
||||
_ => 0
|
||||
};
|
||||
_state.State.TotalFees = metrics.TotalFees;
|
||||
|
||||
// Calculate total balance (USDC wallet + USDC in open positions value)
|
||||
decimal totalBalance = 0;
|
||||
@@ -274,16 +257,16 @@ public class AgentGrain : Grain, IAgentGrain
|
||||
{
|
||||
UserId = (int)this.GetPrimaryKeyLong(),
|
||||
AgentName = _state.State.AgentName,
|
||||
TotalPnL = totalPnL, // Gross PnL before fees
|
||||
NetPnL = netPnL, // Net PnL after fees
|
||||
Wins = totalWins,
|
||||
Losses = totalLosses,
|
||||
TotalROI = totalROI,
|
||||
TotalPnL = metrics.TotalPnL, // Gross PnL before fees
|
||||
NetPnL = metrics.NetPnL, // Net PnL after fees
|
||||
Wins = metrics.Wins,
|
||||
Losses = metrics.Losses,
|
||||
TotalROI = metrics.TotalROI,
|
||||
Runtime = runtime,
|
||||
ActiveStrategiesCount = activeStrategies.Count(),
|
||||
TotalVolume = totalVolume,
|
||||
TotalVolume = metrics.TotalVolume,
|
||||
TotalBalance = totalBalance,
|
||||
TotalFees = totalFees,
|
||||
TotalFees = metrics.TotalFees,
|
||||
};
|
||||
|
||||
// Save summary to database
|
||||
@@ -294,12 +277,13 @@ public class AgentGrain : Grain, IAgentGrain
|
||||
await _state.WriteStateAsync();
|
||||
|
||||
// Insert balance tracking data
|
||||
InsertBalanceTrackingData(totalBalance, botsAllocationUsdValue, netPnL, usdcWalletValue,
|
||||
InsertBalanceTrackingData(totalBalance, botsAllocationUsdValue, metrics.NetPnL, usdcWalletValue,
|
||||
usdcInPositionsValue);
|
||||
|
||||
_logger.LogDebug(
|
||||
"Updated agent summary from position data for user {UserId}: NetPnL={NetPnL}, TotalPnL={TotalPnL}, Fees={Fees}, Volume={Volume}, Wins={Wins}, Losses={Losses}",
|
||||
this.GetPrimaryKeyLong(), netPnL, totalPnL, totalFees, totalVolume, totalWins, totalLosses);
|
||||
this.GetPrimaryKeyLong(), metrics.NetPnL, metrics.TotalPnL, metrics.TotalFees, metrics.TotalVolume,
|
||||
metrics.Wins, metrics.Losses);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -812,17 +812,11 @@ public class TradingBotBase : ITradingBot
|
||||
|
||||
var currentTime = Config.IsForBacktest ? lastCandle.Date : DateTime.UtcNow;
|
||||
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)
|
||||
: 0;
|
||||
var pnlPercentage = TradingBox.CalculatePnLPercentage(currentPnl, positionForSignal.Open.Price, positionForSignal.Open.Quantity);
|
||||
|
||||
var isPositionInProfit = positionForSignal.OriginDirection == TradeDirection.Long
|
||||
? lastCandle.Close > positionForSignal.Open.Price
|
||||
: lastCandle.Close < positionForSignal.Open.Price;
|
||||
var isPositionInProfit = TradingBox.IsPositionInProfit(positionForSignal.Open.Price, lastCandle.Close, positionForSignal.OriginDirection);
|
||||
|
||||
var hasExceededTimeLimit = Config.MaxPositionTimeHours.HasValue &&
|
||||
HasPositionExceededTimeLimit(positionForSignal, currentTime);
|
||||
var hasExceededTimeLimit = TradingBox.HasPositionExceededTimeLimit(positionForSignal.Open.Date, currentTime, Config.MaxPositionTimeHours);
|
||||
|
||||
if (hasExceededTimeLimit)
|
||||
{
|
||||
@@ -1246,29 +1240,16 @@ public class TradingBotBase : ITradingBot
|
||||
.Take(Config.MaxLossStreak)
|
||||
.ToList();
|
||||
|
||||
// If we don't have enough positions to form a streak, we can open
|
||||
if (recentPositions.Count < Config.MaxLossStreak)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if all recent positions were losses
|
||||
var allLosses = recentPositions.All(p => p.ProfitAndLoss?.Realized < 0);
|
||||
if (!allLosses)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we have a loss streak, check if the last position was in the same direction as the signal
|
||||
var lastPosition = recentPositions.First();
|
||||
if (lastPosition.OriginDirection == signal.Direction)
|
||||
var canOpen = TradingBox.CheckLossStreak(recentPositions, Config.MaxLossStreak, signal.Direction);
|
||||
|
||||
if (!canOpen)
|
||||
{
|
||||
var lastPosition = recentPositions.First();
|
||||
await LogWarning(
|
||||
$"🔥 Loss Streak Limit\nCannot open position\nMax loss streak: `{Config.MaxLossStreak}` reached\n📉 Last `{recentPositions.Count}` trades were losses\n🎯 Last position: `{lastPosition.OriginDirection}`\nWaiting for opposite direction signal");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return canOpen;
|
||||
}
|
||||
|
||||
private async Task<bool> CheckBrokerPositions()
|
||||
@@ -1869,17 +1850,9 @@ public class TradingBotBase : ITradingBot
|
||||
if (pnlCalculated && closingPrice > 0)
|
||||
{
|
||||
var entryPrice = position.Open.Price;
|
||||
var positionSize = position.Open.Quantity * position.Open.Leverage;
|
||||
var positionSize = TradingBox.CalculatePositionSize(position.Open.Quantity, position.Open.Leverage);
|
||||
|
||||
decimal pnl;
|
||||
if (position.OriginDirection == TradeDirection.Long)
|
||||
{
|
||||
pnl = (closingPrice - entryPrice) * positionSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
pnl = (entryPrice - closingPrice) * positionSize;
|
||||
}
|
||||
decimal pnl = TradingBox.CalculatePnL(entryPrice, closingPrice, position.Open.Quantity, position.Open.Leverage, position.OriginDirection);
|
||||
|
||||
if (position.ProfitAndLoss == null)
|
||||
{
|
||||
@@ -1901,7 +1874,7 @@ public class TradingBotBase : ITradingBot
|
||||
$"Entry Price: `${entryPrice:F2}` | Exit Price: `${closingPrice:F2}`\n" +
|
||||
$"Position Size: `{position.Open.Quantity:F8}` | Leverage: `{position.Open.Leverage}x`\n" +
|
||||
$"Position Value: `${positionSize:F8}`\n" +
|
||||
$"Price Difference: `${(position.OriginDirection == TradeDirection.Long ? closingPrice - entryPrice : entryPrice - closingPrice):F2}`\n" +
|
||||
$"Price Difference: `${TradingBox.CalculatePriceDifference(entryPrice, closingPrice, position.OriginDirection):F2}`\n" +
|
||||
$"Realized P&L: `${pnl:F2}`\n" +
|
||||
$"Gas Fees: `${position.GasFees:F2}` | UI Fees: `${position.UiFees:F2}`\n" +
|
||||
$"Total Fees: `${position.GasFees + position.UiFees:F2}`\n" +
|
||||
@@ -2308,18 +2281,6 @@ public class TradingBotBase : ITradingBot
|
||||
/// <param name="position">The position to check</param>
|
||||
/// <param name="currentTime">The current time to compare against</param>
|
||||
/// <returns>True if the position has exceeded the time limit, false otherwise</returns>
|
||||
private bool HasPositionExceededTimeLimit(Position position, DateTime currentTime)
|
||||
{
|
||||
if (!Config.MaxPositionTimeHours.HasValue || Config.MaxPositionTimeHours.Value <= 0)
|
||||
{
|
||||
return false; // Time-based closure is disabled
|
||||
}
|
||||
|
||||
var timeOpen = currentTime - position.Open.Date;
|
||||
var maxTimeAllowed = TimeSpan.FromHours((double)Config.MaxPositionTimeHours.Value);
|
||||
|
||||
return timeOpen >= maxTimeAllowed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the trading bot configuration with new settings.
|
||||
@@ -2630,8 +2591,7 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
|
||||
// Calculate cooldown end time based on last position closing time
|
||||
var baseIntervalSeconds = CandleHelpers.GetBaseIntervalInSeconds(Config.Timeframe);
|
||||
var cooldownEndTime = LastPositionClosingTime.Value.AddSeconds(baseIntervalSeconds * Config.CooldownPeriod);
|
||||
var cooldownEndTime = TradingBox.CalculateCooldownEndTime(LastPositionClosingTime.Value, Config.Timeframe, Config.CooldownPeriod);
|
||||
var isInCooldown = (Config.IsForBacktest ? LastCandle.Date : DateTime.UtcNow) < cooldownEndTime;
|
||||
|
||||
if (isInCooldown)
|
||||
|
||||
Reference in New Issue
Block a user