---
title: "Key levels"
description: "Prior-day and prior-week highs and lows plus the running day open, drawn as horizontal levels that are calendar-correct on any chart interval."
order: 72
section: "cookbook"
---

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

# Key levels

Prior-day and prior-week highs and lows plus the running day open, drawn as horizontal levels that are calendar-correct on any chart interval.

Yesterday's high and low, last week's high and low, and today's open are the levels the market keeps reacting to, and half the annotations on any trading chart are someone maintaining them by hand. This recipe maintains them for you: five levels computed from calendar-anchored sessions, drawn as thin lines that are exactly the same on a 1-minute chart and a 4-hour chart, and that never repaint: a prior day high is always the prior completed UTC day.

## The wrun indicator

```typescript sample=cookbook-key-levels
param("show_open", 1, { min: 0, max: 1, description: "1 draws the running day open, 0 hides it" });
input("open", ohlcv.open); // the first input is the bar grid: the chart's own candles
const pdh = output("pdh", none, overlay, { description: "Prior UTC day high" });
const pdl = output("pdl", none, overlay, { description: "Prior UTC day low" });
const pwh = output("pwh", none, overlay, { description: "Prior UTC week high" });
const pwl = output("pwl", none, overlay, { description: "Prior UTC week low" });
const dayOpen = output("day_open", none, overlay, { description: "Open of the running UTC day" });
const openVisible = output("open_visible", none);
// Each bar draws its level from itself to the next bar; consecutive bars chain into one line that
// steps to the new level on the first bar of each period.
segment("pdh_line", { yFrom: pdh, yTo: pdh, from: 0, to: 1, color: "#f5a623", width: 1 });
segment("pdl_line", { yFrom: pdl, yTo: pdl, from: 0, to: 1, color: "#f5a623", width: 1 });
segment("pwh_line", { yFrom: pwh, yTo: pwh, from: 0, to: 1, color: "#4a90d9", width: 1 });
segment("pwl_line", { yFrom: pwl, yTo: pwl, from: 0, to: 1, color: "#4a90d9", width: 1 });
segment("open_line", { yFrom: dayOpen, yTo: dayOpen, from: 0, to: 1, when: openVisible, color: "#f5a623", width: 1, lineStyle: "dashed" });

let showOpen: f64 = 1.0;
let dayIdx: f64 = NaN;
let dayHigh: f64 = NaN;
let dayLow: f64 = NaN;
let runningOpen: f64 = NaN;
let priorDayHigh: f64 = NaN;
let priorDayLow: f64 = NaN;
let weekIdx: f64 = NaN;
let weekHigh: f64 = NaN;
let weekLow: f64 = NaN;
let priorWeekHigh: f64 = NaN;
let priorWeekLow: f64 = NaN;

function onStart(): void {
  showOpen = p_show_open();
}

function onBar(): void {
  const open = bar.open();
  const high = bar.high();
  const low = bar.low();
  const day = Math.floor(bar.time() / 86400.0);
  const week = Math.floor((day + 3.0) / 7.0);
  if (day != dayIdx) {
    // The day that just closed becomes the prior day. A day already running when the history
    // starts is incomplete and never becomes a level.
    if (!isNaN(dayIdx)) {
      priorDayHigh = dayHigh;
      priorDayLow = dayLow;
    }
    dayIdx = day;
    dayHigh = high;
    dayLow = low;
    runningOpen = open;
  } else {
    if (high > dayHigh) dayHigh = high;
    if (low < dayLow) dayLow = low;
  }
  if (week != weekIdx) {
    if (!isNaN(weekIdx)) {
      priorWeekHigh = weekHigh;
      priorWeekLow = weekLow;
    }
    weekIdx = week;
    weekHigh = high;
    weekLow = low;
  } else {
    if (high > weekHigh) weekHigh = high;
    if (low < weekLow) weekLow = low;
  }
  out_pdh(priorDayHigh);
  out_pdl(priorDayLow);
  out_pwh(priorWeekHigh);
  out_pwl(priorWeekLow);
  out_day_open(runningOpen);
  out_open_visible(showOpen != 0.0 ? 1.0 : 0.0);
}
```

