Compare commits
3 commits
22b0d807bb
...
be48c4a8e3
| Author | SHA1 | Date | |
|---|---|---|---|
| be48c4a8e3 | |||
| 926fb64362 | |||
| 5ffd9c1c8e |
2 changed files with 45 additions and 7 deletions
|
|
@ -14,8 +14,8 @@
|
||||||
.hash = "httpz-0.0.0-PNVzrBtMBwAPcQx3mNEgat3Xbsynw-eIC9SmOX5M9XtP",
|
.hash = "httpz-0.0.0-PNVzrBtMBwAPcQx3mNEgat3Xbsynw-eIC9SmOX5M9XtP",
|
||||||
},
|
},
|
||||||
.zfin = .{
|
.zfin = .{
|
||||||
.url = "git+https://git.lerch.org/lobo/zfin#1cd775c27e80371a53aae2a58bb1c8156256abe7",
|
.url = "git+https://git.lerch.org/lobo/zfin#913996072ee06c81121f50657e325ce8a56856e6",
|
||||||
.hash = "zfin-0.0.0-J-B21o_VCwC6uOv6mCU4T_qCGEjz26zfY5u6kQ5XQuaj",
|
.hash = "zfin-0.0.0-J-B21paODADTI5LGofs453BW06vDDK9UZl1LapWa-8eb",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
48
src/main.zig
48
src/main.zig
|
|
@ -65,9 +65,12 @@ fn handleHelp(_: *App, _: *httpz.Request, res: *httpz.Response) !void {
|
||||||
\\ GET /symbols List of tracked symbols
|
\\ GET /symbols List of tracked symbols
|
||||||
\\
|
\\
|
||||||
\\Returns fields:
|
\\Returns fields:
|
||||||
|
\\ lastClose Last closing price
|
||||||
\\ trailing{1,3,5,10}YearReturn Total return with dividend reinvestment
|
\\ trailing{1,3,5,10}YearReturn Total return with dividend reinvestment
|
||||||
\\ price{1,3,5,10}YearReturn Price-only return (from adjusted close)
|
\\ price{1,3,5,10}YearReturn Price-only return (from adjusted close)
|
||||||
\\ volatility 3-year annualized volatility
|
\\ volatility Longest-term available annualized volatility
|
||||||
|
\\ volatilityTerm Period (years) of the volatility field
|
||||||
|
\\ volatility{1,3,5,10}Year Per-period annualized volatility
|
||||||
\\
|
\\
|
||||||
\\XML example (LibreCalc):
|
\\XML example (LibreCalc):
|
||||||
\\ =FILTERXML(WEBSERVICE("http://host/AAPL/returns?fmt=xml"),"//total10YearReturn")
|
\\ =FILTERXML(WEBSERVICE("http://host/AAPL/returns?fmt=xml"),"//total10YearReturn")
|
||||||
|
|
@ -148,6 +151,8 @@ fn handleReturns(app: *App, req: *httpz.Request, res: *httpz.Response) !void {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const last_close = candles[candles.len - 1].close;
|
||||||
|
|
||||||
// Price-only returns (from adjusted close)
|
// Price-only returns (from adjusted close)
|
||||||
const price_ret = zfin.performance.trailingReturns(candles);
|
const price_ret = zfin.performance.trailingReturns(candles);
|
||||||
var date_buf: [10]u8 = undefined;
|
var date_buf: [10]u8 = undefined;
|
||||||
|
|
@ -158,7 +163,16 @@ fn handleReturns(app: *App, req: *httpz.Request, res: *httpz.Response) !void {
|
||||||
const p3y = if (price_ret.three_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 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;
|
const p10y = if (price_ret.ten_year) |r| r.annualized_return else null;
|
||||||
const vol = if (risk.three_year) |r| r.volatility else null;
|
|
||||||
|
// Per-period volatility
|
||||||
|
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;
|
||||||
|
const v10y = if (risk.ten_year) |r| r.volatility else null;
|
||||||
|
|
||||||
|
// Longest-term volatility convenience fields
|
||||||
|
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 with dividend reinvestment (non-fatal if dividends unavailable).
|
// Total returns with dividend reinvestment (non-fatal if dividends unavailable).
|
||||||
// trailing* fields use total return when available, falling back to price-only.
|
// trailing* fields use total return when available, falling back to price-only.
|
||||||
|
|
@ -192,6 +206,7 @@ fn handleReturns(app: *App, req: *httpz.Request, res: *httpz.Response) !void {
|
||||||
\\<returns>
|
\\<returns>
|
||||||
\\ <ticker>{s}</ticker>
|
\\ <ticker>{s}</ticker>
|
||||||
\\ <returnDate>{s}</returnDate>
|
\\ <returnDate>{s}</returnDate>
|
||||||
|
\\ <lastClose>{d:.2}</lastClose>
|
||||||
\\ <trailing1YearReturn>{s}</trailing1YearReturn>
|
\\ <trailing1YearReturn>{s}</trailing1YearReturn>
|
||||||
\\ <trailing3YearReturn>{s}</trailing3YearReturn>
|
\\ <trailing3YearReturn>{s}</trailing3YearReturn>
|
||||||
\\ <trailing5YearReturn>{s}</trailing5YearReturn>
|
\\ <trailing5YearReturn>{s}</trailing5YearReturn>
|
||||||
|
|
@ -201,11 +216,17 @@ fn handleReturns(app: *App, req: *httpz.Request, res: *httpz.Response) !void {
|
||||||
\\ <price5YearReturn>{s}</price5YearReturn>
|
\\ <price5YearReturn>{s}</price5YearReturn>
|
||||||
\\ <price10YearReturn>{s}</price10YearReturn>
|
\\ <price10YearReturn>{s}</price10YearReturn>
|
||||||
\\ <volatility>{s}</volatility>
|
\\ <volatility>{s}</volatility>
|
||||||
|
\\ <volatilityTerm>{s}</volatilityTerm>
|
||||||
|
\\ <volatility1Year>{s}</volatility1Year>
|
||||||
|
\\ <volatility3Year>{s}</volatility3Year>
|
||||||
|
\\ <volatility5Year>{s}</volatility5Year>
|
||||||
|
\\ <volatility10Year>{s}</volatility10Year>
|
||||||
\\</returns>
|
\\</returns>
|
||||||
\\
|
\\
|
||||||
, .{
|
, .{
|
||||||
symbol,
|
symbol,
|
||||||
date_str,
|
date_str,
|
||||||
|
last_close,
|
||||||
fmtPct(arena, t1y),
|
fmtPct(arena, t1y),
|
||||||
fmtPct(arena, t3y),
|
fmtPct(arena, t3y),
|
||||||
fmtPct(arena, t5y),
|
fmtPct(arena, t5y),
|
||||||
|
|
@ -214,7 +235,12 @@ fn handleReturns(app: *App, req: *httpz.Request, res: *httpz.Response) !void {
|
||||||
fmtPct(arena, p3y),
|
fmtPct(arena, p3y),
|
||||||
fmtPct(arena, p5y),
|
fmtPct(arena, p5y),
|
||||||
fmtPct(arena, p10y),
|
fmtPct(arena, p10y),
|
||||||
fmtPct(arena, vol),
|
fmtPct(arena, vol_best),
|
||||||
|
fmtInt(arena, vol_term),
|
||||||
|
fmtPct(arena, v1y),
|
||||||
|
fmtPct(arena, v3y),
|
||||||
|
fmtPct(arena, v5y),
|
||||||
|
fmtPct(arena, v10y),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -222,10 +248,11 @@ fn handleReturns(app: *App, req: *httpz.Request, res: *httpz.Response) !void {
|
||||||
|
|
||||||
res.content_type = httpz.ContentType.JSON;
|
res.content_type = httpz.ContentType.JSON;
|
||||||
res.body = try std.fmt.allocPrint(arena,
|
res.body = try std.fmt.allocPrint(arena,
|
||||||
\\{{"ticker":"{s}","returnDate":"{s}","trailing1YearReturn":{s},"trailing3YearReturn":{s},"trailing5YearReturn":{s},"trailing10YearReturn":{s},"price1YearReturn":{s},"price3YearReturn":{s},"price5YearReturn":{s},"price10YearReturn":{s},"volatility":{s}}}
|
\\{{"ticker":"{s}","returnDate":"{s}","lastClose":{d:.2},"trailing1YearReturn":{s},"trailing3YearReturn":{s},"trailing5YearReturn":{s},"trailing10YearReturn":{s},"price1YearReturn":{s},"price3YearReturn":{s},"price5YearReturn":{s},"price10YearReturn":{s},"volatility":{s},"volatilityTerm":{s},"volatility1Year":{s},"volatility3Year":{s},"volatility5Year":{s},"volatility10Year":{s}}}
|
||||||
, .{
|
, .{
|
||||||
symbol,
|
symbol,
|
||||||
date_str,
|
date_str,
|
||||||
|
last_close,
|
||||||
fmtPct(arena, t1y),
|
fmtPct(arena, t1y),
|
||||||
fmtPct(arena, t3y),
|
fmtPct(arena, t3y),
|
||||||
fmtPct(arena, t5y),
|
fmtPct(arena, t5y),
|
||||||
|
|
@ -234,7 +261,12 @@ fn handleReturns(app: *App, req: *httpz.Request, res: *httpz.Response) !void {
|
||||||
fmtPct(arena, p3y),
|
fmtPct(arena, p3y),
|
||||||
fmtPct(arena, p5y),
|
fmtPct(arena, p5y),
|
||||||
fmtPct(arena, p10y),
|
fmtPct(arena, p10y),
|
||||||
fmtPct(arena, vol),
|
fmtPct(arena, vol_best),
|
||||||
|
fmtInt(arena, vol_term),
|
||||||
|
fmtPct(arena, v1y),
|
||||||
|
fmtPct(arena, v3y),
|
||||||
|
fmtPct(arena, v5y),
|
||||||
|
fmtPct(arena, v10y),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -323,6 +355,12 @@ fn fmtPct(arena: std.mem.Allocator, value: ?f64) []const u8 {
|
||||||
return "null";
|
return "null";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Format an optional integer, or "null" if absent.
|
||||||
|
fn fmtInt(arena: std.mem.Allocator, value: ?u8) []const u8 {
|
||||||
|
if (value) |v| return std.fmt.allocPrint(arena, "{d}", .{v}) catch "null";
|
||||||
|
return "null";
|
||||||
|
}
|
||||||
|
|
||||||
/// Append a watch lot for the given symbol to the portfolio SRF file,
|
/// Append a watch lot for the given symbol to the portfolio SRF file,
|
||||||
/// unless it already exists. Best-effort — errors are logged, not fatal.
|
/// unless it already exists. Best-effort — errors are logged, not fatal.
|
||||||
fn appendWatchSymbol(symbol: []const u8) !void {
|
fn appendWatchSymbol(symbol: []const u8) !void {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue