110 lines
4.7 KiB
C#
110 lines
4.7 KiB
C#
using Managing.Application.Abstractions;
|
|
using Managing.Application.Abstractions.Grains;
|
|
using Managing.Application.Abstractions.Services;
|
|
using Managing.Application.Trading.Commands;
|
|
using Managing.Domain.Shared.Helpers;
|
|
using Managing.Domain.Trades;
|
|
using Microsoft.Extensions.Logging;
|
|
using static Managing.Common.Enums;
|
|
|
|
namespace Managing.Application.Trading.Handlers;
|
|
|
|
public class ClosePositionCommandHandler(
|
|
IExchangeService exchangeService,
|
|
IAccountService accountService,
|
|
ITradingService tradingService,
|
|
IGrainFactory? grainFactory = null,
|
|
ILogger<ClosePositionCommandHandler> logger = null)
|
|
: ICommandHandler<ClosePositionCommand, Position>
|
|
{
|
|
public async Task<Position> Handle(ClosePositionCommand request)
|
|
{
|
|
try
|
|
{
|
|
// Get Trade
|
|
var account = await accountService.GetAccount(request.Position.AccountName, false, false);
|
|
if (request.Position == null)
|
|
{
|
|
_ = await exchangeService.CancelOrder(account, request.Position.Ticker);
|
|
return request.Position;
|
|
}
|
|
|
|
var isForPaperTrading = request.IsForBacktest;
|
|
|
|
var lastPrice = request.Position.Initiator == PositionInitiator.PaperTrading
|
|
? request.ExecutionPrice.GetValueOrDefault()
|
|
: await exchangeService.GetPrice(account, request.Position.Ticker, DateTime.UtcNow);
|
|
|
|
// Check if position still open
|
|
if (!request.IsForBacktest)
|
|
{
|
|
var p = (await exchangeService.GetBrokerPositions(account))
|
|
.FirstOrDefault(x => x.Ticker == request.Position.Ticker);
|
|
|
|
// Position not available on the broker, so be sure to update the status
|
|
if (p == null)
|
|
{
|
|
request.Position.Status = PositionStatus.Finished;
|
|
request.Position.ProfitAndLoss =
|
|
TradingBox.GetProfitAndLoss(request.Position, request.Position.Open.Quantity, lastPrice,
|
|
request.Position.Open.Leverage);
|
|
await tradingService.UpdatePositionAsync(request.Position);
|
|
return request.Position;
|
|
}
|
|
}
|
|
|
|
|
|
var closeRequestedOrders =
|
|
true; // TODO: For gmx no need to close orders since they are closed automatically
|
|
|
|
// Close market
|
|
var closedPosition =
|
|
await exchangeService.ClosePosition(account, request.Position, lastPrice, isForPaperTrading);
|
|
|
|
if (closeRequestedOrders || closedPosition.Status == (TradeStatus.PendingOpen | TradeStatus.Filled))
|
|
{
|
|
request.Position.Status = PositionStatus.Finished;
|
|
request.Position.ProfitAndLoss =
|
|
TradingBox.GetProfitAndLoss(request.Position, closedPosition.Quantity, lastPrice,
|
|
request.Position.Open.Leverage);
|
|
|
|
if (!request.IsForBacktest)
|
|
await tradingService.UpdatePositionAsync(request.Position);
|
|
|
|
// Notify platform summary about the closed position
|
|
try
|
|
{
|
|
var platformGrain = grainFactory?.GetGrain<IPlatformSummaryGrain>("platform-summary");
|
|
if (platformGrain != null)
|
|
{
|
|
var positionClosedEvent = new PositionClosedEvent
|
|
{
|
|
PositionId = request.Position.Identifier,
|
|
Ticker = request.Position.Ticker,
|
|
RealizedPnL = request.Position.ProfitAndLoss?.Realized ?? 0,
|
|
Volume = closedPosition.Quantity * lastPrice * request.Position.Open.Leverage,
|
|
InitialVolume = request.Position.Open.Quantity * request.Position.Open.Price * request.Position.Open.Leverage
|
|
};
|
|
|
|
await platformGrain.OnPositionClosedAsync(positionClosedEvent);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
SentrySdk.CaptureException(ex);
|
|
logger?.LogError(ex, "Failed to notify platform summary about position closure for position {PositionId}", request.Position.Identifier);
|
|
}
|
|
}
|
|
|
|
return request.Position;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger?.LogError(ex, "Error closing position: {Message} \n Stacktrace : {StackTrace}", ex.Message,
|
|
ex.StackTrace);
|
|
|
|
SentrySdk.CaptureException(ex);
|
|
throw;
|
|
}
|
|
}
|
|
} |