diff --git a/src/dotnet/QuantEngine.Core/QuantEngine/GameTheoreticPortfolio.cs b/src/dotnet/QuantEngine.Core/QuantEngine/GameTheoreticPortfolio.cs index 5479acec..9bf5dfc6 100644 --- a/src/dotnet/QuantEngine.Core/QuantEngine/GameTheoreticPortfolio.cs +++ b/src/dotnet/QuantEngine.Core/QuantEngine/GameTheoreticPortfolio.cs @@ -89,12 +89,8 @@ public class GameTheoreticPortfolio PortfolioAllocation currentAllocation, List microstructure) { - var signal = new RebalancingSignal - { - GeneratedAt = DateTime.UtcNow, - ShouldRebalance = false, - Reasons = new(), - }; + var reasons = new List(); + var shouldRebalance = false; // 가중치 드리프트 확인 (>5%) var drift = currentAllocation.Assets @@ -102,21 +98,21 @@ public class GameTheoreticPortfolio if (drift.Any()) { - signal.ShouldRebalance = true; - signal.Reasons.Add("Weight drift exceeds 5%"); + shouldRebalance = true; + reasons.Add("Weight drift exceeds 5%"); } // 호가 스프레드 이상 var badSpread = microstructure - .Where(m => m.BidAskSpread > 0.02 * m.MidPrice); + .Where(m => (double)m.BidAskSpread > 0.02 * (double)m.MidPrice); if (badSpread.Any()) { - signal.ShouldRebalance = true; - signal.Reasons.Add($"Bid-ask spread widened for {badSpread.Count()} assets"); + shouldRebalance = true; + reasons.Add($"Bid-ask spread widened for {badSpread.Count()} assets"); } - return signal; + return new RebalancingSignal(DateTime.UtcNow, shouldRebalance, reasons); } private double CalculateVariance(double[] weights, List assets) @@ -172,9 +168,16 @@ public record MarketMicrostructure public decimal BidAskSpread { get; init; } } -public record RebalancingSignal +public class RebalancingSignal { - public DateTime GeneratedAt { get; init; } - public bool ShouldRebalance { get; init; } - public List Reasons { get; init; } = new(); + public RebalancingSignal(DateTime generatedAt, bool shouldRebalance, List reasons) + { + GeneratedAt = generatedAt; + ShouldRebalance = shouldRebalance; + Reasons = reasons ?? new(); + } + + public DateTime GeneratedAt { get; } + public bool ShouldRebalance { get; } + public List Reasons { get; } } diff --git a/src/dotnet/QuantEngine.Core/Scheduling/SchedulerJobBase.cs b/src/dotnet/QuantEngine.Core/Scheduling/SchedulerJobBase.cs index 4b048602..05f25125 100644 --- a/src/dotnet/QuantEngine.Core/Scheduling/SchedulerJobBase.cs +++ b/src/dotnet/QuantEngine.Core/Scheduling/SchedulerJobBase.cs @@ -18,48 +18,45 @@ public abstract class SchedulerJobBase public async Task ExecuteAsync() { - var result = new JobExecutionResult - { - JobName = JobName, - JobId = JobId, - StartedAt = DateTime.UtcNow, - }; - var stopwatch = Stopwatch.StartNew(); + var startTime = DateTime.UtcNow; try { await OnStartingAsync(); var jobResult = await RunAsync(); - result.Succeeded = jobResult.Succeeded; - result.Message = jobResult.Message; - result.Data = jobResult.Data; + stopwatch.Stop(); + var result = new JobExecutionResult( + JobName, JobId, startTime, + DateTime.UtcNow, stopwatch.ElapsedMilliseconds, + true, jobResult.Message, jobResult.Data, null); await OnCompletedAsync(result); + return result; } catch (OperationCanceledException ex) { - result.Succeeded = false; - result.Message = $"Task cancelled: {ex.Message}"; - result.Exception = ex; + stopwatch.Stop(); + var result = new JobExecutionResult( + JobName, JobId, startTime, + DateTime.UtcNow, stopwatch.ElapsedMilliseconds, + false, $"Task cancelled: {ex.Message}", null, ex); + await OnFailedAsync(result); + return result; } catch (Exception ex) - { - result.Succeeded = false; - result.Message = $"Task failed: {ex.Message}"; - result.Exception = ex; - await OnFailedAsync(result); - } - finally { stopwatch.Stop(); - result.CompletedAt = DateTime.UtcNow; - result.ElapsedMilliseconds = stopwatch.ElapsedMilliseconds; - } + var result = new JobExecutionResult( + JobName, JobId, startTime, + DateTime.UtcNow, stopwatch.ElapsedMilliseconds, + false, $"Task failed: {ex.Message}", null, ex); - return result; + await OnFailedAsync(result); + return result; + } } protected abstract Task RunAsync(); @@ -84,17 +81,32 @@ public abstract class SchedulerJobBase } } -public record JobExecutionResult +public class JobExecutionResult { - public string JobName { get; init; } = string.Empty; - public string JobId { get; init; } = string.Empty; - public DateTime StartedAt { get; init; } - public DateTime CompletedAt { get; init; } - public long ElapsedMilliseconds { get; init; } - public bool Succeeded { get; set; } - public string Message { get; set; } = string.Empty; - public object? Data { get; set; } - public Exception? Exception { get; set; } + public JobExecutionResult(string jobName, string jobId, DateTime startedAt, + DateTime completedAt, long elapsedMs, bool succeeded, string message, + object? data, Exception? exception) + { + JobName = jobName; + JobId = jobId; + StartedAt = startedAt; + CompletedAt = completedAt; + ElapsedMilliseconds = elapsedMs; + Succeeded = succeeded; + Message = message; + Data = data; + Exception = exception; + } + + public string JobName { get; } + public string JobId { get; } + public DateTime StartedAt { get; } + public DateTime CompletedAt { get; } + public long ElapsedMilliseconds { get; } + public bool Succeeded { get; } + public string Message { get; } + public object? Data { get; } + public Exception? Exception { get; } } public record JobRunResult diff --git a/src/dotnet/QuantEngine.Core/Validators/IDataQualityValidator.cs b/src/dotnet/QuantEngine.Core/Validators/IDataQualityValidator.cs index 4d002f33..115337e7 100644 --- a/src/dotnet/QuantEngine.Core/Validators/IDataQualityValidator.cs +++ b/src/dotnet/QuantEngine.Core/Validators/IDataQualityValidator.cs @@ -83,11 +83,11 @@ public record DataQualityReport public int StockId { get; init; } public DateTime EvaluatedAt { get; init; } = DateTime.UtcNow; - public CompletenessCheckResult Completeness { get; init; } - public FreshnessCheckResult Freshness { get; init; } - public ConsistencyCheckResult Consistency { get; init; } - public OutlierCheckResult Outliers { get; init; } - public DuplicateCheckResult Duplicates { get; init; } + public required CompletenessCheckResult Completeness { get; init; } + public required FreshnessCheckResult Freshness { get; init; } + public required ConsistencyCheckResult Consistency { get; init; } + public required OutlierCheckResult Outliers { get; init; } + public required DuplicateCheckResult Duplicates { get; init; } public bool IsValid => Completeness.IsValid && diff --git a/src/dotnet/QuantEngine.Infrastructure/Repositories/MarketDataRepository.cs b/src/dotnet/QuantEngine.Infrastructure/Repositories/MarketDataRepository.cs index 7b7f9285..066c07c2 100644 --- a/src/dotnet/QuantEngine.Infrastructure/Repositories/MarketDataRepository.cs +++ b/src/dotnet/QuantEngine.Infrastructure/Repositories/MarketDataRepository.cs @@ -2,6 +2,7 @@ namespace QuantEngine.Infrastructure.Repositories; using Dapper; using QuantEngine.Core.Repositories; +using System.Data; public class MarketDataRepository : IMarketDataRepository { diff --git a/src/dotnet/QuantEngine.Infrastructure/Validators/DataQualityValidator.cs b/src/dotnet/QuantEngine.Infrastructure/Validators/DataQualityValidator.cs index 0539d70d..665ba710 100644 --- a/src/dotnet/QuantEngine.Infrastructure/Validators/DataQualityValidator.cs +++ b/src/dotnet/QuantEngine.Infrastructure/Validators/DataQualityValidator.cs @@ -1,6 +1,7 @@ namespace QuantEngine.Infrastructure.Validators; using QuantEngine.Core.Validators; +using System.Data; using System.Linq; ///