clean up analysis
This commit is contained in:
parent
b718c1ae39
commit
ce72985054
2 changed files with 28 additions and 57 deletions
|
|
@ -4,9 +4,7 @@ const cli = @import("common.zig");
|
||||||
const fmt = cli.fmt;
|
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, config: zfin.Config, 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 {
|
||||||
_ = config;
|
|
||||||
|
|
||||||
// Load portfolio
|
// Load portfolio
|
||||||
const file_data = std.fs.cwd().readFileAlloc(allocator, file_path, 10 * 1024 * 1024) catch {
|
const file_data = std.fs.cwd().readFileAlloc(allocator, file_path, 10 * 1024 * 1024) catch {
|
||||||
try cli.stderrPrint("Error: Cannot read portfolio file\n");
|
try cli.stderrPrint("Error: Cannot read portfolio file\n");
|
||||||
|
|
@ -23,11 +21,12 @@ pub fn run(allocator: std.mem.Allocator, config: zfin.Config, svc: *zfin.DataSer
|
||||||
const positions = try portfolio.positions(allocator);
|
const positions = try portfolio.positions(allocator);
|
||||||
defer allocator.free(positions);
|
defer allocator.free(positions);
|
||||||
|
|
||||||
// Build prices map from cache
|
const syms = try portfolio.stockSymbols(allocator);
|
||||||
|
defer allocator.free(syms);
|
||||||
|
|
||||||
|
// Build prices from cache
|
||||||
var prices = std.StringHashMap(f64).init(allocator);
|
var prices = std.StringHashMap(f64).init(allocator);
|
||||||
defer prices.deinit();
|
defer prices.deinit();
|
||||||
|
|
||||||
// First pass: try cached candle prices
|
|
||||||
for (positions) |pos| {
|
for (positions) |pos| {
|
||||||
if (pos.shares <= 0) continue;
|
if (pos.shares <= 0) continue;
|
||||||
if (svc.getCachedCandles(pos.symbol)) |cs| {
|
if (svc.getCachedCandles(pos.symbol)) |cs| {
|
||||||
|
|
@ -37,15 +36,16 @@ pub fn run(allocator: std.mem.Allocator, config: zfin.Config, svc: *zfin.DataSer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Build fallback prices for symbols without cached candle data
|
|
||||||
var manual_price_set = try zfin.valuation.buildFallbackPrices(allocator, portfolio.lots, positions, &prices);
|
|
||||||
defer manual_price_set.deinit();
|
|
||||||
|
|
||||||
var summary = zfin.valuation.portfolioSummary(allocator, portfolio, positions, prices, manual_price_set) catch {
|
// Build summary via shared pipeline
|
||||||
|
var pf_data = cli.buildPortfolioData(allocator, portfolio, positions, syms, &prices, svc) catch |err| switch (err) {
|
||||||
|
error.NoAllocations, error.SummaryFailed => {
|
||||||
try cli.stderrPrint("Error computing portfolio summary.\n");
|
try cli.stderrPrint("Error computing portfolio summary.\n");
|
||||||
return;
|
return;
|
||||||
|
},
|
||||||
|
else => return err,
|
||||||
};
|
};
|
||||||
defer summary.deinit(allocator);
|
defer pf_data.deinit(allocator);
|
||||||
|
|
||||||
// Load classification metadata
|
// Load classification metadata
|
||||||
const dir_end = if (std.mem.lastIndexOfScalar(u8, file_path, '/')) |idx| idx + 1 else 0;
|
const dir_end = if (std.mem.lastIndexOfScalar(u8, file_path, '/')) |idx| idx + 1 else 0;
|
||||||
|
|
@ -78,10 +78,10 @@ pub fn run(allocator: std.mem.Allocator, config: zfin.Config, svc: *zfin.DataSer
|
||||||
|
|
||||||
var result = zfin.analysis.analyzePortfolio(
|
var result = zfin.analysis.analyzePortfolio(
|
||||||
allocator,
|
allocator,
|
||||||
summary.allocations,
|
pf_data.summary.allocations,
|
||||||
cm,
|
cm,
|
||||||
portfolio,
|
portfolio,
|
||||||
summary.total_value,
|
pf_data.summary.total_value,
|
||||||
acct_map_opt,
|
acct_map_opt,
|
||||||
) catch {
|
) catch {
|
||||||
try cli.stderrPrint("Error computing analysis.\n");
|
try cli.stderrPrint("Error computing analysis.\n");
|
||||||
|
|
@ -101,51 +101,22 @@ pub fn display(result: zfin.analysis.AnalysisResult, file_path: []const u8, colo
|
||||||
try cli.reset(out, color);
|
try cli.reset(out, color);
|
||||||
try out.print("========================================\n\n", .{});
|
try out.print("========================================\n\n", .{});
|
||||||
|
|
||||||
// Asset Class
|
const sections = [_]struct { items: []const zfin.analysis.BreakdownItem, title: []const u8 }{
|
||||||
try cli.setBold(out, color);
|
.{ .items = result.asset_class, .title = " Asset Class" },
|
||||||
try cli.setFg(out, color, cli.CLR_HEADER);
|
.{ .items = result.sector, .title = " Sector (Equities)" },
|
||||||
try out.print(" Asset Class\n", .{});
|
.{ .items = result.geo, .title = " Geographic" },
|
||||||
try cli.reset(out, color);
|
.{ .items = result.account, .title = " By Account" },
|
||||||
try printBreakdownSection(out, result.asset_class, label_width, bar_width, color);
|
.{ .items = result.tax_type, .title = " By Tax Type" },
|
||||||
|
};
|
||||||
|
|
||||||
// Sector
|
for (sections, 0..) |sec, si| {
|
||||||
if (result.sector.len > 0) {
|
if (si > 0 and sec.items.len == 0) continue;
|
||||||
try out.print("\n", .{});
|
if (si > 0) try out.print("\n", .{});
|
||||||
try cli.setBold(out, color);
|
try cli.setBold(out, color);
|
||||||
try cli.setFg(out, color, cli.CLR_HEADER);
|
try cli.setFg(out, color, cli.CLR_HEADER);
|
||||||
try out.print(" Sector (Equities)\n", .{});
|
try out.print("{s}\n", .{sec.title});
|
||||||
try cli.reset(out, color);
|
try cli.reset(out, color);
|
||||||
try printBreakdownSection(out, result.sector, label_width, bar_width, color);
|
try printBreakdownSection(out, sec.items, label_width, bar_width, color);
|
||||||
}
|
|
||||||
|
|
||||||
// Geographic
|
|
||||||
if (result.geo.len > 0) {
|
|
||||||
try out.print("\n", .{});
|
|
||||||
try cli.setBold(out, color);
|
|
||||||
try cli.setFg(out, color, cli.CLR_HEADER);
|
|
||||||
try out.print(" Geographic\n", .{});
|
|
||||||
try cli.reset(out, color);
|
|
||||||
try printBreakdownSection(out, result.geo, label_width, bar_width, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
// By Account
|
|
||||||
if (result.account.len > 0) {
|
|
||||||
try out.print("\n", .{});
|
|
||||||
try cli.setBold(out, color);
|
|
||||||
try cli.setFg(out, color, cli.CLR_HEADER);
|
|
||||||
try out.print(" By Account\n", .{});
|
|
||||||
try cli.reset(out, color);
|
|
||||||
try printBreakdownSection(out, result.account, label_width, bar_width, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tax Type
|
|
||||||
if (result.tax_type.len > 0) {
|
|
||||||
try out.print("\n", .{});
|
|
||||||
try cli.setBold(out, color);
|
|
||||||
try cli.setFg(out, color, cli.CLR_HEADER);
|
|
||||||
try out.print(" By Tax Type\n", .{});
|
|
||||||
try cli.reset(out, color);
|
|
||||||
try printBreakdownSection(out, result.tax_type, label_width, bar_width, color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unclassified
|
// Unclassified
|
||||||
|
|
|
||||||
|
|
@ -211,7 +211,7 @@ pub fn main() !u8 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try commands.analysis.run(allocator, config, &svc, analysis_file, color, out);
|
try commands.analysis.run(allocator, &svc, analysis_file, color, out);
|
||||||
} else {
|
} else {
|
||||||
try cli.stderrPrint("Unknown command. Run 'zfin help' for usage.\n");
|
try cli.stderrPrint("Unknown command. Run 'zfin help' for usage.\n");
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue