<!-- coverage-seen-elsewhere:
  markers -> markers.md
  price_lines -> price-lines.md
  marker_spec -> markers.md
  on_press -> events.md
  on_double_press -> events.md
  auto_bin -> autobin.md
  bin_width -> autobin.md
  bin_count -> autobin.md
  crosshair_mode -> styling.md
  time_visible -> time-scale.md
  watermark_text -> watermark.md
  base -> line.md
  last_value_visible -> titles-legends.md
  visible -> titles-legends.md
  price_scale_id -> price-scale.md
  price_format -> price-formats.md
  last_price_line -> price-lines.md
  base_line -> price-scale.md
  price_scale -> price-scale.md
  pane -> multi-pane.md
  by -> multi-series.md
-->

# Histogram Chart

A histogram in TVL is a column chart over a time axis: one vertical bar per time bucket, with the bar height set by a server-side aggregation of the rows in that bucket. The classic use is volume (sum the trade size per minute), but the same primitive renders any per-bucket quantity such as trade count, average price, or last seen value.

The TVL histogram does its binning server-side. Hand the chart a million-row trade table and it picks a bin width that matches the screen resolution, computes the aggregation in Deephaven (via `agg_by`), and ships only the rendered bars to the browser. Pick the aggregation with the `agg` parameter (`"sum"`, `"count"`, `"avg"`, `"last"`). The full auto-bin pipeline is documented separately in [autobin](autobin.md).

## What are histogram charts useful for?

- **Volume bars below a price chart**: Render aggregated trade size as a histogram in a second pane below the price candles.
- **Per-bucket counts**: Set `agg="count"` to plot trades-per-minute, events-per-second, or any rate quantity directly from the raw event table. The count is computed by Deephaven via `agg_by`, so the browser never sees the raw rows.
- **Tape diagnostics**: A histogram of `last` per bucket is a quick way to see whether your binning matches the data’s natural cadence.
- **Color-coded categorical bars**: With `color_column` each bar can be tinted by a per-row category (e.g. side = buy/sell), turning the histogram into a quick categorical breakdown.

## Examples

### A basic volume histogram

`tvl.data.volume()` is a 60-row daily volume table (columns `Timestamp` and `Volume`). Pass the value column explicitly because the chart defaults `value="Value"`; `timestamp` defaults to `"Timestamp"` which already matches.

```python order=histogram,data
import deephaven.plot.tradingview_lightweight as tvl
data = tvl.data.volume()

histogram = tvl.histogram(data, timestamp="Timestamp", value="Volume")
```

A single bar rises from the baseline for each row in `data`.

### Customize the bar color

`color` sets a single fill color for every bar. Color kwargs accept a Deephaven theme color (e.g. `"positive"`, `"seafoam-500"`, `"accent-300"`), a hex code (`"#26a69a"`), a named CSS color (`"teal"`), or an `rgb()`/`rgba()` string for transparency. Theme colors adapt automatically when the user switches themes; hardcoded values do not.

```python order=histogram,data
import deephaven.plot.tradingview_lightweight as tvl
data = tvl.data.volume()

histogram = tvl.histogram(
    data,
    timestamp="Timestamp",
    value="Volume",
    color="positive",
)
```

All bars are now drawn in the user’s theme “positive” color.

### Per-bar colors with `color_column`

`color_column` points at a string column in the table whose values are CSS color strings. The histogram colors each bar with the color in that row. The typical pattern is to tag each row “green” or “red” depending on whether it was a buy or a sell.

```python order=histogram,volume_colored
import deephaven.plot.tradingview_lightweight as tvl
data = tvl.data.volume()
volume_colored = data.update([
    'BarColor = (Volume > 1000) ? "positive" : "negative"',
])

histogram = tvl.histogram(
    volume_colored,
    timestamp="Timestamp",
    value="Volume",
    color_column="BarColor",
)
```

Bars above 1000 use the theme’s `positive` color, the rest use `negative`. As with the chart-level `color` argument, any Deephaven theme color, hex code, named CSS color, or `rgb()`/`rgba()` string is valid. `color` still acts as the fallback if a row has a null in `color_column`.

### Change the aggregation

`agg` selects the server-side reduction used when the histogram bins are larger than one row each. Deephaven runs an `agg_by` per bin so the browser only receives the reduced bars. The options are `"sum"` (default), `"count"`, `"avg"`, and `"last"`.

To see each `agg` produce a visibly different chart, the input table needs *many* rows per bin and *variation* within each bin. `tvl.data.stocks()` ships one trade per day across three symbols, so a two-week bin aggregates ~14 trades.

```python order=hist_sum,hist_count,hist_avg,hist_last,data
import deephaven.plot.tradingview_lightweight as tvl
data = tvl.data.stocks()

hist_sum = tvl.histogram(data, timestamp="Timestamp", value="Size", agg="sum", bin_width="P14D", auto_bin=True, title="sum (total per 14 days)")
hist_count = tvl.histogram(data, timestamp="Timestamp", value="Size", agg="count", bin_width="P14D", auto_bin=True, title="count (trades per 14 days)")
hist_avg = tvl.histogram(data, timestamp="Timestamp", value="Size", agg="avg", bin_width="P14D", auto_bin=True, title="avg (mean size per 14 days)")
hist_last = tvl.histogram(data, timestamp="Timestamp", value="Size", agg="last", bin_width="P14D", auto_bin=True, title="last (final size per 14 days)")
```

- `sum` totals every trade size in the bin: the tallest bars, scaled by ~14× the per-trade size.
- `count` is flat at ~14 because the demo trades arrive on a regular cadence.
- `avg` smooths the per-trade noise; with `sum = count × avg` and `count` ≈ constant, `avg` traces the same shape as `sum` at 1/14th the scale.
- `last` ignores the rest of the bin and shows whichever single trade happened to close it: a noisier point sample.

