---
title: "Premium tide"
description: "Which way the whole US listed options market is paying up, summed through the regular session. The net call premium is the call premium bought at the ask minus…"
order: 100
section: "cookbook"
---

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

# Premium tide

Which way the whole US listed options market is paying up, summed through the regular session. The net call premium is the call premium bought at the ask minus the call premium sold at the bid; the net put premium is the same on puts; each print counts up to 2,000,000 of premium, so one block cannot swamp the market. Both run from 09:30 New York as two lines under the chart, calls in green and puts in red, and a pane below sums the net volume: call contracts bought at the ask minus sold at the bid, minus the same on puts. The Tides setting picks what is summed: the whole market or the contracts expiring that day (0DTE), each over every strike or only the out-of-the-money ones. Pick several and each draws one line, its net call minus net put premium, in its own colour. A card at the top right reads the first picked tide in one line (calls lead the tide, puts lead the tide), then its two sums, their difference and its net volume.

The parts are scalar `options_flow` inputs under the market scope, each tide's set behind its own option 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 and session from the `time` source ([Data sources](../core-concepts/data-sources.md#the-time-source)), a histogram in a named pane, 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 `premium-tide` template: the **Premium Tide** card under **Order flow** in the editor's starter list, and it compiles as written.

## The wrun indicator

