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
+
+