diff --git a/build.zig.zon b/build.zig.zon index a67e474..ced3b26 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -14,8 +14,8 @@ .hash = "httpz-0.0.0-PNVzrLjJCAD37S0CcrXpsjSqr86hVjK0rsALTDJ98AAJ", }, .zfin = .{ - .url = "git+https://git.lerch.org/lobo/zfin#b796a46699cf9ace2611f768c159bbf3d842c048", - .hash = "zfin-0.0.0-J-B21lwDPABENCoGdpzMLkYXuhl8zf79td0gWhpsg9zF", + .url = "git+https://git.lerch.org/lobo/zfin#d7a86cd63901e8c823bdeedd548e5beb9759ea9c", + .hash = "zfin-0.0.0-J-B21qcGPABbosCyx3cN-gMOFEN1ZbqrRZpS6WUQtLzC", }, }, } diff --git a/src/main.zig b/src/main.zig index 8f19584..888eed2 100644 --- a/src/main.zig +++ b/src/main.zig @@ -681,12 +681,13 @@ fn refresh(io: std.Io, allocator: std.mem.Allocator, environ: *const std.process sym_ok = false; } - // Classification (Wikidata). Captures CIK if Wikidata - // had it — used to chain into entity_facts below. + // Classification (Wikidata + EDGAR fallback). Captures + // CIK and is_etf — used to chain into entity_facts below. // NotFound is logged as `n/a` (symbol genuinely has no - // Wikidata entry) and doesn't flip sym_ok. + // Wikidata or EDGAR entry) and doesn't flip sym_ok. var cik_buf: ?[]u8 = null; defer if (cik_buf) |b| allocator.free(b); + var is_etf = false; try printRateLimitWait(&svc, .classification, stdout); if (svc.getClassification(sym, .{})) |result| { defer result.deinit(); @@ -694,6 +695,7 @@ fn refresh(io: std.Io, allocator: std.mem.Allocator, environ: *const std.process if (result.data[0].cik) |cik| { cik_buf = allocator.dupe(u8, cik) catch null; } + is_etf = result.data[0].is_etf; } try stdout.print(", classification ok ({s})", .{@tagName(result.source)}); } else |err| switch (err) { @@ -721,22 +723,26 @@ fn refresh(io: std.Io, allocator: std.mem.Allocator, environ: *const std.process } // Entity facts (XBRL). Only attempted when the - // classification step yielded a CIK — funds without - // Wikidata entries don't reach here even though they - // have an EDGAR CIK from the ticker map (production - // zfin chains entity_facts off Wikidata's CIK, so the - // server warms the cache the same way). + // classification step yielded a CIK from a non-fund + // record. ETFs/funds CIKs (iShares Trust, Fidelity series + // CIKs, etc.) don't file the operating-company XBRL + // concepts entity_facts looks for; calling EDGAR for + // them is guaranteed-404 noise. Skip them up front. if (cik_buf) |cik| { - try printRateLimitWait(&svc, .entity_facts, stdout); - if (svc.getEntityFacts(cik, .{})) |result| { - defer result.deinit(); - try stdout.print(", entity_facts ok ({s})", .{@tagName(result.source)}); - } else |err| switch (err) { - zfin.DataError.NotFound => try stdout.print(", entity_facts n/a", .{}), - else => { - try stdout.print(", entity_facts FAILED ({t})", .{err}); - sym_ok = false; - }, + if (is_etf) { + try stdout.print(", entity_facts n/a (ETF)", .{}); + } else { + try printRateLimitWait(&svc, .entity_facts, stdout); + if (svc.getEntityFacts(cik, .{})) |result| { + defer result.deinit(); + try stdout.print(", entity_facts ok ({s})", .{@tagName(result.source)}); + } else |err| switch (err) { + zfin.DataError.NotFound => try stdout.print(", entity_facts n/a", .{}), + else => { + try stdout.print(", entity_facts FAILED ({t})", .{err}); + sym_ok = false; + }, + } } }