```typescript sample=premium-tide
// Premium Tide: which way the whole US listed options market is paying up, summed through the regular session. The net
// call premium is the call premium bought at the ask minus the call premium sold at the bid, and the net put premium
// the same on puts, each print's premium counted up to 2,000,000 so one block cannot swamp the market. Both run from
// 09:30 New York (each trade date starts again) as two lines under the chart, calls in green and puts in red, and a
// pane below sums the net volume: call contracts bought at the ask minus sold at the bid, minus the same on puts.
// Prints at the mid or with no quote count in neither. Tides picks what is summed: the whole market or the contracts
// expiring that day (0DTE), each over every strike or only the out-of-the-money ones, each pick one more read fetched
// only while it is picked. With one tide picked the two lines are its calls and its puts; with several, each picked
// tide draws one line, its net call minus net put premium, in its own colour, and the net volume and the card follow
// the first one picked. The rows come from options_flow's market scope, one per bar: 1-minute rows reach back 7 days,
// 5-minute rows 30 and daily rows are kept, so older intraday bars carry none. A regular-session bar without a row
// adds nothing and is counted, the sums being a floor while that count is above 0; bars outside the regular session
// draw nothing.

param.multi("tides", ["Whole market", "Whole market, out of the money", "0DTE", "0DTE, out of the money"], ["Whole market"], { label: "Tides", description: "What the tide sums; each pick is one more read, fetched only while it is picked, and with several picked each draws its own net premium line" });
input("close", ohlcv.close); // the chart's own candles: the bar grid (the tide reads 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 sums start again on each
input("session", time.session); // 1 in the regular session: the only bars the tide sums
// Six fields a tide, each set read only while its tide is picked (when_bit is the tide's place in Tides): the net call
// and net put premium with each print capped, then the call and put contracts at the ask and at the bid.
input("mkt_ncp", options_flow.net_call_prem_capped, { scope: "market", when: "tides", when_bit: 0, missing: "nan", description: "Whole market: net call premium, each print capped at 2,000,000, USD" });
input("mkt_npp", options_flow.net_put_prem_capped, { scope: "market", when: "tides", when_bit: 0, missing: "nan", description: "Whole market: net put premium, each print capped at 2,000,000, USD" });
input("mkt_ca", options_flow.call_contracts_ask, { scope: "market", when: "tides", when_bit: 0, missing: "nan", description: "Whole market: call contracts bought at the ask" });
input("mkt_cb", options_flow.call_contracts_bid, { scope: "market", when: "tides", when_bit: 0, missing: "nan", description: "Whole market: call contracts sold at the bid" });
input("mkt_pa", options_flow.put_contracts_ask, { scope: "market", when: "tides", when_bit: 0, missing: "nan", description: "Whole market: put contracts bought at the ask" });
input("mkt_pb", options_flow.put_contracts_bid, { scope: "market", when: "tides", when_bit: 0, missing: "nan", description: "Whole market: put contracts sold at the bid" });
input("otm_ncp", options_flow.net_call_prem_capped, { scope: "market", moneyness: "otm", when: "tides", when_bit: 1, missing: "nan", description: "Whole market, out of the money: net call premium, each print capped at 2,000,000, USD" });
input("otm_npp", options_flow.net_put_prem_capped, { scope: "market", moneyness: "otm", when: "tides", when_bit: 1, missing: "nan", description: "Whole market, out of the money: net put premium, each print capped at 2,000,000, USD" });
input("otm_ca", options_flow.call_contracts_ask, { scope: "market", moneyness: "otm", when: "tides", when_bit: 1, missing: "nan", description: "Whole market, out of the money: call contracts bought at the ask" });
input("otm_cb", options_flow.call_contracts_bid, { scope: "market", moneyness: "otm", when: "tides", when_bit: 1, missing: "nan", description: "Whole market, out of the money: call contracts sold at the bid" });
input("otm_pa", options_flow.put_contracts_ask, { scope: "market", moneyness: "otm", when: "tides", when_bit: 1, missing: "nan", description: "Whole market, out of the money: put contracts bought at the ask" });
input("otm_pb", options_flow.put_contracts_bid, { scope: "market", moneyness: "otm", when: "tides", when_bit: 1, missing: "nan", description: "Whole market, out of the money: put contracts sold at the bid" });
input("zd_ncp", options_flow.net_call_prem_capped, { scope: "market", dte: "0dte", when: "tides", when_bit: 2, missing: "nan", description: "0DTE: net call premium, each print capped at 2,000,000, USD" });
input("zd_npp", options_flow.net_put_prem_capped, { scope: "market", dte: "0dte", when: "tides", when_bit: 2, missing: "nan", description: "0DTE: net put premium, each print capped at 2,000,000, USD" });
input("zd_ca", options_flow.call_contracts_ask, { scope: "market", dte: "0dte", when: "tides", when_bit: 2, missing: "nan", description: "0DTE: call contracts bought at the ask" });
input("zd_cb", options_flow.call_contracts_bid, { scope: "market", dte: "0dte", when: "tides", when_bit: 2, missing: "nan", description: "0DTE: call contracts sold at the bid" });
input("zd_pa", options_flow.put_contracts_ask, { scope: "market", dte: "0dte", when: "tides", when_bit: 2, missing: "nan", description: "0DTE: put contracts bought at the ask" });
input("zd_pb", options_flow.put_contracts_bid, { scope: "market", dte: "0dte", when: "tides", when_bit: 2, missing: "nan", description: "0DTE: put contracts sold at the bid" });
input("zd_otm_ncp", options_flow.net_call_prem_capped, { scope: "market", dte: "0dte", moneyness: "otm", when: "tides", when_bit: 3, missing: "nan", description: "0DTE, out of the money: net call premium, each print capped at 2,000,000, USD" });
input("zd_otm_npp", options_flow.net_put_prem_capped, { scope: "market", dte: "0dte", moneyness: "otm", when: "tides", when_bit: 3, missing: "nan", description: "0DTE, out of the money: net put premium, each print capped at 2,000,000, USD" });
input("zd_otm_ca", options_flow.call_contracts_ask, { scope: "market", dte: "0dte", moneyness: "otm", when: "tides", when_bit: 3, missing: "nan", description: "0DTE, out of the money: call contracts bought at the ask" });
input("zd_otm_cb", options_flow.call_contracts_bid, { scope: "market", dte: "0dte", moneyness: "otm", when: "tides", when_bit: 3, missing: "nan", description: "0DTE, out of the money: call contracts sold at the bid" });
input("zd_otm_pa", options_flow.put_contracts_ask, { scope: "market", dte: "0dte", moneyness: "otm", when: "tides", when_bit: 3, missing: "nan", description: "0DTE, out of the money: put contracts bought at the ask" });
input("zd_otm_pb", options_flow.put_contracts_bid, { scope: "market", dte: "0dte", moneyness: "otm", when: "tides", when_bit: 3, missing: "nan", description: "0DTE, out of the money: put contracts sold at the bid" });
pane("volume", { title: "Net volume", height_frac: 0.16, format: "si" });
// One tide picked: its calls and its puts. Several: one net premium line per picked tide, in the tide's colour.
output("ncp", line, lower, { color: "#22c55e", width: 2, label: "Net call premium", format: "usd", description: "One tide picked: its net call premium summed since 09:30 New York, USD; NaN with several picked or outside the regular session" });
output("npp", line, lower, { color: "#ef4444", width: 2, label: "Net put premium", format: "usd", description: "One tide picked: its net put premium summed since 09:30 New York, USD" });
output("net_mkt", line, lower, { color: "#e2e8f0", width: 2, label: "Whole market", format: "usd", description: "Several tides picked: the whole market's net call minus net put premium since 09:30, USD" });
output("net_otm", line, lower, { color: "#94a3b8", width: 2, label: "Whole market, OTM", format: "usd", description: "Several tides picked: the same over the out-of-the-money contracts" });
output("net_zd", line, lower, { color: "#f59e0b", width: 2, label: "0DTE", format: "usd", description: "Several tides picked: the same over the contracts expiring that day" });
output("net_zd_otm", line, lower, { color: "#fcd34d", width: 2, label: "0DTE, OTM", format: "usd", description: "Several tides picked: the same over the 0DTE contracts out of the money" });
output("zero", line, lower, { color: "#334155", width: 1, legend: false, description: "The tide pane's zero line" }); // keeps zero inside the pane's scale
output("net_volume", histogram, lower, { pane: "volume", color_by: "volume_tone", colors: ["#ef4444", "#22c55e"], width: 0.8, label: "Net volume", format: "si", description: "The first picked tide's call contracts bought at the ask minus sold at the bid, minus the puts', summed since 09:30" });
// The readings, data-only: the card reads them and the Console can too.
output("volume_tone", none, lower, { description: "The net volume's colour: 0 under zero, 1 at or above" });
output("lead_ncp", none, lower, { format: "usd", description: "The first picked tide's net call premium this session, USD; NaN before its first row" });
output("lead_npp", none, lower, { format: "usd", description: "The first picked tide's net put premium this session, USD" });
output("net_premium", none, lower, { format: "usd", description: "The first picked tide's net call minus net put premium this session, USD: above zero the tide leans bullish" });
output("lead_volume", none, lower, { format: "si", description: "The first picked tide's net volume this session, contracts" });
output("bar_ncp", none, lower, { format: "usd", description: "This bar's net call premium of the first picked tide, USD; NaN on a bar without a row" });
output("bar_npp", none, lower, { format: "usd", description: "This bar's net put premium of the first picked tide, USD; NaN on a bar without a row" });
output("missing_bars", none, lower, { description: "Regular-session bars of this session the first picked tide carried no row for: the sums are a floor while it is above 0" });
output("tone", none, lower, { description: "The card's colour: 0 puts lead, 1 no reading or even, 2 calls lead" });
string("read", { max_bytes: 48 }); // the headline: which side leads the tide, or why there is no reading
string("title", { max_bytes: 64 }); // the card's title: the first picked tide, and how many more

// The card, in the terminal look: the first picked tide's lean leads as the headline (red puts, slate no reading,
// green calls), then its two sums, their difference, its net volume and the bars with no row.
render.hud("tide_card", {
  position: "top_right",
  safe_area: true,
  look: "terminal",
  title: "{{title}}",
  columns: 2,
  tiles: [
    tile.pill("Tide", "read", { headline: true, color_by: "tone", colors: ["#ef4444", "#94a3b8", "#22c55e"] }),
    tile.value("Net call premium", "lead_ncp", { format: "usd" }),
    tile.value("Net put premium", "lead_npp", { format: "usd" }),
    tile.value("Calls minus puts", "net_premium", { format: "usd" }),
    tile.value("Net volume", "lead_volume", { format: "si" }),
    tile.rows([["Bars with no flow row", "missing_bars", "int"]]),
  ],
});

const TIDES = 4; // the options of Tides, in its order
const FIELDS = 6; // the fields read per tide
const NAMES: StaticArray<string> = ["Whole market", "Whole market, OTM", "0DTE", "0DTE, OTM"];
const ncp = new StaticArray<f64>(TIDES); // this session's sums, per tide
const npp = new StaticArray<f64>(TIDES);
const vol = new StaticArray<f64>(TIDES);
const rows = new StaticArray<i32>(TIDES); // regular-session bars of this session that carried a row
const missing = new StaticArray<i32>(TIDES); // regular-session bars of this session without one
const runRows = new StaticArray<i32>(TIDES); // regular-session rows over the whole run
const cur = new StaticArray<f64>(FIELDS); // this bar's fields of one tide

let picks = 0.0; // the Tides bitmask, read in onStart()
let lead = -1; // the first picked tide, -1 with none
let picked = 0; // how many tides are picked
let day: f64 = NaN; // the trade date the sums run over

function onStart(): void {
  picks = p_tides();
  lead = -1;
  picked = 0;
  for (let t = 0; t < TIDES; t += 1) {
    if (!multiHas(picks, t)) continue;
    if (lead < 0) lead = t;
    picked += 1;
  }
}

// This bar's six fields of tide t into cur: the capped net call and net put premium, then the call and put contracts
// at the ask and at the bid.
function readTide(t: i32): void {
  if (t == 0) { cur[0] = in_mkt_ncp(); cur[1] = in_mkt_npp(); cur[2] = in_mkt_ca(); cur[3] = in_mkt_cb(); cur[4] = in_mkt_pa(); cur[5] = in_mkt_pb(); }
  else if (t == 1) { cur[0] = in_otm_ncp(); cur[1] = in_otm_npp(); cur[2] = in_otm_ca(); cur[3] = in_otm_cb(); cur[4] = in_otm_pa(); cur[5] = in_otm_pb(); }
  else if (t == 2) { cur[0] = in_zd_ncp(); cur[1] = in_zd_npp(); cur[2] = in_zd_ca(); cur[3] = in_zd_cb(); cur[4] = in_zd_pa(); cur[5] = in_zd_pb(); }
  else { cur[0] = in_zd_otm_ncp(); cur[1] = in_zd_otm_npp(); cur[2] = in_zd_otm_ca(); cur[3] = in_zd_otm_cb(); cur[4] = in_zd_otm_pa(); cur[5] = in_zd_otm_pb(); }
}

// Tide t's net premium line, drawn with several tides picked.
function writeNet(t: i32, value: f64): void {
  if (t == 0) out_net_mkt(value);
  else if (t == 1) out_net_otm(value);
  else if (t == 2) out_net_zd(value);
  else out_net_zd_otm(value);
}

// The card's words, on the live bar.
function writeWords(): void {
  sb_clear();
  if (lead < 0) sb_text("Pick a tide");
  else if (runRows[lead] == 0) sb_text("No options flow on this chart yet");
  else if (rows[lead] == 0) sb_text("No options flow yet this session");
  else if (ncp[lead] - npp[lead] > 0.0) sb_text("Calls lead the tide");
  else if (ncp[lead] - npp[lead] < 0.0) sb_text("Puts lead the tide");
  else sb_text("Calls and puts even");
  str_read_sb();
  sb_clear();
  sb_text("Premium tide");
  if (lead >= 0) {
    sb_text(", ");
    sb_text(NAMES[lead]);
  }
  if (picked > 1) {
    sb_text(" +");
    sb_int(picked - 1);
  }
  str_title_sb();
}

// onBar() runs once per bar: a new trade date starts every tide's sums again; on a regular-session bar each picked
// tide's row adds to its sums, or counts as missing; then the lines, the net volume, the readings and, on the live
// bar, the words. A tide that is not picked is skipped even on a host that fetches its lane anyway.
function onBar(): void {
  const date = in_trade_date();
  if (!isNaN(date) && date != day) {
    day = date;
    for (let t = 0; t < TIDES; t += 1) {
      ncp[t] = 0.0;
      npp[t] = 0.0;
      vol[t] = 0.0;
      rows[t] = 0;
      missing[t] = 0;
    }
  }
  const regular = in_session() == 1.0;
  let barNcp: f64 = NaN;
  let barNpp: f64 = NaN;
  for (let t = 0; t < TIDES; t += 1) {
    if (!multiHas(picks, t)) {
      writeNet(t, NaN);
      continue;
    }
    readTide(t);
    let served = true;
    for (let f = 0; f < FIELDS; f += 1) if (isNaN(cur[f])) served = false;
    if (regular) {
      if (served) {
        ncp[t] += cur[0];
        npp[t] += cur[1];
        vol[t] += cur[2] - cur[3] - (cur[4] - cur[5]);
        rows[t] += 1;
        runRows[t] += 1;
      } else missing[t] += 1;
    }
    if (t == lead && served) {
      barNcp = cur[0];
      barNpp = cur[1];
    }
    writeNet(t, picked > 1 && regular && rows[t] > 0 ? ncp[t] - npp[t] : NaN);
  }
  const reading = lead >= 0 && rows[lead] > 0;
  const drawn = reading && regular;
  const net = reading ? ncp[lead] - npp[lead] : NaN;
  out_ncp(drawn && picked == 1 ? ncp[lead] : NaN);
  out_npp(drawn && picked == 1 ? npp[lead] : NaN);
  out_zero(0.0);
  out_net_volume(drawn ? vol[lead] : NaN);
  out_volume_tone(drawn ? (vol[lead] >= 0.0 ? 1.0 : 0.0) : NaN);
  out_lead_ncp(reading ? ncp[lead] : NaN);
  out_lead_npp(reading ? npp[lead] : NaN);
  out_net_premium(net);
  out_lead_volume(reading ? vol[lead] : NaN);
  out_bar_ncp(barNcp);
  out_bar_npp(barNpp);
  out_missing_bars(lead >= 0 ? f64(missing[lead]) : NaN);
  out_tone(reading ? (net > 0.0 ? 2.0 : net < 0.0 ? 0.0 : 1.0) : 1.0);
  if (bar.isLast()) writeWords();
}
```

## How it works

**The tide.** Each bar's row carries the market's premium at the ask and at the bid, calls and puts apart, each print's premium capped at 2,000,000 (`net_call_prem_capped`, `net_put_prem_capped`). The net call premium rises when calls are bought at the ask and falls when they are sold at the bid; the net put premium does the same on puts. Prints at the mid or with no quote to side them count in neither. The net volume is the same reading in contracts: calls bought at the ask minus sold at the bid, minus the puts' (`call_contracts_ask` and the rest).

**The regular session.** The sums start again on each trade date (`time.trade_date`) and add only regular-session bars (`time.session` reads `1`): the tide runs from 09:30 to 16:00 New York. Index options trade before the open too, so the market serves rows on pre-market bars; the tide leaves them out and draws nothing there. On a daily chart every bar is a whole session, so each bar is that day's tide.

**Tides.** `tides` is a multi setting, one option per tide: the whole market (`scope: "market"`), its out-of-the-money contracts (`moneyness: "otm"`, a strike within 0.5% of the underlying counting as out), the contracts expiring on the print's own trade date (`dte: "0dte"`) and those out of the money. Every tide's six inputs carry `when: "tides"` and that option's `when_bit`, so the chart fetches a tide's rows only while it is picked; the code reads the same bits (`multiHas`), so a tide that is not picked draws nothing even on a host that fetched its lane anyway. One tide picked draws its calls and puts (`ncp`, `npp`); several draw one net line each (`net_mkt`, `net_otm`, `net_zd`, `net_zd_otm`) in the tide's colour, and the net volume, the card and its readings follow the first one picked.

