refactor(bot): update paper trading models, broker integration, background services, and test project
This commit is contained in:
@@ -1,45 +1,78 @@
|
||||
using System;
|
||||
using FinlyticBot.Entities;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using FinlyticCore.Database;
|
||||
using FinlyticCore.Dtos.TechnicalAnalysis;
|
||||
using FinlyticCore.Entities.Settings;
|
||||
using FinlyticBot.Database.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace FinlyticBot.Database;
|
||||
|
||||
public class BotDbContext : DbContext, ISettingsDbContext
|
||||
{
|
||||
public BotDbContext(DbContextOptions<BotDbContext> options) : base(options) { }
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
WriteIndented = false
|
||||
};
|
||||
|
||||
public BotDbContext(DbContextOptions<BotDbContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
|
||||
public DbSet<SettingEntity> DynamicSettings => Set<SettingEntity>();
|
||||
public DbSet<ExecutedPaperTradeEntity> ExecutedPaperTrades => Set<ExecutedPaperTradeEntity>();
|
||||
public DbSet<BotAuditLogEntity> BotAuditLogs => Set<BotAuditLogEntity>();
|
||||
public DbSet<BotPositionEntity> Positions => Set<BotPositionEntity>();
|
||||
public DbSet<BotPortfolioSnapshotEntity> PortfolioSnapshots => Set<BotPortfolioSnapshotEntity>();
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
modelBuilder.Entity<ExecutedPaperTradeEntity>(entity =>
|
||||
// 2. ExitPlan JSONB Converter
|
||||
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)
|
||||
);
|
||||
|
||||
// 3. Bot Positions Table
|
||||
modelBuilder.Entity<BotPositionEntity>(entity =>
|
||||
{
|
||||
entity.HasIndex(e => e.TradeId).IsUnique();
|
||||
entity.HasIndex(e => e.Symbol);
|
||||
entity.HasIndex(e => e.Isin);
|
||||
entity.HasIndex(e => e.AlpacaOrderId);
|
||||
entity.HasIndex(e => e.Status);
|
||||
entity.HasIndex(e => e.PlacedAt);
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.HasIndex(e => new { e.Status, e.Venue });
|
||||
entity.HasIndex(e => e.OpenedAtUtc);
|
||||
entity.HasIndex(e => e.ProposalId);
|
||||
|
||||
entity.Property(e => e.ExitPlan)
|
||||
.HasColumnType("jsonb")
|
||||
.HasConversion(exitPlanConverter);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<BotAuditLogEntity>(entity =>
|
||||
// 4. Portfolio Snapshots Table
|
||||
modelBuilder.Entity<BotPortfolioSnapshotEntity>(entity =>
|
||||
{
|
||||
entity.HasIndex(e => e.TradeId);
|
||||
entity.HasIndex(e => e.Symbol);
|
||||
entity.HasIndex(e => e.Action);
|
||||
entity.HasIndex(e => e.Timestamp);
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.HasIndex(e => e.SnapshotDateUtc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class BotDbContextFactory : IDesignTimeDbContextFactory<BotDbContext>
|
||||
{
|
||||
public BotDbContext CreateDbContext(string[] args)
|
||||
{
|
||||
var optionsBuilder = new DbContextOptionsBuilder<BotDbContext>();
|
||||
optionsBuilder.UseNpgsql("Host=localhost;Database=finlytic_bot;Username=postgres;Password=postgres");
|
||||
return new BotDbContext(optionsBuilder.Options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace FinlyticBot.Database.Entities;
|
||||
|
||||
[Table("bot_portfolio_snapshots")]
|
||||
public class BotPortfolioSnapshotEntity
|
||||
{
|
||||
[Key]
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
|
||||
public DateTime SnapshotDateUtc { get; set; } = DateTime.UtcNow;
|
||||
|
||||
[Column(TypeName = "decimal(18,4)")]
|
||||
public decimal TotalEquityEur { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,4)")]
|
||||
public decimal CashEur { get; set; }
|
||||
|
||||
public int OpenPositionsCount { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,4)")]
|
||||
public decimal DailyRealizedPnlEur { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,4)")]
|
||||
public decimal TotalUnrealizedPnlEur { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(6,2)")]
|
||||
public decimal? WinRatePercent { get; set; }
|
||||
|
||||
public DateTime CreatedAtUtc { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using FinlyticCore.Dtos.Bot;
|
||||
using FinlyticCore.Dtos.TechnicalAnalysis;
|
||||
using FinlyticCore.Dtos.Trading;
|
||||
|
||||
namespace FinlyticBot.Database.Entities;
|
||||
|
||||
[Table("bot_positions")]
|
||||
public class BotPositionEntity
|
||||
{
|
||||
[Key]
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
|
||||
public Guid ProposalId { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(20)]
|
||||
public string Isin { get; set; } = string.Empty;
|
||||
|
||||
[MaxLength(30)]
|
||||
public string Symbol { get; set; } = string.Empty;
|
||||
|
||||
public BotExecutionVenue Venue { get; set; } = BotExecutionVenue.SyntheticPaperBroker;
|
||||
|
||||
[MaxLength(60)]
|
||||
public string? AlpacaOrderId { get; set; }
|
||||
|
||||
[MaxLength(60)]
|
||||
public string? ClientOrderId { get; set; }
|
||||
|
||||
public SignalDirection Direction { get; set; } = SignalDirection.Buy;
|
||||
|
||||
[Column(TypeName = "decimal(18,4)")]
|
||||
public decimal Quantity { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,4)")]
|
||||
public decimal EntryPrice { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,4)")]
|
||||
public decimal AverageBuyIn { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,4)")]
|
||||
public decimal InitialStopLoss { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,4)")]
|
||||
public decimal CurrentStopLoss { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,4)")]
|
||||
public decimal CurrentPrice { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,4)")]
|
||||
public decimal TakeProfit1 { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,4)")]
|
||||
public decimal TakeProfit2 { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,4)")]
|
||||
public decimal RealizedPnlEur { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,4)")]
|
||||
public decimal TotalFeesEur { get; set; }
|
||||
|
||||
public BotPositionStatus Status { get; set; } = BotPositionStatus.Active;
|
||||
|
||||
public ExitPlan ExitPlan { get; set; } = null!;
|
||||
|
||||
public DateTime OpenedAtUtc { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public DateTime? ClosedAtUtc { get; set; }
|
||||
|
||||
public DateTime LastSyncAtUtc { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
Reference in New Issue
Block a user