feat: postgres history-first 계약과 적재 경로 추가
- PostgreSQL history contract와 schema/validator를 추가했습니다. - .NET history store, snapshot reader, repository, migration을 연결했습니다. - history-first 운영 모델 문서와 daily signal tracking 문구를 정리했습니다.
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using QuantEngine.Core.Domain;
|
||||
using QuantEngine.Core.Interfaces;
|
||||
|
||||
namespace QuantEngine.Application.Services
|
||||
{
|
||||
public class HistoryIngestionService
|
||||
{
|
||||
private readonly IPostgresqlHistoryStore _store;
|
||||
|
||||
public HistoryIngestionService(IPostgresqlHistoryStore store)
|
||||
{
|
||||
_store = store;
|
||||
}
|
||||
|
||||
public Task<int> AppendDecisionAsync(IDictionary<string, object?> payload)
|
||||
=> _store.AppendAsync("decision_result_history", payload);
|
||||
|
||||
public Task<int> AppendFactorOutputAsync(IDictionary<string, object?> payload)
|
||||
=> _store.AppendAsync("factor_output_history", payload);
|
||||
|
||||
public Task<int> AppendMarketRawAsync(IDictionary<string, object?> payload)
|
||||
=> _store.AppendAsync("market_raw_history", payload);
|
||||
|
||||
public Task<int> AppendGapAsync(IDictionary<string, object?> payload)
|
||||
=> _store.AppendAsync("market_vs_engine_gap_history", payload);
|
||||
|
||||
public Task<int> AppendDecisionAsync(
|
||||
FinalDecisionResult decision,
|
||||
SellDecisionResult? sellDecision = null,
|
||||
TimingDecisionResult? timingDecision = null,
|
||||
string? instrumentId = null,
|
||||
string? sourceVersion = null,
|
||||
string? gate = null)
|
||||
{
|
||||
var payload = new Dictionary<string, object?>
|
||||
{
|
||||
["decision_id"] = Guid.NewGuid().ToString("N"),
|
||||
["decided_at"] = DateTimeOffset.UtcNow,
|
||||
["instrument_id"] = instrumentId ?? string.Empty,
|
||||
["action"] = decision.FinalAction,
|
||||
["gate"] = gate ?? (string.IsNullOrWhiteSpace(sellDecision?.Validation) ? "PASS" : sellDecision.Validation),
|
||||
["score"] = decision.PriorityScore,
|
||||
["source_version"] = sourceVersion ?? decision.DecisionSource,
|
||||
["provenance"] = new Dictionary<string, object?>
|
||||
{
|
||||
["final_action"] = decision.FinalAction,
|
||||
["action_priority"] = decision.ActionPriority,
|
||||
["priority_score"] = decision.PriorityScore,
|
||||
["decision_source"] = decision.DecisionSource,
|
||||
["sell_action"] = sellDecision?.Action,
|
||||
["sell_validation"] = sellDecision?.Validation,
|
||||
["timing_action"] = timingDecision?.Action,
|
||||
["timing_reason"] = timingDecision?.Reason
|
||||
}
|
||||
};
|
||||
|
||||
return _store.AppendAsync("decision_result_history", payload);
|
||||
}
|
||||
|
||||
public Task<int> AppendFactorOutputAsync(
|
||||
string factorId,
|
||||
string factorVersion,
|
||||
double outputValue,
|
||||
string outputGate,
|
||||
string? sourceVersion = null,
|
||||
DateTimeOffset? observedAt = null)
|
||||
{
|
||||
var payload = new Dictionary<string, object?>
|
||||
{
|
||||
["factor_output_id"] = Guid.NewGuid().ToString("N"),
|
||||
["observed_at"] = observedAt ?? DateTimeOffset.UtcNow,
|
||||
["factor_id"] = factorId,
|
||||
["factor_version"] = factorVersion,
|
||||
["output_value"] = outputValue,
|
||||
["output_gate"] = outputGate,
|
||||
["source_version"] = sourceVersion ?? factorVersion,
|
||||
["provenance"] = new Dictionary<string, object?>
|
||||
{
|
||||
["factor_id"] = factorId,
|
||||
["factor_version"] = factorVersion,
|
||||
["output_value"] = outputValue,
|
||||
["output_gate"] = outputGate,
|
||||
["source_version"] = sourceVersion ?? factorVersion
|
||||
}
|
||||
};
|
||||
|
||||
return _store.AppendAsync("factor_output_history", payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using QuantEngine.Core.Interfaces;
|
||||
|
||||
namespace QuantEngine.Application.Services
|
||||
{
|
||||
public class PostgresqlHistorySnapshotReader : IPostgresqlHistorySnapshotReader
|
||||
{
|
||||
private readonly IPostgresqlHistoryStore _store;
|
||||
|
||||
public PostgresqlHistorySnapshotReader(IPostgresqlHistoryStore store)
|
||||
{
|
||||
_store = store;
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<IDictionary<string, object?>>> ReadAsync(string domain, int limit = 500)
|
||||
=> _store.SnapshotAsync(domain, limit);
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,12 @@ namespace QuantEngine.Application.Services
|
||||
public class WorkspaceService
|
||||
{
|
||||
private readonly IWorkspaceRepository _repository;
|
||||
private readonly IPostgresqlHistoryStore _historyStore;
|
||||
|
||||
public WorkspaceService(IWorkspaceRepository repository)
|
||||
public WorkspaceService(IWorkspaceRepository repository, IPostgresqlHistoryStore historyStore)
|
||||
{
|
||||
_repository = repository;
|
||||
_historyStore = historyStore;
|
||||
}
|
||||
|
||||
public Task<IEnumerable<Setting>> GetSettingsAsync() => _repository.GetSettingsAsync();
|
||||
@@ -23,5 +25,8 @@ namespace QuantEngine.Application.Services
|
||||
public Task<IEnumerable<AccountSnapshot>> GetAccountSnapshotsAsync() => _repository.GetAccountSnapshotsAsync();
|
||||
public Task<bool> InsertAccountSnapshotsAsync(IEnumerable<AccountSnapshot> snapshots) => _repository.InsertAccountSnapshotsAsync(snapshots);
|
||||
public Task<bool> ClearAccountSnapshotsAsync() => _repository.ClearAccountSnapshotsAsync();
|
||||
|
||||
public Task<int> AppendHistoryAsync(string domain, IDictionary<string, object?> payload) => _historyStore.AppendAsync(domain, payload);
|
||||
public Task<IReadOnlyList<IDictionary<string, object?>>> ReadHistorySnapshotAsync(string domain, int limit = 500) => _historyStore.SnapshotAsync(domain, limit);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user