* 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
223 lines
9.3 KiB
C#
223 lines
9.3 KiB
C#
using System.Text.Json;
|
|
using Managing.Application.Abstractions.Repositories;
|
|
using Managing.Domain.Synth.Models;
|
|
using Managing.Infrastructure.Databases.PostgreSql.Entities;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace Managing.Infrastructure.Databases.PostgreSql;
|
|
|
|
public class PostgreSqlSynthRepository : ISynthRepository
|
|
{
|
|
private readonly ManagingDbContext _context;
|
|
private readonly ILogger<PostgreSqlSynthRepository> _logger;
|
|
|
|
public PostgreSqlSynthRepository(ManagingDbContext context, ILogger<PostgreSqlSynthRepository> logger)
|
|
{
|
|
_context = context;
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task<SynthMinersLeaderboard?> GetLeaderboardAsync(string cacheKey)
|
|
{
|
|
try
|
|
{
|
|
var entity = await _context.SynthMinersLeaderboards.AsNoTracking().FirstOrDefaultAsync(x => x.CacheKey == cacheKey);
|
|
if (entity == null)
|
|
{
|
|
_logger.LogDebug($"[PG Synth] No leaderboard cache found for key: {cacheKey}");
|
|
return null;
|
|
}
|
|
var miners = JsonSerializer.Deserialize<List<MinerInfo>>(entity.MinersData);
|
|
return new SynthMinersLeaderboard
|
|
{
|
|
Id = entity.Id.ToString(),
|
|
Asset = entity.Asset,
|
|
TimeIncrement = entity.TimeIncrement,
|
|
SignalDate = entity.SignalDate,
|
|
IsBacktest = entity.IsBacktest,
|
|
Miners = miners ?? new List<MinerInfo>(),
|
|
CreatedAt = entity.CreatedAt
|
|
};
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, $"Error retrieving leaderboard cache for key: {cacheKey}");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public async Task SaveLeaderboardAsync(SynthMinersLeaderboard leaderboard)
|
|
{
|
|
try
|
|
{
|
|
leaderboard.CreatedAt = DateTime.UtcNow;
|
|
var cacheKey = leaderboard.GetCacheKey();
|
|
var minersData = JsonSerializer.Serialize(leaderboard.Miners);
|
|
var existing = await _context.SynthMinersLeaderboards.FirstOrDefaultAsync(x => x.CacheKey == cacheKey);
|
|
if (existing != null)
|
|
{
|
|
existing.Asset = leaderboard.Asset;
|
|
existing.TimeIncrement = leaderboard.TimeIncrement;
|
|
existing.SignalDate = leaderboard.SignalDate;
|
|
existing.IsBacktest = leaderboard.IsBacktest;
|
|
existing.MinersData = minersData;
|
|
existing.CreatedAt = leaderboard.CreatedAt;
|
|
_context.SynthMinersLeaderboards.Update(existing);
|
|
_logger.LogDebug($"[PG Synth] Updated leaderboard in DB for key: {cacheKey}");
|
|
}
|
|
else
|
|
{
|
|
var entity = new SynthMinersLeaderboardEntity
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
Asset = leaderboard.Asset,
|
|
TimeIncrement = leaderboard.TimeIncrement,
|
|
SignalDate = leaderboard.SignalDate,
|
|
IsBacktest = leaderboard.IsBacktest,
|
|
MinersData = minersData,
|
|
CacheKey = cacheKey,
|
|
CreatedAt = leaderboard.CreatedAt
|
|
};
|
|
await _context.SynthMinersLeaderboards.AddAsync(entity);
|
|
_logger.LogDebug($"[PG Synth] Saved new leaderboard to DB for key: {cacheKey}");
|
|
}
|
|
await _context.SaveChangesAsync();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, $"Error saving leaderboard cache for key: {leaderboard.GetCacheKey()}");
|
|
}
|
|
}
|
|
|
|
public async Task<List<SynthPrediction>> GetIndividualPredictionsAsync(string asset, int timeIncrement, int timeLength, List<int> minerUids, bool isBacktest, DateTime? signalDate)
|
|
{
|
|
try
|
|
{
|
|
var results = new List<SynthPrediction>();
|
|
foreach (var minerUid in minerUids)
|
|
{
|
|
var cacheKey = $"{asset}_{timeIncrement}_{timeLength}_{minerUid}";
|
|
if (isBacktest && signalDate.HasValue)
|
|
{
|
|
cacheKey += $"_backtest_{signalDate.Value:yyyy-MM-dd-HH}";
|
|
}
|
|
var entity = await _context.SynthPredictions.AsNoTracking().FirstOrDefaultAsync(x => x.CacheKey == cacheKey);
|
|
if (entity != null)
|
|
{
|
|
var prediction = JsonSerializer.Deserialize<MinerPrediction>(entity.PredictionData);
|
|
if (prediction != null)
|
|
{
|
|
results.Add(new SynthPrediction
|
|
{
|
|
Id = entity.Id.ToString(),
|
|
Asset = entity.Asset,
|
|
MinerUid = entity.MinerUid,
|
|
TimeIncrement = entity.TimeIncrement,
|
|
TimeLength = entity.TimeLength,
|
|
SignalDate = entity.SignalDate,
|
|
IsBacktest = entity.IsBacktest,
|
|
Prediction = prediction,
|
|
CreatedAt = entity.CreatedAt
|
|
});
|
|
}
|
|
}
|
|
}
|
|
if (results.Any())
|
|
{
|
|
_logger.LogDebug($"[PG Synth] Retrieved {results.Count}/{minerUids.Count} individual predictions for {asset}");
|
|
}
|
|
else
|
|
{
|
|
_logger.LogDebug($"[PG Synth] No individual predictions found for {asset}");
|
|
}
|
|
return results;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, $"Error retrieving individual predictions cache for asset: {asset}");
|
|
return new List<SynthPrediction>();
|
|
}
|
|
}
|
|
|
|
public async Task SaveIndividualPredictionAsync(SynthPrediction prediction)
|
|
{
|
|
try
|
|
{
|
|
prediction.CreatedAt = DateTime.UtcNow;
|
|
var cacheKey = prediction.GetCacheKey();
|
|
var predictionData = JsonSerializer.Serialize(prediction.Prediction);
|
|
var existing = await _context.SynthPredictions.FirstOrDefaultAsync(x => x.CacheKey == cacheKey).ConfigureAwait(false);
|
|
if (existing != null)
|
|
{
|
|
existing.Asset = prediction.Asset;
|
|
existing.MinerUid = prediction.MinerUid;
|
|
existing.TimeIncrement = prediction.TimeIncrement;
|
|
existing.TimeLength = prediction.TimeLength;
|
|
existing.SignalDate = prediction.SignalDate;
|
|
existing.IsBacktest = prediction.IsBacktest;
|
|
existing.PredictionData = predictionData;
|
|
existing.CreatedAt = prediction.CreatedAt;
|
|
_context.SynthPredictions.Update(existing);
|
|
_logger.LogDebug($"[PG Synth] Updated individual prediction for miner {prediction.MinerUid}");
|
|
}
|
|
else
|
|
{
|
|
var entity = new SynthPredictionEntity
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
Asset = prediction.Asset,
|
|
MinerUid = prediction.MinerUid,
|
|
TimeIncrement = prediction.TimeIncrement,
|
|
TimeLength = prediction.TimeLength,
|
|
SignalDate = prediction.SignalDate,
|
|
IsBacktest = prediction.IsBacktest,
|
|
PredictionData = predictionData,
|
|
CacheKey = cacheKey,
|
|
CreatedAt = prediction.CreatedAt
|
|
};
|
|
await _context.SynthPredictions.AddAsync(entity).ConfigureAwait(false);
|
|
_logger.LogDebug($"[PG Synth] Saved new individual prediction for miner {prediction.MinerUid}");
|
|
}
|
|
await _context.SaveChangesAsync().ConfigureAwait(false);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, $"Error saving individual prediction cache for miner {prediction.MinerUid}: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
public async Task SaveIndividualPredictionsAsync(List<SynthPrediction> predictions)
|
|
{
|
|
if (!predictions.Any())
|
|
return;
|
|
try
|
|
{
|
|
var saveTasks = predictions.Select(SaveIndividualPredictionAsync);
|
|
await Task.WhenAll(saveTasks);
|
|
_logger.LogInformation($"[PG Synth] Successfully saved {predictions.Count} individual predictions to DB");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, $"Error saving batch of {predictions.Count} individual predictions");
|
|
}
|
|
}
|
|
|
|
public async Task CleanupOldDataAsync(int retentionDays = 30)
|
|
{
|
|
try
|
|
{
|
|
var cutoffDate = DateTime.UtcNow.AddDays(-retentionDays);
|
|
var oldLeaderboards = _context.SynthMinersLeaderboards.Where(x => x.CreatedAt < cutoffDate);
|
|
_context.SynthMinersLeaderboards.RemoveRange(oldLeaderboards);
|
|
var oldPredictions = _context.SynthPredictions.Where(x => x.CreatedAt < cutoffDate);
|
|
_context.SynthPredictions.RemoveRange(oldPredictions);
|
|
await _context.SaveChangesAsync();
|
|
_logger.LogInformation($"[PG Synth] Cleaned up old Synth cache data older than {retentionDays} days");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, $"Error during cleanup of old Synth cache data");
|
|
}
|
|
}
|
|
} |