Clean namings and namespace
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
using Managing.Application.Abstractions;
|
||||
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,
|
||||
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);
|
||||
}
|
||||
|
||||
return request.Position;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger?.LogError(ex, "Error closing position: {Message} \n Stacktrace : {StackTrace}", ex.Message,
|
||||
ex.StackTrace);
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Application.Trading.Commands;
|
||||
using Managing.Domain.Trades;
|
||||
using MediatR;
|
||||
|
||||
namespace Managing.Application.Trading.Handlers;
|
||||
|
||||
public class GetTradeCommandHandler : IRequestHandler<GetTradeCommand, Trade>
|
||||
{
|
||||
private readonly IExchangeService _exchangeService;
|
||||
private readonly IAccountService _accountService;
|
||||
public GetTradeCommandHandler(IExchangeService exchangeService, IAccountService accountService)
|
||||
{
|
||||
_exchangeService = exchangeService;
|
||||
_accountService = accountService;
|
||||
}
|
||||
|
||||
public async Task<Trade> Handle(GetTradeCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var account = await _accountService.GetAccount(request.AccountName, true, false);
|
||||
return await _exchangeService.GetTrade(account, request.ExchangeOrderId, request.Ticker);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Application.Trading.Commands;
|
||||
using Managing.Domain.Trades;
|
||||
using MediatR;
|
||||
|
||||
namespace Managing.Application.Trading.Handlers
|
||||
{
|
||||
public class GetTradesCommandHandler : IRequestHandler<GetTradesCommand, List<Trade>>
|
||||
{
|
||||
private readonly IExchangeService _exchangeService;
|
||||
private readonly IAccountService _accountService;
|
||||
|
||||
public GetTradesCommandHandler(IExchangeService exchangeService, IAccountService accountService)
|
||||
{
|
||||
_exchangeService = exchangeService;
|
||||
_accountService = accountService;
|
||||
}
|
||||
|
||||
public async Task<List<Trade>> Handle(GetTradesCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var account = await _accountService.GetAccount(request.AccountName, true, false);
|
||||
return await _exchangeService.GetTrades(account, request.Ticker);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
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.Handlers
|
||||
{
|
||||
public class OpenPositionCommandHandler(
|
||||
IExchangeService exchangeService,
|
||||
IAccountService accountService,
|
||||
ITradingService tradingService)
|
||||
: ICommandHandler<OpenPositionRequest, Position>
|
||||
{
|
||||
public async Task<Position> 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(), 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user