From 5468b1e7f7f5829eef673283502a32cb555fb7fc Mon Sep 17 00:00:00 2001 From: cryptooda Date: Sat, 4 Oct 2025 19:36:27 +0700 Subject: [PATCH] Update closing trade date on SL or TP --- .../Bots/TradingBotBase.cs | 4 + .../Grains/PlatformSummaryGrain.cs | 85 +++++++------------ .../PostgreSql/PostgreSqlTradingRepository.cs | 4 +- .../mollecules/TradesModal/TradesModal.tsx | 14 ++- .../src/pages/botsPage/botList.tsx | 13 +-- 5 files changed, 54 insertions(+), 66 deletions(-) diff --git a/src/Managing.Application/Bots/TradingBotBase.cs b/src/Managing.Application/Bots/TradingBotBase.cs index 0c192b5c..e7fe495d 100644 --- a/src/Managing.Application/Bots/TradingBotBase.cs +++ b/src/Managing.Application/Bots/TradingBotBase.cs @@ -450,6 +450,8 @@ public class TradingBotBase : ITradingBot if (internalPosition.Status.Equals(PositionStatus.Filled)) { internalPosition.Status = PositionStatus.Finished; + // Call HandleClosedPosition to ensure trade dates are properly updated + await HandleClosedPosition(internalPosition); } } } @@ -1161,6 +1163,8 @@ public class TradingBotBase : ITradingBot { // Trade close on exchange => Should close trade manually await SetPositionStatus(signal.Identifier, PositionStatus.Finished); + // Ensure trade dates are properly updated even for canceled/rejected positions + await HandleClosedPosition(position); } } } diff --git a/src/Managing.Application/Grains/PlatformSummaryGrain.cs b/src/Managing.Application/Grains/PlatformSummaryGrain.cs index e3353914..a08bec7c 100644 --- a/src/Managing.Application/Grains/PlatformSummaryGrain.cs +++ b/src/Managing.Application/Grains/PlatformSummaryGrain.cs @@ -443,20 +443,21 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable } /// - /// Calculates a daily snapshot from positions for a specific date + /// Calculates a CUMULATIVE daily snapshot from positions up to a specific date /// /// All positions to analyze - /// The date to calculate the snapshot for - /// A daily snapshot for the specified date + /// The date to calculate the snapshot up to + /// A cumulative daily snapshot for the specified date private async Task CalculateDailySnapshotFromPositionsAsync(List positions, DateTime targetDate) { var dayStart = targetDate; var dayEnd = targetDate.AddDays(1); - // For daily snapshots, we need to consider ALL positions to calculate: - // 1. Volume from trades that occurred on this specific day + // For CUMULATIVE daily snapshots, we need to consider ALL positions to calculate: + // 1. TOTAL volume from all trades that occurred on or before this day // 2. Open interest from positions that were active during this day - // So we'll process all positions and filter the relevant data + // 3. Cumulative position count up to this date + // So we'll process all positions and include relevant data cumulatively // Calculate metrics for this specific day var totalVolume = 0m; @@ -499,84 +500,60 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable foreach (var position in positions) { - // Calculate volume for trades that occurred on this specific day - var dayVolume = 0m; - + // Calculate CUMULATIVE volume up to this point in time + // Include all positions that were opened on or before the target date _logger.LogDebug("Checking position {PositionId}: Position.Date={PositionDate}, TargetDate={TargetDate}, Position.Date.Date={PositionDateOnly}", position.Identifier, position.Date, targetDate, position.Date.Date); - // Add opening volume if position was opened on this day + // Add opening volume if position was opened on or before this day // Use more flexible date comparison to handle timezone differences - if (position.Date.Date == targetDate || - (position.Date >= targetDate && position.Date < targetDate.AddDays(1))) + if (position.Date.Date <= targetDate) { var openingVolume = position.Open.Price * position.Open.Quantity * position.Open.Leverage; - dayVolume += openingVolume; - _logger.LogDebug("Position {PositionId} opened on {TargetDate}: Opening volume = {OpeningVolume}", + totalVolume += openingVolume; + _logger.LogDebug("Position {PositionId} opened on/before {TargetDate}: Opening volume = {OpeningVolume}", position.Identifier, targetDate, openingVolume); } - // Add closing volume if position was closed on this day + // Add closing volume if position was closed on or before this day if (position.IsFinished()) { - if (position.StopLoss.Status == TradeStatus.Filled && - (position.StopLoss.Date.Date == targetDate || - (position.StopLoss.Date >= targetDate && position.StopLoss.Date < targetDate.AddDays(1)))) + if (position.StopLoss.Status == TradeStatus.Filled && position.StopLoss.Date.Date <= targetDate) { var closingVolume = position.StopLoss.Price * position.StopLoss.Quantity * position.StopLoss.Leverage; - dayVolume += closingVolume; - _logger.LogDebug("Position {PositionId} closed on {TargetDate} via StopLoss: Closing volume = {ClosingVolume}", + totalVolume += closingVolume; + _logger.LogDebug("Position {PositionId} closed on/before {TargetDate} via StopLoss: Closing volume = {ClosingVolume}", position.Identifier, targetDate, closingVolume); } - if (position.TakeProfit1.Status == TradeStatus.Filled && - (position.TakeProfit1.Date.Date == targetDate || - (position.TakeProfit1.Date >= targetDate && position.TakeProfit1.Date < targetDate.AddDays(1)))) + if (position.TakeProfit1.Status == TradeStatus.Filled && position.TakeProfit1.Date.Date <= targetDate) { var closingVolume = position.TakeProfit1.Price * position.TakeProfit1.Quantity * position.TakeProfit1.Leverage; - dayVolume += closingVolume; - _logger.LogDebug("Position {PositionId} closed on {TargetDate} via TakeProfit1: Closing volume = {ClosingVolume}", + totalVolume += closingVolume; + _logger.LogDebug("Position {PositionId} closed on/before {TargetDate} via TakeProfit1: Closing volume = {ClosingVolume}", position.Identifier, targetDate, closingVolume); } - if (position.TakeProfit2 != null && position.TakeProfit2.Status == TradeStatus.Filled && - (position.TakeProfit2.Date.Date == targetDate || - (position.TakeProfit2.Date >= targetDate && position.TakeProfit2.Date < targetDate.AddDays(1)))) + if (position.TakeProfit2 != null && position.TakeProfit2.Status == TradeStatus.Filled && position.TakeProfit2.Date.Date <= targetDate) { var closingVolume = position.TakeProfit2.Price * position.TakeProfit2.Quantity * position.TakeProfit2.Leverage; - dayVolume += closingVolume; - _logger.LogDebug("Position {PositionId} closed on {TargetDate} via TakeProfit2: Closing volume = {ClosingVolume}", - position.Identifier, targetDate, closingVolume); + totalVolume += closingVolume; } } - if (dayVolume > 0) - { - _logger.LogDebug("Position {PositionId} contributed {DayVolume} to {TargetDate} total volume", - position.Identifier, dayVolume, targetDate); - } - - totalVolume += dayVolume; - - // Calculate fees and PnL for positions closed on this day - var wasClosedOnThisDay = position.IsFinished() && ( - (position.StopLoss.Status == TradeStatus.Filled && position.StopLoss.Date.Date == targetDate) || - (position.TakeProfit1.Status == TradeStatus.Filled && position.TakeProfit1.Date.Date == targetDate) || - (position.TakeProfit2 != null && position.TakeProfit2.Status == TradeStatus.Filled && position.TakeProfit2.Date.Date == targetDate) + // Calculate CUMULATIVE fees and PnL for positions closed on or before this day + var wasClosedOnOrBeforeThisDay = position.IsFinished() && ( + (position.StopLoss.Status == TradeStatus.Filled && position.StopLoss.Date.Date <= targetDate) || + (position.TakeProfit1.Status == TradeStatus.Filled && position.TakeProfit1.Date.Date <= targetDate) || + (position.TakeProfit2 != null && position.TakeProfit2.Status == TradeStatus.Filled && position.TakeProfit2.Date.Date <= targetDate) ); - if (wasClosedOnThisDay) + if (wasClosedOnOrBeforeThisDay) { totalFees += position.CalculateTotalFees(); totalPnL += position.ProfitAndLoss?.Realized ?? 0; } - // Count positions that were active on this day (opened on or before, closed on or after) - var wasActiveOnThisDay = position.Date.Date <= targetDate && - (!position.IsFinished() || - (position.StopLoss.Status == TradeStatus.Filled && position.StopLoss.Date.Date >= targetDate) || - (position.TakeProfit1.Status == TradeStatus.Filled && position.TakeProfit1.Date.Date >= targetDate) || - (position.TakeProfit2 != null && position.TakeProfit2.Status == TradeStatus.Filled && position.TakeProfit2.Date.Date >= targetDate)); - - if (wasActiveOnThisDay) + // Count positions that were created on or before this day (CUMULATIVE position count) + if (position.Date.Date <= targetDate) { totalPositionCount++; } @@ -586,7 +563,7 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable var totalAgents = await _agentService.GetTotalAgentCount(); var totalStrategies = _state.State.TotalActiveStrategies; - _logger.LogInformation("Calculated daily snapshot for {TargetDate}: TotalVolume={TotalVolume}, MaxOpenInterest={MaxOpenInterest}, TotalPositionCount={TotalPositionCount}", + _logger.LogInformation("Calculated CUMULATIVE daily snapshot for {TargetDate}: CumVolume={TotalVolume}, MaxOpenInterest={MaxOpenInterest}, CumPositionCount={TotalPositionCount}", targetDate, totalVolume, maxOpenInterest, totalPositionCount); return new DailySnapshot diff --git a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlTradingRepository.cs b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlTradingRepository.cs index 474f6af0..9d76c20f 100644 --- a/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlTradingRepository.cs +++ b/src/Managing.Infrastructure.Database/PostgreSql/PostgreSqlTradingRepository.cs @@ -438,13 +438,13 @@ public class PostgreSqlTradingRepository : ITradingRepository /// /// Updates a trade entity with data from a domain trade object - /// Only updates the date if the trade status is changing from Requested to Filled + /// Always updates the date when it changes to ensure accurate execution dates /// private void UpdateTradeEntity(TradeEntity entity, Trade trade) { // Only update the date if the trade status is changing from Requested to Filled // This prevents overwriting dates for trades that are already filled - if (entity.Status != TradeStatus.Filled && trade.Status == TradeStatus.Filled) + if (entity.Status != trade.Status) { entity.Date = trade.Date; } diff --git a/src/Managing.WebApp/src/components/mollecules/TradesModal/TradesModal.tsx b/src/Managing.WebApp/src/components/mollecules/TradesModal/TradesModal.tsx index 8a39ce4c..0a456108 100644 --- a/src/Managing.WebApp/src/components/mollecules/TradesModal/TradesModal.tsx +++ b/src/Managing.WebApp/src/components/mollecules/TradesModal/TradesModal.tsx @@ -1,5 +1,9 @@ import React, {useEffect, useState} from 'react' -import type {ClosePositionRequest, Position, UserStrategyDetailsViewModel} from '../../../generated/ManagingApi' +import type { + ClosePositionRequest, + PositionViewModel, + UserStrategyDetailsViewModel +} from '../../../generated/ManagingApi' import {BotClient, DataClient} from '../../../generated/ManagingApi' import useApiUrlStore from '../../../app/store/apiStore' import Modal from '../Modal/Modal' @@ -9,6 +13,7 @@ interface TradesModalProps { showModal: boolean agentName: string | null strategyName: string | null + botIdentifier: string | undefined onClose: () => void } @@ -16,6 +21,7 @@ const TradesModal: React.FC = ({ showModal, strategyName, agentName, + botIdentifier, onClose, }) => { const { apiUrl } = useApiUrlStore() @@ -49,7 +55,7 @@ const TradesModal: React.FC = ({ } } - const closePosition = async (position: Position) => { + const closePosition = async (position: PositionViewModel) => { if (!agentName) return try { @@ -60,7 +66,7 @@ const TradesModal: React.FC = ({ // Use BotClient instead of fetch const botClient = new BotClient({}, apiUrl) const request: ClosePositionRequest = { - identifier: position.initiatorIdentifier, + identifier: botIdentifier, positionId: position.identifier } @@ -115,7 +121,7 @@ const TradesModal: React.FC = ({ {strategyData.positions && strategyData.positions.length > 0 ? ( - strategyData.positions.map((position: Position) => ( + strategyData.positions.map((position: PositionViewModel) => ( {new Date(position.date).toLocaleString()} diff --git a/src/Managing.WebApp/src/pages/botsPage/botList.tsx b/src/Managing.WebApp/src/pages/botsPage/botList.tsx index 5cd7bc2a..5f3d025e 100644 --- a/src/Managing.WebApp/src/pages/botsPage/botList.tsx +++ b/src/Managing.WebApp/src/pages/botsPage/botList.tsx @@ -72,7 +72,7 @@ const BotList: React.FC = ({ list }) => { const [showManualPositionModal, setShowManualPositionModal] = useState(false) const [selectedBotForManualPosition, setSelectedBotForManualPosition] = useState(null) const [showTradesModal, setShowTradesModal] = useState(false) - const [selectedBotForTrades, setSelectedBotForTrades] = useState<{ name: string; agentName: string } | null>(null) + const [selectedBotForTrades, setSelectedBotForTrades] = useState<{ name: string; agentName: string; identifier: string } | null>(null) const [showBotConfigModal, setShowBotConfigModal] = useState(false) const [selectedBotForUpdate, setSelectedBotForUpdate] = useState<{ identifier: string @@ -144,10 +144,10 @@ const BotList: React.FC = ({ list }) => { ) } - function getTradesBadge(name: string, agentName: string) { + function getTradesBadge(name: string, agentName: string, botIdentifier: string) { const classes = baseBadgeClass() + ' bg-secondary' return ( -