Add user to position + fix few things

This commit is contained in:
2025-04-24 19:46:21 +07:00
parent 76b8b7ebb6
commit c22c925087
17 changed files with 357 additions and 238 deletions

BIN
src/.DS_Store vendored

Binary file not shown.

View File

@@ -151,17 +151,17 @@ builder.Services.AddSwaggerGen(options =>
builder.WebHost.SetupDiscordBot(); builder.WebHost.SetupDiscordBot();
// builder.Services.AddHostedService<FeeWorker>(); // builder.Services.AddHostedService<FeeWorker>();
// builder.Services.AddHostedService<PositionManagerWorker>(); builder.Services.AddHostedService<PositionManagerWorker>();
// builder.Services.AddHostedService<PositionFetcher>(); // builder.Services.AddHostedService<PositionFetcher>();
// builder.Services.AddHostedService<PricesFiveMinutesWorker>(); // builder.Services.AddHostedService<PricesFiveMinutesWorker>();
builder.Services.AddHostedService<PricesFifteenMinutesWorker>(); // builder.Services.AddHostedService<PricesFifteenMinutesWorker>();
builder.Services.AddHostedService<PricesOneHourWorker>(); // builder.Services.AddHostedService<PricesOneHourWorker>();
builder.Services.AddHostedService<PricesFourHoursWorker>(); // builder.Services.AddHostedService<PricesFourHoursWorker>();
builder.Services.AddHostedService<PricesOneDayWorker>(); // builder.Services.AddHostedService<PricesOneDayWorker>();
// builder.Services.AddHostedService<SpotlightWorker>(); // // builder.Services.AddHostedService<SpotlightWorker>();
// builder.Services.AddHostedService<TraderWatcher>(); // // builder.Services.AddHostedService<TraderWatcher>();
builder.Services.AddHostedService<LeaderboardWorker>(); // builder.Services.AddHostedService<LeaderboardWorker>();
builder.Services.AddHostedService<FundingRatesWatcher>(); // builder.Services.AddHostedService<FundingRatesWatcher>();
// App // App
var app = builder.Build(); var app = builder.Build();

View File

@@ -3,6 +3,7 @@ using Managing.Application.Abstractions.Services;
using Managing.Application.Workers; using Managing.Application.Workers;
using Managing.Application.Workers.Abstractions; using Managing.Application.Workers.Abstractions;
using Managing.Domain.Accounts; using Managing.Domain.Accounts;
using Managing.Domain.Shared.Helpers;
using Managing.Domain.Trades; using Managing.Domain.Trades;
using Newtonsoft.Json; using Newtonsoft.Json;
using static Managing.Common.Enums; using static Managing.Common.Enums;
@@ -58,51 +59,84 @@ public class PositionManagerWorker : BaseWorker<PositionManagerWorker>
position.Status = PositionStatus.Updating; position.Status = PositionStatus.Updating;
_tradingService.UpdatePosition(position); _tradingService.UpdatePosition(position);
_logger.LogDebug("Processing risk orders for {Direction} position opened at {OpenDate}", _logger.LogDebug("Verifying position on exchange for {Direction} position opened at {OpenDate}",
position.OriginDirection, position.Date.ToString("o")); position.OriginDirection, position.Date.ToString("o"));
var account = await _accountService.GetAccount(position.AccountName, false, false); var account = await _accountService.GetAccount(position.AccountName, false, false);
var success = true;
// Process and update trades // Get positions directly from broker
var updatedSl = await ProcessTrade(account, position.StopLoss, "SL", async () => var brokerPositions = await _exchangeService.GetBrokerPositions(account);
await _exchangeService.OpenStopLoss(account, position.Ticker, position.OriginDirection, var exchangePosition = brokerPositions.FirstOrDefault(p =>
position.StopLoss.Price, position.StopLoss.Quantity, false, DateTime.UtcNow)); p.Ticker == position.Ticker &&
p.OriginDirection == position.OriginDirection);
if (updatedSl != null) if (exchangePosition == null)
{ {
position.StopLoss = updatedSl; _logger.LogWarning("Position not found on exchange - marking as canceled");
success &= updatedSl.Status.IsActive(); position.Status = PositionStatus.Canceled;
_tradingService.UpdatePosition(position);
continue;
} }
var updatedTp1 = await ProcessTrade(account, position.TakeProfit1, "TP1", async () => // Update with exchange data if available
await _exchangeService.OpenTakeProfit(account, position.Ticker, position.OriginDirection, if (exchangePosition.StopLoss != null)
position.TakeProfit1.Price, position.TakeProfit1.Quantity, false, DateTime.UtcNow));
if (updatedTp1 != null)
{ {
position.TakeProfit1 = updatedTp1; _logger.LogInformation("Stop Loss found on exchange - ID: {OrderId}", exchangePosition.StopLoss.ExchangeOrderId);
success &= updatedTp1.Status.IsActive(); position.StopLoss = exchangePosition.StopLoss;
} }
else
Trade? updatedTp2 = null;
if (position.TakeProfit2 != null)
{ {
updatedTp2 = await ProcessTrade(account, position.TakeProfit2, "TP2", async () => _logger.LogWarning("Stop Loss not found on exchange - creating new SL order");
await _exchangeService.OpenTakeProfit(account, position.Ticker, position.OriginDirection, var updatedSl = await _exchangeService.OpenStopLoss(account, position.Ticker, position.OriginDirection,
position.TakeProfit2.Price, position.TakeProfit2.Quantity, false, DateTime.UtcNow)); position.StopLoss.Price, position.StopLoss.Quantity, false, DateTime.UtcNow);
if (updatedTp2 != null) if (updatedSl != null)
{ {
position.TakeProfit2 = updatedTp2; position.StopLoss = updatedSl;
success &= updatedTp2.Status.IsActive() || updatedTp2.Status == TradeStatus.Cancelled;
} }
} }
// Update position status based on trade states if (exchangePosition.TakeProfit1 != null)
position.Status = success && AllTradesActive(position) {
? PositionStatus.Filled _logger.LogInformation("Take Profit found on exchange - ID: {OrderId}", exchangePosition.TakeProfit1.ExchangeOrderId);
: PositionStatus.PartiallyFilled; position.TakeProfit1 = exchangePosition.TakeProfit1;
}
else
{
_logger.LogWarning("Take Profit not found on exchange - creating new TP order");
var updatedTp1 = await _exchangeService.OpenTakeProfit(account, position.Ticker, position.OriginDirection,
position.TakeProfit1.Price, position.TakeProfit1.Quantity, false, DateTime.UtcNow);
if (updatedTp1 != null)
{
position.TakeProfit1 = updatedTp1;
}
}
// Handle TP2 if it exists
if (position.TakeProfit2 != null)
{
if (exchangePosition.TakeProfit2 != null)
{
_logger.LogInformation("Take Profit 2 found on exchange - ID: {OrderId}", exchangePosition.TakeProfit2.ExchangeOrderId);
position.TakeProfit2 = exchangePosition.TakeProfit2;
}
else
{
_logger.LogWarning("Take Profit 2 not found on exchange - creating new TP2 order");
var updatedTp2 = await _exchangeService.OpenTakeProfit(account, position.Ticker, position.OriginDirection,
position.TakeProfit2.Price, position.TakeProfit2.Quantity, false, DateTime.UtcNow);
if (updatedTp2 != null)
{
position.TakeProfit2 = updatedTp2;
}
}
}
// Update position status based on verification results
var success = AllTradesActive(position);
position.Status = success ? PositionStatus.Filled : PositionStatus.PartiallyFilled;
_logger.LogInformation("Final position status: {Status}", position.Status); _logger.LogInformation("Final position status: {Status}", position.Status);
} }
@@ -119,52 +153,13 @@ public class PositionManagerWorker : BaseWorker<PositionManagerWorker>
} }
} }
private async Task<Trade?> ProcessTrade(Account account, Trade trade, string tradeType, Func<Task<Trade>> createTrade)
{
try
{
// 1. Check existing status on exchange
var exchangeTrade = await _exchangeService.GetTrade(account, trade.ExchangeOrderId, trade.Ticker);
if (exchangeTrade != null && exchangeTrade.Status.IsActive())
{
_logger.LogInformation("{TradeType} already exists on exchange - Status: {Status}",
tradeType, exchangeTrade.Status);
return exchangeTrade;
}
// 2. Only create new order if in pending state
if (trade.Status != TradeStatus.PendingOpen)
{
_logger.LogWarning("{TradeType} creation skipped - Invalid status: {Status}",
tradeType, trade.Status);
return null;
}
// 3. Create new order
var newTrade = await createTrade();
if (newTrade?.Status == TradeStatus.Requested)
{
_logger.LogInformation("{TradeType} successfully created - ExchangeID: {ExchangeOrderId}",
tradeType, newTrade.ExchangeOrderId);
return newTrade;
}
_logger.LogError("{TradeType} creation failed - Null response or invalid status", tradeType);
return null;
}
catch (Exception ex)
{
_logger.LogError(ex, "{TradeType} processing failed", tradeType);
return null;
}
}
private bool AllTradesActive(Position position) private bool AllTradesActive(Position position)
{ {
return position.StopLoss.Status.IsActive() && return position.StopLoss.Status.IsActive() &&
position.TakeProfit1.Status.IsActive() && position.TakeProfit1.Status.IsActive() &&
(position.TakeProfit2?.Status.IsActive() ?? true); (position.TakeProfit2?.Status.IsActive() ?? true);
} }
private async Task ManageFilledPositions() private async Task ManageFilledPositions()
{ {
var positions = GetPositions(PositionStatus.Filled); var positions = GetPositions(PositionStatus.Filled);
@@ -181,22 +176,98 @@ public class PositionManagerWorker : BaseWorker<PositionManagerWorker>
position.Status = PositionStatus.Updating; position.Status = PositionStatus.Updating;
_tradingService.UpdatePosition(position); _tradingService.UpdatePosition(position);
_logger.LogInformation("Managing filled position - Direction: {Direction}, Open Since: {OpenDate}", _logger.LogInformation("Checking position state on exchange - Direction: {Direction}, Open Since: {OpenDate}",
position.OriginDirection, position.Date.ToString("yyyy-MM-dd HH:mm:ss")); position.OriginDirection, position.Date.ToString("yyyy-MM-dd HH:mm:ss"));
var account = await GetAccount(position.AccountName); var account = await GetAccount(position.AccountName);
// Perform position management // Check if position still exists on broker
var updatedPosition = await _tradingService.ManagePosition(account, position); var brokerPositions = await _exchangeService.GetBrokerPositions(account);
var exchangePosition = brokerPositions.FirstOrDefault(p =>
p.Ticker == position.Ticker &&
p.OriginDirection == position.OriginDirection);
// Log status changes if they occurred if (exchangePosition == null)
if (updatedPosition.Status != position.Status)
{ {
_logger.LogInformation("Position status updated: {OldStatus} → {NewStatus}", // Position no longer on exchange - it has been closed
position.Status, updatedPosition.Status); _logger.LogInformation("Position no longer on exchange - marking as finished");
position.Status = PositionStatus.Finished;
// Determine if SL or TP was hit by checking which one is missing
if (exchangePosition?.StopLoss == null && position.StopLoss.Status != TradeStatus.Filled)
{
_logger.LogInformation("Stop loss appears to have been hit");
position.StopLoss.SetStatus(TradeStatus.Filled);
position.ProfitAndLoss = TradingBox.GetProfitAndLoss(
position,
position.StopLoss.Quantity,
position.StopLoss.Price,
position.Open.Leverage);
}
else if (exchangePosition?.TakeProfit1 == null && position.TakeProfit1.Status != TradeStatus.Filled)
{
_logger.LogInformation("Take profit 1 appears to have been hit");
position.TakeProfit1.SetStatus(TradeStatus.Filled);
position.ProfitAndLoss = TradingBox.GetProfitAndLoss(
position,
position.TakeProfit1.Quantity,
position.TakeProfit1.Price,
position.Open.Leverage);
}
else if (exchangePosition?.TakeProfit2 == null && position.TakeProfit2?.Status != TradeStatus.Filled)
{
_logger.LogInformation("Take profit 2 appears to have been hit");
position.TakeProfit2.SetStatus(TradeStatus.Filled);
position.ProfitAndLoss = TradingBox.GetProfitAndLoss(
position,
position.TakeProfit2.Quantity,
position.TakeProfit2.Price,
position.Open.Leverage);
}
// Cancel any remaining orders
await _exchangeService.CancelOrder(account, position.Ticker);
}
else
{
// Position still exists - update with exchange data
_logger.LogInformation("Position still active on exchange with quantity {Quantity}",
exchangePosition.Open?.Quantity ?? 0);
// Update our position with broker data
if (exchangePosition.Open != null)
position.Open = exchangePosition.Open;
if (exchangePosition.StopLoss != null)
position.StopLoss = exchangePosition.StopLoss;
if (exchangePosition.TakeProfit1 != null)
position.TakeProfit1 = exchangePosition.TakeProfit1;
if (exchangePosition.TakeProfit2 != null)
position.TakeProfit2 = exchangePosition.TakeProfit2;
if (exchangePosition.ProfitAndLoss != null)
position.ProfitAndLoss = exchangePosition.ProfitAndLoss;
else
{
// Calculate PNL if not provided
var lastPrice = _exchangeService.GetPrice(account, position.Ticker, DateTime.UtcNow);
position.ProfitAndLoss = TradingBox.GetProfitAndLoss(
position,
position.Open.Quantity,
lastPrice,
position.Open.Leverage);
}
_logger.LogInformation("Updated position from exchange - PNL: {PNL}",
position.ProfitAndLoss?.Net ?? 0);
// Keep status as Filled
position.Status = PositionStatus.Filled;
} }
_tradingService.UpdatePosition(updatedPosition); _tradingService.UpdatePosition(position);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -243,45 +314,47 @@ public class PositionManagerWorker : BaseWorker<PositionManagerWorker>
_tradingService.UpdatePosition(position); _tradingService.UpdatePosition(position);
var account = await GetAccount(position.AccountName); var account = await GetAccount(position.AccountName);
var trade = await _exchangeService.GetTrade(account.Key, position.Open.ExchangeOrderId, position.Ticker);
var openTrade = position.Open;
if (trade.Status == TradeStatus.PendingOpen || trade.Status == TradeStatus.Requested) // Check if position exists on broker
var brokerPositions = await _exchangeService.GetBrokerPositions(account);
var exchangePosition = brokerPositions.FirstOrDefault(p =>
p.Ticker == position.Ticker &&
p.OriginDirection == position.OriginDirection);
if (exchangePosition != null)
{ {
// Position staleness check // Position is confirmed on exchange
if (position.Date < DateTime.UtcNow.AddDays(-1)) position.Status = PositionStatus.PartiallyFilled;
{ position.Open = exchangePosition.Open; // Use the exchange data
position.Status = PositionStatus.Canceled; _tradingService.UpdatePosition(position);
_tradingService.UpdatePosition(position);
_logger.LogWarning("[{Identifier}] Position canceled - stale since {PositionAge} days", _logger.LogInformation("[{Identifier}] Position found on exchange - moving to partially filled status",
position.Identifier, position.Identifier);
(DateTime.UtcNow - position.Date).TotalDays); continue;
} }
else
{ // Position not found on exchange - check for staleness
// Reset status for retry if (position.Date < DateTime.UtcNow.AddDays(-1))
position.Status = PositionStatus.New; {
_tradingService.UpdatePosition(position); position.Status = PositionStatus.Canceled;
_logger.LogInformation("[{Identifier}] Awaiting order fill - {Ticker} (0/{ExpectedQuantity})", _tradingService.UpdatePosition(position);
position.Identifier, _logger.LogWarning("[{Identifier}] Position canceled - stale since {PositionAge} days",
position.Ticker, openTrade.Quantity); position.Identifier,
} (DateTime.UtcNow - position.Date).TotalDays);
} }
else else
{ {
position.Status = PositionStatus.PartiallyFilled; // Reset status to try again
position.Open = openTrade; position.Status = PositionStatus.New;
// Position is now open, now waiting to open SLTP
_tradingService.UpdatePosition(position); _tradingService.UpdatePosition(position);
_logger.LogInformation("[{Identifier}] Position not yet found on exchange - awaiting fill",
_logger.LogInformation("[{Identifier}] Position now open ",
position.Identifier); position.Identifier);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Error processing position {Identifier}", position.Identifier); _logger.LogError(ex, "Error processing position {Identifier}", position.Identifier);
// Consider resetting to New status for retry if needed // Reset to New status for retry
position.Status = PositionStatus.New; position.Status = PositionStatus.New;
_tradingService.UpdatePosition(position); _tradingService.UpdatePosition(position);
} }

View File

@@ -136,9 +136,9 @@ public class TradingController : BaseController
nameof(moneyManagementName)); nameof(moneyManagementName));
} }
var user = await GetUser();
if (moneyManagement != null) if (moneyManagement != null)
{ {
var user = await GetUser();
moneyManagement = await _moneyManagementService.GetMoneyMangement(user, moneyManagementName); moneyManagement = await _moneyManagementService.GetMoneyMangement(user, moneyManagementName);
} }
@@ -149,6 +149,7 @@ public class TradingController : BaseController
ticker, ticker,
PositionInitiator.User, PositionInitiator.User,
DateTime.UtcNow, DateTime.UtcNow,
user,
isForPaperTrading: isForPaperTrading, isForPaperTrading: isForPaperTrading,
price: openPrice); price: openPrice);
var result = await _openTradeCommandHandler.Handle(command); var result = await _openTradeCommandHandler.Handle(command);

