Files
managing-apps/src/Managing.Application/Trading/OpenPositionCommandHandler.cs
Oda 082ae8714b Trading bot grain (#33)
* Trading bot Grain

* Fix a bit more of the trading bot

* Advance on the tradingbot grain

* Fix build

* Fix db script

* Fix user login

* Fix a bit backtest

* Fix cooldown and backtest

* start fixing bot start

* Fix startup

* Setup local db

* Fix build and update candles and scenario

* Add bot registry

* Add reminder

* Updateing the grains

* fix bootstraping

* Save stats on tick

* Save bot data every tick

* Fix serialization

* fix save bot stats

* Fix get candles

* use dict instead of list for position

* Switch hashset to dict

* Fix a bit

* Fix bot launch and bot view

* add migrations

* Remove the tolist

* Add agent grain

* Save agent summary

* clean

* Add save bot

* Update get bots

* Add get bots

* Fix stop/restart

* fix Update config

* Update scanner table on new backtest saved

* Fix backtestRowDetails.tsx

* Fix agentIndex

* Update agentIndex

* Fix more things

* Update user cache

* Fix

* Fix account load/start/restart/run
2025-08-05 04:07:06 +07:00

122 lines
4.8 KiB
C#

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<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);
}
}
}