diff --git a/README.md b/README.md index 68e5284..92e97aa 100644 --- a/README.md +++ b/README.md @@ -61,10 +61,14 @@ XML response format: Use cron to keep the cache warm for all symbols in your portfolio: ```sh -# Refresh all portfolio symbols nightly at 5pm ET (after market close) +# Refresh all portfolio symbols daily at 5pm ET (after market close) 0 17 * * 1-5 cd /path/to/workdir && ZFIN_PORTFOLIO=portfolio.srf zfin-server refresh ``` +The candle cache TTL is 23h45m (not a full 24h), providing a 15-minute jitter +buffer so daily cron runs at the same time always see stale data and trigger a +fresh fetch. Dividend and earnings TTLs are 14 and 30 days respectively. + The `refresh` command reads the portfolio SRF file (defaults to `portfolio.srf` in the current directory if `ZFIN_PORTFOLIO` is not set), extracts all stock and watch symbols, and fetches candles, dividends, and earnings for each. diff --git a/build.zig.zon b/build.zig.zon index 46e66d3..35158b8 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -14,8 +14,8 @@ .hash = "httpz-0.0.0-PNVzrBtMBwAPcQx3mNEgat3Xbsynw-eIC9SmOX5M9XtP", }, .zfin = .{ - .url = "git+https://git.lerch.org/lobo/zfin.git#d25d6acb9b9643e47257f61b489f5fa7fbbac7f3", - .hash = "zfin-0.0.0-J-B21qPuCgDINNDuVwrUiIyzh1i-H8yv1ySdbCMAishl", + .url = "git+https://git.lerch.org/lobo/zfin.git#31ae11cec0c16fcc60c5847b256c4b750aa8dd16", + .hash = "zfin-0.0.0-J-B21rbwCgALvwhOMCdo0yzEtrD-rF-YuSB8KuEVViI3", }, }, } diff --git a/docker/Dockerfile b/docker/Dockerfile index 7c33147..85c737c 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -2,6 +2,7 @@ FROM scratch COPY ca-certificates.crt /etc/ssl/certs/ca-certificates.crt COPY zfin-server /zfin-server ENV HOME=/home/zfin-server +WORKDIR /home/zfin-server USER 1000:1000 EXPOSE 8080 ENTRYPOINT ["/zfin-server", "serve"] diff --git a/src/main.zig b/src/main.zig index 0781838..8a83c2c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -270,12 +270,6 @@ fn printRateLimitWait(svc: *zfin.DataService, stdout: *std.Io.Writer) !void { } } -/// 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"; @@ -417,18 +411,14 @@ fn refresh(allocator: std.mem.Allocator) !void { 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)", .{}); + // Earnings + 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; } try stdout.print("\n", .{});