make price ratio non-null
All checks were successful
Generic zig build / build (push) Successful in 29s

This commit is contained in:
Emil Lerch 2026-03-11 15:34:47 -07:00
parent f8a9607bc9
commit 7ba1df8ba9
Signed by: lobo
GPG key ID: A7B62D657EF764F8
3 changed files with 11 additions and 13 deletions

View file

@ -163,7 +163,7 @@ pub fn portfolioSummary(
// Only apply price_ratio to live/fetched prices. Manual/fallback prices // Only apply price_ratio to live/fetched prices. Manual/fallback prices
// (avg_cost) are already in the correct terms for the share class. // (avg_cost) are already in the correct terms for the share class.
const is_manual = if (manual_prices) |mp| mp.contains(pos.symbol) else false; const is_manual = if (manual_prices) |mp| mp.contains(pos.symbol) else false;
const price = if (is_manual) raw_price else raw_price * (pos.price_ratio orelse 1.0); const price = if (is_manual) raw_price else raw_price * pos.price_ratio;
const mv = pos.shares * price; const mv = pos.shares * price;
total_value += mv; total_value += mv;
total_cost += pos.total_cost; total_cost += pos.total_cost;

11
src/cache/store.zig vendored
View file

@ -906,13 +906,12 @@ test "portfolio: price_ratio round-trip" {
try std.testing.expectEqualStrings("02315N600", portfolio.lots[0].symbol); try std.testing.expectEqualStrings("02315N600", portfolio.lots[0].symbol);
try std.testing.expectEqualStrings("VTTHX", portfolio.lots[0].ticker.?); try std.testing.expectEqualStrings("VTTHX", portfolio.lots[0].ticker.?);
try std.testing.expectEqualStrings("VTTHX", portfolio.lots[0].priceSymbol()); try std.testing.expectEqualStrings("VTTHX", portfolio.lots[0].priceSymbol());
try std.testing.expect(portfolio.lots[0].price_ratio != null); try std.testing.expectApproxEqAbs(@as(f64, 5.185), portfolio.lots[0].price_ratio, 0.001);
try std.testing.expectApproxEqAbs(@as(f64, 5.185), portfolio.lots[0].price_ratio.?, 0.001);
try std.testing.expectEqualStrings("VANGUARD TARGET 2035", portfolio.lots[0].note.?); try std.testing.expectEqualStrings("VANGUARD TARGET 2035", portfolio.lots[0].note.?);
// Regular lot no price_ratio // Regular lot no price_ratio (default 1.0)
try std.testing.expectEqualStrings("AAPL", portfolio.lots[1].symbol); try std.testing.expectEqualStrings("AAPL", portfolio.lots[1].symbol);
try std.testing.expect(portfolio.lots[1].price_ratio == null); try std.testing.expectApproxEqAbs(@as(f64, 1.0), portfolio.lots[1].price_ratio, 0.001);
try std.testing.expect(portfolio.lots[1].ticker == null); try std.testing.expect(portfolio.lots[1].ticker == null);
// Round-trip: serialize and deserialize again // Round-trip: serialize and deserialize again
@ -923,7 +922,7 @@ test "portfolio: price_ratio round-trip" {
defer portfolio2.deinit(); defer portfolio2.deinit();
try std.testing.expectEqual(@as(usize, 2), portfolio2.lots.len); try std.testing.expectEqual(@as(usize, 2), portfolio2.lots.len);
try std.testing.expectApproxEqAbs(@as(f64, 5.185), portfolio2.lots[0].price_ratio.?, 0.001); try std.testing.expectApproxEqAbs(@as(f64, 5.185), portfolio2.lots[0].price_ratio, 0.001);
try std.testing.expectEqualStrings("VTTHX", portfolio2.lots[0].ticker.?); try std.testing.expectEqualStrings("VTTHX", portfolio2.lots[0].ticker.?);
try std.testing.expect(portfolio2.lots[1].price_ratio == null); try std.testing.expectApproxEqAbs(@as(f64, 1.0), portfolio2.lots[1].price_ratio, 0.001);
} }

View file

@ -66,7 +66,7 @@ pub const Lot = struct {
/// (from the `ticker` symbol) is multiplied by this ratio to get the actual /// (from the `ticker` symbol) is multiplied by this ratio to get the actual
/// institutional NAV. E.g. if VTTHX (investor) is $27.78 and the institutional /// institutional NAV. E.g. if VTTHX (investor) is $27.78 and the institutional
/// class trades at $144.04, price_ratio = 144.04 / 27.78 5.185. /// class trades at $144.04, price_ratio = 144.04 / 27.78 5.185.
price_ratio: ?f64 = null, price_ratio: f64 = 1.0,
/// The symbol to use for price fetching (ticker if set, else symbol). /// The symbol to use for price fetching (ticker if set, else symbol).
pub fn priceSymbol(self: Lot) []const u8 { pub fn priceSymbol(self: Lot) []const u8 {
@ -129,7 +129,7 @@ pub const Position = struct {
/// Currently positions() takes the ratio from the first lot that has one. /// Currently positions() takes the ratio from the first lot that has one.
/// Supporting dual-holding of investor + institutional shares of the same /// Supporting dual-holding of investor + institutional shares of the same
/// ticker would require a different grouping key in positions(). /// ticker would require a different grouping key in positions().
price_ratio: ?f64 = null, price_ratio: f64 = 1.0,
}; };
/// A portfolio is a collection of lots. /// A portfolio is a collection of lots.
@ -250,7 +250,7 @@ pub const Portfolio = struct {
entry.value_ptr.account = "Multiple"; entry.value_ptr.account = "Multiple";
} }
// Propagate price_ratio from the first lot that has one // Propagate price_ratio from the first lot that has one
if (entry.value_ptr.price_ratio == null and lot.price_ratio != null) { if (entry.value_ptr.price_ratio == 1.0 and lot.price_ratio != 1.0) {
entry.value_ptr.price_ratio = lot.price_ratio; entry.value_ptr.price_ratio = lot.price_ratio;
} }
} }
@ -537,11 +537,10 @@ test "positions propagates price_ratio from lot" {
for (pos) |p| { for (pos) |p| {
if (std.mem.eql(u8, p.symbol, "VTTHX")) { if (std.mem.eql(u8, p.symbol, "VTTHX")) {
try std.testing.expectApproxEqAbs(@as(f64, 150.0), p.shares, 0.01); try std.testing.expectApproxEqAbs(@as(f64, 150.0), p.shares, 0.01);
try std.testing.expect(p.price_ratio != null); try std.testing.expectApproxEqAbs(@as(f64, 5.185), p.price_ratio, 0.001);
try std.testing.expectApproxEqAbs(@as(f64, 5.185), p.price_ratio.?, 0.001);
} else { } else {
try std.testing.expectEqualStrings("AAPL", p.symbol); try std.testing.expectEqualStrings("AAPL", p.symbol);
try std.testing.expect(p.price_ratio == null); try std.testing.expectApproxEqAbs(@as(f64, 1.0), p.price_ratio, 0.001);
} }
} }
} }