## 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`. The tide reads the whole market whatever the chart's own underlying is, so it sits well on SPY. The class is upcoming: the kit declares it ahead of the chart lane, and a copy you run from the editor follows the US options rule on [Limitations](../reference/limitations.md). One-minute rows reach back 7 days, five-minute rows 30 days and daily rows are kept, so on an intraday chart the older sessions draw nothing; 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 a tide" while Tides is empty, "No options flow on this chart yet" while the first picked tide read no row in the run, "No options flow yet this session" while the newest session has none. Bars with no flow row prints the regular-session bars of the session the first tide carried no row for: above 0, read the sums as at least what they show. A tide that read no row draws no line. A refused source shows its own chip on the legend, with the reason.

## Customize it

- **The whole picture at once.** Pick all four tides: the market's net line, its out-of-the-money one and the same-day contracts' two, side by side.
- **Another tide.** Change one tide's knobs: `scope: "index"` with `dte: "0dte"` reads the indices' same-day contracts, `dte: "week"` the contracts expiring in one to seven days. The equity and ETF scopes read empty until the data plane stamps each underlying's kind. A sheet reads at most 32 inputs, so a fifth tide of six inputs takes the place of one of the four.
- **Uncapped premium.** Swap `net_call_prem_capped` and `net_put_prem_capped` for `net_call_prem` and `net_put_prem` to count every print's premium in full.

## Run it

1. In the editor's Explorer, press the **Templates** icon ("Browse starter templates") and pick **Premium Tide** under **Order flow**.
2. Press **Run** on a US stock chart: the official package draws the tide under the chart, the net volume below it 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 `lead_ncp`, `lead_npp` and `net_premium` to read the first tide's sums on the live bar, or `missing_bars` to see how many bars carried no row.

## Concepts used

- [Data sources](../core-concepts/data-sources.md#us-listed-options-by-the-charts-underlying) for the `options_flow` class, its fields and the `scope`, `dte` and `moneyness` knobs
- [Data sources](../core-concepts/data-sources.md#the-time-source) for `time.trade_date` and `time.session`
- [Setting kinds](../settings/kinds.md) for `param.multi` and its bitmask
- [HUD cards](../presentation/hud-and-hover-cards.md#hud-cards) and [Blocks and tiles](../presentation/hud-and-hover-cards.md#blocks-and-tiles) for the card, its pill and values
- [Limitations](../reference/limitations.md) for the US options rule and the upcoming OPRA sources
