feat(web): implement market-close caching mechanism for collection data
Validators (Pushes and Pull Requests) / validate-ui-and-storage (push) Successful in 22s
Prepare Release / Build & Create Release (push) Successful in 1m1s
Validators (Pushes and Pull Requests) / validate-core (push) Has been cancelled
Prepare Release / Release Notification (push) Successful in 1s
Validators (Pushes and Pull Requests) / validate-ui-and-storage (push) Successful in 22s
Prepare Release / Build & Create Release (push) Successful in 1m1s
Validators (Pushes and Pull Requests) / validate-core (push) Has been cancelled
Prepare Release / Release Notification (push) Successful in 1s
This commit is contained in:
@@ -56,26 +56,53 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator
|
||||
try
|
||||
{
|
||||
_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);
|
||||
CollectionSnapshotRecord? cachedSnapshot = null;
|
||||
if (IsMarketClosed())
|
||||
{
|
||||
var latest = await _repository.GetLatestSnapshotsForTickerAsync(ticker, 1);
|
||||
var todayPrefix = DateTime.UtcNow.AddHours(9).ToString("yyyy-MM-dd");
|
||||
if (latest.Count > 0 && latest[0].CapturedAt.StartsWith(todayPrefix))
|
||||
{
|
||||
cachedSnapshot = latest[0];
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<string, object> normalized;
|
||||
string sourceName;
|
||||
|
||||
if (cachedSnapshot != null)
|
||||
{
|
||||
_logger.LogInformation("Cache hit for ticker {Ticker} (run {RunId})", ticker, runId);
|
||||
normalized = JsonSerializer.Deserialize<Dictionary<string, object>>(cachedSnapshot.PayloadJson)
|
||||
?? new Dictionary<string, object>();
|
||||
sourceName = cachedSnapshot.SourceName.EndsWith(" (Cached)")
|
||||
? cachedSnapshot.SourceName
|
||||
: cachedSnapshot.SourceName + " (Cached)";
|
||||
}
|
||||
else
|
||||
{
|
||||
var kisResult = await kisSource.GetPriceDataAsync(ticker, account);
|
||||
var seedRow = new Dictionary<string, object> { { "Ticker", ticker } };
|
||||
var (norm, provenance) = _normalizer.NormalizeCollectionRow(seedRow, kisResult, null, false);
|
||||
normalized = norm;
|
||||
sourceName = (string)(provenance.GetValueOrDefault("source") ?? "kis_open_api");
|
||||
}
|
||||
|
||||
// Save to DB
|
||||
await _repository.SaveSnapshotAsync(new CollectionSnapshotRecord(
|
||||
RunId: runId,
|
||||
DatasetName: "data_feed",
|
||||
Ticker: ticker,
|
||||
SourceName: (string)(provenance.GetValueOrDefault("source") ?? "kis_open_api"),
|
||||
SourceName: sourceName,
|
||||
PayloadJson: JsonSerializer.Serialize(normalized),
|
||||
CapturedAt: DataNormalizationHelper.KstNowIso()
|
||||
));
|
||||
|
||||
// Track source
|
||||
var source = (string)(provenance.GetValueOrDefault("source") ?? "kis_open_api");
|
||||
if (!sourceCounts.ContainsKey(source))
|
||||
sourceCounts[source] = 0;
|
||||
sourceCounts[source]++;
|
||||
if (!sourceCounts.ContainsKey(sourceName))
|
||||
sourceCounts[sourceName] = 0;
|
||||
sourceCounts[sourceName]++;
|
||||
|
||||
rows.Add(normalized);
|
||||
result.SuccessCount++;
|
||||
@@ -176,6 +203,23 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator
|
||||
|
||||
return Path.Combine(Path.GetTempPath(), "kis_dotnet_collection_v1.json");
|
||||
}
|
||||
|
||||
private static bool IsMarketClosed()
|
||||
{
|
||||
// KST Time conversion (UTC+9)
|
||||
var kst = DateTime.UtcNow.AddHours(9);
|
||||
|
||||
// Weekend check
|
||||
if (kst.DayOfWeek == DayOfWeek.Saturday || kst.DayOfWeek == DayOfWeek.Sunday)
|
||||
return true;
|
||||
|
||||
// Market hours check (09:00 - 15:30)
|
||||
var time = kst.TimeOfDay;
|
||||
if (time < new TimeSpan(9, 0, 0) || time > new TimeSpan(15, 30, 0))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user