using Managing.Application.Abstractions; using Managing.Application.Abstractions.Services; using Managing.Application.Trading.Commands; using Managing.Common; using Managing.Domain.Shared.Helpers; using Managing.Domain.Trades; using static Managing.Common.Enums; namespace Managing.Application.Trading { public class OpenPositionCommandHandler( IExchangeService exchangeService, IAccountService accountService, ITradingService tradingService) : ICommandHandler { public async Task Handle(OpenPositionRequest request) { var account = await accountService.GetAccount(request.AccountName, hideSecrets: false, getBalance: false); if (!request.IsForPaperTrading) { // var cancelOrderResult = await exchangeService.CancelOrder(account, request.Ticker); // if (!cancelOrderResult) // { // throw new Exception($"Not able to close all orders for {request.Ticker}"); // } } var initiator = request.IsForPaperTrading ? PositionInitiator.PaperTrading : request.Initiator; var position = new Position(Guid.NewGuid().ToString(), request.AccountName, request.Direction, request.Ticker, request.MoneyManagement, initiator, request.Date, request.User); if (!string.IsNullOrEmpty(request.SignalIdentifier)) { position.SignalIdentifier = request.SignalIdentifier; } // Always use BotTradingBalance directly as the balance to risk decimal balanceToRisk = request.AmountToTrade; // Minimum check if (balanceToRisk < Constants.GMX.Config.MinimumPositionAmount) { throw new Exception( $"Bot trading balance of {balanceToRisk} USD is less than the minimum {Constants.GMX.Config.MinimumPositionAmount} USD required to trade"); } var price = request.IsForPaperTrading && request.Price.HasValue ? request.Price.Value : await exchangeService.GetPrice(account, request.Ticker, DateTime.Now); var quantity = balanceToRisk / price; var openPrice = request.IsForPaperTrading || request.Price.HasValue ? request.Price.Value : await exchangeService.GetBestPrice(account, request.Ticker, price, quantity, request.Direction); // Determine SL/TP Prices var stopLossPrice = RiskHelpers.GetStopLossPrice(request.Direction, openPrice, request.MoneyManagement); var takeProfitPrice = RiskHelpers.GetTakeProfitPrice(request.Direction, openPrice, request.MoneyManagement); var trade = await exchangeService.OpenTrade( account, request.Ticker, request.Direction, openPrice, quantity, request.MoneyManagement.Leverage, TradeType.Limit, isForPaperTrading: request.IsForPaperTrading, currentDate: request.Date, stopLossPrice: stopLossPrice, takeProfitPrice: takeProfitPrice); position.Open = trade; var closeDirection = request.Direction == TradeDirection.Long ? TradeDirection.Short : TradeDirection.Long; // Stop loss position.StopLoss = exchangeService.BuildEmptyTrade( request.Ticker, stopLossPrice, position.Open.Quantity, closeDirection, request.MoneyManagement.Leverage, TradeType.StopLoss, request.Date, TradeStatus.Requested); // Take profit position.TakeProfit1 = exchangeService.BuildEmptyTrade( request.Ticker, takeProfitPrice, quantity, closeDirection, request.MoneyManagement.Leverage, TradeType.TakeProfit, request.Date, TradeStatus.Requested); position.Status = IsOpenTradeHandled(position.Open.Status, account.Exchange) ? position.Status : PositionStatus.Rejected; if (!request.IsForPaperTrading) { await tradingService.InsertPositionAsync(position); } return position; } private static bool IsOpenTradeHandled(TradeStatus tradeStatus, TradingExchanges exchange) { return tradeStatus == TradeStatus.Filled || (exchange == TradingExchanges.Evm && tradeStatus == TradeStatus.Requested); } } }