diff --git a/PolyTrader.App.csproj b/PolyTrader.App.csproj index 7de9eeb..28f3660 100644 --- a/PolyTrader.App.csproj +++ b/PolyTrader.App.csproj @@ -65,6 +65,8 @@ + + diff --git a/src/PolyTrader.Core/Configuration/DatabaseOptions.cs b/src/PolyTrader.Core/Configuration/DatabaseOptions.cs index 5bb754f..3f73504 100644 --- a/src/PolyTrader.Core/Configuration/DatabaseOptions.cs +++ b/src/PolyTrader.Core/Configuration/DatabaseOptions.cs @@ -8,6 +8,13 @@ namespace PolyTrader.Core.Configuration { public const string SectionName = "Database"; + /// "MySql" (Zielzustand) oder "Mongo" (Übergang/Config-Migration). + public string Provider { get; set; } = "MySql"; + + /// MySQL-Connection-String (aus gitignorierter appsettings.Local.json). + public string MySqlConnectionString { get; set; } = string.Empty; + + // --- MongoDB (nur noch für die einmalige Config-Migration nach MySQL) --- public string ConnectionString { get; set; } = "mongodb://localhost:27017"; public string DatabaseName { get; set; } = "PolyTraderDB"; diff --git a/src/PolyTrader.Core/Models/Position.cs b/src/PolyTrader.Core/Models/Position.cs index fdc8e12..0bd4b5d 100644 --- a/src/PolyTrader.Core/Models/Position.cs +++ b/src/PolyTrader.Core/Models/Position.cs @@ -2,6 +2,11 @@ namespace PolyTraderSharp.Models { public class Position { + // Für MySQL: eine positions-Tabelle mit account_id + is_demo (in Mongo lag das + // im Collection-Namen open_positions_{id} / demo_positions_{id}). + public int AccountId { get; set; } + public bool IsDemo { get; set; } + [MongoDB.Bson.Serialization.Attributes.BsonId] public string TokenId { get; set; } = string.Empty; public string MarketSlug { get; set; } = string.Empty; diff --git a/src/PolyTrader.Core/Persistence/Ef/CoreDbContext.cs b/src/PolyTrader.Core/Persistence/Ef/CoreDbContext.cs new file mode 100644 index 0000000..13f0909 --- /dev/null +++ b/src/PolyTrader.Core/Persistence/Ef/CoreDbContext.cs @@ -0,0 +1,94 @@ +using Microsoft.EntityFrameworkCore; +using PolyTraderSharp.Models; + +namespace PolyTrader.Core.Persistence.Ef +{ + /// + /// EF-Core-Kontext für die Core-Entitäten (eigene Trading-Accounts, Positionen, + /// Markt-Cache, generischer Trade-Log). Modul-Entitäten liegen im + /// CopyTradingDbContext des Moduls (gleiche MySQL-DB, andere Tabellen). + /// + public class CoreDbContext : DbContext + { + public CoreDbContext(DbContextOptions options) : base(options) { } + + public DbSet Accounts => Set(); + public DbSet Positions => Set(); + public DbSet Markets => Set(); + public DbSet TradeLog => Set(); + + protected override void OnModelCreating(ModelBuilder b) + { + b.Entity(e => + { + e.ToTable("accounts"); + e.HasKey(x => x.AccountId); + e.Property(x => x.AccountId).ValueGeneratedNever(); + e.Ignore(x => x.OpenPositions); // Runtime-Only (In-Memory Hot-Path) + e.Property(x => x.Name).HasMaxLength(200); + e.Property(x => x.WalletAddress).HasMaxLength(128); + e.Property(x => x.ApiKey).HasMaxLength(256); + e.Property(x => x.ApiSecret).HasMaxLength(256); + e.Property(x => x.ApiPassphrase).HasMaxLength(256); + e.Property(x => x.PrivateKey).HasMaxLength(256); + e.Property(x => x.PayoutAddress).HasMaxLength(128); + e.Property(x => x.TotalBalance).HasPrecision(18, 6); + e.Property(x => x.AvailableBalance).HasPrecision(18, 6); + e.Property(x => x.PayoutLimitUsd).HasPrecision(18, 6); + }); + + b.Entity(e => + { + e.ToTable("positions"); + e.HasKey(x => new { x.AccountId, x.IsDemo, x.TokenId }); + e.Property(x => x.TokenId).HasMaxLength(120); + e.Property(x => x.MarketSlug).HasMaxLength(300); + e.Property(x => x.ConditionId).HasMaxLength(120); + e.Property(x => x.SourceTraderName).HasMaxLength(200); + e.Property(x => x.SourceTraderAddress).HasMaxLength(128); + e.Property(x => x.MarketQuestion).HasMaxLength(1000); + e.Property(x => x.Outcome).HasMaxLength(200); + e.Property(x => x.Side).HasMaxLength(10); + e.Property(x => x.EntryPrice).HasPrecision(18, 6); + e.Property(x => x.Size).HasPrecision(18, 6); + e.Property(x => x.AmountUsd).HasPrecision(18, 6); + e.Property(x => x.CurrentPrice).HasPrecision(18, 6); + e.Property(x => x.CurrentValueUsd).HasPrecision(18, 6); + }); + + b.Entity(e => + { + e.ToTable("markets"); + e.HasKey(x => x.Id); + e.Property(x => x.Id).HasMaxLength(120); + e.Property(x => x.ConditionId).HasMaxLength(120); + e.Property(x => x.Slug).HasMaxLength(300); + e.Property(x => x.Category).HasMaxLength(200); + e.Property(x => x.Question).HasMaxLength(1000); + e.Property(x => x.ClobTokenIds).HasColumnType("text"); + e.Property(x => x.Outcomes).HasColumnType("text"); + }); + + b.Entity(e => + { + e.ToTable("trade_log"); + e.HasKey(x => x.Id); + e.Property(x => x.Id).HasMaxLength(64); + e.Property(x => x.ModuleName).HasMaxLength(64); + e.Property(x => x.TokenId).HasMaxLength(120); + e.Property(x => x.MarketQuestion).HasMaxLength(1000); + e.Property(x => x.Outcome).HasMaxLength(200); + e.Property(x => x.Side).HasMaxLength(10); + e.Property(x => x.ExitReason).HasMaxLength(200); + e.Property(x => x.EntryPrice).HasPrecision(18, 6); + e.Property(x => x.ExitPrice).HasPrecision(18, 6); + e.Property(x => x.Size).HasPrecision(18, 6); + e.Property(x => x.RealizedPnl).HasPrecision(18, 6); + e.Property(x => x.PnlPercent).HasPrecision(18, 6); + e.HasIndex(x => x.ClosedAt); + e.HasIndex(x => x.ModuleName); + e.HasIndex(x => x.AccountId); + }); + } + } +} diff --git a/src/PolyTrader.Core/Persistence/Ef/CoreDbContextFactory.cs b/src/PolyTrader.Core/Persistence/Ef/CoreDbContextFactory.cs new file mode 100644 index 0000000..995fa13 --- /dev/null +++ b/src/PolyTrader.Core/Persistence/Ef/CoreDbContextFactory.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; + +namespace PolyTrader.Core.Persistence.Ef +{ + /// + /// Design-Time-Factory für EF-Tooling (dotnet ef migrations/database). + /// Liest den Connection-String aus der Umgebungsvariable POLYTRADER_MYSQL, + /// damit keine Zugangsdaten im Code/Repo landen. + /// + public class CoreDbContextFactory : IDesignTimeDbContextFactory + { + public CoreDbContext CreateDbContext(string[] args) + { + var conn = Environment.GetEnvironmentVariable("POLYTRADER_MYSQL") + ?? "Server=localhost;Port=3306;Database=polytrader;User ID=root;Password=;"; + + var options = new DbContextOptionsBuilder() + .UseMySql(conn, ServerVersion.AutoDetect(conn)) + .Options; + + return new CoreDbContext(options); + } + } +} diff --git a/src/PolyTrader.Core/Persistence/Ef/Migrations/20260705112545_InitialCore.Designer.cs b/src/PolyTrader.Core/Persistence/Ef/Migrations/20260705112545_InitialCore.Designer.cs new file mode 100644 index 0000000..7ad0bac --- /dev/null +++ b/src/PolyTrader.Core/Persistence/Ef/Migrations/20260705112545_InitialCore.Designer.cs @@ -0,0 +1,310 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PolyTrader.Core.Persistence.Ef; + +#nullable disable + +namespace PolyTrader.Core.Persistence.Ef.Migrations +{ + [DbContext(typeof(CoreDbContext))] + [Migration("20260705112545_InitialCore")] + partial class InitialCore + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.13") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("PolyTraderSharp.Models.AccountState", b => + { + b.Property("AccountId") + .HasColumnType("int"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ApiPassphrase") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ApiSecret") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("AvailableBalance") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("CloseOnlyMode") + .HasColumnType("tinyint(1)"); + + b.Property("HasOpenLimitOrders") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("IsDemo") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PayoutAddress") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("PayoutLimitUsd") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("PrivateKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("TotalBalance") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("WalletAddress") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.HasKey("AccountId"); + + b.ToTable("accounts", (string)null); + }); + + modelBuilder.Entity("PolyTraderSharp.Models.MarketData", b => + { + b.Property("Id") + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ClobTokenIds") + .IsRequired() + .HasColumnType("text"); + + b.Property("Closed") + .HasColumnType("tinyint(1)"); + + b.Property("ConditionId") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("EndDate") + .HasColumnType("datetime(6)"); + + b.Property("NegRisk") + .HasColumnType("tinyint(1)"); + + b.Property("Outcomes") + .IsRequired() + .HasColumnType("text"); + + b.Property("Question") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.HasKey("Id"); + + b.ToTable("markets", (string)null); + }); + + modelBuilder.Entity("PolyTraderSharp.Models.Position", b => + { + b.Property("AccountId") + .HasColumnType("int"); + + b.Property("IsDemo") + .HasColumnType("tinyint(1)"); + + b.Property("TokenId") + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("AmountUsd") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("ConditionId") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("CurrentPrice") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("CurrentValueUsd") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("EntryPrice") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("ExpiryDate") + .HasColumnType("datetime(6)"); + + b.Property("MarketQuestion") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("MarketSlug") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("OpenedAt") + .HasColumnType("datetime(6)"); + + b.Property("Outcome") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Side") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Size") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("SourceTraderAddress") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("SourceTraderId") + .HasColumnType("int"); + + b.Property("SourceTraderName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("AccountId", "IsDemo", "TokenId"); + + b.ToTable("positions", (string)null); + }); + + modelBuilder.Entity("PolyTraderSharp.Models.TradeRecord", b => + { + b.Property("Id") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("AccountId") + .HasColumnType("int"); + + b.Property("ClosedAt") + .HasColumnType("datetime(6)"); + + b.Property("EntryPrice") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("ExitPrice") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("ExitReason") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("IsDemo") + .HasColumnType("tinyint(1)"); + + b.Property("MarketQuestion") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("ModuleName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("OpenedAt") + .HasColumnType("datetime(6)"); + + b.Property("Outcome") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PnlPercent") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("RealizedPnl") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Side") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Size") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("TokenId") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("ClosedAt"); + + b.HasIndex("ModuleName"); + + b.ToTable("trade_log", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/PolyTrader.Core/Persistence/Ef/Migrations/20260705112545_InitialCore.cs b/src/PolyTrader.Core/Persistence/Ef/Migrations/20260705112545_InitialCore.cs new file mode 100644 index 0000000..f6df15b --- /dev/null +++ b/src/PolyTrader.Core/Persistence/Ef/Migrations/20260705112545_InitialCore.cs @@ -0,0 +1,182 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PolyTrader.Core.Persistence.Ef.Migrations +{ + /// + public partial class InitialCore : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "accounts", + columns: table => new + { + AccountId = table.Column(type: "int", nullable: false), + Name = table.Column(type: "varchar(200)", maxLength: 200, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + WalletAddress = table.Column(type: "varchar(128)", maxLength: 128, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ApiKey = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ApiSecret = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ApiPassphrase = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + PrivateKey = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDemo = table.Column(type: "tinyint(1)", nullable: false), + IsActive = table.Column(type: "tinyint(1)", nullable: false), + CloseOnlyMode = table.Column(type: "tinyint(1)", nullable: false), + PayoutAddress = table.Column(type: "varchar(128)", maxLength: 128, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + PayoutLimitUsd = table.Column(type: "decimal(18,6)", precision: 18, scale: 6, nullable: false), + TotalBalance = table.Column(type: "decimal(18,6)", precision: 18, scale: 6, nullable: false), + AvailableBalance = table.Column(type: "decimal(18,6)", precision: 18, scale: 6, nullable: false), + HasOpenLimitOrders = table.Column(type: "tinyint(1)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_accounts", x => x.AccountId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "markets", + columns: table => new + { + Id = table.Column(type: "varchar(120)", maxLength: 120, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ConditionId = table.Column(type: "varchar(120)", maxLength: 120, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Question = table.Column(type: "varchar(1000)", maxLength: 1000, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Slug = table.Column(type: "varchar(300)", maxLength: 300, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EndDate = table.Column(type: "datetime(6)", nullable: true), + Active = table.Column(type: "tinyint(1)", nullable: false), + Closed = table.Column(type: "tinyint(1)", nullable: false), + Category = table.Column(type: "varchar(200)", maxLength: 200, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClobTokenIds = table.Column(type: "text", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Outcomes = table.Column(type: "text", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + NegRisk = table.Column(type: "tinyint(1)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_markets", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "positions", + columns: table => new + { + AccountId = table.Column(type: "int", nullable: false), + IsDemo = table.Column(type: "tinyint(1)", nullable: false), + TokenId = table.Column(type: "varchar(120)", maxLength: 120, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + MarketSlug = table.Column(type: "varchar(300)", maxLength: 300, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ConditionId = table.Column(type: "varchar(120)", maxLength: 120, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + SourceTraderId = table.Column(type: "int", nullable: false), + SourceTraderName = table.Column(type: "varchar(200)", maxLength: 200, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + SourceTraderAddress = table.Column(type: "varchar(128)", maxLength: 128, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + MarketQuestion = table.Column(type: "varchar(1000)", maxLength: 1000, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Outcome = table.Column(type: "varchar(200)", maxLength: 200, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Side = table.Column(type: "varchar(10)", maxLength: 10, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntryPrice = table.Column(type: "decimal(18,6)", precision: 18, scale: 6, nullable: false), + Size = table.Column(type: "decimal(18,6)", precision: 18, scale: 6, nullable: false), + AmountUsd = table.Column(type: "decimal(18,6)", precision: 18, scale: 6, nullable: false), + CurrentPrice = table.Column(type: "decimal(18,6)", precision: 18, scale: 6, nullable: false), + CurrentValueUsd = table.Column(type: "decimal(18,6)", precision: 18, scale: 6, nullable: false), + ExpiryDate = table.Column(type: "datetime(6)", nullable: true), + OpenedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_positions", x => new { x.AccountId, x.IsDemo, x.TokenId }); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "trade_log", + columns: table => new + { + Id = table.Column(type: "varchar(64)", maxLength: 64, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ModuleName = table.Column(type: "varchar(64)", maxLength: 64, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + AccountId = table.Column(type: "int", nullable: false), + IsDemo = table.Column(type: "tinyint(1)", nullable: false), + TokenId = table.Column(type: "varchar(120)", maxLength: 120, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + MarketQuestion = table.Column(type: "varchar(1000)", maxLength: 1000, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Outcome = table.Column(type: "varchar(200)", maxLength: 200, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Side = table.Column(type: "varchar(10)", maxLength: 10, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntryPrice = table.Column(type: "decimal(18,6)", precision: 18, scale: 6, nullable: false), + ExitPrice = table.Column(type: "decimal(18,6)", precision: 18, scale: 6, nullable: false), + Size = table.Column(type: "decimal(18,6)", precision: 18, scale: 6, nullable: false), + RealizedPnl = table.Column(type: "decimal(18,6)", precision: 18, scale: 6, nullable: false), + PnlPercent = table.Column(type: "decimal(18,6)", precision: 18, scale: 6, nullable: false), + OpenedAt = table.Column(type: "datetime(6)", nullable: false), + ClosedAt = table.Column(type: "datetime(6)", nullable: false), + ExitReason = table.Column(type: "varchar(200)", maxLength: 200, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_trade_log", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_trade_log_AccountId", + table: "trade_log", + column: "AccountId"); + + migrationBuilder.CreateIndex( + name: "IX_trade_log_ClosedAt", + table: "trade_log", + column: "ClosedAt"); + + migrationBuilder.CreateIndex( + name: "IX_trade_log_ModuleName", + table: "trade_log", + column: "ModuleName"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "accounts"); + + migrationBuilder.DropTable( + name: "markets"); + + migrationBuilder.DropTable( + name: "positions"); + + migrationBuilder.DropTable( + name: "trade_log"); + } + } +} diff --git a/src/PolyTrader.Core/Persistence/Ef/Migrations/CoreDbContextModelSnapshot.cs b/src/PolyTrader.Core/Persistence/Ef/Migrations/CoreDbContextModelSnapshot.cs new file mode 100644 index 0000000..0b07bcd --- /dev/null +++ b/src/PolyTrader.Core/Persistence/Ef/Migrations/CoreDbContextModelSnapshot.cs @@ -0,0 +1,307 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PolyTrader.Core.Persistence.Ef; + +#nullable disable + +namespace PolyTrader.Core.Persistence.Ef.Migrations +{ + [DbContext(typeof(CoreDbContext))] + partial class CoreDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.13") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("PolyTraderSharp.Models.AccountState", b => + { + b.Property("AccountId") + .HasColumnType("int"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ApiPassphrase") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ApiSecret") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("AvailableBalance") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("CloseOnlyMode") + .HasColumnType("tinyint(1)"); + + b.Property("HasOpenLimitOrders") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("IsDemo") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PayoutAddress") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("PayoutLimitUsd") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("PrivateKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("TotalBalance") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("WalletAddress") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.HasKey("AccountId"); + + b.ToTable("accounts", (string)null); + }); + + modelBuilder.Entity("PolyTraderSharp.Models.MarketData", b => + { + b.Property("Id") + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ClobTokenIds") + .IsRequired() + .HasColumnType("text"); + + b.Property("Closed") + .HasColumnType("tinyint(1)"); + + b.Property("ConditionId") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("EndDate") + .HasColumnType("datetime(6)"); + + b.Property("NegRisk") + .HasColumnType("tinyint(1)"); + + b.Property("Outcomes") + .IsRequired() + .HasColumnType("text"); + + b.Property("Question") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.HasKey("Id"); + + b.ToTable("markets", (string)null); + }); + + modelBuilder.Entity("PolyTraderSharp.Models.Position", b => + { + b.Property("AccountId") + .HasColumnType("int"); + + b.Property("IsDemo") + .HasColumnType("tinyint(1)"); + + b.Property("TokenId") + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("AmountUsd") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("ConditionId") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("CurrentPrice") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("CurrentValueUsd") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("EntryPrice") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("ExpiryDate") + .HasColumnType("datetime(6)"); + + b.Property("MarketQuestion") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("MarketSlug") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("OpenedAt") + .HasColumnType("datetime(6)"); + + b.Property("Outcome") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Side") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Size") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("SourceTraderAddress") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("SourceTraderId") + .HasColumnType("int"); + + b.Property("SourceTraderName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("AccountId", "IsDemo", "TokenId"); + + b.ToTable("positions", (string)null); + }); + + modelBuilder.Entity("PolyTraderSharp.Models.TradeRecord", b => + { + b.Property("Id") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("AccountId") + .HasColumnType("int"); + + b.Property("ClosedAt") + .HasColumnType("datetime(6)"); + + b.Property("EntryPrice") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("ExitPrice") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("ExitReason") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("IsDemo") + .HasColumnType("tinyint(1)"); + + b.Property("MarketQuestion") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("ModuleName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("OpenedAt") + .HasColumnType("datetime(6)"); + + b.Property("Outcome") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("PnlPercent") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("RealizedPnl") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Side") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Size") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("TokenId") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("ClosedAt"); + + b.HasIndex("ModuleName"); + + b.ToTable("trade_log", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/PolyTrader.Core/PolyTrader.Core.csproj b/src/PolyTrader.Core/PolyTrader.Core.csproj index 404fb98..4845e9d 100644 --- a/src/PolyTrader.Core/PolyTrader.Core.csproj +++ b/src/PolyTrader.Core/PolyTrader.Core.csproj @@ -11,7 +11,12 @@ - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + +