70 lines
2.8 KiB
C#
70 lines
2.8 KiB
C#
using System.Text.Json;
|
|
using QuantEngine.Core.Domain;
|
|
using QuantEngine.Core.Interfaces;
|
|
using QuantEngine.Application.Interfaces;
|
|
|
|
namespace QuantEngine.Application.Services;
|
|
|
|
public sealed record FactorComputationAudit(
|
|
string Ticker,
|
|
int StockBars,
|
|
int IndexBars,
|
|
string State,
|
|
DateTimeOffset ComputedAt,
|
|
string? SourceVersion);
|
|
|
|
public sealed class FactorComputationService
|
|
{
|
|
private readonly HistoryIngestionService _history;
|
|
private readonly IRuntimeAuditTrailService _auditTrail;
|
|
|
|
public FactorComputationService(HistoryIngestionService history, IRuntimeAuditTrailService auditTrail)
|
|
{
|
|
_history = history;
|
|
_auditTrail = auditTrail;
|
|
}
|
|
|
|
public FactorOutputs Compute(
|
|
string ticker,
|
|
List<PriceHistoryDailyRecord> stockBars,
|
|
List<PriceHistoryDailyRecord> indexBars,
|
|
string? sourceVersion = null)
|
|
{
|
|
var computedAt = DateTimeOffset.UtcNow;
|
|
var outputs = FactorCalculator.CalculateFactors(stockBars, indexBars);
|
|
_auditTrail.Append("factor_audit", ticker, new FactorComputationAudit(ticker, stockBars.Count, indexBars.Count, "SUCCEEDED", computedAt, sourceVersion));
|
|
return outputs;
|
|
}
|
|
|
|
public async Task AppendFactorOutputsAsync(
|
|
string ticker,
|
|
string sourceVersion,
|
|
FactorOutputs outputs,
|
|
DateTimeOffset? observedAt = null)
|
|
{
|
|
ticker = RequireValue(ticker, nameof(ticker));
|
|
sourceVersion = RequireValue(sourceVersion, nameof(sourceVersion));
|
|
ArgumentNullException.ThrowIfNull(outputs);
|
|
|
|
var when = observedAt ?? DateTimeOffset.UtcNow;
|
|
await _history.AppendFactorOutputAsync("momentum_20d", sourceVersion, outputs.Momentum20D, "PASS", sourceVersion, when);
|
|
await _history.AppendFactorOutputAsync("momentum_60d", sourceVersion, outputs.Momentum60D, "PASS", sourceVersion, when);
|
|
await _history.AppendFactorOutputAsync("momentum_120d", sourceVersion, outputs.Momentum120D, "PASS", sourceVersion, when);
|
|
await _history.AppendFactorOutputAsync("atr_20pct", sourceVersion, outputs.Atr20Pct, "PASS", sourceVersion, when);
|
|
await _history.AppendFactorOutputAsync("stdev_20d", sourceVersion, outputs.StDev20D, "PASS", sourceVersion, when);
|
|
await _history.AppendFactorOutputAsync("beta_60d", sourceVersion, outputs.Beta60D, "PASS", sourceVersion, when);
|
|
await _history.AppendFactorOutputAsync("rs_20d", sourceVersion, outputs.Rs20D, "PASS", sourceVersion, when);
|
|
_auditTrail.Append("factor_audit", ticker, new FactorComputationAudit(ticker, 0, 0, "PERSISTED", when, sourceVersion));
|
|
}
|
|
|
|
private static string RequireValue(string value, string parameterName)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
{
|
|
throw new ArgumentException("Value is required.", parameterName);
|
|
}
|
|
|
|
return value.Trim();
|
|
}
|
|
}
|