Enhance UI, add AI integration, improve logging and database stats
This commit is contained in:
@@ -32,17 +32,19 @@ public class PolymarketProvider : IPlatformProvider
|
||||
|
||||
var mappedTrades = raw.Select(r =>
|
||||
{
|
||||
var wallet = r.User ?? r.ProxyWallet ?? "";
|
||||
var wallet = !string.IsNullOrEmpty(r.User) ? r.User :
|
||||
!string.IsNullOrEmpty(r.ProxyWallet) ? r.ProxyWallet :
|
||||
platformUserId;
|
||||
var side = MapTradeSide(r);
|
||||
var sideStr = side.ToString().ToUpperInvariant();
|
||||
// Compact format: {txHash}_{assetId}_{side} — no wallet in ID to reduce index size.
|
||||
// Wallet passed transiently via TransientWallet [NotMapped] for MarketHistoryWorker.
|
||||
// Format: {txHash}_{wallet}_{assetId}_{side}
|
||||
// Wallet must be included to avoid cross-user collisions in the global IX_Trades_Platform_PlatformTradeId index.
|
||||
return new Trade
|
||||
{
|
||||
Platform = PlatformType.Polymarket,
|
||||
PlatformTradeId = string.IsNullOrEmpty(r.TransactionHash)
|
||||
? $"{r.Timestamp}_{r.Asset}_{sideStr}"
|
||||
: $"{r.TransactionHash}_{r.Asset}_{sideStr}",
|
||||
? $"{r.Timestamp}_{wallet}_{r.Asset}_{sideStr}"
|
||||
: $"{r.TransactionHash.ToLowerInvariant()}_{wallet}_{r.Asset}_{sideStr}",
|
||||
MarketId = r.ConditionId ?? "",
|
||||
AssetId = r.Asset ?? "",
|
||||
Outcome = r.Outcome ?? "",
|
||||
@@ -51,13 +53,13 @@ public class PolymarketProvider : IPlatformProvider
|
||||
Size = (decimal)r.Size,
|
||||
Amount = (decimal)(r.Price * r.Size),
|
||||
ExecutedAt = DateTimeOffset.FromUnixTimeSeconds(r.Timestamp).UtcDateTime,
|
||||
TransactionHash = r.TransactionHash,
|
||||
TransactionHash = r.TransactionHash?.ToLowerInvariant(),
|
||||
TraderId = 0,
|
||||
TransientWallet = wallet,
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
return mappedTrades.GroupBy(t => t.PlatformTradeId).Select(g => g.First()).ToList();
|
||||
return mappedTrades.GroupBy(t => t.PlatformTradeId, StringComparer.OrdinalIgnoreCase).Select(g => g.First()).ToList();
|
||||
}
|
||||
|
||||
|
||||
@@ -75,8 +77,8 @@ public class PolymarketProvider : IPlatformProvider
|
||||
{
|
||||
Platform = PlatformType.Polymarket,
|
||||
PlatformTradeId = string.IsNullOrEmpty(r.TransactionHash)
|
||||
? $"{r.Timestamp}_{r.Asset}_{sideStr}"
|
||||
: $"{r.TransactionHash}_{r.Asset}_{sideStr}",
|
||||
? $"{r.Timestamp}_{wallet}_{r.Asset}_{sideStr}"
|
||||
: $"{r.TransactionHash.ToLowerInvariant()}_{wallet}_{r.Asset}_{sideStr}",
|
||||
MarketId = r.ConditionId ?? "",
|
||||
AssetId = r.Asset ?? "",
|
||||
Outcome = r.Outcome ?? "",
|
||||
@@ -127,33 +129,76 @@ public class PolymarketProvider : IPlatformProvider
|
||||
public async Task<Market?> GetMarketAsync(string platformMarketId, CancellationToken ct = default)
|
||||
{
|
||||
using var _ = PlatformLogContext.Push(PlatformName);
|
||||
_logger.LogDebug("Fetching market {MarketId}", platformMarketId);
|
||||
var raw = await _api.GetMarketAsync(platformMarketId, ct);
|
||||
if (raw == null)
|
||||
if (raw == null || string.IsNullOrEmpty(raw.ConditionId)) return null;
|
||||
|
||||
var parentTags = "";
|
||||
if (raw.Events != null && raw.Events.Count > 0)
|
||||
{
|
||||
_logger.LogWarning("Market {MarketId} not found", platformMarketId);
|
||||
return null;
|
||||
var ev = raw.Events[0];
|
||||
parentTags = ev.Tags != null ? string.Join(", ", ev.Tags.Select(t => t.Label)) : "";
|
||||
}
|
||||
var market = MapGammaMarket(raw, parentTags);
|
||||
|
||||
// Map the parent Event if available in the Market response
|
||||
if (raw.Events != null && raw.Events.Count > 0)
|
||||
{
|
||||
var rawEv = raw.Events[0];
|
||||
long.TryParse(rawEv.Id, out var numericEventId);
|
||||
market.Event = new Event
|
||||
{
|
||||
Platform = PlatformType.Polymarket,
|
||||
PlatformEventId = numericEventId,
|
||||
Slug = rawEv.Slug,
|
||||
Title = rawEv.Title,
|
||||
Description = rawEv.Description,
|
||||
ImageUrl = rawEv.Image,
|
||||
StartDate = DateTime.TryParse(rawEv.StartDate, out var esd) ? esd : null,
|
||||
EndDate = DateTime.TryParse(rawEv.EndDate, out var eed) ? eed : null,
|
||||
CreatedAt = DateTime.TryParse(rawEv.CreatedAt, out var ecd) ? ecd : DateTime.UtcNow,
|
||||
DbCreatedAt = DateTime.UtcNow,
|
||||
IsActive = rawEv.Active,
|
||||
IsClosed = rawEv.Closed,
|
||||
Tags = rawEv.Tags != null && rawEv.Tags.Count > 0 ? string.Join(", ", rawEv.Tags.Select(t => t.Label)) : string.Empty,
|
||||
LastUpdatedAt = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback empty event if missing (should rarely happen for valid Polymarket markets)
|
||||
market.Event = new Event
|
||||
{
|
||||
Platform = PlatformType.Polymarket,
|
||||
Slug = "unknown-" + market.ConditionId,
|
||||
Title = "Unknown Event",
|
||||
DbCreatedAt = DateTime.UtcNow,
|
||||
LastUpdatedAt = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
|
||||
_logger.LogInformation("Fetched market: {Question}", raw.Question);
|
||||
return MapGammaMarket(raw);
|
||||
return market;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<Market>> GetMarketsAsync(int limit = 100, string? cursor = null, bool includeClosed = false, CancellationToken ct = default)
|
||||
public async Task<IReadOnlyList<Event>> GetEventsAsync(int limit = 100, string? cursor = null, bool includeClosed = false, CancellationToken ct = default)
|
||||
{
|
||||
using var _ = PlatformLogContext.Push(PlatformName);
|
||||
int offset = 0;
|
||||
if (!string.IsNullOrEmpty(cursor) && int.TryParse(cursor, out var parsed))
|
||||
offset = parsed;
|
||||
|
||||
_logger.LogInformation("Fetching markets batch (limit={Limit}, offset={Offset}, includeClosed={Closed})", limit, offset, includeClosed);
|
||||
var raw = await _api.GetMarketsAsync(limit, offset, includeClosed, ct);
|
||||
_logger.LogInformation("Fetched {Count} markets from Gamma API", raw.Count);
|
||||
_logger.LogInformation("Fetching events batch (limit={Limit}, offset={Offset}, includeClosed={Closed})", limit, offset, includeClosed);
|
||||
var rawEvents = await _api.GetEventsAsync(limit, offset, includeClosed, ct);
|
||||
_logger.LogInformation("Fetched {Count} events from Gamma API", rawEvents.Count);
|
||||
|
||||
return raw
|
||||
.Where(m => !string.IsNullOrEmpty(m.ConditionId) && !string.IsNullOrEmpty(m.ClobTokenIds))
|
||||
.Select(MapGammaMarket)
|
||||
.ToList();
|
||||
var events = new List<Event>();
|
||||
foreach (var rawEvent in rawEvents)
|
||||
{
|
||||
var ev = MapGammaEvent(rawEvent);
|
||||
events.Add(ev);
|
||||
}
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<DiscoveredTrader>> GetTopHoldersAsync(string platformMarketId, int limit = 20, CancellationToken ct = default)
|
||||
@@ -192,31 +237,67 @@ public class PolymarketProvider : IPlatformProvider
|
||||
|
||||
// ── Private helpers ──────────────────────────────────────────
|
||||
|
||||
private Market MapGammaMarket(GammaMarketResponse raw)
|
||||
private Event MapGammaEvent(GammaEventResponse rawEvent)
|
||||
{
|
||||
var eventSlug = "";
|
||||
if (raw.Events != null && raw.Events.Count > 0 && !string.IsNullOrEmpty(raw.Events[0].Slug))
|
||||
long.TryParse(rawEvent.Id, out var numericId);
|
||||
|
||||
var ev = new Event
|
||||
{
|
||||
eventSlug = raw.Events[0].Slug;
|
||||
Platform = PlatformType.Polymarket,
|
||||
PlatformEventId = numericId,
|
||||
Slug = rawEvent.Slug,
|
||||
Title = rawEvent.Title,
|
||||
Description = rawEvent.Description,
|
||||
ImageUrl = rawEvent.Image,
|
||||
StartDate = DateTime.TryParse(rawEvent.StartDate, out var sd) ? sd : null,
|
||||
EndDate = DateTime.TryParse(rawEvent.EndDate, out var ed) ? ed : null,
|
||||
CreatedAt = DateTime.TryParse(rawEvent.CreatedAt, out var cd) ? cd : DateTime.UtcNow,
|
||||
DbCreatedAt = DateTime.UtcNow,
|
||||
IsActive = rawEvent.Active,
|
||||
IsClosed = rawEvent.Closed,
|
||||
Tags = rawEvent.Tags != null && rawEvent.Tags.Count > 0
|
||||
? string.Join(", ", rawEvent.Tags.Select(t => t.Label))
|
||||
: string.Empty,
|
||||
LastUpdatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
if (rawEvent.Markets != null)
|
||||
{
|
||||
foreach (var rawMarket in rawEvent.Markets)
|
||||
{
|
||||
if (string.IsNullOrEmpty(rawMarket.ConditionId) || string.IsNullOrEmpty(rawMarket.ClobTokenIds)) continue;
|
||||
|
||||
var market = MapGammaMarket(rawMarket, ev.Tags ?? "");
|
||||
ev.Markets.Add(market);
|
||||
}
|
||||
}
|
||||
|
||||
return ev;
|
||||
}
|
||||
|
||||
private Market MapGammaMarket(GammaMarketResponse raw, string parentTags = "")
|
||||
{
|
||||
long.TryParse(raw.Id, out var marketNumericId);
|
||||
|
||||
var market = new Market
|
||||
{
|
||||
Platform = PlatformType.Polymarket,
|
||||
PlatformMarketId = raw.ConditionId,
|
||||
PlatformMarketId = marketNumericId,
|
||||
ConditionId = raw.ConditionId,
|
||||
QuestionId = raw.QuestionId,
|
||||
MarketSlug = raw.Slug,
|
||||
EventSlug = eventSlug,
|
||||
Description = raw.Description,
|
||||
ImageUrl = raw.Image,
|
||||
Question = raw.Question,
|
||||
Category = raw.Category,
|
||||
Category = string.IsNullOrWhiteSpace(raw.Category) ? parentTags : raw.Category,
|
||||
Volume = (decimal)raw.Volume,
|
||||
Volume24h = (decimal)raw.Volume24hr,
|
||||
Liquidity = (decimal)raw.Liquidity,
|
||||
StartDate = DateTime.TryParse(raw.StartDate, out var sd) ? sd : null,
|
||||
EndDate = DateTime.TryParse(raw.EndDate, out var ed) ? ed : null,
|
||||
CreatedAt = DateTime.TryParse(raw.CreatedAt, out var cd) ? cd : DateTime.UtcNow,
|
||||
StartDate = DateTime.TryParse(raw.StartDate, out var msd) ? msd : null,
|
||||
EndDate = DateTime.TryParse(raw.EndDate ?? raw.EndDateIso, out var med) ? med : null,
|
||||
CreatedAt = DateTime.TryParse(raw.CreatedAt, out var mcd) ? mcd : DateTime.UtcNow,
|
||||
DbCreatedAt = DateTime.UtcNow,
|
||||
IsResolved = raw.Resolved || raw.Closed, // Prefer resolved flag
|
||||
IsResolved = raw.Resolved || raw.Closed,
|
||||
ResolutionOutcome = raw.ResolutionOutcome,
|
||||
LastUpdatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user