diff --git a/src/dotnet/QuantEngine.Application/QuantEngine.Application.csproj b/src/dotnet/QuantEngine.Application/QuantEngine.Application.csproj
index f9926a95..6cd0ba63 100644
--- a/src/dotnet/QuantEngine.Application/QuantEngine.Application.csproj
+++ b/src/dotnet/QuantEngine.Application/QuantEngine.Application.csproj
@@ -6,6 +6,7 @@
+
diff --git a/src/dotnet/QuantEngine.Application/Services/CollectionBootstrapHostedService.cs b/src/dotnet/QuantEngine.Application/Services/CollectionBootstrapHostedService.cs
new file mode 100644
index 00000000..7578f6e2
--- /dev/null
+++ b/src/dotnet/QuantEngine.Application/Services/CollectionBootstrapHostedService.cs
@@ -0,0 +1,110 @@
+using System.Text.Json;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+
+namespace QuantEngine.Application.Services;
+
+///
+/// Lightweight startup bootstrap for collection scheduling/readiness.
+/// Writes a deterministic artifact so deployment can verify the collection
+/// pipeline entry point without forcing a live collection run.
+///
+public sealed class CollectionBootstrapHostedService : IHostedService
+{
+ private readonly ILogger _logger;
+ private readonly GatherTradingDataParser _parser;
+
+ public CollectionBootstrapHostedService(
+ ILogger logger,
+ GatherTradingDataParser parser)
+ {
+ _logger = logger;
+ _parser = parser;
+ }
+
+ public Task StartAsync(CancellationToken cancellationToken)
+ {
+ try
+ {
+ var repoRoot = FindRepoRoot();
+ var outputPath = Path.Combine(repoRoot, "Temp", "collection_bootstrap_v1.json");
+ var tickers = LoadBootstrapTickers();
+
+ Directory.CreateDirectory(Path.GetDirectoryName(outputPath)!);
+ File.WriteAllText(outputPath, JsonSerializer.Serialize(new
+ {
+ gate = "PASS",
+ generated_at_utc = DateTimeOffset.UtcNow,
+ bootstrap = "collection-scheduling-ready",
+ ticker_count = tickers.Count,
+ tickers
+ }, new JsonSerializerOptions { WriteIndented = true }));
+
+ _logger.LogInformation("Collection bootstrap artifact written to {Path}", outputPath);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "Collection bootstrap artifact generation failed");
+ }
+
+ return Task.CompletedTask;
+ }
+
+ public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
+
+ private List LoadBootstrapTickers()
+ {
+ try
+ {
+ var jsonPath = FindGatherTradingDataJson();
+ if (jsonPath is null)
+ {
+ return ["005930"];
+ }
+
+ var data = _parser.ParseGatherTradingData(jsonPath);
+ return data
+ .Select(row => row.TryGetValue("Ticker", out var value) ? value?.ToString()?.Trim('"') : null)
+ .Where(ticker => !string.IsNullOrWhiteSpace(ticker))
+ .Distinct()
+ .Take(10)
+ .ToList()!;
+ }
+ catch
+ {
+ return ["005930"];
+ }
+ }
+
+ private static string FindRepoRoot()
+ {
+ var current = new DirectoryInfo(AppContext.BaseDirectory);
+ while (current != null)
+ {
+ if (Directory.Exists(Path.Combine(current.FullName, ".git")))
+ {
+ return current.FullName;
+ }
+ current = current.Parent;
+ }
+
+ return Directory.GetCurrentDirectory();
+ }
+
+ private static string? FindGatherTradingDataJson()
+ {
+ var current = new DirectoryInfo(AppContext.BaseDirectory);
+ while (current != null)
+ {
+ var candidate = Path.Combine(current.FullName, "GatherTradingData.json");
+ if (File.Exists(candidate))
+ {
+ return candidate;
+ }
+
+ current = current.Parent;
+ }
+
+ return null;
+ }
+}
diff --git a/src/dotnet/QuantEngine.Core.Tests/CollectionBootstrapHostedServiceTests.cs b/src/dotnet/QuantEngine.Core.Tests/CollectionBootstrapHostedServiceTests.cs
new file mode 100644
index 00000000..ff4c4182
--- /dev/null
+++ b/src/dotnet/QuantEngine.Core.Tests/CollectionBootstrapHostedServiceTests.cs
@@ -0,0 +1,45 @@
+using Microsoft.Extensions.Logging;
+using Moq;
+using QuantEngine.Application.Services;
+
+namespace QuantEngine.Core.Tests;
+
+public class CollectionBootstrapHostedServiceTests
+{
+ [Fact]
+ public async Task StartAsync_WritesBootstrapArtifact()
+ {
+ var root = FindRepoRoot();
+ var artifact = Path.Combine(root, "Temp", "collection_bootstrap_v1.json");
+ if (File.Exists(artifact))
+ {
+ File.Delete(artifact);
+ }
+
+ var service = new CollectionBootstrapHostedService(
+ new Mock>().Object,
+ new GatherTradingDataParser());
+
+ await service.StartAsync(CancellationToken.None);
+
+ Assert.True(File.Exists(artifact));
+ var text = await File.ReadAllTextAsync(artifact);
+ Assert.Contains("\"gate\": \"PASS\"", text);
+ Assert.Contains("\"bootstrap\": \"collection-scheduling-ready\"", text);
+ }
+
+ private static string FindRepoRoot()
+ {
+ var current = new DirectoryInfo(AppContext.BaseDirectory);
+ while (current != null)
+ {
+ if (Directory.Exists(Path.Combine(current.FullName, ".git")))
+ {
+ return current.FullName;
+ }
+ current = current.Parent;
+ }
+
+ throw new InvalidOperationException("Repository root not found.");
+ }
+}
diff --git a/src/dotnet/QuantEngine.Web/Program.cs b/src/dotnet/QuantEngine.Web/Program.cs
index 02130ba2..894bc6bb 100644
--- a/src/dotnet/QuantEngine.Web/Program.cs
+++ b/src/dotnet/QuantEngine.Web/Program.cs
@@ -122,6 +122,7 @@ try
builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddOptions();
+ builder.Services.AddHostedService();
// Hangfire Background Jobs
try