## How it works

**The levels come from completed sessions.** The module keeps the running high and low of the current UTC day and week from the day index of `bar.time()`. On the first bar of a new day, the day that just closed becomes the prior day: `pdh` and `pdl` step to its high and low, and hold there for the whole day. Weeks work the same way. A day or week already running when the loaded history starts is incomplete and never becomes a level. This is the same no-repaint contract as the [Regime filter](regime-filter.md): a level is only ever built from bars that have closed.

**The day open is the one level that should move.** It is the open of the first bar of the running day, refreshed once at the boundary and held: a variable assigned on the boundary bar.

**Drawing is a segment per bar.** Each level is a segment from this bar to the next (`from: 0, to: 1`) with both ends on the same output. The pieces chain into one horizontal line that steps to the new level on the first bar of each period. Every level output is `none`: the numbers are computed and readable at the editor's Console prompt, but the segments are what draws, so the levels never appear twice. The `open_line` segment is gated by `open_visible`, which is the `show_open` param turned into an output: a setting becomes a gate.

**Nothing waits for the last bar.** There is no last-bar special case: the level exists on every bar it applies to, so each level has a value on every bar.

## Design notes

- The levels are per-bar segments. Offsets clamp to the loaded range, so a line ends at the newest bar, never past it. A line handle drawn under `bar.isLast()` and extended right is the other form, the one [Volume profile and value area](volume-profile-value-area.md) draws its three levels with ([Drawing objects](../presentation/drawing-objects.md)).
- The levels carry no price tags. A tag per level is a `render.label` over a string slot ([Drawing objects](../presentation/drawing-objects.md)), which switches the file to the second runtime contract; the level values themselves are readable at the editor's Console prompt.
- The show-open switch is a numeric param (`0` or `1`), and each level's color is a `color` option on its segment.
- There is no history depth to declare: state lives in a few module-level variables, however much history is loaded.

## Customize it

- **Add monthly levels.** Compute a month index from the day index and keep a third running high and low; two more outputs and two more segments (7 of the 16 allowed).
- **Prior close and midpoint.** Keep the last close of each day for a `pdc` level; `(pdh + pdl) / 2` is the day's midpoint, a favorite mean-reversion magnet.
- **Style per period.** `lineStyle: "dotted"` on the weekly segments keeps them quieter than the daily ones; `width: 2` makes a level louder.
- **Alert on a level.** The levels are data-only, so the alert dialog does not offer them as conditions. Read `bar.close()` and write a data-only gate `1` on the bar the close crosses above `pdh`, bind the gate to a `const`, and declare `alert("pdh_break", { when: ... })`: once the indicator is published and on a chart, the signal is in the chart's alert dialog ([Alerts](../functions/alerts.md)).

## Run it

1. In the editor's Explorer, press **New indicator** and pick **Blank indicator**: the tab holds the `//@lang=wrun-ts` line alone.
2. Paste the indicator block above under that line and press **Run** on any interval. The prior-day levels appear once a full UTC day has loaded, the prior-week ones once a full week has.
3. At the editor's Console prompt, type `pdh` for the prior day's high on the newest bar, or `last 5 pwh` for the prior week's high over the last five bars.

## Concepts used

- [Time and sessions](../core-concepts/time-and-sessions.md) for the day and week indexes and why the prior day is always a completed UTC day
- [Drawing objects](../presentation/drawing-objects.md) for segments with `from: 0, to: 1` as horizontal levels
- [Options on a setting](../settings/options.md) for the boolean-as-param that becomes a `when` gate
- [Core variables](../core-concepts/core-variables.md) for the running highs and lows kept in module-level variables