View File

@@ -1,6 +1,7 @@
using Managing.Application.Trading; using Managing.Application.Trading;
using Managing.Application.Trading.Commands; using Managing.Application.Trading.Commands;
using Managing.Domain.Trades; using Managing.Domain.Trades;
using Managing.Domain.Users;
using Moq; using Moq;
using Xunit; using Xunit;
using static Managing.Common.Enums; using static Managing.Common.Enums;
@@ -23,7 +24,9 @@ public class PositionTests : BaseTests
Ticker.BTC, Ticker.BTC,
PositionInitiator.User, PositionInitiator.User,
DateTime.UtcNow, DateTime.UtcNow,
isForPaperTrading: false); _account.User,
isForPaperTrading: false,
signalIdentifier: new Guid().ToString());
var handler = new OpenPositionCommandHandler( var handler = new OpenPositionCommandHandler(
_exchangeService, _exchangeService,
_accountService.Object, _accountService.Object,
@@ -41,8 +44,8 @@ public class PositionTests : BaseTests
// _ = new GetAccountPositioqwnInfoListOutputDTO().DecodeOutput(hexPositions).d // _ = new GetAccountPositioqwnInfoListOutputDTO().DecodeOutput(hexPositions).d
// //
var openTrade = await _exchangeService.GetTrade(_account, "", Ticker.BTC); var openTrade = await _exchangeService.GetTrade(_account, "", Ticker.BTC);
var position = new Position("", TradeDirection.Long, Ticker.BTC, MoneyManagement, PositionInitiator.User, var position = new Position("", "", TradeDirection.Long, Ticker.BTC, MoneyManagement, PositionInitiator.User,
DateTime.UtcNow) DateTime.UtcNow, new User())
{ {
Open = openTrade Open = openTrade
}; };

View File

@@ -1,4 +1,5 @@
using Managing.Domain.Trades; using Managing.Domain.Trades;
using Managing.Domain.Users;
using Xunit; using Xunit;
using static Managing.Common.Enums; using static Managing.Common.Enums;
@@ -9,7 +10,8 @@ namespace Managing.Application.Tests
[Theory] [Theory]
[InlineData(1, 100, 110, 10)] [InlineData(1, 100, 110, 10)]
[InlineData(2, 100, 110, 20)] [InlineData(2, 100, 110, 20)]
public void Should_Return_Correct_ProfitAndLoss_Amount(decimal quantity, decimal price, decimal exitPrice, decimal expectedResult) public void Should_Return_Correct_ProfitAndLoss_Amount(decimal quantity, decimal price, decimal exitPrice,
decimal expectedResult)
{ {
// Arrange // Arrange
var init = new List<Tuple<decimal, decimal>>(); var init = new List<Tuple<decimal, decimal>>();
@@ -42,7 +44,8 @@ namespace Managing.Application.Tests
position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection);
// Trigger Stop Loss // Trigger Stop Loss
position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.StopLoss.Price, position.OriginDirection); position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.StopLoss.Price,
position.OriginDirection);
// Assert // Assert
Assert.Equal(20, position.ProfitAndLoss.Realized); Assert.Equal(20, position.ProfitAndLoss.Realized);
@@ -65,7 +68,8 @@ namespace Managing.Application.Tests
position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection);
// Trigger Stop Loss // Trigger Stop Loss
position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.StopLoss.Price, position.OriginDirection); position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.StopLoss.Price,
position.OriginDirection);
// Assert // Assert
Assert.Equal(3.97005582759999752M, position.ProfitAndLoss.Realized); Assert.Equal(3.97005582759999752M, position.ProfitAndLoss.Realized);
@@ -127,8 +131,10 @@ namespace Managing.Application.Tests
// Act // Act
position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection);
position.ProfitAndLoss.AddFill(-position.TakeProfit1.Quantity, position.TakeProfit1.Price, TradeDirection.Short); position.ProfitAndLoss.AddFill(-position.TakeProfit1.Quantity, position.TakeProfit1.Price,
position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.StopLoss.Price, TradeDirection.Short); TradeDirection.Short);
position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.StopLoss.Price,
TradeDirection.Short);
// Assert // Assert
Assert.Equal(20, position.ProfitAndLoss.Realized); Assert.Equal(20, position.ProfitAndLoss.Realized);
@@ -151,7 +157,8 @@ namespace Managing.Application.Tests
position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection);
// Trigger Stop Loss // Trigger Stop Loss
position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.TakeProfit2.Price, TradeDirection.Short); position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.TakeProfit2.Price,
TradeDirection.Short);
// Assert // Assert
Assert.Equal(120, position.ProfitAndLoss.Realized); Assert.Equal(120, position.ProfitAndLoss.Realized);
@@ -174,7 +181,8 @@ namespace Managing.Application.Tests
position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection);
// Trigger Stop Loss // Trigger Stop Loss
position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.StopLoss.Price, TradeDirection.Long); position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.StopLoss.Price,
TradeDirection.Long);
// Assert // Assert
Assert.Equal(20, position.ProfitAndLoss.Realized); Assert.Equal(20, position.ProfitAndLoss.Realized);
@@ -197,7 +205,8 @@ namespace Managing.Application.Tests
position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection); position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection);
// Trigger Stop Loss // Trigger Stop Loss
position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.TakeProfit2.Price, TradeDirection.Long); position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.TakeProfit2.Price,
TradeDirection.Long);
// Assert // Assert
Assert.Equal(120, position.ProfitAndLoss.Realized); Assert.Equal(120, position.ProfitAndLoss.Realized);
@@ -205,46 +214,53 @@ namespace Managing.Application.Tests
private static Position GetFakeShortPosition() private static Position GetFakeShortPosition()
{ {
return new Position("FakeAccount", TradeDirection.Short, Ticker.BTC, null, PositionInitiator.PaperTrading, DateTime.UtcNow) return new Position("", "FakeAccount", TradeDirection.Short, Ticker.BTC, null,
PositionInitiator.PaperTrading, DateTime.UtcNow, new User())
{ {
Open = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.Filled, Open = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.Filled,
TradeType.Market, Ticker.ADA, 10, 100, 1, "OpenOrderId", ""), TradeType.Market, Ticker.ADA, 10, 100, 1, "OpenOrderId", ""),
StopLoss = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen, StopLoss = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen,
TradeType.StopMarket, Ticker.ADA, 10, 110, 1, "StopLossOrderId", ""), TradeType.StopMarket, Ticker.ADA, 10, 110, 1, "StopLossOrderId", ""),
TakeProfit1 = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen, TakeProfit1 = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen,
TradeType.TakeProfitLimit, Ticker.ADA, 6, 90, 1, "TakeProfit1OrderId", ""), TradeType.TakeProfitLimit, Ticker.ADA, 6, 90, 1, "TakeProfit1OrderId", ""),
TakeProfit2 = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen, TakeProfit2 = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen,
TradeType.TakeProfitLimit, Ticker.ADA, 4, 85, 1, "TakeProfit1OrderId", "") TradeType.TakeProfitLimit, Ticker.ADA, 4, 85, 1, "TakeProfit1OrderId", "")
}; };
} }
private static Position GetSolanaLongPosition() private static Position GetSolanaLongPosition()
{ {
return new Position("FakeAccount", TradeDirection.Long, Ticker.BTC, null, PositionInitiator.PaperTrading, DateTime.UtcNow) return new Position("", "FakeAccount", TradeDirection.Long, Ticker.BTC, null,
PositionInitiator.PaperTrading, DateTime.UtcNow, new User())
{ {
Open = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.Filled, Open = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.Filled,
TradeType.Market, Ticker.ADA, (decimal)6.0800904000245037980887037491, (decimal)81.6200, 1, "OpenOrderId", ""), TradeType.Market, Ticker.ADA, (decimal)6.0800904000245037980887037491, (decimal)81.6200, 1,
"OpenOrderId", ""),
StopLoss = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.PendingOpen, StopLoss = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.PendingOpen,
TradeType.StopMarket, Ticker.ADA, (decimal)3.6480542400147022788532222495, (decimal)79.987600, 1, "StopLossOrderId", ""), TradeType.StopMarket, Ticker.ADA, (decimal)3.6480542400147022788532222495, (decimal)79.987600, 1,
"StopLossOrderId", ""),
TakeProfit1 = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.PendingOpen, TakeProfit1 = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.PendingOpen,
TradeType.TakeProfitLimit, Ticker.ADA, (decimal)2.4320361600098015192354814996, (decimal)85.701000, 1, "TakeProfit1OrderId", ""), TradeType.TakeProfitLimit, Ticker.ADA, (decimal)2.4320361600098015192354814996, (decimal)85.701000,
1, "TakeProfit1OrderId", ""),
TakeProfit2 = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.PendingOpen, TakeProfit2 = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.PendingOpen,
TradeType.TakeProfitLimit, Ticker.ADA, (decimal)3.6480542400147022788532222495, (decimal)89.782000, 1, "TakeProfit1OrderId", "") TradeType.TakeProfitLimit, Ticker.ADA, (decimal)3.6480542400147022788532222495, (decimal)89.782000,
1, "TakeProfit1OrderId", "")
}; };
} }
private static Position GetFakeLongPosition() private static Position GetFakeLongPosition()
{ {
return new Position("FakeAccount", TradeDirection.Long, Ticker.BTC, null, PositionInitiator.PaperTrading, DateTime.UtcNow) return new Position("", "FakeAccount", TradeDirection.Long, Ticker.BTC, null,
PositionInitiator.PaperTrading, DateTime.UtcNow, new User())
{ {
Open = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.Filled, Open = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.Filled,
TradeType.Market, Ticker.ADA, 10, 100, 1, "OpenOrderId", ""), TradeType.Market, Ticker.ADA, 10, 100, 1, "OpenOrderId", ""),
StopLoss = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen, StopLoss = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen,
TradeType.StopMarket, Ticker.ADA, 10, 90, 1, "StopLossOrderId", ""), TradeType.StopMarket, Ticker.ADA, 10, 90, 1, "StopLossOrderId", ""),
TakeProfit1 = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen, TakeProfit1 = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen,
TradeType.TakeProfitLimit, Ticker.ADA, 6, 110, 1, "TakeProfit1OrderId", ""), TradeType.TakeProfitLimit, Ticker.ADA, 6, 110, 1, "TakeProfit1OrderId", ""),
TakeProfit2 = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen, TakeProfit2 = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen,
TradeType.TakeProfitLimit, Ticker.ADA, 4, 115, 1, "TakeProfit1OrderId", "") TradeType.TakeProfitLimit, Ticker.ADA, 4, 115, 1, "TakeProfit1OrderId", "")
}; };
} }
} }

