From ea4458e21a8faadb204ab298290cf57071eda4e6 Mon Sep 17 00:00:00 2001 From: cryptooda Date: Sun, 1 Jun 2025 19:04:34 +0700 Subject: [PATCH] Add volumes and fix missing signal --- src/Managing.Application/Bots/TradingBot.cs | 59 ++++++++++++++++++- .../organism/Backtest/backtestRowDetails.tsx | 53 +++++++++++++++++ 2 files changed, 110 insertions(+), 2 deletions(-) diff --git a/src/Managing.Application/Bots/TradingBot.cs b/src/Managing.Application/Bots/TradingBot.cs index 679a4e4..454377e 100644 --- a/src/Managing.Application/Bots/TradingBot.cs +++ b/src/Managing.Application/Bots/TradingBot.cs @@ -287,6 +287,53 @@ public class TradingBot : Bot, ITradingBot } } + private async Task RecreateSignalFromPosition(Position position) + { + try + { + // Get the candle that corresponds to the position opening time + var positionCandle = OptimizedCandles.FirstOrDefault(c => c.Date <= position.Open.Date) + ?? OptimizedCandles.LastOrDefault(); + + if (positionCandle == null) + { + await LogWarning($"Cannot find candle for position {position.Identifier} opened at {position.Open.Date}"); + return null; + } + + // Create a new signal based on position information + var recreatedSignal = new Signal( + ticker: Config.Ticker, + direction: position.OriginDirection, + confidence: Confidence.Medium, // Default confidence for recreated signals + candle: positionCandle, + date: position.Open.Date, + exchange: Account.Exchange, + strategyType: StrategyType.Stc, // Use a valid strategy type for recreated signals + signalType: SignalType.Signal + ); + + // Since Signal identifier is auto-generated, we need to update our position + // to use the new signal identifier, or find another approach + // For now, let's update the position's SignalIdentifier to match the recreated signal + position.SignalIdentifier = recreatedSignal.Identifier; + + recreatedSignal.Status = SignalStatus.PositionOpen; + recreatedSignal.User = Account.User; + + // Add the recreated signal to our collection + Signals.Add(recreatedSignal); + + await LogInformation($"Successfully recreated signal {recreatedSignal.Identifier} for position {position.Identifier}"); + return recreatedSignal; + } + catch (Exception ex) + { + await LogWarning($"Error recreating signal for position {position.Identifier}: {ex.Message}"); + return null; + } + } + private async Task ManagePositions() { // Update positions - iterate through positions instead of signals for better synchronization @@ -295,8 +342,16 @@ public class TradingBot : Bot, ITradingBot var signalForPosition = Signals.FirstOrDefault(s => s.Identifier == position.SignalIdentifier); if (signalForPosition == null) { - await LogWarning($"Cannot find signal for position {position.Identifier}"); - continue; + await LogInformation($"Signal not found for position {position.Identifier}. Recreating signal..."); + + // Recreate the signal based on position information + signalForPosition = await RecreateSignalFromPosition(position); + + if (signalForPosition == null) + { + await LogWarning($"Failed to recreate signal for position {position.Identifier}"); + continue; + } } // Ensure signal status is correctly set to PositionOpen if position is not finished diff --git a/src/Managing.WebApp/src/components/organism/Backtest/backtestRowDetails.tsx b/src/Managing.WebApp/src/components/organism/Backtest/backtestRowDetails.tsx index a086692..50ef9c9 100644 --- a/src/Managing.WebApp/src/components/organism/Backtest/backtestRowDetails.tsx +++ b/src/Managing.WebApp/src/components/organism/Backtest/backtestRowDetails.tsx @@ -81,6 +81,45 @@ const BacktestRowDetails: React.FC = ({ return averageHours.toFixed(2); }; + // Calculate total volume traded with leverage + const getTotalVolumeTraded = () => { + let totalVolume = 0; + + positions.forEach((position) => { + // Calculate volume for open trade + const openLeverage = position.open.leverage || 1; + const openVolume = position.open.quantity * position.open.price * openLeverage; + totalVolume += openVolume; + + // Calculate volume for close trade (stopLoss or takeProfit based on realized P&L) + if (position.profitAndLoss?.realized != null) { + let closeTrade; + if (position.profitAndLoss.realized > 0) { + // Profitable close = Take Profit + closeTrade = position.takeProfit1; + } else { + // Loss or breakeven close = Stop Loss + closeTrade = position.stopLoss; + } + + if (closeTrade) { + const closeLeverage = closeTrade.leverage || 1; + const closeVolume = closeTrade.quantity * closeTrade.price * closeLeverage; + totalVolume += closeVolume; + } + } + }); + + return totalVolume; + }; + + // Calculate estimated UI fee (0.02% of total volume) + const getEstimatedUIFee = () => { + const totalVolume = getTotalVolumeTraded(); + const uiFeePercentage = 0.0002; // 0.02% + return totalVolume * uiFeePercentage; + }; + return ( <>
@@ -146,6 +185,20 @@ const BacktestRowDetails: React.FC = ({ title="Avg Open Time (Losing)" content={getAverageOpenTimeLosing() + " hours"} > + +