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) => (