Add more tests + Log pnl for each backtest

This commit is contained in:
2025-11-14 13:12:04 +07:00
parent 2548e9b757
commit d341ee05c9
11 changed files with 4163 additions and 97 deletions

View File

@@ -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)