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
|
try
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Collecting ticker {Ticker} (run {RunId})", ticker, runId);
|
_logger.LogInformation("Collecting ticker {Ticker} (run {RunId})", ticker, runId);
|
||||||
var kisResult = await kisSource.GetPriceDataAsync(ticker, account);
|
|
||||||
|
|
||||||
var seedRow = new Dictionary<string, object> { { "Ticker", ticker } };
|
CollectionSnapshotRecord? cachedSnapshot = null;
|
||||||
var (normalized, provenance) = _normalizer.NormalizeCollectionRow(seedRow, kisResult, null, false);
|
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
|
// Save to DB
|
||||||
await _repository.SaveSnapshotAsync(new CollectionSnapshotRecord(
|
await _repository.SaveSnapshotAsync(new CollectionSnapshotRecord(
|
||||||
RunId: runId,
|
RunId: runId,
|
||||||
DatasetName: "data_feed",
|
DatasetName: "data_feed",
|
||||||
Ticker: ticker,
|
Ticker: ticker,
|
||||||
SourceName: (string)(provenance.GetValueOrDefault("source") ?? "kis_open_api"),
|
SourceName: sourceName,
|
||||||
PayloadJson: JsonSerializer.Serialize(normalized),
|
PayloadJson: JsonSerializer.Serialize(normalized),
|
||||||
CapturedAt: DataNormalizationHelper.KstNowIso()
|
CapturedAt: DataNormalizationHelper.KstNowIso()
|
||||||
));
|
));
|
||||||
|
|
||||||
// Track source
|
// Track source
|
||||||
var source = (string)(provenance.GetValueOrDefault("source") ?? "kis_open_api");
|
if (!sourceCounts.ContainsKey(sourceName))
|
||||||
if (!sourceCounts.ContainsKey(source))
|
sourceCounts[sourceName] = 0;
|
||||||
sourceCounts[source] = 0;
|
sourceCounts[sourceName]++;
|
||||||
sourceCounts[source]++;
|
|
||||||
|
|
||||||
rows.Add(normalized);
|
rows.Add(normalized);
|
||||||
result.SuccessCount++;
|
result.SuccessCount++;
|
||||||
@@ -176,6 +203,23 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator
|
|||||||
|
|
||||||
return Path.Combine(Path.GetTempPath(), "kis_dotnet_collection_v1.json");
|
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