Trading bot grain (#33)

* Trading bot Grain

* Fix a bit more of the trading bot

* Advance on the tradingbot grain

* Fix build

* Fix db script

* Fix user login

* Fix a bit backtest

* Fix cooldown and backtest

* start fixing bot start

* Fix startup

* Setup local db

* Fix build and update candles and scenario

* Add bot registry

* Add reminder

* Updateing the grains

* fix bootstraping

* Save stats on tick

* Save bot data every tick

* Fix serialization

* fix save bot stats

* Fix get candles

* use dict instead of list for position

* Switch hashset to dict

* Fix a bit

* Fix bot launch and bot view

* add migrations

* Remove the tolist

* Add agent grain

* Save agent summary

* clean

* Add save bot

* Update get bots

* Add get bots

* Fix stop/restart

* fix Update config

* Update scanner table on new backtest saved

* Fix backtestRowDetails.tsx

* Fix agentIndex

* Update agentIndex

* Fix more things

* Update user cache

* Fix

* Fix account load/start/restart/run
This commit is contained in:
Oda
2025-08-04 23:07:06 +02:00
committed by GitHub
parent cd378587aa
commit 082ae8714b
215 changed files with 9562 additions and 14028 deletions

View File

@@ -29,9 +29,10 @@ public class ManagingDbContext : DbContext
public DbSet<SpotlightOverviewEntity> SpotlightOverviews { get; set; }
public DbSet<TraderEntity> Traders { get; set; }
public DbSet<FundingRateEntity> FundingRates { get; set; }
public DbSet<AgentSummaryEntity> AgentSummaries { get; set; }
// Bot entities
public DbSet<BotBackupEntity> BotBackups { get; set; }
public DbSet<BotEntity> Bots { get; set; }
// Settings entities
public DbSet<MoneyManagementEntity> MoneyManagements { get; set; }
@@ -46,6 +47,10 @@ public class ManagingDbContext : DbContext
{
base.OnModelCreating(modelBuilder);
// Configure schema for Orleans tables (if needed for future organization)
// Orleans tables will remain in the default schema for now
// This can be changed later if needed by configuring specific schemas
// Configure Account entity
modelBuilder.Entity<AccountEntity>(entity =>
{
@@ -280,8 +285,7 @@ public class ManagingDbContext : DbContext
// Configure Position entity
modelBuilder.Entity<PositionEntity>(entity =>
{
entity.HasKey(e => e.Id);
entity.Property(e => e.Identifier).IsRequired().HasMaxLength(255);
entity.HasKey(e => e.Identifier);
entity.Property(e => e.ProfitAndLoss).HasColumnType("decimal(18,8)");
entity.Property(e => e.OriginDirection).IsRequired().HasConversion<string>();
entity.Property(e => e.Status).IsRequired().HasConversion<string>();
@@ -348,7 +352,6 @@ public class ManagingDbContext : DbContext
});
// Configure TopVolumeTicker entity
modelBuilder.Entity<TopVolumeTickerEntity>(entity =>
{
@@ -425,22 +428,26 @@ public class ManagingDbContext : DbContext
});
// Configure BotBackup entity
modelBuilder.Entity<BotBackupEntity>(entity =>
modelBuilder.Entity<BotEntity>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasKey(e => e.Identifier);
entity.Property(e => e.Identifier).IsRequired().HasMaxLength(255);
entity.Property(e => e.UserName).HasMaxLength(255);
entity.Property(e => e.Data).IsRequired().HasColumnType("text");
entity.Property(e => e.Name).IsRequired().HasMaxLength(255);
entity.Property(e => e.Status).IsRequired().HasConversion<string>();
entity.Property(e => e.CreateDate).IsRequired();
entity.Property(e => e.StartupTime).IsRequired();
entity.Property(e => e.TradeWins).IsRequired();
entity.Property(e => e.TradeLosses).IsRequired();
entity.Property(e => e.Pnl).HasPrecision(18, 8);
entity.Property(e => e.Roi).HasPrecision(18, 8);
entity.Property(e => e.Volume).HasPrecision(18, 8);
entity.Property(e => e.Fees).HasPrecision(18, 8);
// Create indexes
entity.HasIndex(e => e.Identifier).IsUnique();
entity.HasIndex(e => e.UserName);
entity.HasIndex(e => e.LastStatus);
entity.HasIndex(e => e.Status);
entity.HasIndex(e => e.CreateDate);
// Composite index for user bots
entity.HasIndex(e => new { e.UserName, e.CreateDate });
// Configure relationship with User
entity.HasOne(e => e.User)
.WithMany()
@@ -517,5 +524,105 @@ public class ManagingDbContext : DbContext
entity.HasIndex(e => e.CacheKey).IsUnique();
entity.HasIndex(e => e.CreatedAt);
});
// Configure AgentSummary entity
modelBuilder.Entity<AgentSummaryEntity>(entity =>
{
entity.HasKey(e => e.Id);
entity.Property(e => e.UserId).IsRequired();
entity.Property(e => e.AgentName).IsRequired().HasMaxLength(255);
entity.Property(e => e.TotalPnL).HasColumnType("decimal(18,8)");
entity.Property(e => e.TotalROI).HasColumnType("decimal(18,8)");
entity.Property(e => e.Wins).IsRequired();
entity.Property(e => e.Losses).IsRequired();
entity.Property(e => e.Runtime);
entity.Property(e => e.CreatedAt).IsRequired();
entity.Property(e => e.UpdatedAt).IsRequired();
entity.Property(e => e.ActiveStrategiesCount).IsRequired();
entity.Property(e => e.TotalVolume).HasPrecision(18, 8);
// Create indexes for common queries
entity.HasIndex(e => e.UserId).IsUnique();
entity.HasIndex(e => e.AgentName);
entity.HasIndex(e => e.TotalPnL);
entity.HasIndex(e => e.UpdatedAt);
// Configure relationship with User
entity.HasOne(e => e.User)
.WithMany()
.HasForeignKey(e => e.UserId)
.OnDelete(DeleteBehavior.Cascade);
});
}
/// <summary>
/// Ensures Orleans tables are properly initialized in the database.
/// This method can be called during application startup to verify Orleans infrastructure.
/// </summary>
public async Task EnsureOrleansTablesExistAsync()
{
// Orleans tables are automatically created by the Orleans framework
// when using AdoNetClustering and AdoNetReminderService.
// This method serves as a verification point and can be extended
// for custom Orleans table management if needed.
// For now, we just ensure the database is accessible
await Database.CanConnectAsync();
}
/// <summary>
/// Gets Orleans table statistics for monitoring purposes.
/// This helps track Orleans table sizes and performance.
/// </summary>
public async Task<Dictionary<string, long>> GetOrleansTableStatsAsync()
{
var stats = new Dictionary<string, long>();
// Orleans table names
var orleansTables = new[]
{
"orleansmembershiptable",
"orleansmembershipversiontable",
"orleansquery",
"orleansreminderstable",
"orleansstorage"
};
foreach (var tableName in orleansTables)
{
try
{
var count = await Database.SqlQueryRaw<long>($"SELECT COUNT(*) FROM {tableName}").FirstOrDefaultAsync();
stats[tableName] = count;
}
catch
{
// Table might not exist yet (normal during startup)
stats[tableName] = -1;
}
}
return stats;
}
/// <summary>
/// Database organization strategy:
/// - Application tables: Default schema (public)
/// - Orleans tables: Default schema (public) - managed by Orleans framework
/// - Future consideration: Move Orleans tables to 'orleans' schema if needed
///
/// Benefits of current approach:
/// - Single database simplifies deployment and backup
/// - Orleans tables are automatically managed by the framework
/// - No additional configuration complexity
/// - Easier monitoring and maintenance
/// </summary>
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
// Add any additional configuration here if needed
}
}