diff --git a/src/Managing.Api/Controllers/MoneyManagementController.cs b/src/Managing.Api/Controllers/MoneyManagementController.cs
index 8f4da960..fc63dfd0 100644
--- a/src/Managing.Api/Controllers/MoneyManagementController.cs
+++ b/src/Managing.Api/Controllers/MoneyManagementController.cs
@@ -1,5 +1,4 @@
-using Managing.Application.Abstractions;
-using Managing.Application.Abstractions.Services;
+using Managing.Application.Abstractions.Services;
using Managing.Domain.MoneyManagements;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@@ -84,12 +83,12 @@ public class MoneyManagementController : BaseController
{
var user = await GetUser();
var result = await _moneyManagementService.GetMoneyMangement(user, name);
-
+
if (result == null)
{
return NotFound($"Money management strategy '{name}' not found");
}
-
+
return Ok(result);
}
catch (Exception ex)
@@ -110,12 +109,12 @@ public class MoneyManagementController : BaseController
{
var user = await GetUser();
var result = await _moneyManagementService.DeleteMoneyManagement(user, name);
-
+
if (!result)
{
return NotFound($"Money management strategy '{name}' not found or could not be deleted");
}
-
+
return Ok(new { success = true, message = $"Money management strategy '{name}' deleted successfully" });
}
catch (Exception ex)
diff --git a/src/Managing.Api/Controllers/SentryTestController.cs b/src/Managing.Api/Controllers/SentryTestController.cs
index 13517f20..0d8b480d 100644
--- a/src/Managing.Api/Controllers/SentryTestController.cs
+++ b/src/Managing.Api/Controllers/SentryTestController.cs
@@ -1,6 +1,4 @@
using Microsoft.AspNetCore.Mvc;
-using Sentry;
-using System;
namespace Managing.Api.Controllers
{
@@ -26,22 +24,22 @@ namespace Managing.Api.Controllers
{
// Add breadcrumbs for context
SentrySdk.AddBreadcrumb("About to capture test exception", "test");
-
+
// Add context to the error
SentrySdk.ConfigureScope(scope =>
{
scope.SetTag("test_type", "manual_exception");
scope.SetExtra("timestamp", DateTime.Now);
});
-
+
// Log to both Serilog and Sentry
_logger.LogError(ex, "Test exception captured in SentryTestController");
-
+
// Explicitly capture exception
SentrySdk.CaptureException(ex);
-
- return Ok(new
- {
+
+ return Ok(new
+ {
message = "Exception manually captured and sent to Sentry",
exceptionMessage = ex.Message,
timestamp = DateTime.Now
@@ -53,7 +51,7 @@ namespace Managing.Api.Controllers
public IActionResult ThrowException()
{
_logger.LogInformation("About to throw an uncaught exception");
-
+
// This should be automatically captured by Sentry middleware
throw new InvalidOperationException($"Uncaught exception from ThrowException endpoint - {DateTime.Now}");
}
@@ -63,12 +61,12 @@ namespace Managing.Api.Controllers
{
// Send a simple message to Sentry
SentrySdk.CaptureMessage("Test message from Managing API", SentryLevel.Info);
-
- return Ok(new
- {
+
+ return Ok(new
+ {
message = "Test message sent to Sentry",
timestamp = DateTime.Now
});
}
}
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/Managing.Api/Exceptions/SentryErrorCapture.cs b/src/Managing.Api/Exceptions/SentryErrorCapture.cs
index 9c603dbc..91ed6594 100644
--- a/src/Managing.Api/Exceptions/SentryErrorCapture.cs
+++ b/src/Managing.Api/Exceptions/SentryErrorCapture.cs
@@ -1,5 +1,3 @@
-using Sentry;
-
namespace Managing.Api.Exceptions;
///
@@ -14,14 +12,15 @@ public static class SentryErrorCapture
/// A descriptive name for where the error occurred
/// Optional dictionary of additional data to include
/// The Sentry event ID
- public static SentryId CaptureException(Exception exception, string contextName, IDictionary extraData = null)
+ public static SentryId CaptureException(Exception exception, string contextName,
+ IDictionary extraData = null)
{
return SentrySdk.CaptureException(exception, scope =>
{
// Add context information
scope.SetTag("context", contextName);
scope.SetTag("error_type", exception.GetType().Name);
-
+
// Add any extra data provided
if (extraData != null)
{
@@ -30,7 +29,7 @@ public static class SentryErrorCapture
scope.SetExtra(kvp.Key, kvp.Value?.ToString() ?? "null");
}
}
-
+
// Add extra info from the exception's Data dictionary if available
foreach (var key in exception.Data.Keys)
{
@@ -39,7 +38,7 @@ public static class SentryErrorCapture
scope.SetExtra($"exception_data_{keyStr}", exception.Data[key].ToString());
}
}
-
+
// Add a breadcrumb for context
scope.AddBreadcrumb(
message: $"Exception in {contextName}",
@@ -48,7 +47,7 @@ public static class SentryErrorCapture
);
});
}
-
+
///
/// Enriches an exception with additional context data before throwing
///
@@ -64,10 +63,10 @@ public static class SentryErrorCapture
exception.Data[item.Key] = item.Value;
}
}
-
+
return exception;
}
-
+
///
/// Captures a message in Sentry with additional context
///
@@ -76,17 +75,18 @@ public static class SentryErrorCapture
/// A descriptive name for where the message originated
/// Optional dictionary of additional data to include
/// The Sentry event ID
- public static SentryId CaptureMessage(string message, SentryLevel level, string contextName, IDictionary extraData = null)
+ public static SentryId CaptureMessage(string message, SentryLevel level, string contextName,
+ IDictionary extraData = null)
{
// First capture the message with the specified level
var id = SentrySdk.CaptureMessage(message, level);
-
+
// Then add context via a scope
SentrySdk.ConfigureScope(scope =>
{
// Add context information
scope.SetTag("context", contextName);
-
+
// Add any extra data provided
if (extraData != null)
{
@@ -95,7 +95,7 @@ public static class SentryErrorCapture
scope.SetExtra(kvp.Key, kvp.Value?.ToString() ?? "null");
}
}
-
+
// Add a breadcrumb for context
scope.AddBreadcrumb(
message: $"Message from {contextName}",
@@ -103,7 +103,7 @@ public static class SentryErrorCapture
level: BreadcrumbLevel.Info
);
});
-
+
return id;
}
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/Managing.Api/Middleware/SentryDiagnosticsMiddleware.cs b/src/Managing.Api/Middleware/SentryDiagnosticsMiddleware.cs
index eee21b5c..616b7e32 100644
--- a/src/Managing.Api/Middleware/SentryDiagnosticsMiddleware.cs
+++ b/src/Managing.Api/Middleware/SentryDiagnosticsMiddleware.cs
@@ -1,4 +1,3 @@
-using Sentry;
using System.Text;
namespace Managing.Api.Middleware
@@ -37,17 +36,20 @@ namespace Managing.Api.Middleware
// Check if Sentry is initialized
response.AppendLine("## Sentry SDK Status");
response.AppendLine($"Sentry Enabled: {SentrySdk.IsEnabled}");
- response.AppendLine($"Application Environment: {Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}");
+ response.AppendLine(
+ $"Application Environment: {Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}");
response.AppendLine();
-
+
// Send a test event
response.AppendLine("## Test Event");
try
{
- var id = SentrySdk.CaptureMessage($"Diagnostics test from {context.Request.Host} at {DateTime.Now}", SentryLevel.Info);
+ var id = SentrySdk.CaptureMessage($"Diagnostics test from {context.Request.Host} at {DateTime.Now}",
+ SentryLevel.Info);
response.AppendLine($"Test Event ID: {id}");
- response.AppendLine("Test event was sent to Sentry. Check your Sentry dashboard to confirm it was received.");
-
+ response.AppendLine(
+ "Test event was sent to Sentry. Check your Sentry dashboard to confirm it was received.");
+
// Try to send an exception too
try
{
@@ -69,10 +71,11 @@ namespace Managing.Api.Middleware
response.AppendLine("## Connectivity Check");
response.AppendLine("If events are not appearing in Sentry, check the following:");
response.AppendLine("1. Verify your DSN is correct in appsettings.json");
- response.AppendLine("2. Ensure your network allows outbound HTTPS connections to sentry.apps.managing.live");
+ response.AppendLine(
+ "2. Ensure your network allows outbound HTTPS connections to sentry.apps.managing.live");
response.AppendLine("3. Check Sentry server logs for any ingestion issues");
response.AppendLine("4. Verify your Sentry project is correctly configured to receive events");
-
+
// Return the diagnostic information
context.Response.ContentType = "text/plain";
await context.Response.WriteAsync(response.ToString());
@@ -87,4 +90,4 @@ namespace Managing.Api.Middleware
return builder.UseMiddleware();
}
}
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/Managing.Api/Models/Requests/OpenPositionManuallyRequest.cs b/src/Managing.Api/Models/Requests/OpenPositionManuallyRequest.cs
index 3b3dae2c..860e5d98 100644
--- a/src/Managing.Api/Models/Requests/OpenPositionManuallyRequest.cs
+++ b/src/Managing.Api/Models/Requests/OpenPositionManuallyRequest.cs
@@ -1,4 +1,3 @@
-using Managing.Common;
using static Managing.Common.Enums;
namespace Managing.Api.Models.Requests;
@@ -7,4 +6,4 @@ public class OpenPositionManuallyRequest
{
public string BotName { get; set; }
public TradeDirection Direction { get; set; }
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/Managing.Application.Tests/LightMoneyManagementTests.cs b/src/Managing.Application.Tests/LightMoneyManagementTests.cs
index 3e6027cf..eba8ae17 100644
--- a/src/Managing.Application.Tests/LightMoneyManagementTests.cs
+++ b/src/Managing.Application.Tests/LightMoneyManagementTests.cs
@@ -1,4 +1,3 @@
-using Managing.Domain.MoneyManagements;
using Xunit;
using static Managing.Common.Enums;
@@ -14,8 +13,8 @@ namespace Managing.Application.Tests
{
Name = "Test1",
Timeframe = Timeframe.FifteenMinutes,
- StopLoss = 10, // 10%
- TakeProfit = 20, // 20%
+ StopLoss = 10, // 10%
+ TakeProfit = 20, // 20%
Leverage = 1
};
@@ -35,7 +34,7 @@ namespace Managing.Application.Tests
{
Name = "Test2",
Timeframe = Timeframe.FifteenMinutes,
- StopLoss = 0.1m, // Already 0.1 (10%)
+ StopLoss = 0.1m, // Already 0.1 (10%)
TakeProfit = 0.2m, // Already 0.2 (20%)
Leverage = 1
};
@@ -56,7 +55,7 @@ namespace Managing.Application.Tests
{
Name = "Test3",
Timeframe = Timeframe.FifteenMinutes,
- StopLoss = 15, // 15% (should be formatted)
+ StopLoss = 15, // 15% (should be formatted)
TakeProfit = 0.25m, // Already 0.25 (25%) (should NOT be formatted)
Leverage = 1
};
@@ -77,7 +76,7 @@ namespace Managing.Application.Tests
{
Name = "Test4a",
Timeframe = Timeframe.FifteenMinutes,
- StopLoss = 0.01m, // 1% as decimal
+ StopLoss = 0.01m, // 1% as decimal
TakeProfit = 0.02m, // 2% as decimal
Leverage = 1
};
@@ -98,8 +97,8 @@ namespace Managing.Application.Tests
{
Name = "Test4b",
Timeframe = Timeframe.FifteenMinutes,
- StopLoss = 1m, // 1% as percentage
- TakeProfit = 2m, // 2% as percentage
+ StopLoss = 1m, // 1% as percentage
+ TakeProfit = 2m, // 2% as percentage
Leverage = 1
};
@@ -119,8 +118,8 @@ namespace Managing.Application.Tests
{
Name = "Test5",
Timeframe = Timeframe.FifteenMinutes,
- StopLoss = 1.0m, // Exactly 1.0 (boundary)
- TakeProfit = 0.5m, // 0.5% (should not be formatted)
+ StopLoss = 1.0m, // Exactly 1.0 (boundary)
+ TakeProfit = 0.5m, // 0.5% (should not be formatted)
Leverage = 1
};
@@ -134,7 +133,7 @@ namespace Managing.Application.Tests
[Theory]
[InlineData(0.01, 0.01)] // 1% as decimal - should not change
- [InlineData(0.5, 0.5)] // 0.5% as decimal - should not change
+ [InlineData(0.5, 0.5)] // 0.5% as decimal - should not change
[InlineData(0.25, 0.25)] // 0.25% as decimal - should not change
public void FormatPercentage_WithDecimalValuesLessThanOne_ShouldNotFormat(decimal input, decimal expected)
{
@@ -157,11 +156,12 @@ namespace Managing.Application.Tests
}
[Theory]
- [InlineData(1, 0.01)] // 1% as percentage - should format to 0.01
- [InlineData(5, 0.05)] // 5% as percentage - should format to 0.05
- [InlineData(10, 0.1)] // 10% as percentage - should format to 0.1
- [InlineData(50, 0.5)] // 50% as percentage - should format to 0.5
- public void FormatPercentage_WithPercentageValuesGreaterThanOrEqualToOne_ShouldFormat(decimal input, decimal expected)
+ [InlineData(1, 0.01)] // 1% as percentage - should format to 0.01
+ [InlineData(5, 0.05)] // 5% as percentage - should format to 0.05
+ [InlineData(10, 0.1)] // 10% as percentage - should format to 0.1
+ [InlineData(50, 0.5)] // 50% as percentage - should format to 0.5
+ public void FormatPercentage_WithPercentageValuesGreaterThanOrEqualToOne_ShouldFormat(decimal input,
+ decimal expected)
{
// Arrange
var moneyManagement = new LightMoneyManagement
@@ -189,7 +189,7 @@ namespace Managing.Application.Tests
{
Name = "Test8a",
Timeframe = Timeframe.FifteenMinutes,
- StopLoss = 0.01m, // 1% as decimal
+ StopLoss = 0.01m, // 1% as decimal
TakeProfit = 0.0075m, // 0.75% as decimal
Leverage = 1
};
@@ -211,8 +211,8 @@ namespace Managing.Application.Tests
{
Name = "Test8b",
Timeframe = Timeframe.FifteenMinutes,
- StopLoss = 1m, // 1% as percentage
- TakeProfit = 0.75m, // 0.75% as percentage
+ StopLoss = 1m, // 1% as percentage
+ TakeProfit = 0.75m, // 0.75% as percentage
Leverage = 1
};
@@ -224,4 +224,4 @@ namespace Managing.Application.Tests
Assert.Equal(0.75m, moneyManagement.TakeProfit); // Not formatted because < 1
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Managing.Application/Bots/FuturesBot.cs b/src/Managing.Application/Bots/FuturesBot.cs
index 56e80063..30ec37e7 100644
--- a/src/Managing.Application/Bots/FuturesBot.cs
+++ b/src/Managing.Application/Bots/FuturesBot.cs
@@ -1,4 +1,3 @@
-using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Grains;
using Managing.Application.Abstractions.Services;
using Managing.Application.Trading.Commands;
@@ -19,7 +18,7 @@ using static Managing.Common.Enums;
namespace Managing.Application.Bots;
-public class FuturesBot : TradingBotBase, ITradingBot
+public class FuturesBot : TradingBotBase
{
public FuturesBot(
ILogger logger,
@@ -72,24 +71,14 @@ public class FuturesBot : TradingBotBase, ITradingBot
// For live trading, get position from database via trading service
return await ServiceScopeHelpers.WithScopedService(
_scopeFactory,
- async tradingService => { return await tradingService.GetPositionByIdentifierAsync(position.Identifier); });
- }
-
- protected override async Task UpdatePositionWithBrokerData(Position position, List brokerPositions)
- {
- // Live trading broker position synchronization logic is handled in the base UpdatePosition method
- // This override allows for any futures-specific synchronization if needed
- await base.UpdatePositionWithBrokerData(position, brokerPositions);
+ async tradingService => await tradingService.GetPositionByIdentifierAsync(position.Identifier));
}
protected override async Task GetCurrentCandleForPositionClose(Account account, string ticker)
{
// For live trading, get real-time candle from exchange
return await ServiceScopeHelpers.WithScopedService(_scopeFactory,
- async exchangeService =>
- {
- return await exchangeService.GetCandle(Account, Config.Ticker, DateTime.UtcNow);
- });
+ async exchangeService => await exchangeService.GetCandle(Account, Config.Ticker, DateTime.UtcNow));
}
protected override async Task CheckBrokerPositions()
@@ -170,7 +159,7 @@ public class FuturesBot : TradingBotBase, ITradingBot
}
else
{
- // Broker has a position but we don't have any internal tracking
+ // Broker has a position, but we don't have any internal tracking
Logger.LogWarning(
$"⚠️ Orphaned Broker Position Detected\n" +
$"Broker has position for {Config.Ticker} ({brokerPositionForTicker.OriginDirection})\n" +
@@ -196,7 +185,7 @@ public class FuturesBot : TradingBotBase, ITradingBot
if (Config.TradingType == TradingType.BacktestFutures) return;
await ServiceScopeHelpers.WithScopedService(_scopeFactory, async accountService =>
{
- var account = await accountService.GetAccountByAccountName(Config.AccountName, false, false);
+ var account = await accountService.GetAccountByAccountName(Config.AccountName, false);
Account = account;
});
}
@@ -297,7 +286,6 @@ public class FuturesBot : TradingBotBase, ITradingBot
$"Cannot verify if position is closed\n" +
$"Will retry on next execution cycle");
// Don't change position status, wait for next cycle
- return;
}
else if (existsInHistory)
{
@@ -309,7 +297,6 @@ public class FuturesBot : TradingBotBase, ITradingBot
internalPosition.Status = PositionStatus.Finished;
await HandleClosedPosition(internalPosition);
- return;
}
else
{
@@ -347,11 +334,11 @@ public class FuturesBot : TradingBotBase, ITradingBot
}
var orders = await ServiceScopeHelpers.WithScopedService>(_scopeFactory,
- async exchangeService => { return [.. await exchangeService.GetOpenOrders(Account, Config.Ticker)]; });
+ async exchangeService => [.. await exchangeService.GetOpenOrders(Account, Config.Ticker)]);
- if (orders.Any())
+ if (orders.Count != 0)
{
- var ordersCount = orders.Count();
+ var ordersCount = orders.Count;
if (ordersCount >= 3)
{
var currentTime = DateTime.UtcNow;
@@ -386,7 +373,6 @@ public class FuturesBot : TradingBotBase, ITradingBot
positionForSignal.TakeProfit1.SetStatus(TradeStatus.Cancelled);
await UpdatePositionDatabase(positionForSignal);
- return;
}
else
{
@@ -538,7 +524,7 @@ public class FuturesBot : TradingBotBase, ITradingBot
///
/// The position to check
/// True if position found in exchange history with PnL, false otherwise; hadError indicates Web3/infra issues
- protected async Task<(bool found, bool hadError)> CheckPositionInExchangeHistory(Position position)
+ private async Task<(bool found, bool hadError)> CheckPositionInExchangeHistory(Position position)
{
try
{
@@ -564,7 +550,7 @@ public class FuturesBot : TradingBotBase, ITradingBot
.OrderByDescending(p => p.Open?.Date ?? DateTime.MinValue)
.FirstOrDefault();
- if (recentPosition != null && recentPosition.ProfitAndLoss != null)
+ if (recentPosition is { ProfitAndLoss: not null })
{
await LogDebugAsync(
$"✅ Position Found in Exchange History\n" +
@@ -757,7 +743,7 @@ public class FuturesBot : TradingBotBase, ITradingBot
.OrderByDescending(p => p.Open?.Date ?? DateTime.MinValue)
.FirstOrDefault();
- if (brokerPosition != null && brokerPosition.ProfitAndLoss != null)
+ if (brokerPosition is { ProfitAndLoss: not null })
{
await LogDebugAsync(
$"✅ Broker Position History Found\n" +
@@ -934,7 +920,7 @@ public class FuturesBot : TradingBotBase, ITradingBot
// Fallback to current candle if available
if (currentCandle != null)
{
- recentCandles = new List { currentCandle };
+ recentCandles = [currentCandle];
}
else
{
@@ -950,8 +936,8 @@ public class FuturesBot : TradingBotBase, ITradingBot
var minPriceRecent = recentCandles.Min(c => c.Low);
var maxPriceRecent = recentCandles.Max(c => c.High);
- bool wasStopLossHit = false;
- bool wasTakeProfitHit = false;
+ var wasStopLossHit = false;
+ var wasTakeProfitHit = false;
if (position.OriginDirection == TradeDirection.Long)
{
@@ -1193,7 +1179,7 @@ public class FuturesBot : TradingBotBase, ITradingBot
signal.Date,
Account.User,
Config.BotTradingBalance,
- isForPaperTrading: false, // Futures is live trading
+ isForPaperTrading: false,
lastPrice,
signalIdentifier: signal.Identifier,
initiatorIdentifier: Identifier,
@@ -1203,10 +1189,8 @@ public class FuturesBot : TradingBotBase, ITradingBot
.WithScopedServices(
_scopeFactory,
async (exchangeService, accountService, tradingService) =>
- {
- return await new OpenPositionCommandHandler(exchangeService, accountService, tradingService)
- .Handle(command);
- });
+ await new OpenPositionCommandHandler(exchangeService, accountService, tradingService)
+ .Handle(command));
return position;
}
@@ -1230,7 +1214,7 @@ public class FuturesBot : TradingBotBase, ITradingBot
if (quantity == 0)
{
await LogDebugAsync($"✅ Trade already closed on exchange for position: `{position.Identifier}`");
- await HandleClosedPosition(position, forceMarketClose ? lastPrice : (decimal?)null, forceMarketClose);
+ await HandleClosedPosition(position, forceMarketClose ? lastPrice : null, forceMarketClose);
}
else
{
@@ -1258,7 +1242,7 @@ public class FuturesBot : TradingBotBase, ITradingBot
await SetPositionStatus(signal.Identifier, PositionStatus.Finished);
}
- await HandleClosedPosition(closedPosition, forceMarketClose ? lastPrice : (decimal?)null,
+ await HandleClosedPosition(closedPosition, forceMarketClose ? lastPrice : null,
forceMarketClose);
}
else
@@ -1275,7 +1259,7 @@ public class FuturesBot : TradingBotBase, ITradingBot
// Trade close on exchange => Should close trade manually
await SetPositionStatus(signal.Identifier, PositionStatus.Finished);
// Ensure trade dates are properly updated even for canceled/rejected positions
- await HandleClosedPosition(position, forceMarketClose ? lastPrice : (decimal?)null,
+ await HandleClosedPosition(position, forceMarketClose ? lastPrice : null,
forceMarketClose);
}
}
diff --git a/src/Managing.Application/Bots/SpotBot.cs b/src/Managing.Application/Bots/SpotBot.cs
index 4788ce47..f7fdc658 100644
--- a/src/Managing.Application/Bots/SpotBot.cs
+++ b/src/Managing.Application/Bots/SpotBot.cs
@@ -94,7 +94,7 @@ public class SpotBot : TradingBotBase
// Try to get current price from exchange
currentPrice = await ServiceScopeHelpers.WithScopedService(
_scopeFactory,
- async exchangeService => { return await exchangeService.GetCurrentPrice(Account, Config.Ticker); });
+ async exchangeService => await exchangeService.GetCurrentPrice(Account, Config.Ticker));
}
if (currentPrice == 0)
@@ -144,10 +144,7 @@ public class SpotBot : TradingBotBase
{
// For live trading, get real-time candle from exchange
return await ServiceScopeHelpers.WithScopedService(_scopeFactory,
- async exchangeService =>
- {
- return await exchangeService.GetCandle(Account, Config.Ticker, DateTime.UtcNow);
- });
+ async exchangeService => await exchangeService.GetCandle(Account, Config.Ticker, DateTime.UtcNow));
}
protected override async Task CheckBrokerPositions()
@@ -164,7 +161,7 @@ public class SpotBot : TradingBotBase
if (hasOpenPosition)
{
// We have an internal position - verify it matches broker balance
- if (tokenBalance != null && tokenBalance.Amount > 0)
+ if (tokenBalance is { Amount: > 0 })
{
await LogDebugAsync(
$"✅ Spot Position Verified\n" +
@@ -174,17 +171,16 @@ public class SpotBot : TradingBotBase
$"Position matches broker balance");
return false; // Position already open, cannot open new one
}
- else
- {
- await LogWarningAsync(
- $"⚠️ Position Mismatch\n" +
- $"Ticker: {Config.Ticker}\n" +
- $"Internal position exists but no token balance found\n" +
- $"Position may need synchronization");
- return false; // Don't allow opening new position until resolved
- }
+
+ await LogWarningAsync(
+ $"⚠️ Position Mismatch\n" +
+ $"Ticker: {Config.Ticker}\n" +
+ $"Internal position exists but no token balance found\n" +
+ $"Position may need synchronization");
+ return false; // Don't allow opening new position until resolved
}
- else if (tokenBalance != null && tokenBalance.Value > 1m)
+
+ if (tokenBalance is { Value: > 1m })
{
// We have a token balance but no internal position - orphaned position
await LogWarningAsync(
@@ -212,7 +208,7 @@ public class SpotBot : TradingBotBase
if (Config.TradingType == TradingType.BacktestSpot) return;
await ServiceScopeHelpers.WithScopedService(_scopeFactory, async accountService =>
{
- var account = await accountService.GetAccountByAccountName(Config.AccountName, false, false);
+ var account = await accountService.GetAccountByAccountName(Config.AccountName, false);
Account = account;
});
}
@@ -227,7 +223,7 @@ public class SpotBot : TradingBotBase
_scopeFactory,
async exchangeService => await exchangeService.GetBalance(Account, Config.Ticker));
- if (tokenBalance != null && tokenBalance.Amount > 0)
+ if (tokenBalance is { Amount: > 0 })
{
// Verify that the token balance matches the position amount with 0.1% tolerance
var positionQuantity = internalPosition.Open.Quantity;
@@ -281,7 +277,7 @@ public class SpotBot : TradingBotBase
// Calculate and update PnL based on current price
var currentPrice = await ServiceScopeHelpers.WithScopedService(
_scopeFactory,
- async exchangeService => { return await exchangeService.GetCurrentPrice(Account, Config.Ticker); });
+ async exchangeService => await exchangeService.GetCurrentPrice(Account, Config.Ticker));
if (currentPrice > 0)
{
@@ -353,17 +349,16 @@ public class SpotBot : TradingBotBase
await LogDebugAsync(
$"🔍 Checking Spot Position History for Position: `{position.Identifier}`\nTicker: `{Config.Ticker}`");
- List positionHistory = null;
- await ServiceScopeHelpers.WithScopedService(_scopeFactory,
+ var positionHistory = await ServiceScopeHelpers.WithScopedService>(
+ _scopeFactory,
async exchangeService =>
{
var fromDate = DateTime.UtcNow.AddHours(-24);
var toDate = DateTime.UtcNow;
- positionHistory =
- await exchangeService.GetSpotPositionHistory(Account, Config.Ticker, fromDate, toDate);
+ return await exchangeService.GetSpotPositionHistory(Account, Config.Ticker, fromDate, toDate);
});
- if (positionHistory != null && positionHistory.Any())
+ if (positionHistory != null && positionHistory.Count != 0)
{
var recentPosition = positionHistory
.OrderByDescending(p => p.Date)
@@ -415,28 +410,28 @@ public class SpotBot : TradingBotBase
}
}
- protected override async Task MonitorSynthRisk(LightSignal signal, Position position)
+ protected override Task MonitorSynthRisk(LightSignal signal, Position position)
{
// Spot trading doesn't use Synth risk monitoring (futures-specific feature)
- return;
+ return Task.CompletedTask;
}
- protected override async Task RecoverOpenPositionFromBroker(LightSignal signal, Position positionForSignal)
+ protected override Task RecoverOpenPositionFromBroker(LightSignal signal, Position positionForSignal)
{
// Spot trading doesn't have broker positions to recover
// Positions are token balances, not tracked positions
- return false;
+ return Task.FromResult(false);
}
- protected override async Task ReconcileWithBrokerHistory(Position position, Candle currentCandle)
+ protected override Task ReconcileWithBrokerHistory(Position position, Candle currentCandle)
{
// Spot trading doesn't have broker position history like futures
// Return false to continue with candle-based calculation
- return false;
+ return Task.FromResult(false);
}
- protected override async Task<(decimal closingPrice, bool pnlCalculated)> CalculatePositionClosingFromCandles(
- Position position, Candle currentCandle, bool forceMarketClose, decimal? forcedClosingPrice)
+ protected override Task<(decimal closingPrice, bool pnlCalculated)> CalculatePositionClosingFromCandles(
+ Position position, Candle? currentCandle, bool forceMarketClose, decimal? forcedClosingPrice)
{
decimal closingPrice = 0;
bool pnlCalculated = false;
@@ -529,7 +524,7 @@ public class SpotBot : TradingBotBase
? closingPrice > position.Open.Price
: closingPrice < position.Open.Price;
- if (isManualCloseProfitable)
+ if (isManualCloseProfitable && position.TakeProfit1 != null)
{
position.TakeProfit1.SetPrice(closingPrice, 2);
position.TakeProfit1.SetDate(currentCandle.Date);
@@ -542,9 +537,9 @@ public class SpotBot : TradingBotBase
}
else
{
- position.StopLoss.SetPrice(closingPrice, 2);
- position.StopLoss.SetDate(currentCandle.Date);
- position.StopLoss.SetStatus(TradeStatus.Filled);
+ position.StopLoss?.SetPrice(closingPrice, 2);
+ position.StopLoss?.SetDate(currentCandle.Date);
+ position.StopLoss?.SetStatus(TradeStatus.Filled);
if (position.TakeProfit1 != null)
{
@@ -561,11 +556,11 @@ public class SpotBot : TradingBotBase
pnlCalculated = true;
}
- return (closingPrice, pnlCalculated);
+ return Task.FromResult((closingPrice, pnlCalculated));
}
protected override async Task UpdateSignalsCore(IReadOnlyList candles,
- Dictionary preCalculatedIndicatorValues = null)
+ Dictionary? preCalculatedIndicatorValues = null)
{
// For spot trading, always fetch signals regardless of open positions
// Check if we're in cooldown period
@@ -625,7 +620,7 @@ public class SpotBot : TradingBotBase
return !await IsInCooldownPeriodAsync() && await CheckLossStreak(signal);
}
- protected override async Task HandleFlipPosition(LightSignal signal, Position openedPosition,
+ protected override async Task HandleFlipPosition(LightSignal signal, Position openedPosition,
LightSignal previousSignal, decimal lastPrice)
{
// For spot trading, SHORT signals should close the open LONG position
@@ -694,10 +689,8 @@ public class SpotBot : TradingBotBase
.WithScopedServices(
_scopeFactory,
async (exchangeService, accountService, tradingService) =>
- {
- return await new OpenSpotPositionCommandHandler(exchangeService, accountService, tradingService)
- .Handle(command);
- });
+ await new OpenSpotPositionCommandHandler(exchangeService, accountService, tradingService)
+ .Handle(command));
return position;
}
@@ -725,7 +718,7 @@ public class SpotBot : TradingBotBase
await SetPositionStatus(signal.Identifier, PositionStatus.Finished);
}
- await HandleClosedPosition(closedPosition, forceMarketClose ? lastPrice : (decimal?)null,
+ await HandleClosedPosition(closedPosition, forceMarketClose ? lastPrice : null,
forceMarketClose);
}
else
@@ -742,7 +735,7 @@ public class SpotBot : TradingBotBase
// Trade close on exchange => Should close trade manually
await SetPositionStatus(signal.Identifier, PositionStatus.Finished);
// Ensure trade dates are properly updated even for canceled/rejected positions
- await HandleClosedPosition(position, forceMarketClose ? lastPrice : (decimal?)null,
+ await HandleClosedPosition(position, forceMarketClose ? lastPrice : null,
forceMarketClose);
}
}
diff --git a/src/Managing.Application/Trading/Handlers/OpenSpotPositionCommandHandler.cs b/src/Managing.Application/Trading/Handlers/OpenSpotPositionCommandHandler.cs
index 38ffc4af..a1b5bcd5 100644
--- a/src/Managing.Application/Trading/Handlers/OpenSpotPositionCommandHandler.cs
+++ b/src/Managing.Application/Trading/Handlers/OpenSpotPositionCommandHandler.cs
@@ -2,183 +2,152 @@ using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services;
using Managing.Application.Trading.Commands;
using Managing.Common;
-using Managing.Core.Exceptions;
using Managing.Domain.Accounts;
using Managing.Domain.Shared.Helpers;
using Managing.Domain.Trades;
using static Managing.Common.Enums;
-namespace Managing.Application.Trading.Handlers
+namespace Managing.Application.Trading.Handlers;
+
+public class OpenSpotPositionCommandHandler(
+ IExchangeService exchangeService,
+ IAccountService accountService,
+ ITradingService tradingService)
+ : ICommandHandler
{
- public class OpenSpotPositionCommandHandler(
- IExchangeService exchangeService,
- IAccountService accountService,
- ITradingService tradingService)
- : ICommandHandler
+ public async Task Handle(OpenSpotPositionRequest request)
{
- public async Task Handle(OpenSpotPositionRequest request)
+ var account = await accountService.GetAccount(request.AccountName, hideSecrets: false, getBalance: false);
+
+ var initiator = request.IsForPaperTrading ? PositionInitiator.PaperTrading : request.Initiator;
+ var position = new Position(Guid.NewGuid(), account.Id, request.Direction,
+ request.Ticker,
+ request.MoneyManagement,
+ initiator, request.Date, request.User);
+
+ if (!string.IsNullOrEmpty(request.SignalIdentifier))
{
- var account = await accountService.GetAccount(request.AccountName, hideSecrets: false, getBalance: false);
-
- var initiator = request.IsForPaperTrading ? PositionInitiator.PaperTrading : request.Initiator;
- var position = new Position(Guid.NewGuid(), account.Id, request.Direction,
- request.Ticker,
- request.MoneyManagement,
- initiator, request.Date, request.User);
-
- if (!string.IsNullOrEmpty(request.SignalIdentifier))
- {
- position.SignalIdentifier = request.SignalIdentifier;
- }
-
- position.InitiatorIdentifier = request.InitiatorIdentifier;
- position.TradingType = request.TradingType;
-
- // Always use BotTradingBalance directly as the balance to risk
- // Round to 2 decimal places to prevent precision errors
- decimal balanceToRisk = Math.Round(request.AmountToTrade, 0, MidpointRounding.ToZero);
-
- // 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
- : price;
-
- // For spot trading, determine swap direction
- // Long: Swap USDC -> Token (buy token with USDC)
- // Short: Swap Token -> USDC (sell token for USDC)
- Ticker fromTicker;
- Ticker toTicker;
- double swapAmount;
-
- if (request.Direction == TradeDirection.Long)
- {
- fromTicker = Ticker.USDC;
- toTicker = request.Ticker;
- swapAmount = (double)balanceToRisk;
- }
- else
- {
- fromTicker = request.Ticker;
- toTicker = Ticker.USDC;
- swapAmount = (double)quantity;
- }
-
- // For backtest/paper trading, simulate the swap without calling the exchange
- SwapInfos swapResult;
- if (request.IsForPaperTrading)
- {
- // Simulate successful swap for backtest
- swapResult = new SwapInfos
- {
- Success = true,
- Hash = Guid.NewGuid().ToString(),
- Message = "Backtest spot position opened successfully"
- };
- }
- else
- {
- // For live trading, call SwapGmxTokensAsync
- swapResult = await tradingService.SwapGmxTokensAsync(
- request.User,
- request.AccountName,
- fromTicker,
- toTicker,
- swapAmount,
- "market",
- null,
- 0.5);
- }
-
- if (!swapResult.Success)
- {
- position.Status = PositionStatus.Rejected;
- throw new InvalidOperationException($"Failed to open spot position: {swapResult.Error ?? swapResult.Message}");
- }
-
- // Build the opening trade
- var trade = exchangeService.BuildEmptyTrade(
- request.Ticker,
- openPrice,
- quantity,
- request.Direction,
- 1, // Spot trading has no leverage
- TradeType.Market,
- request.Date,
- TradeStatus.Filled);
-
- position.Open = trade;
-
- // Calculate and set fees for the position
- position.GasFees = TradingBox.CalculateOpeningGasFees();
-
- // Set UI fees for opening
- var positionSizeUsd = TradingBox.GetVolumeForPosition(position);
- position.UiFees = TradingBox.CalculateOpeningUiFees(positionSizeUsd);
-
- var closeDirection = request.Direction == TradeDirection.Long
- ? TradeDirection.Short
- : TradeDirection.Long;
-
- // Determine SL/TP Prices
- var stopLossPrice = RiskHelpers.GetStopLossPrice(request.Direction, openPrice, request.MoneyManagement);
- var takeProfitPrice = RiskHelpers.GetTakeProfitPrice(request.Direction, openPrice, request.MoneyManagement);
-
- // Stop loss
- position.StopLoss = exchangeService.BuildEmptyTrade(
- request.Ticker,
- stopLossPrice,
- position.Open.Quantity,
- closeDirection,
- 1, // Spot trading has no leverage
- TradeType.StopLoss,
- request.Date,
- TradeStatus.Requested);
-
- // Take profit
- position.TakeProfit1 = exchangeService.BuildEmptyTrade(
- request.Ticker,
- takeProfitPrice,
- quantity,
- closeDirection,
- 1, // Spot trading has no leverage
- TradeType.TakeProfit,
- request.Date,
- TradeStatus.Requested);
-
- position.Status = IsOpenTradeHandled(position.Open.Status)
- ? position.Status
- : PositionStatus.Rejected;
-
- if (position.Status == PositionStatus.Rejected)
- {
- SentrySdk.CaptureException(
- new Exception($"Position {position.Identifier} for {request.SignalIdentifier} rejected"));
- }
-
- if (!request.IsForPaperTrading)
- {
- await tradingService.InsertPositionAsync(position);
- }
-
- return position;
+ position.SignalIdentifier = request.SignalIdentifier;
}
- private static bool IsOpenTradeHandled(TradeStatus tradeStatus)
+ position.InitiatorIdentifier = request.InitiatorIdentifier;
+ position.TradingType = request.TradingType;
+
+ // Always use BotTradingBalance directly as the balance to risk
+ // Round to 2 decimal places to prevent precision errors
+ decimal balanceToRisk = Math.Round(request.AmountToTrade, 0, MidpointRounding.ToZero);
+
+ // Minimum check
+ if (balanceToRisk < Constants.GMX.Config.MinimumPositionAmount)
{
- return tradeStatus == TradeStatus.Filled
- || tradeStatus == TradeStatus.Requested;
+ 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 ?? price
+ : price;
+
+ SwapInfos swapResult;
+ if (request.IsForPaperTrading)
+ {
+ // Simulate successful swap for backtest
+ swapResult = new SwapInfos
+ {
+ Success = true,
+ Hash = Guid.NewGuid().ToString(),
+ Message = "Backtest spot position opened successfully"
+ };
+ }
+ else
+ {
+ // For live trading, call SwapGmxTokensAsync
+ swapResult = await tradingService.SwapGmxTokensAsync(
+ request.User,
+ request.AccountName,
+ Ticker.USDC,
+ request.Ticker,
+ (double)balanceToRisk);
+ }
+
+ if (!swapResult.Success)
+ {
+ position.Status = PositionStatus.Rejected;
+ throw new InvalidOperationException(
+ $"Failed to open spot position: {swapResult.Error ?? swapResult.Message}");
+ }
+
+ // Build the opening trade
+ var trade = exchangeService.BuildEmptyTrade(
+ request.Ticker,
+ openPrice,
+ quantity,
+ request.Direction,
+ 1, // Spot trading has no leverage
+ TradeType.Market,
+ request.Date,
+ TradeStatus.Filled);
+
+ position.Open = trade;
+
+ // Calculate and set fees for the position
+ position.GasFees = TradingBox.CalculateOpeningGasFees();
+
+ // Set UI fees for opening
+ var positionSizeUsd = position.Open.Quantity * position.Open.Price;
+ position.UiFees = TradingBox.CalculateOpeningUiFees(positionSizeUsd);
+
+ // Determine SL/TP Prices
+ var stopLossPrice = RiskHelpers.GetStopLossPrice(request.Direction, openPrice, request.MoneyManagement);
+ var takeProfitPrice = RiskHelpers.GetTakeProfitPrice(request.Direction, openPrice, request.MoneyManagement);
+
+ // Stop loss
+ position.StopLoss = exchangeService.BuildEmptyTrade(
+ request.Ticker,
+ stopLossPrice,
+ position.Open.Quantity,
+ TradeDirection.Short,
+ 1, // Spot trading has no leverage
+ TradeType.StopLoss,
+ request.Date,
+ TradeStatus.Requested);
+
+ // Take profit
+ position.TakeProfit1 = exchangeService.BuildEmptyTrade(
+ request.Ticker,
+ takeProfitPrice,
+ quantity,
+ TradeDirection.Short,
+ 1, // Spot trading has no leverage
+ TradeType.TakeProfit,
+ request.Date,
+ TradeStatus.Requested);
+
+ position.Status = IsOpenTradeHandled(position.Open.Status)
+ ? position.Status
+ : PositionStatus.Rejected;
+
+ if (position.Status == PositionStatus.Rejected)
+ {
+ SentrySdk.CaptureException(
+ new Exception($"Position {position.Identifier} for {request.SignalIdentifier} rejected"));
+ }
+
+ if (!request.IsForPaperTrading)
+ {
+ await tradingService.InsertPositionAsync(position);
+ }
+
+ return position;
}
-}
+ private static bool IsOpenTradeHandled(TradeStatus tradeStatus)
+ {
+ return tradeStatus is TradeStatus.Filled or TradeStatus.Requested;
+ }
+}
\ No newline at end of file
diff --git a/src/Managing.Aspire.ServiceDefaults/Extensions.cs b/src/Managing.Aspire.ServiceDefaults/Extensions.cs
index aab5715e..229c194b 100644
--- a/src/Managing.Aspire.ServiceDefaults/Extensions.cs
+++ b/src/Managing.Aspire.ServiceDefaults/Extensions.cs
@@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.ServiceDiscovery;
using OpenTelemetry;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
@@ -111,4 +110,4 @@ public static class Extensions
return app;
}
-}
+}
\ No newline at end of file
diff --git a/src/Managing.Core/Exceptions/SentryErrorCapture.cs b/src/Managing.Core/Exceptions/SentryErrorCapture.cs
index 0c13abcd..a29f7f1c 100644
--- a/src/Managing.Core/Exceptions/SentryErrorCapture.cs
+++ b/src/Managing.Core/Exceptions/SentryErrorCapture.cs
@@ -1,5 +1,3 @@
-using Sentry;
-
namespace Managing.Core.Exceptions;
///
@@ -14,14 +12,15 @@ public static class SentryErrorCapture
/// A descriptive name for where the error occurred
/// Optional dictionary of additional data to include
/// The Sentry event ID
- public static SentryId CaptureException(Exception exception, string contextName, IDictionary extraData = null)
+ public static SentryId CaptureException(Exception exception, string contextName,
+ IDictionary extraData = null)
{
return SentrySdk.CaptureException(exception, scope =>
{
// Add context information
scope.SetTag("context", contextName);
scope.SetTag("error_type", exception.GetType().Name);
-
+
// Add any extra data provided
if (extraData != null)
{
@@ -30,7 +29,7 @@ public static class SentryErrorCapture
scope.SetExtra(kvp.Key, kvp.Value?.ToString() ?? "null");
}
}
-
+
// Add extra info from the exception's Data dictionary if available
foreach (var key in exception.Data.Keys)
{
@@ -39,7 +38,7 @@ public static class SentryErrorCapture
scope.SetExtra($"exception_data_{keyStr}", exception.Data[key].ToString());
}
}
-
+
// Add a breadcrumb for context
scope.AddBreadcrumb(
message: $"Exception in {contextName}",
@@ -48,7 +47,7 @@ public static class SentryErrorCapture
);
});
}
-
+
///
/// Enriches an exception with additional context data before throwing
///
@@ -64,10 +63,10 @@ public static class SentryErrorCapture
exception.Data[item.Key] = item.Value;
}
}
-
+
return exception;
}
-
+
///
/// Captures a message in Sentry with additional context
///
@@ -76,17 +75,18 @@ public static class SentryErrorCapture
/// A descriptive name for where the message originated
/// Optional dictionary of additional data to include
/// The Sentry event ID
- public static SentryId CaptureMessage(string message, SentryLevel level, string contextName, IDictionary extraData = null)
+ public static SentryId CaptureMessage(string message, SentryLevel level, string contextName,
+ IDictionary extraData = null)
{
// First capture the message with the specified level
var id = SentrySdk.CaptureMessage(message, level);
-
+
// Then add context via a scope
SentrySdk.ConfigureScope(scope =>
{
// Add context information
scope.SetTag("context", contextName);
-
+
// Add any extra data provided
if (extraData != null)
{
@@ -95,7 +95,7 @@ public static class SentryErrorCapture
scope.SetExtra(kvp.Key, kvp.Value?.ToString() ?? "null");
}
}
-
+
// Add a breadcrumb for context
scope.AddBreadcrumb(
message: $"Message from {contextName}",
@@ -103,7 +103,7 @@ public static class SentryErrorCapture
level: BreadcrumbLevel.Info
);
});
-
+
return id;
}
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/Managing.Core/Middleawares/GlobalErrorHandlingMiddleware.cs b/src/Managing.Core/Middleawares/GlobalErrorHandlingMiddleware.cs
index 04029066..84d8f628 100644
--- a/src/Managing.Core/Middleawares/GlobalErrorHandlingMiddleware.cs
+++ b/src/Managing.Core/Middleawares/GlobalErrorHandlingMiddleware.cs
@@ -1,9 +1,8 @@
using System.Net;
using System.Text.Json;
+using Managing.Core.Exceptions;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
-using Sentry;
-using Managing.Core.Exceptions;
namespace Managing.Core.Middleawares;
@@ -11,13 +10,13 @@ public class GlobalErrorHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
-
+
public GlobalErrorHandlingMiddleware(RequestDelegate next, ILogger logger)
{
_next = next;
_logger = logger;
}
-
+
public async Task Invoke(HttpContext context)
{
try
@@ -29,12 +28,12 @@ public class GlobalErrorHandlingMiddleware
await HandleExceptionAsync(context, ex);
}
}
-
+
private Task HandleExceptionAsync(HttpContext context, Exception exception)
{
HttpStatusCode status;
string errorMessage;
-
+
// Determine the appropriate status code based on exception type
status = exception switch
{
@@ -43,41 +42,41 @@ public class GlobalErrorHandlingMiddleware
ValidationException => HttpStatusCode.BadRequest,
FormatException => HttpStatusCode.BadRequest,
InvalidOperationException => HttpStatusCode.BadRequest,
-
+
// 401 Unauthorized
UnauthorizedAccessException => HttpStatusCode.Unauthorized,
-
+
// 403 Forbidden
ForbiddenException => HttpStatusCode.Forbidden,
-
+
// 404 Not Found
KeyNotFoundException => HttpStatusCode.NotFound,
FileNotFoundException => HttpStatusCode.NotFound,
DirectoryNotFoundException => HttpStatusCode.NotFound,
NotFoundException => HttpStatusCode.NotFound,
-
+
// 408 Request Timeout
TimeoutException => HttpStatusCode.RequestTimeout,
-
+
// 409 Conflict
ConflictException => HttpStatusCode.Conflict,
-
+
// 429 Too Many Requests
RateLimitExceededException => HttpStatusCode.TooManyRequests,
-
+
// 501 Not Implemented
NotImplementedException => HttpStatusCode.NotImplemented,
-
+
// 503 Service Unavailable
ServiceUnavailableException => HttpStatusCode.ServiceUnavailable,
-
+
// 500 Internal Server Error (default)
_ => HttpStatusCode.InternalServerError
};
-
+
// Log the error with appropriate severity based on status code
var isServerError = (int)status >= 500;
-
+
if (isServerError)
{
_logger.LogError(exception, "Server Error: {StatusCode} on {Path}", (int)status, context.Request.Path);
@@ -86,29 +85,29 @@ public class GlobalErrorHandlingMiddleware
{
_logger.LogWarning(exception, "Client Error: {StatusCode} on {Path}", (int)status, context.Request.Path);
}
-
+
// Capture exception in Sentry with request context
var sentryId = SentrySdk.CaptureException(exception, scope =>
{
// Add HTTP request information
scope.SetTag("http.method", context.Request.Method);
scope.SetTag("http.url", context.Request.Path);
-
+
// Add request details
scope.SetExtra("query_string", context.Request.QueryString.ToString());
-
+
// Add custom tags to help with filtering
scope.SetTag("error_type", exception.GetType().Name);
scope.SetTag("status_code", ((int)status).ToString());
scope.SetTag("host", context.Request.Host.ToString());
scope.SetTag("path", context.Request.Path.ToString());
-
+
// Add any correlation IDs if available
if (context.Request.Headers.TryGetValue("X-Correlation-ID", out var correlationId))
{
scope.SetTag("correlation_id", correlationId.ToString());
}
-
+
// Additional context based on exception type
if (exception is ValidationException)
{
@@ -118,7 +117,7 @@ public class GlobalErrorHandlingMiddleware
{
scope.SetTag("error_category", "not_found");
}
-
+
// Add additional context from exception data if available
foreach (var key in exception.Data.Keys)
{
@@ -127,7 +126,7 @@ public class GlobalErrorHandlingMiddleware
scope.SetExtra(keyStr, exception.Data[key].ToString());
}
}
-
+
// Add breadcrumb for the request
scope.AddBreadcrumb(
message: $"Request to {context.Request.Path}",
@@ -135,7 +134,7 @@ public class GlobalErrorHandlingMiddleware
level: BreadcrumbLevel.Info
);
});
-
+
// Use a more user-friendly error message in production
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Production")
{
@@ -154,7 +153,7 @@ public class GlobalErrorHandlingMiddleware
{
errorMessage = exception.Message;
}
-
+
// Create the error response
var errorResponse = new ErrorResponse
{
@@ -162,23 +161,23 @@ public class GlobalErrorHandlingMiddleware
Message = errorMessage,
TraceId = sentryId.ToString()
};
-
+
// Only include stack trace in development environment
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") != "Production")
{
errorResponse.StackTrace = exception.StackTrace;
}
-
+
var result = JsonSerializer.Serialize(errorResponse, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
-
+
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)status;
return context.Response.WriteAsync(result);
}
-
+
// Custom error response class
private class ErrorResponse
{
diff --git a/src/Managing.Core/Middleawares/SentryDiagnosticsMiddleware.cs b/src/Managing.Core/Middleawares/SentryDiagnosticsMiddleware.cs
index c7bfd805..fb018ea6 100644
--- a/src/Managing.Core/Middleawares/SentryDiagnosticsMiddleware.cs
+++ b/src/Managing.Core/Middleawares/SentryDiagnosticsMiddleware.cs
@@ -2,7 +2,6 @@ using System.Text;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
-using Sentry;
namespace Managing.Core.Middleawares;
diff --git a/src/Managing.Domain.Tests/Managing.Domain.SimpleTests/UnitTest1.cs b/src/Managing.Domain.Tests/Managing.Domain.SimpleTests/UnitTest1.cs
index f7a95149..6b909003 100644
--- a/src/Managing.Domain.Tests/Managing.Domain.SimpleTests/UnitTest1.cs
+++ b/src/Managing.Domain.Tests/Managing.Domain.SimpleTests/UnitTest1.cs
@@ -1,7 +1,7 @@
using FluentAssertions;
using Managing.Common;
+using Managing.Domain.Candles;
using Managing.Domain.Shared.Helpers;
-using static Managing.Common.Enums;
using Xunit;
namespace Managing.Domain.SimpleTests;
@@ -16,12 +16,12 @@ public class SimpleTradingBoxTests
public void GetHodlPercentage_WithPriceIncrease_CalculatesCorrectPercentage()
{
// Arrange
- var candle1 = new Managing.Domain.Candles.Candle
+ var candle1 = new Candle
{
Close = 100m,
Date = DateTime.UtcNow
};
- var candle2 = new Managing.Domain.Candles.Candle
+ var candle2 = new Candle
{
Close = 110m,
Date = DateTime.UtcNow.AddHours(1)
@@ -38,12 +38,12 @@ public class SimpleTradingBoxTests
public void GetHodlPercentage_WithPriceDecrease_CalculatesNegativePercentage()
{
// Arrange
- var candle1 = new Managing.Domain.Candles.Candle
+ var candle1 = new Candle
{
Close = 100m,
Date = DateTime.UtcNow
};
- var candle2 = new Managing.Domain.Candles.Candle
+ var candle2 = new Candle
{
Close = 90m,
Date = DateTime.UtcNow.AddHours(1)
@@ -97,4 +97,4 @@ public class SimpleTradingBoxTests
// Assert
result.Should().Be(fee);
}
-}
+}
\ No newline at end of file
diff --git a/src/Managing.Domain.Tests/SimpleTradingBoxTests.cs b/src/Managing.Domain.Tests/SimpleTradingBoxTests.cs
index 241afbae..529e4d1c 100644
--- a/src/Managing.Domain.Tests/SimpleTradingBoxTests.cs
+++ b/src/Managing.Domain.Tests/SimpleTradingBoxTests.cs
@@ -1,15 +1,6 @@
using FluentAssertions;
-using Managing.Common;
-using Managing.Domain.Accounts;
using Managing.Domain.Candles;
-using Managing.Domain.Indicators;
-using Managing.Domain.MoneyManagements;
-using Managing.Domain.Scenarios;
using Managing.Domain.Shared.Helpers;
-using Managing.Domain.Statistics;
-using Managing.Domain.Strategies;
-using Managing.Domain.Strategies.Base;
-using Managing.Domain.Trades;
using Xunit;
using static Managing.Common.Enums;
@@ -25,12 +16,12 @@ public class SimpleTradingBoxTests
public void GetHodlPercentage_WithPriceIncrease_CalculatesCorrectPercentage()
{
// Arrange
- var candle1 = new Managing.Domain.Candles.Candle
+ var candle1 = new Candle
{
Close = 100m,
Date = DateTime.UtcNow
};
- var candle2 = new Managing.Domain.Candles.Candle
+ var candle2 = new Candle
{
Close = 110m,
Date = DateTime.UtcNow.AddHours(1)
@@ -47,12 +38,12 @@ public class SimpleTradingBoxTests
public void GetHodlPercentage_WithPriceDecrease_CalculatesNegativePercentage()
{
// Arrange
- var candle1 = new Managing.Domain.Candles.Candle
+ var candle1 = new Candle
{
Close = 100m,
Date = DateTime.UtcNow
};
- var candle2 = new Managing.Domain.Candles.Candle
+ var candle2 = new Candle
{
Close = 90m,
Date = DateTime.UtcNow.AddHours(1)
@@ -106,4 +97,4 @@ public class SimpleTradingBoxTests
// Assert
result.Should().Be(fee);
}
-}
+}
\ No newline at end of file
diff --git a/src/Managing.Infrastructure.Tests/PrivyTradingTests.cs b/src/Managing.Infrastructure.Tests/PrivyTradingTests.cs
index 06064a74..8a6fe6a7 100644
--- a/src/Managing.Infrastructure.Tests/PrivyTradingTests.cs
+++ b/src/Managing.Infrastructure.Tests/PrivyTradingTests.cs
@@ -1,11 +1,6 @@
using Managing.Common;
using Managing.Domain.Trades;
-using Managing.Infrastructure.Evm;
-using Managing.Infrastructure.Evm.Models.Privy;
-using Managing.Infrastructure.Evm.Services;
using Xunit;
-using Managing.Infrastructure.Evm.Abstractions;
-using Microsoft.Extensions.Options;
namespace Managing.Infrastructure.Tests;
diff --git a/src/Managing.Infrastructure.Web3/Models/Proxy/GetGmxTradesResponse.cs b/src/Managing.Infrastructure.Web3/Models/Proxy/GetGmxTradesResponse.cs
index f1e79b2e..e6e65f66 100644
--- a/src/Managing.Infrastructure.Web3/Models/Proxy/GetGmxTradesResponse.cs
+++ b/src/Managing.Infrastructure.Web3/Models/Proxy/GetGmxTradesResponse.cs
@@ -1,12 +1,10 @@
using System.Text.Json.Serialization;
-using Managing.Domain.Trades;
using Newtonsoft.Json;
namespace Managing.Infrastructure.Evm.Models.Proxy;
public class GetGmxTradesResponse : Web3ProxyBaseResponse
{
-
[JsonProperty("trades")]
[JsonPropertyName("trades")]
public List Trades { get; set; }
@@ -53,4 +51,4 @@ public class GmxTrade
[JsonProperty("exchangeOrderId")]
[JsonPropertyName("exchangeOrderId")]
public string ExchangeOrderId { get; set; }
-}
+}
\ No newline at end of file