Improve Platform stats
This commit is contained in:
@@ -96,8 +96,11 @@ public class TradingBotBase : ITradingBot
|
||||
await LogInformation(startupMessage);
|
||||
|
||||
// Notify AgentGrain about bot startup
|
||||
await NotifyAgentGrainAsync(AgentSummaryEventType.BotStarted,
|
||||
await NotifyAgentAndPlatformGrainAsync(AgentSummaryEventType.BotStarted,
|
||||
$"Bot: {Config.Name}, Ticker: {Config.Ticker}");
|
||||
|
||||
// Notify platform summary about active strategy count change
|
||||
await NotifyPlatformSummaryAboutStrategyCount();
|
||||
break;
|
||||
|
||||
case BotStatus.Running:
|
||||
@@ -372,81 +375,60 @@ public class TradingBotBase : ITradingBot
|
||||
{
|
||||
try
|
||||
{
|
||||
Position position = null;
|
||||
List<Position> positionsExchange = null;
|
||||
Position internalPosition = null;
|
||||
List<Position> brokerPositions = null;
|
||||
await ServiceScopeHelpers.WithScopedService<ITradingService>(_scopeFactory, async tradingService =>
|
||||
{
|
||||
position = Config.IsForBacktest
|
||||
internalPosition = Config.IsForBacktest
|
||||
? positionForSignal
|
||||
: await tradingService.GetPositionByIdentifierAsync(positionForSignal.Identifier);
|
||||
|
||||
if (Config.IsForBacktest)
|
||||
{
|
||||
positionsExchange = new List<Position> { position };
|
||||
brokerPositions = new List<Position> { internalPosition };
|
||||
}
|
||||
else
|
||||
{
|
||||
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
|
||||
brokerPositions = await ServiceScopeHelpers.WithScopedService<IExchangeService, List<Position>>(_scopeFactory,
|
||||
async exchangeService =>
|
||||
{
|
||||
positionsExchange = (await exchangeService.GetBrokerPositions(Account)).ToList();
|
||||
return [.. await exchangeService.GetBrokerPositions(Account)];
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (!Config.IsForBacktest)
|
||||
{
|
||||
var brokerPosition = positionsExchange.FirstOrDefault(p => p.Ticker == Config.Ticker);
|
||||
var brokerPosition = brokerPositions.FirstOrDefault(p => p.Ticker == Config.Ticker);
|
||||
if (brokerPosition != null)
|
||||
{
|
||||
UpdatePositionPnl(positionForSignal.Identifier, brokerPosition.ProfitAndLoss.Realized);
|
||||
internalPosition.ProfitAndLoss = brokerPosition.ProfitAndLoss;
|
||||
internalPosition.Status = PositionStatus.Filled;
|
||||
|
||||
if (position.Status.Equals(PositionStatus.New))
|
||||
if (internalPosition.Status.Equals(PositionStatus.New))
|
||||
{
|
||||
await SetPositionStatus(position.SignalIdentifier, PositionStatus.Filled);
|
||||
await SetPositionStatus(internalPosition.SignalIdentifier, PositionStatus.Filled);
|
||||
|
||||
// Notify platform summary about the executed trade
|
||||
try
|
||||
{
|
||||
await ServiceScopeHelpers.WithScopedService<IGrainFactory>(_scopeFactory,
|
||||
async grainFactory =>
|
||||
{
|
||||
var platformGrain =
|
||||
grainFactory.GetGrain<IPlatformSummaryGrain>("platform-summary");
|
||||
var tradeExecutedEvent = new TradeExecutedEvent
|
||||
{
|
||||
TradeId = position.Identifier,
|
||||
Ticker = position.Ticker,
|
||||
Volume = position.Open.Price * position.Open.Quantity * position.Open.Leverage
|
||||
};
|
||||
|
||||
await platformGrain.OnTradeExecutedAsync(tradeExecutedEvent);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex,
|
||||
"Failed to notify platform summary about trade execution for position {PositionId}",
|
||||
position.Identifier);
|
||||
}
|
||||
await NotifyAgentAndPlatformGrainAsync(AgentSummaryEventType.PositionOpened,
|
||||
$"Position found on broker: {internalPosition.Identifier}", internalPosition);
|
||||
}
|
||||
|
||||
position = brokerPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!position.Status.Equals(PositionStatus.New))
|
||||
if (!internalPosition.Status.Equals(PositionStatus.New))
|
||||
{
|
||||
position.Status = PositionStatus.Filled;
|
||||
internalPosition.Status = PositionStatus.Filled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (position.Status == PositionStatus.New)
|
||||
if (internalPosition.Status == PositionStatus.New)
|
||||
{
|
||||
List<Trade> orders = null;
|
||||
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
|
||||
async exchangeService => { orders = await exchangeService.GetOpenOrders(Account, Config.Ticker); });
|
||||
var orders = await ServiceScopeHelpers.WithScopedService<IExchangeService, List<Trade>>(_scopeFactory,
|
||||
async exchangeService => { return [.. await exchangeService.GetOpenOrders(Account, Config.Ticker)]; });
|
||||
|
||||
if (orders.Any())
|
||||
{
|
||||
if (orders.Count() >= 3)
|
||||
@@ -485,6 +467,38 @@ public class TradingBotBase : ITradingBot
|
||||
$"⏳ **Waiting for Orders**\nPosition has `{orders.Count()}` open orders\nElapsed: `{timeSinceRequest.TotalMinutes:F1}min`\nWaiting `{remainingMinutes:F1}min` more before canceling");
|
||||
}
|
||||
}
|
||||
else if (orders.Count() == 2 && Positions[internalPosition.Identifier].Status == PositionStatus.New)
|
||||
{
|
||||
// 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...");
|
||||
|
||||
Position brokerPosition = null;
|
||||
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory, async exchangeService =>
|
||||
{
|
||||
var brokerPositions = await exchangeService.GetBrokerPositions(Account);
|
||||
brokerPosition = brokerPositions.FirstOrDefault(p => p.Ticker == Config.Ticker);
|
||||
});
|
||||
|
||||
if (brokerPosition != null)
|
||||
{
|
||||
await LogInformation(
|
||||
$"✅ **Position Found on Broker**\nPosition is already open on broker\nUpdating position status to Filled");
|
||||
|
||||
UpdatePositionPnl(positionForSignal.Identifier, brokerPosition.ProfitAndLoss.Realized);
|
||||
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}", internalPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
await LogInformation(
|
||||
$"⏸️ **Position Pending**\nPosition still waiting to open\n`{orders.Count()}` open orders remaining");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await LogInformation(
|
||||
@@ -498,11 +512,11 @@ public class TradingBotBase : ITradingBot
|
||||
await HandleClosedPosition(positionForSignal);
|
||||
}
|
||||
}
|
||||
else if (position.Status == PositionStatus.Finished || position.Status == PositionStatus.Flipped)
|
||||
else if (internalPosition.Status == PositionStatus.Finished || internalPosition.Status == PositionStatus.Flipped)
|
||||
{
|
||||
await HandleClosedPosition(positionForSignal);
|
||||
}
|
||||
else if (position.Status == PositionStatus.Filled || position.Status == PositionStatus.PartiallyFilled)
|
||||
else if (internalPosition.Status == PositionStatus.Filled || internalPosition.Status == PositionStatus.PartiallyFilled)
|
||||
{
|
||||
Candle lastCandle = null;
|
||||
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory, async exchangeService =>
|
||||
@@ -598,7 +612,7 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (position.Status == PositionStatus.Rejected || position.Status == PositionStatus.Canceled)
|
||||
else if (internalPosition.Status == PositionStatus.Rejected || internalPosition.Status == PositionStatus.Canceled)
|
||||
{
|
||||
await LogWarning($"Open position trade is rejected for signal {signal.Identifier}");
|
||||
if (signal.Status == SignalStatus.PositionOpen)
|
||||
@@ -642,6 +656,20 @@ public class TradingBotBase : ITradingBot
|
||||
currentPrice, true);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
internalPosition.ProfitAndLoss = internalPosition.ProfitAndLoss;
|
||||
|
||||
// Save updated position to database
|
||||
await tradingService.UpdatePositionAsync(internalPosition);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -769,8 +797,11 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
|
||||
// Notify AgentGrain about position opening
|
||||
await NotifyAgentGrainAsync(AgentSummaryEventType.PositionOpened,
|
||||
$"Signal: {signal.Identifier}");
|
||||
await NotifyAgentAndPlatformGrainAsync(AgentSummaryEventType.PositionOpened,
|
||||
$"Signal: {signal.Identifier}", position);
|
||||
|
||||
// Publish TradeExecutedEvent for the opening trade (this handles both position opening and trade execution)
|
||||
await PublishTradeExecutedEventAsync(position.Open, position, signal.Identifier, 0);
|
||||
|
||||
Logger.LogInformation($"Position requested");
|
||||
return position; // Return the created position without adding to list
|
||||
@@ -893,7 +924,7 @@ public class TradingBotBase : ITradingBot
|
||||
{
|
||||
List<Position> positions = null;
|
||||
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
|
||||
async exchangeService => { positions = (await exchangeService.GetBrokerPositions(Account)).ToList(); });
|
||||
async exchangeService => { positions = [.. await exchangeService.GetBrokerPositions(Account)]; });
|
||||
if (!positions.Any(p => p.Ticker == Config.Ticker))
|
||||
{
|
||||
return true;
|
||||
@@ -905,7 +936,7 @@ public class TradingBotBase : ITradingBot
|
||||
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
|
||||
async exchangeService =>
|
||||
{
|
||||
orders = (await exchangeService.GetOpenOrders(Account, Config.Ticker)).ToList();
|
||||
orders = [.. await exchangeService.GetOpenOrders(Account, Config.Ticker)];
|
||||
});
|
||||
|
||||
var reason = $"Cannot open position. There is already a position open for {Config.Ticker} on the broker.";
|
||||
@@ -930,50 +961,6 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actual closing date of a position by checking which trade (Stop Loss or Take Profit) was executed.
|
||||
/// </summary>
|
||||
/// <param name="position">The finished position</param>
|
||||
/// <returns>The date when the position was closed, or null if cannot be determined</returns>
|
||||
private DateTime? GetPositionClosingDate(Position position)
|
||||
{
|
||||
if (!position.IsFinished())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check which trade actually closed the position
|
||||
if (position.StopLoss?.Status == TradeStatus.Filled && position.StopLoss.Date != default)
|
||||
{
|
||||
return position.StopLoss.Date;
|
||||
}
|
||||
|
||||
if (position.TakeProfit1?.Status == TradeStatus.Filled && position.TakeProfit1.Date != default)
|
||||
{
|
||||
return position.TakeProfit1.Date;
|
||||
}
|
||||
|
||||
if (position.TakeProfit2?.Status == TradeStatus.Filled && position.TakeProfit2.Date != default)
|
||||
{
|
||||
return position.TakeProfit2.Date;
|
||||
}
|
||||
|
||||
// Fallback: if we can't determine the exact closing trade, use the latest date available
|
||||
var availableDates = new List<DateTime>();
|
||||
|
||||
if (position.StopLoss?.Date != default)
|
||||
availableDates.Add(position.StopLoss.Date);
|
||||
|
||||
if (position.TakeProfit1?.Date != default)
|
||||
availableDates.Add(position.TakeProfit1.Date);
|
||||
|
||||
if (position.TakeProfit2?.Date != default)
|
||||
availableDates.Add(position.TakeProfit2.Date);
|
||||
|
||||
return availableDates.Any() ? availableDates.Max() : position.Open.Date;
|
||||
}
|
||||
|
||||
public async Task CloseTrade(LightSignal signal, Position position, Trade tradeToClose, decimal lastPrice,
|
||||
bool tradeClosingPosition = false)
|
||||
{
|
||||
@@ -1217,15 +1204,22 @@ public class TradingBotBase : ITradingBot
|
||||
Config.BotTradingBalance += position.ProfitAndLoss.Realized;
|
||||
|
||||
Logger.LogInformation(
|
||||
$"💰 **Balance Updated**\nNew bot trading balance: `${Config.BotTradingBalance:F2}`");
|
||||
string.Format("💰 **Balance Updated**\nNew bot trading balance: `${0:F2}`", Config.BotTradingBalance));
|
||||
}
|
||||
|
||||
// Notify AgentGrain about position closing
|
||||
var pnlInfo = position.ProfitAndLoss?.Realized != null
|
||||
? $"PnL: {position.ProfitAndLoss.Realized:F2}"
|
||||
? string.Format("PnL: {0:F2}", position.ProfitAndLoss.Realized)
|
||||
: "PnL: Unknown";
|
||||
await NotifyAgentGrainAsync(AgentSummaryEventType.PositionClosed,
|
||||
$"Signal: {position.SignalIdentifier}, {pnlInfo}");
|
||||
await NotifyAgentAndPlatformGrainAsync(AgentSummaryEventType.PositionClosed,
|
||||
string.Format("Signal: {0}, {1}", position.SignalIdentifier, pnlInfo), position);
|
||||
|
||||
// Publish TradeExecutedEvent for the closing trade
|
||||
var closingTrade = GetClosingTrade(position);
|
||||
if (closingTrade != null)
|
||||
{
|
||||
await PublishTradeExecutedEventAsync(closingTrade, position, position.SignalIdentifier, position.ProfitAndLoss?.Realized ?? 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1425,6 +1419,20 @@ public class TradingBotBase : ITradingBot
|
||||
Config.IsForWatchingOnly = !Config.IsForWatchingOnly;
|
||||
await LogInformation(
|
||||
$"🔄 **Watch Mode Toggle**\nBot: `{Config.Name}`\nWatch Only: `{(Config.IsForWatchingOnly ? "ON" : "OFF")}`");
|
||||
|
||||
// Notify platform summary about strategy count change
|
||||
await NotifyPlatformSummaryAboutStrategyCount();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles bot stopping and notifies platform summary
|
||||
/// </summary>
|
||||
public async Task StopBot()
|
||||
{
|
||||
await LogInformation($"🛑 **Bot Stopped**\nBot: `{Config.Name}`\nTicker: `{Config.Ticker}`");
|
||||
|
||||
// Notify platform summary about strategy count change
|
||||
await NotifyPlatformSummaryAboutStrategyCount();
|
||||
}
|
||||
|
||||
public async Task LogInformation(string message)
|
||||
@@ -1909,41 +1917,183 @@ public class TradingBotBase : ITradingBot
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a notification to the AgentGrain to trigger summary updates
|
||||
/// Publishes a TradeExecutedEvent to the platform summary grain
|
||||
/// </summary>
|
||||
/// <param name="eventType">The type of event (e.g., PositionOpened, PositionClosed)</param>
|
||||
/// <param name="additionalData">Optional additional context data</param>
|
||||
private async Task NotifyAgentGrainAsync(AgentSummaryEventType eventType, string additionalData = null)
|
||||
/// <param name="trade">The trade that was executed</param>
|
||||
/// <param name="position">The position this trade belongs to</param>
|
||||
/// <param name="signalIdentifier">The signal identifier</param>
|
||||
/// <param name="pnl">The PnL for this trade</param>
|
||||
private async Task PublishTradeExecutedEventAsync(Trade trade, Position position, string signalIdentifier, decimal pnl)
|
||||
{
|
||||
if (Config.IsForBacktest || Account?.User == null)
|
||||
if (Config.IsForBacktest)
|
||||
{
|
||||
return; // Skip notifications for backtest or when no user context
|
||||
return; // Skip notifications for backtest
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await ServiceScopeHelpers.WithScopedService<IGrainFactory>(_scopeFactory, async grainFactory =>
|
||||
{
|
||||
var agentGrain = grainFactory.GetGrain<IAgentGrain>(Account.User.Id);
|
||||
|
||||
var updateEvent = new AgentSummaryUpdateEvent
|
||||
var platformGrain = grainFactory.GetGrain<IPlatformSummaryGrain>("platform-summary");
|
||||
var tradeExecutedEvent = new TradeExecutedEvent
|
||||
{
|
||||
UserId = Account.User.Id,
|
||||
BotId = Identifier,
|
||||
EventType = eventType,
|
||||
Timestamp = DateTime.UtcNow,
|
||||
AdditionalData = additionalData
|
||||
TradeId = Guid.NewGuid(), // Generate new ID for the event
|
||||
PositionId = position.Identifier,
|
||||
StrategyId = position.InitiatorIdentifier,
|
||||
Ticker = position.Ticker,
|
||||
Volume = trade.Price * trade.Quantity * trade.Leverage,
|
||||
PnL = pnl,
|
||||
Fee = trade.Fee,
|
||||
Direction = trade.Direction
|
||||
};
|
||||
|
||||
await agentGrain.OnAgentSummaryUpdateAsync(updateEvent);
|
||||
|
||||
Logger.LogDebug("Sent agent notification: {EventType} for bot {BotId}", eventType, Identifier);
|
||||
await platformGrain.OnTradeExecutedAsync(tradeExecutedEvent);
|
||||
Logger.LogDebug("Published TradeExecutedEvent for trade {TradeId} in position {PositionId}", tradeExecutedEvent.TradeId, position.Identifier);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "Failed to send agent notification: {EventType} for bot {BotId}", eventType,
|
||||
Identifier);
|
||||
Logger.LogError(ex, "Failed to publish TradeExecutedEvent for position {PositionId}", position.Identifier);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the trade that was used to close the position
|
||||
/// </summary>
|
||||
/// <param name="position">The position to check</param>
|
||||
/// <returns>The closing trade, or null if none found</returns>
|
||||
private Trade GetClosingTrade(Position position)
|
||||
{
|
||||
// Check which trade was used to close the position
|
||||
if (position.StopLoss?.Status == TradeStatus.Filled)
|
||||
{
|
||||
return position.StopLoss;
|
||||
}
|
||||
else if (position.TakeProfit1?.Status == TradeStatus.Filled)
|
||||
{
|
||||
return position.TakeProfit1;
|
||||
}
|
||||
else if (position.TakeProfit2?.Status == TradeStatus.Filled)
|
||||
{
|
||||
return position.TakeProfit2;
|
||||
}
|
||||
|
||||
// If no specific closing trade is found, create a synthetic one based on the position
|
||||
// This handles cases where the position was closed manually or by the exchange
|
||||
if (position.ProfitAndLoss?.Realized != null)
|
||||
{
|
||||
var closeDirection = position.OriginDirection == TradeDirection.Long ? TradeDirection.Short : TradeDirection.Long;
|
||||
return new Trade(
|
||||
DateTime.UtcNow,
|
||||
closeDirection,
|
||||
TradeStatus.Filled,
|
||||
TradeType.StopMarket,
|
||||
position.Ticker,
|
||||
position.Open.Quantity,
|
||||
position.Open.Price, // Use open price as approximation
|
||||
position.Open.Leverage,
|
||||
"synthetic-close",
|
||||
"Position closed"
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the platform summary grain about active strategy count changes
|
||||
/// </summary>
|
||||
private async Task NotifyPlatformSummaryAboutStrategyCount()
|
||||
{
|
||||
if (Config.IsForBacktest)
|
||||
{
|
||||
return; // Skip notifications for backtest
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await ServiceScopeHelpers.WithScopedService<IGrainFactory>(_scopeFactory, async grainFactory =>
|
||||
{
|
||||
var platformGrain = grainFactory.GetGrain<IPlatformSummaryGrain>("platform-summary");
|
||||
|
||||
// Get current active strategy count from the platform
|
||||
var currentSummary = await platformGrain.GetPlatformSummaryAsync();
|
||||
var currentActiveCount = currentSummary.TotalActiveStrategies;
|
||||
|
||||
// Update the count (this will trigger a refresh if needed)
|
||||
await platformGrain.UpdateActiveStrategyCountAsync(currentActiveCount);
|
||||
|
||||
Logger.LogDebug("Notified platform summary about strategy count: {Count}", currentActiveCount);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "Failed to notify platform summary about strategy count");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies both AgentGrain and PlatformSummaryGrain about bot events
|
||||
/// </summary>
|
||||
/// <param name="eventType">The type of event (e.g., PositionOpened, PositionClosed)</param>
|
||||
/// <param name="additionalData">Optional additional context data</param>
|
||||
/// <param name="position">Optional position data for platform summary events</param>
|
||||
private async Task NotifyAgentAndPlatformGrainAsync(AgentSummaryEventType eventType, string additionalData = null, Position position = null)
|
||||
{
|
||||
if (Config.IsForBacktest)
|
||||
{
|
||||
return; // Skip notifications for backtest
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
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
|
||||
{
|
||||
UserId = Account.User.Id,
|
||||
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 platformGrain = grainFactory.GetGrain<IPlatformSummaryGrain>("platform-summary");
|
||||
|
||||
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);
|
||||
break;
|
||||
|
||||
case AgentSummaryEventType.PositionClosed when position != null:
|
||||
var positionClosedEvent = new PositionClosedEvent
|
||||
{
|
||||
PositionId = position.Identifier,
|
||||
Ticker = position.Ticker,
|
||||
RealizedPnL = position.ProfitAndLoss?.Realized ?? 0,
|
||||
Volume = position.Open.Price * position.Open.Quantity * position.Open.Leverage,
|
||||
};
|
||||
await platformGrain.OnPositionClosedAsync(positionClosedEvent);
|
||||
Logger.LogDebug("Sent platform position closed notification for position {PositionId}", position.Identifier);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "Failed to send notifications: {EventType} for bot {BotId}", eventType, Identifier);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user