View File

@@ -331,7 +331,7 @@ public class TradingBot : Bot, ITradingBot
: TradingService.GetPositionByIdentifier(positionForSignal.Identifier); : TradingService.GetPositionByIdentifier(positionForSignal.Identifier);
var positionsExchange = IsForBacktest var positionsExchange = IsForBacktest
? new List<Position>{position} ? new List<Position> { position }
: await TradingService.GetBrokerPositions(Account); : await TradingService.GetBrokerPositions(Account);
if (!IsForBacktest) if (!IsForBacktest)
@@ -344,11 +344,13 @@ public class TradingBot : Bot, ITradingBot
var orders = await ExchangeService.GetOpenOrders(Account, Ticker); var orders = await ExchangeService.GetOpenOrders(Account, Ticker);
if (orders.Any()) if (orders.Any())
{ {
await LogInformation($"Cannot update Position. Position is still waiting for opening. There is {orders.Count()} open orders."); await LogInformation(
$"Cannot update Position. Position is still waiting for opening. There is {orders.Count()} open orders.");
} }
else else
{ {
await LogWarning($"Cannot update Position. No position on exchange and no orders. Position {signal.Identifier} might be closed already."); await LogWarning(
$"Cannot update Position. No position on exchange and no orders. Position {signal.Identifier} might be closed already.");
await HandleClosedPosition(positionForSignal); await HandleClosedPosition(positionForSignal);
} }
} }
@@ -525,15 +527,16 @@ public class TradingBot : Bot, ITradingBot
Ticker, Ticker,
IsForBacktest ? PositionInitiator.PaperTrading : PositionInitiator.Bot, IsForBacktest ? PositionInitiator.PaperTrading : PositionInitiator.Bot,
signal.Date, signal.Date,
User,
IsForBacktest, IsForBacktest,
lastPrice, lastPrice,
balance: WalletBalances.LastOrDefault().Value, balance: WalletBalances.LastOrDefault().Value,
fee: Fee); fee: Fee,
signalIdentifier: signal.Identifier);
var position = await new OpenPositionCommandHandler(ExchangeService, AccountService, TradingService) var position = await new OpenPositionCommandHandler(ExchangeService, AccountService, TradingService)
.Handle(command); .Handle(command);
if (position != null) if (position != null)
{ {
position.SignalIdentifier = signal.Identifier; position.SignalIdentifier = signal.Identifier;
@@ -839,7 +842,7 @@ public class TradingBot : Bot, ITradingBot
// Create a fake signal for manual position opening // Create a fake signal for manual position opening
var signal = new Signal(Ticker, direction, Confidence.Low, lastCandle, lastCandle.Date, TradingExchanges.GmxV2, var signal = new Signal(Ticker, direction, Confidence.Low, lastCandle, lastCandle.Date, TradingExchanges.GmxV2,
StrategyType.Stc, SignalType.Signal); StrategyType.Stc, SignalType.Signal);
signal.Status = SignalStatus.WaitingForPosition; // Ensure status is correct signal.Status = SignalStatus.WaitingForPosition; // Ensure status is correct
signal.User = Account.User; // Assign user signal.User = Account.User; // Assign user

View File

@@ -1,5 +1,6 @@
using Managing.Domain.MoneyManagements; using Managing.Domain.MoneyManagements;
using Managing.Domain.Trades; using Managing.Domain.Trades;
using Managing.Domain.Users;
using MediatR; using MediatR;
using static Managing.Common.Enums; using static Managing.Common.Enums;
@@ -14,11 +15,13 @@ namespace Managing.Application.Trading.Commands
Ticker ticker, Ticker ticker,
PositionInitiator initiator, PositionInitiator initiator,
DateTime date, DateTime date,
User user,
bool isForPaperTrading = false, bool isForPaperTrading = false,
decimal? price = null, decimal? price = null,
decimal? balance = 1000, decimal? balance = 1000,
decimal? fee = null, decimal? fee = null,
bool? ignoreSLTP = false) bool? ignoreSLTP = false,
string signalIdentifier = null)
{ {
AccountName = accountName; AccountName = accountName;
MoneyManagement = moneyManagement; MoneyManagement = moneyManagement;
@@ -31,8 +34,11 @@ namespace Managing.Application.Trading.Commands
Initiator = initiator; Initiator = initiator;
Fee = fee; Fee = fee;
IgnoreSLTP = ignoreSLTP; IgnoreSLTP = ignoreSLTP;
User = user;
SignalIdentifier = signalIdentifier;
} }
public string SignalIdentifier { get; set; }
public string AccountName { get; } public string AccountName { get; }
public MoneyManagement MoneyManagement { get; } public MoneyManagement MoneyManagement { get; }
public TradeDirection Direction { get; } public TradeDirection Direction { get; }
@@ -44,5 +50,6 @@ namespace Managing.Application.Trading.Commands
public decimal? Balance { get; } public decimal? Balance { get; }
public DateTime Date { get; set; } public DateTime Date { get; set; }
public PositionInitiator Initiator { get; internal set; } public PositionInitiator Initiator { get; internal set; }
public User User { get; internal set; }
} }
} }

View File

@@ -26,8 +26,15 @@ namespace Managing.Application.Trading
} }
var initiator = request.IsForPaperTrading ? PositionInitiator.PaperTrading : request.Initiator; var initiator = request.IsForPaperTrading ? PositionInitiator.PaperTrading : request.Initiator;
var position = new Position(request.AccountName, request.Direction, request.Ticker, request.MoneyManagement, var position = new Position(new Guid().ToString(), request.AccountName, request.Direction, request.Ticker,
initiator, request.Date); request.MoneyManagement,
initiator, request.Date, request.User);
if (!string.IsNullOrEmpty(request.SignalIdentifier))
{
position.SignalIdentifier = request.SignalIdentifier;
}
var balance = request.IsForPaperTrading var balance = request.IsForPaperTrading
? request.Balance.GetValueOrDefault() ? request.Balance.GetValueOrDefault()
: exchangeService.GetBalance(account, request.IsForPaperTrading).Result; : exchangeService.GetBalance(account, request.IsForPaperTrading).Result;
@@ -63,7 +70,7 @@ namespace Managing.Application.Trading
TradeType.Limit, TradeType.Limit,
isForPaperTrading: request.IsForPaperTrading, isForPaperTrading: request.IsForPaperTrading,
currentDate: request.Date, currentDate: request.Date,
stopLossPrice: stopLossPrice, // Pass determined SL price stopLossPrice: stopLossPrice, // Pass determined SL price
takeProfitPrice: takeProfitPrice); // Pass determined TP price takeProfitPrice: takeProfitPrice); // Pass determined TP price
//trade.Fee = TradingHelpers.GetFeeAmount(fee, openPrice * quantity, account.Exchange); //trade.Fee = TradingHelpers.GetFeeAmount(fee, openPrice * quantity, account.Exchange);

