centralize portfolio load
This commit is contained in:
parent
83d20cc45c
commit
acdda0313d
4 changed files with 69 additions and 54 deletions
|
|
@ -5,24 +5,12 @@ const fmt = cli.fmt;
|
||||||
|
|
||||||
/// CLI `analysis` command: show portfolio analysis breakdowns.
|
/// CLI `analysis` command: show portfolio analysis breakdowns.
|
||||||
pub fn run(allocator: std.mem.Allocator, svc: *zfin.DataService, file_path: []const u8, color: bool, out: *std.Io.Writer) !void {
|
pub fn run(allocator: std.mem.Allocator, svc: *zfin.DataService, file_path: []const u8, color: bool, out: *std.Io.Writer) !void {
|
||||||
// Load portfolio
|
var loaded = cli.loadPortfolio(allocator, file_path) orelse return;
|
||||||
const file_data = std.fs.cwd().readFileAlloc(allocator, file_path, 10 * 1024 * 1024) catch {
|
defer loaded.deinit(allocator);
|
||||||
try cli.stderrPrint("Error: Cannot read portfolio file\n");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
defer allocator.free(file_data);
|
|
||||||
|
|
||||||
var portfolio = zfin.cache.deserializePortfolio(allocator, file_data) catch {
|
const portfolio = loaded.portfolio;
|
||||||
try cli.stderrPrint("Error: Cannot parse portfolio file\n");
|
const positions = loaded.positions;
|
||||||
return;
|
const syms = loaded.syms;
|
||||||
};
|
|
||||||
defer portfolio.deinit();
|
|
||||||
|
|
||||||
const positions = try portfolio.positions(allocator);
|
|
||||||
defer allocator.free(positions);
|
|
||||||
|
|
||||||
const syms = try portfolio.stockSymbols(allocator);
|
|
||||||
defer allocator.free(syms);
|
|
||||||
|
|
||||||
// Build prices from cache
|
// Build prices from cache
|
||||||
var prices = std.StringHashMap(f64).init(allocator);
|
var prices = std.StringHashMap(f64).init(allocator);
|
||||||
|
|
|
||||||
|
|
@ -250,6 +250,60 @@ pub fn loadPortfolioPrices(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Portfolio loading ────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Result of loading and parsing a portfolio file. Caller must call deinit().
|
||||||
|
pub const LoadedPortfolio = struct {
|
||||||
|
file_data: []const u8,
|
||||||
|
portfolio: zfin.Portfolio,
|
||||||
|
positions: []const zfin.Position,
|
||||||
|
syms: []const []const u8,
|
||||||
|
|
||||||
|
pub fn deinit(self: *LoadedPortfolio, allocator: std.mem.Allocator) void {
|
||||||
|
allocator.free(self.syms);
|
||||||
|
allocator.free(self.positions);
|
||||||
|
self.portfolio.deinit();
|
||||||
|
allocator.free(self.file_data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Read, deserialize, and extract positions + symbols from a portfolio file.
|
||||||
|
/// Returns null (with stderr message) on read/parse errors.
|
||||||
|
pub fn loadPortfolio(allocator: std.mem.Allocator, file_path: []const u8) ?LoadedPortfolio {
|
||||||
|
const file_data = std.fs.cwd().readFileAlloc(allocator, file_path, 10 * 1024 * 1024) catch {
|
||||||
|
stderrPrint("Error: Cannot read portfolio file\n") catch {};
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
var portfolio = zfin.cache.deserializePortfolio(allocator, file_data) catch {
|
||||||
|
allocator.free(file_data);
|
||||||
|
stderrPrint("Error: Cannot parse portfolio file\n") catch {};
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const positions = portfolio.positions(allocator) catch {
|
||||||
|
portfolio.deinit();
|
||||||
|
allocator.free(file_data);
|
||||||
|
stderrPrint("Error: Cannot compute positions\n") catch {};
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const syms = portfolio.stockSymbols(allocator) catch {
|
||||||
|
allocator.free(positions);
|
||||||
|
portfolio.deinit();
|
||||||
|
allocator.free(file_data);
|
||||||
|
stderrPrint("Error: Cannot get stock symbols\n") catch {};
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.file_data = file_data,
|
||||||
|
.portfolio = portfolio,
|
||||||
|
.positions = positions,
|
||||||
|
.syms = syms,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// ── Portfolio data pipeline ──────────────────────────────────
|
// ── Portfolio data pipeline ──────────────────────────────────
|
||||||
|
|
||||||
/// Result of the shared portfolio data pipeline. Caller must call deinit().
|
/// Result of the shared portfolio data pipeline. Caller must call deinit().
|
||||||
|
|
|
||||||
|
|
@ -11,33 +11,18 @@ fn setIntentFg(out: *std.Io.Writer, color: bool, intent: fmt.StyleIntent) !void
|
||||||
|
|
||||||
pub fn run(allocator: std.mem.Allocator, svc: *zfin.DataService, file_path: []const u8, watchlist_path: ?[]const u8, force_refresh: bool, color: bool, out: *std.Io.Writer) !void {
|
pub fn run(allocator: std.mem.Allocator, svc: *zfin.DataService, file_path: []const u8, watchlist_path: ?[]const u8, force_refresh: bool, color: bool, out: *std.Io.Writer) !void {
|
||||||
// Load portfolio from SRF file
|
// Load portfolio from SRF file
|
||||||
const data = std.fs.cwd().readFileAlloc(allocator, file_path, 10 * 1024 * 1024) catch |err| {
|
var loaded = cli.loadPortfolio(allocator, file_path) orelse return;
|
||||||
try cli.stderrPrint("Error reading portfolio file: ");
|
defer loaded.deinit(allocator);
|
||||||
try cli.stderrPrint(@errorName(err));
|
|
||||||
try cli.stderrPrint("\n");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
defer allocator.free(data);
|
|
||||||
|
|
||||||
var portfolio = zfin.cache.deserializePortfolio(allocator, data) catch {
|
const portfolio = loaded.portfolio;
|
||||||
try cli.stderrPrint("Error parsing portfolio file.\n");
|
const positions = loaded.positions;
|
||||||
return;
|
const syms = loaded.syms;
|
||||||
};
|
|
||||||
defer portfolio.deinit();
|
|
||||||
|
|
||||||
if (portfolio.lots.len == 0) {
|
if (portfolio.lots.len == 0) {
|
||||||
try cli.stderrPrint("Portfolio is empty.\n");
|
try cli.stderrPrint("Portfolio is empty.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get stock/ETF positions (excludes options, CDs, cash)
|
|
||||||
const positions = try portfolio.positions(allocator);
|
|
||||||
defer allocator.free(positions);
|
|
||||||
|
|
||||||
// Get unique stock/ETF symbols and fetch current prices
|
|
||||||
const syms = try portfolio.stockSymbols(allocator);
|
|
||||||
defer allocator.free(syms);
|
|
||||||
|
|
||||||
var prices = std.StringHashMap(f64).init(allocator);
|
var prices = std.StringHashMap(f64).init(allocator);
|
||||||
defer prices.deinit();
|
defer prices.deinit();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,24 +19,12 @@ const stock_benchmark = "SPY";
|
||||||
const bond_benchmark = "AGG";
|
const bond_benchmark = "AGG";
|
||||||
|
|
||||||
pub fn run(allocator: std.mem.Allocator, svc: *zfin.DataService, file_path: []const u8, color: bool, out: *std.Io.Writer) !void {
|
pub fn run(allocator: std.mem.Allocator, svc: *zfin.DataService, file_path: []const u8, color: bool, out: *std.Io.Writer) !void {
|
||||||
// Load portfolio
|
var loaded = cli.loadPortfolio(allocator, file_path) orelse return;
|
||||||
const file_data = std.fs.cwd().readFileAlloc(allocator, file_path, 10 * 1024 * 1024) catch {
|
defer loaded.deinit(allocator);
|
||||||
try cli.stderrPrint("Error: Cannot read portfolio file\n");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
defer allocator.free(file_data);
|
|
||||||
|
|
||||||
var portfolio = zfin.cache.deserializePortfolio(allocator, file_data) catch {
|
const portfolio = loaded.portfolio;
|
||||||
try cli.stderrPrint("Error: Cannot parse portfolio file\n");
|
const positions = loaded.positions;
|
||||||
return;
|
const syms = loaded.syms;
|
||||||
};
|
|
||||||
defer portfolio.deinit();
|
|
||||||
|
|
||||||
const positions = try portfolio.positions(allocator);
|
|
||||||
defer allocator.free(positions);
|
|
||||||
|
|
||||||
const syms = try portfolio.stockSymbols(allocator);
|
|
||||||
defer allocator.free(syms);
|
|
||||||
|
|
||||||
// Build prices from cache
|
// Build prices from cache
|
||||||
var prices = std.StringHashMap(f64).init(allocator);
|
var prices = std.StringHashMap(f64).init(allocator);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue