From d13ac9fd211aa1a1fc0e4b6f9eacf4d8b09d08d0 Mon Sep 17 00:00:00 2001 From: cryptooda Date: Mon, 22 Sep 2025 23:56:45 +0700 Subject: [PATCH] fix backtest below 10usdc + update trade --- .../Repositories/ITradingRepository.cs | 1 + .../Services/ITradingService.cs | 1 + .../Bots/Grains/BacktestTradingBotGrain.cs | 4 +- .../Bots/TradingBotBase.cs | 31 +++++++++++-- .../Trading/TradingService.cs | 5 ++ .../PostgreSql/PostgreSqlTradingRepository.cs | 46 +++++++++++++++++++ 6 files changed, 83 insertions(+), 5 deletions(-) diff --git a/src/Managing.Application.Abstractions/Repositories/ITradingRepository.cs b/src/Managing.Application.Abstractions/Repositories/ITradingRepository.cs index c04735ad..326c777b 100644 --- a/src/Managing.Application.Abstractions/Repositories/ITradingRepository.cs +++ b/src/Managing.Application.Abstractions/Repositories/ITradingRepository.cs @@ -33,4 +33,5 @@ public interface ITradingRepository Task UpdateStrategyAsync(IndicatorBase indicatorBase); Task GetStrategyByNameUserAsync(string name, User user); Task GetScenarioByNameUserAsync(string scenarioName, User user); + Task UpdateTradeAsync(Trade trade); } \ No newline at end of file diff --git a/src/Managing.Application.Abstractions/Services/ITradingService.cs b/src/Managing.Application.Abstractions/Services/ITradingService.cs index 59fde499..f0c04db0 100644 --- a/src/Managing.Application.Abstractions/Services/ITradingService.cs +++ b/src/Managing.Application.Abstractions/Services/ITradingService.cs @@ -19,6 +19,7 @@ public interface ITradingService Task GetScenarioByNameAsync(string scenario); Task InsertPositionAsync(Position position); Task UpdatePositionAsync(Position position); + Task UpdateTradeAsync(Trade trade); Task GetIndicatorByNameAsync(string strategy); Task InsertScenarioAsync(Scenario scenario); Task InsertIndicatorAsync(IndicatorBase indicatorBase); diff --git a/src/Managing.Application/Bots/Grains/BacktestTradingBotGrain.cs b/src/Managing.Application/Bots/Grains/BacktestTradingBotGrain.cs index 828abd89..95fea441 100644 --- a/src/Managing.Application/Bots/Grains/BacktestTradingBotGrain.cs +++ b/src/Managing.Application/Bots/Grains/BacktestTradingBotGrain.cs @@ -98,8 +98,8 @@ public class BacktestTradingBotGrain : Grain, IBacktestTradingBotGrain if (currentWalletBalance < Constants.GMX.Config.MinimumPositionAmount) { _logger.LogWarning( - "Backtest stopped early: Wallet balance fell below 10 USDC (Current: {CurrentBalance:F2} USDC) at candle {CurrentCandle}/{TotalCandles} from {CandleDate}", - currentWalletBalance, currentCandle, totalCandles, candle.Date.ToString("yyyy-MM-dd HH:mm")); + "Backtest stopped early: Wallet balance fell below {MinimumPositionAmount} USDC (Current: {CurrentBalance:F2} USDC) at candle {CurrentCandle}/{TotalCandles} from {CandleDate}", + Constants.GMX.Config.MinimumPositionAmount, currentWalletBalance, currentCandle, totalCandles, candle.Date.ToString("yyyy-MM-dd HH:mm")); break; } } diff --git a/src/Managing.Application/Bots/TradingBotBase.cs b/src/Managing.Application/Bots/TradingBotBase.cs index 46d0f598..14e6dace 100644 --- a/src/Managing.Application/Bots/TradingBotBase.cs +++ b/src/Managing.Application/Bots/TradingBotBase.cs @@ -420,6 +420,12 @@ public class TradingBotBase : ITradingBot if (!internalPosition.Status.Equals(PositionStatus.New)) { internalPosition.Status = PositionStatus.Filled; + + // Update Open trade status when position becomes Filled + if (internalPosition.Open != null) + { + internalPosition.Open.SetStatus(TradeStatus.Filled); + } } } } @@ -431,7 +437,8 @@ public class TradingBotBase : ITradingBot if (orders.Any()) { - if (orders.Count() >= 3) + var ordersCount = orders.Count(); + if (ordersCount >= 3) { var currentTime = Config.IsForBacktest ? LastCandle?.Date ?? DateTime.UtcNow : DateTime.UtcNow; var timeSinceRequest = currentTime - positionForSignal.Open.Date; @@ -467,7 +474,7 @@ public class TradingBotBase : ITradingBot $"⏳ **Waiting for Orders**\nPosition has `{orders.Count()}` open orders\nElapsed: `{timeSinceRequest.TotalMinutes:F1}min`\nWaiting `{remainingMinutes:F1}min` more before canceling"); } } - else if (orders.Count() == 2 && Positions[internalPosition.Identifier].Status == PositionStatus.New) + else if (ordersCount == 2) { // Check if position is already open on broker with 2 orders await LogInformation( @@ -488,7 +495,6 @@ public class TradingBotBase : ITradingBot UpdatePositionPnl(positionForSignal.Identifier, brokerPosition.ProfitAndLoss.Realized); await SetPositionStatus(signal.Identifier, PositionStatus.Filled); - // Notify platform summary about the executed trade await NotifyAgentAndPlatformGrainAsync(AgentSummaryEventType.PositionOpened, $"Position found on broker with 2 orders: {internalPosition.Identifier}", internalPosition); @@ -1118,6 +1124,7 @@ public class TradingBotBase : ITradingBot { closingPrice = position.StopLoss.Price; position.StopLoss.SetDate(currentCandle.Date); + position.StopLoss.SetStatus(TradeStatus.Filled); Logger.LogInformation( $"🛑 **Stop Loss Execution Confirmed**\n" + @@ -1129,6 +1136,7 @@ public class TradingBotBase : ITradingBot { closingPrice = position.TakeProfit1.Price; position.TakeProfit1.SetDate(currentCandle.Date); + position.TakeProfit1.SetStatus(TradeStatus.Filled); Logger.LogInformation( $"🎯 **Take Profit Execution Confirmed**\n" + @@ -1154,10 +1162,12 @@ public class TradingBotBase : ITradingBot if (isManualCloseProfitable) { position.TakeProfit1.SetDate(currentCandle.Date); + position.TakeProfit1.SetStatus(TradeStatus.Filled); } else { position.StopLoss.SetDate(currentCandle.Date); + position.StopLoss.SetStatus(TradeStatus.Filled); } Logger.LogInformation( @@ -1193,6 +1203,15 @@ public class TradingBotBase : ITradingBot await SetPositionStatus(position.SignalIdentifier, PositionStatus.Finished); + // Update position in database with all trade changes + if (!Config.IsForBacktest) + { + await ServiceScopeHelpers.WithScopedService(_scopeFactory, async tradingService => + { + await tradingService.UpdatePositionAsync(position); + }); + } + // Update the last position closing time for cooldown period tracking LastPositionClosingTime = Config.IsForBacktest ? currentCandle.Date : DateTime.UtcNow; @@ -1299,6 +1318,12 @@ public class TradingBotBase : ITradingBot Positions.Values.First(p => p.SignalIdentifier == signalIdentifier).Status = positionStatus; await LogInformation( $"📊 **Position Status Change**\nPosition: `{signalIdentifier}`\nStatus: `{position.Status}` → `{positionStatus}`"); + + // Update Open trade status when position becomes Filled + if (positionStatus == PositionStatus.Filled && position.Open != null) + { + position.Open.SetStatus(TradeStatus.Filled); + } } SetSignalStatus(signalIdentifier, diff --git a/src/Managing.Application/Trading/TradingService.cs b/src/Managing.Application/Trading/TradingService.cs index 62b0a258..d42da74e 100644 --- a/src/Managing.Application/Trading/TradingService.cs +++ b/src/Managing.Application/Trading/TradingService.cs @@ -184,6 +184,11 @@ public class TradingService : ITradingService await _tradingRepository.UpdatePositionAsync(position); } + public async Task UpdateTradeAsync(Trade trade) + { + await _tradingRepository.UpdateTradeAsync(trade); + } + public async Task> GetPositionsAsync() { var positions = new List(); diff --git a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlTradingRepository.cs b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlTradingRepository.cs index 3a77c031..cddd89d6 100644 --- a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlTradingRepository.cs +++ b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlTradingRepository.cs @@ -402,6 +402,52 @@ public class PostgreSqlTradingRepository : ITradingRepository : null; entity.UpdatedAt = DateTime.UtcNow; + // Update related trades + if (position.Open != null) + { + await UpdateTradeAsync(position.Open); + } + + if (position.StopLoss != null) + { + await UpdateTradeAsync(position.StopLoss); + } + + if (position.TakeProfit1 != null) + { + await UpdateTradeAsync(position.TakeProfit1); + } + + if (position.TakeProfit2 != null) + { + await UpdateTradeAsync(position.TakeProfit2); + } + + await _context.SaveChangesAsync(); + } + } + + public async Task UpdateTradeAsync(Trade trade) + { + var entity = _context.Trades + .AsTracking() + .FirstOrDefault(t => t.ExchangeOrderId == trade.ExchangeOrderId); + + if (entity != null) + { + entity.Date = trade.Date; + entity.Direction = trade.Direction; + entity.Status = trade.Status; + entity.TradeType = trade.TradeType; + entity.Ticker = trade.Ticker; + entity.Fee = trade.Fee; + entity.Quantity = trade.Quantity; + entity.Price = trade.Price; + entity.Leverage = trade.Leverage; + entity.ExchangeOrderId = trade.ExchangeOrderId; + entity.Message = trade.Message; + entity.UpdatedAt = DateTime.UtcNow; + await _context.SaveChangesAsync(); } }