feat: Add category performance, update WebUI and OpenRouter integration, fix bugs
This commit is contained in:
@@ -18,6 +18,7 @@ public class AppDbContext : DbContext
|
||||
public DbSet<MarketAnalytics> MarketAnalytics => Set<MarketAnalytics>();
|
||||
public DbSet<TraderPosition> TraderPositions => Set<TraderPosition>();
|
||||
public DbSet<MarketOutcomePriceSnapshot> MarketOutcomePriceSnapshots => Set<MarketOutcomePriceSnapshot>();
|
||||
public DbSet<TraderCategoryPerformance> TraderCategoryPerformances => Set<TraderCategoryPerformance>();
|
||||
|
||||
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
|
||||
|
||||
@@ -54,7 +55,7 @@ public class AppDbContext : DbContext
|
||||
// Outcome: labels can be long (e.g. anime titles or sports match descriptions)
|
||||
e.Property(t => t.Outcome).HasMaxLength(128);
|
||||
// Price: 0.00–1.00 on prediction markets, 6 decimals sufficient
|
||||
e.Property(t => t.Price).HasPrecision(10, 6);
|
||||
e.Property(t => t.Price).HasPrecision(18, 6);
|
||||
// Size: number of shares, needs more integer digits
|
||||
e.Property(t => t.Size).HasPrecision(14, 6);
|
||||
e.Property(t => t.Amount).HasPrecision(18, 4);
|
||||
@@ -92,7 +93,8 @@ public class AppDbContext : DbContext
|
||||
e.Property(m => m.Question).HasMaxLength(1024);
|
||||
e.Property(m => m.Description).HasMaxLength(4096);
|
||||
e.Property(m => m.ImageUrl).HasMaxLength(1024);
|
||||
e.Property(m => m.Category).HasMaxLength(128);
|
||||
e.Property(m => m.Category).HasConversion<string>().HasMaxLength(64);
|
||||
e.Property(m => m.Subcategory).HasMaxLength(128);
|
||||
e.Property(m => m.Volume).HasPrecision(18, 4);
|
||||
e.Property(m => m.Volume24h).HasPrecision(18, 4);
|
||||
e.Property(m => m.Liquidity).HasPrecision(18, 4);
|
||||
@@ -142,6 +144,17 @@ public class AppDbContext : DbContext
|
||||
e.HasOne(a => a.Trader).WithMany().HasForeignKey(a => a.TraderId).OnDelete(DeleteBehavior.SetNull);
|
||||
});
|
||||
|
||||
// TraderCategoryPerformance
|
||||
mb.Entity<TraderCategoryPerformance>(e =>
|
||||
{
|
||||
e.HasKey(tcp => tcp.Id);
|
||||
e.HasOne(tcp => tcp.Trader).WithMany().HasForeignKey(tcp => tcp.TraderId).OnDelete(DeleteBehavior.Cascade);
|
||||
e.Property(tcp => tcp.Category).HasConversion<string>().HasMaxLength(64);
|
||||
e.Property(tcp => tcp.TotalVolume).HasPrecision(18, 4);
|
||||
e.Property(tcp => tcp.TotalPnL).HasPrecision(18, 4);
|
||||
e.HasIndex(tcp => new { tcp.TraderId, tcp.Category }).IsUnique();
|
||||
});
|
||||
|
||||
// PlatformConfig
|
||||
mb.Entity<PlatformConfig>(e =>
|
||||
{
|
||||
|
||||
@@ -192,8 +192,8 @@ public class MarketRepository : IMarketRepository
|
||||
existing.PlatformMarketId = updated.PlatformMarketId;
|
||||
existing.QuestionId = updated.QuestionId;
|
||||
existing.Description = updated.Description;
|
||||
existing.ImageUrl = updated.ImageUrl;
|
||||
existing.Category = updated.Category;
|
||||
existing.Subcategory = updated.Subcategory;
|
||||
existing.Volume = updated.Volume;
|
||||
existing.Volume24h = updated.Volume24h;
|
||||
existing.Liquidity = updated.Liquidity;
|
||||
@@ -230,7 +230,7 @@ public class MarketRepository : IMarketRepository
|
||||
market.Description = StringHelper.Truncate(market.Description, 4096);
|
||||
market.MarketSlug = StringHelper.Truncate(market.MarketSlug, 512) ?? "";
|
||||
market.ImageUrl = StringHelper.Truncate(market.ImageUrl, 1024);
|
||||
market.Category = StringHelper.Truncate(market.Category, 128) ?? "";
|
||||
market.Subcategory = StringHelper.Truncate(market.Subcategory, 128) ?? "";
|
||||
|
||||
foreach (var o in market.Outcomes)
|
||||
{
|
||||
|
||||
@@ -14,27 +14,27 @@ public class TradeRepository : ITradeRepository
|
||||
=> await _db.Trades.FirstOrDefaultAsync(t => t.Platform == platform && t.PlatformTradeId == platformTradeId, ct);
|
||||
|
||||
public async Task<IReadOnlyList<Trade>> GetByTraderIdAsync(int traderId, int skip = 0, int take = 50, CancellationToken ct = default)
|
||||
=> await _db.Trades.Include(t => t.Trader).Where(t => t.TraderId == traderId)
|
||||
=> await _db.Trades.Include(t => t.Trader).Include(t => t.DbMarket).Where(t => t.TraderId == traderId)
|
||||
.OrderByDescending(t => t.ExecutedAt).Skip(skip).Take(take).ToListAsync(ct);
|
||||
|
||||
public async Task<IReadOnlyList<Trade>> GetByDbMarketIdAsync(int dbMarketId, int skip = 0, int take = 50, CancellationToken ct = default)
|
||||
=> await _db.Trades.Include(t => t.Trader).Where(t => t.DbMarketId == dbMarketId)
|
||||
=> await _db.Trades.Include(t => t.Trader).Include(t => t.DbMarket).Where(t => t.DbMarketId == dbMarketId)
|
||||
.OrderByDescending(t => t.ExecutedAt).Skip(skip).Take(take).ToListAsync(ct);
|
||||
|
||||
public async Task<IReadOnlyList<Trade>> GetByMarketIdAsync(string platformMarketId, int skip = 0, int take = 50, CancellationToken ct = default)
|
||||
=> await _db.Trades.Include(t => t.Trader).Where(t => t.MarketId == platformMarketId)
|
||||
=> await _db.Trades.Include(t => t.Trader).Include(t => t.DbMarket).Where(t => t.MarketId == platformMarketId)
|
||||
.OrderByDescending(t => t.ExecutedAt).Skip(skip).Take(take).ToListAsync(ct);
|
||||
|
||||
public async Task<IReadOnlyList<Trade>> GetRecentAsync(int count = 50, PlatformType? platform = null, CancellationToken ct = default)
|
||||
{
|
||||
var q = _db.Trades.Include(t => t.Trader).AsQueryable();
|
||||
var q = _db.Trades.Include(t => t.Trader).Include(t => t.DbMarket).AsQueryable();
|
||||
if (platform.HasValue) q = q.Where(t => t.Platform == platform.Value);
|
||||
return await q.OrderByDescending(t => t.ExecutedAt).Take(count).ToListAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<Trade>> GetLargestAsync(int count = 5, DateTime? since = null, CancellationToken ct = default)
|
||||
{
|
||||
var q = _db.Trades.Include(t => t.Trader).AsQueryable();
|
||||
var q = _db.Trades.Include(t => t.Trader).Include(t => t.DbMarket).AsQueryable();
|
||||
if (since.HasValue) q = q.Where(t => t.ExecutedAt >= since.Value);
|
||||
return await q.OrderByDescending(t => t.Amount).Take(count).ToListAsync(ct);
|
||||
}
|
||||
|
||||
@@ -11,10 +11,10 @@ public class TraderRepository : ITraderRepository
|
||||
public TraderRepository(AppDbContext db) => _db = db;
|
||||
|
||||
public async Task<Trader?> GetByIdAsync(int id, CancellationToken ct = default)
|
||||
=> await _db.Traders.Include(t => t.CurrentScore).FirstOrDefaultAsync(t => t.Id == id, ct);
|
||||
=> await _db.Traders.Include(t => t.CurrentScore).Include(t => t.CategoryPerformances).FirstOrDefaultAsync(t => t.Id == id, ct);
|
||||
|
||||
public async Task<Trader?> GetByPlatformIdAsync(PlatformType platform, string platformUserId, CancellationToken ct = default)
|
||||
=> await _db.Traders.Include(t => t.CurrentScore)
|
||||
=> await _db.Traders.Include(t => t.CurrentScore).Include(t => t.CategoryPerformances)
|
||||
.FirstOrDefaultAsync(t => t.Platform == platform && t.PlatformUserId == platformUserId, ct);
|
||||
|
||||
public async Task<IReadOnlyList<Trader>> GetAllAsync(PlatformType? platform = null, int skip = 0, int take = 50, CancellationToken ct = default)
|
||||
@@ -75,15 +75,18 @@ public class TraderRepository : ITraderRepository
|
||||
|
||||
public async Task<IReadOnlyList<Trader>> GetTradersDueForTradeUpdateAsync(int cooldownHours = 12, int take = 20, CancellationToken ct = default)
|
||||
{
|
||||
// Prioritize:
|
||||
// 1. Traders needing initial import (IsInitialImportComplete == false)
|
||||
// 2. Traders where LastTradesUpdatedAt < cutoff (cooldownHours)
|
||||
|
||||
var cutoff = DateTime.UtcNow.AddHours(-cooldownHours);
|
||||
var normalCutoff = DateTime.UtcNow.AddHours(-cooldownHours);
|
||||
var priorityCutoff = DateTime.UtcNow.AddHours(-1); // Sync priority traders more often, but not continuously
|
||||
|
||||
return await _db.Traders
|
||||
.Where(t => !t.IsInitialImportComplete || t.LastTradesUpdatedAt == null || t.LastTradesUpdatedAt < cutoff)
|
||||
.OrderBy(t => t.IsInitialImportComplete) // false (0) comes before true (1)
|
||||
.Include(t => t.WatchlistEntries)
|
||||
.Where(t => t.LastTradesUpdatedAt == null ||
|
||||
(!t.IsInitialImportComplete) ||
|
||||
((!t.IsAutoDiscovered || t.WatchlistEntries.Any()) && t.LastTradesUpdatedAt < priorityCutoff) ||
|
||||
(t.IsAutoDiscovered && t.LastTradesUpdatedAt < normalCutoff))
|
||||
.OrderBy(t => t.IsAutoDiscovered) // Manual first (false = 0)
|
||||
.ThenByDescending(t => t.WatchlistEntries.Any()) // Watchlisted next (true = 1)
|
||||
.ThenBy(t => t.IsInitialImportComplete) // New ones next (false = 0)
|
||||
.ThenBy(t => t.LastTradesUpdatedAt ?? DateTime.MinValue) // Oldest first
|
||||
.Take(take)
|
||||
.ToListAsync(ct);
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
using Predictalytics.Domain.Enums;
|
||||
|
||||
namespace Predictalytics.Infrastructure.Helpers;
|
||||
|
||||
public static class MarketCategoryMapper
|
||||
{
|
||||
public static (MarketCategory Category, string Subcategory) Map(string rawCategory, string tags)
|
||||
{
|
||||
var searchString = $"{rawCategory} {tags}".ToLowerInvariant();
|
||||
|
||||
if (searchString.Contains("politic") || searchString.Contains("election") || searchString.Contains("trump") || searchString.Contains("biden"))
|
||||
return (MarketCategory.Politics, GetSubcategory(rawCategory, tags, "Elections"));
|
||||
|
||||
if (searchString.Contains("crypto") || searchString.Contains("bitcoin") || searchString.Contains("eth") || searchString.Contains("solana"))
|
||||
return (MarketCategory.Crypto, GetSubcategory(rawCategory, tags, "Crypto"));
|
||||
|
||||
if (searchString.Contains("sport") || searchString.Contains("nfl") || searchString.Contains("nba") || searchString.Contains("soccer") || searchString.Contains("tennis"))
|
||||
return (MarketCategory.Sports, GetSubcategory(rawCategory, tags, "Sports"));
|
||||
|
||||
if (searchString.Contains("pop") || searchString.Contains("culture") || searchString.Contains("movie") || searchString.Contains("oscars") || searchString.Contains("music"))
|
||||
return (MarketCategory.PopCulture, GetSubcategory(rawCategory, tags, "Pop Culture"));
|
||||
|
||||
if (searchString.Contains("science") || searchString.Contains("space") || searchString.Contains("weather") || searchString.Contains("climate"))
|
||||
return (MarketCategory.Science, GetSubcategory(rawCategory, tags, "Science"));
|
||||
|
||||
if (searchString.Contains("news") || searchString.Contains("global") || searchString.Contains("world"))
|
||||
return (MarketCategory.GlobalNews, GetSubcategory(rawCategory, tags, "Global News"));
|
||||
|
||||
if (searchString.Contains("economy") || searchString.Contains("finance") || searchString.Contains("business") || searchString.Contains("fed"))
|
||||
return (MarketCategory.Economy, GetSubcategory(rawCategory, tags, "Economy"));
|
||||
|
||||
return (MarketCategory.Other, GetSubcategory(rawCategory, tags, "Other"));
|
||||
}
|
||||
|
||||
private static string GetSubcategory(string rawCategory, string tags, string fallback)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(rawCategory) && !rawCategory.Equals("OVERALL", StringComparison.OrdinalIgnoreCase))
|
||||
return rawCategory;
|
||||
|
||||
var firstTag = tags.Split(',', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault()?.Trim();
|
||||
if (!string.IsNullOrWhiteSpace(firstTag))
|
||||
return firstTag;
|
||||
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
+855
@@ -0,0 +1,855 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Predictalytics.Infrastructure.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Predictalytics.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20260705114852_AddMarketCategoryAndSubcategory")]
|
||||
partial class AddMarketCategoryAndSubcategory
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Alert", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<bool>("IsRead")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4096)
|
||||
.HasColumnType("varchar(4096)");
|
||||
|
||||
b.Property<int>("Platform")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Severity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("varchar(512)");
|
||||
|
||||
b.Property<int?>("TraderId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CreatedAt");
|
||||
|
||||
b.HasIndex("TraderId");
|
||||
|
||||
b.ToTable("Alerts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Event", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime>("DbCreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(4096)
|
||||
.HasColumnType("varchar(4096)");
|
||||
|
||||
b.Property<DateTime?>("EndDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("ImageUrl")
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("varchar(1024)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("IsClosed")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<DateTime?>("LastUpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("Platform")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<long>("PlatformEventId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("varchar(512)");
|
||||
|
||||
b.Property<DateTime?>("StartDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Tags")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("varchar(1024)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("varchar(1024)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Platform", "PlatformEventId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Events");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Market", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Category")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("varchar(64)");
|
||||
|
||||
b.Property<string>("ConditionId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime>("DbCreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(4096)
|
||||
.HasColumnType("varchar(4096)");
|
||||
|
||||
b.Property<DateTime?>("EndDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("EventId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ImageUrl")
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("varchar(1024)");
|
||||
|
||||
b.Property<bool>("IsResolved")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<DateTime?>("LastTradesUpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime?>("LastUpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<decimal>("Liquidity")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<string>("MarketSlug")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("varchar(512)");
|
||||
|
||||
b.Property<int>("Platform")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<long>("PlatformMarketId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Question")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("varchar(1024)");
|
||||
|
||||
b.Property<string>("QuestionId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<string>("ResolutionOutcome")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime?>("StartDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Subcategory")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)");
|
||||
|
||||
b.Property<decimal>("Volume")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<decimal>("Volume24h")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("EventId");
|
||||
|
||||
b.HasIndex("Platform", "PlatformMarketId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Markets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.MarketAnalytics", b =>
|
||||
{
|
||||
b.Property<int>("MarketId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("AverageTradeSize")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<decimal>("BotActivityScore")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.Property<DateTime>("LastCalculatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("UniqueTradersCount")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("MarketId");
|
||||
|
||||
b.ToTable("MarketAnalytics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.MarketOutcome", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<decimal>("CurrentPrice")
|
||||
.HasPrecision(18, 8)
|
||||
.HasColumnType("decimal(18,8)");
|
||||
|
||||
b.Property<string>("Label")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<int>("MarketId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("OutcomeIndex")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("TokenId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TokenId");
|
||||
|
||||
b.HasIndex("MarketId", "OutcomeIndex")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("MarketOutcomes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.MarketOutcomePriceSnapshot", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("MarketOutcomeId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("Price")
|
||||
.HasPrecision(10, 6)
|
||||
.HasColumnType("decimal(10,6)");
|
||||
|
||||
b.Property<DateTime>("Timestamp")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MarketOutcomeId", "Timestamp");
|
||||
|
||||
b.ToTable("MarketOutcomePriceSnapshots");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.PlatformConfig", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("BaseUrl")
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("varchar(1024)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)");
|
||||
|
||||
b.Property<string>("SettingsJson")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PlatformConfigs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Trade", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<decimal>("Amount")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<string>("AssetId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(80)
|
||||
.HasColumnType("varchar(80)");
|
||||
|
||||
b.Property<int?>("DbMarketId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("ExecutedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<bool>("IsContextEnriched")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("MarketId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(66)
|
||||
.HasColumnType("varchar(66)");
|
||||
|
||||
b.Property<int?>("MarketOutcomeId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Outcome")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)");
|
||||
|
||||
b.Property<int>("Platform")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("PlatformTradeId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<decimal?>("PostTradePrice1m")
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<decimal?>("PreTradePrice1m")
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<decimal>("Price")
|
||||
.HasPrecision(10, 6)
|
||||
.HasColumnType("decimal(10,6)");
|
||||
|
||||
b.Property<int>("Side")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.HasPrecision(14, 6)
|
||||
.HasColumnType("decimal(14,6)");
|
||||
|
||||
b.Property<int>("TraderId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("TransactionHash")
|
||||
.HasMaxLength(66)
|
||||
.HasColumnType("varchar(66)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AssetId");
|
||||
|
||||
b.HasIndex("DbMarketId");
|
||||
|
||||
b.HasIndex("ExecutedAt");
|
||||
|
||||
b.HasIndex("MarketOutcomeId");
|
||||
|
||||
b.HasIndex("TraderId");
|
||||
|
||||
b.HasIndex("Platform", "PlatformTradeId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Trades");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Trader", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("AiStrategySummary")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime?>("AiStrategyUpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<bool>("IsAutoDiscovered")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("IsInitialImportComplete")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("IsSuspectedBot")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<DateTime?>("LastApiErrorAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime?>("LastPolledAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime?>("LastTradesUpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int?>("ManualPriorityOverride")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("Platform")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("PlatformUserId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)");
|
||||
|
||||
b.Property<int>("Strategy")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Tier")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("TotalPnl")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<int>("TotalTrades")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("WinRate")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Platform", "PlatformUserId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Traders");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderAnalytics", b =>
|
||||
{
|
||||
b.Property<int>("TraderId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("LastCalculatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<decimal>("OverallPnL")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<decimal>("OverallWinRate")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.Property<decimal>("PnL24h")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<decimal>("PnL30d")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<decimal>("PnL7d")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<decimal>("WinRate24h")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.Property<decimal>("WinRate30d")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.Property<decimal>("WinRate7d")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.HasKey("TraderId");
|
||||
|
||||
b.ToTable("TraderAnalytics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderPosition", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<decimal>("AvgCost")
|
||||
.HasPrecision(10, 6)
|
||||
.HasColumnType("decimal(10,6)");
|
||||
|
||||
b.Property<DateTime>("LastUpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("MarketOutcomeId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("RealizedPnl")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<decimal>("SharesHeld")
|
||||
.HasPrecision(14, 6)
|
||||
.HasColumnType("decimal(14,6)");
|
||||
|
||||
b.Property<int>("TraderId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MarketOutcomeId");
|
||||
|
||||
b.HasIndex("TraderId", "MarketOutcomeId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("TraderPositions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderScore", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<decimal>("ActivityScore")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.Property<DateTime>("CalculatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<decimal>("CombinedScore")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.Property<decimal>("CopytradingScore")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.Property<decimal>("QualityScore")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.Property<int>("Rank")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("TimingScore")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.Property<int>("TraderId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("VolumeScore")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TraderId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("TraderScores");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.WatchlistEntry", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<bool>("AlertsEnabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Label")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("TraderId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TraderId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("WatchlistEntries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Alert", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||
.WithMany()
|
||||
.HasForeignKey("TraderId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Trader");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Market", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.Event", "Event")
|
||||
.WithMany("Markets")
|
||||
.HasForeignKey("EventId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Event");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.MarketAnalytics", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.Market", "Market")
|
||||
.WithOne("Analytics")
|
||||
.HasForeignKey("Predictalytics.Domain.Entities.MarketAnalytics", "MarketId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Market");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.MarketOutcome", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.Market", "Market")
|
||||
.WithMany("Outcomes")
|
||||
.HasForeignKey("MarketId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Market");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.MarketOutcomePriceSnapshot", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.MarketOutcome", "MarketOutcome")
|
||||
.WithMany()
|
||||
.HasForeignKey("MarketOutcomeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("MarketOutcome");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Trade", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.Market", "DbMarket")
|
||||
.WithMany()
|
||||
.HasForeignKey("DbMarketId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("Predictalytics.Domain.Entities.MarketOutcome", "MarketOutcome")
|
||||
.WithMany()
|
||||
.HasForeignKey("MarketOutcomeId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||
.WithMany("Trades")
|
||||
.HasForeignKey("TraderId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("DbMarket");
|
||||
|
||||
b.Navigation("MarketOutcome");
|
||||
|
||||
b.Navigation("Trader");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderAnalytics", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||
.WithOne("Analytics")
|
||||
.HasForeignKey("Predictalytics.Domain.Entities.TraderAnalytics", "TraderId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Trader");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderPosition", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.MarketOutcome", "MarketOutcome")
|
||||
.WithMany()
|
||||
.HasForeignKey("MarketOutcomeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||
.WithMany("Positions")
|
||||
.HasForeignKey("TraderId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("MarketOutcome");
|
||||
|
||||
b.Navigation("Trader");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderScore", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||
.WithOne("CurrentScore")
|
||||
.HasForeignKey("Predictalytics.Domain.Entities.TraderScore", "TraderId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Trader");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.WatchlistEntry", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||
.WithMany("WatchlistEntries")
|
||||
.HasForeignKey("TraderId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Trader");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Event", b =>
|
||||
{
|
||||
b.Navigation("Markets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Market", b =>
|
||||
{
|
||||
b.Navigation("Analytics");
|
||||
|
||||
b.Navigation("Outcomes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Trader", b =>
|
||||
{
|
||||
b.Navigation("Analytics");
|
||||
|
||||
b.Navigation("CurrentScore");
|
||||
|
||||
b.Navigation("Positions");
|
||||
|
||||
b.Navigation("Trades");
|
||||
|
||||
b.Navigation("WatchlistEntries");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Predictalytics.Infrastructure.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddMarketCategoryAndSubcategory : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Subcategory",
|
||||
table: "Markets",
|
||||
type: "varchar(128)",
|
||||
maxLength: 128,
|
||||
nullable: false,
|
||||
defaultValue: "")
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.Sql("UPDATE Markets SET Subcategory = Category;");
|
||||
migrationBuilder.Sql("UPDATE Markets SET Category = 'Other';");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Category",
|
||||
table: "Markets",
|
||||
type: "varchar(64)",
|
||||
maxLength: 64,
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "varchar(128)",
|
||||
oldMaxLength: 128)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Subcategory",
|
||||
table: "Markets");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Category",
|
||||
table: "Markets",
|
||||
type: "varchar(128)",
|
||||
maxLength: 128,
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "varchar(64)",
|
||||
oldMaxLength: 64)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
}
|
||||
}
|
||||
src/Predictalytics.Infrastructure/Migrations/20260705115634_AddTraderCategoryPerformance.Designer.cs
Generated
+904
@@ -0,0 +1,904 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Predictalytics.Infrastructure.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Predictalytics.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20260705115634_AddTraderCategoryPerformance")]
|
||||
partial class AddTraderCategoryPerformance
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Alert", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<bool>("IsRead")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4096)
|
||||
.HasColumnType("varchar(4096)");
|
||||
|
||||
b.Property<int>("Platform")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Severity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("varchar(512)");
|
||||
|
||||
b.Property<int?>("TraderId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CreatedAt");
|
||||
|
||||
b.HasIndex("TraderId");
|
||||
|
||||
b.ToTable("Alerts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Event", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime>("DbCreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(4096)
|
||||
.HasColumnType("varchar(4096)");
|
||||
|
||||
b.Property<DateTime?>("EndDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("ImageUrl")
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("varchar(1024)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("IsClosed")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<DateTime?>("LastUpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("Platform")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<long>("PlatformEventId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("varchar(512)");
|
||||
|
||||
b.Property<DateTime?>("StartDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Tags")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("varchar(1024)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("varchar(1024)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Platform", "PlatformEventId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Events");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Market", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Category")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("varchar(64)");
|
||||
|
||||
b.Property<string>("ConditionId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime>("DbCreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(4096)
|
||||
.HasColumnType("varchar(4096)");
|
||||
|
||||
b.Property<DateTime?>("EndDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("EventId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ImageUrl")
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("varchar(1024)");
|
||||
|
||||
b.Property<bool>("IsResolved")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<DateTime?>("LastTradesUpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime?>("LastUpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<decimal>("Liquidity")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<string>("MarketSlug")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("varchar(512)");
|
||||
|
||||
b.Property<int>("Platform")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<long>("PlatformMarketId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Question")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("varchar(1024)");
|
||||
|
||||
b.Property<string>("QuestionId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<string>("ResolutionOutcome")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime?>("StartDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Subcategory")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)");
|
||||
|
||||
b.Property<decimal>("Volume")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<decimal>("Volume24h")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("EventId");
|
||||
|
||||
b.HasIndex("Platform", "PlatformMarketId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Markets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.MarketAnalytics", b =>
|
||||
{
|
||||
b.Property<int>("MarketId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("AverageTradeSize")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<decimal>("BotActivityScore")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.Property<DateTime>("LastCalculatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("UniqueTradersCount")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("MarketId");
|
||||
|
||||
b.ToTable("MarketAnalytics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.MarketOutcome", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<decimal>("CurrentPrice")
|
||||
.HasPrecision(18, 8)
|
||||
.HasColumnType("decimal(18,8)");
|
||||
|
||||
b.Property<string>("Label")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<int>("MarketId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("OutcomeIndex")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("TokenId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TokenId");
|
||||
|
||||
b.HasIndex("MarketId", "OutcomeIndex")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("MarketOutcomes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.MarketOutcomePriceSnapshot", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("MarketOutcomeId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("Price")
|
||||
.HasPrecision(10, 6)
|
||||
.HasColumnType("decimal(10,6)");
|
||||
|
||||
b.Property<DateTime>("Timestamp")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MarketOutcomeId", "Timestamp");
|
||||
|
||||
b.ToTable("MarketOutcomePriceSnapshots");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.PlatformConfig", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("BaseUrl")
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("varchar(1024)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)");
|
||||
|
||||
b.Property<string>("SettingsJson")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PlatformConfigs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Trade", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<decimal>("Amount")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<string>("AssetId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(80)
|
||||
.HasColumnType("varchar(80)");
|
||||
|
||||
b.Property<int?>("DbMarketId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("ExecutedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<bool>("IsContextEnriched")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("MarketId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(66)
|
||||
.HasColumnType("varchar(66)");
|
||||
|
||||
b.Property<int?>("MarketOutcomeId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Outcome")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)");
|
||||
|
||||
b.Property<int>("Platform")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("PlatformTradeId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<decimal?>("PostTradePrice1m")
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<decimal?>("PreTradePrice1m")
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<decimal>("Price")
|
||||
.HasPrecision(18, 6)
|
||||
.HasColumnType("decimal(18,6)");
|
||||
|
||||
b.Property<int>("Side")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.HasPrecision(14, 6)
|
||||
.HasColumnType("decimal(14,6)");
|
||||
|
||||
b.Property<int>("TraderId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("TransactionHash")
|
||||
.HasMaxLength(66)
|
||||
.HasColumnType("varchar(66)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AssetId");
|
||||
|
||||
b.HasIndex("DbMarketId");
|
||||
|
||||
b.HasIndex("ExecutedAt");
|
||||
|
||||
b.HasIndex("MarketOutcomeId");
|
||||
|
||||
b.HasIndex("TraderId");
|
||||
|
||||
b.HasIndex("Platform", "PlatformTradeId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Trades");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Trader", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("AiStrategySummary")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime?>("AiStrategyUpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<bool>("IsAutoDiscovered")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("IsInitialImportComplete")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("IsSuspectedBot")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<DateTime?>("LastApiErrorAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime?>("LastPolledAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime?>("LastTradesUpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int?>("ManualPriorityOverride")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("Platform")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("PlatformUserId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)");
|
||||
|
||||
b.Property<int>("Strategy")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Tier")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("TotalPnl")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<int>("TotalTrades")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("WinRate")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Platform", "PlatformUserId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Traders");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderAnalytics", b =>
|
||||
{
|
||||
b.Property<int>("TraderId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("LastCalculatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<decimal>("OverallPnL")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<decimal>("OverallWinRate")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.Property<decimal>("PnL24h")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<decimal>("PnL30d")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<decimal>("PnL7d")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<decimal>("WinRate24h")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.Property<decimal>("WinRate30d")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.Property<decimal>("WinRate7d")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.HasKey("TraderId");
|
||||
|
||||
b.ToTable("TraderAnalytics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderCategoryPerformance", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Category")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("varchar(64)");
|
||||
|
||||
b.Property<decimal>("TotalPnL")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<int>("TotalTrades")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("TotalVolume")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<int>("TraderId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("WinningTrades")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TraderId", "Category")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("TraderCategoryPerformances");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderPosition", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<decimal>("AvgCost")
|
||||
.HasPrecision(10, 6)
|
||||
.HasColumnType("decimal(10,6)");
|
||||
|
||||
b.Property<DateTime>("LastUpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("MarketOutcomeId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("RealizedPnl")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<decimal>("SharesHeld")
|
||||
.HasPrecision(14, 6)
|
||||
.HasColumnType("decimal(14,6)");
|
||||
|
||||
b.Property<int>("TraderId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MarketOutcomeId");
|
||||
|
||||
b.HasIndex("TraderId", "MarketOutcomeId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("TraderPositions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderScore", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<decimal>("ActivityScore")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.Property<DateTime>("CalculatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<decimal>("CombinedScore")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.Property<decimal>("CopytradingScore")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.Property<decimal>("QualityScore")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.Property<int>("Rank")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("TimingScore")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.Property<int>("TraderId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("VolumeScore")
|
||||
.HasPrecision(8, 4)
|
||||
.HasColumnType("decimal(8,4)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TraderId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("TraderScores");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.WatchlistEntry", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<bool>("AlertsEnabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Label")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("TraderId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TraderId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("WatchlistEntries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Alert", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||
.WithMany()
|
||||
.HasForeignKey("TraderId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Trader");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Market", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.Event", "Event")
|
||||
.WithMany("Markets")
|
||||
.HasForeignKey("EventId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Event");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.MarketAnalytics", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.Market", "Market")
|
||||
.WithOne("Analytics")
|
||||
.HasForeignKey("Predictalytics.Domain.Entities.MarketAnalytics", "MarketId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Market");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.MarketOutcome", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.Market", "Market")
|
||||
.WithMany("Outcomes")
|
||||
.HasForeignKey("MarketId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Market");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.MarketOutcomePriceSnapshot", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.MarketOutcome", "MarketOutcome")
|
||||
.WithMany()
|
||||
.HasForeignKey("MarketOutcomeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("MarketOutcome");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Trade", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.Market", "DbMarket")
|
||||
.WithMany()
|
||||
.HasForeignKey("DbMarketId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("Predictalytics.Domain.Entities.MarketOutcome", "MarketOutcome")
|
||||
.WithMany()
|
||||
.HasForeignKey("MarketOutcomeId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||
.WithMany("Trades")
|
||||
.HasForeignKey("TraderId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("DbMarket");
|
||||
|
||||
b.Navigation("MarketOutcome");
|
||||
|
||||
b.Navigation("Trader");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderAnalytics", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||
.WithOne("Analytics")
|
||||
.HasForeignKey("Predictalytics.Domain.Entities.TraderAnalytics", "TraderId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Trader");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderCategoryPerformance", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||
.WithMany()
|
||||
.HasForeignKey("TraderId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Trader");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderPosition", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.MarketOutcome", "MarketOutcome")
|
||||
.WithMany()
|
||||
.HasForeignKey("MarketOutcomeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||
.WithMany("Positions")
|
||||
.HasForeignKey("TraderId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("MarketOutcome");
|
||||
|
||||
b.Navigation("Trader");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderScore", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||
.WithOne("CurrentScore")
|
||||
.HasForeignKey("Predictalytics.Domain.Entities.TraderScore", "TraderId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Trader");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.WatchlistEntry", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||
.WithMany("WatchlistEntries")
|
||||
.HasForeignKey("TraderId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Trader");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Event", b =>
|
||||
{
|
||||
b.Navigation("Markets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Market", b =>
|
||||
{
|
||||
b.Navigation("Analytics");
|
||||
|
||||
b.Navigation("Outcomes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.Trader", b =>
|
||||
{
|
||||
b.Navigation("Analytics");
|
||||
|
||||
b.Navigation("CurrentScore");
|
||||
|
||||
b.Navigation("Positions");
|
||||
|
||||
b.Navigation("Trades");
|
||||
|
||||
b.Navigation("WatchlistEntries");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Predictalytics.Infrastructure.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddTraderCategoryPerformance : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<decimal>(
|
||||
name: "Price",
|
||||
table: "Trades",
|
||||
type: "decimal(18,6)",
|
||||
precision: 18,
|
||||
scale: 6,
|
||||
nullable: false,
|
||||
oldClrType: typeof(decimal),
|
||||
oldType: "decimal(10,6)",
|
||||
oldPrecision: 10,
|
||||
oldScale: 6);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "TraderCategoryPerformances",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
TraderId = table.Column<int>(type: "int", nullable: false),
|
||||
Category = table.Column<string>(type: "varchar(64)", maxLength: 64, nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
TotalVolume = table.Column<decimal>(type: "decimal(18,4)", precision: 18, scale: 4, nullable: false),
|
||||
TotalPnL = table.Column<decimal>(type: "decimal(18,4)", precision: 18, scale: 4, nullable: false),
|
||||
TotalTrades = table.Column<int>(type: "int", nullable: false),
|
||||
WinningTrades = table.Column<int>(type: "int", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_TraderCategoryPerformances", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_TraderCategoryPerformances_Traders_TraderId",
|
||||
column: x => x.TraderId,
|
||||
principalTable: "Traders",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_TraderCategoryPerformances_TraderId_Category",
|
||||
table: "TraderCategoryPerformances",
|
||||
columns: new[] { "TraderId", "Category" },
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "TraderCategoryPerformances");
|
||||
|
||||
migrationBuilder.AlterColumn<decimal>(
|
||||
name: "Price",
|
||||
table: "Trades",
|
||||
type: "decimal(10,6)",
|
||||
precision: 10,
|
||||
scale: 6,
|
||||
nullable: false,
|
||||
oldClrType: typeof(decimal),
|
||||
oldType: "decimal(18,6)",
|
||||
oldPrecision: 18,
|
||||
oldScale: 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -143,8 +143,8 @@ namespace Predictalytics.Infrastructure.Migrations
|
||||
|
||||
b.Property<string>("Category")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)");
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("varchar(64)");
|
||||
|
||||
b.Property<string>("ConditionId")
|
||||
.IsRequired()
|
||||
@@ -211,6 +211,11 @@ namespace Predictalytics.Infrastructure.Migrations
|
||||
b.Property<DateTime?>("StartDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Subcategory")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)");
|
||||
|
||||
b.Property<decimal>("Volume")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
@@ -406,8 +411,8 @@ namespace Predictalytics.Infrastructure.Migrations
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<decimal>("Price")
|
||||
.HasPrecision(10, 6)
|
||||
.HasColumnType("decimal(10,6)");
|
||||
.HasPrecision(18, 6)
|
||||
.HasColumnType("decimal(18,6)");
|
||||
|
||||
b.Property<int>("Side")
|
||||
.HasColumnType("int");
|
||||
@@ -565,6 +570,44 @@ namespace Predictalytics.Infrastructure.Migrations
|
||||
b.ToTable("TraderAnalytics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderCategoryPerformance", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Category")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("varchar(64)");
|
||||
|
||||
b.Property<decimal>("TotalPnL")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<int>("TotalTrades")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("TotalVolume")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<int>("TraderId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("WinningTrades")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TraderId", "Category")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("TraderCategoryPerformances");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderPosition", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -776,6 +819,17 @@ namespace Predictalytics.Infrastructure.Migrations
|
||||
b.Navigation("Trader");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderCategoryPerformance", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||
.WithMany()
|
||||
.HasForeignKey("TraderId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Trader");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderPosition", b =>
|
||||
{
|
||||
b.HasOne("Predictalytics.Domain.Entities.MarketOutcome", "MarketOutcome")
|
||||
|
||||
@@ -246,6 +246,9 @@ public class LimitlessProvider : IPlatformProvider
|
||||
private Market MapLimitlessMarket(LimitlessMarketResponse raw)
|
||||
{
|
||||
var conditionId = raw.Address ?? raw.Slug ?? Guid.NewGuid().ToString();
|
||||
var firstCat = raw.Categories?.FirstOrDefault() ?? "";
|
||||
var catMap = Predictalytics.Infrastructure.Helpers.MarketCategoryMapper.Map(firstCat, string.Join(",", raw.Categories ?? []));
|
||||
|
||||
var market = new Market
|
||||
{
|
||||
Platform = PlatformType.Limitless,
|
||||
@@ -254,7 +257,8 @@ public class LimitlessProvider : IPlatformProvider
|
||||
MarketSlug = raw.Slug ?? "",
|
||||
Question = raw.Title ?? "",
|
||||
Description = raw.Description ?? "",
|
||||
Category = raw.Categories?.FirstOrDefault() ?? "",
|
||||
Category = catMap.Category,
|
||||
Subcategory = catMap.Subcategory,
|
||||
ImageUrl = raw.ImageUrl ?? "",
|
||||
Volume = decimal.TryParse(raw.VolumeFormatted?.Replace(" USDC", ""), out var vol) ? vol : 0,
|
||||
Liquidity = (decimal)(raw.Liquidity ?? 0),
|
||||
|
||||
@@ -279,6 +279,8 @@ public class PolymarketProvider : IPlatformProvider
|
||||
{
|
||||
long.TryParse(raw.Id, out var marketNumericId);
|
||||
|
||||
var catMap = Predictalytics.Infrastructure.Helpers.MarketCategoryMapper.Map(raw.Category ?? "", parentTags);
|
||||
|
||||
var market = new Market
|
||||
{
|
||||
Platform = PlatformType.Polymarket,
|
||||
@@ -289,7 +291,8 @@ public class PolymarketProvider : IPlatformProvider
|
||||
Description = raw.Description,
|
||||
ImageUrl = raw.Image,
|
||||
Question = raw.Question,
|
||||
Category = string.IsNullOrWhiteSpace(raw.Category) ? parentTags : raw.Category,
|
||||
Category = catMap.Category,
|
||||
Subcategory = catMap.Subcategory,
|
||||
Volume = (decimal)raw.Volume,
|
||||
Volume24h = (decimal)raw.Volume24hr,
|
||||
Liquidity = (decimal)raw.Liquidity,
|
||||
|
||||
@@ -225,6 +225,31 @@ public class PositionPnLEngine : IPositionPnLEngine
|
||||
trader.TotalPnl = overallPnl;
|
||||
trader.WinRate = winRateOverall;
|
||||
|
||||
// Calculate Category Performance
|
||||
var existingCatPerf = await _db.TraderCategoryPerformances
|
||||
.Where(tcp => tcp.TraderId == traderId)
|
||||
.ToDictionaryAsync(tcp => tcp.Category, ct);
|
||||
|
||||
var newCatPerf = CalculateCategoryPerformances(trades, tempPositions);
|
||||
|
||||
foreach (var kvp in newCatPerf)
|
||||
{
|
||||
if (existingCatPerf.TryGetValue(kvp.Key, out var existing))
|
||||
{
|
||||
existing.TotalVolume = kvp.Value.TotalVolume;
|
||||
existing.TotalPnL = kvp.Value.TotalPnL;
|
||||
existing.TotalTrades = kvp.Value.TotalTrades;
|
||||
existing.WinningTrades = kvp.Value.WinningTrades;
|
||||
_db.TraderCategoryPerformances.Update(existing);
|
||||
}
|
||||
else
|
||||
{
|
||||
var newEntity = kvp.Value;
|
||||
newEntity.TraderId = traderId;
|
||||
_db.TraderCategoryPerformances.Add(newEntity);
|
||||
}
|
||||
}
|
||||
|
||||
// Save changes to database
|
||||
await _db.SaveChangesAsync(ct);
|
||||
|
||||
@@ -322,4 +347,64 @@ public class PositionPnLEngine : IPositionPnLEngine
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Dictionary<MarketCategory, TraderCategoryPerformance> CalculateCategoryPerformances(
|
||||
List<Trade> trades,
|
||||
Dictionary<int, TraderPosition> finalPositions)
|
||||
{
|
||||
var result = new Dictionary<MarketCategory, TraderCategoryPerformance>();
|
||||
|
||||
var tradesByMarket = trades
|
||||
.Where(t => t.MarketOutcome?.Market != null)
|
||||
.GroupBy(t => t.MarketOutcome!.Market!);
|
||||
|
||||
foreach (var marketGroup in tradesByMarket)
|
||||
{
|
||||
var market = marketGroup.Key;
|
||||
var category = market.Category;
|
||||
|
||||
if (!result.TryGetValue(category, out var perf))
|
||||
{
|
||||
perf = new TraderCategoryPerformance { Category = category };
|
||||
result[category] = perf;
|
||||
}
|
||||
|
||||
// Add volume
|
||||
perf.TotalVolume += marketGroup.Sum(t => t.Amount);
|
||||
|
||||
// Determine if market is closed for this trader
|
||||
var outcomeIds = marketGroup
|
||||
.Where(t => t.MarketOutcomeId.HasValue)
|
||||
.Select(t => t.MarketOutcomeId!.Value)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
var isClosed = outcomeIds.All(oid => !finalPositions.TryGetValue(oid, out var pos) || pos.SharesHeld == 0);
|
||||
if (!isClosed && market.IsResolved)
|
||||
{
|
||||
isClosed = true;
|
||||
}
|
||||
|
||||
if (isClosed)
|
||||
{
|
||||
decimal marketPnl = 0;
|
||||
foreach (var oid in outcomeIds)
|
||||
{
|
||||
if (finalPositions.TryGetValue(oid, out var pos))
|
||||
{
|
||||
marketPnl += pos.RealizedPnl;
|
||||
}
|
||||
}
|
||||
|
||||
perf.TotalPnL += marketPnl;
|
||||
perf.TotalTrades += 1;
|
||||
if (marketPnl > 0)
|
||||
{
|
||||
perf.WinningTrades += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user