Clean and update event
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
using Managing.Application.Abstractions;
|
||||
using Managing.Application.Abstractions.Grains;
|
||||
using Managing.Application.Abstractions.Models;
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Application.Trading.Commands;
|
||||
using Managing.Application.Trading.Handlers;
|
||||
@@ -95,10 +94,6 @@ public class TradingBotBase : ITradingBot
|
||||
$"📢 I'll notify you when signals are triggered.";
|
||||
|
||||
await LogInformation(startupMessage);
|
||||
|
||||
// Notify AgentGrain about bot startup
|
||||
await NotifyAgentAndPlatformGrainAsync(AgentSummaryEventType.BotStarted,
|
||||
$"Bot: {Config.Name}, Ticker: {Config.Ticker}");
|
||||
break;
|
||||
|
||||
case BotStatus.Running:
|
||||
@@ -393,36 +388,29 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
});
|
||||
|
||||
NotificationEventType eventType = NotificationEventType.PositionUpdated;
|
||||
if (!Config.IsForBacktest)
|
||||
{
|
||||
var brokerPosition = brokerPositions.FirstOrDefault(p => p.Ticker == Config.Ticker);
|
||||
if (brokerPosition != null)
|
||||
{
|
||||
// Calculate net PnL after fees for broker position
|
||||
var previousPositionStatus = internalPosition.Status;
|
||||
// Position found on the broker, means the position is filled
|
||||
var brokerNetPnL = brokerPosition.GetNetPnL();
|
||||
UpdatePositionPnl(positionForSignal.Identifier, brokerNetPnL);
|
||||
internalPosition.ProfitAndLoss = new ProfitAndLoss { Realized = brokerNetPnL };
|
||||
internalPosition.Status = PositionStatus.Filled;
|
||||
await SetPositionStatus(internalPosition.SignalIdentifier, PositionStatus.Filled);
|
||||
|
||||
// Update Open trade status when position is found on broker
|
||||
if (internalPosition.Open != null)
|
||||
internalPosition.Open.SetStatus(TradeStatus.Filled);
|
||||
positionForSignal.Open.SetStatus(TradeStatus.Filled);
|
||||
eventType = NotificationEventType.PositionOpened;
|
||||
|
||||
await UpdatePositionDatabase(internalPosition);
|
||||
|
||||
if (previousPositionStatus != PositionStatus.Filled && internalPosition.Status == PositionStatus.Filled)
|
||||
{
|
||||
internalPosition.Open.SetStatus(TradeStatus.Filled);
|
||||
}
|
||||
|
||||
// Also update the position in the bot's positions dictionary
|
||||
if (positionForSignal.Open != null)
|
||||
{
|
||||
positionForSignal.Open.SetStatus(TradeStatus.Filled);
|
||||
}
|
||||
|
||||
if (internalPosition.Status.Equals(PositionStatus.New))
|
||||
{
|
||||
await SetPositionStatus(internalPosition.SignalIdentifier, PositionStatus.Filled);
|
||||
|
||||
// Notify platform summary about the executed trade
|
||||
await NotifyAgentAndPlatformGrainAsync(AgentSummaryEventType.PositionOpened,
|
||||
$"Position found on broker: {internalPosition.Identifier}", internalPosition);
|
||||
await NotifyAgentAndPlatformGrainAsync(NotificationEventType.PositionUpdated, internalPosition);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -431,18 +419,6 @@ public class TradingBotBase : ITradingBot
|
||||
if (internalPosition.Status.Equals(PositionStatus.Filled))
|
||||
{
|
||||
internalPosition.Status = PositionStatus.Finished;
|
||||
|
||||
// Update Open trade status when position becomes Finished
|
||||
if (internalPosition.Open != null)
|
||||
{
|
||||
internalPosition.Open.SetStatus(TradeStatus.Filled);
|
||||
}
|
||||
|
||||
// Also update the position in the bot's positions dictionary
|
||||
if (positionForSignal.Open != null)
|
||||
{
|
||||
positionForSignal.Open.SetStatus(TradeStatus.Filled);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -496,6 +472,7 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
else if (ordersCount == 2)
|
||||
{
|
||||
// TODO: This should never happen, but just in case
|
||||
// Check if position is already open on broker with 2 orders
|
||||
await LogInformation(
|
||||
$"🔍 **Checking Broker Position**\nPosition has exactly `{orders.Count()}` open orders\nChecking if position is already open on broker...");
|
||||
@@ -532,8 +509,7 @@ public class TradingBotBase : ITradingBot
|
||||
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}",
|
||||
await NotifyAgentAndPlatformGrainAsync(NotificationEventType.PositionOpened,
|
||||
internalPosition);
|
||||
}
|
||||
else
|
||||
@@ -560,8 +536,7 @@ public class TradingBotBase : ITradingBot
|
||||
{
|
||||
await HandleClosedPosition(positionForSignal);
|
||||
}
|
||||
else if (internalPosition.Status == PositionStatus.Filled ||
|
||||
internalPosition.Status == PositionStatus.PartiallyFilled)
|
||||
else if (internalPosition.Status == PositionStatus.Filled)
|
||||
{
|
||||
Candle lastCandle = null;
|
||||
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory, async exchangeService =>
|
||||
@@ -600,6 +575,7 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
}
|
||||
|
||||
// For backtest and to make sure position is closed based on SL and TP
|
||||
if (positionForSignal.OriginDirection == TradeDirection.Long)
|
||||
{
|
||||
if (positionForSignal.StopLoss.Price >= lastCandle.Low)
|
||||
@@ -779,36 +755,7 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
}
|
||||
|
||||
if (!Config.IsForBacktest)
|
||||
{
|
||||
// Update position in database with broker data
|
||||
await ServiceScopeHelpers.WithScopedService<ITradingService>(_scopeFactory, async tradingService =>
|
||||
{
|
||||
// Update the internal position with broker data
|
||||
internalPosition.Status = PositionStatus.Filled;
|
||||
// Apply fees to the internal position PnL before saving
|
||||
if (internalPosition.ProfitAndLoss != null)
|
||||
{
|
||||
var totalFees = internalPosition.CalculateTotalFees();
|
||||
internalPosition.ProfitAndLoss.Realized = internalPosition.ProfitAndLoss.Realized - totalFees;
|
||||
}
|
||||
|
||||
// Update Open trade status when position is updated to Filled
|
||||
if (internalPosition.Open != null)
|
||||
{
|
||||
internalPosition.Open.SetStatus(TradeStatus.Filled);
|
||||
}
|
||||
|
||||
// Also update the position in the bot's positions dictionary
|
||||
if (positionForSignal.Open != null)
|
||||
{
|
||||
positionForSignal.Open.SetStatus(TradeStatus.Filled);
|
||||
}
|
||||
|
||||
// Save updated position to database
|
||||
await tradingService.UpdatePositionAsync(internalPosition);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -818,6 +765,14 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdatePositionDatabase(Position position)
|
||||
{
|
||||
await ServiceScopeHelpers.WithScopedService<ITradingService>(_scopeFactory, async tradingService =>
|
||||
{
|
||||
await tradingService.UpdatePositionAsync(position);
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<Position> OpenPosition(LightSignal signal)
|
||||
{
|
||||
Logger.LogInformation($"Opening position for {signal.Identifier}");
|
||||
@@ -936,8 +891,8 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
|
||||
// Notify AgentGrain about position opening
|
||||
await NotifyAgentAndPlatformGrainAsync(AgentSummaryEventType.PositionOpened,
|
||||
$"Signal: {signal.Identifier}", position);
|
||||
await NotifyAgentAndPlatformGrainAsync(NotificationEventType.PositionOpened,
|
||||
position);
|
||||
|
||||
Logger.LogInformation($"Position requested");
|
||||
return position; // Return the created position without adding to list
|
||||
@@ -1418,13 +1373,6 @@ public class TradingBotBase : ITradingBot
|
||||
string.Format("💰 **Balance Updated**\nNew bot trading balance: `${0:F2}`",
|
||||
Config.BotTradingBalance));
|
||||
}
|
||||
|
||||
// Notify AgentGrain about position closing
|
||||
var pnlInfo = position.ProfitAndLoss?.Realized != null
|
||||
? string.Format("PnL: {0:F2}", position.ProfitAndLoss.Realized)
|
||||
: "PnL: Unknown";
|
||||
await NotifyAgentAndPlatformGrainAsync(AgentSummaryEventType.PositionClosed,
|
||||
string.Format("Signal: {0}, {1}", position.SignalIdentifier, pnlInfo), position);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1555,7 +1503,7 @@ public class TradingBotBase : ITradingBot
|
||||
public decimal GetProfitAndLoss()
|
||||
{
|
||||
// Calculate net PnL after deducting fees for each position
|
||||
var netPnl = Positions.Values.Where(p => p.ProfitAndLoss != null && p.IsFinished())
|
||||
var netPnl = Positions.Values.Where(p => p.ProfitAndLoss != null)
|
||||
.Sum(p => p.GetNetPnL());
|
||||
return netPnl;
|
||||
}
|
||||
@@ -2120,13 +2068,12 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies both AgentGrain and PlatformSummaryGrain about bot events
|
||||
/// Notifies both AgentGrain and PlatformSummaryGrain about bot events using unified event data
|
||||
/// </summary>
|
||||
/// <param name="eventType">The type of event (e.g., PositionOpened, PositionClosed)</param>
|
||||
/// <param name="additionalData">Optional additional context data</param>
|
||||
/// <param name="eventType">The type of event (e.g., PositionOpened, PositionClosed, PositionUpdated)</param>
|
||||
/// <param name="position">Optional position data for platform summary events</param>
|
||||
private async Task NotifyAgentAndPlatformGrainAsync(AgentSummaryEventType eventType, string additionalData = null,
|
||||
Position position = null)
|
||||
private async Task NotifyAgentAndPlatformGrainAsync(NotificationEventType eventType,
|
||||
Position position)
|
||||
{
|
||||
if (Config.IsForBacktest)
|
||||
{
|
||||
@@ -2137,33 +2084,13 @@ public class TradingBotBase : ITradingBot
|
||||
{
|
||||
await ServiceScopeHelpers.WithScopedService<IGrainFactory>(_scopeFactory, async grainFactory =>
|
||||
{
|
||||
// Notify AgentGrain (user-specific metrics)
|
||||
if (Account?.User != null)
|
||||
{
|
||||
var agentGrain = grainFactory.GetGrain<IAgentGrain>(Account.User.Id);
|
||||
|
||||
var updateEvent = new AgentSummaryUpdateEvent
|
||||
{
|
||||
BotId = Identifier,
|
||||
EventType = eventType,
|
||||
Timestamp = DateTime.UtcNow,
|
||||
AdditionalData = additionalData
|
||||
};
|
||||
|
||||
await agentGrain.OnAgentSummaryUpdateAsync(updateEvent);
|
||||
Logger.LogDebug("Sent agent notification: {EventType} for bot {BotId}", eventType, Identifier);
|
||||
}
|
||||
|
||||
// Notify PlatformSummaryGrain (platform-wide metrics)
|
||||
var agentGrain = grainFactory.GetGrain<IAgentGrain>(Account.User.Id);
|
||||
var platformGrain = grainFactory.GetGrain<IPlatformSummaryGrain>("platform-summary");
|
||||
|
||||
// Create unified event objects based on event type
|
||||
switch (eventType)
|
||||
{
|
||||
case AgentSummaryEventType.PositionOpened when position != null:
|
||||
// Position opening is now handled by TradeExecutedEvent in PublishTradeExecutedEventAsync
|
||||
Logger.LogDebug(
|
||||
"Position opened notification sent via TradeExecutedEvent for position {PositionId}",
|
||||
position.Identifier);
|
||||
case NotificationEventType.PositionOpened:
|
||||
var positionOpenEvent = new PositionOpenEvent
|
||||
{
|
||||
PositionIdentifier = position.Identifier,
|
||||
@@ -2172,10 +2099,15 @@ public class TradingBotBase : ITradingBot
|
||||
Fee = position.GasFees + position.UiFees,
|
||||
Direction = position.OriginDirection
|
||||
};
|
||||
|
||||
await agentGrain.OnPositionOpenedAsync(positionOpenEvent);
|
||||
await platformGrain.OnPositionOpenAsync(positionOpenEvent);
|
||||
|
||||
Logger.LogDebug("Sent position opened event to both grains for position {PositionId}",
|
||||
position.Identifier);
|
||||
break;
|
||||
|
||||
case AgentSummaryEventType.PositionClosed when position != null:
|
||||
case NotificationEventType.PositionClosed:
|
||||
var positionClosedEvent = new PositionClosedEvent
|
||||
{
|
||||
PositionIdentifier = position.Identifier,
|
||||
@@ -2183,8 +2115,23 @@ public class TradingBotBase : ITradingBot
|
||||
RealizedPnL = position.ProfitAndLoss?.Realized ?? 0,
|
||||
Volume = position.Open.Price * position.Open.Quantity * position.Open.Leverage,
|
||||
};
|
||||
|
||||
await agentGrain.OnPositionClosedAsync(positionClosedEvent);
|
||||
await platformGrain.OnPositionClosedAsync(positionClosedEvent);
|
||||
Logger.LogDebug("Sent platform position closed notification for position {PositionId}",
|
||||
|
||||
Logger.LogDebug("Sent position closed event to both grains for position {PositionId}",
|
||||
position.Identifier);
|
||||
break;
|
||||
|
||||
case NotificationEventType.PositionUpdated:
|
||||
var positionUpdatedEvent = new PositionUpdatedEvent
|
||||
{
|
||||
PositionIdentifier = position.Identifier,
|
||||
};
|
||||
|
||||
await agentGrain.OnPositionUpdatedAsync(positionUpdatedEvent);
|
||||
|
||||
Logger.LogDebug("Sent position updated event to both grains for position {PositionId}",
|
||||
position.Identifier);
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user