portfolio cli/tui cleanup
This commit is contained in:
parent
b4f3857cef
commit
88241b2c7b
3 changed files with 28 additions and 23 deletions
|
|
@ -3,7 +3,7 @@ const zfin = @import("../root.zig");
|
||||||
const cli = @import("common.zig");
|
const cli = @import("common.zig");
|
||||||
const fmt = cli.fmt;
|
const fmt = cli.fmt;
|
||||||
|
|
||||||
pub fn run(allocator: std.mem.Allocator, config: zfin.Config, 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| {
|
const data = std.fs.cwd().readFileAlloc(allocator, file_path, 10 * 1024 * 1024) catch |err| {
|
||||||
try cli.stderrPrint("Error reading portfolio file: ");
|
try cli.stderrPrint("Error reading portfolio file: ");
|
||||||
|
|
@ -56,9 +56,6 @@ pub fn run(allocator: std.mem.Allocator, config: zfin.Config, svc: *zfin.DataSer
|
||||||
const all_syms_count = syms.len + watch_syms.items.len;
|
const all_syms_count = syms.len + watch_syms.items.len;
|
||||||
|
|
||||||
if (all_syms_count > 0) {
|
if (all_syms_count > 0) {
|
||||||
if (config.twelvedata_key == null) {
|
|
||||||
try cli.stderrPrint("Warning: TWELVEDATA_API_KEY not set. Cannot fetch current prices.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Progress callback for per-symbol output
|
// Progress callback for per-symbol output
|
||||||
var progress_ctx = cli.LoadProgress{
|
var progress_ctx = cli.LoadProgress{
|
||||||
|
|
@ -201,11 +198,11 @@ pub fn display(
|
||||||
const gl_abs = if (summary.unrealized_gain_loss >= 0) summary.unrealized_gain_loss else -summary.unrealized_gain_loss;
|
const gl_abs = if (summary.unrealized_gain_loss >= 0) summary.unrealized_gain_loss else -summary.unrealized_gain_loss;
|
||||||
try out.print(" Value: {s} Cost: {s} ", .{ fmt.fmtMoneyAbs(&val_buf, summary.total_value), fmt.fmtMoneyAbs(&cost_buf, summary.total_cost) });
|
try out.print(" Value: {s} Cost: {s} ", .{ fmt.fmtMoneyAbs(&val_buf, summary.total_value), fmt.fmtMoneyAbs(&cost_buf, summary.total_cost) });
|
||||||
try cli.setGainLoss(out, color, summary.unrealized_gain_loss);
|
try cli.setGainLoss(out, color, summary.unrealized_gain_loss);
|
||||||
if (summary.unrealized_gain_loss >= 0) {
|
try out.print("Gain/Loss: {c}{s} ({d:.1}%)", .{
|
||||||
try out.print("Gain/Loss: +{s} ({d:.1}%)", .{ fmt.fmtMoneyAbs(&gl_buf, gl_abs), summary.unrealized_return * 100.0 });
|
@as(u8, if (summary.unrealized_gain_loss >= 0) '+' else '-'),
|
||||||
} else {
|
fmt.fmtMoneyAbs(&gl_buf, gl_abs),
|
||||||
try out.print("Gain/Loss: -{s} ({d:.1}%)", .{ fmt.fmtMoneyAbs(&gl_buf, gl_abs), summary.unrealized_return * 100.0 });
|
summary.unrealized_return * 100.0,
|
||||||
}
|
});
|
||||||
try cli.reset(out, color);
|
try cli.reset(out, color);
|
||||||
try out.print("\n", .{});
|
try out.print("\n", .{});
|
||||||
}
|
}
|
||||||
|
|
@ -369,11 +366,10 @@ pub fn display(
|
||||||
"", "", "", "TOTAL", fmt.fmtMoneyAbs(&total_mv_buf, summary.total_value),
|
"", "", "", "TOTAL", fmt.fmtMoneyAbs(&total_mv_buf, summary.total_value),
|
||||||
});
|
});
|
||||||
try cli.setGainLoss(out, color, summary.unrealized_gain_loss);
|
try cli.setGainLoss(out, color, summary.unrealized_gain_loss);
|
||||||
if (summary.unrealized_gain_loss >= 0) {
|
try out.print("{c}{s:>13}", .{
|
||||||
try out.print("+{s:>13}", .{fmt.fmtMoneyAbs(&total_gl_buf, gl_abs)});
|
@as(u8, if (summary.unrealized_gain_loss >= 0) '+' else '-'),
|
||||||
} else {
|
fmt.fmtMoneyAbs(&total_gl_buf, gl_abs),
|
||||||
try out.print("-{s:>13}", .{fmt.fmtMoneyAbs(&total_gl_buf, gl_abs)});
|
});
|
||||||
}
|
|
||||||
try cli.reset(out, color);
|
try cli.reset(out, color);
|
||||||
try out.print(" {s:>7}\n", .{"100.0%"});
|
try out.print(" {s:>7}\n", .{"100.0%"});
|
||||||
}
|
}
|
||||||
|
|
@ -382,11 +378,10 @@ pub fn display(
|
||||||
var rpl_buf: [24]u8 = undefined;
|
var rpl_buf: [24]u8 = undefined;
|
||||||
const rpl_abs = if (summary.realized_gain_loss >= 0) summary.realized_gain_loss else -summary.realized_gain_loss;
|
const rpl_abs = if (summary.realized_gain_loss >= 0) summary.realized_gain_loss else -summary.realized_gain_loss;
|
||||||
try cli.setGainLoss(out, color, summary.realized_gain_loss);
|
try cli.setGainLoss(out, color, summary.realized_gain_loss);
|
||||||
if (summary.realized_gain_loss >= 0) {
|
try out.print("\n Realized P&L: {c}{s}\n", .{
|
||||||
try out.print("\n Realized P&L: +{s}\n", .{fmt.fmtMoneyAbs(&rpl_buf, rpl_abs)});
|
@as(u8, if (summary.realized_gain_loss >= 0) '+' else '-'),
|
||||||
} else {
|
fmt.fmtMoneyAbs(&rpl_buf, rpl_abs),
|
||||||
try out.print("\n Realized P&L: -{s}\n", .{fmt.fmtMoneyAbs(&rpl_buf, rpl_abs)});
|
});
|
||||||
}
|
|
||||||
try cli.reset(out, color);
|
try cli.reset(out, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ pub fn main() !u8 {
|
||||||
file_path = args[pi];
|
file_path = args[pi];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try commands.portfolio.run(allocator, config, &svc, file_path, watchlist_path, force_refresh, color, out);
|
try commands.portfolio.run(allocator, &svc, file_path, watchlist_path, force_refresh, color, out);
|
||||||
} else if (std.mem.eql(u8, command, "lookup")) {
|
} else if (std.mem.eql(u8, command, "lookup")) {
|
||||||
if (args.len < 3) {
|
if (args.len < 3) {
|
||||||
try cli.stderrPrint("Error: 'lookup' requires a CUSIP argument\n");
|
try cli.stderrPrint("Error: 'lookup' requires a CUSIP argument\n");
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,16 @@ const gl_col_start: usize = col_end_market_value;
|
||||||
|
|
||||||
// ── Data loading ──────────────────────────────────────────────
|
// ── Data loading ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Load portfolio data: prices, summary, candle map, and historical snapshots.
|
||||||
|
///
|
||||||
|
/// Call paths:
|
||||||
|
/// 1. First tab visit: loadTabData() → here (guarded by portfolio_loaded flag)
|
||||||
|
/// 2. Manual refresh (r/F5): refreshCurrentTab() clears portfolio_loaded → loadTabData() → here
|
||||||
|
/// 3. Disk reload (R): reloadPortfolioFile() — separate function, cache-only, no network
|
||||||
|
///
|
||||||
|
/// On first call, uses prefetched_prices (populated before TUI started).
|
||||||
|
/// On refresh, fetches live via svc.loadPrices. Tab switching skips this
|
||||||
|
/// entirely because the portfolio_loaded guard in loadTabData() short-circuits.
|
||||||
pub fn loadPortfolioData(self: *App) void {
|
pub fn loadPortfolioData(self: *App) void {
|
||||||
self.portfolio_loaded = true;
|
self.portfolio_loaded = true;
|
||||||
self.freePortfolioSummary();
|
self.freePortfolioSummary();
|
||||||
|
|
@ -894,7 +904,7 @@ fn drawWelcomeScreen(self: *App, arena: std.mem.Allocator, buf: []vaxis.Cell, wi
|
||||||
.{ .text = "", .style = th.contentStyle() },
|
.{ .text = "", .style = th.contentStyle() },
|
||||||
.{ .text = " Portfolio mode:", .style = th.contentStyle() },
|
.{ .text = " Portfolio mode:", .style = th.contentStyle() },
|
||||||
.{ .text = " zfin -p portfolio.srf Load a portfolio file", .style = th.mutedStyle() },
|
.{ .text = " zfin -p portfolio.srf Load a portfolio file", .style = th.mutedStyle() },
|
||||||
.{ .text = try std.fmt.allocPrint(arena, " portfolio.srf Auto-loaded from cwd if present", .{}), .style = th.mutedStyle() },
|
.{ .text = " portfolio.srf Auto-loaded from cwd if present", .style = th.mutedStyle() },
|
||||||
.{ .text = "", .style = th.contentStyle() },
|
.{ .text = "", .style = th.contentStyle() },
|
||||||
.{ .text = " Navigation:", .style = th.contentStyle() },
|
.{ .text = " Navigation:", .style = th.contentStyle() },
|
||||||
.{ .text = " h / l Previous / next tab", .style = th.mutedStyle() },
|
.{ .text = " h / l Previous / next tab", .style = th.mutedStyle() },
|
||||||
|
|
@ -906,8 +916,8 @@ fn drawWelcomeScreen(self: *App, arena: std.mem.Allocator, buf: []vaxis.Cell, wi
|
||||||
.{ .text = " q Quit", .style = th.mutedStyle() },
|
.{ .text = " q Quit", .style = th.mutedStyle() },
|
||||||
.{ .text = "", .style = th.contentStyle() },
|
.{ .text = "", .style = th.contentStyle() },
|
||||||
.{ .text = " Sample portfolio.srf:", .style = th.contentStyle() },
|
.{ .text = " Sample portfolio.srf:", .style = th.contentStyle() },
|
||||||
.{ .text = " symbol::VTI,shares::100,open_date::2024-01-15,open_price::220.50", .style = th.dimStyle() },
|
.{ .text = " symbol::VTI,shares::100,open_date::2024-01-15,open_price::220.50", .style = th.mutedStyle() },
|
||||||
.{ .text = " symbol::AAPL,shares::50,open_date::2024-03-01,open_price::170.00", .style = th.dimStyle() },
|
.{ .text = " symbol::AAPL,shares::50,open_date::2024-03-01,open_price::170.00", .style = th.mutedStyle() },
|
||||||
};
|
};
|
||||||
try self.drawStyledContent(arena, buf, width, height, &welcome_lines);
|
try self.drawStyledContent(arena, buf, width, height, &welcome_lines);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue