Implement spot position history retrieval in SpotBot and related services

- Added CheckSpotPositionInExchangeHistory method to SpotBot for verifying closed positions against exchange history.
- Enhanced logging for Web3Proxy errors during position verification.
- Introduced GetSpotPositionHistory method in IEvmManager, IExchangeService, and IWeb3ProxyService interfaces.
- Implemented GetSpotPositionHistory in EvmManager and ExchangeService to fetch historical swap data.
- Updated GMX SDK integration to support fetching spot position history.
- Modified generated API types to include new trading type and position history structures.
This commit is contained in:
2025-12-07 19:20:47 +07:00
parent 15d8b38d8b
commit a2ed4edd32
17 changed files with 978 additions and 84 deletions

View File

@@ -337,11 +337,31 @@ public class SpotBot : TradingBotBase, ITradingBot
// No token balance found - check if position was closed
if (internalPosition.Status == PositionStatus.Filled)
{
var (positionFoundInHistory, hadWeb3ProxyError) =
await CheckSpotPositionInExchangeHistory(internalPosition);
if (hadWeb3ProxyError)
{
await LogWarningAsync(
$"⏳ Web3Proxy Error During Spot Position Verification\n" +
$"Position: `{internalPosition.Identifier}`\n" +
$"Cannot verify if position is closed\n" +
$"Will retry on next execution cycle");
return;
}
if (positionFoundInHistory)
{
internalPosition.Status = PositionStatus.Finished;
await HandleClosedPosition(internalPosition);
return;
}
await LogDebugAsync(
$"⚠️ Position Status Check\n" +
$"Internal position `{internalPosition.Identifier}` shows Filled\n" +
$"But no token balance found on broker\n" +
$"Position may have been closed");
$"But no token balance found on broker or in history\n" +
$"Will retry verification on next cycle");
}
}
}
@@ -351,6 +371,56 @@ public class SpotBot : TradingBotBase, ITradingBot
}
}
private async Task<(bool found, bool hadError)> CheckSpotPositionInExchangeHistory(Position position)
{
try
{
await LogDebugAsync(
$"🔍 Checking Spot Position History for Position: `{position.Identifier}`\nTicker: `{Config.Ticker}`");
List<Position> positionHistory = null;
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
async exchangeService =>
{
var fromDate = DateTime.UtcNow.AddHours(-24);
var toDate = DateTime.UtcNow;
positionHistory =
await exchangeService.GetSpotPositionHistory(Account, Config.Ticker, fromDate, toDate);
});
if (positionHistory != null && positionHistory.Any())
{
var recentPosition = positionHistory
.OrderByDescending(p => p.Date)
.FirstOrDefault();
if (recentPosition != null)
{
await LogDebugAsync(
$"✅ Spot Position Found in History\n" +
$"Position: `{position.Identifier}`\n" +
$"Ticker: `{recentPosition.Ticker}`\n" +
$"Date: `{recentPosition.Date}`");
return (true, false);
}
}
await LogDebugAsync(
$"❌ No Spot Position Found in Exchange History\nPosition: `{position.Identifier}`\nPosition may still be open or data is delayed");
return (false, false);
}
catch (Exception ex)
{
Logger.LogError(ex, "Error checking spot position history for position {PositionId}", position.Identifier);
await LogWarningAsync(
$"⚠️ Web3Proxy Error During Spot Position History Check\n" +
$"Position: `{position.Identifier}`\n" +
$"Error: {ex.Message}\n" +
$"Will retry on next execution cycle");
return (false, true);
}
}
protected override async Task HandleOrderManagementAndPositionStatus(LightSignal signal, Position internalPosition,
Position positionForSignal)
{