View File

@@ -1,4 +1,5 @@
using Managing.Application.Abstractions; using InfluxDB.Client.Api.Domain;
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services; using Managing.Application.Abstractions.Services;
using Managing.Application.Trading.Commands; using Managing.Application.Trading.Commands;
using Managing.Application.Trading; using Managing.Application.Trading;
@@ -63,10 +64,9 @@ public class OpenPosition : FlowBase
var Positions = JsonConvert.DeserializeObject<List<Position>>(_cacheService.GetValue(POSITIONS_KEY)); var Positions = JsonConvert.DeserializeObject<List<Position>>(_cacheService.GetValue(POSITIONS_KEY));
var Signals = JsonConvert.DeserializeObject<HashSet<Signal>>(_cacheService.GetValue(POSITIONS_KEY)); var Signals = JsonConvert.DeserializeObject<HashSet<Signal>>(_cacheService.GetValue(POSITIONS_KEY));
Fee = _cacheService.GetOrSave(FEE_KEY, () => Fee = _cacheService.GetOrSave(FEE_KEY,
{ () => { return _tradingService.GetFee(Account, OpenPositionParameters.IsForBacktest); },
return _tradingService.GetFee(Account, OpenPositionParameters.IsForBacktest); TimeSpan.FromDays(1));
}, TimeSpan.FromDays(1));
await ExecuteOpenPosition(signal, Positions, Signals, Candles, Account); await ExecuteOpenPosition(signal, Positions, Signals, Candles, Account);
@@ -82,13 +82,16 @@ public class OpenPosition : FlowBase
} }
} }
private async Task ExecuteOpenPosition(Signal signal, List<Position> positions, HashSet<Signal> signals, HashSet<Candle> candles, Account account) private async Task ExecuteOpenPosition(Signal signal, List<Position> positions, HashSet<Signal> signals,
HashSet<Candle> candles, Account account)
{ {
// Check if a position is already open // Check if a position is already open
var openedPosition = positions.FirstOrDefault(p => p.Status == PositionStatus.Filled var openedPosition = positions.FirstOrDefault(p => p.Status == PositionStatus.Filled
&& p.SignalIdentifier != signal.Identifier); && p.SignalIdentifier != signal.Identifier);
var lastPrice = OpenPositionParameters.IsForBacktest ? candles.Last().Close : _exchangeService.GetPrice(account, signal.Ticker, DateTime.UtcNow); var lastPrice = OpenPositionParameters.IsForBacktest
? candles.Last().Close
: _exchangeService.GetPrice(account, signal.Ticker, DateTime.UtcNow);
// If position open // If position open
if (openedPosition != null) if (openedPosition != null)
@@ -110,7 +113,8 @@ public class OpenPosition : FlowBase
{ {
//await LogInformation("Try to flip the position because of an opposite direction signal"); //await LogInformation("Try to flip the position because of an opposite direction signal");
//await CloseTrade(previousSignal, openedPosition, openedPosition.Open, lastPrice, true); //await CloseTrade(previousSignal, openedPosition, openedPosition.Open, lastPrice, true);
positions.FirstOrDefault(s => s.Identifier == previousSignal.Identifier).Status = PositionStatus.Flipped; positions.FirstOrDefault(s => s.Identifier == previousSignal.Identifier).Status =
PositionStatus.Flipped;
await ExecuteOpenPosition(signal, positions, signals, candles, account); await ExecuteOpenPosition(signal, positions, signals, candles, account);
//await LogInformation($"Position {previousSignal.Identifier} flipped by {signal.Identifier} at {lastPrice}$"); //await LogInformation($"Position {previousSignal.Identifier} flipped by {signal.Identifier} at {lastPrice}$");
@@ -134,23 +138,27 @@ public class OpenPosition : FlowBase
try try
{ {
var moneyManagement = await _settingsRepository.GetMoneyManagement(OpenPositionParameters.MoneyManagementName); var moneyManagement =
var WalletBalances = JsonConvert.DeserializeObject<Dictionary<DateTime, decimal>>(_cacheService.GetValue(WALLET_BALANCES)); await _settingsRepository.GetMoneyManagement(OpenPositionParameters.MoneyManagementName);
var WalletBalances =
JsonConvert.DeserializeObject<Dictionary<DateTime, decimal>>(
_cacheService.GetValue(WALLET_BALANCES));
var command = new OpenPositionRequest( var command = new OpenPositionRequest(
OpenPositionParameters.AccountName, OpenPositionParameters.AccountName,
moneyManagement, moneyManagement,
signal.Direction, signal.Direction,
signal.Ticker, signal.Ticker,
PositionInitiator.Bot, PositionInitiator.Bot,
signal.Date, signal.Date,
account.User,
OpenPositionParameters.IsForBacktest, OpenPositionParameters.IsForBacktest,
lastPrice, lastPrice,
balance: WalletBalances.LastOrDefault().Value, balance: WalletBalances.LastOrDefault().Value,
fee: Fee); fee: Fee);
var position = await new OpenPositionCommandHandler(_exchangeService, _accountService, _tradingService) var position = await new OpenPositionCommandHandler(_exchangeService, _accountService, _tradingService)
.Handle(command); .Handle(command);
if (position != null) if (position != null)
{ {
@@ -158,18 +166,21 @@ public class OpenPosition : FlowBase
{ {
position.SignalIdentifier = signal.Identifier; position.SignalIdentifier = signal.Identifier;
positions.Add(position); positions.Add(position);
signals.FirstOrDefault(s => s.Identifier == signal.Identifier).Status = SignalStatus.PositionOpen; signals.FirstOrDefault(s => s.Identifier == signal.Identifier).Status =
SignalStatus.PositionOpen;
if (!OpenPositionParameters.IsForBacktest) if (!OpenPositionParameters.IsForBacktest)
{ {
await _messengerService.SendPosition(position); await _messengerService.SendPosition(position);
} }
Output = JsonConvert.SerializeObject(position); Output = JsonConvert.SerializeObject(position);
//Logger.LogInformation($"Position requested"); //Logger.LogInformation($"Position requested");
} }
else else
{ {
positions.FirstOrDefault(s => s.Identifier == signal.Identifier).Status = PositionStatus.Rejected; positions.FirstOrDefault(s => s.Identifier == signal.Identifier).Status =
PositionStatus.Rejected;
signals.FirstOrDefault(s => s.Identifier == signal.Identifier).Status = SignalStatus.Expired; signals.FirstOrDefault(s => s.Identifier == signal.Identifier).Status = SignalStatus.Expired;
} }
} }
@@ -180,18 +191,18 @@ public class OpenPosition : FlowBase
//await LogWarning($"Cannot open trade : {ex.Message}"); //await LogWarning($"Cannot open trade : {ex.Message}");
} }
} }
} }
private bool CanOpenPosition(Signal signal, List<Position> positions, HashSet<Signal> signals, HashSet<Candle> candles) private bool CanOpenPosition(Signal signal, List<Position> positions, HashSet<Signal> signals,
HashSet<Candle> candles)
{ {
if (positions.Count == 0) if (positions.Count == 0)
return true; return true;
var lastPosition = positions.LastOrDefault(p => p.IsFinished() var lastPosition = positions.LastOrDefault(p => p.IsFinished()
&& p.SignalIdentifier != signal.Identifier && p.SignalIdentifier != signal.Identifier
&& p.ProfitAndLoss.Realized < 0 && p.ProfitAndLoss.Realized < 0
&& p.OriginDirection == signal.Direction); && p.OriginDirection == signal.Direction);
if (lastPosition == null) if (lastPosition == null)
return true; return true;

View File

@@ -7,9 +7,10 @@ namespace Managing.Domain.Trades
{ {
public class Position public class Position
{ {
public Position(string accountName, TradeDirection originDirection, Ticker ticker, MoneyManagement moneyManagement, PositionInitiator positionInitiator, DateTime date) public Position(string identifier, string accountName, TradeDirection originDirection, Ticker ticker,
MoneyManagement moneyManagement, PositionInitiator positionInitiator, DateTime date, User user)
{ {
Identifier = Guid.NewGuid().ToString(); Identifier = identifier;
AccountName = accountName; AccountName = accountName;
OriginDirection = originDirection; OriginDirection = originDirection;
Ticker = ticker; Ticker = ticker;
@@ -17,34 +18,24 @@ namespace Managing.Domain.Trades
Initiator = positionInitiator; Initiator = positionInitiator;
Date = date; Date = date;
Status = Initiator == PositionInitiator.PaperTrading ? PositionStatus.Filled : PositionStatus.New; Status = Initiator == PositionInitiator.PaperTrading ? PositionStatus.Filled : PositionStatus.New;
User = user;
} }
[Required] [Required] public string AccountName { get; }
public string AccountName { get; } [Required] public DateTime Date { get; set; }
[Required] [Required] public TradeDirection OriginDirection { get; }
public DateTime Date { get; set; } [Required] public Ticker Ticker { get; }
[Required] [Required] public MoneyManagement MoneyManagement { get; }
public TradeDirection OriginDirection { get; } [Required] public Trade Open { get; set; }
[Required] [Required] public Trade StopLoss { get; set; }
public Ticker Ticker { get; } [Required] public Trade TakeProfit1 { get; set; }
[Required]
public MoneyManagement MoneyManagement { get; }
[Required]
public Trade Open { get; set; }
[Required]
public Trade StopLoss { get; set; }
[Required]
public Trade TakeProfit1 { get; set; }
public Trade TakeProfit2 { get; set; } public Trade TakeProfit2 { get; set; }
public ProfitAndLoss ProfitAndLoss { get; set; } public ProfitAndLoss ProfitAndLoss { get; set; }
[Required] [Required] public PositionStatus Status { get; set; }
public PositionStatus Status { get; set; }
public string SignalIdentifier { get; set; } public string SignalIdentifier { get; set; }
[Required] [Required] public string Identifier { get; set; }
public string Identifier { get; set; } [Required] public PositionInitiator Initiator { get; }
[Required] [Required] public User User { get; set; }
public PositionInitiator Initiator { get; }
public User User { get; set; }
public bool IsFinished() public bool IsFinished()
{ {

View File

@@ -257,7 +257,7 @@ public static class MongoMappers
MoneyManagement = Map(position.MoneyManagement), MoneyManagement = Map(position.MoneyManagement),
Initiator = position.Initiator, Initiator = position.Initiator,
Ticker = position.Ticker, Ticker = position.Ticker,
User = position.User != null ? Map(position.User) : null User = Map(position.User)
}; };
if (position.StopLoss != null) if (position.StopLoss != null)
@@ -294,8 +294,8 @@ public static class MongoMappers
public static Position Map(PositionDto dto) public static Position Map(PositionDto dto)
{ {
var position = new Position(dto.AccountName, originDirection: dto.OriginDirection, dto.Ticker, var position = new Position(dto.Identifier, dto.AccountName, originDirection: dto.OriginDirection, dto.Ticker,
Map(dto.MoneyManagement), dto.Initiator, dto.Date) Map(dto.MoneyManagement), dto.Initiator, dto.Date, Map(dto.User))
{ {
Open = new Trade(date: dto.Open.Date, direction: dto.Open.Direction, status: dto.Open.Status, Open = new Trade(date: dto.Open.Date, direction: dto.Open.Direction, status: dto.Open.Status,
tradeType: dto.Open.TradeType, ticker: dto.Open.Ticker, quantity: dto.Open.Quantity, tradeType: dto.Open.TradeType, ticker: dto.Open.Ticker, quantity: dto.Open.Quantity,
@@ -305,7 +305,7 @@ public static class MongoMappers
Status = dto.Status, Status = dto.Status,
SignalIdentifier = dto.SignalIdentifier, SignalIdentifier = dto.SignalIdentifier,
Identifier = dto.Identifier, Identifier = dto.Identifier,
User = dto.User != null ? Map(dto.User) : null User = Map(dto.User)
}; };
if (dto.StopLoss != null) if (dto.StopLoss != null)

View File

@@ -7,6 +7,7 @@ using Managing.Common;
using Managing.Core; using Managing.Core;
using Managing.Domain.MoneyManagements; using Managing.Domain.MoneyManagements;
using Managing.Domain.Trades; using Managing.Domain.Trades;
using Managing.Domain.Users;
using static Managing.Common.Enums; using static Managing.Common.Enums;
namespace Managing.Infrastructure.Messengers.Discord namespace Managing.Infrastructure.Messengers.Discord
@@ -73,7 +74,7 @@ namespace Managing.Infrastructure.Messengers.Discord
TakeProfit = takeProfit.GetValueOrDefault(), TakeProfit = takeProfit.GetValueOrDefault(),
}; };
var tradeCommand = new OpenPositionRequest(accountName, moneymanagement, direction, ticker, var tradeCommand = new OpenPositionRequest(accountName, moneymanagement, direction, ticker,
PositionInitiator.User, DateTime.UtcNow); PositionInitiator.User, DateTime.UtcNow, new User());
var result = await _openTradeCommandHandler.Handle(tradeCommand); var result = await _openTradeCommandHandler.Handle(tradeCommand);
var builder = new ComponentBuilder().WithButton("Close Position", var builder = new ComponentBuilder().WithButton("Close Position",
$"{Constants.DiscordButtonAction.ClosePosition}|{result.Open.ExchangeOrderId}"); $"{Constants.DiscordButtonAction.ClosePosition}|{result.Open.ExchangeOrderId}");

View File

@@ -273,6 +273,7 @@ namespace Managing.Infrastructure.Messengers.Discord
ticker, ticker,
initiator, initiator,
DateTime.UtcNow, DateTime.UtcNow,
defaultUser,
ignoreSLTP: ignoreSLTP); ignoreSLTP: ignoreSLTP);
var position = await new OpenPositionCommandHandler(exchangeService, accountService, tradingService) var position = await new OpenPositionCommandHandler(exchangeService, accountService, tradingService)
.Handle(tradeCommand); .Handle(tradeCommand);

View File

@@ -8,6 +8,7 @@ using Managing.Infrastructure.Evm.Models.Gmx.v2;
using Managing.Infrastructure.Evm.Models.Proxy; using Managing.Infrastructure.Evm.Models.Proxy;
using Nethereum.Web3; using Nethereum.Web3;
using Managing.Domain.MoneyManagements; using Managing.Domain.MoneyManagements;
using Managing.Domain.Users;
using static Managing.Common.Enums; using static Managing.Common.Enums;
namespace Managing.Infrastructure.Evm.Services.Gmx; namespace Managing.Infrastructure.Evm.Services.Gmx;
@@ -161,12 +162,13 @@ internal static class GmxV2Mappers
{ {
try try
{ {
var position = new Position("", var position = new Position("", "",
MiscExtensions.ParseEnum<TradeDirection>(gmxPosition.Direction), MiscExtensions.ParseEnum<TradeDirection>(gmxPosition.Direction),
MiscExtensions.ParseEnum<Ticker>(gmxPosition.Ticker), MiscExtensions.ParseEnum<Ticker>(gmxPosition.Ticker),
new MoneyManagement(), new MoneyManagement(),
PositionInitiator.User, PositionInitiator.User,
gmxPosition.Date); gmxPosition.Date,
new User());
position.Open = Map(gmxPosition.Open); position.Open = Map(gmxPosition.Open);
position.TakeProfit1 = Map(gmxPosition.TakeProfit1); position.TakeProfit1 = Map(gmxPosition.TakeProfit1);
position.StopLoss = Map(gmxPosition.StopLoss); position.StopLoss = Map(gmxPosition.StopLoss);
@@ -180,9 +182,11 @@ internal static class GmxV2Mappers
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"Error mapping GMX position {gmxPosition?.ExchangeOrderId}: {ex.Message} \n StackTrace: {ex.StackTrace}"); Console.WriteLine(
$"Error mapping GMX position {gmxPosition?.ExchangeOrderId}: {ex.Message} \n StackTrace: {ex.StackTrace}");
} }
} }
return positions; return positions;
} }

View File

@@ -35,7 +35,7 @@ namespace Managing.Infrastructure.Evm.Services
endpoint = $"/{endpoint}"; endpoint = $"/{endpoint}";
} }
var url = $"{_settings.BaseUrl}privy{endpoint}"; var url = $"{_settings.BaseUrl}/api/privy{endpoint}";
try try
{ {
@@ -62,7 +62,7 @@ namespace Managing.Infrastructure.Evm.Services
endpoint = $"/{endpoint}"; endpoint = $"/{endpoint}";
} }
var url = $"{_settings.BaseUrl}privy{endpoint}"; var url = $"{_settings.BaseUrl}/api/privy{endpoint}";
if (payload != null) if (payload != null)
{ {
@@ -166,7 +166,8 @@ namespace Managing.Infrastructure.Evm.Services
try try
{ {
// Try to parse as structured error if it doesn't match the simple format // Try to parse as structured error if it doesn't match the simple format
var structuredErrorResponse = await response.Content.ReadFromJsonAsync<Web3ProxyErrorResponse>(_jsonOptions); var structuredErrorResponse =
await response.Content.ReadFromJsonAsync<Web3ProxyErrorResponse>(_jsonOptions);
if (structuredErrorResponse?.ErrorDetails != null) if (structuredErrorResponse?.ErrorDetails != null)
{ {

View File

@@ -321,7 +321,7 @@ export async function increaseOrderHelper(
referralCodeForTxn: params.referralCodeForTxn, referralCodeForTxn: params.referralCodeForTxn,
triggerPrice: params.limitPrice, triggerPrice: params.limitPrice,
collateralTokenAddress: collateralToken.address, collateralTokenAddress: collateralToken.address,
isLong: true, isLong: params.isLong,
receiveTokenAddress: collateralTokenAddress, receiveTokenAddress: collateralTokenAddress,
indexToken: marketInfo.indexToken, indexToken: marketInfo.indexToken,
marketInfo, marketInfo,