Key levels

View as MarkdownOpen the editor

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

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);
}
BTCUSDT perpetual on Binance, 1 hour bars, Aug 10 to Aug 18, 2026Real output from OpenMarket's engine

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: 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 draws its three levels with (Drawing objects).
  • The levels carry no price tags. A tag per level is a render.label over a string slot (Drawing objects), 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).

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