projections CLI kitty-chart for return-backtest and convergence (if terminal supported)
This commit is contained in:
parent
42a16cbbd3
commit
a70e61d873
1 changed files with 66 additions and 16 deletions
|
|
@ -24,6 +24,7 @@ const shiller = @import("../data/shiller.zig");
|
|||
const chart_export = @import("../chart_export.zig");
|
||||
const projection_chart = @import("../charts/projection_chart.zig");
|
||||
const projections = @import("../analytics/projections.zig");
|
||||
const forecast_chart = @import("../charts/forecast_chart.zig");
|
||||
const braille = @import("../charts/braille.zig");
|
||||
const term_graphics = @import("../term_graphics.zig");
|
||||
const term_query = @import("../term_query.zig");
|
||||
|
|
@ -251,9 +252,19 @@ pub fn run(ctx: *framework.RunCtx, parsed: ParsedArgs) !void {
|
|||
defer pf.deinit(allocator);
|
||||
const file_path = pf.path;
|
||||
|
||||
// Inline kitty charts when the terminal supports it (or `--chart
|
||||
// kitty` forces it). No braille fallback for the projection-family
|
||||
// charts - non-kitty terminals get table-only output. Shared by the
|
||||
// bands, convergence, and return-backtest modes.
|
||||
const kitty_caps: ?term_query.Caps = switch (ctx.globals.chart_config.mode) {
|
||||
.braille => null,
|
||||
.kitty => ctx.graphics_caps,
|
||||
.auto => if (ctx.graphics_caps.kitty) ctx.graphics_caps else null,
|
||||
};
|
||||
|
||||
switch (parsed) {
|
||||
.convergence => |args| try runConvergence(io, allocator, file_path, args.export_chart, color, out),
|
||||
.return_backtest => |args| try runReturnBacktest(io, allocator, file_path, args.real, args.export_chart, color, out),
|
||||
.convergence => |args| try runConvergence(io, allocator, file_path, args.export_chart, color, out, kitty_caps),
|
||||
.return_backtest => |args| try runReturnBacktest(io, allocator, file_path, args.real, args.export_chart, color, out, kitty_caps),
|
||||
.compare => |args| {
|
||||
_ = ctx.svc orelse return error.MissingDataService;
|
||||
// Pre-load today's live composition only when it's
|
||||
|
|
@ -295,14 +306,6 @@ pub fn run(ctx: *framework.RunCtx, parsed: ParsedArgs) !void {
|
|||
// total. Snapshot-only as-of paths ignore it.
|
||||
var live = try loadLiveData(ctx, today, color);
|
||||
defer if (live) |*l| l.deinit(allocator);
|
||||
// Inline kitty band chart when supported (or forced). There's
|
||||
// no braille fallback for projections - non-kitty terminals
|
||||
// keep the table-only output.
|
||||
const kitty_caps: ?term_query.Caps = switch (ctx.globals.chart_config.mode) {
|
||||
.braille => null,
|
||||
.kitty => ctx.graphics_caps,
|
||||
.auto => if (ctx.graphics_caps.kitty) ctx.graphics_caps else null,
|
||||
};
|
||||
try runBands(
|
||||
io,
|
||||
allocator,
|
||||
|
|
@ -597,6 +600,19 @@ fn prepOverlayChart(
|
|||
};
|
||||
}
|
||||
|
||||
/// Pixel + cell dimensions for an inline projection-family chart at
|
||||
/// the standard column width, derived from the terminal's cell size.
|
||||
/// Shared by the bands, convergence, and return-backtest inline-kitty
|
||||
/// paths so they all render at the same on-screen footprint.
|
||||
const ProjChartDims = struct { width: u32, height: u32, cols: u16, rows: u16 };
|
||||
|
||||
fn projectionChartDims(caps: term_query.Caps) ProjChartDims {
|
||||
const cols = term_graphics.projection_cols;
|
||||
const rows = term_graphics.rowsForWidth(cols, caps.cell_w, caps.cell_h);
|
||||
const dims = term_graphics.pixelDims(cols, rows, caps.cell_w, caps.cell_h);
|
||||
return .{ .width = dims.width, .height = dims.height, .cols = cols, .rows = rows };
|
||||
}
|
||||
|
||||
/// Render the percentile-band chart (longest horizon, with the actuals
|
||||
/// overlay when present) as kitty graphics at `term_graphics.projection_cols`
|
||||
/// wide and emit it inline. Returns `error.InsufficientData` when bands
|
||||
|
|
@ -617,14 +633,11 @@ fn emitBandsKitty(
|
|||
// overlay window) the same way the PNG-export path does.
|
||||
const oc = prepOverlayChart(va, ctx, bands_ec);
|
||||
|
||||
const cols = term_graphics.projection_cols;
|
||||
const rows = term_graphics.rowsForWidth(cols, caps.cell_w, caps.cell_h);
|
||||
const dims = term_graphics.pixelDims(cols, rows, caps.cell_w, caps.cell_h);
|
||||
|
||||
var rendered = try projection_chart.renderToSurface(io, va, oc.bands, dims.width, dims.height, theme.default_theme, oc.overlay, true, ctx.retirement.boundaryYear());
|
||||
const d = projectionChartDims(caps);
|
||||
var rendered = try projection_chart.renderToSurface(io, va, oc.bands, d.width, d.height, theme.default_theme, oc.overlay, true, ctx.retirement.boundaryYear());
|
||||
defer rendered.deinit(va);
|
||||
const rgb = try rendered.extractRgb(va);
|
||||
try term_graphics.placeInline(out, va, rgb, dims.width, dims.height, cols, rows);
|
||||
try term_graphics.placeInline(out, va, rgb, d.width, d.height, d.cols, d.rows);
|
||||
}
|
||||
|
||||
pub fn runBands(
|
||||
|
|
@ -1202,6 +1215,7 @@ pub fn runConvergence(
|
|||
export_chart: ?[]const u8,
|
||||
color: bool,
|
||||
out: *std.Io.Writer,
|
||||
kitty_caps: ?term_query.Caps,
|
||||
) !void {
|
||||
var arena_state = std.heap.ArenaAllocator.init(allocator);
|
||||
defer arena_state.deinit();
|
||||
|
|
@ -1234,6 +1248,20 @@ pub fn runConvergence(
|
|||
return;
|
||||
}
|
||||
|
||||
// Inline kitty chart above the table when supported. No braille
|
||||
// fallback - non-kitty terminals get the table only. Too few points
|
||||
// skips the chart and still renders the table below.
|
||||
if (kitty_caps) |kc| {
|
||||
const d = projectionChartDims(kc);
|
||||
if (forecast_chart.renderConvergenceChart(io, va, points, d.width, d.height, theme.default_theme)) |result| {
|
||||
try out.print("\n", .{});
|
||||
try term_graphics.placeInline(out, va, result.rgb_data, d.width, d.height, d.cols, d.rows);
|
||||
} else |err| switch (err) {
|
||||
error.InsufficientData => {},
|
||||
else => return err,
|
||||
}
|
||||
}
|
||||
|
||||
const lines = try view.convergenceLines(va, points);
|
||||
try renderForecastLines(out, color, lines);
|
||||
}
|
||||
|
|
@ -1258,6 +1286,7 @@ pub fn runReturnBacktest(
|
|||
export_chart: ?[]const u8,
|
||||
color: bool,
|
||||
out: *std.Io.Writer,
|
||||
kitty_caps: ?term_query.Caps,
|
||||
) !void {
|
||||
var arena_state = std.heap.ArenaAllocator.init(allocator);
|
||||
defer arena_state.deinit();
|
||||
|
|
@ -1298,6 +1327,19 @@ pub fn runReturnBacktest(
|
|||
return;
|
||||
}
|
||||
|
||||
// Inline kitty chart above the table when supported (see
|
||||
// runConvergence). Too few anchors skips the chart; table still renders.
|
||||
if (kitty_caps) |kc| {
|
||||
const d = projectionChartDims(kc);
|
||||
if (forecast_chart.renderBacktestChart(io, va, anchors, d.width, d.height, theme.default_theme)) |result| {
|
||||
try out.print("\n", .{});
|
||||
try term_graphics.placeInline(out, va, result.rgb_data, d.width, d.height, d.cols, d.rows);
|
||||
} else |err| switch (err) {
|
||||
error.InsufficientData => {},
|
||||
else => return err,
|
||||
}
|
||||
}
|
||||
|
||||
const lines = try view.backtestLines(va, anchors, real_mode);
|
||||
try renderForecastLines(out, color, lines);
|
||||
}
|
||||
|
|
@ -1904,6 +1946,14 @@ test "parseArgs: --overlay-actuals without --as-of is rejected" {
|
|||
try testing.expectError(error.MutuallyExclusive, parseArgsForTest(today, &args));
|
||||
}
|
||||
|
||||
test "projectionChartDims: standard column width, sane pixel/row footprint" {
|
||||
const caps = term_query.Caps{ .kitty = true, .cell_w = 10, .cell_h = 20 };
|
||||
const d = projectionChartDims(caps);
|
||||
try testing.expectEqual(term_graphics.projection_cols, d.cols);
|
||||
try testing.expect(d.rows > 0);
|
||||
try testing.expect(d.width > 0 and d.height > 0);
|
||||
}
|
||||
|
||||
const snapshot_model = @import("../models/snapshot.zig");
|
||||
const snapshot = @import("snapshot.zig");
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue