Files
QuantEngineByItz/src/dotnet/QuantEngine.Application/Services/FormulaService.cs
T
kjh2064 cb814b8aa2
Validators (Pushes and Pull Requests) / validate-ui-and-storage (push) Successful in 17s
Validators (Pushes and Pull Requests) / validate-core (push) Has been cancelled
refactor(dotnet): normalize formula service inputs
2026-07-13 01:28:05 +09:00

82 lines
3.3 KiB
C#

using System.Collections.Generic;
using System.Threading.Tasks;
using QuantEngine.Core.Domain;
using QuantEngine.Core.Interfaces;
namespace QuantEngine.Application.Services
{
public class FormulaService
{
private readonly IPostgresqlHistoryStore _historyStore;
private readonly DecisionLearningService _learningService;
public FormulaService(IPostgresqlHistoryStore historyStore, DecisionLearningService learningService)
{
_historyStore = historyStore;
_learningService = learningService;
}
public TimingDecisionResult ComputeTimingDecision(Dictionary<string, object> ctx)
=> FormulaEngine.ComputeTimingDecision(RequireContext(ctx));
public SellDecisionResult ComputeSellDecision(Dictionary<string, object> ctx)
=> FormulaEngine.ComputeSellDecision(RequireContext(ctx));
public FinalDecisionResult ComputeFinalDecision(Dictionary<string, object> ctx)
=> FormulaEngine.ComputeFinalDecision(RequireContext(ctx));
public async Task<Guid> ComputeAndRecordFinalDecisionAsync(
Dictionary<string, object> ctx,
string decisionKey,
string instrumentId,
string sourceVersion,
IEnumerable<FactorEvidenceInput> factorEvidence)
{
var normalizedContext = RequireContext(ctx);
var decision = ComputeFinalDecision(normalizedContext);
return await _learningService.RecordDecisionAsync(
RequireValue(decisionKey, nameof(decisionKey)),
DateTimeOffset.UtcNow,
RequireValue(instrumentId, nameof(instrumentId)),
decision.FinalAction,
"PASS",
Convert.ToDecimal(decision.PriorityScore),
RequireValue(sourceVersion, nameof(sourceVersion)),
factorEvidence,
new { context_keys = normalizedContext.Keys.OrderBy(key => key).ToArray() },
new { formula = "FormulaEngine.ComputeFinalDecision", source_version = sourceVersion });
}
public CashShortfallResult ComputeCashShortfallHarness(
Dictionary<string, object> asResult,
double totalAsset,
Dictionary<string, object> cashFloorInfo,
double mrsScore)
=> FormulaEngine.ComputeCashShortfallHarness(asResult, totalAsset, cashFloorInfo, mrsScore);
public CashRecoveryPlanResult ComputeCashRecoveryOptimizer(
List<Dictionary<string, object>> sellCandidates,
double cashShortfallMinKrw)
=> FormulaEngine.ComputeCashRecoveryOptimizer(sellCandidates, cashShortfallMinKrw);
public Task<int> AppendFormulaRunAsync(string formulaName, Dictionary<string, object?> payload)
=> _historyStore.AppendAsync($"formula_{RequireValue(formulaName, nameof(formulaName))}_history", payload);
private static Dictionary<string, object> RequireContext(Dictionary<string, object> ctx)
{
ArgumentNullException.ThrowIfNull(ctx);
return ctx;
}
private static string RequireValue(string value, string parameterName)
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("Value is required.", parameterName);
}
return value.Trim();
}
}
}