@
R3 Slice 1: EF-Core-Infrastruktur im Core + InitialCore-Migration - Pomelo.EntityFrameworkCore.MySql 8.0.3 + EF Core Design 8.0.11 (Core), NuGet-Allowlist erweitert - Configuration/DatabaseOptions + DatabaseServerVersion (MariaDB 11.8.6 gepinnt) - Persistence/Entities: CorePosition, CoreTrade, CoreBudget, CoreWorkerLog, CoreSetting - Persistence/Ef/CoreDbContext (core_-Tabellen) + Design-Time-Factory (env IBKRTRADER_MYSQL) - DependencyInjection/AddCorePersistence (AddDbContextFactory), in Program verdrahtet (Connection aus appsettings.Local.json) - EF-Migration InitialCore erzeugt (5 core_-Tabellen) - Consumer noch auf Dapper (folgt in Slice 2); Build + 30/30 Tests + smoke-ui gruen Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> @
This commit is contained in:
@@ -18,6 +18,12 @@
|
||||
<package pattern="Dapper" />
|
||||
<package pattern="HtmlAgilityPack" />
|
||||
<package pattern="Newtonsoft.Json" />
|
||||
<!-- EF Core / Pomelo (MySQL/MariaDB) -->
|
||||
<package pattern="Microsoft.EntityFrameworkCore.*" />
|
||||
<package pattern="Pomelo.*" />
|
||||
<package pattern="Humanizer.*" />
|
||||
<package pattern="Mono.TextTemplating" />
|
||||
<package pattern="dotnet-ef" />
|
||||
<!-- Test-Pakete -->
|
||||
<package pattern="xunit*" />
|
||||
<package pattern="NSubstitute" />
|
||||
|
||||
+11
-3
@@ -1,7 +1,9 @@
|
||||
using IBKRTrader.Core.AI;
|
||||
using IBKRTrader.Core.Budget;
|
||||
using IBKRTrader.Core.Configuration;
|
||||
using IBKRTrader.Core.Database;
|
||||
using IBKRTrader.Core.Database.Migrations;
|
||||
using IBKRTrader.Core.DependencyInjection;
|
||||
using IBKRTrader.Core.IBKR;
|
||||
using IBKRTrader.Core.Logging;
|
||||
using IBKRTrader.Core.Modularity;
|
||||
@@ -49,7 +51,7 @@ internal static class Program
|
||||
config.AddJsonFile("appsettings.Local.json", optional: true, reloadOnChange: false))
|
||||
.ConfigureServices((context, services) =>
|
||||
{
|
||||
RegisterCoreServices(services);
|
||||
RegisterCoreServices(services, context.Configuration);
|
||||
foreach (var module in modules)
|
||||
{
|
||||
services.AddSingleton(module);
|
||||
@@ -77,8 +79,14 @@ internal static class Program
|
||||
}
|
||||
|
||||
/// <summary>Registriert alle Core-Services im DI-Container.</summary>
|
||||
private static void RegisterCoreServices(IServiceCollection services)
|
||||
private static void RegisterCoreServices(IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
// EF-Core-Persistenz (Connection aus appsettings.Local.json).
|
||||
services.AddCorePersistence(new DatabaseOptions
|
||||
{
|
||||
MySqlConnectionString = configuration["Database:MySqlConnectionString"] ?? string.Empty
|
||||
});
|
||||
|
||||
// Settings zuerst laden (eine Quelle, als Singleton weitergereicht).
|
||||
var settingsService = new SettingsService();
|
||||
settingsService.Load();
|
||||
@@ -194,7 +202,7 @@ internal static class Program
|
||||
config.AddJsonFile("appsettings.Local.json", optional: true, reloadOnChange: false))
|
||||
.ConfigureServices((context, services) =>
|
||||
{
|
||||
RegisterCoreServices(services);
|
||||
RegisterCoreServices(services, context.Configuration);
|
||||
foreach (var module in modules)
|
||||
{
|
||||
services.AddSingleton(module);
|
||||
|
||||
@@ -92,12 +92,11 @@ IBKRTrader.App (WinExe, Root) – Generic Host + Shell (Launcher) + C
|
||||
App legt keine Tabellen zur Laufzeit an. Server: **MariaDB 11.8.6** (via `--db-version` bestätigt) →
|
||||
Pin `new MariaDbServerVersion(new Version(11, 8, 6))`. Verbindung aus `appsettings.Local.json`.
|
||||
- [x] `--db-version`-Diagnose (Serverversion für den EF-Pin)
|
||||
- [ ] `Configuration/DatabaseOptions` (aus appsettings.Local.json) + `DatabaseServerVersion`-Pin
|
||||
- [ ] `AddCorePersistence` + `CoreDbContext` + Entities + EF-Repos (core_position, core_trade_history, core_budget, core_worker_log, core_settings)
|
||||
- [ ] Consumer umstellen: `PortfolioService`, `BudgetService`, `TradeHistoryService`, `WorkerBase`-Log, IBKR
|
||||
- [ ] Modul-DbContext (ct_) im CongressTrading-Projekt + EF-Repo
|
||||
- [ ] EF-Migrationen erzeugen (`dotnet ef migrations add`); Dapper + manuelle Migrationen entfernen
|
||||
- Hinweis: nur build-verifizierbar (Unit-Tests laufen ohne DB); Schema-Anwendung erfolgt extern
|
||||
- [x] **Slice 1:** `Configuration/DatabaseOptions` + `DatabaseServerVersion`-Pin (MariaDB 11.8.6); `AddCorePersistence` (`AddDbContextFactory`) + `CoreDbContext` + Entities (core_position, core_trade_history, core_budget, core_worker_log, core_settings) + Design-Time-Factory; **EF-Migration `InitialCore` erzeugt**
|
||||
- [ ] **Slice 2:** Consumer umstellen: `PortfolioService`, `BudgetService`, `TradeHistoryService`, `WorkerBase`-Log
|
||||
- [ ] **Slice 3:** Modul-DbContext (ct_) im CongressTrading-Projekt + Umstellung
|
||||
- [ ] **Slice 4:** Dapper + `DatabaseService` + manuelle Migrationen (CoreMigrations/IBKRMigrations/CongressMigrations) entfernen; IBKR-Marktdaten auf EF
|
||||
- Hinweis: nur build-verifizierbar (Unit-Tests ohne DB); Schema-Anwendung extern via `dotnet ef database update` (env `IBKRTRADER_MYSQL`)
|
||||
|
||||
### R4 – Trading-Kern einфügen
|
||||
- [ ] Risk/Execution/Portfolio/Broker-Seam nach Core/Trading (aus Phase 3 portiert)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace IBKRTrader.Core.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Datenbank-Konfiguration, gebunden an die "Database"-Sektion in appsettings(.Local).json.
|
||||
/// </summary>
|
||||
public class DatabaseOptions
|
||||
{
|
||||
public const string SectionName = "Database";
|
||||
|
||||
/// <summary>MySQL/MariaDB-Connection-String (aus gitignorierter appsettings.Local.json).</summary>
|
||||
public string MySqlConnectionString { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fest gepinnte Ziel-Server-Version (MariaDB 11.8.6, via <c>--db-version</c> bestätigt). Bewusst
|
||||
/// gepinnt statt <c>ServerVersion.AutoDetect</c>: AutoDetect öffnet beim Bau der DbContext-Optionen
|
||||
/// eine blockierende DB-Verbindung – ist die DB langsam/nicht erreichbar, hängt der Start. Mit fester
|
||||
/// Version startet die App unabhängig von der DB, und Migrations-Scaffolding läuft ohne DB-Verbindung.
|
||||
/// </summary>
|
||||
public static class DatabaseServerVersion
|
||||
{
|
||||
public static ServerVersion Value => new MariaDbServerVersion(new Version(11, 8, 6));
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using IBKRTrader.Core.Configuration;
|
||||
using IBKRTrader.Core.Persistence.Ef;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace IBKRTrader.Core.DependencyInjection;
|
||||
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Registriert die Core-Persistenzschicht (EF Core / Pomelo / MariaDB) über einen
|
||||
/// DbContextFactory (thread-safe, kurzlebiger Context je Operation).
|
||||
/// </summary>
|
||||
public static IServiceCollection AddCorePersistence(this IServiceCollection services, DatabaseOptions options)
|
||||
{
|
||||
services.AddDbContextFactory<CoreDbContext>(o =>
|
||||
o.UseMySql(options.MySqlConnectionString, DatabaseServerVersion.Value));
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,12 @@
|
||||
<PackageReference Include="Dapper" Version="2.1.35" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.4" />
|
||||
<!-- EF Core / Pomelo (MariaDB 11.8.6). EF 8 laeuft auf net10. -->
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.11">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Erlaubt dem Testprojekt, interne Helfer zu testen. -->
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
using IBKRTrader.Core.Persistence.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace IBKRTrader.Core.Persistence.Ef;
|
||||
|
||||
/// <summary>
|
||||
/// EF-Core-Kontext für die Core-Entitäten (core_-Tabellen). Modul-Entitäten liegen in eigenen
|
||||
/// Modul-DbContexts (gleiche MariaDB, andere Tabellen mit Modul-Präfix).
|
||||
/// </summary>
|
||||
public class CoreDbContext : DbContext
|
||||
{
|
||||
public CoreDbContext(DbContextOptions<CoreDbContext> options) : base(options) { }
|
||||
|
||||
public DbSet<CorePosition> Positions => Set<CorePosition>();
|
||||
public DbSet<CoreTrade> TradeHistory => Set<CoreTrade>();
|
||||
public DbSet<CoreBudget> Budgets => Set<CoreBudget>();
|
||||
public DbSet<CoreWorkerLog> WorkerLog => Set<CoreWorkerLog>();
|
||||
public DbSet<CoreSetting> Settings => Set<CoreSetting>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder b)
|
||||
{
|
||||
b.Entity<CorePosition>(e =>
|
||||
{
|
||||
e.ToTable("core_position");
|
||||
e.HasKey(x => new { x.Module, x.Symbol });
|
||||
e.Property(x => x.Module).HasMaxLength(50);
|
||||
e.Property(x => x.Symbol).HasMaxLength(20);
|
||||
e.Property(x => x.AvgPrice).HasPrecision(18, 4);
|
||||
});
|
||||
|
||||
b.Entity<CoreTrade>(e =>
|
||||
{
|
||||
e.ToTable("core_trade_history");
|
||||
e.HasKey(x => x.Id);
|
||||
e.Property(x => x.Module).HasMaxLength(50);
|
||||
e.Property(x => x.Symbol).HasMaxLength(20);
|
||||
e.Property(x => x.Action).HasMaxLength(10);
|
||||
e.Property(x => x.IbkrOrderId).HasMaxLength(100);
|
||||
e.Property(x => x.Status).HasMaxLength(50);
|
||||
e.Property(x => x.Quantity).HasPrecision(18, 4);
|
||||
e.Property(x => x.Price).HasPrecision(18, 4);
|
||||
e.Property(x => x.TotalValue).HasPrecision(18, 4);
|
||||
e.HasIndex(x => x.Symbol);
|
||||
e.HasIndex(x => x.Module);
|
||||
});
|
||||
|
||||
b.Entity<CoreBudget>(e =>
|
||||
{
|
||||
e.ToTable("core_budget");
|
||||
e.HasKey(x => x.Module);
|
||||
e.Property(x => x.Module).HasMaxLength(50);
|
||||
e.Property(x => x.TotalBudget).HasPrecision(18, 2);
|
||||
e.Property(x => x.UsedBudget).HasPrecision(18, 2);
|
||||
e.Property(x => x.MaxPerTrade).HasPrecision(18, 2);
|
||||
});
|
||||
|
||||
b.Entity<CoreWorkerLog>(e =>
|
||||
{
|
||||
e.ToTable("core_worker_log");
|
||||
e.HasKey(x => x.Id);
|
||||
e.Property(x => x.WorkerName).HasMaxLength(100);
|
||||
e.Property(x => x.Module).HasMaxLength(50);
|
||||
e.Property(x => x.Status).HasMaxLength(20);
|
||||
e.HasIndex(x => new { x.WorkerName, x.StartedAt });
|
||||
});
|
||||
|
||||
b.Entity<CoreSetting>(e =>
|
||||
{
|
||||
e.ToTable("core_settings");
|
||||
e.HasKey(x => x.Key);
|
||||
e.Property(x => x.Key).HasMaxLength(100);
|
||||
e.Property(x => x.Value).HasColumnType("text");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using IBKRTrader.Core.Configuration;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
|
||||
namespace IBKRTrader.Core.Persistence.Ef;
|
||||
|
||||
/// <summary>
|
||||
/// Design-Time-Factory für EF-Tooling (dotnet ef migrations/database). Liest den Connection-String
|
||||
/// aus der Umgebungsvariable IBKRTRADER_MYSQL, damit keine Zugangsdaten im Repo landen. Nutzt die
|
||||
/// fest gepinnte Server-Version, sodass Migrations-Scaffolding OHNE DB-Verbindung funktioniert.
|
||||
/// </summary>
|
||||
public class CoreDbContextFactory : IDesignTimeDbContextFactory<CoreDbContext>
|
||||
{
|
||||
public CoreDbContext CreateDbContext(string[] args)
|
||||
{
|
||||
var conn = Environment.GetEnvironmentVariable("IBKRTRADER_MYSQL")
|
||||
?? "Server=localhost;Port=3306;Database=ibkrtrader;User ID=root;Password=;";
|
||||
|
||||
var options = new DbContextOptionsBuilder<CoreDbContext>()
|
||||
.UseMySql(conn, DatabaseServerVersion.Value)
|
||||
.Options;
|
||||
|
||||
return new CoreDbContext(options);
|
||||
}
|
||||
}
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using IBKRTrader.Core.Persistence.Ef;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace IBKRTrader.Core.Persistence.Ef.Migrations
|
||||
{
|
||||
[DbContext(typeof(CoreDbContext))]
|
||||
[Migration("20260728083916_InitialCore")]
|
||||
partial class InitialCore
|
||||
{
|
||||
/// <inheritdoc />
|
||||
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("IBKRTrader.Core.Persistence.Entities.CoreBudget", b =>
|
||||
{
|
||||
b.Property<string>("Module")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)");
|
||||
|
||||
b.Property<decimal>("MaxPerTrade")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.Property<decimal>("TotalBudget")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<decimal>("UsedBudget")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.HasKey("Module");
|
||||
|
||||
b.ToTable("core_budget", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IBKRTrader.Core.Persistence.Entities.CorePosition", b =>
|
||||
{
|
||||
b.Property<string>("Module")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)");
|
||||
|
||||
b.Property<string>("Symbol")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("varchar(20)");
|
||||
|
||||
b.Property<decimal>("AvgPrice")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Module", "Symbol");
|
||||
|
||||
b.ToTable("core_position", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IBKRTrader.Core.Persistence.Entities.CoreSetting", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("core_settings", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IBKRTrader.Core.Persistence.Entities.CoreTrade", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<string>("Action")
|
||||
.IsRequired()
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("varchar(10)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("IbkrOrderId")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.Property<string>("Module")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<decimal>("Price")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<decimal>("Quantity")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)");
|
||||
|
||||
b.Property<string>("Symbol")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("varchar(20)");
|
||||
|
||||
b.Property<decimal>("TotalValue")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<DateTime>("TradedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Module");
|
||||
|
||||
b.HasIndex("Symbol");
|
||||
|
||||
b.ToTable("core_trade_history", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IBKRTrader.Core.Persistence.Entities.CoreWorkerLog", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime?>("FinishedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Module")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)");
|
||||
|
||||
b.Property<DateTime?>("StartedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("varchar(20)");
|
||||
|
||||
b.Property<string>("WorkerName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("WorkerName", "StartedAt");
|
||||
|
||||
b.ToTable("core_worker_log", (string)null);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace IBKRTrader.Core.Persistence.Ef.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class InitialCore : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterDatabase()
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "core_budget",
|
||||
columns: table => new
|
||||
{
|
||||
Module = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
TotalBudget = table.Column<decimal>(type: "decimal(18,2)", precision: 18, scale: 2, nullable: false),
|
||||
UsedBudget = table.Column<decimal>(type: "decimal(18,2)", precision: 18, scale: 2, nullable: false),
|
||||
MaxPerTrade = table.Column<decimal>(type: "decimal(18,2)", precision: 18, scale: 2, nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_core_budget", x => x.Module);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "core_position",
|
||||
columns: table => new
|
||||
{
|
||||
Module = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Symbol = table.Column<string>(type: "varchar(20)", maxLength: 20, nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Quantity = table.Column<int>(type: "int", nullable: false),
|
||||
AvgPrice = table.Column<decimal>(type: "decimal(18,4)", precision: 18, scale: 4, nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_core_position", x => new { x.Module, x.Symbol });
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "core_settings",
|
||||
columns: table => new
|
||||
{
|
||||
Key = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Value = table.Column<string>(type: "text", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_core_settings", x => x.Key);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "core_trade_history",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
Module = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Symbol = table.Column<string>(type: "varchar(20)", maxLength: 20, nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Action = table.Column<string>(type: "varchar(10)", maxLength: 10, nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Quantity = table.Column<decimal>(type: "decimal(18,4)", precision: 18, scale: 4, nullable: false),
|
||||
Price = table.Column<decimal>(type: "decimal(18,4)", precision: 18, scale: 4, nullable: false),
|
||||
TotalValue = table.Column<decimal>(type: "decimal(18,4)", precision: 18, scale: 4, nullable: false),
|
||||
TradedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
IbkrOrderId = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Status = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Notes = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_core_trade_history", x => x.Id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "core_worker_log",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
WorkerName = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Module = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
StartedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
|
||||
FinishedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
|
||||
Status = table.Column<string>(type: "varchar(20)", maxLength: 20, nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Message = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_core_worker_log", x => x.Id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_core_trade_history_Module",
|
||||
table: "core_trade_history",
|
||||
column: "Module");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_core_trade_history_Symbol",
|
||||
table: "core_trade_history",
|
||||
column: "Symbol");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_core_worker_log_WorkerName_StartedAt",
|
||||
table: "core_worker_log",
|
||||
columns: new[] { "WorkerName", "StartedAt" });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "core_budget");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "core_position");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "core_settings");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "core_trade_history");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "core_worker_log");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using IBKRTrader.Core.Persistence.Ef;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace IBKRTrader.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("IBKRTrader.Core.Persistence.Entities.CoreBudget", b =>
|
||||
{
|
||||
b.Property<string>("Module")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)");
|
||||
|
||||
b.Property<decimal>("MaxPerTrade")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.Property<decimal>("TotalBudget")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<decimal>("UsedBudget")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.HasKey("Module");
|
||||
|
||||
b.ToTable("core_budget", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IBKRTrader.Core.Persistence.Entities.CorePosition", b =>
|
||||
{
|
||||
b.Property<string>("Module")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)");
|
||||
|
||||
b.Property<string>("Symbol")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("varchar(20)");
|
||||
|
||||
b.Property<decimal>("AvgPrice")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Module", "Symbol");
|
||||
|
||||
b.ToTable("core_position", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IBKRTrader.Core.Persistence.Entities.CoreSetting", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("core_settings", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IBKRTrader.Core.Persistence.Entities.CoreTrade", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<string>("Action")
|
||||
.IsRequired()
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("varchar(10)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("IbkrOrderId")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.Property<string>("Module")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<decimal>("Price")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<decimal>("Quantity")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)");
|
||||
|
||||
b.Property<string>("Symbol")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("varchar(20)");
|
||||
|
||||
b.Property<decimal>("TotalValue")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("decimal(18,4)");
|
||||
|
||||
b.Property<DateTime>("TradedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Module");
|
||||
|
||||
b.HasIndex("Symbol");
|
||||
|
||||
b.ToTable("core_trade_history", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IBKRTrader.Core.Persistence.Entities.CoreWorkerLog", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime?>("FinishedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Module")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)");
|
||||
|
||||
b.Property<DateTime?>("StartedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("varchar(20)");
|
||||
|
||||
b.Property<string>("WorkerName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("WorkerName", "StartedAt");
|
||||
|
||||
b.ToTable("core_worker_log", (string)null);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
namespace IBKRTrader.Core.Persistence.Entities;
|
||||
|
||||
/// <summary>Offene Position eines Moduls (Tabelle core_position, PK Module+Symbol).</summary>
|
||||
public class CorePosition
|
||||
{
|
||||
public string Module { get; set; } = "";
|
||||
public string Symbol { get; set; } = "";
|
||||
public int Quantity { get; set; }
|
||||
public decimal AvgPrice { get; set; }
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Trade-Historie (Tabelle core_trade_history).</summary>
|
||||
public class CoreTrade
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string Module { get; set; } = "";
|
||||
public string Symbol { get; set; } = "";
|
||||
public string Action { get; set; } = ""; // BUY / SELL
|
||||
public decimal Quantity { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
public decimal TotalValue { get; set; }
|
||||
public DateTime TradedAt { get; set; }
|
||||
public string? IbkrOrderId { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Budget je Modul (Tabelle core_budget, PK Module).</summary>
|
||||
public class CoreBudget
|
||||
{
|
||||
public string Module { get; set; } = "";
|
||||
public decimal TotalBudget { get; set; }
|
||||
public decimal UsedBudget { get; set; }
|
||||
public decimal MaxPerTrade { get; set; }
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Worker-Lauf-Protokoll (Tabelle core_worker_log).</summary>
|
||||
public class CoreWorkerLog
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string WorkerName { get; set; } = "";
|
||||
public string Module { get; set; } = "Core";
|
||||
public DateTime? StartedAt { get; set; }
|
||||
public DateTime? FinishedAt { get; set; }
|
||||
public string Status { get; set; } = "Running";
|
||||
public string? Message { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Key-Value-Einstellungen (Tabelle core_settings, PK Key).</summary>
|
||||
public class CoreSetting
|
||||
{
|
||||
public string Key { get; set; } = "";
|
||||
public string? Value { get; set; }
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user