Files
Finlytic/FinlyticTechnicals/Database/TechnicalAnalysisDbContext.cs
T

130 lines
5.3 KiB
C#

using System;
using System.Collections.Generic;
using System.Text.Json;
using FinlyticCore.Database;
using FinlyticCore.Dtos.TechnicalAnalysis;
using FinlyticCore.Entities.Settings;
using FinlyticTechnicals.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace FinlyticTechnicals.Database;
public class TechnicalAnalysisDbContext : DbContext, ISettingsDbContext
{
private static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = false
};
public TechnicalAnalysisDbContext(DbContextOptions<TechnicalAnalysisDbContext> options) : base(options)
{
}
public DbSet<SettingEntity> DynamicSettings => Set<SettingEntity>();
public DbSet<FtaCandleEntity> FtaCandles => Set<FtaCandleEntity>();
public DbSet<FtaTechnicalSetupEntity> FtaTechnicalSetups => Set<FtaTechnicalSetupEntity>();
public DbSet<FtaDetectedPatternEntity> FtaDetectedPatterns => Set<FtaDetectedPatternEntity>();
public DbSet<FtaMonitoredUniverseAssetEntity> MonitoredUniverseAssets => Set<FtaMonitoredUniverseAssetEntity>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// 1. Settings Table
modelBuilder.Entity<SettingEntity>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasIndex(e => e.Key).IsUnique();
});
// 2. FTA Candles Table & Composite Time-Series Index
modelBuilder.Entity<FtaCandleEntity>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasIndex(e => new { e.Isin, e.Timeframe, e.TimestampUtc });
entity.HasIndex(e => e.TimestampUtc);
});
// 3. JSONB Value Converters for Complex Types
var exitPlanConverter = new ValueConverter<ExitPlan, string>(
v => JsonSerializer.Serialize(v, JsonOptions),
v => JsonSerializer.Deserialize<ExitPlan>(v, JsonOptions) ?? new ExitPlan(ExitStrategyType.FixedSingleTarget, 0m, new List<TakeProfitStage>(), null, null, null, null)
);
var patternsConverter = new ValueConverter<List<PatternResultDto>, string>(
v => JsonSerializer.Serialize(v, JsonOptions),
v => JsonSerializer.Deserialize<List<PatternResultDto>>(v, JsonOptions) ?? new List<PatternResultDto>()
);
var indicatorSnapshotConverter = new ValueConverter<Dictionary<string, decimal>, string>(
v => JsonSerializer.Serialize(v, JsonOptions),
v => JsonSerializer.Deserialize<Dictionary<string, decimal>>(v, JsonOptions) ?? new Dictionary<string, decimal>()
);
var extraDataConverter = new ValueConverter<Dictionary<string, object>?, string>(
v => v == null ? "{}" : JsonSerializer.Serialize(v, JsonOptions),
v => string.IsNullOrWhiteSpace(v) ? null : JsonSerializer.Deserialize<Dictionary<string, object>>(v, JsonOptions)
);
// 4. FTA Technical Setups Table & JSONB mappings
modelBuilder.Entity<FtaTechnicalSetupEntity>(entity =>
{
entity.HasKey(e => e.SetupId);
entity.HasIndex(e => new { e.Isin, e.IsActive, e.ExpiresAtUtc });
entity.HasIndex(e => e.CreatedAtUtc);
entity.HasIndex(e => e.QualityScore);
entity.HasIndex(e => e.IsTopPick);
entity.HasIndex(e => new { e.IsActive, e.IsTopPick, e.QualityScore });
entity.Property(e => e.ExitPlan)
.HasColumnType("jsonb")
.HasConversion(exitPlanConverter);
entity.Property(e => e.TriggeringPatterns)
.HasColumnType("jsonb")
.HasConversion(patternsConverter);
entity.Property(e => e.IndicatorSnapshot)
.HasColumnType("jsonb")
.HasConversion(indicatorSnapshotConverter);
});
// 5. FTA Detected Patterns Table & JSONB mappings
modelBuilder.Entity<FtaDetectedPatternEntity>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasIndex(e => new { e.Isin, e.DetectedAtUtc });
entity.HasIndex(e => e.PatternType);
entity.HasIndex(e => e.QualityScore);
entity.Property(e => e.ExtraData)
.HasColumnType("jsonb")
.HasConversion(extraDataConverter);
});
// 6. Monitored Universe Assets Table (persisted backing store for TechnicalUniverseManager, cleared on
// every service startup - see FtaMonitoredUniverseAssetEntity's doc comment)
modelBuilder.Entity<FtaMonitoredUniverseAssetEntity>(entity =>
{
entity.HasKey(e => e.Isin);
entity.HasIndex(e => e.Source);
entity.HasIndex(e => e.ExpiresAtUtc);
});
}
}
public class TechnicalAnalysisDbContextFactory : IDesignTimeDbContextFactory<TechnicalAnalysisDbContext>
{
public TechnicalAnalysisDbContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<TechnicalAnalysisDbContext>();
optionsBuilder.UseNpgsql("Host=localhost;Database=finlytic_ta;Username=postgres;Password=postgres");
return new TechnicalAnalysisDbContext(optionsBuilder.Options);
}
}