See [autobin](autobin.md) for the full story on when each value is the right pick.

### Set a chart title

`title` is the series legend label and shows up on hover. Use it when the chart is embedded next to other content where “Volume” alone is too generic.

```python order=histogram,data
import deephaven.plot.tradingview_lightweight as tvl
data = tvl.data.volume()

histogram = tvl.histogram(data, timestamp="Timestamp", value="Volume", title="Daily traded volume")
```

The legend now reads “Daily traded volume”.

### Continuous bars

By default (`continuous=True`) histogram bars are drawn end-to-end, with gaps between sections of dense data as appropriate. Set `continuous=False` to draw bars with a fixed width, regardless of data gaps.

```python order=histogram,data
import deephaven.plot.tradingview_lightweight as tvl
data = tvl.data.volume()

histogram = tvl.histogram(
    data,
    timestamp="Timestamp",
    value="Volume",
    continuous=False,
)
```

## Auto-binning

When the input table is larger than `AUTO_BIN_THRESHOLD` (5000 rows) the histogram automatically aggregates server-side: pick a bin width that maps cleanly to the screen pixel grid, reduce each bin with `agg`, and stream only the resulting bars to the client. You can override the heuristic with three knobs:

- `auto_bin=True`: force auto-bin on, regardless of size.
- `auto_bin=False`: force it off (raw rows; can be slow on big tables).
- `bin_width="PT1M"`: set the bin width directly with an ISO-8601 duration.
- `bin_count=500`: request a target number of bins.

These knobs and the underlying pipeline are documented in detail in [autobin](autobin.md).

## API Reference

A histogram series renders one vertical bar per time bucket with
height equal to the per-bin aggregated value.  Use the agg
parameter to select the reduction (sum / count / avg / last).

**Returns:** `TvlChart` A chart wrapping a single histogram series.

**Raises:** ValueError -- If agg is not one of the supported values.

<ParamTable param={{"module_name": "deephaven.plot.tradingview_lightweight.", "name": "histogram", "parameters": [{"name": "table", "type": "Any", "description": "Deephaven table with the data."}, {"name": "timestamp", "type": "str", "description": "Column name for the time axis."}, {"name": "value", "type": "str", "description": "Column name supplying the bar height value."}, {"name": "color", "type": "Optional[Color]", "description": "Fixed bar color (CSS color).", "default": "None"}, {"name": "base", "type": "Optional[float]", "description": "Baseline value from which bars are drawn (default 0).", "default": "None"}, {"name": "color_column", "type": "Optional[str]", "description": "Per-row bar color column.", "default": "None"}, {"name": "last_value_visible", "type": "Optional[bool]", "description": "Show the last-value badge.", "default": "None"}, {"name": "title", "type": "Optional[str]", "description": "Title shown in the series tooltip / legend.", "default": "None"}, {"name": "visible", "type": "Optional[bool]", "description": "Series visibility.", "default": "None"}, {"name": "price_scale_id", "type": "Optional[str]", "description": "Price-scale ID.", "default": "None"}, {"name": "price_format", "type": "Optional[PriceFormat]", "description": "Per-series price format.", "default": "None"}, {"name": "last_price_line", "type": "Optional[LastPriceLine]", "description": "Styling for the auto last-price horizontal rule; build with last_price_line().", "default": "None"}, {"name": "base_line", "type": "Optional[BaseLine]", "description": "Styling for the zero/index base line; build with base_line().", "default": "None"}, {"name": "price_scale", "type": "Optional[PriceScale]", "description": "Options for the price scale this series binds to; build with price_scale().", "default": "None"}, {"name": "pane", "type": "Optional[int]", "description": "Pane index.", "default": "None"}, {"name": "markers", "type": "Optional[list[Marker]]", "description": "Static markers.", "default": "None"}, {"name": "price_lines", "type": "Optional[list[PriceLine]]", "description": "Horizontal price lines.", "default": "None"}, {"name": "marker_spec", "type": "Optional[MarkerSpec]", "description": "Table-driven marker spec.", "default": "None"}, {"name": "auto_bin", "type": "Optional[bool]", "description": "Tri-state auto-bin control. None (default) auto-bins when the table exceeds 5000 rows; True forces aggregation even for small tables; False ships the raw table.", "default": "None"}, {"name": "bin_width", "type": "Optional[str]", "description": "ISO 8601 duration override (e.g. \"PT1S\", \"PT5M\", \"P1D\").  Bypasses nice-duration snapping.", "default": "None"}, {"name": "bin_count", "type": "Optional[int]", "description": "Target number of bins for the initial aggregation (default 5000).", "default": "None"}, {"name": "agg", "type": "str", "description": "Per-bin reduction for the value column.  One of \"sum\" (default), \"count\", \"avg\", \"last\".", "default": "'sum'"}, {"name": "continuous", "type": "bool", "description": "True (default) renders bars spanning their full time bin (end-to-end, no gaps between adjacent bins); False uses the built-in fixed pixel-width renderer.", "default": "True"}, {"name": "by", "type": "Optional[str]", "description": "Column name to partition the table by. When set, one runtime series is created per unique value; new partition keys discovered at runtime (ticking tables) add new series automatically.", "default": "None"}, {"name": "on_press", "type": "Optional[PressEventCallable]", "description": "Server-side callback invoked when the user presses (clicks) on the chart. Receives a TvlPressEvent dict, or no argument.", "default": "None"}, {"name": "on_double_press", "type": "Optional[PressEventCallable]", "description": "Server-side callback invoked when the user double-presses on the chart.", "default": "None"}]}} />
