Postgres (#30)
* Add postgres * Migrate users * Migrate geneticRequest * Try to fix Concurrent call * Fix asyncawait * Fix async and concurrent * Migrate backtests * Add cache for user by address * Fix backtest migration * Fix not open connection * Fix backtest command error * Fix concurrent * Fix all concurrency * Migrate TradingRepo * Fix scenarios * Migrate statistic repo * Save botbackup * Add settings et moneymanagement * Add bot postgres * fix a bit more backups * Fix bot model * Fix loading backup * Remove cache market for read positions * Add workers to postgre * Fix workers api * Reduce get Accounts for workers * Migrate synth to postgre * Fix backtest saved * Remove mongodb * botservice decorrelation * Fix tradingbot scope call * fix tradingbot * fix concurrent * Fix scope for genetics * Fix account over requesting * Fix bundle backtest worker * fix a lot of things * fix tab backtest * Remove optimized moneymanagement * Add light signal to not use User and too much property * Make money management lighter * insert indicators to awaitable * Migrate add strategies to await * Refactor scenario and indicator retrieval to use asynchronous methods throughout the application * add more async await * Add services * Fix and clean * Fix bot a bit * Fix bot and add message for cooldown * Remove fees * Add script to deploy db * Update dfeeploy script * fix script * Add idempotent script and backup * finish script migration * Fix did user and agent name on start bot
This commit is contained in:
@@ -1,46 +0,0 @@
|
||||
using Managing.Application.Abstractions.Repositories;
|
||||
using Managing.Domain.Accounts;
|
||||
using Managing.Infrastructure.Databases.MongoDb;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Abstractions;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
|
||||
namespace Managing.Infrastructure.Databases;
|
||||
|
||||
public class AccountRepository : IAccountRepository
|
||||
{
|
||||
private readonly IMongoRepository<AccountDto> _accountRepository;
|
||||
|
||||
public AccountRepository(IMongoRepository<AccountDto> accountRepository)
|
||||
{
|
||||
_accountRepository = accountRepository;
|
||||
}
|
||||
|
||||
public void DeleteAccountByName(string name)
|
||||
{
|
||||
var account = _accountRepository.FindOne(a => a.Name == name);
|
||||
_accountRepository.DeleteById(account.Id.ToString());
|
||||
}
|
||||
|
||||
public async Task<Account> GetAccountByKeyAsync(string key)
|
||||
{
|
||||
var account = await _accountRepository.FindOneAsync(a => a.Key == key);
|
||||
return MongoMappers.Map(account);
|
||||
}
|
||||
|
||||
public async Task<Account> GetAccountByNameAsync(string name)
|
||||
{
|
||||
var account = await _accountRepository.FindOneAsync(a => a.Name == name);
|
||||
return MongoMappers.Map(account);
|
||||
}
|
||||
|
||||
public IEnumerable<Account> GetAccounts()
|
||||
{
|
||||
var accounts = _accountRepository.FindAll();
|
||||
return MongoMappers.Map(accounts);
|
||||
}
|
||||
|
||||
public async Task InsertAccountAsync(Account account)
|
||||
{
|
||||
await _accountRepository.InsertOneAsync(MongoMappers.Map(account));
|
||||
}
|
||||
}
|
||||
@@ -1,388 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using Managing.Application.Abstractions.Repositories;
|
||||
using Managing.Domain.Backtests;
|
||||
using Managing.Domain.Users;
|
||||
using Managing.Infrastructure.Databases.MongoDb;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Abstractions;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace Managing.Infrastructure.Databases;
|
||||
|
||||
public class BacktestRepository : IBacktestRepository
|
||||
{
|
||||
private readonly IMongoRepository<BacktestDto> _backtestRepository;
|
||||
private readonly IMongoRepository<BundleBacktestRequestDto> _bundleBacktestRepository;
|
||||
|
||||
public BacktestRepository(
|
||||
IMongoRepository<BacktestDto> backtestRepository,
|
||||
IMongoRepository<BundleBacktestRequestDto> bundleBacktestRepository)
|
||||
{
|
||||
_backtestRepository = backtestRepository;
|
||||
_bundleBacktestRepository = bundleBacktestRepository;
|
||||
}
|
||||
|
||||
// User-specific operations
|
||||
public void InsertBacktestForUser(User user, Backtest result)
|
||||
{
|
||||
ValidateBacktestData(result);
|
||||
result.User = user;
|
||||
var dto = MongoMappers.Map(result);
|
||||
_backtestRepository.InsertOne(dto);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that all numeric fields in the backtest are of the correct type
|
||||
/// </summary>
|
||||
private void ValidateBacktestData(Backtest backtest)
|
||||
{
|
||||
// Ensure FinalPnl is a valid decimal
|
||||
if (backtest.FinalPnl.GetType() != typeof(decimal))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"FinalPnl must be of type decimal, but got {backtest.FinalPnl.GetType().Name}");
|
||||
}
|
||||
|
||||
// Ensure other numeric fields are correct
|
||||
if (backtest.GrowthPercentage.GetType() != typeof(decimal))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"GrowthPercentage must be of type decimal, but got {backtest.GrowthPercentage.GetType().Name}");
|
||||
}
|
||||
|
||||
if (backtest.HodlPercentage.GetType() != typeof(decimal))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"HodlPercentage must be of type decimal, but got {backtest.HodlPercentage.GetType().Name}");
|
||||
}
|
||||
|
||||
if (backtest.Score.GetType() != typeof(double))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Score must be of type double, but got {backtest.Score.GetType().Name}");
|
||||
}
|
||||
|
||||
if (backtest.WinRate.GetType() != typeof(int))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"WinRate must be of type int, but got {backtest.WinRate.GetType().Name}");
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Backtest> GetBacktestsByUser(User user)
|
||||
{
|
||||
var backtests = _backtestRepository.AsQueryable()
|
||||
.Where(b => b.User.Name == user.Name)
|
||||
.ToList();
|
||||
|
||||
return backtests.Select(b => MongoMappers.Map(b));
|
||||
}
|
||||
|
||||
public IEnumerable<Backtest> GetBacktestsByRequestId(string requestId)
|
||||
{
|
||||
var backtests = _backtestRepository.AsQueryable()
|
||||
.Where(b => b.RequestId == requestId)
|
||||
.ToList();
|
||||
|
||||
return backtests.Select(b => MongoMappers.Map(b));
|
||||
}
|
||||
|
||||
public (IEnumerable<LightBacktest> Backtests, int TotalCount) GetBacktestsByRequestIdPaginated(string requestId,
|
||||
int page, int pageSize, string sortBy = "score", string sortOrder = "desc")
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
var collection = _backtestRepository.GetCollection(); // You may need to expose this in your repo
|
||||
|
||||
var filter = Builders<BacktestDto>.Filter.Eq(b => b.RequestId, requestId);
|
||||
|
||||
var afterQueryMs = stopwatch.ElapsedMilliseconds;
|
||||
var totalCount = collection.CountDocuments(filter);
|
||||
var afterCountMs = stopwatch.ElapsedMilliseconds;
|
||||
|
||||
var projection = Builders<BacktestDto>.Projection
|
||||
.Include(b => b.Identifier)
|
||||
.Include(b => b.FinalPnl)
|
||||
.Include(b => b.WinRate)
|
||||
.Include(b => b.GrowthPercentage)
|
||||
.Include(b => b.HodlPercentage)
|
||||
.Include(b => b.StartDate)
|
||||
.Include(b => b.EndDate)
|
||||
.Include(b => b.Score)
|
||||
.Include(b => b.ScoreMessage)
|
||||
.Include(b => b.Config)
|
||||
.Include(b => b.Fees)
|
||||
.Include(b => b.Statistics);
|
||||
|
||||
// Build sort definition
|
||||
var sortDefinition = sortBy.ToLower() switch
|
||||
{
|
||||
"score" => sortOrder == "desc"
|
||||
? Builders<BacktestDto>.Sort.Descending(b => b.Score)
|
||||
: Builders<BacktestDto>.Sort.Ascending(b => b.Score),
|
||||
"finalpnl" => sortOrder == "desc"
|
||||
? Builders<BacktestDto>.Sort.Descending(b => b.FinalPnl)
|
||||
: Builders<BacktestDto>.Sort.Ascending(b => b.FinalPnl),
|
||||
"winrate" => sortOrder == "desc"
|
||||
? Builders<BacktestDto>.Sort.Descending(b => b.WinRate)
|
||||
: Builders<BacktestDto>.Sort.Ascending(b => b.WinRate),
|
||||
"growthpercentage" => sortOrder == "desc"
|
||||
? Builders<BacktestDto>.Sort.Descending(b => b.GrowthPercentage)
|
||||
: Builders<BacktestDto>.Sort.Ascending(b => b.GrowthPercentage),
|
||||
"hodlpercentage" => sortOrder == "desc"
|
||||
? Builders<BacktestDto>.Sort.Descending(b => b.HodlPercentage)
|
||||
: Builders<BacktestDto>.Sort.Ascending(b => b.HodlPercentage),
|
||||
_ => sortOrder == "desc"
|
||||
? Builders<BacktestDto>.Sort.Descending(b => b.Score)
|
||||
: Builders<BacktestDto>.Sort.Ascending(b => b.Score)
|
||||
};
|
||||
|
||||
var afterProjectionMs = stopwatch.ElapsedMilliseconds;
|
||||
var backtests = collection
|
||||
.Find(filter)
|
||||
.Project<BacktestDto>(projection)
|
||||
.Sort(sortDefinition)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Limit(pageSize)
|
||||
.ToList();
|
||||
var afterToListMs = stopwatch.ElapsedMilliseconds;
|
||||
|
||||
Console.WriteLine(
|
||||
$"[BacktestRepo] Query: {afterQueryMs}ms, Count: {afterCountMs - afterQueryMs}ms, Projection: {afterProjectionMs - afterCountMs}ms, ToList: {afterToListMs - afterProjectionMs}ms, Total: {afterToListMs}ms");
|
||||
|
||||
var mappedBacktests = backtests.Select(b => new LightBacktest
|
||||
{
|
||||
Id = b.Identifier,
|
||||
Config = MongoMappers.Map(b.Config),
|
||||
FinalPnl = b.FinalPnl,
|
||||
WinRate = b.WinRate,
|
||||
GrowthPercentage = b.GrowthPercentage,
|
||||
HodlPercentage = b.HodlPercentage,
|
||||
StartDate = b.StartDate,
|
||||
EndDate = b.EndDate,
|
||||
MaxDrawdown = b.Statistics?.MaxDrawdown,
|
||||
Fees = b.Fees,
|
||||
SharpeRatio = b.Statistics?.SharpeRatio != null ? (double)b.Statistics.SharpeRatio : null,
|
||||
Score = b.Score,
|
||||
ScoreMessage = b.ScoreMessage ?? string.Empty
|
||||
});
|
||||
|
||||
return (mappedBacktests, (int)totalCount);
|
||||
}
|
||||
|
||||
public Backtest GetBacktestByIdForUser(User user, string id)
|
||||
{
|
||||
var backtest = _backtestRepository.FindOne(b => b.Identifier == id);
|
||||
|
||||
// Check if backtest exists and belongs to the user
|
||||
if (backtest != null && backtest.User != null && backtest.User.Name == user.Name)
|
||||
{
|
||||
return MongoMappers.Map(backtest);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void DeleteBacktestByIdForUser(User user, string id)
|
||||
{
|
||||
var backtest = _backtestRepository.FindOne(b => b.Identifier == id);
|
||||
|
||||
if (backtest != null && backtest.User != null && backtest.User.Name == user.Name)
|
||||
{
|
||||
_backtestRepository.DeleteById(backtest.Id.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteBacktestsByIdsForUser(User user, IEnumerable<string> ids)
|
||||
{
|
||||
var backtests = _backtestRepository.AsQueryable()
|
||||
.Where(b => b.User != null && b.User.Name == user.Name && ids.Contains(b.Identifier))
|
||||
.ToList();
|
||||
|
||||
foreach (var backtest in backtests)
|
||||
{
|
||||
_backtestRepository.DeleteById(backtest.Id.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteAllBacktestsForUser(User user)
|
||||
{
|
||||
var backtests = _backtestRepository.AsQueryable()
|
||||
.Where(b => b.User != null && b.User.Name == user.Name)
|
||||
.ToList();
|
||||
|
||||
foreach (var backtest in backtests)
|
||||
{
|
||||
_backtestRepository.DeleteById(backtest.Id.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteBacktestsByRequestId(string requestId)
|
||||
{
|
||||
var backtests = _backtestRepository.AsQueryable()
|
||||
.Where(b => b.RequestId == requestId)
|
||||
.ToList();
|
||||
|
||||
foreach (var backtest in backtests)
|
||||
{
|
||||
_backtestRepository.DeleteById(backtest.Id.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public (IEnumerable<LightBacktest> Backtests, int TotalCount) GetBacktestsByUserPaginated(User user, int page,
|
||||
int pageSize, string sortBy = "score", string sortOrder = "desc")
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
var collection = _backtestRepository.GetCollection();
|
||||
|
||||
var filter = Builders<BacktestDto>.Filter.Eq(b => b.User.Name, user.Name);
|
||||
|
||||
var afterQueryMs = stopwatch.ElapsedMilliseconds;
|
||||
var totalCount = collection.CountDocuments(filter);
|
||||
var afterCountMs = stopwatch.ElapsedMilliseconds;
|
||||
|
||||
var projection = Builders<BacktestDto>.Projection
|
||||
.Include(b => b.Identifier)
|
||||
.Include(b => b.FinalPnl)
|
||||
.Include(b => b.WinRate)
|
||||
.Include(b => b.GrowthPercentage)
|
||||
.Include(b => b.HodlPercentage)
|
||||
.Include(b => b.StartDate)
|
||||
.Include(b => b.EndDate)
|
||||
.Include(b => b.Score)
|
||||
.Include(b => b.ScoreMessage)
|
||||
.Include(b => b.Config)
|
||||
.Include(b => b.Fees)
|
||||
.Include(b => b.Statistics);
|
||||
|
||||
// Build sort definition
|
||||
var sortDefinition = sortBy.ToLower() switch
|
||||
{
|
||||
"score" => sortOrder == "desc"
|
||||
? Builders<BacktestDto>.Sort.Descending(b => b.Score)
|
||||
: Builders<BacktestDto>.Sort.Ascending(b => b.Score),
|
||||
"finalpnl" => sortOrder == "desc"
|
||||
? Builders<BacktestDto>.Sort.Descending(b => b.FinalPnl)
|
||||
: Builders<BacktestDto>.Sort.Ascending(b => b.FinalPnl),
|
||||
"winrate" => sortOrder == "desc"
|
||||
? Builders<BacktestDto>.Sort.Descending(b => b.WinRate)
|
||||
: Builders<BacktestDto>.Sort.Ascending(b => b.WinRate),
|
||||
"growthpercentage" => sortOrder == "desc"
|
||||
? Builders<BacktestDto>.Sort.Descending(b => b.GrowthPercentage)
|
||||
: Builders<BacktestDto>.Sort.Ascending(b => b.GrowthPercentage),
|
||||
"hodlpercentage" => sortOrder == "desc"
|
||||
? Builders<BacktestDto>.Sort.Descending(b => b.HodlPercentage)
|
||||
: Builders<BacktestDto>.Sort.Ascending(b => b.HodlPercentage),
|
||||
_ => sortOrder == "desc"
|
||||
? Builders<BacktestDto>.Sort.Descending(b => b.Score)
|
||||
: Builders<BacktestDto>.Sort.Ascending(b => b.Score)
|
||||
};
|
||||
|
||||
var afterProjectionMs = stopwatch.ElapsedMilliseconds;
|
||||
var backtests = collection
|
||||
.Find(filter)
|
||||
.Project<BacktestDto>(projection)
|
||||
.Sort(sortDefinition)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Limit(pageSize)
|
||||
.ToList();
|
||||
var afterToListMs = stopwatch.ElapsedMilliseconds;
|
||||
|
||||
Console.WriteLine(
|
||||
$"[BacktestRepo] User Query: {afterQueryMs}ms, Count: {afterCountMs - afterQueryMs}ms, Projection: {afterProjectionMs - afterCountMs}ms, ToList: {afterToListMs - afterProjectionMs}ms, Total: {afterToListMs}ms");
|
||||
|
||||
var mappedBacktests = backtests.Select(b => new LightBacktest
|
||||
{
|
||||
Id = b.Identifier,
|
||||
Config = MongoMappers.Map(b.Config),
|
||||
FinalPnl = b.FinalPnl,
|
||||
WinRate = b.WinRate,
|
||||
GrowthPercentage = b.GrowthPercentage,
|
||||
HodlPercentage = b.HodlPercentage,
|
||||
StartDate = b.StartDate,
|
||||
EndDate = b.EndDate,
|
||||
MaxDrawdown = b.Statistics?.MaxDrawdown,
|
||||
Fees = b.Fees,
|
||||
SharpeRatio = b.Statistics?.SharpeRatio != null ? (double)b.Statistics.SharpeRatio : null,
|
||||
Score = b.Score,
|
||||
ScoreMessage = b.ScoreMessage ?? string.Empty
|
||||
});
|
||||
|
||||
return (mappedBacktests, (int)totalCount);
|
||||
}
|
||||
|
||||
// Bundle backtest methods
|
||||
public void InsertBundleBacktestRequestForUser(User user, BundleBacktestRequest bundleRequest)
|
||||
{
|
||||
bundleRequest.User = user;
|
||||
var dto = MongoMappers.Map(bundleRequest);
|
||||
_bundleBacktestRepository.InsertOne(dto);
|
||||
}
|
||||
|
||||
public IEnumerable<BundleBacktestRequest> GetBundleBacktestRequestsByUser(User user)
|
||||
{
|
||||
var projection = Builders<BundleBacktestRequestDto>.Projection
|
||||
.Include(b => b.RequestId)
|
||||
.Include(b => b.Status)
|
||||
.Include(b => b.CreatedAt)
|
||||
.Include(b => b.CurrentBacktest)
|
||||
.Include(b => b.EstimatedTimeRemainingSeconds)
|
||||
.Include(b => b.TotalBacktests)
|
||||
.Include(b => b.CurrentBacktest)
|
||||
.Include(b => b.CompletedAt)
|
||||
.Include(b => b.ErrorMessage)
|
||||
.Include(b => b.ProgressInfo)
|
||||
.Include(b => b.Name)
|
||||
.Include(b => b.CompletedBacktests)
|
||||
.Include(b => b.User);
|
||||
|
||||
var filter = Builders<BundleBacktestRequestDto>.Filter.Eq(b => b.User.Name, user.Name);
|
||||
var bundleRequests = _bundleBacktestRepository.GetCollection()
|
||||
.Find(filter)
|
||||
.Project<BundleBacktestRequestDto>(projection)
|
||||
.ToList();
|
||||
|
||||
return bundleRequests.Select(MongoMappers.Map);
|
||||
}
|
||||
|
||||
public BundleBacktestRequest? GetBundleBacktestRequestByIdForUser(User user, string id)
|
||||
{
|
||||
var bundleRequest = _bundleBacktestRepository.FindOne(b => b.RequestId == id && b.User.Name == user.Name);
|
||||
|
||||
if (bundleRequest != null && bundleRequest.User.Name == user.Name)
|
||||
{
|
||||
return MongoMappers.Map(bundleRequest);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void UpdateBundleBacktestRequest(BundleBacktestRequest bundleRequest)
|
||||
{
|
||||
var existingRequest = _bundleBacktestRepository.FindOne(b => b.RequestId == bundleRequest.RequestId);
|
||||
if (existingRequest != null)
|
||||
{
|
||||
var dto = MongoMappers.Map(bundleRequest);
|
||||
dto.Id = existingRequest.Id; // Preserve the MongoDB ObjectId
|
||||
_bundleBacktestRepository.ReplaceOne(dto);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteBundleBacktestRequestByIdForUser(User user, string id)
|
||||
{
|
||||
var bundleRequest = _bundleBacktestRepository.FindOne(b => b.RequestId == id);
|
||||
|
||||
if (bundleRequest != null && bundleRequest.User.Name == user.Name)
|
||||
{
|
||||
_bundleBacktestRepository.DeleteById(bundleRequest.Id.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<BundleBacktestRequest> GetBundleBacktestRequestsByStatus(BundleBacktestRequestStatus status)
|
||||
{
|
||||
var requests = _bundleBacktestRepository.AsQueryable()
|
||||
.Where(b => b.Status == status)
|
||||
.ToList();
|
||||
|
||||
return requests.Select(MongoMappers.Map);
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using Managing.Application.Abstractions.Repositories;
|
||||
using Managing.Domain.Bots;
|
||||
using Managing.Infrastructure.Databases.MongoDb;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Abstractions;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
|
||||
namespace Managing.Infrastructure.Databases;
|
||||
|
||||
public class BotRepository : IBotRepository
|
||||
{
|
||||
private readonly IMongoRepository<BotDto> _botRepository;
|
||||
|
||||
public BotRepository(IMongoRepository<BotDto> botRepository)
|
||||
{
|
||||
_botRepository = botRepository;
|
||||
}
|
||||
|
||||
public async Task InsertBotAsync(BotBackup bot)
|
||||
{
|
||||
await _botRepository.InsertOneAsync(MongoMappers.Map(bot));
|
||||
}
|
||||
|
||||
public IEnumerable<BotBackup> GetBots()
|
||||
{
|
||||
var bots = _botRepository.FindAll();
|
||||
return bots.Select(b => MongoMappers.Map(b));
|
||||
}
|
||||
|
||||
public async Task UpdateBackupBot(BotBackup bot)
|
||||
{
|
||||
var b = await _botRepository.FindOneAsync(b => b.Identifier == bot.Identifier);
|
||||
var dto = MongoMappers.Map(bot);
|
||||
dto.Id = b.Id;
|
||||
_botRepository.Update(dto);
|
||||
}
|
||||
|
||||
public async Task DeleteBotBackup(string identifier)
|
||||
{
|
||||
var backup = await _botRepository.FindOneAsync(b => b.Identifier == identifier);
|
||||
await _botRepository.DeleteOneAsync(b => b.Id == backup.Id);
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
using Managing.Application.Abstractions.Repositories;
|
||||
using Managing.Domain.Backtests;
|
||||
using Managing.Domain.Users;
|
||||
using Managing.Infrastructure.Databases.MongoDb;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Abstractions;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
|
||||
namespace Managing.Infrastructure.Databases;
|
||||
|
||||
public class GeneticRepository : IGeneticRepository
|
||||
{
|
||||
private readonly IMongoRepository<GeneticRequestDto> _geneticRequestRepository;
|
||||
|
||||
public GeneticRepository(IMongoRepository<GeneticRequestDto> geneticRequestRepository)
|
||||
{
|
||||
_geneticRequestRepository = geneticRequestRepository;
|
||||
}
|
||||
|
||||
public void InsertGeneticRequestForUser(User user, GeneticRequest geneticRequest)
|
||||
{
|
||||
geneticRequest.User = user;
|
||||
_geneticRequestRepository.InsertOne(MongoMappers.Map(geneticRequest));
|
||||
}
|
||||
|
||||
public IEnumerable<GeneticRequest> GetGeneticRequestsByUser(User user)
|
||||
{
|
||||
var geneticRequests = _geneticRequestRepository.AsQueryable()
|
||||
.Where(gr => gr.User.Name == user.Name)
|
||||
.OrderByDescending(gr => gr.CreatedAt)
|
||||
.ToList();
|
||||
|
||||
return geneticRequests.Select(gr => MongoMappers.Map(gr));
|
||||
}
|
||||
|
||||
public GeneticRequest GetGeneticRequestByIdForUser(User user, string id)
|
||||
{
|
||||
var geneticRequest = _geneticRequestRepository.FindById(id);
|
||||
|
||||
// Check if genetic request exists and belongs to the user
|
||||
if (geneticRequest != null && geneticRequest.User != null && geneticRequest.User.Name == user.Name)
|
||||
{
|
||||
return MongoMappers.Map(geneticRequest);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void UpdateGeneticRequest(GeneticRequest geneticRequest)
|
||||
{
|
||||
var existingRequest = _geneticRequestRepository.FindOne(gr => gr.RequestId == geneticRequest.RequestId);
|
||||
if (existingRequest != null)
|
||||
{
|
||||
var updatedDto = MongoMappers.Map(geneticRequest);
|
||||
updatedDto.Id = existingRequest.Id; // Preserve the MongoDB ObjectId
|
||||
_geneticRequestRepository.ReplaceOne(updatedDto);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteGeneticRequestByIdForUser(User user, string id)
|
||||
{
|
||||
var geneticRequest = _geneticRequestRepository.FindOne(gr => gr.RequestId == id);
|
||||
|
||||
if (geneticRequest != null && geneticRequest.User != null && geneticRequest.User.Name == user.Name)
|
||||
{
|
||||
_geneticRequestRepository.DeleteById(geneticRequest.Id.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteAllGeneticRequestsForUser(User user)
|
||||
{
|
||||
var geneticRequests = _geneticRequestRepository.AsQueryable()
|
||||
.Where(gr => gr.User != null && gr.User.Name == user.Name)
|
||||
.ToList();
|
||||
|
||||
foreach (var geneticRequest in geneticRequests)
|
||||
{
|
||||
_geneticRequestRepository.DeleteById(geneticRequest.Id.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<GeneticRequest> GetPendingGeneticRequests()
|
||||
{
|
||||
var pendingRequests = _geneticRequestRepository.AsQueryable()
|
||||
.Where(gr => gr.Status == "Pending")
|
||||
.OrderBy(gr => gr.CreatedAt)
|
||||
.ToList();
|
||||
|
||||
return pendingRequests.Select(gr => MongoMappers.Map(gr));
|
||||
}
|
||||
}
|
||||
@@ -19,11 +19,6 @@ public static class PriceHelpers
|
||||
CloseTime = candle.Date,
|
||||
High = candle.High,
|
||||
Low = candle.Low,
|
||||
BaseVolume = candle.BaseVolume,
|
||||
QuoteVolume = candle.QuoteVolume,
|
||||
TradeCount = candle.TradeCount,
|
||||
TakerBuyBaseVolume = candle.TakerBuyBaseVolume,
|
||||
TakerBuyQuoteVolume = candle.TakerBuyQuoteVolume,
|
||||
Timeframe = candle.Timeframe.ToString()
|
||||
};
|
||||
|
||||
@@ -42,11 +37,6 @@ public static class PriceHelpers
|
||||
Date = dto.CloseTime,
|
||||
High = dto.High,
|
||||
Low = dto.Low,
|
||||
BaseVolume = dto.BaseVolume,
|
||||
QuoteVolume = dto.QuoteVolume,
|
||||
TradeCount = dto.TradeCount,
|
||||
TakerBuyBaseVolume = dto.TakerBuyBaseVolume,
|
||||
TakerBuyQuoteVolume = dto.TakerBuyQuoteVolume,
|
||||
Timeframe = MiscExtensions.ParseEnum<Timeframe>(dto.Timeframe)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,11 +10,17 @@
|
||||
<PackageReference Include="InfluxDB.Client" Version="4.14.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.1"/>
|
||||
<PackageReference Include="MongoDB.Bson" Version="2.25.0"/>
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.25.0"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.11"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.11"/>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.10"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.11"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Managing.Application.Workers\Managing.Application.Workers.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Migrations\"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
1224
src/Managing.Infrastructure.Database/Migrations/20250723194312_InitialCreate.Designer.cs
generated
Normal file
1224
src/Managing.Infrastructure.Database/Migrations/20250723194312_InitialCreate.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1224
src/Managing.Infrastructure.Database/Migrations/20250723221025_UpdateBotBackupDataToText.Designer.cs
generated
Normal file
1224
src/Managing.Infrastructure.Database/Migrations/20250723221025_UpdateBotBackupDataToText.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Managing.Infrastructure.Databases.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class UpdateBotBackupDataToText : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Data",
|
||||
table: "BotBackups",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "jsonb");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Data",
|
||||
table: "BotBackups",
|
||||
type: "jsonb",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text");
|
||||
}
|
||||
}
|
||||
}
|
||||
1259
src/Managing.Infrastructure.Database/Migrations/20250724141819_AddWorkerEntity.Designer.cs
generated
Normal file
1259
src/Managing.Infrastructure.Database/Migrations/20250724141819_AddWorkerEntity.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Managing.Infrastructure.Databases.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddWorkerEntity : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Workers",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
WorkerType = table.Column<string>(type: "text", nullable: false),
|
||||
StartTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
LastRunTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
ExecutionCount = table.Column<int>(type: "integer", nullable: false),
|
||||
DelayTicks = table.Column<long>(type: "bigint", nullable: false),
|
||||
IsActive = table.Column<bool>(type: "boolean", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Workers", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Workers_WorkerType",
|
||||
table: "Workers",
|
||||
column: "WorkerType",
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Workers");
|
||||
}
|
||||
}
|
||||
}
|
||||
1349
src/Managing.Infrastructure.Database/Migrations/20250724160015_AddSynthEntities.Designer.cs
generated
Normal file
1349
src/Managing.Infrastructure.Database/Migrations/20250724160015_AddSynthEntities.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Managing.Infrastructure.Databases.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddSynthEntities : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "SynthMinersLeaderboards",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
Asset = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
TimeIncrement = table.Column<int>(type: "integer", nullable: false),
|
||||
SignalDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
IsBacktest = table.Column<bool>(type: "boolean", nullable: false),
|
||||
MinersData = table.Column<string>(type: "jsonb", nullable: false),
|
||||
CacheKey = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_SynthMinersLeaderboards", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "SynthPredictions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
Asset = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
MinerUid = table.Column<int>(type: "integer", nullable: false),
|
||||
TimeIncrement = table.Column<int>(type: "integer", nullable: false),
|
||||
TimeLength = table.Column<int>(type: "integer", nullable: false),
|
||||
SignalDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
IsBacktest = table.Column<bool>(type: "boolean", nullable: false),
|
||||
PredictionData = table.Column<string>(type: "jsonb", nullable: false),
|
||||
CacheKey = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_SynthPredictions", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_SynthMinersLeaderboards_CacheKey",
|
||||
table: "SynthMinersLeaderboards",
|
||||
column: "CacheKey",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_SynthMinersLeaderboards_CreatedAt",
|
||||
table: "SynthMinersLeaderboards",
|
||||
column: "CreatedAt");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_SynthPredictions_CacheKey",
|
||||
table: "SynthPredictions",
|
||||
column: "CacheKey",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_SynthPredictions_CreatedAt",
|
||||
table: "SynthPredictions",
|
||||
column: "CreatedAt");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "SynthMinersLeaderboards");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "SynthPredictions");
|
||||
}
|
||||
}
|
||||
}
|
||||
1349
src/Managing.Infrastructure.Database/Migrations/20250724164138_UpdateScoreMessageToText.Designer.cs
generated
Normal file
1349
src/Managing.Infrastructure.Database/Migrations/20250724164138_UpdateScoreMessageToText.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Managing.Infrastructure.Databases.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class UpdateScoreMessageToText : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ScoreMessage",
|
||||
table: "Backtests",
|
||||
type: "text",
|
||||
maxLength: 1000,
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "character varying(1000)",
|
||||
oldMaxLength: 1000);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ScoreMessage",
|
||||
table: "Backtests",
|
||||
type: "character varying(1000)",
|
||||
maxLength: 1000,
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldMaxLength: 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
1364
src/Managing.Infrastructure.Database/Migrations/20250725014014_AddUserIdToBundleBacktestRequest.Designer.cs
generated
Normal file
1364
src/Managing.Infrastructure.Database/Migrations/20250725014014_AddUserIdToBundleBacktestRequest.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,49 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Managing.Infrastructure.Databases.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddUserIdToBundleBacktestRequest : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "UserId",
|
||||
table: "BundleBacktestRequests",
|
||||
type: "integer",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BundleBacktestRequests_UserId",
|
||||
table: "BundleBacktestRequests",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_BundleBacktestRequests_Users_UserId",
|
||||
table: "BundleBacktestRequests",
|
||||
column: "UserId",
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_BundleBacktestRequests_Users_UserId",
|
||||
table: "BundleBacktestRequests");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_BundleBacktestRequests_UserId",
|
||||
table: "BundleBacktestRequests");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "UserId",
|
||||
table: "BundleBacktestRequests");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Managing.Infrastructure.Databases.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class RemoveOptimizedMoneyManagementJsonFromBacktestEntity : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "OptimizedMoneyManagementJson",
|
||||
table: "Backtests");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "OptimizedMoneyManagementJson",
|
||||
table: "Backtests",
|
||||
type: "jsonb",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
}
|
||||
}
|
||||
}
|
||||
1375
src/Managing.Infrastructure.Database/Migrations/20250725172635_AddUserIdToMoneyManagement.Designer.cs
generated
Normal file
1375
src/Managing.Infrastructure.Database/Migrations/20250725172635_AddUserIdToMoneyManagement.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,49 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Managing.Infrastructure.Databases.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddUserIdToMoneyManagement : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "UserId",
|
||||
table: "MoneyManagements",
|
||||
type: "integer",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MoneyManagements_UserId",
|
||||
table: "MoneyManagements",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_MoneyManagements_Users_UserId",
|
||||
table: "MoneyManagements",
|
||||
column: "UserId",
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_MoneyManagements_Users_UserId",
|
||||
table: "MoneyManagements");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_MoneyManagements_UserId",
|
||||
table: "MoneyManagements");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "UserId",
|
||||
table: "MoneyManagements");
|
||||
}
|
||||
}
|
||||
}
|
||||
1390
src/Managing.Infrastructure.Database/Migrations/20250725173315_AddUserIdToBotBackup.Designer.cs
generated
Normal file
1390
src/Managing.Infrastructure.Database/Migrations/20250725173315_AddUserIdToBotBackup.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,49 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Managing.Infrastructure.Databases.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddUserIdToBotBackup : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "UserId",
|
||||
table: "BotBackups",
|
||||
type: "integer",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BotBackups_UserId",
|
||||
table: "BotBackups",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_BotBackups_Users_UserId",
|
||||
table: "BotBackups",
|
||||
column: "UserId",
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_BotBackups_Users_UserId",
|
||||
table: "BotBackups");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_BotBackups_UserId",
|
||||
table: "BotBackups");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "UserId",
|
||||
table: "BotBackups");
|
||||
}
|
||||
}
|
||||
}
|
||||
1356
src/Managing.Infrastructure.Database/Migrations/20250725202808_RemoveFeeEntity.Designer.cs
generated
Normal file
1356
src/Managing.Infrastructure.Database/Migrations/20250725202808_RemoveFeeEntity.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Managing.Infrastructure.Databases.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class RemoveFeeEntity : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Fees");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Fees",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Cost = table.Column<decimal>(type: "numeric(18,8)", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
Exchange = table.Column<string>(type: "text", nullable: false),
|
||||
LastUpdate = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Fees", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Fees_Exchange",
|
||||
table: "Fees",
|
||||
column: "Exchange",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Fees_LastUpdate",
|
||||
table: "Fees",
|
||||
column: "LastUpdate");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,58 +0,0 @@
|
||||
using System.Linq.Expressions;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Abstractions
|
||||
{
|
||||
public interface IMongoRepository<TDocument> where TDocument : IDocument
|
||||
{
|
||||
IQueryable<TDocument> AsQueryable();
|
||||
|
||||
IEnumerable<TDocument> FilterBy(
|
||||
Expression<Func<TDocument, bool>> filterExpression);
|
||||
|
||||
IEnumerable<TDocument> FindAll();
|
||||
IEnumerable<TDocument> FilterBy(FilterDefinition<TDocument> filter);
|
||||
|
||||
IEnumerable<TProjected> FilterBy<TProjected>(
|
||||
Expression<Func<TDocument, bool>> filterExpression,
|
||||
Expression<Func<TDocument, TProjected>> projectionExpression);
|
||||
|
||||
TDocument FindOne(Expression<Func<TDocument, bool>> filterExpression);
|
||||
|
||||
Task<TDocument> FindOneAsync(Expression<Func<TDocument, bool>> filterExpression);
|
||||
|
||||
TDocument FindById(string id);
|
||||
|
||||
Task<TDocument> FindByIdAsync(string id);
|
||||
|
||||
void InsertOne(TDocument document);
|
||||
|
||||
Task InsertOneAsync(TDocument document);
|
||||
|
||||
void InsertMany(ICollection<TDocument> documents);
|
||||
|
||||
Task InsertManyAsync(ICollection<TDocument> documents);
|
||||
|
||||
void ReplaceOne(TDocument document);
|
||||
|
||||
Task ReplaceOneAsync(TDocument document);
|
||||
|
||||
void DeleteOne(Expression<Func<TDocument, bool>> filterExpression);
|
||||
|
||||
Task DeleteOneAsync(Expression<Func<TDocument, bool>> filterExpression);
|
||||
|
||||
void DeleteById(string id);
|
||||
|
||||
Task DeleteByIdAsync(string id);
|
||||
|
||||
void DeleteMany(Expression<Func<TDocument, bool>> filterExpression);
|
||||
|
||||
Task DeleteManyAsync(Expression<Func<TDocument, bool>> filterExpression);
|
||||
|
||||
void Update(TDocument entity);
|
||||
void CreateIndex(string column);
|
||||
void DropCollection();
|
||||
IMongoCollection<TDocument> GetCollection();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Attributes
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public class BsonCollectionAttribute : Attribute
|
||||
{
|
||||
public string CollectionName { get; }
|
||||
|
||||
public BsonCollectionAttribute(string collectionName)
|
||||
{
|
||||
CollectionName = collectionName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
|
||||
[BsonCollection("Accounts")]
|
||||
public class AccountDto : Document
|
||||
{
|
||||
public UserDto User { get; set; }
|
||||
public string Name { get; set; }
|
||||
public TradingExchanges Exchanges { get; set; }
|
||||
public AccountType Type { get; set; }
|
||||
public string Key { get; set; }
|
||||
public string Secret { get; set; }
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
using Exilion.TradingAtomics;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
||||
{
|
||||
[BsonCollection("Backtests")]
|
||||
public class BacktestDto : Document
|
||||
{
|
||||
[BsonRepresentation(BsonType.Decimal128)]
|
||||
public decimal FinalPnl { get; set; }
|
||||
|
||||
public int WinRate { get; set; }
|
||||
[BsonRepresentation(BsonType.Decimal128)]
|
||||
public decimal GrowthPercentage { get; set; }
|
||||
[BsonRepresentation(BsonType.Decimal128)]
|
||||
public decimal HodlPercentage { get; set; }
|
||||
public TradingBotConfigDto Config { get; set; }
|
||||
public List<PositionDto> Positions { get; set; }
|
||||
public List<SignalDto> Signals { get; set; }
|
||||
public DateTime StartDate { get; set; }
|
||||
public DateTime EndDate { get; set; }
|
||||
public MoneyManagementDto MoneyManagement { get; internal set; }
|
||||
public MoneyManagementDto OptimizedMoneyManagement { get; internal set; }
|
||||
public UserDto User { get; set; }
|
||||
public PerformanceMetrics Statistics { get; set; }
|
||||
[BsonRepresentation(BsonType.Decimal128)]
|
||||
public decimal Fees { get; set; }
|
||||
public double Score { get; set; }
|
||||
public string ScoreMessage { get; set; } = string.Empty;
|
||||
public string Identifier { get; set; }
|
||||
public string RequestId { get; set; }
|
||||
public string? Metadata { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
|
||||
|
||||
[BsonCollection("BadTrader")]
|
||||
public class BadTraderDto : Document
|
||||
{
|
||||
public string Address { get; set; }
|
||||
public int Winrate { get; set; }
|
||||
public decimal Pnl { get; set; }
|
||||
public int TradeCount { get; set; }
|
||||
public decimal AverageWin { get; set; }
|
||||
public decimal AverageLoss { get; set; }
|
||||
public decimal Roi { get; set; }
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
|
||||
[BsonCollection("BestTrader")]
|
||||
public class BestTraderDto : Document
|
||||
{
|
||||
public string Address { get; set; }
|
||||
public int Winrate { get; set; }
|
||||
public decimal Pnl { get; set; }
|
||||
public int TradeCount { get; set; }
|
||||
public decimal AverageWin { get; set; }
|
||||
public decimal AverageLoss { get; set; }
|
||||
public decimal Roi { get; set; }
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
|
||||
[BsonCollection("Bots")]
|
||||
public class BotDto : Document
|
||||
{
|
||||
public string Data { get; set; }
|
||||
public string Identifier { get; set; }
|
||||
public UserDto User { get; set; }
|
||||
public BotStatus LastStatus { get; set; }
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Managing.Domain.Backtests;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
|
||||
[BsonCollection("BundleBacktestRequests")]
|
||||
public class BundleBacktestRequestDto : Document
|
||||
{
|
||||
public string RequestId { get; set; } = string.Empty;
|
||||
public UserDto User { get; set; } = new();
|
||||
public DateTime? CompletedAt { get; set; }
|
||||
public BundleBacktestRequestStatus Status { get; set; }
|
||||
public string BacktestRequestsJson { get; set; } = string.Empty;
|
||||
public int TotalBacktests { get; set; }
|
||||
public int CompletedBacktests { get; set; }
|
||||
public int FailedBacktests { get; set; }
|
||||
public string? ErrorMessage { get; set; }
|
||||
public string? ProgressInfo { get; set; }
|
||||
public string? CurrentBacktest { get; set; }
|
||||
public int? EstimatedTimeRemainingSeconds { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public List<string> Results { get; set; } = new();
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
||||
{
|
||||
[BsonCollection("Candles")]
|
||||
public class CandleDto : Document
|
||||
{
|
||||
public TradingExchanges Exchange { get; set; }
|
||||
public Timeframe Timeframe { get; set; }
|
||||
public string Ticker { get; set; }
|
||||
[BsonDateTimeOptions]
|
||||
public DateTime OpenTime { get; set; }
|
||||
[BsonDateTimeOptions]
|
||||
public DateTime CloseTime { get; set; }
|
||||
public decimal Open { get; set; }
|
||||
public decimal Close { get; set; }
|
||||
public decimal High { get; set; }
|
||||
public decimal Low { get; set; }
|
||||
public decimal BaseVolume { get; set; }
|
||||
public decimal QuoteVolume { get; set; }
|
||||
public int TradeCount { get; set; }
|
||||
public decimal TakerBuyBaseVolume { get; set; }
|
||||
public decimal TakerBuyQuoteVolume { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
|
||||
[BsonCollection("Fees")]
|
||||
public class FeeDto : Document
|
||||
{
|
||||
public decimal Cost { get; set; }
|
||||
public TradingExchanges Exchange { get; set; }
|
||||
public DateTime LastUpdate { get; set; }
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using Managing.Common;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
|
||||
[BsonCollection("FundingRates")]
|
||||
public class FundingRateDto : Document
|
||||
{
|
||||
public Enums.Ticker Ticker { get; set; }
|
||||
public decimal Rate { get; set; }
|
||||
public Enums.TradingExchanges Exchange { get; set; }
|
||||
public DateTime Date { get; set; }
|
||||
public Enums.TradeDirection Direction { get; set; }
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
||||
{
|
||||
[BsonCollection("GeneticRequests")]
|
||||
public class GeneticRequestDto : Document
|
||||
{
|
||||
public string RequestId { get; set; }
|
||||
public UserDto User { get; set; }
|
||||
public DateTime? CompletedAt { get; set; }
|
||||
public string Status { get; set; }
|
||||
public Ticker Ticker { get; set; }
|
||||
public Timeframe Timeframe { get; set; }
|
||||
public DateTime StartDate { get; set; }
|
||||
public DateTime EndDate { get; set; }
|
||||
public decimal Balance { get; set; }
|
||||
public int PopulationSize { get; set; }
|
||||
public int Generations { get; set; }
|
||||
public double MutationRate { get; set; }
|
||||
public GeneticSelectionMethod SelectionMethod { get; set; }
|
||||
public GeneticCrossoverMethod CrossoverMethod { get; set; }
|
||||
public GeneticMutationMethod MutationMethod { get; set; }
|
||||
public int ElitismPercentage { get; set; }
|
||||
public double MaxTakeProfit { get; set; }
|
||||
public List<IndicatorType> EligibleIndicators { get; set; } = new();
|
||||
public double? BestFitness { get; set; }
|
||||
public string? BestIndividual { get; set; }
|
||||
public string? ErrorMessage { get; set; }
|
||||
public string? ProgressInfo { get; set; }
|
||||
public string? BestChromosome { get; set; }
|
||||
public double? BestFitnessSoFar { get; set; }
|
||||
public int CurrentGeneration { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
||||
{
|
||||
[BsonCollection("Indicators")]
|
||||
public class IndicatorDto : Document
|
||||
{
|
||||
public IndicatorType Type { get; set; }
|
||||
public Timeframe Timeframe { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int MinimumHistory { get; set; }
|
||||
public int? Period { get; set; }
|
||||
public int? FastPeriods { get; set; }
|
||||
public int? SlowPeriods { get; set; }
|
||||
public int? SignalPeriods { get; set; }
|
||||
public double? Multiplier { get; set; }
|
||||
public int? StochPeriods { get; set; }
|
||||
public int? SmoothPeriods { get; set; }
|
||||
public int? CyclePeriods { get; set; }
|
||||
public SignalType SignalType { get; set; }
|
||||
public UserDto User { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
||||
{
|
||||
[BsonCollection("MoneyManagement")]
|
||||
public class MoneyManagementDto : Document
|
||||
{
|
||||
public Timeframe Timeframe { get; set; }
|
||||
public RiskLevel RiskLevel { get; set; }
|
||||
public decimal BalanceAtRisk { get; set; }
|
||||
public decimal StopLoss { get; set; }
|
||||
public decimal TakeProfit { get; set; }
|
||||
public decimal Leverage { get; set; }
|
||||
public string Name { get; internal set; }
|
||||
public UserDto User { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
||||
{
|
||||
[BsonCollection("Positions")]
|
||||
public class PositionDto : Document
|
||||
{
|
||||
[BsonDateTimeOptions]
|
||||
public DateTime Date { get; set; }
|
||||
public TradeDto Open { get; set; }
|
||||
public TradeDto StopLoss { get; set; }
|
||||
public TradeDto TakeProfit1 { get; set; }
|
||||
public TradeDto TakeProfit2 { get; set; }
|
||||
public decimal ProfitAndLoss { get; set; }
|
||||
public TradeDirection OriginDirection { get; set; }
|
||||
public string Identifier { get; set; }
|
||||
public PositionStatus Status { get; set; }
|
||||
public Ticker Ticker { get; set; }
|
||||
public string SignalIdentifier { get; set; }
|
||||
public string AccountName { get; set; }
|
||||
public MoneyManagementDto MoneyManagement { get; set; }
|
||||
public PositionInitiator Initiator { get; set; }
|
||||
public UserDto User { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
||||
{
|
||||
public class RiskManagementDto
|
||||
{
|
||||
public decimal AdverseProbabilityThreshold { get; set; }
|
||||
public decimal FavorableProbabilityThreshold { get; set; }
|
||||
public decimal RiskAversion { get; set; }
|
||||
public decimal KellyMinimumThreshold { get; set; }
|
||||
public decimal KellyMaximumCap { get; set; }
|
||||
public decimal MaxLiquidationProbability { get; set; }
|
||||
public int SignalValidationTimeHorizonHours { get; set; }
|
||||
public int PositionMonitoringTimeHorizonHours { get; set; }
|
||||
public decimal PositionWarningThreshold { get; set; }
|
||||
public decimal PositionAutoCloseThreshold { get; set; }
|
||||
public decimal KellyFractionalMultiplier { get; set; }
|
||||
public RiskToleranceLevel RiskTolerance { get; set; }
|
||||
public bool UseExpectedUtility { get; set; }
|
||||
public bool UseKellyCriterion { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
||||
{
|
||||
[BsonCollection("Scenarios")]
|
||||
public class ScenarioDto : Document
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public List<IndicatorDto> Indicators { get; set; }
|
||||
public int LoopbackPeriod { get; set; }
|
||||
public UserDto User { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
||||
{
|
||||
[BsonCollection("Signals")]
|
||||
public class SignalDto : Document
|
||||
{
|
||||
public TradeDirection Direction { get; set; }
|
||||
public Confidence Confidence { get; set; }
|
||||
public DateTime Date { get; set; }
|
||||
public CandleDto Candle { get; set; }
|
||||
public string Identifier { get; set; }
|
||||
public Ticker Ticker { get; set; }
|
||||
public SignalStatus Status { get; set; }
|
||||
public Timeframe Timeframe { get; set; }
|
||||
public IndicatorType Type { get; set; }
|
||||
public SignalType SignalType { get; set; }
|
||||
public UserDto User { get; set; }
|
||||
public string IndicatorName { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
|
||||
[BsonCollection("SpotlighOverview")]
|
||||
public class SpotlighOverviewDto : Document
|
||||
{
|
||||
public List<SpotlightDto> Spotlights { get; set; }
|
||||
public DateTime DateTime { get; set; }
|
||||
public Guid Identifier { get; set; }
|
||||
public int ScenarioCount { get; set; }
|
||||
}
|
||||
|
||||
public class SpotlightDto
|
||||
{
|
||||
public ScenarioDto Scenario { get; set; }
|
||||
public List<TickerSignalDto> TickerSignals { get; set; }
|
||||
}
|
||||
|
||||
public class TickerSignalDto
|
||||
{
|
||||
public Ticker Ticker { get; set; }
|
||||
public List<SignalDto> FiveMinutes { get; set; }
|
||||
public List<SignalDto> FifteenMinutes { get; set; }
|
||||
public List<SignalDto> OneHour { get; set; }
|
||||
public List<SignalDto> FourHour { get; set; }
|
||||
public List<SignalDto> OneDay { get; set; }
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
|
||||
/// <summary>
|
||||
/// MongoDB DTO for storing Synth miners leaderboard data
|
||||
/// </summary>
|
||||
[BsonCollection("SynthMinersLeaderboard")]
|
||||
public class SynthMinersLeaderboardDto : Document
|
||||
{
|
||||
/// <summary>
|
||||
/// Asset symbol (e.g., "BTC", "ETH")
|
||||
/// </summary>
|
||||
public string Asset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Time increment used for this leaderboard data
|
||||
/// </summary>
|
||||
public int TimeIncrement { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Signal date for which this leaderboard was retrieved (for backtests)
|
||||
/// Null for live trading data
|
||||
/// </summary>
|
||||
public DateTime? SignalDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this is backtest data or live data
|
||||
/// </summary>
|
||||
public bool IsBacktest { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Serialized JSON of miners list
|
||||
/// </summary>
|
||||
public string MinersData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cache key for quick lookup
|
||||
/// </summary>
|
||||
public string CacheKey { get; set; }
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
|
||||
/// <summary>
|
||||
/// MongoDB DTO for storing Synth miners predictions data
|
||||
/// </summary>
|
||||
[BsonCollection("SynthMinersPredictions")]
|
||||
public class SynthMinersPredictionsDto : Document
|
||||
{
|
||||
/// <summary>
|
||||
/// Asset symbol (e.g., "BTC", "ETH")
|
||||
/// </summary>
|
||||
public string Asset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Time increment used for these predictions
|
||||
/// </summary>
|
||||
public int TimeIncrement { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Time length (horizon) for these predictions in seconds
|
||||
/// </summary>
|
||||
public int TimeLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Signal date for which these predictions were retrieved (for backtests)
|
||||
/// Null for live trading data
|
||||
/// </summary>
|
||||
public DateTime? SignalDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this is backtest data or live data
|
||||
/// </summary>
|
||||
public bool IsBacktest { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Serialized JSON of miner UIDs list
|
||||
/// </summary>
|
||||
public string MinerUidsData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Serialized JSON of predictions data
|
||||
/// </summary>
|
||||
public string PredictionsData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When this prediction data was fetched from the API
|
||||
/// </summary>
|
||||
public DateTime FetchedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cache key for quick lookup
|
||||
/// </summary>
|
||||
public string CacheKey { get; set; }
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
|
||||
/// <summary>
|
||||
/// MongoDB DTO for storing individual Synth miner prediction data
|
||||
/// </summary>
|
||||
[BsonCollection("SynthPredictions")]
|
||||
public class SynthPredictionDto : Document
|
||||
{
|
||||
/// <summary>
|
||||
/// Asset symbol (e.g., "BTC", "ETH")
|
||||
/// </summary>
|
||||
public string Asset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Miner UID that provided this prediction
|
||||
/// </summary>
|
||||
public int MinerUid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Time increment used for this prediction
|
||||
/// </summary>
|
||||
public int TimeIncrement { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Time length (horizon) for this prediction in seconds
|
||||
/// </summary>
|
||||
public int TimeLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Signal date for which this prediction was retrieved (for backtests)
|
||||
/// Null for live trading data
|
||||
/// </summary>
|
||||
public DateTime? SignalDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this is backtest data or live data
|
||||
/// </summary>
|
||||
public bool IsBacktest { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Serialized JSON of the prediction data
|
||||
/// </summary>
|
||||
public string PredictionData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cache key for quick lookup
|
||||
/// </summary>
|
||||
public string CacheKey { get; set; }
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
|
||||
[BsonCollection("TopVolumeTickers")]
|
||||
public class TopVolumeTickerDto : Document
|
||||
{
|
||||
public Ticker Ticker { get; set; }
|
||||
public DateTime Date { get; set; }
|
||||
public decimal Volume { get; set; }
|
||||
public int Rank { get; set; }
|
||||
public TradingExchanges Exchange { get; set; }
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
||||
{
|
||||
[BsonCollection("Trades")]
|
||||
public class TradeDto : Document
|
||||
{
|
||||
[BsonDateTimeOptions]
|
||||
public DateTime Date { get; set; }
|
||||
public TradeDirection Direction { get; set; }
|
||||
public TradeStatus Status { get; set; }
|
||||
public TradeType TradeType { get; set; }
|
||||
public Ticker Ticker { get; set; }
|
||||
public decimal Fee { get; set; }
|
||||
public decimal Quantity { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
public decimal Leverage { get; set; }
|
||||
public string ExchangeOrderId { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections
|
||||
{
|
||||
public class TradingBotConfigDto
|
||||
{
|
||||
public string AccountName { get; set; }
|
||||
public MoneyManagementDto MoneyManagement { get; set; }
|
||||
public Ticker Ticker { get; set; }
|
||||
public Timeframe Timeframe { get; set; }
|
||||
public bool IsForWatchingOnly { get; set; }
|
||||
public decimal BotTradingBalance { get; set; }
|
||||
public bool IsForBacktest { get; set; }
|
||||
public int CooldownPeriod { get; set; }
|
||||
public int MaxLossStreak { get; set; }
|
||||
public bool FlipPosition { get; set; }
|
||||
public string Name { get; set; }
|
||||
public RiskManagementDto RiskManagement { get; set; }
|
||||
public ScenarioDto Scenario { get; set; }
|
||||
public string ScenarioName { get; set; }
|
||||
public decimal? MaxPositionTimeHours { get; set; }
|
||||
public bool CloseEarlyWhenProfitable { get; set; }
|
||||
public bool FlipOnlyWhenInProfit { get; set; }
|
||||
public bool UseSynthApi { get; set; }
|
||||
public bool UseForPositionSizing { get; set; }
|
||||
public bool UseForSignalFiltering { get; set; }
|
||||
public bool UseForDynamicStopLoss { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
|
||||
[BsonCollection("Users")]
|
||||
public class UserDto : Document
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string AgentName { get; set; }
|
||||
public string AvatarUrl { get; set; }
|
||||
public string TelegramChannel { get; set; }
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
|
||||
[BsonCollection("Workers")]
|
||||
public class WorkerDto : Document
|
||||
{
|
||||
public WorkerType WorkerType { get; set; }
|
||||
public DateTime StartTime { get; set; }
|
||||
public DateTime? LastRunTime { get; set; }
|
||||
public int ExecutionCount { get; set; }
|
||||
public TimeSpan Delay { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using Managing.Domain.Workflows.Synthetics;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
|
||||
[BsonCollection("Workflow")]
|
||||
public class WorkflowDto : Document
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public WorkflowUsage Usage { get; set; }
|
||||
public List<SyntheticFlow> Flows { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Configurations
|
||||
{
|
||||
public abstract class Document : IDocument
|
||||
{
|
||||
[BsonId]
|
||||
public ObjectId Id { get; set; }
|
||||
[BsonDateTimeOptions]
|
||||
public DateTime CreatedAt => Id.CreationTime;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Configurations
|
||||
{
|
||||
public interface IDocument
|
||||
{
|
||||
[BsonId]
|
||||
[BsonRepresentation(BsonType.String)]
|
||||
ObjectId Id { get; set; }
|
||||
|
||||
DateTime CreatedAt { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Configurations
|
||||
{
|
||||
public interface IManagingDatabaseSettings
|
||||
{
|
||||
string ConnectionString { get; set; }
|
||||
string DatabaseName { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
|
||||
public class ManagingDatabaseSettings : IManagingDatabaseSettings
|
||||
{
|
||||
public string ConnectionString { get; set; }
|
||||
public string DatabaseName { get; set; }
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
using Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb;
|
||||
|
||||
/// <summary>
|
||||
/// Service responsible for creating and managing MongoDB indexes
|
||||
/// </summary>
|
||||
public class IndexService
|
||||
{
|
||||
private readonly IMongoDatabase _database;
|
||||
private readonly ILogger<IndexService> _logger;
|
||||
|
||||
public IndexService(
|
||||
IOptions<ManagingDatabaseSettings> databaseSettings,
|
||||
ILogger<IndexService> logger)
|
||||
{
|
||||
var settings = databaseSettings.Value;
|
||||
var client = new MongoClient(settings.ConnectionString);
|
||||
_database = client.GetDatabase(settings.DatabaseName);
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates all necessary indexes for the application
|
||||
/// </summary>
|
||||
public async Task CreateIndexesAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Creating MongoDB indexes...");
|
||||
|
||||
// Create indexes for BacktestDto
|
||||
await CreateBacktestIndexesAsync();
|
||||
|
||||
await CreateBundleBacktestIndexesAsync();
|
||||
|
||||
// Create indexes for GeneticRequestDto
|
||||
await CreateGeneticRequestIndexesAsync();
|
||||
|
||||
_logger.LogInformation("MongoDB indexes created successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to create MongoDB indexes");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates indexes for the BacktestDto collection
|
||||
/// </summary>
|
||||
private async Task CreateBacktestIndexesAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var collection = _database.GetCollection<BacktestDto>("Backtests");
|
||||
|
||||
// Create index on RequestId for faster queries
|
||||
var requestIdIndexKeys = Builders<BacktestDto>.IndexKeys.Ascending(b => b.RequestId);
|
||||
var requestIdIndexOptions = new CreateIndexOptions { Name = "RequestId_Index" };
|
||||
var requestIdIndexModel = new CreateIndexModel<BacktestDto>(requestIdIndexKeys, requestIdIndexOptions);
|
||||
|
||||
// Create index (MongoDB will ignore if it already exists)
|
||||
await collection.Indexes.CreateOneAsync(requestIdIndexModel);
|
||||
|
||||
_logger.LogInformation("Backtest RequestId index created successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to create Backtest indexes");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CreateBundleBacktestIndexesAsync()
|
||||
{
|
||||
var bundleCollection = _database.GetCollection<BundleBacktestRequestDto>("BundleBacktestRequests");
|
||||
// Index on RequestId (unique)
|
||||
var requestIdIndex = Builders<BundleBacktestRequestDto>.IndexKeys.Ascending(b => b.RequestId);
|
||||
await bundleCollection.Indexes.CreateOneAsync(new CreateIndexModel<BundleBacktestRequestDto>(requestIdIndex, new CreateIndexOptions { Unique = true }));
|
||||
// Index on User.Name (non-unique)
|
||||
var userNameIndex = Builders<BundleBacktestRequestDto>.IndexKeys.Ascending("User.Name");
|
||||
await bundleCollection.Indexes.CreateOneAsync(new CreateIndexModel<BundleBacktestRequestDto>(userNameIndex));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates indexes for the GeneticRequestDto collection
|
||||
/// </summary>
|
||||
private async Task CreateGeneticRequestIndexesAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var collection = _database.GetCollection<GeneticRequestDto>("GeneticRequests");
|
||||
|
||||
// Create index on RequestId for faster queries
|
||||
var requestIdIndexKeys = Builders<GeneticRequestDto>.IndexKeys.Ascending(gr => gr.RequestId);
|
||||
var requestIdIndexOptions = new CreateIndexOptions { Name = "RequestId_Index" };
|
||||
var requestIdIndexModel = new CreateIndexModel<GeneticRequestDto>(requestIdIndexKeys, requestIdIndexOptions);
|
||||
|
||||
// Create index on User.Name for user-specific queries
|
||||
var userIndexKeys = Builders<GeneticRequestDto>.IndexKeys.Ascending("User.Name");
|
||||
var userIndexOptions = new CreateIndexOptions { Name = "User_Name_Index" };
|
||||
var userIndexModel = new CreateIndexModel<GeneticRequestDto>(userIndexKeys, userIndexOptions);
|
||||
|
||||
// Create index on Status for filtering by status
|
||||
var statusIndexKeys = Builders<GeneticRequestDto>.IndexKeys.Ascending(gr => gr.Status);
|
||||
var statusIndexOptions = new CreateIndexOptions { Name = "Status_Index" };
|
||||
var statusIndexModel = new CreateIndexModel<GeneticRequestDto>(statusIndexKeys, statusIndexOptions);
|
||||
|
||||
// Create indexes (MongoDB will ignore if they already exist)
|
||||
await collection.Indexes.CreateManyAsync(new[]
|
||||
{
|
||||
requestIdIndexModel,
|
||||
userIndexModel,
|
||||
statusIndexModel
|
||||
});
|
||||
|
||||
_logger.LogInformation("GeneticRequest indexes created successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to create GeneticRequest indexes");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb
|
||||
{
|
||||
public static class MongoHelpers
|
||||
{
|
||||
public static async Task EnsureIndexExists(this IMongoDatabase database, string collectionName, string indexName)
|
||||
{
|
||||
var collection = database.GetCollection<BsonDocument>(collectionName);
|
||||
var index = new BsonDocument
|
||||
{
|
||||
{indexName, 1}
|
||||
};
|
||||
|
||||
var indexModel = new CreateIndexModel<BsonDocument>(index, new CreateIndexOptions { Unique = true });
|
||||
await collection.Indexes.CreateOneAsync(indexModel).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,183 +0,0 @@
|
||||
using System.Linq.Expressions;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Abstractions;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.MongoDb
|
||||
{
|
||||
public class MongoRepository<TDocument> : IMongoRepository<TDocument>
|
||||
where TDocument : IDocument
|
||||
{
|
||||
private readonly IMongoCollection<TDocument> _collection;
|
||||
private readonly IMongoDatabase _database;
|
||||
|
||||
public MongoRepository(IManagingDatabaseSettings settings)
|
||||
{
|
||||
_database = new MongoClient(settings.ConnectionString).GetDatabase(settings.DatabaseName);
|
||||
_collection = _database.GetCollection<TDocument>(GetCollectionName(typeof(TDocument)));
|
||||
}
|
||||
|
||||
private protected string GetCollectionName(Type documentType)
|
||||
{
|
||||
return ((BsonCollectionAttribute)documentType.GetCustomAttributes(
|
||||
typeof(BsonCollectionAttribute),
|
||||
true)
|
||||
.FirstOrDefault())?.CollectionName;
|
||||
}
|
||||
|
||||
public virtual IQueryable<TDocument> AsQueryable()
|
||||
{
|
||||
return _collection.AsQueryable();
|
||||
}
|
||||
|
||||
public virtual IEnumerable<TDocument> FilterBy(
|
||||
Expression<Func<TDocument, bool>> filterExpression)
|
||||
{
|
||||
return _collection.Find(filterExpression).ToEnumerable();
|
||||
}
|
||||
|
||||
public virtual IEnumerable<TDocument> FilterBy(FilterDefinition<TDocument> filter)
|
||||
{
|
||||
return _collection.Find(filter).ToEnumerable();
|
||||
}
|
||||
|
||||
public virtual IEnumerable<TDocument> FindAll()
|
||||
{
|
||||
return _collection.Find(_ => true).ToEnumerable();
|
||||
}
|
||||
|
||||
public virtual IEnumerable<TProjected> FilterBy<TProjected>(
|
||||
Expression<Func<TDocument, bool>> filterExpression,
|
||||
Expression<Func<TDocument, TProjected>> projectionExpression)
|
||||
{
|
||||
return _collection.Find(filterExpression).Project(projectionExpression).ToEnumerable();
|
||||
}
|
||||
|
||||
public virtual TDocument FindOne(Expression<Func<TDocument, bool>> filterExpression)
|
||||
{
|
||||
return _collection.Find(filterExpression).FirstOrDefault();
|
||||
}
|
||||
|
||||
public virtual Task<TDocument> FindOneAsync(Expression<Func<TDocument, bool>> filterExpression)
|
||||
{
|
||||
return Task.Run(() => _collection.Find(filterExpression).FirstOrDefaultAsync());
|
||||
}
|
||||
|
||||
public virtual TDocument FindById(string id)
|
||||
{
|
||||
var objectId = new ObjectId(id);
|
||||
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, objectId);
|
||||
return _collection.Find(filter).SingleOrDefault();
|
||||
}
|
||||
|
||||
public virtual Task<TDocument> FindByIdAsync(string id)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var objectId = new ObjectId(id);
|
||||
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, objectId);
|
||||
return _collection.Find(filter).SingleOrDefaultAsync();
|
||||
});
|
||||
}
|
||||
|
||||
public virtual void InsertOne(TDocument document)
|
||||
{
|
||||
_collection.InsertOne(document);
|
||||
}
|
||||
|
||||
public virtual Task InsertOneAsync(TDocument document)
|
||||
{
|
||||
return Task.Run(() => _collection.InsertOneAsync(document));
|
||||
}
|
||||
|
||||
public void InsertMany(ICollection<TDocument> documents)
|
||||
{
|
||||
_collection.InsertMany(documents);
|
||||
}
|
||||
|
||||
public void DropCollection()
|
||||
{
|
||||
_database.DropCollection(GetCollectionName(typeof(TDocument)));
|
||||
}
|
||||
|
||||
public virtual async Task InsertManyAsync(ICollection<TDocument> documents)
|
||||
{
|
||||
await _collection.InsertManyAsync(documents);
|
||||
}
|
||||
|
||||
public void ReplaceOne(TDocument document)
|
||||
{
|
||||
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, document.Id);
|
||||
_collection.FindOneAndReplace(filter, document);
|
||||
}
|
||||
|
||||
public virtual async Task ReplaceOneAsync(TDocument document)
|
||||
{
|
||||
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, document.Id);
|
||||
await _collection.FindOneAndReplaceAsync(filter, document);
|
||||
}
|
||||
|
||||
public virtual void Update(TDocument entity)
|
||||
{
|
||||
if (entity.Id == ObjectId.Empty)
|
||||
{
|
||||
entity.Id = ObjectId.GenerateNewId();
|
||||
}
|
||||
|
||||
var option = new ReplaceOptions { IsUpsert = true };
|
||||
_collection.ReplaceOne(x => entity != null && x.Id == entity.Id, entity, option);
|
||||
}
|
||||
|
||||
public virtual void DeleteOne(Expression<Func<TDocument, bool>> filterExpression)
|
||||
{
|
||||
_collection.FindOneAndDelete(filterExpression);
|
||||
}
|
||||
|
||||
public virtual Task DeleteOneAsync(Expression<Func<TDocument, bool>> filterExpression)
|
||||
{
|
||||
return Task.Run(() => _collection.FindOneAndDeleteAsync(filterExpression));
|
||||
}
|
||||
|
||||
public virtual void DeleteById(string id)
|
||||
{
|
||||
var objectId = new ObjectId(id);
|
||||
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, objectId);
|
||||
_collection.FindOneAndDelete(filter);
|
||||
}
|
||||
|
||||
public virtual Task DeleteByIdAsync(string id)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var objectId = new ObjectId(id);
|
||||
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, objectId);
|
||||
_collection.FindOneAndDeleteAsync(filter);
|
||||
});
|
||||
}
|
||||
|
||||
public virtual void DeleteMany(Expression<Func<TDocument, bool>> filterExpression)
|
||||
{
|
||||
_collection.DeleteMany(filterExpression);
|
||||
}
|
||||
|
||||
public virtual Task DeleteManyAsync(Expression<Func<TDocument, bool>> filterExpression)
|
||||
{
|
||||
return _collection.DeleteManyAsync(filterExpression);
|
||||
}
|
||||
|
||||
public virtual void CreateIndex(string column)
|
||||
{
|
||||
var keys = Builders<TDocument>.IndexKeys.Ascending(column);
|
||||
var indexOptions = new CreateIndexOptions { Unique = true };
|
||||
var model = new CreateIndexModel<TDocument>(keys, indexOptions);
|
||||
_collection.Indexes.CreateOne(model);
|
||||
}
|
||||
|
||||
public IMongoCollection<TDocument> GetCollection()
|
||||
{
|
||||
return _collection;
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,11 @@
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql.Configurations;
|
||||
|
||||
public class PostgreSqlSettings : IPostgreSqlSettings
|
||||
{
|
||||
public string ConnectionString { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public interface IPostgreSqlSettings
|
||||
{
|
||||
string ConnectionString { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql;
|
||||
|
||||
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<ManagingDbContext>
|
||||
{
|
||||
public ManagingDbContext CreateDbContext(string[] args)
|
||||
{
|
||||
var optionsBuilder = new DbContextOptionsBuilder<ManagingDbContext>();
|
||||
|
||||
// Use a default connection string for design-time migrations
|
||||
// This should match your local PostgreSQL setup
|
||||
optionsBuilder.UseNpgsql("Host=localhost;Port=5432;Database=managing;Username=postgres;Password=postgres");
|
||||
|
||||
return new ManagingDbContext(optionsBuilder.Options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
|
||||
public class AccountEntity
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public TradingExchanges Exchange { get; set; }
|
||||
public AccountType Type { get; set; }
|
||||
public string? Key { get; set; }
|
||||
public string? Secret { get; set; }
|
||||
public int? UserId { get; set; }
|
||||
|
||||
// Navigation properties
|
||||
public UserEntity? User { get; set; }
|
||||
|
||||
// Store balances as JSON if needed, or skip them entirely
|
||||
// public string? BalancesJson { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
|
||||
[Table("Backtests")]
|
||||
public class BacktestEntity
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string Identifier { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string RequestId { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
[Column(TypeName = "decimal(18,8)")]
|
||||
public decimal FinalPnl { get; set; }
|
||||
|
||||
[Required]
|
||||
public int WinRate { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column(TypeName = "decimal(18,8)")]
|
||||
public decimal GrowthPercentage { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column(TypeName = "decimal(18,8)")]
|
||||
public decimal HodlPercentage { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column(TypeName = "jsonb")]
|
||||
public string ConfigJson { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
[Column(TypeName = "jsonb")]
|
||||
public string PositionsJson { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
[Column(TypeName = "jsonb")]
|
||||
public string SignalsJson { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
public DateTime StartDate { get; set; }
|
||||
|
||||
[Required]
|
||||
public DateTime EndDate { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column(TypeName = "jsonb")]
|
||||
public string MoneyManagementJson { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string UserName { get; set; } = string.Empty;
|
||||
|
||||
[Column(TypeName = "jsonb")]
|
||||
public string? StatisticsJson { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column(TypeName = "decimal(18,8)")]
|
||||
public decimal Fees { get; set; }
|
||||
|
||||
[Required]
|
||||
public double Score { get; set; }
|
||||
|
||||
[Column(TypeName = "text")]
|
||||
public string ScoreMessage { get; set; } = string.Empty;
|
||||
|
||||
[Column(TypeName = "text")]
|
||||
public string? Metadata { get; set; }
|
||||
|
||||
[Required]
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
[Required]
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
|
||||
[Table("BotBackups")]
|
||||
public class BotBackupEntity
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string Identifier { get; set; }
|
||||
|
||||
[MaxLength(255)]
|
||||
public string? UserName { get; set; }
|
||||
|
||||
public int? UserId { get; set; }
|
||||
|
||||
// Navigation properties
|
||||
[ForeignKey("UserId")]
|
||||
public UserEntity? User { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Bot configuration and state data stored as JSON string
|
||||
/// </summary>
|
||||
[Column(TypeName = "text")]
|
||||
public string Data { get; set; }
|
||||
|
||||
public BotStatus LastStatus { get; set; }
|
||||
|
||||
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Managing.Domain.Backtests;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
|
||||
[Table("BundleBacktestRequests")]
|
||||
public class BundleBacktestRequestEntity
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string RequestId { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string UserName { get; set; } = string.Empty;
|
||||
|
||||
// Foreign key to User entity
|
||||
public int? UserId { get; set; }
|
||||
|
||||
// Navigation property to User entity
|
||||
public UserEntity? User { get; set; }
|
||||
|
||||
[Required]
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public DateTime? CompletedAt { get; set; }
|
||||
|
||||
[Required]
|
||||
public BundleBacktestRequestStatus Status { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column(TypeName = "text")]
|
||||
public string BacktestRequestsJson { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
public int TotalBacktests { get; set; }
|
||||
|
||||
[Required]
|
||||
public int CompletedBacktests { get; set; }
|
||||
|
||||
[Required]
|
||||
public int FailedBacktests { get; set; }
|
||||
|
||||
[Column(TypeName = "text")]
|
||||
public string? ErrorMessage { get; set; }
|
||||
|
||||
[Column(TypeName = "text")]
|
||||
public string? ProgressInfo { get; set; }
|
||||
|
||||
[MaxLength(500)]
|
||||
public string? CurrentBacktest { get; set; }
|
||||
|
||||
public int? EstimatedTimeRemainingSeconds { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
[Column(TypeName = "jsonb")]
|
||||
public string ResultsJson { get; set; } = "[]";
|
||||
|
||||
[Required]
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
|
||||
[Table("FundingRates")]
|
||||
public class FundingRateEntity
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
public Ticker Ticker { get; set; }
|
||||
public TradingExchanges Exchange { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,8)")]
|
||||
public decimal Rate { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,8)")]
|
||||
public decimal OpenInterest { get; set; }
|
||||
|
||||
public DateTime Date { get; set; }
|
||||
public TradeDirection Direction { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
|
||||
public class GeneticRequestEntity
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string RequestId { get; set; }
|
||||
public int? UserId { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? CompletedAt { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public string Status { get; set; } // GeneticRequestStatus as string
|
||||
public Ticker Ticker { get; set; }
|
||||
public Timeframe Timeframe { get; set; }
|
||||
public DateTime StartDate { get; set; }
|
||||
public DateTime EndDate { get; set; }
|
||||
public decimal Balance { get; set; }
|
||||
public int PopulationSize { get; set; }
|
||||
public int Generations { get; set; }
|
||||
public double MutationRate { get; set; }
|
||||
public GeneticSelectionMethod SelectionMethod { get; set; }
|
||||
public GeneticCrossoverMethod CrossoverMethod { get; set; }
|
||||
public GeneticMutationMethod MutationMethod { get; set; }
|
||||
public int ElitismPercentage { get; set; }
|
||||
public double MaxTakeProfit { get; set; }
|
||||
public string? EligibleIndicatorsJson { get; set; } // Store List<IndicatorType> as JSON
|
||||
public double? BestFitness { get; set; }
|
||||
public string? BestIndividual { get; set; }
|
||||
public string? ErrorMessage { get; set; }
|
||||
public string? ProgressInfo { get; set; }
|
||||
public string? BestChromosome { get; set; }
|
||||
public double? BestFitnessSoFar { get; set; }
|
||||
public int CurrentGeneration { get; set; }
|
||||
|
||||
// Navigation properties
|
||||
public UserEntity? User { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
|
||||
[Table("Indicators")]
|
||||
public class IndicatorEntity
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string Name { get; set; }
|
||||
|
||||
public IndicatorType Type { get; set; }
|
||||
public Timeframe Timeframe { get; set; }
|
||||
public SignalType SignalType { get; set; }
|
||||
|
||||
public int MinimumHistory { get; set; }
|
||||
public int? Period { get; set; }
|
||||
public int? FastPeriods { get; set; }
|
||||
public int? SlowPeriods { get; set; }
|
||||
public int? SignalPeriods { get; set; }
|
||||
public double? Multiplier { get; set; }
|
||||
public int? StochPeriods { get; set; }
|
||||
public int? SmoothPeriods { get; set; }
|
||||
public int? CyclePeriods { get; set; }
|
||||
|
||||
[MaxLength(255)]
|
||||
public string? UserName { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
// Navigation property for the many-to-many relationship with scenarios
|
||||
public virtual ICollection<ScenarioIndicatorEntity> ScenarioIndicators { get; set; } = new List<ScenarioIndicatorEntity>();
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
|
||||
[Table("MoneyManagements")]
|
||||
public class MoneyManagementEntity
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Required]
|
||||
public Timeframe Timeframe { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column(TypeName = "decimal(18,8)")]
|
||||
public decimal StopLoss { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column(TypeName = "decimal(18,8)")]
|
||||
public decimal TakeProfit { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column(TypeName = "decimal(18,8)")]
|
||||
public decimal Leverage { get; set; }
|
||||
|
||||
[MaxLength(255)]
|
||||
public string? UserName { get; set; }
|
||||
|
||||
public int? UserId { get; set; }
|
||||
|
||||
// Navigation properties
|
||||
[ForeignKey("UserId")]
|
||||
public UserEntity? User { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
|
||||
[Table("Positions")]
|
||||
public class PositionEntity
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string Identifier { get; set; }
|
||||
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,8)")]
|
||||
public decimal ProfitAndLoss { get; set; }
|
||||
|
||||
public TradeDirection OriginDirection { get; set; }
|
||||
public PositionStatus Status { get; set; }
|
||||
public Ticker Ticker { get; set; }
|
||||
public PositionInitiator Initiator { get; set; }
|
||||
|
||||
[MaxLength(255)]
|
||||
public string SignalIdentifier { get; set; }
|
||||
|
||||
[MaxLength(255)]
|
||||
public string AccountName { get; set; }
|
||||
|
||||
[MaxLength(255)]
|
||||
public string? UserName { get; set; }
|
||||
|
||||
// Foreign keys to trades
|
||||
public int? OpenTradeId { get; set; }
|
||||
public int? StopLossTradeId { get; set; }
|
||||
public int? TakeProfit1TradeId { get; set; }
|
||||
public int? TakeProfit2TradeId { get; set; }
|
||||
|
||||
// Money management data stored as JSON
|
||||
[Column(TypeName = "text")]
|
||||
public string? MoneyManagementJson { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
// Navigation properties
|
||||
[ForeignKey("OpenTradeId")]
|
||||
public virtual TradeEntity? OpenTrade { get; set; }
|
||||
|
||||
[ForeignKey("StopLossTradeId")]
|
||||
public virtual TradeEntity? StopLossTrade { get; set; }
|
||||
|
||||
[ForeignKey("TakeProfit1TradeId")]
|
||||
public virtual TradeEntity? TakeProfit1Trade { get; set; }
|
||||
|
||||
[ForeignKey("TakeProfit2TradeId")]
|
||||
public virtual TradeEntity? TakeProfit2Trade { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
|
||||
[Table("Scenarios")]
|
||||
public class ScenarioEntity
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string Name { get; set; }
|
||||
|
||||
public int LoopbackPeriod { get; set; }
|
||||
|
||||
[MaxLength(255)]
|
||||
public string? UserName { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
// Navigation property for the many-to-many relationship with indicators
|
||||
public virtual ICollection<ScenarioIndicatorEntity> ScenarioIndicators { get; set; } = new List<ScenarioIndicatorEntity>();
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
|
||||
[Table("ScenarioIndicators")]
|
||||
public class ScenarioIndicatorEntity
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
public int ScenarioId { get; set; }
|
||||
public int IndicatorId { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
// Navigation properties
|
||||
[ForeignKey("ScenarioId")]
|
||||
public virtual ScenarioEntity Scenario { get; set; } = null!;
|
||||
|
||||
[ForeignKey("IndicatorId")]
|
||||
public virtual IndicatorEntity Indicator { get; set; } = null!;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
|
||||
[Table("Signals")]
|
||||
public class SignalEntity
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string Identifier { get; set; }
|
||||
|
||||
public TradeDirection Direction { get; set; }
|
||||
public Confidence Confidence { get; set; }
|
||||
public DateTime Date { get; set; }
|
||||
public Ticker Ticker { get; set; }
|
||||
public SignalStatus Status { get; set; }
|
||||
public Timeframe Timeframe { get; set; }
|
||||
public IndicatorType Type { get; set; }
|
||||
public SignalType SignalType { get; set; }
|
||||
|
||||
[MaxLength(255)]
|
||||
public string IndicatorName { get; set; }
|
||||
|
||||
[MaxLength(255)]
|
||||
public string? UserName { get; set; }
|
||||
|
||||
// Candle data stored as JSON
|
||||
[Column(TypeName = "text")]
|
||||
public string? CandleJson { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
|
||||
[Table("SpotlightOverviews")]
|
||||
public class SpotlightOverviewEntity
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
public Guid Identifier { get; set; }
|
||||
public DateTime DateTime { get; set; }
|
||||
public int ScenarioCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// JSON column containing the complex nested spotlights data
|
||||
/// This stores the List<SpotlightDto> as JSON to avoid complex normalization
|
||||
/// </summary>
|
||||
[Column(TypeName = "jsonb")]
|
||||
public string SpotlightsJson { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
|
||||
public class SynthMinersLeaderboardEntity
|
||||
{
|
||||
[Key]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(32)]
|
||||
public string Asset { get; set; }
|
||||
|
||||
[Required]
|
||||
public int TimeIncrement { get; set; }
|
||||
|
||||
public DateTime? SignalDate { get; set; }
|
||||
|
||||
[Required]
|
||||
public bool IsBacktest { get; set; }
|
||||
|
||||
[Column(TypeName = "jsonb")]
|
||||
public string MinersData { get; set; } // JSON serialized List<MinerInfo>
|
||||
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string CacheKey { get; set; }
|
||||
|
||||
[Required]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
|
||||
public class SynthPredictionEntity
|
||||
{
|
||||
[Key]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(32)]
|
||||
public string Asset { get; set; }
|
||||
|
||||
[Required]
|
||||
public int MinerUid { get; set; }
|
||||
|
||||
[Required]
|
||||
public int TimeIncrement { get; set; }
|
||||
|
||||
[Required]
|
||||
public int TimeLength { get; set; }
|
||||
|
||||
public DateTime? SignalDate { get; set; }
|
||||
|
||||
[Required]
|
||||
public bool IsBacktest { get; set; }
|
||||
|
||||
[Column(TypeName = "jsonb")]
|
||||
public string PredictionData { get; set; } // JSON serialized MinerPrediction
|
||||
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string CacheKey { get; set; }
|
||||
|
||||
[Required]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
|
||||
[Table("TopVolumeTickers")]
|
||||
public class TopVolumeTickerEntity
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
public Ticker Ticker { get; set; }
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,8)")]
|
||||
public decimal Volume { get; set; }
|
||||
|
||||
public int Rank { get; set; }
|
||||
public TradingExchanges Exchange { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
|
||||
[Table("Trades")]
|
||||
public class TradeEntity
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
public DateTime Date { get; set; }
|
||||
public TradeDirection Direction { get; set; }
|
||||
public TradeStatus Status { get; set; }
|
||||
public TradeType TradeType { get; set; }
|
||||
public Ticker Ticker { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,8)")]
|
||||
public decimal Fee { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,8)")]
|
||||
public decimal Quantity { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,8)")]
|
||||
public decimal Price { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,8)")]
|
||||
public decimal Leverage { get; set; }
|
||||
|
||||
[MaxLength(255)]
|
||||
public string? ExchangeOrderId { get; set; }
|
||||
|
||||
[Column(TypeName = "text")]
|
||||
public string? Message { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
|
||||
[Table("Traders")]
|
||||
public class TraderEntity
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string Address { get; set; }
|
||||
|
||||
public int Winrate { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,8)")]
|
||||
public decimal Pnl { get; set; }
|
||||
|
||||
public int TradeCount { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,8)")]
|
||||
public decimal AverageWin { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,8)")]
|
||||
public decimal AverageLoss { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,8)")]
|
||||
public decimal Roi { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this is a best trader (true) or bad trader (false)
|
||||
/// This allows us to use one table for both types
|
||||
/// </summary>
|
||||
public bool IsBestTrader { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
|
||||
public class UserEntity
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string? AgentName { get; set; }
|
||||
public string? AvatarUrl { get; set; }
|
||||
public string? TelegramChannel { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
|
||||
[Table("Workers")]
|
||||
public class WorkerEntity
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required]
|
||||
public WorkerType WorkerType { get; set; }
|
||||
|
||||
[Required]
|
||||
public DateTime StartTime { get; set; }
|
||||
|
||||
public DateTime? LastRunTime { get; set; }
|
||||
|
||||
[Required]
|
||||
public int ExecutionCount { get; set; }
|
||||
|
||||
[Required]
|
||||
public long DelayTicks { get; set; } // TimeSpan is not supported, store as ticks
|
||||
|
||||
[Required]
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,521 @@
|
||||
using Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql;
|
||||
|
||||
public class ManagingDbContext : DbContext
|
||||
{
|
||||
public ManagingDbContext(DbContextOptions<ManagingDbContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
|
||||
public DbSet<AccountEntity> Accounts { get; set; }
|
||||
public DbSet<UserEntity> Users { get; set; }
|
||||
public DbSet<GeneticRequestEntity> GeneticRequests { get; set; }
|
||||
public DbSet<BacktestEntity> Backtests { get; set; }
|
||||
public DbSet<BundleBacktestRequestEntity> BundleBacktestRequests { get; set; }
|
||||
|
||||
// Trading entities
|
||||
public DbSet<ScenarioEntity> Scenarios { get; set; }
|
||||
public DbSet<IndicatorEntity> Indicators { get; set; }
|
||||
public DbSet<ScenarioIndicatorEntity> ScenarioIndicators { get; set; }
|
||||
public DbSet<SignalEntity> Signals { get; set; }
|
||||
public DbSet<PositionEntity> Positions { get; set; }
|
||||
public DbSet<TradeEntity> Trades { get; set; }
|
||||
|
||||
|
||||
// Statistics entities
|
||||
public DbSet<TopVolumeTickerEntity> TopVolumeTickers { get; set; }
|
||||
public DbSet<SpotlightOverviewEntity> SpotlightOverviews { get; set; }
|
||||
public DbSet<TraderEntity> Traders { get; set; }
|
||||
public DbSet<FundingRateEntity> FundingRates { get; set; }
|
||||
|
||||
// Bot entities
|
||||
public DbSet<BotBackupEntity> BotBackups { get; set; }
|
||||
|
||||
// Settings entities
|
||||
public DbSet<MoneyManagementEntity> MoneyManagements { get; set; }
|
||||
|
||||
// Worker entities
|
||||
public DbSet<WorkerEntity> Workers { get; set; }
|
||||
|
||||
public DbSet<SynthMinersLeaderboardEntity> SynthMinersLeaderboards { get; set; }
|
||||
public DbSet<SynthPredictionEntity> SynthPredictions { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
// Configure Account entity
|
||||
modelBuilder.Entity<AccountEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Name).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.Key).HasMaxLength(500);
|
||||
entity.Property(e => e.Secret).HasMaxLength(500);
|
||||
entity.Property(e => e.Exchange)
|
||||
.IsRequired()
|
||||
.HasConversion<string>(); // Store enum as string
|
||||
entity.Property(e => e.Type)
|
||||
.IsRequired()
|
||||
.HasConversion<string>(); // Store enum as string
|
||||
|
||||
// Create unique index on account name
|
||||
entity.HasIndex(e => e.Name).IsUnique();
|
||||
entity.HasIndex(e => e.Key);
|
||||
|
||||
// Configure relationship with User
|
||||
entity.HasOne(e => e.User)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
});
|
||||
|
||||
// Configure User entity
|
||||
modelBuilder.Entity<UserEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Name).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.AgentName).HasMaxLength(255);
|
||||
entity.Property(e => e.AvatarUrl).HasMaxLength(500);
|
||||
entity.Property(e => e.TelegramChannel).HasMaxLength(255);
|
||||
});
|
||||
|
||||
// Configure GeneticRequest entity
|
||||
modelBuilder.Entity<GeneticRequestEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.RequestId).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.Status).IsRequired().HasMaxLength(50);
|
||||
entity.Property(e => e.Ticker)
|
||||
.IsRequired()
|
||||
.HasConversion<string>(); // Store enum as string
|
||||
entity.Property(e => e.Timeframe)
|
||||
.IsRequired()
|
||||
.HasConversion<string>(); // Store enum as string
|
||||
entity.Property(e => e.SelectionMethod)
|
||||
.IsRequired()
|
||||
.HasConversion<string>(); // Store enum as string
|
||||
entity.Property(e => e.CrossoverMethod)
|
||||
.IsRequired()
|
||||
.HasConversion<string>(); // Store enum as string
|
||||
entity.Property(e => e.MutationMethod)
|
||||
.IsRequired()
|
||||
.HasConversion<string>(); // Store enum as string
|
||||
entity.Property(e => e.Balance).HasColumnType("decimal(18,8)");
|
||||
entity.Property(e => e.BestIndividual).HasMaxLength(4000);
|
||||
entity.Property(e => e.ErrorMessage).HasMaxLength(2000);
|
||||
entity.Property(e => e.ProgressInfo).HasMaxLength(4000);
|
||||
entity.Property(e => e.BestChromosome).HasMaxLength(4000);
|
||||
entity.Property(e => e.EligibleIndicatorsJson).HasMaxLength(2000);
|
||||
|
||||
// Create indexes
|
||||
entity.HasIndex(e => e.RequestId).IsUnique();
|
||||
entity.HasIndex(e => e.Status);
|
||||
entity.HasIndex(e => e.CreatedAt);
|
||||
|
||||
// Configure relationship with User
|
||||
entity.HasOne(e => e.User)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
});
|
||||
|
||||
// Configure Backtest entity
|
||||
modelBuilder.Entity<BacktestEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Identifier).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.RequestId).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.UserName).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.FinalPnl).HasColumnType("decimal(18,8)");
|
||||
entity.Property(e => e.GrowthPercentage).HasColumnType("decimal(18,8)");
|
||||
entity.Property(e => e.HodlPercentage).HasColumnType("decimal(18,8)");
|
||||
entity.Property(e => e.Fees).HasColumnType("decimal(18,8)");
|
||||
entity.Property(e => e.ConfigJson).HasColumnType("jsonb");
|
||||
entity.Property(e => e.PositionsJson).HasColumnType("jsonb");
|
||||
entity.Property(e => e.SignalsJson).HasColumnType("jsonb");
|
||||
entity.Property(e => e.MoneyManagementJson).HasColumnType("jsonb");
|
||||
entity.Property(e => e.StatisticsJson).HasColumnType("jsonb");
|
||||
entity.Property(e => e.ScoreMessage).HasMaxLength(1000);
|
||||
entity.Property(e => e.Metadata).HasColumnType("text");
|
||||
|
||||
// Create indexes for common queries
|
||||
entity.HasIndex(e => e.Identifier).IsUnique();
|
||||
entity.HasIndex(e => e.RequestId);
|
||||
entity.HasIndex(e => e.UserName);
|
||||
entity.HasIndex(e => e.Score);
|
||||
entity.HasIndex(e => e.FinalPnl);
|
||||
entity.HasIndex(e => e.WinRate);
|
||||
entity.HasIndex(e => e.StartDate);
|
||||
entity.HasIndex(e => e.EndDate);
|
||||
entity.HasIndex(e => e.CreatedAt);
|
||||
|
||||
// Composite indexes for efficient pagination and filtering
|
||||
entity.HasIndex(e => new { e.UserName, e.Score });
|
||||
entity.HasIndex(e => new { e.RequestId, e.Score });
|
||||
});
|
||||
|
||||
// Configure BundleBacktestRequest entity
|
||||
modelBuilder.Entity<BundleBacktestRequestEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.RequestId).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.UserName).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.Name).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.Status)
|
||||
.IsRequired()
|
||||
.HasConversion<string>(); // Store enum as string
|
||||
entity.Property(e => e.BacktestRequestsJson).HasColumnType("text");
|
||||
entity.Property(e => e.ErrorMessage).HasColumnType("text");
|
||||
entity.Property(e => e.ProgressInfo).HasColumnType("text");
|
||||
entity.Property(e => e.CurrentBacktest).HasMaxLength(500);
|
||||
entity.Property(e => e.ResultsJson).HasColumnType("jsonb");
|
||||
|
||||
// Configure relationship with User
|
||||
entity.HasOne(e => e.User)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
// Create indexes for common queries
|
||||
entity.HasIndex(e => e.RequestId).IsUnique();
|
||||
entity.HasIndex(e => e.UserName);
|
||||
entity.HasIndex(e => e.Status);
|
||||
entity.HasIndex(e => e.CreatedAt);
|
||||
entity.HasIndex(e => e.CompletedAt);
|
||||
entity.HasIndex(e => e.UserId);
|
||||
|
||||
// Composite index for user queries ordered by creation date
|
||||
entity.HasIndex(e => new { e.UserName, e.CreatedAt });
|
||||
});
|
||||
|
||||
// Configure Scenario entity
|
||||
modelBuilder.Entity<ScenarioEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Name).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.UserName).HasMaxLength(255);
|
||||
|
||||
// Create indexes
|
||||
entity.HasIndex(e => e.Name);
|
||||
entity.HasIndex(e => e.UserName);
|
||||
entity.HasIndex(e => e.CreatedAt);
|
||||
|
||||
// Composite index for user scenarios
|
||||
entity.HasIndex(e => new { e.UserName, e.Name });
|
||||
});
|
||||
|
||||
// Configure Indicator entity
|
||||
modelBuilder.Entity<IndicatorEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Name).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.Type).IsRequired().HasConversion<string>();
|
||||
entity.Property(e => e.Timeframe).IsRequired().HasConversion<string>();
|
||||
entity.Property(e => e.SignalType).IsRequired().HasConversion<string>();
|
||||
entity.Property(e => e.UserName).HasMaxLength(255);
|
||||
|
||||
// Create indexes
|
||||
entity.HasIndex(e => e.Name);
|
||||
entity.HasIndex(e => e.Type);
|
||||
entity.HasIndex(e => e.UserName);
|
||||
entity.HasIndex(e => e.CreatedAt);
|
||||
|
||||
// Composite index for user indicators
|
||||
entity.HasIndex(e => new { e.UserName, e.Name });
|
||||
});
|
||||
|
||||
// Configure ScenarioIndicator junction table
|
||||
modelBuilder.Entity<ScenarioIndicatorEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
|
||||
// Configure relationships
|
||||
entity.HasOne(e => e.Scenario)
|
||||
.WithMany(s => s.ScenarioIndicators)
|
||||
.HasForeignKey(e => e.ScenarioId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
entity.HasOne(e => e.Indicator)
|
||||
.WithMany(i => i.ScenarioIndicators)
|
||||
.HasForeignKey(e => e.IndicatorId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
// Create indexes
|
||||
entity.HasIndex(e => e.ScenarioId);
|
||||
entity.HasIndex(e => e.IndicatorId);
|
||||
entity.HasIndex(e => new { e.ScenarioId, e.IndicatorId }).IsUnique();
|
||||
});
|
||||
|
||||
// Configure Signal entity
|
||||
modelBuilder.Entity<SignalEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Identifier).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.Direction).IsRequired().HasConversion<string>();
|
||||
entity.Property(e => e.Confidence).IsRequired().HasConversion<string>();
|
||||
entity.Property(e => e.Ticker).IsRequired().HasConversion<string>();
|
||||
entity.Property(e => e.Status).IsRequired().HasConversion<string>();
|
||||
entity.Property(e => e.Timeframe).IsRequired().HasConversion<string>();
|
||||
entity.Property(e => e.Type).IsRequired().HasConversion<string>();
|
||||
entity.Property(e => e.SignalType).IsRequired().HasConversion<string>();
|
||||
entity.Property(e => e.IndicatorName).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.UserName).HasMaxLength(255);
|
||||
entity.Property(e => e.CandleJson).HasColumnType("text");
|
||||
|
||||
// Create indexes
|
||||
entity.HasIndex(e => e.Identifier);
|
||||
entity.HasIndex(e => e.UserName);
|
||||
entity.HasIndex(e => e.Date);
|
||||
entity.HasIndex(e => e.Ticker);
|
||||
entity.HasIndex(e => e.Status);
|
||||
entity.HasIndex(e => e.CreatedAt);
|
||||
|
||||
// Composite indexes for common queries
|
||||
entity.HasIndex(e => new { e.UserName, e.Date });
|
||||
entity.HasIndex(e => new { e.Identifier, e.Date, e.UserName }).IsUnique();
|
||||
});
|
||||
|
||||
// Configure Position entity
|
||||
modelBuilder.Entity<PositionEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Identifier).IsRequired().HasMaxLength(255);
|
||||
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>();
|
||||
entity.Property(e => e.Ticker).IsRequired().HasConversion<string>();
|
||||
entity.Property(e => e.Initiator).IsRequired().HasConversion<string>();
|
||||
entity.Property(e => e.SignalIdentifier).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.AccountName).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.UserName).HasMaxLength(255);
|
||||
entity.Property(e => e.MoneyManagementJson).HasColumnType("text");
|
||||
|
||||
// Configure relationships with trades
|
||||
entity.HasOne(e => e.OpenTrade)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.OpenTradeId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
entity.HasOne(e => e.StopLossTrade)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.StopLossTradeId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
entity.HasOne(e => e.TakeProfit1Trade)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.TakeProfit1TradeId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
entity.HasOne(e => e.TakeProfit2Trade)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.TakeProfit2TradeId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
// Create indexes
|
||||
entity.HasIndex(e => e.Identifier).IsUnique();
|
||||
entity.HasIndex(e => e.UserName);
|
||||
entity.HasIndex(e => e.Status);
|
||||
entity.HasIndex(e => e.Initiator);
|
||||
entity.HasIndex(e => e.Date);
|
||||
entity.HasIndex(e => e.CreatedAt);
|
||||
|
||||
// Composite indexes
|
||||
entity.HasIndex(e => new { e.UserName, e.Identifier });
|
||||
});
|
||||
|
||||
// Configure Trade entity
|
||||
modelBuilder.Entity<TradeEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Direction).IsRequired().HasConversion<string>();
|
||||
entity.Property(e => e.Status).IsRequired().HasConversion<string>();
|
||||
entity.Property(e => e.TradeType).IsRequired().HasConversion<string>();
|
||||
entity.Property(e => e.Ticker).IsRequired().HasConversion<string>();
|
||||
entity.Property(e => e.Fee).HasColumnType("decimal(18,8)");
|
||||
entity.Property(e => e.Quantity).HasColumnType("decimal(18,8)");
|
||||
entity.Property(e => e.Price).HasColumnType("decimal(18,8)");
|
||||
entity.Property(e => e.Leverage).HasColumnType("decimal(18,8)");
|
||||
entity.Property(e => e.ExchangeOrderId).HasMaxLength(255);
|
||||
entity.Property(e => e.Message).HasColumnType("text");
|
||||
|
||||
// Create indexes
|
||||
entity.HasIndex(e => e.Date);
|
||||
entity.HasIndex(e => e.Status);
|
||||
entity.HasIndex(e => e.ExchangeOrderId);
|
||||
entity.HasIndex(e => e.CreatedAt);
|
||||
});
|
||||
|
||||
|
||||
|
||||
// Configure TopVolumeTicker entity
|
||||
modelBuilder.Entity<TopVolumeTickerEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Volume).HasPrecision(18, 8);
|
||||
|
||||
// Create indexes
|
||||
entity.HasIndex(e => e.Ticker);
|
||||
entity.HasIndex(e => e.Date);
|
||||
entity.HasIndex(e => e.Exchange);
|
||||
entity.HasIndex(e => e.Rank);
|
||||
|
||||
// Composite indexes for efficient queries
|
||||
entity.HasIndex(e => new { e.Exchange, e.Date });
|
||||
entity.HasIndex(e => new { e.Date, e.Rank });
|
||||
});
|
||||
|
||||
// Configure SpotlightOverview entity
|
||||
modelBuilder.Entity<SpotlightOverviewEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Identifier).IsRequired();
|
||||
entity.Property(e => e.SpotlightsJson).HasColumnType("jsonb");
|
||||
|
||||
// Create indexes
|
||||
entity.HasIndex(e => e.Identifier).IsUnique();
|
||||
entity.HasIndex(e => e.DateTime);
|
||||
entity.HasIndex(e => e.ScenarioCount);
|
||||
|
||||
// Composite index for efficient queries
|
||||
entity.HasIndex(e => new { e.DateTime, e.ScenarioCount });
|
||||
});
|
||||
|
||||
// Configure Trader entity
|
||||
modelBuilder.Entity<TraderEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Address).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.Pnl).HasPrecision(18, 8);
|
||||
entity.Property(e => e.AverageWin).HasPrecision(18, 8);
|
||||
entity.Property(e => e.AverageLoss).HasPrecision(18, 8);
|
||||
entity.Property(e => e.Roi).HasPrecision(18, 8);
|
||||
|
||||
// Create indexes
|
||||
entity.HasIndex(e => e.Address);
|
||||
entity.HasIndex(e => e.IsBestTrader);
|
||||
entity.HasIndex(e => e.Winrate);
|
||||
entity.HasIndex(e => e.Pnl);
|
||||
entity.HasIndex(e => e.Roi);
|
||||
|
||||
// Composite indexes for efficient queries
|
||||
entity.HasIndex(e => new { e.IsBestTrader, e.Winrate });
|
||||
entity.HasIndex(e => new { e.IsBestTrader, e.Roi });
|
||||
entity.HasIndex(e => new { e.Address, e.IsBestTrader }).IsUnique();
|
||||
});
|
||||
|
||||
// Configure FundingRate entity
|
||||
modelBuilder.Entity<FundingRateEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Rate).HasPrecision(18, 8);
|
||||
entity.Property(e => e.OpenInterest).HasPrecision(18, 8);
|
||||
|
||||
// Create indexes
|
||||
entity.HasIndex(e => e.Ticker);
|
||||
entity.HasIndex(e => e.Exchange);
|
||||
entity.HasIndex(e => e.Date);
|
||||
entity.HasIndex(e => e.Direction);
|
||||
|
||||
// Composite indexes for efficient queries
|
||||
entity.HasIndex(e => new { e.Ticker, e.Exchange });
|
||||
entity.HasIndex(e => new { e.Exchange, e.Date });
|
||||
entity.HasIndex(e => new { e.Ticker, e.Exchange, e.Date }).IsUnique();
|
||||
});
|
||||
|
||||
// Configure BotBackup entity
|
||||
modelBuilder.Entity<BotBackupEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Identifier).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.UserName).HasMaxLength(255);
|
||||
entity.Property(e => e.Data).IsRequired().HasColumnType("text");
|
||||
|
||||
// Create indexes
|
||||
entity.HasIndex(e => e.Identifier).IsUnique();
|
||||
entity.HasIndex(e => e.UserName);
|
||||
entity.HasIndex(e => e.LastStatus);
|
||||
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()
|
||||
.HasForeignKey(e => e.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
});
|
||||
|
||||
// Configure MoneyManagement entity
|
||||
modelBuilder.Entity<MoneyManagementEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Name).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.Timeframe).IsRequired().HasConversion<string>();
|
||||
entity.Property(e => e.StopLoss).HasColumnType("decimal(18,8)");
|
||||
entity.Property(e => e.TakeProfit).HasColumnType("decimal(18,8)");
|
||||
entity.Property(e => e.Leverage).HasColumnType("decimal(18,8)");
|
||||
entity.Property(e => e.UserName).HasMaxLength(255);
|
||||
|
||||
// Create indexes
|
||||
entity.HasIndex(e => e.Name);
|
||||
entity.HasIndex(e => e.UserName);
|
||||
|
||||
// Composite index for user money managements
|
||||
entity.HasIndex(e => new { e.UserName, e.Name });
|
||||
|
||||
// Configure relationship with User
|
||||
entity.HasOne(e => e.User)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
});
|
||||
|
||||
// Configure Worker entity
|
||||
modelBuilder.Entity<WorkerEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.WorkerType).IsRequired().HasConversion<string>();
|
||||
entity.Property(e => e.StartTime).IsRequired();
|
||||
entity.Property(e => e.LastRunTime);
|
||||
entity.Property(e => e.ExecutionCount).IsRequired();
|
||||
entity.Property(e => e.DelayTicks).IsRequired();
|
||||
entity.Property(e => e.IsActive).IsRequired();
|
||||
entity.HasIndex(e => e.WorkerType).IsUnique();
|
||||
});
|
||||
|
||||
// Configure SynthMinersLeaderboard entity
|
||||
modelBuilder.Entity<SynthMinersLeaderboardEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Asset).IsRequired().HasMaxLength(32);
|
||||
entity.Property(e => e.TimeIncrement).IsRequired();
|
||||
entity.Property(e => e.SignalDate);
|
||||
entity.Property(e => e.IsBacktest).IsRequired();
|
||||
entity.Property(e => e.MinersData).HasColumnType("jsonb");
|
||||
entity.Property(e => e.CacheKey).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.CreatedAt).IsRequired();
|
||||
entity.HasIndex(e => e.CacheKey).IsUnique();
|
||||
entity.HasIndex(e => e.CreatedAt);
|
||||
});
|
||||
|
||||
// Configure SynthPrediction entity
|
||||
modelBuilder.Entity<SynthPredictionEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Asset).IsRequired().HasMaxLength(32);
|
||||
entity.Property(e => e.MinerUid).IsRequired();
|
||||
entity.Property(e => e.TimeIncrement).IsRequired();
|
||||
entity.Property(e => e.TimeLength).IsRequired();
|
||||
entity.Property(e => e.SignalDate);
|
||||
entity.Property(e => e.IsBacktest).IsRequired();
|
||||
entity.Property(e => e.PredictionData).HasColumnType("jsonb");
|
||||
entity.Property(e => e.CacheKey).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.CreatedAt).IsRequired();
|
||||
entity.HasIndex(e => e.CacheKey).IsUnique();
|
||||
entity.HasIndex(e => e.CreatedAt);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
using System.Data;
|
||||
using Managing.Application.Abstractions.Repositories;
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Domain.Accounts;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql;
|
||||
|
||||
public class PostgreSqlAccountRepository : IAccountRepository
|
||||
{
|
||||
private readonly ManagingDbContext _context;
|
||||
private readonly ICacheService _cacheService;
|
||||
|
||||
public PostgreSqlAccountRepository(ManagingDbContext context, ICacheService cacheService)
|
||||
{
|
||||
_context = context;
|
||||
_cacheService = cacheService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the database connection is open before executing queries
|
||||
/// </summary>
|
||||
private async Task EnsureConnectionOpenAsync()
|
||||
{
|
||||
if (_context.Database.GetDbConnection().State != ConnectionState.Open)
|
||||
{
|
||||
await _context.Database.OpenConnectionAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely closes the database connection if it was opened by us
|
||||
/// </summary>
|
||||
private async Task SafeCloseConnectionAsync()
|
||||
{
|
||||
if (_context.Database.GetDbConnection().State == ConnectionState.Open)
|
||||
{
|
||||
await _context.Database.CloseConnectionAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteAccountByName(string name)
|
||||
{
|
||||
var accountEntity = _context.Accounts
|
||||
.AsTracking() // Explicitly enable tracking for delete operations
|
||||
.FirstOrDefault(a => a.Name == name);
|
||||
if (accountEntity != null)
|
||||
{
|
||||
_context.Accounts.Remove(accountEntity);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Account> GetAccountByKeyAsync(string key)
|
||||
{
|
||||
try
|
||||
{
|
||||
await EnsureConnectionOpenAsync();
|
||||
|
||||
var accountEntity = await _context.Accounts
|
||||
.AsNoTracking()
|
||||
.Include(a => a.User)
|
||||
.FirstOrDefaultAsync(a => a.Key == key)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return PostgreSqlMappers.Map(accountEntity);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// If there's an error, try to reset the connection
|
||||
await SafeCloseConnectionAsync();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Account> GetAccountByNameAsync(string name)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cacheKey = $"account_{name}";
|
||||
var cachedAccount = _cacheService.GetValue<Account>(cacheKey);
|
||||
if (cachedAccount != null)
|
||||
{
|
||||
return cachedAccount;
|
||||
}
|
||||
|
||||
var accountEntity = await _context.Accounts
|
||||
.AsNoTracking()
|
||||
.Include(a => a.User)
|
||||
.FirstOrDefaultAsync(a => a.Name == name)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var account = PostgreSqlMappers.Map(accountEntity);
|
||||
_cacheService.SaveValue(cacheKey, account, TimeSpan.FromHours(1));
|
||||
return account;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// If there's an error, try to reset the connection
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Account>> GetAccountsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await EnsureConnectionOpenAsync();
|
||||
|
||||
// Use proper async operations with AsNoTracking for optimal performance
|
||||
var accountEntities = await _context.Accounts
|
||||
.AsNoTracking()
|
||||
.Include(a => a.User)
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return PostgreSqlMappers.Map(accountEntities);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// If there's an error, try to reset the connection
|
||||
await SafeCloseConnectionAsync();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task InsertAccountAsync(Account account)
|
||||
{
|
||||
var accountEntity = PostgreSqlMappers.Map(account);
|
||||
|
||||
// Handle User relationship - check if user exists or create new one
|
||||
if (account.User != null)
|
||||
{
|
||||
var existingUser = await _context.Users
|
||||
.AsTracking() // Explicitly enable tracking for this operation
|
||||
.FirstOrDefaultAsync(u => u.Name == account.User.Name)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (existingUser != null)
|
||||
{
|
||||
accountEntity.UserId = existingUser.Id;
|
||||
accountEntity.User = null; // Prevent EF from trying to insert duplicate user
|
||||
}
|
||||
else
|
||||
{
|
||||
// Let EF handle the new user creation
|
||||
accountEntity.UserId = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Balances are not stored in PostgreSQL, they remain in domain logic only
|
||||
|
||||
_context.Accounts.Add(accountEntity);
|
||||
await _context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,730 @@
|
||||
using System.Diagnostics;
|
||||
using Exilion.TradingAtomics;
|
||||
using Managing.Application.Abstractions.Repositories;
|
||||
using Managing.Domain.Backtests;
|
||||
using Managing.Domain.Bots;
|
||||
using Managing.Domain.Users;
|
||||
using Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql;
|
||||
|
||||
public class PostgreSqlBacktestRepository : IBacktestRepository
|
||||
{
|
||||
private readonly ManagingDbContext _context;
|
||||
|
||||
public PostgreSqlBacktestRepository(ManagingDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
// User-specific operations
|
||||
public void InsertBacktestForUser(User user, Backtest result)
|
||||
{
|
||||
ValidateBacktestData(result);
|
||||
result.User = user;
|
||||
|
||||
var entity = PostgreSqlMappers.Map(result);
|
||||
_context.Backtests.Add(entity);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that all numeric fields in the backtest are of the correct type
|
||||
/// </summary>
|
||||
private void ValidateBacktestData(Backtest backtest)
|
||||
{
|
||||
// Ensure FinalPnl is a valid decimal
|
||||
if (backtest.FinalPnl.GetType() != typeof(decimal))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"FinalPnl must be of type decimal, but got {backtest.FinalPnl.GetType().Name}");
|
||||
}
|
||||
|
||||
// Ensure other numeric fields are correct
|
||||
if (backtest.GrowthPercentage.GetType() != typeof(decimal))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"GrowthPercentage must be of type decimal, but got {backtest.GrowthPercentage.GetType().Name}");
|
||||
}
|
||||
|
||||
if (backtest.HodlPercentage.GetType() != typeof(decimal))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"HodlPercentage must be of type decimal, but got {backtest.HodlPercentage.GetType().Name}");
|
||||
}
|
||||
|
||||
if (backtest.Score.GetType() != typeof(double))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Score must be of type double, but got {backtest.Score.GetType().Name}");
|
||||
}
|
||||
|
||||
if (backtest.WinRate.GetType() != typeof(int))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"WinRate must be of type int, but got {backtest.WinRate.GetType().Name}");
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Backtest> GetBacktestsByUser(User user)
|
||||
{
|
||||
var entities = _context.Backtests
|
||||
.AsNoTracking()
|
||||
.Where(b => b.UserName == user.Name)
|
||||
.ToList();
|
||||
|
||||
return entities.Select(PostgreSqlMappers.Map);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Backtest>> GetBacktestsByUserAsync(User user)
|
||||
{
|
||||
var entities = await _context.Backtests
|
||||
.AsNoTracking()
|
||||
.Where(b => b.UserName == user.Name)
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return entities.Select(PostgreSqlMappers.Map);
|
||||
}
|
||||
|
||||
public IEnumerable<Backtest> GetBacktestsByRequestId(string requestId)
|
||||
{
|
||||
var entities = _context.Backtests
|
||||
.AsNoTracking()
|
||||
.Where(b => b.RequestId == requestId)
|
||||
.ToList();
|
||||
|
||||
return entities.Select(PostgreSqlMappers.Map);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Backtest>> GetBacktestsByRequestIdAsync(string requestId)
|
||||
{
|
||||
var entities = await _context.Backtests
|
||||
.AsNoTracking()
|
||||
.Where(b => b.RequestId == requestId)
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return entities.Select(PostgreSqlMappers.Map);
|
||||
}
|
||||
|
||||
public (IEnumerable<LightBacktest> Backtests, int TotalCount) GetBacktestsByRequestIdPaginated(string requestId,
|
||||
int page, int pageSize, string sortBy = "score", string sortOrder = "desc")
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
var baseQuery = _context.Backtests
|
||||
.AsNoTracking()
|
||||
.Where(b => b.RequestId == requestId);
|
||||
|
||||
var afterQueryMs = stopwatch.ElapsedMilliseconds;
|
||||
var totalCount = baseQuery.Count();
|
||||
var afterCountMs = stopwatch.ElapsedMilliseconds;
|
||||
|
||||
// Apply sorting
|
||||
IQueryable<BacktestEntity> sortedQuery = sortBy.ToLower() switch
|
||||
{
|
||||
"score" => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.Score)
|
||||
: baseQuery.OrderBy(b => b.Score),
|
||||
"finalpnl" => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.FinalPnl)
|
||||
: baseQuery.OrderBy(b => b.FinalPnl),
|
||||
"winrate" => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.WinRate)
|
||||
: baseQuery.OrderBy(b => b.WinRate),
|
||||
"growthpercentage" => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.GrowthPercentage)
|
||||
: baseQuery.OrderBy(b => b.GrowthPercentage),
|
||||
"hodlpercentage" => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.HodlPercentage)
|
||||
: baseQuery.OrderBy(b => b.HodlPercentage),
|
||||
_ => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.Score)
|
||||
: baseQuery.OrderBy(b => b.Score)
|
||||
};
|
||||
|
||||
var afterSortMs = stopwatch.ElapsedMilliseconds;
|
||||
var entities = sortedQuery
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToList();
|
||||
var afterToListMs = stopwatch.ElapsedMilliseconds;
|
||||
|
||||
Console.WriteLine(
|
||||
$"[PostgreSqlBacktestRepo] Query: {afterQueryMs}ms, Count: {afterCountMs - afterQueryMs}ms, Sort: {afterSortMs - afterCountMs}ms, ToList: {afterToListMs - afterSortMs}ms, Total: {afterToListMs}ms");
|
||||
|
||||
var mappedBacktests = entities.Select(entity => new LightBacktest
|
||||
{
|
||||
Id = entity.Identifier,
|
||||
Config = JsonConvert.DeserializeObject<TradingBotConfig>(entity.ConfigJson),
|
||||
FinalPnl = entity.FinalPnl,
|
||||
WinRate = entity.WinRate,
|
||||
GrowthPercentage = entity.GrowthPercentage,
|
||||
HodlPercentage = entity.HodlPercentage,
|
||||
StartDate = entity.StartDate,
|
||||
EndDate = entity.EndDate,
|
||||
MaxDrawdown = !string.IsNullOrEmpty(entity.StatisticsJson)
|
||||
? JsonConvert.DeserializeObject<PerformanceMetrics>(entity.StatisticsJson)?.MaxDrawdown
|
||||
: null,
|
||||
Fees = entity.Fees,
|
||||
SharpeRatio = !string.IsNullOrEmpty(entity.StatisticsJson)
|
||||
? JsonConvert.DeserializeObject<PerformanceMetrics>(entity.StatisticsJson)?.SharpeRatio != null
|
||||
? (double?)JsonConvert.DeserializeObject<PerformanceMetrics>(entity.StatisticsJson).SharpeRatio
|
||||
: null
|
||||
: null,
|
||||
Score = entity.Score,
|
||||
ScoreMessage = entity.ScoreMessage ?? string.Empty
|
||||
});
|
||||
|
||||
return (mappedBacktests, totalCount);
|
||||
}
|
||||
|
||||
public async Task<(IEnumerable<LightBacktest> Backtests, int TotalCount)> GetBacktestsByRequestIdPaginatedAsync(
|
||||
string requestId, int page, int pageSize, string sortBy = "score", string sortOrder = "desc")
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
var baseQuery = _context.Backtests
|
||||
.AsNoTracking()
|
||||
.Where(b => b.RequestId == requestId);
|
||||
|
||||
var afterQueryMs = stopwatch.ElapsedMilliseconds;
|
||||
var totalCount = await baseQuery.CountAsync().ConfigureAwait(false);
|
||||
var afterCountMs = stopwatch.ElapsedMilliseconds;
|
||||
|
||||
// Apply sorting
|
||||
IQueryable<BacktestEntity> sortedQuery = sortBy.ToLower() switch
|
||||
{
|
||||
"score" => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.Score)
|
||||
: baseQuery.OrderBy(b => b.Score),
|
||||
"finalpnl" => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.FinalPnl)
|
||||
: baseQuery.OrderBy(b => b.FinalPnl),
|
||||
"winrate" => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.WinRate)
|
||||
: baseQuery.OrderBy(b => b.WinRate),
|
||||
"growthpercentage" => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.GrowthPercentage)
|
||||
: baseQuery.OrderBy(b => b.GrowthPercentage),
|
||||
"hodlpercentage" => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.HodlPercentage)
|
||||
: baseQuery.OrderBy(b => b.HodlPercentage),
|
||||
_ => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.Score)
|
||||
: baseQuery.OrderBy(b => b.Score)
|
||||
};
|
||||
|
||||
var afterSortMs = stopwatch.ElapsedMilliseconds;
|
||||
var entities = await sortedQuery
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
var afterToListMs = stopwatch.ElapsedMilliseconds;
|
||||
|
||||
Console.WriteLine(
|
||||
$"[PostgreSqlBacktestRepo] Query: {afterQueryMs}ms, Count: {afterCountMs - afterQueryMs}ms, Sort: {afterSortMs - afterCountMs}ms, ToList: {afterToListMs - afterSortMs}ms, Total: {afterToListMs}ms");
|
||||
|
||||
var mappedBacktests = entities.Select(entity => new LightBacktest
|
||||
{
|
||||
Id = entity.Identifier,
|
||||
Config = JsonConvert.DeserializeObject<TradingBotConfig>(entity.ConfigJson),
|
||||
FinalPnl = entity.FinalPnl,
|
||||
WinRate = entity.WinRate,
|
||||
GrowthPercentage = entity.GrowthPercentage,
|
||||
HodlPercentage = entity.HodlPercentage,
|
||||
StartDate = entity.StartDate,
|
||||
EndDate = entity.EndDate,
|
||||
MaxDrawdown = !string.IsNullOrEmpty(entity.StatisticsJson)
|
||||
? JsonConvert.DeserializeObject<PerformanceMetrics>(entity.StatisticsJson)?.MaxDrawdown
|
||||
: null,
|
||||
Fees = entity.Fees,
|
||||
SharpeRatio = !string.IsNullOrEmpty(entity.StatisticsJson)
|
||||
? JsonConvert.DeserializeObject<PerformanceMetrics>(entity.StatisticsJson)?.SharpeRatio != null
|
||||
? (double?)JsonConvert.DeserializeObject<PerformanceMetrics>(entity.StatisticsJson).SharpeRatio
|
||||
: null
|
||||
: null,
|
||||
Score = entity.Score,
|
||||
ScoreMessage = entity.ScoreMessage ?? string.Empty
|
||||
});
|
||||
|
||||
return (mappedBacktests, totalCount);
|
||||
}
|
||||
|
||||
public Backtest GetBacktestByIdForUser(User user, string id)
|
||||
{
|
||||
var entity = _context.Backtests
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(b => b.Identifier == id && b.UserName == user.Name);
|
||||
|
||||
return entity != null ? PostgreSqlMappers.Map(entity) : null;
|
||||
}
|
||||
|
||||
public async Task<Backtest> GetBacktestByIdForUserAsync(User user, string id)
|
||||
{
|
||||
var entity = await _context.Backtests
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(b => b.Identifier == id && b.UserName == user.Name)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return entity != null ? PostgreSqlMappers.Map(entity) : null;
|
||||
}
|
||||
|
||||
public void DeleteBacktestByIdForUser(User user, string id)
|
||||
{
|
||||
var entity = _context.Backtests
|
||||
.AsTracking()
|
||||
.FirstOrDefault(b => b.Identifier == id && b.UserName == user.Name);
|
||||
|
||||
if (entity != null)
|
||||
{
|
||||
_context.Backtests.Remove(entity);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteBacktestByIdForUserAsync(User user, string id)
|
||||
{
|
||||
var entity = await _context.Backtests
|
||||
.AsTracking()
|
||||
.FirstOrDefaultAsync(b => b.Identifier == id && b.UserName == user.Name)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (entity != null)
|
||||
{
|
||||
_context.Backtests.Remove(entity);
|
||||
await _context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteBacktestsByIdsForUser(User user, IEnumerable<string> ids)
|
||||
{
|
||||
var entities = _context.Backtests
|
||||
.AsTracking()
|
||||
.Where(b => b.UserName == user.Name && ids.Contains(b.Identifier))
|
||||
.ToList();
|
||||
|
||||
if (entities.Any())
|
||||
{
|
||||
_context.Backtests.RemoveRange(entities);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteBacktestsByIdsForUserAsync(User user, IEnumerable<string> ids)
|
||||
{
|
||||
var entities = await _context.Backtests
|
||||
.AsTracking()
|
||||
.Where(b => b.UserName == user.Name && ids.Contains(b.Identifier))
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (entities.Any())
|
||||
{
|
||||
_context.Backtests.RemoveRange(entities);
|
||||
await _context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteAllBacktestsForUser(User user)
|
||||
{
|
||||
var entities = _context.Backtests
|
||||
.AsTracking()
|
||||
.Where(b => b.UserName == user.Name)
|
||||
.ToList();
|
||||
|
||||
if (entities.Any())
|
||||
{
|
||||
_context.Backtests.RemoveRange(entities);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteBacktestsByRequestId(string requestId)
|
||||
{
|
||||
var entities = _context.Backtests
|
||||
.AsTracking()
|
||||
.Where(b => b.RequestId == requestId)
|
||||
.ToList();
|
||||
|
||||
if (entities.Any())
|
||||
{
|
||||
_context.Backtests.RemoveRange(entities);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteBacktestsByRequestIdAsync(string requestId)
|
||||
{
|
||||
var entities = await _context.Backtests
|
||||
.AsTracking()
|
||||
.Where(b => b.RequestId == requestId)
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (entities.Any())
|
||||
{
|
||||
_context.Backtests.RemoveRange(entities);
|
||||
await _context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public (IEnumerable<LightBacktest> Backtests, int TotalCount) GetBacktestsByUserPaginated(User user, int page,
|
||||
int pageSize, string sortBy = "score", string sortOrder = "desc")
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
var baseQuery = _context.Backtests
|
||||
.AsNoTracking()
|
||||
.Where(b => b.UserName == user.Name);
|
||||
|
||||
var afterQueryMs = stopwatch.ElapsedMilliseconds;
|
||||
var totalCount = baseQuery.Count();
|
||||
var afterCountMs = stopwatch.ElapsedMilliseconds;
|
||||
|
||||
// Apply sorting
|
||||
IQueryable<BacktestEntity> sortedQuery = sortBy.ToLower() switch
|
||||
{
|
||||
"score" => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.Score)
|
||||
: baseQuery.OrderBy(b => b.Score),
|
||||
"finalpnl" => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.FinalPnl)
|
||||
: baseQuery.OrderBy(b => b.FinalPnl),
|
||||
"winrate" => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.WinRate)
|
||||
: baseQuery.OrderBy(b => b.WinRate),
|
||||
"growthpercentage" => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.GrowthPercentage)
|
||||
: baseQuery.OrderBy(b => b.GrowthPercentage),
|
||||
"hodlpercentage" => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.HodlPercentage)
|
||||
: baseQuery.OrderBy(b => b.HodlPercentage),
|
||||
_ => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.Score)
|
||||
: baseQuery.OrderBy(b => b.Score)
|
||||
};
|
||||
|
||||
var afterSortMs = stopwatch.ElapsedMilliseconds;
|
||||
var entities = sortedQuery
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToList();
|
||||
var afterToListMs = stopwatch.ElapsedMilliseconds;
|
||||
|
||||
Console.WriteLine(
|
||||
$"[PostgreSqlBacktestRepo] User Query: {afterQueryMs}ms, Count: {afterCountMs - afterQueryMs}ms, Sort: {afterSortMs - afterCountMs}ms, ToList: {afterToListMs - afterSortMs}ms, Total: {afterToListMs}ms");
|
||||
|
||||
var mappedBacktests = entities.Select(entity => new LightBacktest
|
||||
{
|
||||
Id = entity.Identifier,
|
||||
Config = JsonConvert.DeserializeObject<TradingBotConfig>(entity.ConfigJson),
|
||||
FinalPnl = entity.FinalPnl,
|
||||
WinRate = entity.WinRate,
|
||||
GrowthPercentage = entity.GrowthPercentage,
|
||||
HodlPercentage = entity.HodlPercentage,
|
||||
StartDate = entity.StartDate,
|
||||
EndDate = entity.EndDate,
|
||||
MaxDrawdown = !string.IsNullOrEmpty(entity.StatisticsJson)
|
||||
? JsonConvert.DeserializeObject<PerformanceMetrics>(entity.StatisticsJson)?.MaxDrawdown
|
||||
: null,
|
||||
Fees = entity.Fees,
|
||||
SharpeRatio = !string.IsNullOrEmpty(entity.StatisticsJson)
|
||||
? JsonConvert.DeserializeObject<PerformanceMetrics>(entity.StatisticsJson)?.SharpeRatio != null
|
||||
? (double?)JsonConvert.DeserializeObject<PerformanceMetrics>(entity.StatisticsJson).SharpeRatio
|
||||
: null
|
||||
: null,
|
||||
Score = entity.Score,
|
||||
ScoreMessage = entity.ScoreMessage ?? string.Empty
|
||||
});
|
||||
|
||||
return (mappedBacktests, totalCount);
|
||||
}
|
||||
|
||||
public async Task<(IEnumerable<LightBacktest> Backtests, int TotalCount)> GetBacktestsByUserPaginatedAsync(
|
||||
User user, int page, int pageSize, string sortBy = "score", string sortOrder = "desc")
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
var baseQuery = _context.Backtests
|
||||
.AsNoTracking()
|
||||
.Where(b => b.UserName == user.Name);
|
||||
|
||||
var afterQueryMs = stopwatch.ElapsedMilliseconds;
|
||||
var totalCount = await baseQuery.CountAsync().ConfigureAwait(false);
|
||||
var afterCountMs = stopwatch.ElapsedMilliseconds;
|
||||
|
||||
// Apply sorting
|
||||
IQueryable<BacktestEntity> sortedQuery = sortBy.ToLower() switch
|
||||
{
|
||||
"score" => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.Score)
|
||||
: baseQuery.OrderBy(b => b.Score),
|
||||
"finalpnl" => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.FinalPnl)
|
||||
: baseQuery.OrderBy(b => b.FinalPnl),
|
||||
"winrate" => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.WinRate)
|
||||
: baseQuery.OrderBy(b => b.WinRate),
|
||||
"growthpercentage" => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.GrowthPercentage)
|
||||
: baseQuery.OrderBy(b => b.GrowthPercentage),
|
||||
"hodlpercentage" => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.HodlPercentage)
|
||||
: baseQuery.OrderBy(b => b.HodlPercentage),
|
||||
_ => sortOrder == "desc"
|
||||
? baseQuery.OrderByDescending(b => b.Score)
|
||||
: baseQuery.OrderBy(b => b.Score)
|
||||
};
|
||||
|
||||
var afterSortMs = stopwatch.ElapsedMilliseconds;
|
||||
var entities = await sortedQuery
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
var afterToListMs = stopwatch.ElapsedMilliseconds;
|
||||
|
||||
Console.WriteLine(
|
||||
$"[PostgreSqlBacktestRepo] User Query: {afterQueryMs}ms, Count: {afterCountMs - afterQueryMs}ms, Sort: {afterSortMs - afterCountMs}ms, ToList: {afterToListMs - afterSortMs}ms, Total: {afterToListMs}ms");
|
||||
|
||||
var mappedBacktests = entities.Select(entity => new LightBacktest
|
||||
{
|
||||
Id = entity.Identifier,
|
||||
Config = JsonConvert.DeserializeObject<TradingBotConfig>(entity.ConfigJson),
|
||||
FinalPnl = entity.FinalPnl,
|
||||
WinRate = entity.WinRate,
|
||||
GrowthPercentage = entity.GrowthPercentage,
|
||||
HodlPercentage = entity.HodlPercentage,
|
||||
StartDate = entity.StartDate,
|
||||
EndDate = entity.EndDate,
|
||||
MaxDrawdown = !string.IsNullOrEmpty(entity.StatisticsJson)
|
||||
? JsonConvert.DeserializeObject<PerformanceMetrics>(entity.StatisticsJson)?.MaxDrawdown
|
||||
: null,
|
||||
Fees = entity.Fees,
|
||||
SharpeRatio = !string.IsNullOrEmpty(entity.StatisticsJson)
|
||||
? JsonConvert.DeserializeObject<PerformanceMetrics>(entity.StatisticsJson)?.SharpeRatio != null
|
||||
? (double?)JsonConvert.DeserializeObject<PerformanceMetrics>(entity.StatisticsJson).SharpeRatio
|
||||
: null
|
||||
: null,
|
||||
Score = entity.Score,
|
||||
ScoreMessage = entity.ScoreMessage ?? string.Empty
|
||||
});
|
||||
|
||||
return (mappedBacktests, totalCount);
|
||||
}
|
||||
|
||||
// Bundle backtest methods
|
||||
public void InsertBundleBacktestRequestForUser(User user, BundleBacktestRequest bundleRequest)
|
||||
{
|
||||
bundleRequest.User = user;
|
||||
var entity = PostgreSqlMappers.Map(bundleRequest);
|
||||
|
||||
// Set the UserId by finding the user entity
|
||||
var userEntity = _context.Users.FirstOrDefault(u => u.Name == user.Name);
|
||||
if (userEntity != null)
|
||||
{
|
||||
entity.UserId = userEntity.Id;
|
||||
}
|
||||
|
||||
_context.BundleBacktestRequests.Add(entity);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
|
||||
public async Task InsertBundleBacktestRequestForUserAsync(User user, BundleBacktestRequest bundleRequest)
|
||||
{
|
||||
bundleRequest.User = user;
|
||||
var entity = PostgreSqlMappers.Map(bundleRequest);
|
||||
|
||||
// Set the UserId by finding the user entity
|
||||
var userEntity = await _context.Users.FirstOrDefaultAsync(u => u.Name == user.Name);
|
||||
if (userEntity != null)
|
||||
{
|
||||
entity.UserId = userEntity.Id;
|
||||
}
|
||||
|
||||
await _context.BundleBacktestRequests.AddAsync(entity);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public IEnumerable<BundleBacktestRequest> GetBundleBacktestRequestsByUser(User user)
|
||||
{
|
||||
var entities = _context.BundleBacktestRequests
|
||||
.AsNoTracking()
|
||||
.Include(b => b.User)
|
||||
.Where(b => b.UserName == user.Name)
|
||||
.OrderByDescending(b => b.CreatedAt)
|
||||
.ToList();
|
||||
|
||||
return entities.Select(PostgreSqlMappers.Map);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<BundleBacktestRequest>> GetBundleBacktestRequestsByUserAsync(User user)
|
||||
{
|
||||
var entities = await _context.BundleBacktestRequests
|
||||
.AsNoTracking()
|
||||
.Include(b => b.User)
|
||||
.Where(b => b.UserName == user.Name)
|
||||
.OrderByDescending(b => b.CreatedAt)
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return entities.Select(PostgreSqlMappers.Map);
|
||||
}
|
||||
|
||||
public BundleBacktestRequest? GetBundleBacktestRequestByIdForUser(User user, string id)
|
||||
{
|
||||
var entity = _context.BundleBacktestRequests
|
||||
.AsNoTracking()
|
||||
.Include(b => b.User)
|
||||
.FirstOrDefault(b => b.RequestId == id && b.UserName == user.Name);
|
||||
|
||||
return entity != null ? PostgreSqlMappers.Map(entity) : null;
|
||||
}
|
||||
|
||||
public async Task<BundleBacktestRequest?> GetBundleBacktestRequestByIdForUserAsync(User user, string id)
|
||||
{
|
||||
var entity = await _context.BundleBacktestRequests
|
||||
.AsNoTracking()
|
||||
.Include(b => b.User)
|
||||
.FirstOrDefaultAsync(b => b.RequestId == id && b.UserName == user.Name)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return entity != null ? PostgreSqlMappers.Map(entity) : null;
|
||||
}
|
||||
|
||||
public void UpdateBundleBacktestRequest(BundleBacktestRequest bundleRequest)
|
||||
{
|
||||
var entity = _context.BundleBacktestRequests
|
||||
.AsTracking()
|
||||
.FirstOrDefault(b => b.RequestId == bundleRequest.RequestId);
|
||||
|
||||
if (entity != null)
|
||||
{
|
||||
// Update the entity properties
|
||||
entity.Status = bundleRequest.Status;
|
||||
entity.CompletedAt = bundleRequest.CompletedAt;
|
||||
entity.CompletedBacktests = bundleRequest.CompletedBacktests;
|
||||
entity.FailedBacktests = bundleRequest.FailedBacktests;
|
||||
entity.ErrorMessage = bundleRequest.ErrorMessage;
|
||||
entity.ProgressInfo = bundleRequest.ProgressInfo;
|
||||
entity.CurrentBacktest = bundleRequest.CurrentBacktest;
|
||||
entity.EstimatedTimeRemainingSeconds = bundleRequest.EstimatedTimeRemainingSeconds;
|
||||
entity.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
// Serialize Results to JSON
|
||||
if (bundleRequest.Results != null && bundleRequest.Results.Any())
|
||||
{
|
||||
try
|
||||
{
|
||||
entity.ResultsJson = JsonConvert.SerializeObject(bundleRequest.Results);
|
||||
}
|
||||
catch
|
||||
{
|
||||
entity.ResultsJson = "[]";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entity.ResultsJson = "[]";
|
||||
}
|
||||
|
||||
_context.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateBundleBacktestRequestAsync(BundleBacktestRequest bundleRequest)
|
||||
{
|
||||
var entity = await _context.BundleBacktestRequests
|
||||
.AsTracking()
|
||||
.FirstOrDefaultAsync(b => b.RequestId == bundleRequest.RequestId)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (entity != null)
|
||||
{
|
||||
// Update the entity properties
|
||||
entity.Status = bundleRequest.Status;
|
||||
entity.CompletedAt = bundleRequest.CompletedAt;
|
||||
entity.CompletedBacktests = bundleRequest.CompletedBacktests;
|
||||
entity.FailedBacktests = bundleRequest.FailedBacktests;
|
||||
entity.ErrorMessage = bundleRequest.ErrorMessage;
|
||||
entity.ProgressInfo = bundleRequest.ProgressInfo;
|
||||
entity.CurrentBacktest = bundleRequest.CurrentBacktest;
|
||||
entity.EstimatedTimeRemainingSeconds = bundleRequest.EstimatedTimeRemainingSeconds;
|
||||
entity.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
// Serialize Results to JSON
|
||||
if (bundleRequest.Results != null && bundleRequest.Results.Any())
|
||||
{
|
||||
try
|
||||
{
|
||||
entity.ResultsJson = JsonConvert.SerializeObject(bundleRequest.Results);
|
||||
}
|
||||
catch
|
||||
{
|
||||
entity.ResultsJson = "[]";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entity.ResultsJson = "[]";
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteBundleBacktestRequestByIdForUser(User user, string id)
|
||||
{
|
||||
var entity = _context.BundleBacktestRequests
|
||||
.AsTracking()
|
||||
.FirstOrDefault(b => b.RequestId == id && b.UserName == user.Name);
|
||||
|
||||
if (entity != null)
|
||||
{
|
||||
_context.BundleBacktestRequests.Remove(entity);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteBundleBacktestRequestByIdForUserAsync(User user, string id)
|
||||
{
|
||||
var entity = await _context.BundleBacktestRequests
|
||||
.AsTracking()
|
||||
.FirstOrDefaultAsync(b => b.RequestId == id && b.UserName == user.Name)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (entity != null)
|
||||
{
|
||||
_context.BundleBacktestRequests.Remove(entity);
|
||||
await _context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<BundleBacktestRequest> GetBundleBacktestRequestsByStatus(BundleBacktestRequestStatus status)
|
||||
{
|
||||
var entities = _context.BundleBacktestRequests
|
||||
.AsNoTracking()
|
||||
.Include(b => b.User)
|
||||
.Where(b => b.Status == status)
|
||||
.ToList();
|
||||
|
||||
return entities.Select(PostgreSqlMappers.Map);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<BundleBacktestRequest>> GetBundleBacktestRequestsByStatusAsync(BundleBacktestRequestStatus status)
|
||||
{
|
||||
var entities = await _context.BundleBacktestRequests
|
||||
.AsNoTracking()
|
||||
.Include(b => b.User)
|
||||
.Where(b => b.Status == status)
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return entities.Select(PostgreSqlMappers.Map);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using Managing.Application.Abstractions.Repositories;
|
||||
using Managing.Domain.Bots;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql;
|
||||
|
||||
public class PostgreSqlBotRepository : IBotRepository
|
||||
{
|
||||
private readonly ManagingDbContext _context;
|
||||
|
||||
public PostgreSqlBotRepository(ManagingDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task InsertBotAsync(BotBackup bot)
|
||||
{
|
||||
bot.CreateDate = DateTime.UtcNow;
|
||||
var entity = PostgreSqlMappers.Map(bot);
|
||||
// Set the UserId if user is provided
|
||||
if (bot.User != null)
|
||||
{
|
||||
var userEntity = await _context.Users
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(u => u.Name == bot.User.Name)
|
||||
.ConfigureAwait(false);
|
||||
entity.UserId = userEntity?.Id;
|
||||
}
|
||||
|
||||
await _context.BotBackups.AddAsync(entity).ConfigureAwait(false);
|
||||
await _context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<BotBackup>> GetBotsAsync()
|
||||
{
|
||||
var entities = await _context.BotBackups
|
||||
.AsNoTracking()
|
||||
.Include(m => m.User)
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return PostgreSqlMappers.Map(entities);
|
||||
}
|
||||
|
||||
public async Task UpdateBackupBot(BotBackup bot)
|
||||
{
|
||||
var existingEntity = await _context.BotBackups
|
||||
.AsTracking()
|
||||
.FirstOrDefaultAsync(b => b.Identifier == bot.Identifier)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (existingEntity == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Bot backup with identifier '{bot.Identifier}' not found");
|
||||
}
|
||||
|
||||
// Update the entity properties
|
||||
existingEntity.Data = bot.SerializeData(); // Use the serialized data string
|
||||
existingEntity.LastStatus = bot.LastStatus;
|
||||
existingEntity.UpdatedAt = DateTime.UtcNow;
|
||||
existingEntity.UserName = bot.User?.Name;
|
||||
|
||||
await _context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task DeleteBotBackup(string identifier)
|
||||
{
|
||||
var entity = await _context.BotBackups
|
||||
.AsTracking()
|
||||
.FirstOrDefaultAsync(b => b.Identifier == identifier)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (entity == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Bot backup with identifier '{identifier}' not found");
|
||||
}
|
||||
|
||||
_context.BotBackups.Remove(entity);
|
||||
await _context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<BotBackup?> GetBotByIdentifierAsync(string identifier)
|
||||
{
|
||||
var entity = await _context.BotBackups
|
||||
.AsNoTracking()
|
||||
.Include(m => m.User)
|
||||
.FirstOrDefaultAsync(b => b.Identifier == identifier)
|
||||
.ConfigureAwait(false);
|
||||
return PostgreSqlMappers.Map(entity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
using System.Text.Json;
|
||||
using Managing.Application.Abstractions.Repositories;
|
||||
using Managing.Domain.Backtests;
|
||||
using Managing.Domain.Users;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql;
|
||||
|
||||
public class PostgreSqlGeneticRepository : IGeneticRepository
|
||||
{
|
||||
private readonly ManagingDbContext _context;
|
||||
|
||||
public PostgreSqlGeneticRepository(ManagingDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public void InsertGeneticRequestForUser(User user, GeneticRequest geneticRequest)
|
||||
{
|
||||
geneticRequest.User = user;
|
||||
var geneticRequestEntity = PostgreSqlMappers.Map(geneticRequest);
|
||||
|
||||
// Handle User relationship - check if user exists or create new one
|
||||
if (user != null)
|
||||
{
|
||||
var existingUser = _context.Users
|
||||
.AsTracking() // Explicitly enable tracking for this operation
|
||||
.FirstOrDefault(u => u.Name == user.Name);
|
||||
if (existingUser != null)
|
||||
{
|
||||
geneticRequestEntity.UserId = existingUser.Id;
|
||||
geneticRequestEntity.User = null; // Prevent EF from trying to insert duplicate user
|
||||
}
|
||||
else
|
||||
{
|
||||
// Let EF handle the new user creation
|
||||
geneticRequestEntity.UserId = null;
|
||||
}
|
||||
}
|
||||
|
||||
_context.GeneticRequests.Add(geneticRequestEntity);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
|
||||
public IEnumerable<GeneticRequest> GetGeneticRequestsByUser(User user)
|
||||
{
|
||||
// Use synchronous operations and AsNoTracking to avoid concurrency issues
|
||||
var geneticRequestEntities = _context.GeneticRequests
|
||||
.AsNoTracking()
|
||||
.Include(gr => gr.User)
|
||||
.Where(gr => gr.User != null && gr.User.Name == user.Name)
|
||||
.OrderByDescending(gr => gr.CreatedAt)
|
||||
.ToList();
|
||||
|
||||
return PostgreSqlMappers.Map(geneticRequestEntities);
|
||||
}
|
||||
|
||||
public GeneticRequest GetGeneticRequestByIdForUser(User user, string id)
|
||||
{
|
||||
// Use synchronous operations and AsNoTracking to avoid concurrency issues
|
||||
var geneticRequestEntity = _context.GeneticRequests
|
||||
.AsNoTracking()
|
||||
.Include(gr => gr.User)
|
||||
.FirstOrDefault(gr => gr.RequestId == id);
|
||||
|
||||
// Check if genetic request exists and belongs to the user
|
||||
if (geneticRequestEntity != null && geneticRequestEntity.User != null &&
|
||||
geneticRequestEntity.User.Name == user.Name)
|
||||
{
|
||||
return PostgreSqlMappers.Map(geneticRequestEntity);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task UpdateGeneticRequestAsync(GeneticRequest geneticRequest)
|
||||
{
|
||||
var existingEntity = _context.GeneticRequests
|
||||
.Include(gr => gr.User)
|
||||
.FirstOrDefault(gr => gr.RequestId == geneticRequest.RequestId);
|
||||
|
||||
if (existingEntity != null)
|
||||
{
|
||||
// Update the existing entity with new values
|
||||
existingEntity.CompletedAt = geneticRequest.CompletedAt;
|
||||
existingEntity.UpdatedAt = DateTime.UtcNow;
|
||||
existingEntity.Status = geneticRequest.Status.ToString();
|
||||
existingEntity.BestFitness = geneticRequest.BestFitness;
|
||||
existingEntity.BestIndividual = geneticRequest.BestIndividual;
|
||||
existingEntity.ErrorMessage = geneticRequest.ErrorMessage;
|
||||
existingEntity.ProgressInfo = geneticRequest.ProgressInfo;
|
||||
existingEntity.BestChromosome = geneticRequest.BestChromosome;
|
||||
existingEntity.BestFitnessSoFar = geneticRequest.BestFitnessSoFar;
|
||||
existingEntity.CurrentGeneration = geneticRequest.CurrentGeneration;
|
||||
|
||||
// Update EligibleIndicators JSON
|
||||
if (geneticRequest.EligibleIndicators != null && geneticRequest.EligibleIndicators.Any())
|
||||
{
|
||||
try
|
||||
{
|
||||
existingEntity.EligibleIndicatorsJson = JsonSerializer.Serialize(geneticRequest.EligibleIndicators);
|
||||
}
|
||||
catch
|
||||
{
|
||||
existingEntity.EligibleIndicatorsJson = "[]";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
existingEntity.EligibleIndicatorsJson = "[]";
|
||||
}
|
||||
|
||||
// Only update the tracked entity, do not attach a new one
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteGeneticRequestByIdForUser(User user, string id)
|
||||
{
|
||||
var geneticRequestEntity = _context.GeneticRequests
|
||||
.Include(gr => gr.User)
|
||||
.FirstOrDefault(gr => gr.RequestId == id);
|
||||
|
||||
if (geneticRequestEntity != null && geneticRequestEntity.User != null &&
|
||||
geneticRequestEntity.User.Name == user.Name)
|
||||
{
|
||||
_context.GeneticRequests.Remove(geneticRequestEntity);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteAllGeneticRequestsForUser(User user)
|
||||
{
|
||||
var geneticRequestEntities = _context.GeneticRequests
|
||||
.Include(gr => gr.User)
|
||||
.Where(gr => gr.User != null && gr.User.Name == user.Name)
|
||||
.ToList();
|
||||
|
||||
if (geneticRequestEntities.Any())
|
||||
{
|
||||
_context.GeneticRequests.RemoveRange(geneticRequestEntities);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<GeneticRequest>> GetGeneticRequestsAsync(GeneticRequestStatus status)
|
||||
{
|
||||
var requests = await _context.GeneticRequests
|
||||
.AsNoTracking()
|
||||
.Include(gr => gr.User)
|
||||
.Where(gr => gr.Status == status.ToString())
|
||||
.OrderBy(gr => gr.CreatedAt)
|
||||
.ToListAsync();
|
||||
return PostgreSqlMappers.Map(requests).ToList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,916 @@
|
||||
using Exilion.TradingAtomics;
|
||||
using Managing.Domain.Accounts;
|
||||
using Managing.Domain.Backtests;
|
||||
using Managing.Domain.Bots;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Scenarios;
|
||||
using Managing.Domain.Statistics;
|
||||
using Managing.Domain.Strategies;
|
||||
using Managing.Domain.Trades;
|
||||
using Managing.Domain.Users;
|
||||
using Managing.Domain.Workers;
|
||||
using Managing.Infrastructure.Databases.PostgreSql.Entities;
|
||||
using Newtonsoft.Json;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
using SystemJsonSerializer = System.Text.Json.JsonSerializer;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql;
|
||||
|
||||
public static class PostgreSqlMappers
|
||||
{
|
||||
#region Account Mappings
|
||||
|
||||
public static Account Map(AccountEntity entity)
|
||||
{
|
||||
if (entity == null) return null;
|
||||
|
||||
return new Account
|
||||
{
|
||||
Name = entity.Name,
|
||||
Exchange = entity.Exchange,
|
||||
Type = entity.Type,
|
||||
Key = entity.Key,
|
||||
Secret = entity.Secret,
|
||||
User = entity.User != null ? Map(entity.User) : null,
|
||||
Balances = new List<Balance>() // Empty list for now, balances handled separately if needed
|
||||
};
|
||||
}
|
||||
|
||||
public static AccountEntity Map(Account account)
|
||||
{
|
||||
if (account == null) return null;
|
||||
|
||||
return new AccountEntity
|
||||
{
|
||||
Name = account.Name,
|
||||
Exchange = account.Exchange,
|
||||
Type = account.Type,
|
||||
Key = account.Key,
|
||||
Secret = account.Secret,
|
||||
User = account.User != null ? Map(account.User) : null
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<Account> Map(IEnumerable<AccountEntity> entities)
|
||||
{
|
||||
return entities?.Select(Map) ?? Enumerable.Empty<Account>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MoneyManagement Mappings
|
||||
|
||||
public static MoneyManagement Map(MoneyManagementEntity entity)
|
||||
{
|
||||
if (entity == null) return null;
|
||||
|
||||
return new MoneyManagement
|
||||
{
|
||||
Name = entity.Name,
|
||||
Timeframe = entity.Timeframe,
|
||||
StopLoss = entity.StopLoss,
|
||||
TakeProfit = entity.TakeProfit,
|
||||
Leverage = entity.Leverage,
|
||||
User = entity.User != null ? Map(entity.User) : null
|
||||
};
|
||||
}
|
||||
|
||||
public static MoneyManagementEntity Map(MoneyManagement moneyManagement)
|
||||
{
|
||||
if (moneyManagement == null) return null;
|
||||
|
||||
return new MoneyManagementEntity
|
||||
{
|
||||
Name = moneyManagement.Name,
|
||||
Timeframe = moneyManagement.Timeframe,
|
||||
StopLoss = moneyManagement.StopLoss,
|
||||
TakeProfit = moneyManagement.TakeProfit,
|
||||
Leverage = moneyManagement.Leverage,
|
||||
UserName = moneyManagement.User?.Name,
|
||||
User = moneyManagement.User != null ? Map(moneyManagement.User) : null
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<MoneyManagement> Map(IEnumerable<MoneyManagementEntity> entities)
|
||||
{
|
||||
return entities?.Select(Map) ?? Enumerable.Empty<MoneyManagement>();
|
||||
}
|
||||
|
||||
public static MoneyManagementEntity Map(LightMoneyManagement lightMoneyManagement)
|
||||
{
|
||||
if (lightMoneyManagement == null) return null;
|
||||
|
||||
return new MoneyManagementEntity
|
||||
{
|
||||
Name = lightMoneyManagement.Name,
|
||||
Timeframe = lightMoneyManagement.Timeframe,
|
||||
StopLoss = lightMoneyManagement.StopLoss,
|
||||
TakeProfit = lightMoneyManagement.TakeProfit,
|
||||
Leverage = lightMoneyManagement.Leverage
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region User Mappings
|
||||
|
||||
public static User Map(UserEntity entity)
|
||||
{
|
||||
if (entity == null) return null;
|
||||
|
||||
return new User
|
||||
{
|
||||
Name = entity.Name,
|
||||
AgentName = entity.AgentName,
|
||||
AvatarUrl = entity.AvatarUrl,
|
||||
TelegramChannel = entity.TelegramChannel
|
||||
};
|
||||
}
|
||||
|
||||
public static UserEntity Map(User user)
|
||||
{
|
||||
if (user == null) return null;
|
||||
|
||||
return new UserEntity
|
||||
{
|
||||
Name = user.Name,
|
||||
AgentName = user.AgentName,
|
||||
AvatarUrl = user.AvatarUrl,
|
||||
TelegramChannel = user.TelegramChannel
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GeneticRequest Mappings
|
||||
|
||||
public static GeneticRequest Map(GeneticRequestEntity entity)
|
||||
{
|
||||
if (entity == null) return null;
|
||||
|
||||
var geneticRequest = new GeneticRequest(entity.RequestId)
|
||||
{
|
||||
User = entity.User != null ? Map(entity.User) : null,
|
||||
CreatedAt = entity.CreatedAt,
|
||||
CompletedAt = entity.CompletedAt,
|
||||
Status = Enum.Parse<GeneticRequestStatus>(entity.Status),
|
||||
Ticker = entity.Ticker,
|
||||
Timeframe = entity.Timeframe,
|
||||
StartDate = entity.StartDate,
|
||||
EndDate = entity.EndDate,
|
||||
Balance = entity.Balance,
|
||||
PopulationSize = entity.PopulationSize,
|
||||
Generations = entity.Generations,
|
||||
MutationRate = entity.MutationRate,
|
||||
SelectionMethod = entity.SelectionMethod,
|
||||
CrossoverMethod = entity.CrossoverMethod,
|
||||
MutationMethod = entity.MutationMethod,
|
||||
ElitismPercentage = entity.ElitismPercentage,
|
||||
MaxTakeProfit = entity.MaxTakeProfit,
|
||||
BestFitness = entity.BestFitness,
|
||||
BestIndividual = entity.BestIndividual,
|
||||
ErrorMessage = entity.ErrorMessage,
|
||||
ProgressInfo = entity.ProgressInfo,
|
||||
BestChromosome = entity.BestChromosome,
|
||||
BestFitnessSoFar = entity.BestFitnessSoFar,
|
||||
CurrentGeneration = entity.CurrentGeneration
|
||||
};
|
||||
|
||||
// Deserialize EligibleIndicators from JSON
|
||||
if (!string.IsNullOrEmpty(entity.EligibleIndicatorsJson))
|
||||
{
|
||||
try
|
||||
{
|
||||
geneticRequest.EligibleIndicators = SystemJsonSerializer.Deserialize<List<IndicatorType>>(entity.EligibleIndicatorsJson) ?? new List<IndicatorType>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
geneticRequest.EligibleIndicators = new List<IndicatorType>();
|
||||
}
|
||||
}
|
||||
|
||||
return geneticRequest;
|
||||
}
|
||||
|
||||
public static GeneticRequestEntity Map(GeneticRequest geneticRequest)
|
||||
{
|
||||
if (geneticRequest == null) return null;
|
||||
|
||||
var entity = new GeneticRequestEntity
|
||||
{
|
||||
RequestId = geneticRequest.RequestId,
|
||||
User = geneticRequest.User != null ? Map(geneticRequest.User) : null,
|
||||
CreatedAt = geneticRequest.CreatedAt,
|
||||
CompletedAt = geneticRequest.CompletedAt,
|
||||
UpdatedAt = DateTime.UtcNow,
|
||||
Status = geneticRequest.Status.ToString(),
|
||||
Ticker = geneticRequest.Ticker,
|
||||
Timeframe = geneticRequest.Timeframe,
|
||||
StartDate = geneticRequest.StartDate,
|
||||
EndDate = geneticRequest.EndDate,
|
||||
Balance = geneticRequest.Balance,
|
||||
PopulationSize = geneticRequest.PopulationSize,
|
||||
Generations = geneticRequest.Generations,
|
||||
MutationRate = geneticRequest.MutationRate,
|
||||
SelectionMethod = geneticRequest.SelectionMethod,
|
||||
CrossoverMethod = geneticRequest.CrossoverMethod,
|
||||
MutationMethod = geneticRequest.MutationMethod,
|
||||
ElitismPercentage = geneticRequest.ElitismPercentage,
|
||||
MaxTakeProfit = geneticRequest.MaxTakeProfit,
|
||||
BestFitness = geneticRequest.BestFitness,
|
||||
BestIndividual = geneticRequest.BestIndividual,
|
||||
ErrorMessage = geneticRequest.ErrorMessage,
|
||||
ProgressInfo = geneticRequest.ProgressInfo,
|
||||
BestChromosome = geneticRequest.BestChromosome,
|
||||
BestFitnessSoFar = geneticRequest.BestFitnessSoFar,
|
||||
CurrentGeneration = geneticRequest.CurrentGeneration
|
||||
};
|
||||
|
||||
// Serialize EligibleIndicators to JSON
|
||||
if (geneticRequest.EligibleIndicators != null && geneticRequest.EligibleIndicators.Any())
|
||||
{
|
||||
try
|
||||
{
|
||||
entity.EligibleIndicatorsJson = SystemJsonSerializer.Serialize(geneticRequest.EligibleIndicators);
|
||||
}
|
||||
catch
|
||||
{
|
||||
entity.EligibleIndicatorsJson = "[]";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entity.EligibleIndicatorsJson = "[]";
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
public static IEnumerable<GeneticRequest> Map(IEnumerable<GeneticRequestEntity> entities)
|
||||
{
|
||||
return entities?.Select(Map) ?? Enumerable.Empty<GeneticRequest>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Backtest Mappings
|
||||
|
||||
public static Backtest Map(BacktestEntity entity)
|
||||
{
|
||||
if (entity == null) return null;
|
||||
|
||||
// Deserialize JSON fields using MongoMappers for compatibility
|
||||
var config = JsonConvert.DeserializeObject<TradingBotConfig>(entity.ConfigJson);
|
||||
var positions = JsonConvert.DeserializeObject<List<Position>>(entity.PositionsJson) ?? new List<Position>();
|
||||
var signals = JsonConvert.DeserializeObject<List<LightSignal>>(entity.SignalsJson) ?? new List<LightSignal>();
|
||||
var statistics = !string.IsNullOrEmpty(entity.StatisticsJson)
|
||||
? JsonConvert.DeserializeObject<PerformanceMetrics>(entity.StatisticsJson)
|
||||
: null;
|
||||
|
||||
var backtest = new Backtest(config, positions, signals)
|
||||
{
|
||||
Id = entity.Identifier,
|
||||
FinalPnl = entity.FinalPnl,
|
||||
WinRate = entity.WinRate,
|
||||
GrowthPercentage = entity.GrowthPercentage,
|
||||
HodlPercentage = entity.HodlPercentage,
|
||||
StartDate = entity.StartDate,
|
||||
EndDate = entity.EndDate,
|
||||
User = new User { Name = entity.UserName },
|
||||
Statistics = statistics,
|
||||
Fees = entity.Fees,
|
||||
Score = entity.Score,
|
||||
ScoreMessage = entity.ScoreMessage,
|
||||
RequestId = entity.RequestId,
|
||||
Metadata = entity.Metadata
|
||||
};
|
||||
|
||||
return backtest;
|
||||
}
|
||||
|
||||
public static BacktestEntity Map(Backtest backtest)
|
||||
{
|
||||
if (backtest == null) return null;
|
||||
|
||||
return new BacktestEntity
|
||||
{
|
||||
Identifier = backtest.Id,
|
||||
RequestId = backtest.RequestId,
|
||||
FinalPnl = backtest.FinalPnl,
|
||||
WinRate = backtest.WinRate,
|
||||
GrowthPercentage = backtest.GrowthPercentage,
|
||||
HodlPercentage = backtest.HodlPercentage,
|
||||
ConfigJson = JsonConvert.SerializeObject(backtest.Config),
|
||||
PositionsJson = JsonConvert.SerializeObject(backtest.Positions),
|
||||
SignalsJson = JsonConvert.SerializeObject(backtest.Signals),
|
||||
StartDate = backtest.StartDate,
|
||||
EndDate = backtest.EndDate,
|
||||
MoneyManagementJson = JsonConvert.SerializeObject(backtest.Config?.MoneyManagement),
|
||||
UserName = backtest.User?.Name ?? string.Empty,
|
||||
StatisticsJson = backtest.Statistics != null ? JsonConvert.SerializeObject(backtest.Statistics) : null,
|
||||
Fees = backtest.Fees,
|
||||
Score = backtest.Score,
|
||||
ScoreMessage = backtest.ScoreMessage ?? string.Empty,
|
||||
Metadata = backtest.Metadata?.ToString(),
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<Backtest> Map(IEnumerable<BacktestEntity> entities)
|
||||
{
|
||||
return entities?.Select(Map) ?? Enumerable.Empty<Backtest>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region BundleBacktestRequest Mappings
|
||||
|
||||
public static BundleBacktestRequest Map(BundleBacktestRequestEntity entity)
|
||||
{
|
||||
if (entity == null) return null;
|
||||
|
||||
var bundleRequest = new BundleBacktestRequest(entity.RequestId)
|
||||
{
|
||||
User = entity.User != null ? Map(entity.User) : new User { Name = entity.UserName },
|
||||
CreatedAt = entity.CreatedAt,
|
||||
CompletedAt = entity.CompletedAt,
|
||||
Status = entity.Status,
|
||||
BacktestRequestsJson = entity.BacktestRequestsJson,
|
||||
TotalBacktests = entity.TotalBacktests,
|
||||
CompletedBacktests = entity.CompletedBacktests,
|
||||
FailedBacktests = entity.FailedBacktests,
|
||||
ErrorMessage = entity.ErrorMessage,
|
||||
ProgressInfo = entity.ProgressInfo,
|
||||
CurrentBacktest = entity.CurrentBacktest,
|
||||
EstimatedTimeRemainingSeconds = entity.EstimatedTimeRemainingSeconds,
|
||||
Name = entity.Name
|
||||
};
|
||||
|
||||
// Deserialize Results from JSON
|
||||
if (!string.IsNullOrEmpty(entity.ResultsJson))
|
||||
{
|
||||
try
|
||||
{
|
||||
bundleRequest.Results = JsonConvert.DeserializeObject<List<string>>(entity.ResultsJson) ?? new List<string>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
bundleRequest.Results = new List<string>();
|
||||
}
|
||||
}
|
||||
|
||||
return bundleRequest;
|
||||
}
|
||||
|
||||
public static BundleBacktestRequestEntity Map(BundleBacktestRequest bundleRequest)
|
||||
{
|
||||
if (bundleRequest == null) return null;
|
||||
|
||||
var entity = new BundleBacktestRequestEntity
|
||||
{
|
||||
RequestId = bundleRequest.RequestId,
|
||||
UserName = bundleRequest.User?.Name ?? string.Empty,
|
||||
UserId = null, // Will be set by the repository when saving
|
||||
CreatedAt = bundleRequest.CreatedAt,
|
||||
CompletedAt = bundleRequest.CompletedAt,
|
||||
Status = bundleRequest.Status,
|
||||
BacktestRequestsJson = bundleRequest.BacktestRequestsJson,
|
||||
TotalBacktests = bundleRequest.TotalBacktests,
|
||||
CompletedBacktests = bundleRequest.CompletedBacktests,
|
||||
FailedBacktests = bundleRequest.FailedBacktests,
|
||||
ErrorMessage = bundleRequest.ErrorMessage,
|
||||
ProgressInfo = bundleRequest.ProgressInfo,
|
||||
CurrentBacktest = bundleRequest.CurrentBacktest,
|
||||
EstimatedTimeRemainingSeconds = bundleRequest.EstimatedTimeRemainingSeconds,
|
||||
Name = bundleRequest.Name,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
// Serialize Results to JSON
|
||||
if (bundleRequest.Results != null && bundleRequest.Results.Any())
|
||||
{
|
||||
try
|
||||
{
|
||||
entity.ResultsJson = JsonConvert.SerializeObject(bundleRequest.Results);
|
||||
}
|
||||
catch
|
||||
{
|
||||
entity.ResultsJson = "[]";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entity.ResultsJson = "[]";
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
public static IEnumerable<BundleBacktestRequest> Map(IEnumerable<BundleBacktestRequestEntity> entities)
|
||||
{
|
||||
return entities?.Select(Map) ?? Enumerable.Empty<BundleBacktestRequest>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Trading Mappings
|
||||
|
||||
// Scenario mappings
|
||||
public static Scenario Map(ScenarioEntity entity)
|
||||
{
|
||||
if (entity == null) return null;
|
||||
|
||||
return new Scenario(entity.Name, entity.LoopbackPeriod)
|
||||
{
|
||||
User = entity.UserName != null ? new User { Name = entity.UserName } : null,
|
||||
Indicators = new List<Indicator>() // Will be populated separately when needed
|
||||
};
|
||||
}
|
||||
|
||||
public static ScenarioEntity Map(Scenario scenario)
|
||||
{
|
||||
if (scenario == null) return null;
|
||||
|
||||
return new ScenarioEntity
|
||||
{
|
||||
Name = scenario.Name,
|
||||
LoopbackPeriod = scenario.LoopbackPeriod ?? 1,
|
||||
UserName = scenario.User?.Name
|
||||
};
|
||||
}
|
||||
|
||||
// Indicator mappings
|
||||
public static Indicator Map(IndicatorEntity entity)
|
||||
{
|
||||
if (entity == null) return null;
|
||||
|
||||
return new Indicator(entity.Name, entity.Type)
|
||||
{
|
||||
SignalType = entity.SignalType,
|
||||
MinimumHistory = entity.MinimumHistory,
|
||||
Period = entity.Period,
|
||||
FastPeriods = entity.FastPeriods,
|
||||
SlowPeriods = entity.SlowPeriods,
|
||||
SignalPeriods = entity.SignalPeriods,
|
||||
Multiplier = entity.Multiplier,
|
||||
SmoothPeriods = entity.SmoothPeriods,
|
||||
StochPeriods = entity.StochPeriods,
|
||||
CyclePeriods = entity.CyclePeriods,
|
||||
User = entity.UserName != null ? new User { Name = entity.UserName } : null
|
||||
};
|
||||
}
|
||||
|
||||
public static IndicatorEntity Map(Indicator indicator)
|
||||
{
|
||||
if (indicator == null) return null;
|
||||
|
||||
return new IndicatorEntity
|
||||
{
|
||||
Name = indicator.Name,
|
||||
Type = indicator.Type,
|
||||
Timeframe = Timeframe.FifteenMinutes, // Default timeframe
|
||||
SignalType = indicator.SignalType,
|
||||
MinimumHistory = indicator.MinimumHistory,
|
||||
Period = indicator.Period,
|
||||
FastPeriods = indicator.FastPeriods,
|
||||
SlowPeriods = indicator.SlowPeriods,
|
||||
SignalPeriods = indicator.SignalPeriods,
|
||||
Multiplier = indicator.Multiplier,
|
||||
SmoothPeriods = indicator.SmoothPeriods,
|
||||
StochPeriods = indicator.StochPeriods,
|
||||
CyclePeriods = indicator.CyclePeriods,
|
||||
UserName = indicator.User?.Name
|
||||
};
|
||||
}
|
||||
|
||||
// Signal mappings
|
||||
public static Signal Map(SignalEntity entity)
|
||||
{
|
||||
if (entity == null) return null;
|
||||
|
||||
var candle = !string.IsNullOrEmpty(entity.CandleJson)
|
||||
? JsonConvert.DeserializeObject<Candle>(entity.CandleJson)
|
||||
: null;
|
||||
|
||||
return new Signal(
|
||||
entity.Ticker,
|
||||
entity.Direction,
|
||||
entity.Confidence,
|
||||
candle,
|
||||
entity.Date,
|
||||
TradingExchanges.Evm, // Default exchange
|
||||
entity.Type,
|
||||
entity.SignalType,
|
||||
entity.IndicatorName,
|
||||
entity.UserName != null ? new User { Name = entity.UserName } : null)
|
||||
{
|
||||
Status = entity.Status
|
||||
};
|
||||
}
|
||||
|
||||
public static SignalEntity Map(Signal signal)
|
||||
{
|
||||
if (signal == null) return null;
|
||||
|
||||
return new SignalEntity
|
||||
{
|
||||
Identifier = signal.Identifier,
|
||||
Direction = signal.Direction,
|
||||
Confidence = signal.Confidence,
|
||||
Date = signal.Date,
|
||||
Ticker = signal.Ticker,
|
||||
Status = signal.Status,
|
||||
Timeframe = signal.Timeframe,
|
||||
Type = signal.IndicatorType,
|
||||
SignalType = signal.SignalType,
|
||||
IndicatorName = signal.IndicatorName,
|
||||
UserName = signal.User?.Name,
|
||||
CandleJson = signal.Candle != null ? JsonConvert.SerializeObject(signal.Candle) : null
|
||||
};
|
||||
}
|
||||
|
||||
// Position mappings
|
||||
public static Position Map(PositionEntity entity)
|
||||
{
|
||||
if (entity == null) return null;
|
||||
|
||||
// Deserialize money management
|
||||
var moneyManagement = new MoneyManagement(); // Default money management
|
||||
if (!string.IsNullOrEmpty(entity.MoneyManagementJson))
|
||||
{
|
||||
moneyManagement = JsonConvert.DeserializeObject<MoneyManagement>(entity.MoneyManagementJson) ?? new MoneyManagement();
|
||||
}
|
||||
|
||||
var position = new Position(
|
||||
entity.Identifier,
|
||||
entity.AccountName,
|
||||
entity.OriginDirection,
|
||||
entity.Ticker,
|
||||
moneyManagement,
|
||||
entity.Initiator,
|
||||
entity.Date,
|
||||
entity.UserName != null ? new User { Name = entity.UserName } : null)
|
||||
{
|
||||
Status = entity.Status,
|
||||
SignalIdentifier = entity.SignalIdentifier
|
||||
};
|
||||
|
||||
// Set ProfitAndLoss with proper type
|
||||
position.ProfitAndLoss = new ProfitAndLoss { Realized = entity.ProfitAndLoss };
|
||||
|
||||
// Map related trades
|
||||
if (entity.OpenTrade != null)
|
||||
position.Open = Map(entity.OpenTrade);
|
||||
if (entity.StopLossTrade != null)
|
||||
position.StopLoss = Map(entity.StopLossTrade);
|
||||
if (entity.TakeProfit1Trade != null)
|
||||
position.TakeProfit1 = Map(entity.TakeProfit1Trade);
|
||||
if (entity.TakeProfit2Trade != null)
|
||||
position.TakeProfit2 = Map(entity.TakeProfit2Trade);
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
public static PositionEntity Map(Position position)
|
||||
{
|
||||
if (position == null) return null;
|
||||
|
||||
return new PositionEntity
|
||||
{
|
||||
Identifier = position.Identifier,
|
||||
Date = position.Date,
|
||||
ProfitAndLoss = position.ProfitAndLoss?.Realized ?? 0,
|
||||
OriginDirection = position.OriginDirection,
|
||||
Status = position.Status,
|
||||
Ticker = position.Ticker,
|
||||
Initiator = position.Initiator,
|
||||
SignalIdentifier = position.SignalIdentifier,
|
||||
AccountName = position.AccountName,
|
||||
UserName = position.User?.Name,
|
||||
MoneyManagementJson = position.MoneyManagement != null ? JsonConvert.SerializeObject(position.MoneyManagement) : null
|
||||
};
|
||||
}
|
||||
|
||||
// Trade mappings
|
||||
public static Trade Map(TradeEntity entity)
|
||||
{
|
||||
if (entity == null) return null;
|
||||
|
||||
return new Trade(
|
||||
entity.Date,
|
||||
entity.Direction,
|
||||
entity.Status,
|
||||
entity.TradeType,
|
||||
entity.Ticker,
|
||||
entity.Quantity,
|
||||
entity.Price,
|
||||
entity.Leverage,
|
||||
entity.ExchangeOrderId,
|
||||
entity.Message)
|
||||
{
|
||||
Fee = entity.Fee
|
||||
};
|
||||
}
|
||||
|
||||
public static TradeEntity Map(Trade trade)
|
||||
{
|
||||
if (trade == null) return null;
|
||||
|
||||
return new TradeEntity
|
||||
{
|
||||
Date = trade.Date,
|
||||
Direction = trade.Direction,
|
||||
Status = trade.Status,
|
||||
TradeType = trade.TradeType,
|
||||
Ticker = trade.Ticker,
|
||||
Fee = trade.Fee,
|
||||
Quantity = trade.Quantity,
|
||||
Price = trade.Price,
|
||||
Leverage = trade.Leverage,
|
||||
ExchangeOrderId = trade.ExchangeOrderId,
|
||||
Message = trade.Message
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Collection mappings
|
||||
public static IEnumerable<Scenario> Map(IEnumerable<ScenarioEntity> entities)
|
||||
{
|
||||
return entities?.Select(Map) ?? Enumerable.Empty<Scenario>();
|
||||
}
|
||||
|
||||
public static IEnumerable<Indicator> Map(IEnumerable<IndicatorEntity> entities)
|
||||
{
|
||||
return entities?.Select(Map) ?? Enumerable.Empty<Indicator>();
|
||||
}
|
||||
|
||||
public static IEnumerable<Signal> Map(IEnumerable<SignalEntity> entities)
|
||||
{
|
||||
return entities?.Select(Map) ?? Enumerable.Empty<Signal>();
|
||||
}
|
||||
|
||||
public static IEnumerable<Position> Map(IEnumerable<PositionEntity> entities)
|
||||
{
|
||||
return entities?.Select(Map) ?? Enumerable.Empty<Position>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Bot Mappings
|
||||
|
||||
// BotBackup mappings
|
||||
public static BotBackup Map(BotBackupEntity entity)
|
||||
{
|
||||
if (entity == null) return null;
|
||||
|
||||
var botBackup = new BotBackup
|
||||
{
|
||||
Identifier = entity.Identifier,
|
||||
User = entity.User != null ? Map(entity.User) : null,
|
||||
LastStatus = entity.LastStatus,
|
||||
CreateDate = entity.CreateDate
|
||||
};
|
||||
|
||||
// Deserialize the JSON data using the helper method
|
||||
botBackup.DeserializeData(entity.Data);
|
||||
|
||||
return botBackup;
|
||||
}
|
||||
|
||||
public static BotBackupEntity Map(BotBackup botBackup)
|
||||
{
|
||||
if (botBackup == null) return null;
|
||||
|
||||
return new BotBackupEntity
|
||||
{
|
||||
Identifier = botBackup.Identifier,
|
||||
UserName = botBackup.User?.Name,
|
||||
User = botBackup.User != null ? Map(botBackup.User) : null,
|
||||
Data = botBackup.SerializeData(), // Serialize the data using the helper method
|
||||
LastStatus = botBackup.LastStatus,
|
||||
CreateDate = botBackup.CreateDate,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<BotBackup> Map(IEnumerable<BotBackupEntity> entities)
|
||||
{
|
||||
return entities?.Select(Map) ?? Enumerable.Empty<BotBackup>();
|
||||
}
|
||||
|
||||
public static IEnumerable<BotBackupEntity> Map(IEnumerable<BotBackup> botBackups)
|
||||
{
|
||||
return botBackups?.Select(Map) ?? Enumerable.Empty<BotBackupEntity>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Statistics Mappings
|
||||
|
||||
// TopVolumeTicker mappings
|
||||
public static TopVolumeTicker Map(TopVolumeTickerEntity entity)
|
||||
{
|
||||
if (entity == null) return null;
|
||||
|
||||
return new TopVolumeTicker
|
||||
{
|
||||
Ticker = entity.Ticker,
|
||||
Date = entity.Date,
|
||||
Volume = entity.Volume,
|
||||
Rank = entity.Rank,
|
||||
Exchange = entity.Exchange
|
||||
};
|
||||
}
|
||||
|
||||
public static TopVolumeTickerEntity Map(TopVolumeTicker topVolumeTicker)
|
||||
{
|
||||
if (topVolumeTicker == null) return null;
|
||||
|
||||
return new TopVolumeTickerEntity
|
||||
{
|
||||
Ticker = topVolumeTicker.Ticker,
|
||||
Date = topVolumeTicker.Date,
|
||||
Volume = topVolumeTicker.Volume,
|
||||
Rank = topVolumeTicker.Rank,
|
||||
Exchange = topVolumeTicker.Exchange
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<TopVolumeTicker> Map(IEnumerable<TopVolumeTickerEntity> entities)
|
||||
{
|
||||
return entities?.Select(Map) ?? Enumerable.Empty<TopVolumeTicker>();
|
||||
}
|
||||
|
||||
// SpotlightOverview mappings
|
||||
public static SpotlightOverview Map(SpotlightOverviewEntity entity)
|
||||
{
|
||||
if (entity == null) return null;
|
||||
|
||||
var overview = new SpotlightOverview
|
||||
{
|
||||
Identifier = entity.Identifier,
|
||||
DateTime = entity.DateTime,
|
||||
ScenarioCount = entity.ScenarioCount,
|
||||
Spotlights = new List<Spotlight>()
|
||||
};
|
||||
|
||||
// Deserialize the JSON spotlights data
|
||||
if (!string.IsNullOrEmpty(entity.SpotlightsJson))
|
||||
{
|
||||
try
|
||||
{
|
||||
overview.Spotlights = SystemJsonSerializer.Deserialize<List<Spotlight>>(entity.SpotlightsJson) ?? new List<Spotlight>();
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
// If deserialization fails, return empty list
|
||||
overview.Spotlights = new List<Spotlight>();
|
||||
}
|
||||
}
|
||||
|
||||
return overview;
|
||||
}
|
||||
|
||||
public static SpotlightOverviewEntity Map(SpotlightOverview overview)
|
||||
{
|
||||
if (overview == null) return null;
|
||||
|
||||
var entity = new SpotlightOverviewEntity
|
||||
{
|
||||
Identifier = overview.Identifier,
|
||||
DateTime = overview.DateTime,
|
||||
ScenarioCount = overview.ScenarioCount
|
||||
};
|
||||
|
||||
// Serialize the spotlights to JSON
|
||||
if (overview.Spotlights != null)
|
||||
{
|
||||
entity.SpotlightsJson = SystemJsonSerializer.Serialize(overview.Spotlights);
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
public static IEnumerable<SpotlightOverview> Map(IEnumerable<SpotlightOverviewEntity> entities)
|
||||
{
|
||||
return entities?.Select(Map) ?? Enumerable.Empty<SpotlightOverview>();
|
||||
}
|
||||
|
||||
// Trader mappings
|
||||
public static Trader Map(TraderEntity entity)
|
||||
{
|
||||
if (entity == null) return null;
|
||||
|
||||
return new Trader
|
||||
{
|
||||
Address = entity.Address,
|
||||
Winrate = entity.Winrate,
|
||||
Pnl = entity.Pnl,
|
||||
TradeCount = entity.TradeCount,
|
||||
AverageWin = entity.AverageWin,
|
||||
AverageLoss = entity.AverageLoss,
|
||||
Roi = entity.Roi
|
||||
};
|
||||
}
|
||||
|
||||
public static TraderEntity Map(Trader trader, bool isBestTrader)
|
||||
{
|
||||
if (trader == null) return null;
|
||||
|
||||
return new TraderEntity
|
||||
{
|
||||
Address = trader.Address,
|
||||
Winrate = trader.Winrate,
|
||||
Pnl = trader.Pnl,
|
||||
TradeCount = trader.TradeCount,
|
||||
AverageWin = trader.AverageWin,
|
||||
AverageLoss = trader.AverageLoss,
|
||||
Roi = trader.Roi,
|
||||
IsBestTrader = isBestTrader
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<Trader> Map(IEnumerable<TraderEntity> entities)
|
||||
{
|
||||
return entities?.Select(Map) ?? Enumerable.Empty<Trader>();
|
||||
}
|
||||
|
||||
// FundingRate mappings
|
||||
public static FundingRate Map(FundingRateEntity entity)
|
||||
{
|
||||
if (entity == null) return null;
|
||||
|
||||
return new FundingRate
|
||||
{
|
||||
Ticker = entity.Ticker,
|
||||
Exchange = entity.Exchange,
|
||||
Rate = entity.Rate,
|
||||
OpenInterest = entity.OpenInterest,
|
||||
Date = entity.Date,
|
||||
Direction = entity.Direction
|
||||
};
|
||||
}
|
||||
|
||||
public static FundingRateEntity Map(FundingRate fundingRate)
|
||||
{
|
||||
if (fundingRate == null) return null;
|
||||
|
||||
return new FundingRateEntity
|
||||
{
|
||||
Ticker = fundingRate.Ticker,
|
||||
Exchange = fundingRate.Exchange,
|
||||
Rate = fundingRate.Rate,
|
||||
OpenInterest = fundingRate.OpenInterest,
|
||||
Date = fundingRate.Date,
|
||||
Direction = fundingRate.Direction
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<FundingRate> Map(IEnumerable<FundingRateEntity> entities)
|
||||
{
|
||||
return entities?.Select(Map) ?? Enumerable.Empty<FundingRate>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Worker Mappings
|
||||
|
||||
public static Worker Map(WorkerEntity entity)
|
||||
{
|
||||
if (entity == null) return null;
|
||||
return new Worker
|
||||
{
|
||||
WorkerType = entity.WorkerType,
|
||||
StartTime = entity.StartTime,
|
||||
LastRunTime = entity.LastRunTime,
|
||||
ExecutionCount = entity.ExecutionCount,
|
||||
Delay = TimeSpan.FromTicks(entity.DelayTicks),
|
||||
IsActive = entity.IsActive
|
||||
};
|
||||
}
|
||||
|
||||
public static WorkerEntity Map(Worker worker)
|
||||
{
|
||||
if (worker == null) return null;
|
||||
return new WorkerEntity
|
||||
{
|
||||
WorkerType = worker.WorkerType,
|
||||
StartTime = worker.StartTime,
|
||||
LastRunTime = worker.LastRunTime,
|
||||
ExecutionCount = worker.ExecutionCount,
|
||||
DelayTicks = worker.Delay.Ticks,
|
||||
IsActive = worker.IsActive
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<Worker> Map(IEnumerable<WorkerEntity> entities)
|
||||
{
|
||||
return entities?.Select(Map) ?? Enumerable.Empty<Worker>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
using Managing.Application.Abstractions.Repositories;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Users;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Managing.Infrastructure.Databases.PostgreSql;
|
||||
|
||||
public class PostgreSqlSettingsRepository : ISettingsRepository
|
||||
{
|
||||
private readonly ManagingDbContext _context;
|
||||
|
||||
public PostgreSqlSettingsRepository(ManagingDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task DeleteMoneyManagementAsync(string name)
|
||||
{
|
||||
var moneyManagement = await _context.MoneyManagements
|
||||
.AsTracking()
|
||||
.FirstOrDefaultAsync(m => m.Name == name)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (moneyManagement != null)
|
||||
{
|
||||
_context.MoneyManagements.Remove(moneyManagement);
|
||||
await _context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteMoneyManagementsAsync()
|
||||
{
|
||||
var moneyManagements = await _context.MoneyManagements
|
||||
.AsTracking()
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
_context.MoneyManagements.RemoveRange(moneyManagements);
|
||||
await _context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<MoneyManagement> GetMoneyManagement(string name)
|
||||
{
|
||||
var moneyManagement = await _context.MoneyManagements
|
||||
.AsNoTracking()
|
||||
.Include(m => m.User)
|
||||
.FirstOrDefaultAsync(m => m.Name == name)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return PostgreSqlMappers.Map(moneyManagement);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<MoneyManagement>> GetMoneyManagementsAsync()
|
||||
{
|
||||
var moneyManagements = await _context.MoneyManagements
|
||||
.AsNoTracking()
|
||||
.Include(m => m.User)
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return PostgreSqlMappers.Map(moneyManagements);
|
||||
}
|
||||
|
||||
public async Task InsertMoneyManagement(LightMoneyManagement request, User user)
|
||||
{
|
||||
// Check if money management already exists for the same user
|
||||
var existingMoneyManagement = await _context.MoneyManagements
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(m => m.Name == request.Name &&
|
||||
((user == null && m.UserName == null) ||
|
||||
(user != null && m.UserName == user.Name)))
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (existingMoneyManagement != null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Money management with name '{request.Name}' already exists for user '{user?.Name}'");
|
||||
}
|
||||
|
||||
var entity = PostgreSqlMappers.Map(request);
|
||||
entity.UserName = user?.Name;
|
||||
|
||||
// Set the UserId if user is provided
|
||||
if (user != null)
|
||||
{
|
||||
var userEntity = await _context.Users
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(u => u.Name == user.Name)
|
||||
.ConfigureAwait(false);
|
||||
entity.UserId = userEntity?.Id;
|
||||
}
|
||||
|
||||
await _context.MoneyManagements.AddAsync(entity).ConfigureAwait(false);
|
||||
await _context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task UpdateMoneyManagementAsync(LightMoneyManagement moneyManagement, User user)
|
||||
{
|
||||
var entity = await _context.MoneyManagements
|
||||
.AsTracking()
|
||||
.FirstOrDefaultAsync(m => m.Name == moneyManagement.Name &&
|
||||
(user != null && m.UserName == user.Name))
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (entity != null)
|
||||
{
|
||||
entity.Timeframe = moneyManagement.Timeframe;
|
||||
entity.StopLoss = moneyManagement.StopLoss;
|
||||
entity.TakeProfit = moneyManagement.TakeProfit;
|
||||
entity.Leverage = moneyManagement.Leverage;
|
||||
entity.UserName = user?.Name;
|
||||
entity.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
await _context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
// User-specific implementations
|
||||
public async Task<MoneyManagement> GetMoneyManagementByUser(User user, string name)
|
||||
{
|
||||
var moneyManagement = await _context.MoneyManagements
|
||||
.AsNoTracking()
|
||||
.Include(m => m.User)
|
||||
.FirstOrDefaultAsync(m =>
|
||||
m.Name == name &&
|
||||
m.UserName != null &&
|
||||
m.UserName == user.Name)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return PostgreSqlMappers.Map(moneyManagement);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<MoneyManagement>> GetMoneyManagementsByUserAsync(User user)
|
||||
{
|
||||
var moneyManagements = await _context.MoneyManagements
|
||||
.AsNoTracking()
|
||||
.Include(m => m.User)
|
||||
.Where(m => m.UserName != null && m.UserName == user.Name)
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return PostgreSqlMappers.Map(moneyManagements);
|
||||
}
|
||||
|
||||
public async Task DeleteMoneyManagementByUserAsync(User user, string name)
|
||||
{
|
||||
var moneyManagement = await _context.MoneyManagements
|
||||
.AsTracking()
|
||||
.FirstOrDefaultAsync(m =>
|
||||
m.Name == name &&
|
||||
m.UserName != null &&
|
||||
m.UserName == user.Name)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (moneyManagement != null)
|
||||
{
|
||||
_context.MoneyManagements.Remove(moneyManagement);
|
||||
await _context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteMoneyManagementsByUserAsync(User user)
|
||||
{
|
||||
var moneyManagements = await _context.MoneyManagements
|
||||
.AsTracking()
|
||||
.Where(m => m.UserName != null && m.UserName == user.Name)
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
_context.MoneyManagements.RemoveRange(moneyManagements);
|
||||
await _context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user