From d24f0ecc2e5f246d296045ea400591434bc679a6 Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 7 Jul 2026 10:56:52 +0200 Subject: [PATCH] Fix WinRate 0% bug: Optimize TradeReconciliationWorker with bulk SQL and reset Trader analytics flag --- query.csx | Bin 0 -> 1772 bytes .../Services/TradeReconciliationWorker.cs | 63 ++++++++++-------- 2 files changed, 35 insertions(+), 28 deletions(-) create mode 100644 query.csx diff --git a/query.csx b/query.csx new file mode 100644 index 0000000000000000000000000000000000000000..4150902d439451c03f94b7db8758aa99534309e5 GIT binary patch literal 1772 zcmd6o-D?w35XI*?;QtUsBn4S~Hm2Y=1%t)33cg62z13>WCYz1*qy5*_-@S@_uiR%=FFM7H@|)^ZDGeYwfEMwk$q!US+dX8w~qbIme~v6GvDwUvOc!b3UCTL z0xPqppjWI{pg+TQ0D7_$?4^CO$No+mNV`~*<&IaeXTolwj6u$92h1bK65kT0W8O>O zli3sewWpoKpV@uIH-RS|3V#bB+00jSe~yW+m>-!bQg{l#V_%3=qTT{q$xIv*v#t&J zP5C}?ZC9)DnwZsTP35FK(j9lBalGnrUFz%pZOTDGl6gc&};V;cq?elZ$L zX8X>1NOmKe!>4Dr7$fkNiJk*i3o=p#KJX>I`{xQDvM#;@yR2UCV4uM2*YSj^9w{7a z*D)4#N95SV;qHoN;_=$qG&PbYOLiAtDe0lA{6FlssAW|1qB~|}q>k9E?;~^Yi!JKg_p5s;7E1q*lj1$yUGcYgjnZUp3X(=41FXAX-W_xfs z@S5r@s+X7ax%6M>HzBJjXI$s_YWpkqQ@85o7!pNygtB_+^&S&Tx5thn-gG^uSag=U zc8BpVJMXplDvRspJZNz~wbLtdj-74J-%&$davl1vN9@$Mk8XC|T$#NgvhMX%{~she z;I^OMJ$#~X?!)W{-_88YPj>u&L}!G@s_x;l->OFVwQCf^aQpoR DH&8fv literal 0 HcmV?d00001 diff --git a/src/Predictalytics.Worker/Services/TradeReconciliationWorker.cs b/src/Predictalytics.Worker/Services/TradeReconciliationWorker.cs index fffd70f..b75b68e 100644 --- a/src/Predictalytics.Worker/Services/TradeReconciliationWorker.cs +++ b/src/Predictalytics.Worker/Services/TradeReconciliationWorker.cs @@ -16,7 +16,7 @@ public class TradeReconciliationWorker : BackgroundService private readonly ILogger _logger; private const int BatchSize = 250; - private const int IntervalMinutes = 15; + private const int IntervalSeconds = 30; public TradeReconciliationWorker(IServiceProvider services, ILogger logger) { @@ -26,7 +26,7 @@ public class TradeReconciliationWorker : BackgroundService protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - _logger.LogInformation("🛠️ TradeReconciliationWorker started (batch: {Batch}, interval: {Min}m)", BatchSize, IntervalMinutes); + _logger.LogInformation("🛠️ TradeReconciliationWorker started (batch: {Batch}, interval: {Sec}s)", BatchSize, IntervalSeconds); // Initial delay to let other workers settle await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken); @@ -52,7 +52,7 @@ public class TradeReconciliationWorker : BackgroundService _logger.LogError(ex, "Error in TradeReconciliationWorker cycle"); } - await Task.Delay(TimeSpan.FromMinutes(IntervalMinutes), stoppingToken); + await Task.Delay(TimeSpan.FromSeconds(IntervalSeconds), stoppingToken); } _logger.LogInformation("🛠️ TradeReconciliationWorker stopped"); @@ -61,37 +61,44 @@ public class TradeReconciliationWorker : BackgroundService private async Task ReconcileBatchAsync(CancellationToken ct) { using var scope = _services.CreateScope(); - var tradeRepo = scope.ServiceProvider.GetRequiredService(); - var marketRepo = scope.ServiceProvider.GetRequiredService(); + var db = scope.ServiceProvider.GetRequiredService(); - var orphanedTrades = await tradeRepo.GetOrphanedTradesAsync(BatchSize, ct); - if (orphanedTrades.Count == 0) return 0; - - int count = 0; - foreach (var trade in orphanedTrades) + // Bulk update orphaned trades + int reconciledCount = 0; + try { - if (ct.IsCancellationRequested) break; + reconciledCount = await Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.ExecuteSqlRawAsync(db.Database, @" + UPDATE Trades t + INNER JOIN MarketOutcomes o ON t.AssetId = o.TokenId + INNER JOIN Markets m ON o.MarketId = m.Id + SET t.MarketOutcomeId = o.Id, + t.Outcome = o.Label, + t.DbMarketId = m.Id + WHERE t.MarketOutcomeId IS NULL AND t.AssetId != ''; + ", ct); - try + if (reconciledCount > 0) { - var outcome = await marketRepo.GetOutcomeByTokenIdAsync(trade.AssetId, ct); - if (outcome != null) - { - trade.MarketOutcomeId = outcome.Id; - trade.Outcome = outcome.Label; - if (outcome.Market != null) - trade.DbMarketId = outcome.Market.Id; - - await tradeRepo.UpdateAsync(trade, ct); - count++; - } - } - catch (Exception ex) - { - _logger.LogWarning("Failed to reconcile trade {TradeId}: {Msg}", trade.Id, ex.Message); + // Force re-analysis of traders who now have new linked trades that haven't been applied + await Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.ExecuteSqlRawAsync(db.Database, @" + UPDATE Traders t + SET LastAnalyzedAt = NULL + WHERE LastAnalyzedAt IS NOT NULL + AND EXISTS ( + SELECT 1 FROM Trades tr + LEFT JOIN TraderPositions tp ON tr.TraderId = tp.TraderId AND tr.MarketOutcomeId = tp.MarketOutcomeId + WHERE tr.TraderId = t.Id + AND tr.MarketOutcomeId IS NOT NULL + AND (tp.Id IS NULL OR tr.Id > tp.LastAppliedTradeId) + ); + ", ct); } } + catch (Exception ex) + { + _logger.LogError(ex, "Error executing bulk reconciliation SQL"); + } - return count; + return reconciledCount; } }