Skip to main content
Render backtest data as ASCII art directly in the terminal. Works with the frozen dataclasses from the Plot Data module - no matplotlib, no GUI, no external dependencies.

Line Charts

Time-series with Bresenham line drawing

Area Charts

Filled regions for drawdowns and underwater curves

Histograms

Horizontal bar charts from HistogramData

Heatmaps

Shaded block characters for correlation and monthly returns

Scatter Plots

Buy/sell markers with triangle glyphs

Dashboard

Full PlotBundle rendered as a multi-section report

Quick Start

import horizon as hz

result = hz.backtest(strategy, ...)
bundle = hz.from_backtest(result)
print(hz.dashboard(bundle))
Output:
═══════════════════════════════════════════════════════════════════════
  EQUITY (normalized to 1.0)

    1.49 │                                                 ···
    1.39 │                                           ·····
    1.29 │                                     ·····
    1.19 │                              ······
    1.09 │                       ·····
    0.99 │·····················
         └────────────────────────────────────────────────────

═══════════════════════════════════════════════════════════════════════
  DRAWDOWN (%)

       0 │▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
    -2.5 │░░░░░░░░░░░░░░░░░░░░
         └────────────────────────────────────────────────────

Individual Charts

line_chart

print(hz.line_chart(timestamps, values, width=72, height=18, title="Price"))
ParameterTypeDefaultDescription
timestampsSequence[float]requiredX-axis values
valuesSequence[float]requiredY-axis values
widthint72Chart width in characters
heightint18Chart height in rows
titlestr""Title above chart
markerstr"·"Character for line drawing

multi_line

Overlay multiple series with different markers.
print(hz.multi_line([
    (ts, sharpe_vals, "Sharpe", "·"),
    (ts, sortino_vals, "Sortino", "×"),
], title="Rolling Metrics"))

area_chart

Filled area from the line down to the bottom axis.
neg_dd = [-d for d in bundle.underwater.drawdown_pct]
print(hz.area_chart(bundle.underwater.timestamps, neg_dd, title="Drawdown"))

bar_chart

Horizontal bars with labels.
print(hz.bar_chart(
    ["Strategy A", "Strategy B", "Strategy C"],
    [12.5, -3.2, 8.7],
    title="Returns (%)",
))

histogram_chart

Render a HistogramData from the plotting module.
h = hz.return_distribution(equity_curve)
print(hz.histogram_chart(h, title="Return Distribution"))

ascii_heatmap

Render a HeatmapData using shade characters: ░ ▒ ▓ █.
hm = hz.monthly_returns_heatmap(equity_curve)
print(hz.ascii_heatmap(hm))

ascii_scatter

Render a TradeScatterData with (buy) and (sell) markers.
ts = hz.trade_scatter(trades)
print(hz.ascii_scatter(ts))

ascii_calibration

Render a CalibrationPlotData with perfect line (·) and actual points ().
cal = hz.calibration_plot(trades, outcomes)
print(hz.ascii_calibration(cal))

sparkline

Compact single-row chart using Unicode block elements.
print(hz.sparkline(values, width=40))
# ▁▂▃▅▇█▇▅▃▂▁▂▃▅▇█▇▅▃▂▁▂▃▅▇█▇▅▃▂▁▂▃▅▇█▇▅▃▂

dashboard

Render an entire PlotBundle as a multi-section ASCII report.
bundle = hz.from_backtest(result)
print(hz.dashboard(bundle, width=72, height=16))
Includes: equity curve, drawdown, return distribution, PnL distribution, rolling metrics, monthly heatmap, trade scatter, and calibration (when available).

Use Cases

  • Claude Code / AI agents: Get visual feedback without leaving the terminal
  • CI/CD pipelines: Embed charts in build logs
  • SSH sessions: Visualize on remote machines without X forwarding
  • Logging: Append charts to strategy log files
  • Notebooks: Quick preview before rendering with matplotlib