using Exilion.TradingAtomics; using Managing.Domain.Accounts; using Managing.Domain.Backtests; using Managing.Domain.Bots; using Managing.Domain.Candles; using Managing.Domain.Indicators; 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 { Id = entity.Id, 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(), // Empty list for now, balances handled separately if needed IsGmxInitialized = entity.IsGmxInitialized }; } public static AccountEntity Map(Account account) { if (account == null) return null; return new AccountEntity { Id = account.Id, Name = account.Name, Exchange = account.Exchange, Type = account.Type, Key = account.Key, Secret = account.Secret, UserId = account.User.Id, IsGmxInitialized = account.IsGmxInitialized }; } public static IEnumerable Map(IEnumerable entities) { return entities?.Select(Map) ?? Enumerable.Empty(); } #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, UserId = moneyManagement.User?.Id ?? 0 }; } public static IEnumerable Map(IEnumerable entities) { return entities?.Select(Map) ?? Enumerable.Empty(); } 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, Id = entity.Id, // Assuming Id is the primary key for UserEntity Accounts = entity.Accounts?.Select(MapAccountWithoutUser).ToList() ?? new List() }; } // Helper method to map AccountEntity without User to avoid circular reference private static Account MapAccountWithoutUser(AccountEntity entity) { if (entity == null) return null; return new Account { Id = entity.Id, Name = entity.Name, Exchange = entity.Exchange, Type = entity.Type, Key = entity.Key, Secret = entity.Secret, User = null, // Don't map User to avoid circular reference Balances = new List(), // Empty list for now, balances handled separately if needed IsGmxInitialized = entity.IsGmxInitialized }; } 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(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>(entity.EligibleIndicatorsJson) ?? new List(); } catch { geneticRequest.EligibleIndicators = new List(); } } return geneticRequest; } public static GeneticRequestEntity Map(GeneticRequest geneticRequest) { if (geneticRequest == null) return null; var entity = new GeneticRequestEntity { RequestId = geneticRequest.RequestId, UserId = geneticRequest.User?.Id ?? 0, 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 Map(IEnumerable entities) { return entities?.Select(Map) ?? Enumerable.Empty(); } #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(entity.ConfigJson); var positionsList = JsonConvert.DeserializeObject>(entity.PositionsJson) ?? new List(); var positions = positionsList.ToDictionary(p => p.Identifier, p => p); var signalsList = JsonConvert.DeserializeObject>(entity.SignalsJson) ?? new List(); var signals = signalsList.ToDictionary(s => s.Identifier, s => s); var statistics = !string.IsNullOrEmpty(entity.StatisticsJson) ? JsonConvert.DeserializeObject(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 = entity.User != null ? Map(entity.User) : null, Statistics = statistics, Fees = entity.Fees, Score = entity.Score, ScoreMessage = entity.ScoreMessage, RequestId = entity.RequestId, Metadata = entity.Metadata, InitialBalance = entity.InitialBalance, NetPnl = entity.NetPnl }; 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), Name = backtest.Config?.Name ?? string.Empty, Ticker = backtest.Config?.Ticker.ToString() ?? string.Empty, Timeframe = (int)backtest.Config.Timeframe, IndicatorsCsv = string.Join(',', backtest.Config.Scenario.Indicators.Select(i => i.Type.ToString())), IndicatorsCount = backtest.Config.Scenario.Indicators.Count, PositionsJson = JsonConvert.SerializeObject(backtest.Positions.Values.ToList()), SignalsJson = JsonConvert.SerializeObject(backtest.Signals.Values.ToList()), StartDate = backtest.StartDate, EndDate = backtest.EndDate, Duration = backtest.EndDate - backtest.StartDate, MoneyManagementJson = JsonConvert.SerializeObject(backtest.Config?.MoneyManagement), UserId = backtest.User?.Id ?? 0, StatisticsJson = backtest.Statistics != null ? JsonConvert.SerializeObject(backtest.Statistics) : null, SharpeRatio = backtest.Statistics?.SharpeRatio ?? 0m, MaxDrawdown = backtest.Statistics?.MaxDrawdown ?? 0m, MaxDrawdownRecoveryTime = backtest.Statistics?.MaxDrawdownRecoveryTime ?? TimeSpan.Zero, Fees = backtest.Fees, Score = backtest.Score, ScoreMessage = backtest.ScoreMessage ?? string.Empty, Metadata = backtest.Metadata?.ToString(), CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow, InitialBalance = backtest.InitialBalance, NetPnl = backtest.NetPnl }; } public static IEnumerable Map(IEnumerable entities) { return entities?.Select(Map) ?? Enumerable.Empty(); } #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) : null, CreatedAt = entity.CreatedAt, CompletedAt = entity.CompletedAt, Status = entity.Status, UniversalConfigJson = entity.UniversalConfigJson, DateTimeRangesJson = entity.DateTimeRangesJson, MoneyManagementVariantsJson = entity.MoneyManagementVariantsJson, TickerVariantsJson = entity.TickerVariantsJson, TotalBacktests = entity.TotalBacktests, CompletedBacktests = entity.CompletedBacktests, FailedBacktests = entity.FailedBacktests, ErrorMessage = entity.ErrorMessage, ProgressInfo = entity.ProgressInfo, CurrentBacktest = entity.CurrentBacktest, EstimatedTimeRemainingSeconds = entity.EstimatedTimeRemainingSeconds, Name = entity.Name, Version = entity.Version }; // Deserialize Results from JSON if (!string.IsNullOrEmpty(entity.ResultsJson)) { try { bundleRequest.Results = JsonConvert.DeserializeObject>(entity.ResultsJson) ?? new List(); } catch { bundleRequest.Results = new List(); } } return bundleRequest; } public static BundleBacktestRequestEntity Map(BundleBacktestRequest bundleRequest) { if (bundleRequest == null) return null; var entity = new BundleBacktestRequestEntity { RequestId = bundleRequest.RequestId, UserId = bundleRequest.User?.Id ?? 0, CreatedAt = bundleRequest.CreatedAt, CompletedAt = bundleRequest.CompletedAt, Status = bundleRequest.Status, UniversalConfigJson = bundleRequest.UniversalConfigJson, DateTimeRangesJson = bundleRequest.DateTimeRangesJson, MoneyManagementVariantsJson = bundleRequest.MoneyManagementVariantsJson, TickerVariantsJson = bundleRequest.TickerVariantsJson, TotalBacktests = bundleRequest.TotalBacktests, CompletedBacktests = bundleRequest.CompletedBacktests, FailedBacktests = bundleRequest.FailedBacktests, ErrorMessage = bundleRequest.ErrorMessage, ProgressInfo = bundleRequest.ProgressInfo, CurrentBacktest = bundleRequest.CurrentBacktest, EstimatedTimeRemainingSeconds = bundleRequest.EstimatedTimeRemainingSeconds, Name = bundleRequest.Name, Version = bundleRequest.Version, 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 Map(IEnumerable entities) { return entities?.Select(Map) ?? Enumerable.Empty(); } #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.User != null ? Map(entity.User) : null, Indicators = new List() // 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, UserId = scenario.User?.Id ?? 0 }; } // Indicator mappings public static IndicatorBase Map(IndicatorEntity entity) { if (entity == null) return null; return new IndicatorBase(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.User != null ? Map(entity.User) : null }; } public static IndicatorEntity Map(IndicatorBase indicatorBase) { if (indicatorBase == null) return null; return new IndicatorEntity { Name = indicatorBase.Name, Type = indicatorBase.Type, Timeframe = Timeframe.FifteenMinutes, // Default timeframe SignalType = indicatorBase.SignalType, MinimumHistory = indicatorBase.MinimumHistory, Period = indicatorBase.Period, FastPeriods = indicatorBase.FastPeriods, SlowPeriods = indicatorBase.SlowPeriods, SignalPeriods = indicatorBase.SignalPeriods, Multiplier = indicatorBase.Multiplier, SmoothPeriods = indicatorBase.SmoothPeriods, StochPeriods = indicatorBase.StochPeriods, CyclePeriods = indicatorBase.CyclePeriods, UserId = indicatorBase.User?.Id ?? 0 }; } // Signal mappings public static Signal Map(SignalEntity entity) { if (entity == null) return null; var candle = !string.IsNullOrEmpty(entity.CandleJson) ? JsonConvert.DeserializeObject(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.User != null ? Map(entity.User) : 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, UserId = signal.User?.Id ?? 0, 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(entity.MoneyManagementJson) ?? new MoneyManagement(); } var position = new Position( entity.Identifier, entity.AccountId, entity.OriginDirection, entity.Ticker, moneyManagement, entity.Initiator, entity.Date, entity.User != null ? Map(entity.User) : null) { Status = entity.Status, SignalIdentifier = entity.SignalIdentifier, InitiatorIdentifier = entity.InitiatorIdentifier }; // Set ProfitAndLoss with proper type position.ProfitAndLoss = new ProfitAndLoss { Realized = entity.ProfitAndLoss, Net = entity.NetPnL }; // Set fee properties position.UiFees = entity.UiFees; position.GasFees = entity.GasFees; // 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, AccountId = position.AccountId, ProfitAndLoss = position.ProfitAndLoss?.Realized ?? 0, UiFees = position.UiFees, GasFees = position.GasFees, OriginDirection = position.OriginDirection, Status = position.Status, Ticker = position.Ticker, Initiator = position.Initiator, SignalIdentifier = position.SignalIdentifier, UserId = position.User?.Id ?? 0, InitiatorIdentifier = position.InitiatorIdentifier, MoneyManagementJson = position.MoneyManagement != null ? JsonConvert.SerializeObject(position.MoneyManagement) : null, NetPnL = position.ProfitAndLoss?.Net ?? (position.ProfitAndLoss?.Realized - position.UiFees - position.GasFees ?? 0) }; } // 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); } 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, Quantity = trade.Quantity, Price = trade.Price, Leverage = trade.Leverage, ExchangeOrderId = trade.ExchangeOrderId, Message = trade.Message }; } // Collection mappings public static IEnumerable Map(IEnumerable entities) { return entities?.Select(Map) ?? Enumerable.Empty(); } public static IEnumerable Map(IEnumerable entities) { return entities?.Select(Map) ?? Enumerable.Empty(); } public static IEnumerable Map(IEnumerable entities) { return entities?.Select(Map) ?? Enumerable.Empty(); } public static IEnumerable Map(IEnumerable entities) { return entities?.Select(Map) ?? Enumerable.Empty(); } #endregion #region Bot Mappings // BotBackup mappings public static Bot Map(BotEntity entity) { if (entity == null) return null; var bot = new Bot { Identifier = entity.Identifier, User = entity.User != null ? Map(entity.User) : null, Status = entity.Status, CreateDate = entity.CreateDate, Name = entity.Name, Ticker = entity.Ticker, StartupTime = entity.StartupTime, LastStartTime = entity.LastStartTime, LastStopTime = entity.LastStopTime, AccumulatedRunTimeSeconds = entity.AccumulatedRunTimeSeconds, TradeWins = entity.TradeWins, TradeLosses = entity.TradeLosses, Pnl = entity.Pnl, NetPnL = entity.NetPnL, Roi = entity.Roi, Volume = entity.Volume, Fees = entity.Fees, LongPositionCount = entity.LongPositionCount, ShortPositionCount = entity.ShortPositionCount }; return bot; } public static BotEntity Map(Bot bot) { if (bot == null) return null; return new BotEntity { Identifier = bot.Identifier, UserId = bot.User.Id, Status = bot.Status, CreateDate = bot.CreateDate, Name = bot.Name, Ticker = bot.Ticker, StartupTime = bot.StartupTime, LastStartTime = bot.LastStartTime, LastStopTime = bot.LastStopTime, AccumulatedRunTimeSeconds = bot.AccumulatedRunTimeSeconds, TradeWins = bot.TradeWins, TradeLosses = bot.TradeLosses, Pnl = bot.Pnl, NetPnL = bot.NetPnL, Roi = bot.Roi, Volume = bot.Volume, Fees = bot.Fees, LongPositionCount = bot.LongPositionCount, ShortPositionCount = bot.ShortPositionCount, UpdatedAt = DateTime.UtcNow }; } public static IEnumerable Map(IEnumerable entities) { return entities?.Select(Map) ?? Enumerable.Empty(); } #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 Map(IEnumerable entities) { return entities?.Select(Map) ?? Enumerable.Empty(); } // 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() }; // Deserialize the JSON spotlights data if (!string.IsNullOrEmpty(entity.SpotlightsJson)) { try { overview.Spotlights = SystemJsonSerializer.Deserialize>(entity.SpotlightsJson) ?? new List(); } catch (JsonException) { // If deserialization fails, return empty list overview.Spotlights = new List(); } } 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 Map(IEnumerable entities) { return entities?.Select(Map) ?? Enumerable.Empty(); } // 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 Map(IEnumerable entities) { return entities?.Select(Map) ?? Enumerable.Empty(); } // 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 Map(IEnumerable entities) { return entities?.Select(Map) ?? Enumerable.Empty(); } #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 Map(IEnumerable entities) { return entities?.Select(Map) ?? Enumerable.Empty(); } #endregion private static int? ExtractBundleIndex(string name) { if (string.IsNullOrWhiteSpace(name)) return null; var hashIndex = name.LastIndexOf('#'); if (hashIndex < 0 || hashIndex + 1 >= name.Length) return null; var numberPart = name.Substring(hashIndex + 1).Trim(); return int.TryParse(numberPart, out var n) ? n : (int?)null; } }