From c596d8c12f70e1b25543bd04e43f133413b0e67a Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Wed, 11 Mar 2026 11:04:05 -0700 Subject: [PATCH] refresh fixes --- src/main.zig | 58 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/src/main.zig b/src/main.zig index 959cb78..0781838 100644 --- a/src/main.zig +++ b/src/main.zig @@ -261,7 +261,22 @@ fn upperDupe(allocator: std.mem.Allocator, s: []const u8) ![]u8 { return d; } -/// Format as percentage string for XML (e.g., 0.1234 -> "12.34000") +fn printRateLimitWait(svc: *zfin.DataService, stdout: *std.Io.Writer) !void { + if (svc.estimateWaitSeconds()) |wait| { + if (wait > 0) { + try stdout.print("\n (rate limit -- waiting {d}s)\n ", .{wait}); + try stdout.flush(); + } + } +} + +/// Mutual funds typically have 5-letter tickers ending in X. +/// These don't have quarterly earnings on Finnhub. +fn isMutualFund(symbol: []const u8) bool { + return symbol.len == 5 and symbol[4] == 'X'; +} + +/// Format as percentage (e.g., 0.1234 -> "12.34000"), or "null" if absent. fn fmtPct(arena: std.mem.Allocator, value: ?f64) []const u8 { if (value) |v| return std.fmt.allocPrint(arena, "{d:.5}", .{v * 100.0}) catch "null"; return "null"; @@ -345,16 +360,11 @@ fn refresh(allocator: std.mem.Allocator) !void { }; defer allocator.free(data); - const portfolio = zfin.cache.deserializePortfolio(allocator, data) catch { + var portfolio = zfin.cache.deserializePortfolio(allocator, data) catch { log.err("failed to parse portfolio", .{}); return error.ParseFailed; }; - defer { - for (portfolio.lots) |*lot| { - if (lot.note) |n| allocator.free(n); - } - allocator.free(portfolio.lots); - } + defer portfolio.deinit(); var symbols = std.StringHashMap(void).init(allocator); defer symbols.deinit(); @@ -382,21 +392,13 @@ fn refresh(allocator: std.mem.Allocator) !void { var it = symbols.iterator(); while (it.next()) |entry| { const sym = entry.key_ptr.*; - - // Check if we need to wait for rate limits - if (svc.estimateWaitSeconds()) |wait| { - if (wait > 0) { - try stdout.print(" (rate limit -- waiting {d}s)\n", .{wait}); - try stdout.flush(); - } - } - try stdout.print("{s}: ", .{sym}); try stdout.flush(); var sym_ok = true; // Candles + try printRateLimitWait(&svc, stdout); if (svc.getCandles(sym)) |result| { allocator.free(result.data); try stdout.print("candles ok", .{}); @@ -406,21 +408,27 @@ fn refresh(allocator: std.mem.Allocator) !void { } // Dividends + try printRateLimitWait(&svc, stdout); if (svc.getDividends(sym)) |result| { - allocator.free(result.data); + zfin.Dividend.freeSlice(allocator, result.data); try stdout.print(", dividends ok", .{}); } else |err| { try stdout.print(", dividends FAILED ({s})", .{@errorName(err)}); sym_ok = false; } - // Earnings - if (svc.getEarnings(sym)) |result| { - allocator.free(result.data); - try stdout.print(", earnings ok", .{}); - } else |err| { - try stdout.print(", earnings FAILED ({s})", .{@errorName(err)}); - sym_ok = false; + // Earnings (skip for mutual funds — they don't report quarterly earnings) + if (!isMutualFund(sym)) { + try printRateLimitWait(&svc, stdout); + if (svc.getEarnings(sym)) |result| { + allocator.free(result.data); + try stdout.print(", earnings ok", .{}); + } else |err| { + try stdout.print(", earnings FAILED ({s})", .{@errorName(err)}); + sym_ok = false; + } + } else { + try stdout.print(", earnings skipped (fund)", .{}); } try stdout.print("\n", .{});