From 1e8174e637183e05ed752e6442a7e3f9eb81aa22 Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Sun, 29 Mar 2026 10:57:25 -0700 Subject: [PATCH] update return calculations --- build.zig.zon | 4 ++-- src/main.zig | 55 ++++++++++++++++++--------------------------------- 2 files changed, 21 insertions(+), 38 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 5cc6c82..8161dfb 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#ecadfb492d8730c3226c738b6cb63e688d7f89dc", - .hash = "zfin-0.0.0-J-B21tqwDACaht3HgLXCYDXJsyZsMwzncEKeX2JYiiJG", + .url = "git+https://git.lerch.org/lobo/zfin#2ac4156bc1227ca7691a22a35411ff77064ae697", + .hash = "zfin-0.0.0-J-B21razDADDEOFs3alWrMtStAaULKO7H6Et1ViNwkpr", }, }, } diff --git a/src/main.zig b/src/main.zig index f8916ef..07677b2 100644 --- a/src/main.zig +++ b/src/main.zig @@ -137,14 +137,17 @@ fn handleReturns(app: *App, req: *httpz.Request, res: *httpz.Response) !void { } } - const candle_result = app.svc.getCandles(symbol) catch { + const result = app.svc.getTrailingReturns(symbol) catch { res.status = 404; res.body = "Symbol not found or fetch failed"; return; }; - defer app.allocator.free(candle_result.data); - const candles = candle_result.data; + defer app.allocator.free(result.candles); + if (result.dividends) |divs| { + defer zfin.Dividend.freeSlice(app.allocator, divs); + } + const candles = result.candles; if (candles.len == 0) { res.status = 404; res.body = "No candle data"; @@ -152,19 +155,24 @@ fn handleReturns(app: *App, req: *httpz.Request, res: *httpz.Response) !void { } const last_close = candles[candles.len - 1].close; - - // Price-only returns (from adjusted close) - const price_ret = zfin.performance.trailingReturns(candles); var date_buf: [10]u8 = undefined; const date_str = candles[candles.len - 1].date.format(&date_buf); - const risk = zfin.risk.trailingRisk(candles); - const p1y = if (price_ret.one_year) |r| r.annualized_return else null; - const p3y = if (price_ret.three_year) |r| r.annualized_return else null; - const p5y = if (price_ret.five_year) |r| r.annualized_return else null; - const p10y = if (price_ret.ten_year) |r| r.annualized_return else null; + // Price-only returns (from adjusted close) + const p1y = if (result.asof_price.one_year) |r| r.annualized_return else null; + const p3y = if (result.asof_price.three_year) |r| r.annualized_return else null; + const p5y = if (result.asof_price.five_year) |r| r.annualized_return else null; + const p10y = if (result.asof_price.ten_year) |r| r.annualized_return else null; + + // Total returns (best of adj_close and dividend reinvestment) + const total = result.asof_total orelse result.asof_price; + const t1y = if (total.one_year) |r| r.annualized_return else null; + const t3y = if (total.three_year) |r| r.annualized_return else null; + const t5y = if (total.five_year) |r| r.annualized_return else null; + const t10y = if (total.ten_year) |r| r.annualized_return else null; // Per-period volatility + const risk = zfin.risk.trailingRisk(candles); const v1y = if (risk.one_year) |r| r.volatility else null; const v3y = if (risk.three_year) |r| r.volatility else null; const v5y = if (risk.five_year) |r| r.volatility else null; @@ -174,31 +182,6 @@ fn handleReturns(app: *App, req: *httpz.Request, res: *httpz.Response) !void { const vol_best = v10y orelse v5y orelse v3y orelse v1y; const vol_term: ?u8 = if (v10y != null) 10 else if (v5y != null) 5 else if (v3y != null) 3 else if (v1y != null) 1 else null; - // Total returns: adj_close is the primary source (accounts for splits + dividends). - // Dividend-reinvestment only fills gaps where adj_close returns null - // (e.g. stable-NAV funds with short candle history). - var t1y: ?f64 = p1y; - var t3y: ?f64 = p3y; - var t5y: ?f64 = p5y; - var t10y: ?f64 = p10y; - - if (app.svc.getDividends(symbol)) |div_result| { - defer zfin.Dividend.freeSlice(app.allocator, div_result.data); - const total = zfin.performance.trailingReturnsWithDividends(candles, div_result.data); - if (t1y == null) if (total.one_year) |r| { - t1y = r.annualized_return; - }; - if (t3y == null) if (total.three_year) |r| { - t3y = r.annualized_return; - }; - if (t5y == null) if (total.five_year) |r| { - t5y = r.annualized_return; - }; - if (t10y == null) if (total.ten_year) |r| { - t10y = r.annualized_return; - }; - } else |_| {} - // Check if XML requested if (q.get("fmt")) |fmt| { if (std.ascii.eqlIgnoreCase(fmt, "xml")) {