83 lines
4.2 KiB
C#
83 lines
4.2 KiB
C#
using Dapper;
|
|
using QuantEngine.Core.Interfaces;
|
|
using QuantEngine.Infrastructure.Data;
|
|
|
|
namespace QuantEngine.Infrastructure.Repositories;
|
|
|
|
public sealed class NormalizedLearningStore : INormalizedLearningStore
|
|
{
|
|
private readonly IDbConnectionFactory _connectionFactory;
|
|
|
|
public NormalizedLearningStore(IDbConnectionFactory connectionFactory) => _connectionFactory = connectionFactory;
|
|
|
|
public async Task<Guid> AppendSourceObservationAsync(SourceObservationRecord record)
|
|
{
|
|
var id = Guid.NewGuid();
|
|
using var conn = _connectionFactory.CreateConnection();
|
|
await conn.ExecuteAsync(@"INSERT INTO engine_history.source_observation
|
|
(observation_id, observed_at, instrument_id, source_name, source_version, payload, provenance)
|
|
VALUES (@Id, @ObservedAt, @InstrumentId, @SourceName, @SourceVersion,
|
|
CAST(@PayloadJson AS jsonb), CAST(@ProvenanceJson AS jsonb))", new { Id = id, record.ObservedAt, record.InstrumentId, record.SourceName, record.SourceVersion, record.PayloadJson, record.ProvenanceJson });
|
|
return id;
|
|
}
|
|
|
|
public async Task<Guid> AppendFactorObservationAsync(FactorObservationRecord record)
|
|
{
|
|
using var conn = _connectionFactory.CreateConnection();
|
|
await conn.ExecuteAsync(@"INSERT INTO engine_history.factor_observation
|
|
(factor_observation_id, observation_id, factor_id, factor_version, observed_at,
|
|
numeric_value, text_value, gate, provenance)
|
|
VALUES (@FactorObservationId, @ObservationId, @FactorId, @FactorVersion, @ObservedAt,
|
|
@NumericValue, @TextValue, @Gate, CAST(@ProvenanceJson AS jsonb))", new
|
|
{
|
|
record.FactorObservationId,
|
|
record.ObservationId,
|
|
record.FactorId,
|
|
record.FactorVersion,
|
|
record.ObservedAt,
|
|
record.NumericValue,
|
|
record.TextValue,
|
|
record.Gate,
|
|
record.ProvenanceJson
|
|
});
|
|
return record.FactorObservationId;
|
|
}
|
|
|
|
public async Task<Guid> AppendDecisionAsync(DecisionEventRecord record)
|
|
{
|
|
var id = Guid.NewGuid();
|
|
using var conn = _connectionFactory.CreateConnection();
|
|
await conn.ExecuteAsync(@"INSERT INTO engine_history.decision_event
|
|
(decision_id, decision_key, decided_at, instrument_id, action, gate, score,
|
|
source_version, trace, provenance)
|
|
VALUES (@Id, @DecisionKey, @DecidedAt, @InstrumentId, @Action, @Gate, @Score,
|
|
@SourceVersion, CAST(@TraceJson AS jsonb), CAST(@ProvenanceJson AS jsonb))", new { Id = id, record.DecisionKey, record.DecidedAt, record.InstrumentId, record.Action, record.Gate, record.Score, record.SourceVersion, record.TraceJson, record.ProvenanceJson });
|
|
return id;
|
|
}
|
|
|
|
public async Task AppendDecisionFactorEvidenceAsync(Guid decisionId, Guid factorObservationId, string role)
|
|
{
|
|
using var conn = _connectionFactory.CreateConnection();
|
|
await conn.ExecuteAsync(@"INSERT INTO engine_history.decision_factor_evidence
|
|
(decision_id, factor_observation_id, role) VALUES (@DecisionId, @FactorObservationId, @Role)", new { decisionId, factorObservationId, role });
|
|
}
|
|
|
|
public async Task AppendOutcomeAsync(OutcomeEvaluationRecord record)
|
|
{
|
|
using var conn = _connectionFactory.CreateConnection();
|
|
await conn.ExecuteAsync(@"INSERT INTO engine_history.outcome_evaluation
|
|
(decision_id, horizon_days, evaluated_at, realized_return, benchmark_return,
|
|
excess_return, outcome_class, evaluation_gate, provenance)
|
|
VALUES (@DecisionId, @HorizonDays, @EvaluatedAt, @RealizedReturn, @BenchmarkReturn,
|
|
@ExcessReturn, @OutcomeClass, @EvaluationGate, CAST(@ProvenanceJson AS jsonb))
|
|
ON CONFLICT (decision_id, horizon_days) DO UPDATE SET
|
|
evaluated_at = EXCLUDED.evaluated_at,
|
|
realized_return = EXCLUDED.realized_return,
|
|
benchmark_return = EXCLUDED.benchmark_return,
|
|
excess_return = EXCLUDED.excess_return,
|
|
outcome_class = EXCLUDED.outcome_class,
|
|
evaluation_gate = EXCLUDED.evaluation_gate,
|
|
provenance = EXCLUDED.provenance", record);
|
|
}
|
|
}
|