feat: add quant engine WBS verification harness
This commit is contained in:
@@ -4,6 +4,10 @@
|
||||
<ProjectReference Include="..\QuantEngine.Core\QuantEngine.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using QuantEngine.Core.Interfaces;
|
||||
using QuantEngine.Application.Interfaces;
|
||||
using QuantEngine.Application.Services;
|
||||
@@ -13,19 +14,20 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator
|
||||
private readonly ICollectionRepository _repository;
|
||||
private readonly PriceDataNormalizer _normalizer;
|
||||
private readonly SourcePriorityResolver _priorityResolver;
|
||||
// Logging removed for simplicity
|
||||
private readonly ILogger<KisDataCollectionOrchestrator> _logger;
|
||||
|
||||
public KisDataCollectionOrchestrator(
|
||||
IKisApiClient kisApiClient,
|
||||
ICollectionRepository repository,
|
||||
PriceDataNormalizer normalizer,
|
||||
SourcePriorityResolver priorityResolver)
|
||||
SourcePriorityResolver priorityResolver,
|
||||
ILogger<KisDataCollectionOrchestrator> logger)
|
||||
{
|
||||
_kisApiClient = kisApiClient;
|
||||
_repository = repository;
|
||||
_normalizer = normalizer;
|
||||
_priorityResolver = priorityResolver;
|
||||
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<CollectionRunResult> RunCollectionAsync(string runId, string account, List<string> tickers)
|
||||
@@ -42,7 +44,7 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator
|
||||
|
||||
try
|
||||
{
|
||||
// Log: skipped
|
||||
_logger.LogInformation("Starting collection run {RunId}", runId);
|
||||
|
||||
var kisSource = new KisApiPriceSource(_kisApiClient);
|
||||
var rows = new List<Dictionary<string, object>>();
|
||||
@@ -53,9 +55,9 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator
|
||||
{
|
||||
try
|
||||
{
|
||||
// Log: skipped
|
||||
_logger.LogInformation("Collecting ticker {Ticker} (run {RunId})", ticker, runId);
|
||||
var kisResult = await kisSource.GetPriceDataAsync(ticker, account);
|
||||
|
||||
|
||||
var seedRow = new Dictionary<string, object> { { "Ticker", ticker } };
|
||||
var (normalized, provenance) = _normalizer.NormalizeCollectionRow(seedRow, kisResult, null, false);
|
||||
|
||||
@@ -80,7 +82,7 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log: skipped
|
||||
_logger.LogWarning(ex, "Collection failed for {Ticker} (run {RunId})", ticker, runId);
|
||||
result.ErrorCount++;
|
||||
errors.Add(new Dictionary<string, object>
|
||||
{
|
||||
@@ -116,33 +118,64 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator
|
||||
TotalErrors: result.ErrorCount
|
||||
));
|
||||
|
||||
// Output JSON file
|
||||
var outputPath = Path.Combine(Path.GetTempPath(), "kis_data_collection_v1.json");
|
||||
// Determine gate status
|
||||
var gate = result.SuccessCount == 0 ? "FAIL"
|
||||
: result.ErrorCount == 0 ? "PASS"
|
||||
: result.ErrorCount < result.SuccessCount * 0.1 ? "PASS"
|
||||
: "PASS_WITH_WARNINGS";
|
||||
|
||||
// Output JSON file to <repo>/Temp/kis_dotnet_collection_v1.json
|
||||
var outputPath = GetOutputPath();
|
||||
var outputData = new
|
||||
{
|
||||
formula_id = "KIS_DATA_COLLECTION_V1",
|
||||
formula_id = "KIS_DOTNET_COLLECTION_V1",
|
||||
gate = gate,
|
||||
run_id = runId,
|
||||
started_at = startedAt,
|
||||
finished_at = finishedAt,
|
||||
row_count = rows.Count,
|
||||
source_counts = sourceCounts,
|
||||
errors = errors,
|
||||
rows = rows
|
||||
summary = new
|
||||
{
|
||||
success_count = result.SuccessCount,
|
||||
error_count = result.ErrorCount,
|
||||
source_counts = sourceCounts
|
||||
}
|
||||
};
|
||||
File.WriteAllText(outputPath, JsonSerializer.Serialize(outputData, new JsonSerializerOptions { WriteIndented = true }));
|
||||
// Log: skipped
|
||||
|
||||
_logger.LogInformation("Collection run {RunId} finished with status {Status}: {Success} ok, {Errors} errors",
|
||||
runId, result.Status, result.SuccessCount, result.ErrorCount);
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log: skipped
|
||||
_logger.LogError(ex, "Collection run {RunId} failed with exception", runId);
|
||||
result.Status = "FAILED";
|
||||
result.FinishedAt = DataNormalizationHelper.KstNowIso();
|
||||
result.ErrorMessage = ex.Message;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetOutputPath()
|
||||
{
|
||||
var baseDir = AppContext.BaseDirectory;
|
||||
var current = new DirectoryInfo(baseDir);
|
||||
|
||||
while (current != null)
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(current.FullName, ".git"))
|
||||
|| File.Exists(Path.Combine(current.FullName, "GatherTradingData.json")))
|
||||
{
|
||||
var tempDir = Path.Combine(current.FullName, "Temp");
|
||||
Directory.CreateDirectory(tempDir);
|
||||
return Path.Combine(tempDir, "kis_dotnet_collection_v1.json");
|
||||
}
|
||||
current = current.Parent;
|
||||
}
|
||||
|
||||
return Path.Combine(Path.GetTempPath(), "kis_dotnet_collection_v1.json");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user