---
title: "Market share"
description: "Where the US listed options market trades its contracts, exchange by exchange. A donut under the chart cuts the market's contracts of today, or of the newest…"
order: 146
section: "cookbook"
---

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

# Market share

Where the US listed options market trades its contracts, exchange by exchange. A donut under the chart cuts the market's contracts of today, or of the newest sessions, into each exchange's share, the part no picked exchange accounts for as Other and the total in the hole. A table beside it lists each exchange with its exchange id, its call and put contracts, the contracts measured and its share, largest first, then Other and every venue together. Calls or puts narrows the measure to one side. A card at the top right names the leader with its share, the next two, how much of the market the picked exchanges account for and the bars left out.

The parts are scalar `options_flow` inputs: the market scope's call and put contracts as the whole, and each exchange's under `scope: "venue"` named by its exchange id, behind the options of a multi setting through `when` and `when_bit` ([Data sources](../core-concepts/data-sources.md#us-listed-options-by-the-charts-underlying)); the bar's trade date from the `time` source ([Data sources](../core-concepts/data-sources.md#the-time-source)); a `panel.pie` and a `panel.table` over frames written on the live bar ([Frames and panels](../presentation/cards-frames-panels.md)); and a `render.hud` card in the terminal look over data-only outputs and text slots ([HUD cards](../presentation/hud-and-hover-cards.md#hud-cards)). This is also the `market-share` template: the **Market Share** card under **Beyond the time axis** in the editor's starter list, and it compiles as written.

## The wrun indicator

```typescript sample=market-share
// Market Share: where the US listed options market trades its contracts, exchange by exchange. A donut under the chart
// cuts the market's contracts of today, or of the newest sessions, into each exchange's share, the part no picked
// exchange accounts for as Other, the total in the hole; a table beside it lists each exchange with its exchange id,
// its call and put contracts, the contracts measured and its share, largest first, then Other and every venue
// together. Calls or puts narrows the measure to one side. A card at the top right names the leader with its share,
// the next two, how much of the market the picked exchanges account for and the bars left out. Each exchange is one
// read (options_flow's venue scope, named by its exchange id), fetched only while it is picked in Venues; the whole
// is the market scope's own total. Every exchange read is one data slot of the chart's budget on a copy run from the
// editor (the market and nine exchanges are ten, a Plus chart's whole budget; OpenMarket's own package charges one), so
// the nine exchanges that trade the most contracts are read by name, two fields each, the five largest picked by
// default and the other four in Venues, and the rest (Cboe BZX, Nasdaq MRX, MIAX Sapphire and Emerald, MEMX, Nasdaq
// GEMX and BX, Cboe C2, MIAX Pearl and any newer venue) read as Other. A bar counts only when the market and at least
// one exchange both served a row, so the shares always divide like by like; a bar with one and not the other is left
// out and counted. Venue rows are kept 7 days at
// 1 minute and daily for good (no 5-minute tier), the market's 7 days at 1 minute, 30 at 5 minutes and daily for
// good, so on an intraday chart older bars carry no venue row.

section("Share");
param.choice("types", ["calls and puts", "calls", "puts"], "calls and puts", { label: "Calls or puts", description: "The contracts the shares are of: both sides, the calls or the puts" });
param.int("sessions", 1, { min: 1, max: 30, label: "Sessions", description: "The newest trade dates the shares cover, today included" });
param.multi("venues", ["Cboe Options", "NYSE Arca", "NYSE American", "Nasdaq PHLX", "Nasdaq Options", "MIAX Options", "Cboe EDGX", "Nasdaq ISE", "BOX"], ["Cboe Options", "NYSE Arca", "NYSE American", "Nasdaq PHLX", "Nasdaq Options"], { label: "Venues", description: "The exchanges read, one read each and fetched only while picked (each read one data slot on a copy run from the editor); what the market traded elsewhere reads as Other" });
input("close", ohlcv.close); // the chart's own candles: the bar grid (the shares read the whole market on any US stock chart)
input("trade_date", time.trade_date); // the bar's trade date, the New York date on a US stock: the sessions the window counts
// The whole: the market scope's call and put contracts. Then each exchange's under the venue scope, named by its id
// and read only while picked (when_bit is its place in Venues).
input("mkt_calls", options_flow.call_contracts_total, { scope: "market", missing: "nan", description: "Every venue: call contracts in the bar" });
input("mkt_puts", options_flow.put_contracts_total, { scope: "market", missing: "nan", description: "Every venue: put contracts in the bar" });
input("xcbo_calls", options_flow.call_contracts_total, { scope: "venue", venue: "XCBO", when: "venues", when_bit: 0, missing: "nan", description: "Cboe Options: call contracts in the bar" });
input("xcbo_puts", options_flow.put_contracts_total, { scope: "venue", venue: "XCBO", when: "venues", when_bit: 0, missing: "nan", description: "Cboe Options: put contracts in the bar" });
input("arco_calls", options_flow.call_contracts_total, { scope: "venue", venue: "ARCO", when: "venues", when_bit: 1, missing: "nan", description: "NYSE Arca: call contracts in the bar" });
input("arco_puts", options_flow.put_contracts_total, { scope: "venue", venue: "ARCO", when: "venues", when_bit: 1, missing: "nan", description: "NYSE Arca: put contracts in the bar" });
input("amxo_calls", options_flow.call_contracts_total, { scope: "venue", venue: "AMXO", when: "venues", when_bit: 2, missing: "nan", description: "NYSE American: call contracts in the bar" });
input("amxo_puts", options_flow.put_contracts_total, { scope: "venue", venue: "AMXO", when: "venues", when_bit: 2, missing: "nan", description: "NYSE American: put contracts in the bar" });
input("xphl_calls", options_flow.call_contracts_total, { scope: "venue", venue: "XPHL", when: "venues", when_bit: 3, missing: "nan", description: "Nasdaq PHLX: call contracts in the bar" });
input("xphl_puts", options_flow.put_contracts_total, { scope: "venue", venue: "XPHL", when: "venues", when_bit: 3, missing: "nan", description: "Nasdaq PHLX: put contracts in the bar" });
input("xndq_calls", options_flow.call_contracts_total, { scope: "venue", venue: "XNDQ", when: "venues", when_bit: 4, missing: "nan", description: "Nasdaq Options: call contracts in the bar" });
input("xndq_puts", options_flow.put_contracts_total, { scope: "venue", venue: "XNDQ", when: "venues", when_bit: 4, missing: "nan", description: "Nasdaq Options: put contracts in the bar" });
input("xmio_calls", options_flow.call_contracts_total, { scope: "venue", venue: "XMIO", when: "venues", when_bit: 5, missing: "nan", description: "MIAX Options: call contracts in the bar" });
input("xmio_puts", options_flow.put_contracts_total, { scope: "venue", venue: "XMIO", when: "venues", when_bit: 5, missing: "nan", description: "MIAX Options: put contracts in the bar" });
input("edgo_calls", options_flow.call_contracts_total, { scope: "venue", venue: "EDGO", when: "venues", when_bit: 6, missing: "nan", description: "Cboe EDGX: call contracts in the bar" });
input("edgo_puts", options_flow.put_contracts_total, { scope: "venue", venue: "EDGO", when: "venues", when_bit: 6, missing: "nan", description: "Cboe EDGX: put contracts in the bar" });
input("xisx_calls", options_flow.call_contracts_total, { scope: "venue", venue: "XISX", when: "venues", when_bit: 7, missing: "nan", description: "Nasdaq ISE: call contracts in the bar" });
input("xisx_puts", options_flow.put_contracts_total, { scope: "venue", venue: "XISX", when: "venues", when_bit: 7, missing: "nan", description: "Nasdaq ISE: put contracts in the bar" });
input("xbox_calls", options_flow.call_contracts_total, { scope: "venue", venue: "XBOX", when: "venues", when_bit: 8, missing: "nan", description: "BOX: call contracts in the bar" });
input("xbox_puts", options_flow.put_contracts_total, { scope: "venue", venue: "XBOX", when: "venues", when_bit: 8, missing: "nan", description: "BOX: put contracts in the bar" });
// The readings, data-only, one value per bar over the window ending at that bar: the card reads them and the Console
// can too.
output("market_total", none, overlay, { format: "si", description: "The market's contracts over the window's counted bars (the calls, the puts or both, as the setting picks)" });
output("venues_total", none, overlay, { format: "si", description: "The picked exchanges' contracts over the same bars" });
output("coverage", none, overlay, { format: "%", decimals: 1, description: "The picked exchanges' contracts over the market's, percent: below 100 the rest is Other" });
output("top_share", none, overlay, { format: "%", decimals: 1, description: "The leading exchange's share of the market's contracts, percent" });
output("other_share", none, overlay, { format: "%", decimals: 1, description: "The market's contracts no picked exchange accounts for, percent" });
output("counted_bars", none, overlay, { description: "Bars in the window where the market and an exchange both served a row" });
output("skipped_bars", none, overlay, { description: "Bars in the window left out: the market or the exchanges served a row, not both" });
string("lead_text", { max_bytes: 48 }); // the card's headline: the leader and its share, or why there is none
string("second_text", { max_bytes: 32 }); // the next two
string("third_text", { max_bytes: 32 });
string("title", { max_bytes: 48 }); // the card's title: the side and the window

// The donut and the table, written on the live bar only; the frames carry the rows and the per-run words.
const share_rows = frame("share_rows", { max_bytes: 2048 });
const venue_rows = frame("venue_rows", { max_bytes: 4096 });
panel.pie({ name: "share", title: "Market share", x: "category", place: "below", frame: share_rows, hole: 0.6, labels: false, legend_style: "none", hole_total: true, hole_caption: "contracts today", slice_gap: 1, format: "si", chrome: "none", height_frac: 0.25 }); // the exchanges ride the table, in the same colours
panel.table({
  name: "venues",
  title: "By exchange",
  x: "category",
  place: "below",
  frame: venue_rows,
  height_frac: 0.25,
  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,
  series: [
    { name: "Exchange" },
    { name: "Id" },
    { name: "Calls", align: "right", format: "si" },
    { name: "Puts", align: "right", format: "si" },
    { name: "Contracts", align: "right", format: "si" },
    { name: "Share", align: "right", format: "%", decimals: 1 },
  ],
});

// The card, in the terminal look: the leader and its share, the next two, the picked exchanges' coverage of the
// market and the bars left out.
render.hud("share_card", {
  position: "top_right",
  safe_area: true,
  look: "terminal",
  title: "{{title}}",
  columns: 1,
  tiles: [
    tile.pill("Leader", "lead_text", { headline: true }),
    tile.rows([
      ["Second", "second_text"],
      ["Third", "third_text"],
      ["Picked exchanges", "coverage", "%"],
      ["Bars left out", "skipped_bars", "int"],
    ]),
  ],
});

const VENUES = 9; // the options of Venues, in its order
const ENTITIES = 10; // the market, then each exchange
const STORE = 3; // per entity: call contracts, put contracts, bars with a row
const MAX_SESSIONS = 30; // the sessions setting's largest value
const NAMES: StaticArray<string> = ["Cboe Options", "NYSE Arca", "NYSE American", "Nasdaq PHLX", "Nasdaq Options", "MIAX Options", "Cboe EDGX", "Nasdaq ISE", "BOX"];
const IDS: StaticArray<string> = ["XCBO", "ARCO", "AMXO", "XPHL", "XNDQ", "XMIO", "EDGO", "XISX", "XBOX"];
const INKS: StaticArray<string> = ["#1d4ed8", "#d97706", "#fbbf24", "#0f766e", "#14b8a6", "#6d28d9", "#60a5fa", "#2dd4bf", "#e11d48"];
const OTHER_INK = "#64748b";
const SIDES: StaticArray<string> = ["contracts", "call contracts", "put contracts"];
const ring = new StaticArray<f64>(MAX_SESSIONS * ENTITIES * STORE); // the finished sessions' sums, newest at head - 1
const ringBars = new StaticArray<i32>(MAX_SESSIONS); // each finished session's counted bars
const ringSkipped = new StaticArray<i32>(MAX_SESSIONS); // and its bars left out
const today = new StaticArray<f64>(ENTITIES * STORE); // today's sums
const past = new StaticArray<f64>(ENTITIES * STORE); // the window's finished sessions, summed at each new trade date
const win = new StaticArray<f64>(ENTITIES * STORE); // the window: past plus today
const got = new StaticArray<f64>(2); // this bar's call and put contracts of one entity
const barRows = new StaticArray<f64>(ENTITIES * STORE); // this bar's rows, entity by entity
const shown = new StaticArray<f64>(VENUES); // the window's measure per picked exchange
const order = new StaticArray<i32>(VENUES); // the picked exchanges sorted by the measure, largest first
const itemKey = new StaticArray<i32>(VENUES); // each item's exchange index

let types = 0; // settings, read in onStart()
let sessions = 1;
let venues = 0.0;
let day: f64 = NaN; // today's trade date
let head = 0; // the ring's next slot, and how many slots hold a session
let stored = 0;
let todayBars = 0; // today's counted bars and bars left out
let todaySkipped = 0;
let pastBars = 0; // the same over the window's finished sessions
let pastSkipped = 0;
let runBars = 0; // counted bars over the whole run
let items = 0; // how many exchanges the donut and the table list: the picked ones

function onStart(): void {
  types = i32(p_types());
  sessions = i32(p_sessions());
  if (sessions < 1) sessions = 1;
  if (sessions > MAX_SESSIONS) sessions = MAX_SESSIONS;
  venues = p_venues();
}

// This bar's call and put contracts of entity e into got: the market (0), then exchange e - 1.
function readEntity(e: i32): void {
  if (e == 0) { got[0] = in_mkt_calls(); got[1] = in_mkt_puts(); }
  else if (e == 1) { got[0] = in_xcbo_calls(); got[1] = in_xcbo_puts(); }
  else if (e == 2) { got[0] = in_arco_calls(); got[1] = in_arco_puts(); }
  else if (e == 3) { got[0] = in_amxo_calls(); got[1] = in_amxo_puts(); }
  else if (e == 4) { got[0] = in_xphl_calls(); got[1] = in_xphl_puts(); }
  else if (e == 5) { got[0] = in_xndq_calls(); got[1] = in_xndq_puts(); }
  else if (e == 6) { got[0] = in_xmio_calls(); got[1] = in_xmio_puts(); }
  else if (e == 7) { got[0] = in_edgo_calls(); got[1] = in_edgo_puts(); }
  else if (e == 8) { got[0] = in_xisx_calls(); got[1] = in_xisx_puts(); }
  else { got[0] = in_xbox_calls(); got[1] = in_xbox_puts(); }
}

// Whether exchange v is read: picked in Venues (a host that fetches its lane anyway is not read).
function picked(v: i32): bool {
  return multiHas(venues, v);
}

// Entity e's contracts in sums, as the setting picks: both sides, the calls or the puts.
function measureIn(sums: StaticArray<f64>, e: i32): f64 {
  const at = e * STORE;
  return types == 1 ? sums[at] : types == 2 ? sums[at + 1] : sums[at] + sums[at + 1];
}

// A new trade date: today's sums join the ring as its newest session, and the window's finished sessions (the newest
// sessions - 1 of them) are summed again.
function closeSession(): void {
  const base = head * ENTITIES * STORE;
  for (let i = 0; i < ENTITIES * STORE; i += 1) {
    ring[base + i] = today[i];
    today[i] = 0.0;
  }
  ringBars[head] = todayBars;
  ringSkipped[head] = todaySkipped;
  todayBars = 0;
  todaySkipped = 0;
  head = (head + 1) % MAX_SESSIONS;
  if (stored < MAX_SESSIONS) stored += 1;
  for (let i = 0; i < ENTITIES * STORE; i += 1) past[i] = 0.0;
  pastBars = 0;
  pastSkipped = 0;
  const take = sessions - 1 < stored ? sessions - 1 : stored;
  for (let k = 1; k <= take; k += 1) {
    const slot = (head - k + MAX_SESSIONS) % MAX_SESSIONS;
    const at = slot * ENTITIES * STORE;
    for (let i = 0; i < ENTITIES * STORE; i += 1) past[i] += ring[at + i];
    pastBars += ringBars[slot];
    pastSkipped += ringSkipped[slot];
  }
}

// The picked exchanges and their measure over the window.
function fillShown(): void {
  items = 0;
  for (let v = 0; v < VENUES; v += 1) {
    if (!picked(v)) continue;
    itemKey[items] = v;
    shown[items] = measureIn(win, v + 1);
    items += 1;
  }
}

// The items sorted by the measure, largest first (insertion sort, ties in Venues' order).
function sortItems(): void {
  for (let k = 0; k < items; k += 1) order[k] = k;
  for (let i = 1; i < items; i += 1) {
    const key = order[i];
    let j = i - 1;
    while (j >= 0 && shown[order[j]] < shown[key]) {
      order[j + 1] = order[j];
      j -= 1;
    }
    order[j + 1] = key;
  }
}

// The picked exchanges' window calls (0) or puts (1): the part of the market they account for.
function pickedSide(side: i32): f64 {
  let sum = 0.0;
  for (let v = 0; v < VENUES; v += 1) if (picked(v)) sum += win[(v + 1) * STORE + side];
  return sum;
}

// The window in words: " today" or ", 5 sessions", into the frame or the line.
function fbWindow(): void {
  if (sessions == 1) fb_text(" today");
  else {
    fb_text(", ");
    fb_int(sessions);
    fb_text(" sessions");
  }
}

function sbWindow(): void {
  if (sessions == 1) sb_text(" today");
  else {
    sb_text(", ");
    sb_int(sessions);
    sb_text(" sessions");
  }
}

// The donut and the table, on the live bar. The donut: one slice per exchange with a share, largest first, then Other;
// the table: each exchange with its id, calls, puts, the measure and its share, then Other and every venue together.
// An exchange that read no row in the window says so instead of a zero.
function writeFrames(total: f64, venuesTotal: f64, reading: bool): void {
  fb_clear();
  fb_text('{"hole_caption":"');
  fb_text(SIDES[types]);
  fbWindow();
  fb_text('","rows":[');
  let slices = 0;
  for (let j = 0; j < items; j += 1) {
    const k = order[j];
    if (!(shown[k] > 0.0)) continue;
    if (slices > 0) fb_text(",");
    fb_text("[");
    fb_str(NAMES[itemKey[k]]);
    fb_text(",");
    fb_f64(shown[k], 0);
    fb_text(",");
    fb_str(INKS[itemKey[k]]);
    fb_text("]");
    slices += 1;
  }
  if (reading && total - venuesTotal > 0.0) {
    if (slices > 0) fb_text(",");
    fb_text('["Other",');
    fb_f64(total - venuesTotal, 0);
    fb_text(",");
    fb_str(OTHER_INK);
    fb_text("]");
  }
  fb_text("]}");
  writeFrameBuffer(FRAME_SHARE_ROWS);

  fb_clear();
  fb_text('{"title":"By exchange');
  fbWindow();
  fb_text('","rows":[');
  for (let j = 0; j < items; j += 1) {
    const k = order[j];
    const at = (itemKey[k] + 1) * STORE;
    if (j > 0) fb_text(",");
    fb_text('[{"text":');
    fb_str(NAMES[itemKey[k]]);
    fb_text(',"text_color":');
    fb_str(INKS[itemKey[k]]);
    fb_text(',"font_weight":"bold"},');
    fb_str(IDS[itemKey[k]]);
    if (win[at + 2] == 0.0) {
      fb_text(',{"text":"no rows","text_color":"theme.muted"},"","",""]');
      continue;
    }
    fb_text(",");
    fb_f64(win[at], 0);
    fb_text(",");
    fb_f64(win[at + 1], 0);
    fb_text(",");
    fb_f64(shown[k], 0);
    fb_text(",");
    if (reading) fb_f64((shown[k] / total) * 100.0, 1);
    else fb_text('""');
    fb_text("]");
  }
  if (reading) {
    if (items > 0) fb_text(",");
    fb_text('[{"text":"Other","text_color":"theme.muted"},"",');
    fb_f64(Math.max(win[0] - pickedSide(0), 0.0), 0);
    fb_text(",");
    fb_f64(Math.max(win[1] - pickedSide(1), 0.0), 0);
    fb_text(",");
    fb_f64(Math.max(total - venuesTotal, 0.0), 0);
    fb_text(",");
    fb_f64((Math.max(total - venuesTotal, 0.0) / total) * 100.0, 1);
    fb_text('],[{"text":"Every venue","font_weight":"bold"},"",');
    fb_f64(win[0], 0);
    fb_text(",");
    fb_f64(win[1], 0);
    fb_text(",");
    fb_f64(total, 0);
    fb_text(",100.0]");
  }
  fb_text("]}");
  writeFrameBuffer(FRAME_VENUE_ROWS);
}

// The card's words, on the live bar: the leader, the next two and the title.
function writeWords(total: f64, reading: bool, counted: i32): void {
  sb_clear();
  if (items == 0) sb_text("Pick an exchange");
  else if (runBars == 0) sb_text("No venue rows on this chart yet");
  else if (counted == 0) sb_text("No venue rows in this window");
  else if (!reading) sb_text("Nothing traded in this window");
  else {
    sb_text(NAMES[itemKey[order[0]]]);
    sb_text(" leads: ");
    sb_f64((shown[order[0]] / total) * 100.0, 1);
    sb_text("%");
  }
  str_lead_text_sb();
  for (let place = 1; place <= 2; place += 1) {
    sb_clear();
    if (reading && items > place) {
      sb_text(NAMES[itemKey[order[place]]]);
      sb_text(" ");
      sb_f64((shown[order[place]] / total) * 100.0, 1);
      sb_text("%");
    } else sb_text("n/a");
    if (place == 1) str_second_text_sb();
    else str_third_text_sb();
  }
  sb_clear();
  sb_text("Market share, ");
  sb_text(SIDES[types]);
  sbWindow();
  str_title_sb();
}

// onBar() runs once per bar: a new trade date closes today into the ring; this bar's rows join today when the market
// and at least one picked exchange both served one (otherwise the bar is left out and counted); then the window's
// readings and, on the live bar, the donut, the table and the card.
function onBar(): void {
  const date = in_trade_date();
  if (!isNaN(date) && date != day) {
    if (!isNaN(day)) closeSession();
    day = date;
  }
  let marketServed = false;
  let venueServed = false;
  for (let e = 0; e < ENTITIES; e += 1) {
    const at = e * STORE;
    barRows[at + 2] = 0.0;
    if (e > 0 && !picked(e - 1)) continue;
    readEntity(e);
    if (isNaN(got[0]) || isNaN(got[1])) continue;
    barRows[at] = got[0];
    barRows[at + 1] = got[1];
    barRows[at + 2] = 1.0;
    if (e == 0) marketServed = true;
    else venueServed = true;
  }
  if (marketServed && venueServed) {
    for (let e = 0; e < ENTITIES; e += 1) {
      const at = e * STORE;
      if (barRows[at + 2] != 1.0) continue;
      today[at] += barRows[at];
      today[at + 1] += barRows[at + 1];
      today[at + 2] += 1.0;
    }
    todayBars += 1;
    runBars += 1;
  } else if (marketServed || venueServed) todaySkipped += 1;
  for (let i = 0; i < ENTITIES * STORE; i += 1) win[i] = past[i] + today[i];
  const counted = pastBars + todayBars;
  const total = measureIn(win, 0);
  const reading = counted > 0 && total > 0.0;
  fillShown();
  let venuesTotal = 0.0;
  let top = 0.0;
  for (let k = 0; k < items; k += 1) {
    venuesTotal += shown[k];
    if (shown[k] > top) top = shown[k];
  }
  out_market_total(counted > 0 ? total : NaN);
  out_venues_total(counted > 0 ? venuesTotal : NaN);
  out_coverage(reading ? (venuesTotal / total) * 100.0 : NaN);
  out_top_share(reading && items > 0 ? (top / total) * 100.0 : NaN);
  out_other_share(reading ? (Math.max(total - venuesTotal, 0.0) / total) * 100.0 : NaN);
  out_counted_bars(f64(counted));
  out_skipped_bars(f64(pastSkipped + todaySkipped));
  if (!bar.isLast()) return;
  sortItems();
  writeFrames(total, venuesTotal, reading);
  writeWords(total, reading, counted);
}
```

## How it works

**The whole and its parts.** The market scope's row carries every exchange's prints together; each venue row carries one exchange's (`call_contracts_total` and `put_contracts_total`, two of the five fields the venue rows serve). A share is an exchange's contracts over the market's, so the shares of the picked exchanges and Other add up to the whole.

**Nine exchanges.** Each read is one data slot of the chart's budget on a copy you run from the editor or publish yourself: one slot per distinct `options_flow` read, whether or not Venues picks it (OpenMarket's own package charges one). The market and nine exchanges are ten reads, a Plus chart's whole budget, so the package reads the nine that trade the most contracts by name: Cboe Options, NYSE Arca and American, Nasdaq PHLX and the Nasdaq Options Market, MIAX Options, Cboe EDGX, Nasdaq ISE and BOX, the five largest picked by default and the other four in Venues. The rest of the market (Cboe BZX, Nasdaq MRX, MIAX Sapphire and Emerald, MEMX, Nasdaq GEMX and BX, Cboe C2, MIAX Pearl and any newer venue) reads as Other, never as a share of the nine.

**Like by like.** A bar counts only when the market and at least one picked exchange both served a row; a bar with one and not the other is left out and counted (`skipped_bars`). Venue rows are kept 7 days at one minute and daily for good, with no five-minute tier, while the market's rows reach 30 days at five minutes: on an intraday chart the bars between 7 and 30 days back carry the market's row alone, and counting them would shrink every share. An exchange that served no row on a counted bar adds 0 there.

**The window and Venues.** Sessions is the newest trade dates the shares cover, today included; each finished session's sums are kept in a ring of 30, so the window is summed again only when a new trade date starts. `venues` is a multi setting, one option per exchange: every exchange's two inputs carry `when: "venues"` and that option's `when_bit`, so the chart fetches an exchange's rows only while it is picked, and the code reads the same bits (`multiHas`), so an exchange that is not picked is neither read nor listed, even on a host that fetched its lane anyway; its part of the market lands in Other.

## Where it runs

US stock and ETF charts, and the option contract charts of those underlyings, through OpenMarket's official package once the chart serves `options_flow`'s venue scope. The shares read the whole market whatever the chart's own underlying is. The class is upcoming: the kit declares it ahead of the chart lane, the chart names each exchange to the data plane by its exchange id (an exchange it cannot name yet is refused by name on the legend), and a copy you run from the editor follows the US options rule on [Limitations](../reference/limitations.md). On a daily chart the window reaches back to the first day the data plane kept; on an intraday chart, 7 days. An alert and a Protected indicator on OpenMarket's servers refuse the class by name.

## When data is missing

The card says why instead of going blank: "Pick an exchange" while Venues is empty, "No venue rows on this chart yet" while no bar of the run carried both rows, "No venue rows in this window" while the window has none. Bars left out prints the window's bars that carried one row and not the other. In the table an exchange without a row in the window says "no rows" instead of a zero. A refused source shows its own chip on the legend, with the reason.

## Customize it

- **A week of shares.** Set Sessions to 5: the donut and the table cover the newest five trade dates.
- **Where the calls trade.** Set Calls or puts to calls: each exchange's share of the call contracts.
- **Another exchange.** Change one exchange's `venue` id (`"MXOP"` for MEMX, `"C2OX"` for Cboe C2) with its name and colour in the lists: the reads stay at ten, and the exchange it replaces moves to Other. Each exchange you add is one more data slot on a copy run from the editor.

## Run it

1. In the editor's Explorer, press the **Templates** icon ("Browse starter templates") and pick **Market Share** under **Beyond the time axis**.
2. Press **Run** on a US stock chart: the official package draws the donut and the table under the chart and the card at the top right; a copy that cannot read US options says so on the legend.
3. At the editor's Console prompt, type `top_share`, `coverage` and `other_share` to read the window on the live bar, or `skipped_bars` to see how many bars were left out.

## Concepts used

- [Data sources](../core-concepts/data-sources.md#us-listed-options-by-the-charts-underlying) for the `options_flow` class, the venue scope and its fields
- [Data sources](../core-concepts/data-sources.md#the-time-source) for `time.trade_date`
- [Setting kinds](../settings/kinds.md) for `param.multi` and its bitmask
- [Frames and panels](../presentation/cards-frames-panels.md) for the donut, the table and their frames
- [HUD cards](../presentation/hud-and-hover-cards.md#hud-cards) for the card, its pill and rows
- [Limitations](../reference/limitations.md) for the US options rule and the upcoming OPRA sources
