Phase 3: Trading-Kern (Risk, Execution, Portfolio) mit sicherem Broker-Default - Core/Trading/TradingModels: Signal, Order(Request/Result), RiskContext/Decision, Account, Position, Quote, ExecutionResult, Enums (Side/OrderType/Mode) - IBrokerClient + NullBrokerClient (sicherer Default, handelt NIE bis IBKR-Adapter verifiziert) - RiskService (+IRiskService): Sizing nach MaxTrade%, Modul-Limit, Slippage; Buy/Sell - PortfolioService (+IPortfolioService): core_position + core_trade_history + core_budget - ExecutionService (+IExecutionService): Signal -> Kurs -> Konto -> Risiko -> Order -> Buchung - TradingSettings in AppSettings (Paper/Live, TradingEnabled, Risikoparameter) - CoreMigrations: core_position; DI-Registrierung der Trading-Services - Tests: RiskService (11) + ExecutionService (6, NSubstitute) -> 38/38 gruen Offen (bewusst gekapselt): echter IbkrBrokerClient gegen Client-Portal-Gateway (manuell verifizieren). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> @
92 lines
3.9 KiB
C#
92 lines
3.9 KiB
C#
using IBKRTrader.Core.Logging;
|
|
|
|
namespace IBKRTrader.Core.Database.Migrations;
|
|
|
|
/// <summary>
|
|
/// Erstellt alle core_xxx-Tabellen idempotent (IF NOT EXISTS).
|
|
/// Wird einmalig beim App-Start ausgeführt.
|
|
/// </summary>
|
|
public class CoreMigrations
|
|
{
|
|
private readonly DatabaseService _db;
|
|
private readonly LoggingService _logger;
|
|
|
|
public CoreMigrations(DatabaseService db, LoggingService logger)
|
|
{
|
|
_db = db;
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task RunAsync()
|
|
{
|
|
_logger.Info("Core", "Starte Core-Datenbankmigrationen...");
|
|
await CreateCoreSettingsAsync();
|
|
await CreateCoreWorkerLogAsync();
|
|
await CreateCoreTradeHistoryAsync();
|
|
await CreateCoreBudgetAsync();
|
|
await CreateCorePositionAsync();
|
|
_logger.Info("Core", "Core-Migrationen abgeschlossen.");
|
|
}
|
|
|
|
// ─── Tabellen ─────────────────────────────────────────────────────────────
|
|
|
|
private Task CreateCoreSettingsAsync() => _db.ExecuteAsync(@"
|
|
CREATE TABLE IF NOT EXISTS `core_settings` (
|
|
`key` VARCHAR(100) NOT NULL PRIMARY KEY,
|
|
`value` TEXT,
|
|
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
ON UPDATE CURRENT_TIMESTAMP
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
|
|
|
|
private Task CreateCoreWorkerLogAsync() => _db.ExecuteAsync(@"
|
|
CREATE TABLE IF NOT EXISTS `core_worker_log` (
|
|
`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
`worker_name` VARCHAR(100) NOT NULL,
|
|
`module` VARCHAR(50) NOT NULL DEFAULT 'Core',
|
|
`started_at` DATETIME,
|
|
`finished_at` DATETIME,
|
|
`status` ENUM('Running','Success','Error') DEFAULT 'Running',
|
|
`message` TEXT,
|
|
INDEX `idx_worker` (`worker_name`, `started_at`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
|
|
|
|
private Task CreateCoreTradeHistoryAsync() => _db.ExecuteAsync(@"
|
|
CREATE TABLE IF NOT EXISTS `core_trade_history` (
|
|
`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
`module` VARCHAR(50) NOT NULL,
|
|
`symbol` VARCHAR(20) NOT NULL,
|
|
`action` ENUM('BUY','SELL') NOT NULL,
|
|
`quantity` DECIMAL(18,4),
|
|
`price` DECIMAL(18,4),
|
|
`total_value` DECIMAL(18,4),
|
|
`traded_at` DATETIME,
|
|
`ibkr_order_id` VARCHAR(100),
|
|
`status` VARCHAR(50),
|
|
`notes` TEXT,
|
|
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
INDEX `idx_symbol` (`symbol`),
|
|
INDEX `idx_module` (`module`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
|
|
|
|
private Task CreateCoreBudgetAsync() => _db.ExecuteAsync(@"
|
|
CREATE TABLE IF NOT EXISTS `core_budget` (
|
|
`module` VARCHAR(50) NOT NULL PRIMARY KEY,
|
|
`total_budget` DECIMAL(18,2) DEFAULT 0,
|
|
`used_budget` DECIMAL(18,2) DEFAULT 0,
|
|
`max_per_trade` DECIMAL(18,2) DEFAULT 0,
|
|
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
ON UPDATE CURRENT_TIMESTAMP
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
|
|
|
|
private Task CreateCorePositionAsync() => _db.ExecuteAsync(@"
|
|
CREATE TABLE IF NOT EXISTS `core_position` (
|
|
`module` VARCHAR(50) NOT NULL,
|
|
`symbol` VARCHAR(20) NOT NULL,
|
|
`quantity` INT NOT NULL DEFAULT 0,
|
|
`avg_price` DECIMAL(18,4) NOT NULL DEFAULT 0,
|
|
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
ON UPDATE CURRENT_TIMESTAMP,
|
|
PRIMARY KEY (`module`, `symbol`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
|
|
}
|