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

@@ -1002,6 +1002,25 @@ public class EvmManager : IEvmManager
return result;
}
public async Task<List<Position>> GetSpotPositionHistory(
Account account,
Ticker ticker,
DateTime? fromDate = null,
DateTime? toDate = null,
int pageIndex = 0,
int pageSize = 20)
{
var result = await _web3ProxyService.GetGmxSpotPositionHistoryAsync(
account.Key,
pageIndex,
pageSize,
ticker.ToString(),
fromDate,
toDate);
return result;
}
public async Task<decimal> GetKudaiStakedBalance(string address)
{
try

View File

@@ -697,5 +697,98 @@ namespace Managing.Infrastructure.Evm.Services
return positions;
}
public async Task<List<Position>> GetGmxSpotPositionHistoryAsync(
string account,
int pageIndex = 0,
int pageSize = 20,
string? ticker = null,
DateTime? fromDate = null,
DateTime? toDate = null)
{
var payload = new
{
account,
pageIndex,
pageSize,
ticker,
fromDateTime = fromDate?.ToString("O"),
toDateTime = toDate?.ToString("O")
};
var response = await GetGmxServiceAsync<GetGmxPositionHistoryResponse>("/spot-position-history", payload);
if (response == null)
{
throw new Web3ProxyException("Spot position history response is null");
}
if (!response.Success)
{
throw new Web3ProxyException($"Spot position history request failed: {response.Error}");
}
var positions = new List<Position>();
if (response.Positions != null)
{
foreach (var g in response.Positions)
{
try
{
var tickerEnum = MiscExtensions.ParseEnum<Ticker>(g.Ticker);
var directionEnum = MiscExtensions.ParseEnum<TradeDirection>(g.Direction);
var position = new Position(
Guid.NewGuid(),
0,
directionEnum,
tickerEnum,
null,
PositionInitiator.Bot,
g.Date,
null
)
{
Status = MiscExtensions.ParseEnum<PositionStatus>(g.Status)
};
if (g.Open != null)
{
position.Open = new Trade(
g.Open.Date,
MiscExtensions.ParseEnum<TradeDirection>(g.Open.Direction),
MiscExtensions.ParseEnum<TradeStatus>(g.Open.Status),
TradeType.Market,
tickerEnum,
(decimal)g.Open.Quantity,
(decimal)g.Open.Price,
(decimal)g.Open.Leverage,
g.Open.ExchangeOrderId,
string.Empty
);
}
position.ProfitAndLoss = new ProfitAndLoss
{
Realized = g.ProfitAndLoss?.Realized != null
? (decimal)g.ProfitAndLoss.Realized
: (decimal)g.Pnl,
Net = g.ProfitAndLoss?.Net != null ? (decimal)g.ProfitAndLoss.Net : (decimal)g.Pnl
};
position.UiFees = g.UiFees.HasValue ? (decimal)g.UiFees.Value : 0;
position.GasFees = g.GasFees.HasValue ? (decimal)g.GasFees.Value : 0;
positions.Add(position);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to map GMX spot position history entry");
}
}
}
return positions;
}
}
}