---
title: "Venue share"
description: "Where BTC is trading right now. Below the candles, a donut gives each venue's share of the last 24 hours of dollar volume (this chart's venue in teal, Bybit…"
order: 109
section: "cookbook"
---

<!-- source: docs/indicators/cookbook/venue-share.md; generated by packages/cli/scripts/gen-indicator-docs.ts, do not edit -->

# Venue share

![Venue share donut and venue table under BTC candles, with a paper dial card at the top right naming the widest premium](/wrun/images/venue-share.png)

Where BTC is trading right now. Below the candles, a donut gives each venue's share of the last 24 hours of dollar volume (this chart's venue in teal, Bybit sky, OKX violet, Coinbase rose, Binance spot slate, the total in the hole). Under it, a table lists every venue with its last price, its premium to this chart's price in basis points (signed, in the chart's up colour above and down colour below, bold and tinted once it is wide), its 24h dollar volume as an inline bar and its share. A card at the top right, in the dial look (paper, a printed dial's type, the headline after a led), leads with the widest premium ("Coinbase +4.6 bp"), in slate while it is within the highlight and in the chart's up or down colour once it is wide, then the largest venue with its share and the high-low spread across the venues. The legend names the hours in use ("Venue Share 24h"). Every number is measured on the live bar; history bars only fill the volume window.

The parts are the chart's own close and volume plus four pinned closes and volumes, Bybit BTCUSDT, OKX BTC-USDT-SWAP, Coinbase BTC-USD and Binance spot BTCUSDT, each with `missing: "nan"` ([Multi-source and aggregation](../core-concepts/multi-source.md), [Data sources](../core-concepts/data-sources.md)); two frames feeding a `panel.pie` donut and a `panel.table` of styled cells ([Cards, frames and panels](../presentation/cards-frames-panels.md)); five `param.color` venue inks and a `param.text` for the chart's own row name ([Setting kinds](../settings/kinds.md)); `chart.interval_sec()` turning hours into bars ([Sessions and units](../settings/sessions-and-units.md#chart-context)); a `render.hud` card in the `dial` look ([HUD cards](../presentation/hud-and-hover-cards.md#hud-cards)); and one `draw.label` handle for the line of words on a chart that is not a BTC market ([Drawing objects](../presentation/drawing-objects.md)). This is also the `venue-share` template: the **Venue Share** card under **Beyond the time axis** in the editor's starter list, and it compiles as written.

## The wrun indicator

```typescript sample=venue-share
// Venue Share: where BTC is trading right now. The trader sees a donut below the candles with each venue's share of
// the last 24 hours of dollar volume (this chart's venue in teal, Bybit sky, OKX violet, Coinbase rose, Binance spot
// slate, the total in the hole), under it a table of venue, last price, premium to this chart's price in basis
// points (signed, up colour above, down colour below, bold and tinted past the highlight), 24h dollar volume as an
// inline bar and the share; and a card at the top right in the dial look: the widest premium as its headline
// ("Coinbase +12.3 bp"), the largest venue with its share, and the high-low spread across the venues. Every number
// is measured on the live bar; history bars only fill the volume window. On a chart that is not a BTC market the
// example writes one line of words at the top right and nothing else.

param.int("hours", 24, { min: 1, max: 168, label: "Window in hours", description: "Hours of dollar volume behind the shares (bars = hours / the chart interval, at most 10080 bars)" });
param.number("highlight_bp", 5, { min: 1, max: 50, step: 0.5, label: "Wide premium, basis points", description: "A premium at or past this many basis points reads wide: bold and tinted in the table, the card's tone" });
param.text("chart_label", "This chart", { max_bytes: 24, label: "Chart venue name", hint: "How this chart's venue is named in the donut and the table" });
section("Venue colours");
param.color("chart_ink", "#2dd4bf", { label: "This chart" });
param.color("bybit_ink", "#38bdf8", { label: "Bybit perp" });
param.color("okx_ink", "#a78bfa", { label: "OKX perp" });
param.color("coinbase_ink", "#fb7185", { label: "Coinbase" });
param.color("binance_ink", "#94a3b8", { label: "Binance spot" });
chart.interval_sec(); // the bar interval in seconds, so hours become bars without reading the bar time
legend({ title: "{{hours}}h" });

// Inputs: the chart's own candles are the grid and the reference price; four BTC venues ride beside them as pinned
// closes and volumes, NaN on a bar the venue did not report (missing: "nan"), so a silent venue contributes nothing.
input("close", ohlcv.close);
input("volume", ohlcv.volume);
input("bybit_close", ohlcv.close, { symbol: "BTCUSDT", exchange: "BYBIT", missing: "nan", description: "Bybit BTCUSDT perpetual close" });
input("bybit_volume", ohlcv.volume, { symbol: "BTCUSDT", exchange: "BYBIT", missing: "nan", description: "Bybit BTCUSDT perpetual volume, coins" });
input("okx_close", ohlcv.close, { symbol: "BTC-USDT-SWAP", exchange: "OKEX_SWAP", missing: "nan", description: "OKX BTC-USDT perpetual swap close" });
input("okx_volume", ohlcv.volume, { symbol: "BTC-USDT-SWAP", exchange: "OKEX_SWAP", missing: "nan", description: "OKX BTC-USDT perpetual swap volume, coins" });
input("coinbase_close", ohlcv.close, { symbol: "BTC-USD", exchange: "COINBASE", missing: "nan", description: "Coinbase BTC-USD spot close" });
input("coinbase_volume", ohlcv.volume, { symbol: "BTC-USD", exchange: "COINBASE", missing: "nan", description: "Coinbase BTC-USD spot volume, coins" });
input("binance_close", ohlcv.close, { symbol: "BTCUSDT", exchange: "BINANCE", missing: "nan", description: "Binance BTCUSDT spot close" });
input("binance_volume", ohlcv.volume, { symbol: "BTCUSDT", exchange: "BINANCE", missing: "nan", description: "Binance BTCUSDT spot volume, coins" });

// Outputs: data only, one value per bar, so the Console reads the card's numbers and they have history.
output("widest_bp", none, overlay, { format: "0.0", unit: " bp", description: "The widest venue premium to this chart's price, basis points, signed" });
output("spread_bp", none, overlay, { format: "0.0", unit: " bp", description: "Highest venue price minus the lowest, as basis points of this chart's price" });
output("top_share", none, overlay, { format: "%", decimals: 1, description: "The largest venue's share of the window's dollar volume, percent" });
output("premium_tone", none, overlay, { description: "The card's tone: 0 the widest premium is within the highlight (slate), 1 above and wide (up colour), 2 below and wide (down colour)" });

// The card's words, written on the live bar only.
string("headline_text", { max_bytes: 32 }); // "Coinbase +12.3 bp"
string("largest_text", { max_bytes: 32 }); // "Bybit 34.1%"
string("spread_text", { max_bytes: 16 }); // "23.4 bp"
string("notice_text", { max_bytes: 48 }); // the one line of words on a chart that is not a BTC market
string("window_text", { max_bytes: 8 }); // "24h": the card title's window, written on the live bar

// The donut and the table, written on the live bar only; the frames carry the rows and the per-run words.
const shareRows = frame("share_rows", { max_bytes: 2048 });
const venueRows = frame("venue_rows", { max_bytes: 4096 });
panel.pie({ name: "share", title: "Share of dollar volume", x: "category", place: "below", frame: shareRows, hole: 0.62, labels: false, legend_style: "title", hole_total: true, hole_caption: "24h volume", slice_gap: 2, format: "usd", chrome: "none", height_frac: 0.25 }); // the venues ride the title's chips and the table, so the ring and its hole text keep the whole pane
panel.table({
  name: "venues",
  title: "Venues",
  x: "category",
  place: "below",
  frame: venueRows,
  height_frac: 0.185,
  position: "top_center",
  font_size: 10,
  cell_padding: 3,
  valign: "middle",
  header_text_color: "theme.muted",
  grid_color: "theme.grid",
  grid_width: 1,
  grid_lines: "rows",
  border_width: 0,
  column_widths: [120, 100, 100, 150, 80],
  series: [
    { name: "Venue" },
    { name: "Last", align: "right", format: "price" },
    { name: "Premium", align: "right", format: "0.0", signed: true, unit: " bp" },
    { name: "24h volume", align: "right", format: "usd" },
    { name: "Share", align: "right", format: "%", decimals: 1 },
  ],
});

// The card, in the dial look (paper, a printed dial's type): the widest premium leads as the headline, lit in the
// chart's up or down colour once it is wide, then the largest venue and the high-low spread as two rows.
render.hud("venues_card", {
  position: "top_right",
  safe_area: true,
  look: "dial",
  title: "BTC across venues, {{window_text}}",
  columns: 1,
  width: 290,
  tiles: [
    tile.pill("Widest premium", "headline_text", { headline: true, font_size: 20, color_by: "premium_tone", colors: ["#64748b", "theme.up", "theme.down"] }),
    tile.rows("Across the venues", [["Largest venue", "largest_text"], ["High-low spread", "spread_text"]]),
  ],
});

// The one line of words on a chart that is not a BTC market: a slate label in pane pixels from the top-right corner,
// under the card.
handles.label({ anchor: "top_right", align: "right", color: "#94a3b8", size: 11 });
const notice = draw.label(0);

const VENUES = 5; // this chart, then the four pins
const MAX_BARS = 10080; // 168 hours of 1-minute bars: the ring is sized for the largest window at load
const NAMES: StaticArray<string> = ["This chart", "Bybit", "OKX", "Coinbase", "Binance spot"];
const HEX: StaticArray<string> = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"];
const usdRing = new StaticArray<f64>(VENUES * MAX_BARS); // each venue's dollar volume per bar, 0 where it did not report
const usdSum = new StaticArray<f64>(VENUES); // the window sums, recounted from the ring on the live bar
const liveClose = new StaticArray<f64>(VENUES); // this bar's close per venue
const premium = new StaticArray<f64>(VENUES); // this bar's premium to the chart, basis points
const order = new StaticArray<i32>(VENUES); // the venues sorted by dollar volume, largest first
const inks = new StaticArray<i32>(VENUES); // the venue colours, packed, read from the settings
let chartLabel: string = "This chart";
let windowBars = 96; // bars in the window, from the hours setting and the chart interval
let highlightBp = 5.0;
let head = 0; // the ring's write cursor and how many slots hold a bar
let filled = 0;

function fbColor(c: i32): void { // a packed colour as "#rrggbb" into the frame, no allocation
  const r = red(c);
  const g = green(c);
  const b = blue(c);
  fb_text('"#');
  fb_text(HEX[(r >> 4) & 15]);
  fb_text(HEX[r & 15]);
  fb_text(HEX[(g >> 4) & 15]);
  fb_text(HEX[g & 15]);
  fb_text(HEX[(b >> 4) & 15]);
  fb_text(HEX[b & 15]);
  fb_text('"');
}

function fbName(v: i32): void { // a venue's name as a JSON string
  if (v == 0) fb_str(chartLabel);
  else fb_str(NAMES[v]);
}

function usdOf(volume: f64, close: f64): f64 { // a bar's dollar volume, 0 where the venue did not report
  return isNaN(volume) || isNaN(close) || volume <= 0.0 ? 0.0 : volume * close;
}

function onStart(): void {
  const interval = p_chart_interval_sec(); // 0 when the chart did not fill it: a 15-minute bar is assumed
  const seconds = p_hours() * 3600.0;
  let bars = interval > 0.0 ? i32(Math.round(seconds / interval)) : i32(Math.round(seconds / 900.0));
  if (bars < 1) bars = 1;
  if (bars > MAX_BARS) bars = MAX_BARS;
  windowBars = bars;
  highlightBp = p_highlight_bp();
  chartLabel = pt_chart_label();
  if (chartLabel.length == 0) chartLabel = "This chart";
  inks[0] = fromPacked(p_chart_ink());
  inks[1] = fromPacked(p_bybit_ink());
  inks[2] = fromPacked(p_okx_ink());
  inks[3] = fromPacked(p_coinbase_ink());
  inks[4] = fromPacked(p_binance_ink());
}

// onBar() runs once per bar: this bar's dollar volume per venue into the ring and the premiums to the chart's close;
// the window sums and the card's numbers on every bar (data only); the words, the donut and the table on the live bar.
function onBar(): void {
  const close = bar.close();
  liveClose[0] = close;
  liveClose[1] = in_bybit_close();
  liveClose[2] = in_okx_close();
  liveClose[3] = in_coinbase_close();
  liveClose[4] = in_binance_close();
  usdRing[head] = usdOf(bar.volume(), close);
  usdRing[MAX_BARS + head] = usdOf(in_bybit_volume(), liveClose[1]);
  usdRing[2 * MAX_BARS + head] = usdOf(in_okx_volume(), liveClose[2]);
  usdRing[3 * MAX_BARS + head] = usdOf(in_coinbase_volume(), liveClose[3]);
  usdRing[4 * MAX_BARS + head] = usdOf(in_binance_volume(), liveClose[4]);
  head = (head + 1) % windowBars;
  if (filled < windowBars) filled += 1;

  // The window sums, recounted from the slots (a running sum keeps a rounding crumb after a quiet stretch).
  let total = 0.0;
  for (let v = 0; v < VENUES; v += 1) {
    let sum = 0.0;
    const base = v * MAX_BARS;
    for (let i = 0; i < filled; i += 1) sum += usdRing[base + i];
    usdSum[v] = sum;
    total += sum;
  }

  // Is this a BTC market: the chart's close against the mean of the venues that reported on this bar.
  let reference = 0.0;
  let reported = 0;
  for (let v = 1; v < VENUES; v += 1) {
    if (isNaN(liveClose[v]) || liveClose[v] <= 0.0) continue;
    reference += liveClose[v];
    reported += 1;
  }
  const isBtc = reported > 0 && !isNaN(close) && close > 0.0 && Math.abs(close / (reference / f64(reported)) - 1.0) <= 0.1;

  // The premiums, the widest one, the high-low spread and the largest venue.
  let widest = 0; // the pinned venue with the widest premium, 0 while none reported
  let widestAbs = -1.0;
  let high = close;
  let low = close;
  let largest = 0;
  for (let v = 0; v < VENUES; v += 1) {
    const venueClose = liveClose[v];
    const served = v == 0 || (!isNaN(venueClose) && venueClose > 0.0 && usdSum[v] > 0.0);
    premium[v] = v == 0 ? 0.0 : served && close > 0.0 ? (venueClose / close - 1.0) * 10000.0 : NaN;
    if (v > 0 && served) {
      if (Math.abs(premium[v]) > widestAbs) {
        widestAbs = Math.abs(premium[v]);
        widest = v;
      }
      if (venueClose > high) high = venueClose;
      if (venueClose < low) low = venueClose;
    }
    if (usdSum[v] > usdSum[largest]) largest = v;
  }
  const widestBp = widest > 0 ? premium[widest] : NaN;
  const spreadBp = close > 0.0 && high >= low ? ((high - low) / close) * 10000.0 : NaN;
  const topShare = total > 0.0 ? (usdSum[largest] / total) * 100.0 : NaN;
  const tone = isNaN(widestBp) || Math.abs(widestBp) < highlightBp ? 0.0 : widestBp > 0.0 ? 1.0 : 2.0;
  if (isBtc) {
    out_widest_bp(widestBp);
    out_spread_bp(spreadBp);
    out_top_share(topShare);
    out_premium_tone(tone);
  }
  if (!bar.isLast()) return;
  const hours = p_hours();
  sb_clear();
  sb_f64(hours, 0);
  sb_text("h");
  str_window_text_sb(); // the card's title reads the window, so it follows the hours setting

  if (!isBtc) { // not a BTC market: the card states it in the quiet tone, the one line of words sits under it, nothing else draws
    out_premium_tone(0.0);
    sb_clear();
    sb_text("Not a BTC market");
    str_headline_text_sb();
    sb_clear();
    sb_text("n/a");
    str_largest_text_sb();
    sb_clear();
    sb_text("n/a");
    str_spread_text_sb();
    sb_clear();
    sb_text("Venue share is written for BTC markets");
    str_notice_text_sb();
    notice.set(16, 210).text(str_notice_text_sb);
    return;
  }

  // The card's words.
  sb_clear();
  if (widest > 0) {
    sb_text(NAMES[widest]);
    sb_text(widestBp >= 0.0 ? " +" : " ");
    sb_f64(widestBp, 1);
    sb_text(" bp");
  } else sb_text("No venue reported");
  str_headline_text_sb();
  sb_clear();
  if (largest == 0) sb_text(chartLabel);
  else sb_text(NAMES[largest]);
  if (!isNaN(topShare)) {
    sb_text(" ");
    sb_f64(topShare, 1);
    sb_text("%");
  }
  str_largest_text_sb();
  sb_clear();
  if (isNaN(spreadBp)) sb_text("n/a");
  else {
    sb_f64(spreadBp, 1);
    sb_text(" bp");
  }
  str_spread_text_sb();

  // The venues sorted by dollar volume, largest first (insertion sort over five rows).
  for (let v = 0; v < VENUES; v += 1) order[v] = v;
  for (let i = 1; i < VENUES; i += 1) {
    const key = order[i];
    let j = i - 1;
    while (j >= 0 && usdSum[order[j]] < usdSum[key]) {
      order[j + 1] = order[j];
      j -= 1;
    }
    order[j + 1] = key;
  }

  // The donut: one slice per venue that traded in the window, in its own colour, big and small slices interleaved
  // (largest, smallest, second largest, ...) so two thin labels never sit side by side; the hole prints the total.
  let served = 0;
  for (let k = 0; k < VENUES; k += 1) if (usdSum[order[k]] > 0.0) served += 1;
  fb_clear();
  fb_text('{"hole_caption":"');
  fb_f64(hours, 0);
  fb_text('h volume","rows":[');
  let slices = 0;
  for (let k = 0; k < served; k += 1) {
    const v = order[(k & 1) == 0 ? k >> 1 : served - 1 - (k >> 1)];
    if (slices > 0) fb_text(",");
    fb_text("[");
    fbName(v);
    fb_text(",");
    fb_f64(usdSum[v], 0);
    fb_text(",");
    fbColor(inks[v]);
    fb_text("]");
    slices += 1;
  }
  fb_text("]}");
  writeFrameBuffer(FRAME_SHARE_ROWS);

  // The table: venue (in its donut colour), last, premium (signed, coloured by sign, bold and tinted when wide),
  // dollar volume with an inline bar against the largest venue, share; a venue with no volume in the window reads
  // "not served" and leaves the donut.
  const biggest = usdSum[largest];
  fb_clear();
  fb_text('{"title":"Venues, last ');
  fb_f64(hours, 0);
  fb_text('h","caption":"');
  fb_int(filled);
  fb_text("/");
  fb_int(windowBars);
  fb_text(' bars loaded","rows":[');
  for (let k = 0; k < VENUES; k += 1) {
    const v = order[k];
    if (k > 0) fb_text(",");
    fb_text('[{"text":');
    fbName(v);
    fb_text(',"text_color":');
    fbColor(inks[v]);
    fb_text(',"font_weight":"bold"}');
    if (usdSum[v] <= 0.0 || isNaN(liveClose[v])) { // the lane served nothing in the window
      fb_text(',{"text":"not served","text_color":"theme.muted"},"","",""]');
      continue;
    }
    fb_text(",");
    fb_num(liveClose[v]);
    if (v == 0) fb_text(',{"text":"reference","text_color":"theme.muted"}');
    else {
      const bp = premium[v];
      const wide = Math.abs(bp) >= highlightBp;
      fb_text(',{"value":');
      fb_f64(bp, 1);
      if (Math.abs(bp) < 0.05) fb_text(',"text_color":"theme.muted"');
      else {
        fb_text(bp > 0.0 ? ',"text_color":"theme.up"' : ',"text_color":"theme.down"');
        if (wide) fb_text(bp > 0.0 ? ',"background_color":"theme.up","opacity":0.16,"font_weight":"bold"' : ',"background_color":"theme.down","opacity":0.16,"font_weight":"bold"');
      }
      fb_text("}");
    }
    fb_text(',{"value":');
    fb_f64(usdSum[v], 0);
    fb_text(',"bar":');
    fb_f64(biggest > 0.0 ? usdSum[v] / biggest : 0.0, 3);
    fb_text(',"color":');
    fbColor(inks[v]);
    fb_text("},");
    fb_f64(total > 0.0 ? (usdSum[v] / total) * 100.0 : 0.0, 1);
    fb_text("]");
  }
  fb_text("]}");
  writeFrameBuffer(FRAME_VENUE_ROWS);
}
```

## How it works

**Four venues ride the chart's grid.** `input("close", ohlcv.close)` and `input("volume", ohlcv.volume)` are the chart's own candles, the grid and the reference price. The four pins (`bybit_close` and `bybit_volume` on `BYBIT` `BTCUSDT`, `okx_close` and `okx_volume` on `OKEX_SWAP` `BTC-USDT-SWAP`, `coinbase_close` and `coinbase_volume` on `COINBASE` `BTC-USD`, `binance_close` and `binance_volume` on `BINANCE` `BTCUSDT`) fetch each venue's candles at the chart's interval and join them to the chart's bars by timestamp; each declares `missing: "nan"`, so a bar the venue did not report reads NaN and contributes nothing. Volumes are in coins on every venue, so a bar's dollar volume is its volume times its close (`usdOf`), 0 where the venue did not report. Ten inputs in all; `bar.time()` would take an input slot of its own, so hours become bars through the chart interval instead.

**Hours become bars through the chart interval.** `chart.interval_sec()` declares a hidden setting the chart fills before `onStart()` (900 on a 15m chart), and `onStart()` divides `hours` (24) in seconds by it to size the window: 96 bars on a 15m chart, at most 10080 (168 hours of one-minute bars, the ring's size), and a 15-minute bar is assumed when the interval reads 0. `usdRing` holds every venue's dollar volume per bar; on every bar the five window sums are recounted from the slots (a running sum would keep a rounding crumb after a quiet stretch) and the total is their sum. `legend({ title: "{{hours}}h" })` prints the window after the name.

**The premiums are measured on the live bar.** A pinned venue is served on a bar while it has a close on that bar and dollar volume in the window; its premium is its close over the chart's close, minus one, in basis points, and the chart's own row is the reference. `widest_bp` is the served premium of the largest size, signed; `spread_bp` the highest served close minus the lowest, as basis points of the chart's close; `top_share` the largest venue's share of the window's dollar volume; `premium_tone` 0 while the widest premium is within `highlight_bp` (5), 1 above and wide, 2 below and wide. The four are data-only outputs written on every bar, so they have history and the Console reads them. Whether this is a BTC market is decided on the bar too: the chart's close against the mean of the venue closes that reported, within 10 percent; past it nothing is written.

**Two frames feed the donut and the table.** On the live bar the venues are sorted by dollar volume, largest first, and `share_rows` is built through the frame buffer (`fb_clear`, `fb_text`, `fb_str`, `fb_f64`, `writeFrameBuffer`) as one `[name, value, color]` slice per venue that traded in the window, interleaved (largest, smallest, second largest, ...) so two thin labels never sit side by side; the frame's `hole_caption` names the hours ("24h volume") and `hole_total: true` prints the total in the hole. `labels: false` keeps the slice names off the ring and `legend_style: "title"` puts them in chips on the pane's title row, one per slice in its colour, so at `height_frac: 0.25` the ring and the hole's total and caption keep the whole pane (the table carries every share). `venue_rows` carries five rows of styled cells for `panel.table`: the name bold in its venue colour; the last price in the chart's own digits (`format: "price"`); the premium as a `value` printed through the column's `format: "0.0"`, `signed: true` and `unit: " bp"`, in `theme.up` or `theme.down` by sign (`theme.muted` within 0.05 bp), with a `background_color` tint at `opacity` 0.16 and `font_weight` bold once it is at or past the highlight; the dollar volume as a `value` with a `bar` against the largest venue in the venue's colour; and the share to one decimal. The frame's `title` names the hours ("Venues, last 24h") and its `caption` the bars loaded ("96/96 bars loaded"). The two panes take 0.165 and 0.185 of the chart's height, the table placed `top_center`.

**The venue colours are settings.** A panel's frame colours take no `"@name"` binding, so the five inks are `param.color` settings under the **Venue colours** section, read once in `onStart()` through `fromPacked(p_chart_ink())` and its four twins and written into both frames as `"#rrggbb"` by `fbColor` (the `red`, `green` and `blue` channel readers over a 16-entry hex table, no allocation); the Style page shows them as ordinary colour pickers. The chart's own row is "This chart" unless `chart_label`, a `param.text` read with `pt_chart_label()`, names it.

**The card reads slots and a ladder.** `render.hud("venues_card", { position: "top_right", safe_area: true, look: "dial", title: "BTC across venues, {{window_text}}", columns: 1, width: 290, tiles: [...] })` is one card of two tiles, its title reading the `window_text` slot ("24h", written from `hours` on the live bar), so the title follows the window: a headline `tile.pill` at `font_size` 20 reading the `headline_text` slot ("Coinbase +4.6 bp", or "No venue reported" while no pin is served), coloured by the `premium_tone` ladder (`["#64748b", "theme.up", "theme.down"]`: slate within the highlight, the chart's up or down colour past it, so the tone reads on the dial's paper and on a black look alike), and a `tile.rows` of `largest_text` ("This chart 49.1%") and `spread_text` ("4.6 bp"). The slots are written on the live bar only. The dial look sets the paper (`#EBE8E1`), the ink and the led the pill draws as, and `safe_area: true` keeps the card under the pane action bar and clear of the price-axis tags. On a chart that is not a BTC market the card still draws (a HUD card has no hide): it reads "Not a BTC market" in the quiet tone with "n/a" rows, and the `draw.label` handle declared with `handles.label({ anchor: "top_right", align: "right", color: "#94a3b8", size: 11 })` prints the one line of words in pane pixels under it.

## Where it runs

BTC charts on any venue: BTCUSDT on Binance Futures at 15m in the picture, where the chart's own candles are half of the five-venue dollar volume, and a 1h chart of the same market reads the same total and shares (the window is 24 hours on both; the premiums move a few tenths of a basis point between the two as the venues' last prices tick). A chart that is itself one of the four pinned venues (Bybit BTCUSDT) lists that venue twice, as "This chart" and as its pin. On a chart finer than one minute the window clamps at 10080 bars: the caption counts the clamped bars while the card's title still names the hours. On any other market (ETH, a stock, FX) the run still mounts: no output is written, the card states "Not a BTC market" with "n/a" rows, and one line of words sits under it.

## When data is missing

On a chart that is not a BTC market the chart prints one sentence at the top right, "Venue share is written for BTC markets", and the card reads "Not a BTC market" with "n/a" in both rows. A bar a venue did not report reads NaN (`missing: "nan"`) and adds nothing to that venue's window; a venue with no dollar volume in the window and no live close keeps its row, reads "not served" in the Last column with the rest blank, leaves the donut and is left out of the widest premium and the spread, and while no pin is served on the live bar the headline reads "No venue reported". While the loaded history is shorter than the window the shares are measured over the bars loaded and the caption says so ("48/96 bars loaded"). The four pins as written are served; a pin changed to a market the chart cannot load refuses the whole run by name before any bar, in a toast and on the overlay's **Could not load** chip ([Exchange and symbol format](../reference/symbol-format.md)).

## Customize it

- **More or fewer hours.** `hours` (24; 1 to 168) sets the window behind the shares and the table; bars are hours over the chart interval, at most 10080, and the legend, the card's title, the hole's caption and the table's title follow it.
- **A tighter highlight.** `highlight_bp` (5; 1 to 50 in steps of 0.5): a premium at or past it reads bold and tinted in the table, and the card's headline takes the chart's up or down colour.
- **Name and colour the venues.** `chart_label` ("This chart", up to 24 bytes) names the chart's own row in the donut and the table ("Binance perp" on a Binance perp chart); the five inks under **Venue colours** are colour pickers on the Style page, and each reruns the module.
- **Another venue.** Swap one pin's `exchange` and `symbol` for another BTC market the chart serves, such as `BYBIT_SPOT` and `BTCUSDT` ([Exchange and symbol format](../reference/symbol-format.md)), rename it in `NAMES`, and Run again; the premiums and the shares follow.
- **Change the look.** The card is written in the `dial` look; the Look row on the indicator's Style page switches it without code ([The Style page](../settings/style-page.md#the-look-row)), and `terminal` (black, mono upper-case labels, an amber title bar) is the second look this example was shot in ([Looks](../presentation/hud-and-hover-cards.md#looks)).

## Run it

1. In the editor's Explorer, press the **Templates** icon ("Browse starter templates") and pick **Venue Share** under **Beyond the time axis**.
2. Press **Run** on a BTC chart, such as BTCUSDT on Binance Futures at 15m: the donut and the table fill two panes below the candles, the card appears at the top right and the legend reads the hours ("Venue Share 24h").
3. At the editor's Console prompt, type `last 20 widest_bp` to read the widest venue premium, in basis points, on the last 20 bars.

## Concepts used

- [Multi-source and aggregation](../core-concepts/multi-source.md) for a pinned `ohlcv` input on another market, and [Data sources](../core-concepts/data-sources.md) for `missing: "nan"`
- [Cards, frames and panels](../presentation/cards-frames-panels.md) for frames, the `fb_*` builders, `panel.pie` and the styled cells of `panel.table`
- [HUD cards](../presentation/hud-and-hover-cards.md#hud-cards) for `render.hud`, `safe_area` and `width`, [Looks](../presentation/hud-and-hover-cards.md#looks) for `dial` and `terminal`, and [Blocks and tiles](../presentation/hud-and-hover-cards.md#blocks-and-tiles) for the headline `tile.pill` and `tile.rows`
- [Setting kinds](../settings/kinds.md) for `param.color` and `param.text` and their readers, and [The Style page](../settings/style-page.md#the-look-row) for the Look row and the colour pickers
- [Sessions and units](../settings/sessions-and-units.md#chart-context) for `chart.interval_sec()`
- [Colors](../functions/colors-kit.md) for `fromPacked` and the `red`, `green` and `blue` channel readers
- [Drawing objects](../presentation/drawing-objects.md) for `handles.label` with an `anchor` and the `draw.label` handle
- [Exchange and symbol format](../reference/symbol-format.md) for the venue ids and a pin refused by name
