Multi-timeframe

View as MarkdownOpen the editor

Read higher timeframes from any wrun indicator on the chart, three ways: fold the chart's own bars into buckets on time.bar_open_sec, confirming a candle only after it closes; pin a secondary input to a coarser interval, which the chart fetches as real candles and hands to each bar as of that bar's close; or read a whole stretch of a timeframe's closed candles as a candles stream, reaching back as far as you ask, whatever the chart has loaded. Rolling buckets, calendar buckets (day, Monday week, month, quarter, year), confirmed and developing values, and the offset-N form all fall out of the same integer math. Finer bars inside each chart bar come from the intrabar celled source.

Introduction

An indicator is not locked to its chart's timeframe. From a 1h chart you can read the 4h trend, pull a daily level, or reset a sum on the Monday open. Three forms cover it:

  • The bucket fold (any input): bucket bars by their open time, keep the running bucket's values, and fold a bucket into the higher-timeframe statistic only when the next bucket's first bar arrives. It works on every input, including the ones that cannot pin an interval (trades, liquidations, implied volatility).
  • The interval pin (a secondary ohlcv input, and funding or oi on the chart's own market): input("h4", ohlcv.close, { interval: "4h" }) reads real 4h candles, and the chart hands each one to a chart bar only as of that bar's close. The no-repaint discipline is built into the data path.
  • The candles stream (a celled input): input("d", candles.cells, { interval: "1d", bars: 400 }) reaches back 400 closed days. The first bar receives those that had closed by its own close, and each later bar the day that closed with it. The ./sdk/candles kit turns that stream into calendar months, quarters and years, or a list of the newest candles.

The headline property is shared by all three: none of them repaints.

The bucket fold is confirmed by default

In most charting languages a higher-timeframe lookup is a repaint trap: you ask for the 4h close on a 1h chart and, while the 4h candle is still forming, get its live, not-yet-final value. Your signal looks perfect in backtest and fires a bar early in production, because history got a value the live chart never had.

The fold closes that trap. The 4h reading on any 1h bar uses only the 4h candles that had already closed by that bar: the module remembers the last close it saw in the current bucket, and when a bar from the NEXT bucket arrives it folds that remembered close into the 4h statistic. The value you see in history is the value the module saw live. A 4h regime filter, on any chart interval:

param("ema_len", 20, { min: 2, max: 200, description: "EMA length in 4h candles" });
output("h4_close", line, overlay, { color: "#7c3aed", width: 1, description: "Confirmed 4h close" });
output("h4_ema", line, overlay, { color: "#2563eb", width: 2, description: "EMA of confirmed 4h closes: the trend baseline" });
output("regime", none, overlay, { description: "1 when the confirmed 4h close is above its 4h EMA, 0 otherwise" });
output("bull_dot", shape, overlay, { color: "#16a34a", shape_where: "is_bullish", description: "Bullish 4h regime" });
output("bear_dot", shape, overlay, { color: "#dc2626", shape_where: "is_bearish", description: "Bearish 4h regime" });
output("is_bullish", none);
output("is_bearish", none);

const H4: f64 = 14400.0; // 4h in seconds; buckets are floored from the epoch, like a rolling "4h" token
let ema = new Ema(20);
let bucket: f64 = NaN;
let bucketClose: f64 = NaN; // the running bucket's latest close
let h4Close: f64 = NaN; // the last CLOSED bucket's close
let h4Ema: f64 = NaN;

function onStart(): void {
  ema = new Ema(i32(p_ema_len()));
}

function onBar(): void {
  const close = bar.close();
  const b = Math.floor(bar.time() / H4);
  if (b != bucket) {
    // The bucket changed: what we remembered is a CLOSED 4h candle. Fold it now, never earlier.
    if (!isNaN(bucketClose)) {
      h4Close = bucketClose;
      h4Ema = ema.update(bucketClose);
    }
    bucket = b;
  }
  bucketClose = close;
  if (isNaN(h4Ema)) return;
  const regime = h4Close > h4Ema ? 1.0 : 0.0;
  out_h4_close(h4Close);
  out_h4_ema(h4Ema);
  out_regime(regime);
  out_bull_dot(bar.low());
  out_bear_dot(bar.high());
  out_is_bullish(regime == 1.0 ? 1.0 : 0.0);
  out_is_bearish(regime == 0.0 ? 1.0 : 0.0);
}
BTCUSDT perpetual on Binance, 1 hour bars, Aug 10 to Aug 18, 2026Real output from OpenMarket's engine

The 4h EMA only steps when a 4h candle closes, so it draws as a staircase across four 1h bars. That flat-then-step shape is the visual signature of a correct, confirmed higher timeframe (Repainting). The fold works on any chart interval that divides the bucket: a 15m chart contributes sixteen bars per bucket, a 1h chart four, and a 4h chart one (the fold then confirms each bar on the next).

When you do want the live value

Sometimes you genuinely want the forming bucket, for example a live 4h close ticking inside the current period. In a fold that value is already in your hands: it is bucketClose, the running variable. Write it to an output of its own, label it as developing, and never build a signal on it: developing values change as the bucket fills, so a cross built on them will not reproduce. Confirmed is what you get by reading the folded variable instead.

Offset N: the bucket before that

To read the completed period before the most recent one, keep a small ring of closed-bucket values: the last few h4Close values in a StaticArray<f64> you rotate at each bucket change, with entry 1 one bucket back (Variables). A pinned input reads the same thing with one option, offset: 1 (Offset).

Timeframe tokens: rolling versus calendar

Timeframe tokens come in two flavours, and the difference is where the bucket boundaries fall. Both are one line of integer math over the bar's open time in epoch seconds:

Rolling buckets are N seconds wide, floored from the Unix epoch: Math.floor(bar_t / N). "4h" is N = 14400, "1d" is N = 86400 (and its buckets start at 00:00 UTC purely because that is where epoch days fall), "7d" is N = 604800, whose week boundaries land on Thursdays.

Calendar buckets anchor to real UTC calendar boundaries:

TokenBucket indexUTC anchor
"1D"dayIndex = floor(bar_t / 86400)00:00
"1W"floor((dayIndex + 3) / 7)Monday 00:00 (epoch day 0 was a Thursday; + 3 shifts the week start)
"1M"civilYear * 12 + civilMonththe first of the month
"1Q"civilYear * 4 + (civilMonth - 1) / 3Jan / Apr / Jul / Oct 1
"1Y"civilYearJan 1

civilYear and civilMonth come from the days-to-civil function on Time and sessions. A Monday-anchored "1W" and a rolling "7d" produce different closes over the same chart, because their week boundaries fall on different days; write the one you mean. Multi-count calendar tokens ("2W", "3M") are just a different index (weekIndex / 2), so there is nothing to reject.

The cookbook's anchored VWAP and key levels are day and Monday-week folds; the regime filter above is a rolling 4h fold. The ./sdk/resample module writes this fold for you on the built-ins' timeframes (5m to 4h rolling, 1D and 1W on the exchange trade date), with averages and EMAs over the closed candles, confirmed or developing (Higher-timeframe kit).

The interval pin: a real coarser feed

The second form reads real coarser candles, and it is the one that also reaches another symbol. A secondary ohlcv input pinned to a coarser interval, on the chart's market or on a market named by symbol and exchange, is fetched as its own series of closed candles, and each chart bar reads the latest of them that had closed by that bar's close.

// The first input: the chart's own market and interval.
input("close", ohlcv.close);
// A coarser interval of the same market: each daily candle arrives as of its close.
input("daily", ohlcv.close, { interval: "1d", description: "Prior daily close, as of close" });
// Another symbol at a coarser interval: the request() form, pinned by symbol and exchange together.
input("eth_4h", ohlcv.close, { symbol: "ETHUSDT", exchange: "BINANCE_FUTURES", interval: "4h", description: "ETH 4h close, as of close" });
output("daily", line, overlay, { color: "#f59e0b", description: "Daily close, stepping once per day" });
output("eth_4h", line, lower, { color: "#7c3aed", description: "ETH 4h close" });
output("ratio_to_daily", line, lower, { color: "#2563eb", unit: "%", description: "This bar's close relative to the last daily close" });

function onBar(): void {
  const close = bar.close();
  const daily = in_daily();
  const eth = in_eth_4h();
  if (isNaN(daily) || isNaN(eth)) return;
  out_daily(daily);
  out_eth_4h(eth);
  out_ratio_to_daily(daily == 0.0 ? NaN : ((close - daily) / daily) * 100.0);
}
BTCUSDT perpetual on Binance, 1 hour bars, Aug 10 to Aug 18, 2026Real output from OpenMarket's engine

Add it to a 1h chart: daily steps once a day, eth_4h reads the ETHUSDT perpetual on Binance at 4h whatever market the chart shows, and ratio_to_daily compares every hourly close with the last closed daily candle. The chart checks every pin before it fetches anything and refuses, by name, a pin it cannot serve.

When a pinned candle arrives

On a 1h chart pinned to 4h, the 00:00 to 04:00 candle belongs to the 03:00 bar, the bar whose close is 04:00: while that bar is still forming it reads the previous 4h candle, and once the bar has closed the chart evaluates it again with the new candle in place. The bucket fold above shows the same candle from the 04:00 bar, one bar later. Both are causal: neither reads a candle before it closed.

A pin reads the confirmed view unless the input declares another one with view, or lists extra views with views, each read through its own accessor:

text
input("h4", ohlcv.close, { interval: "4h", views: "forming,is_new_period" });
// in_h4()                 confirmed: the latest 4h candle closed by this bar's close, carried forward
// in_h4_forming()         forming: this bar's own 4h bucket so far, a developing value
// in_h4_is_new_period()   is_new_period: 1 on the bar where the held 4h candle changes, else 0

forming folds the chart's own bars (or, for a pinned market, that market's bars at the chart's interval), goes up to 1w, and needs the chart's own candles as the first input; like the fold's bucketClose, it is for display, not for signals. is_new_period reads 0 on the forming bar until it closes.

Offset: the candle before that

offset: N reads the pinned candle N candles before the one confirmed holds, counted in the pin's own candles:

text
input("wk_high", ohlcv.high, { interval: "1w" });                  // the last closed week's high
input("wk_high_before", ohlcv.high, { interval: "1w", offset: 1 }); // the week before that one

N runs from 1 to 500, and the editor writes the offset view into the sheet for you. Until N + 1 candles have closed the input reads NaN (0 under missing: "zero"), and the bar still runs. offset: 0 would be the confirmed reading itself, so leave the option off for it. A calendar offset (the quarter before last) comes from the candles stream below.

What the chart serves

InputInterval pinMarket pin
a secondary ohlcv inputcoarser than the chart and a whole multiple of its intervalsymbol + exchange, at the chart's interval or with a coarser one
funding (rate_close), oicoarser, on the chart's own market (bucketed from their own rows)refused
oddscoarser, confirmed and is_new_period views onlythe market's condition id in symbol, always
trades, liquidations, implied_volatility, skew, etf_flow, timerefusedrefused
candles (celled)required: any interval word, finer than, equal to or coarser than the chart'ssymbol + exchange
intrabar (celled)required: finer than the chart's and dividing it evenlysymbol + exchange
other celled inputs (book, volume_profile, options_chain)refusedrefused
the first inputrefused: it follows the chart's intervalrefused: it follows the chart's market (an odds first input is the exception)

For the feed pins above, a finer interval is refused, and so is one that is not a whole multiple of the chart's (a 5m pin on a 3m chart), a view without an interval, and a forming view above 1w. The interval words are 1m, 5m, 15m, 30m, 1h, 4h, 1d, 1w (MINUTE, FIVE_MINUTES, FIFTEEN_MINUTES, THIRTY_MINUTES, HOUR, FOUR_HOURS, DAY and WEEK work too). An interval pin equal to the chart's own interval is no pin at all: the input reads bars at the chart's interval.

What a coarse pin costs: the chart fetches the pinned candles from two of them before the first loaded bar (N more for offset: N, and further back with bars, below), and the value steps once per pinned candle. Keep the default missing: "carry" on a coarse pin: it carries the latest closed candle across the bars in between, while nan and zero give those bars the fill and only the bar that admits a new candle reads it.

The chart's custom timeframes

The chart's custom timeframes are interval words too: 2m, 3m, 10m, 45m, 2h, 6h, 8h, 12h and 3d (TWO_MINUTES, THREE_MINUTES, TEN_MINUTES, FORTY_FIVE_MINUTES, TWO_HOURS, SIX_HOURS, EIGHT_HOURS, TWELVE_HOURS and THREE_DAYS work too). They work everywhere a pin does: a pin and its views, offset, bars, a candles stream and intrabar, by the same rules as the eight above.

They are chart only. The chart serves them; anywhere else an Indicator that pins one is refused by name, wrun_custom_interval_unsupported, rather than reading a nearby interval in its place.

Every custom bar opens on the UTC grid of its own length counted from 1970-01-01, the grid the chart draws them on, so the whole-multiple rule decides what nests:

ChartPinWhy
45m1d works, 1h is refused45m divides a day (32 bars, restarting at midnight UTC) but not an hour
1d3d worksthree whole days; a 3d bar opens every third day from 1970-01-01, so its weekday drifts
3d1w is refusedthree days never divide a week, whose bars open on Mondays
2h6h, 8h, 12h and 1d work, 45m is refused45m is finer than the chart: read it as a candles stream instead

A future follows its exchange's session instead: a CME future's bars open on the 17:00 Chicago session, and its 3d bar holds three trade dates, so one can span a weekend or a holiday and close days after its open plus three days. A pin and its views, a candles stream and intrabar read such a bar only once it closes, never while it is still forming.

Stocks, forex and gold

A pin on a stock, forex or gold market, and any pin on a stock or forex chart, is served like a crypto pin, its candles joined onto the chart's bars. A 3d pin on those markets is refused and an index pin reads NaN; the session rules are on Multi-source.

History: the candles stream

A pin hands each bar one candle. When the indicator needs many candles at once (every day since January 1st, last quarter's range, the last 20 weekly candles), read the timeframe as a stream of its closed candles:

text
input("close", ohlcv.close);                              // the first input stays a scalar: it is the bar grid
input("d", candles.cells, { interval: "1d", bars: 400 }); // the newest 400 closed days, then each day as it closes
  • The tuple. Six numbers per candle, oldest first: [offset_ms, open, high, low, close, volume]. offset_ms is the candle's open minus the bar's open, negative for history, so the candle opened at bar.time() + offset_ms / 1000 epoch seconds.
  • When candles arrive. The first bar carries the backlog: every closed candle from the start of the stream up to that bar. Each later bar carries the candles that closed since the bar before it, which on most bars is none. A candle arrives on the first bar whose close is at or after its own, so a forming candle never arrives and none arrives twice.
  • interval is required and may be any interval word: coarser than the chart's (daily candles on a 1h chart), equal to it, or finer. symbol and exchange together read another market.
  • bars (1 to 5000) sets the depth: the stream starts at least bars candles before the forming one (earlier when the chart has loaded more), so by the newest bar the indicator has seen at least the newest bars closed candles. Without it the stream starts one or two candles before the first bar the indicator runs.
  • max_cells may be left out: the editor then declares the larger of bars and 2. A finer interval also needs room for the candles one bar can close, the chart's interval over the stream's plus one (61 for 1m candles on a 1h chart), and Run names the number when the cap is short. A block over the cap stops the run; it is never cut short.
  • view: "forming" turns an input into the timeframe's live candle: input("d_live", candles.cells, { interval: "1d", view: "forming" }) carries the day still forming, as the market serves it so far, as one tuple on the live bar (its offset_ms is that candle's open minus the bar's) and an empty block on every other bar, any interval, finer than the chart's or coarser. It takes no bars, and max_cells 1 holds it. The closed stream and this candle together are the newest candles through now.
  • view: "forming_open" turns an input into the open of the candle each bar sits in, on every bar: input("d_open", candles.cells, { interval: "1d", view: "forming_open" }) hands every bar one tuple [offset_ms, open, NaN, NaN, NaN, NaN], the open of the day holding that bar, today's still-forming day included. A candle's open is settled once the candle starts trading, so the value never looks ahead and never changes; the day's high, low, close and volume as of the bar are not known, so they read NaN. This is the running open of a higher timeframe, which the stream (a candle only once it closes) cannot give a bar in the middle of a day: Woodie pivots, for one, read the new period's open on its first bar even when the chart's first loaded bar is 21:00. A bar no candle holds (a gap in that timeframe) gets an empty block. Any interval, no bars, and max_cells 1 holds it.

The ./sdk/candles kit reads the stream: a Periods folds it into calendar days, weeks, months, quarters or years, and a CandleList keeps the newest candles. A Periods takes a 1d or finer stream, or a 1w one told so; a 3d stream is not a calendar unit, so read it through CandleList. Feed it every bar, empty blocks included, because each bar moves its clock (Calendar periods and candle lists).

A yearly VWAP

The volume-weighted average price of every day closed since January 1st, on any chart interval:

input("close", ohlcv.close); // a scalar first: the first input is the bar grid
input("d", candles.cells, { interval: "1d", bars: 400, description: "The newest 400 closed days" });
output("yvwap", line, overlay, { color: "#f59e0b", width: 2, description: "VWAP of this year's closed days" });

const year = new Periods(period.YEAR);

function onBar(): void {
  // Every bar feeds its block, an empty one too: the bar moves the period clock.
  year.load(in_d_view(), in_d_cells(), bar.time());
  out_yvwap(year.developing().vwap());
}
BTCUSDT perpetual on Binance, 1 hour bars, Aug 10 to Aug 18, 2026Real output from OpenMarket's engine

On a 1h chart the first bar receives the backlog, every day closed from 400 days back up to that bar, and each later bar receives the day that closed with it (the 23:00 bar receives that day) or nothing. Periods files each day under its calendar year, which starts January 1st at 00:00 UTC, and keeps the year's VWAP sums, so developing().vwap() is the VWAP of this year's closed days and steps once a day. 400 days always reach back past January 1st, so the current year is whole. A year the stream does not reach back to the start of reads NaN, never a partial VWAP. For a line that also moves inside the day, add the chart's own bars of the forming day to the period's pv and pvVolume sums.

A grid of timeframes

A matrix of small candle charts, one per timeframe, pinned to a corner of the pane: for each timeframe a candles stream into a CandleList and its live candle, and the draw.minicharts widget, whose frame the indicator rewrites on the live bar:

input("close", ohlcv.close); // a scalar first: the first input is the bar grid
input("m15", candles.cells, { interval: "15m", bars: 18, max_cells: 100 }); // a 1d chart closes 96 of them per bar
input("m15_live", candles.cells, { interval: "15m", view: "forming" }); // the live 15m candle, on the live bar
input("h1", candles.cells, { interval: "1h", bars: 18, max_cells: 25 });
input("h1_live", candles.cells, { interval: "1h", view: "forming" });
input("h4", candles.cells, { interval: "4h", bars: 18 });
input("h4_live", candles.cells, { interval: "4h", view: "forming" });
input("d1", candles.cells, { interval: "1d", bars: 18 });
input("d1_live", candles.cells, { interval: "1d", view: "forming" });
output("held", none, lower, { description: "Closed daily candles the grid holds" }); // data-only: a reading a watch can use
// The settings: each "@" in the grid below binds one of them to a look option.
param.color("panelBg", "#f7f3e8ee", { label: "Panel background", group: "Style" });
param.color("borderColor", "#c8c1b3", { label: "Border color", group: "Style" });
param.color("textColor", "#18202b", { label: "Text color", group: "Style" }); // the text and the wicks
param.color("bullColor", "#13a983", { label: "Bull candle", group: "Style" });
param.color("bearColor", "#e04f5f", { label: "Bear candle", group: "Style" });
param.bool("showFastMA", true, { label: "Show fast MA", group: "Moving averages" });
param.int("fastMALen", 5, { label: "Fast MA length", min: 2, max: 30, group: "Moving averages" });
param.color("fastMAColor", "#f59e0b", { label: "Fast MA color", group: "Moving averages" });
param.bool("showSlowMA", true, { label: "Show slow MA", group: "Moving averages" });
param.int("slowMALen", 12, { label: "Slow MA length", min: 3, max: 60, group: "Moving averages" });
param.color("slowMAColor", "#2563eb", { label: "Slow MA color", group: "Moving averages" });
const grid = frame("grid", { max_bytes: 16384 });
draw.minicharts({
  name: "matrix", frame: grid, x: -84, y: 42, columns: 2, panel_width: 250, panel_height: 144,
  background_color: "@panelBg", border_color: "@borderColor", text_color: "@textColor", wick_color: "@textColor",
  bull_color: "@bullColor", bear_color: "@bearColor",
  show_fast_ma: "@showFastMA", ma_fast_length: "@fastMALen", ma_fast_color: "@fastMAColor",
  show_slow_ma: "@showSlowMA", ma_slow_length: "@slowMALen", ma_slow_color: "@slowMAColor",
  ma_width: 1, show_change: true, show_volume: true,
});

const m15 = new CandleList(18);
const h1 = new CandleList(18);
const h4 = new CandleList(18);
const d1 = new CandleList(18);

function onBar(): void {
  // Every bar feeds every stream, an empty block too: the bar moves each list's clock.
  m15.load(in_m15_view(), in_m15_cells(), bar.time());
  h1.load(in_h1_view(), in_h1_cells(), bar.time());
  h4.load(in_h4_view(), in_h4_cells(), bar.time());
  d1.load(in_d1_view(), in_d1_cells(), bar.time());
  out_held(f64(d1.count()));
  if (!bar.isLast()) return;
  // Each panel: the newest closed candles, then the timeframe's live one, 18 in all.
  mc_begin("{{symbol}}");
  mc_leg("15m", m15, 18, in_m15_live_view(), in_m15_live_cells(), bar.time());
  mc_leg("1h", h1, 18, in_h1_live_view(), in_h1_live_cells(), bar.time());
  mc_leg("4h", h4, 18, in_h4_live_view(), in_h4_live_cells(), bar.time());
  mc_leg("1d", d1, 18, in_d1_live_view(), in_d1_live_cells(), bar.time());
  writeMiniCharts(FRAME_GRID);
}
BTCUSDT perpetual on Binance, 1 hour bars, Aug 10 to Aug 18, 2026Real output from OpenMarket's engine

The declaration holds the grid's look: anchor with x and y pixel offsets from it, columns, the panel size and gap, the colours, candle_style, the two moving averages and the change badge and volume strip, which the chart draws from the candles (Cards, frames and panels). A look option takes a literal, or an "@<setting>" reference where a setting should change it: a colour takes a param.color, a switch (show_fast_ma, show_slow_ma, show_change, show_volume) a param.bool, an average's length a param.int whose min is 1 or more, ma_width a number setting kept within 0.5 to 5, and candle_style a param.choice over the style words. The setting's default draws until someone changes it, and one setting may drive several options: here textColor colours the text and the wicks. A reference to a setting of another kind stops the build with the option's name. A grid that binds nothing gets settings of its own instead: the panel, border, text, candle and wick colours, the candle style, each declared average's switch, length and colour, the average width and the change and volume switches, labelled and defaulting to the values written in the declaration. The frame holds what changes: the title and each panel's label and candles, written with mc_begin and mc_leg (or mc_candles, mc_panel and mc_bar for candles of your own) and sent with writeMiniCharts. The chart writes the market's name in place of {{symbol}}.

On the live bar each panel holds its newest closed candles and ends with the timeframe's live candle, whatever the chart's interval: the view: "forming" inputs carry it, finer than the chart's or coarser. On every other bar those inputs are empty, so a panel shows closed candles only.

How far back history reaches

bars asks; the data decides. On the chart a stream or a pin reaches back 15,000 of its own candles (25,000 on 1h, 50,000 on 30m). Where history stops sooner, as it does at about a year (365 days) on some data plans, the run is not refused: the stream starts where history is served, and a warning names that start. About a year of history still holds the current quarter, and the current year until its last day or two.

Two things to plan for

  • Keep a scalar input first. The first input line is the bar grid, so a file that opens with the stream is refused when it runs (wrun_candles_primary: input 'd' reads candles at index 0; ...). Put input("close", ohlcv.close); above it.
  • A stream across a weekend. On a market that pauses (a CME future breaks daily and closes for the weekend), a stream pinned to a market that trades around the clock (BTC) hands the first bar after a pause every candle that closed during it. On an intraday interval that block can exceed max_cells and stop the run: raise max_cells to hold a weekend of candles, or, for candles finer than the chart's, read them through intrabar, which slices each bar instead.

Lower timeframes

The intrabar celled source attaches the finer bars inside each chart bar as cells:

wrun
input("close", ohlcv.close);
input("m1", intrabar.cells, { interval: "1m", max_cells: 60 });

Each chart bar carries one [offset_ms, open, high, low, close, volume] tuple per CLOSED finer bar it contains, ascending by open, offset_ms the finer open minus the bar's open (the finer bar's own open in epoch milliseconds is the chart bar's open in seconds times 1000 plus offset_ms). The forming finer bar never ships; a covered bar with no finer bars is a present empty block (in_m1_cells() = 0), a bar the finer history does not reach reads -1, and blocks are never truncated (declare max_cells at least the finer bars per chart bar: 60 for 1m under 1h, or Run refuses wrun_intrabar_ratio_over_cap). The interval is REQUIRED, finer than the chart's, a whole divisor of it and an interval word, the custom timeframes included (3m slices a 45m chart into 15 bars, while 10m would straddle its bars and is refused; wrun_intrabar_leg_not_finer, wrun_intrabar_leg_off_grid), so the finest is 1m and the chart must be 2m or coarser. The source reads the chart's own market, or another one named by symbol and exchange together, and can never be the first input. On a closed bar with full coverage, folding the block (first open, max high, min low, last close, summed volume) is the bar's own candle; on a pinned market it is that market's candle over the same span. The other celled sources slice the same bar by price (volume_profile) or level (book; Data sources).

What the fold can see

A fold is computed from the bars the chart loaded: a bucket already running when the loaded history starts is incomplete, so keep its statistic NaN until the first boundary passes (the cookbook's anchored VWAP does this). The chart applies the same rule to a forming view: a bucket that opened before the first loaded bar reads NaN until panning back loads the rest. A coarse pin is not limited this way: it fetches its own candles, so a daily level resolves from the first loaded 1m bar without loading a day of minutes. Neither is a candles stream: it reaches bars candles back whatever the chart has loaded, which is how a 1m chart reads this year's days.

Boundaries

The chart refuses, by name, every pin it cannot serve (the table above), so a pinned file never silently reads the chart's own bars in place of the candles it asked for. Test a new fold by writing its bucket index to a data-only output and reading it back at the editor's Console prompt (last 20 bucket prints the last twenty values of an output named bucket).

An alert reads pins within 600 bars of the chart's interval: another market's candles on a secondary ohlcv input, and a coarser pin of 1m to 1w that is a whole multiple of the chart's interval, read once its candle has closed. It refuses a custom timeframe ("This Indicator is pinned to a different interval than the chart."), an offset view, intrabar cells and candles streams. The 600 chart bars hold 600 / (leg / chart) candles of a pin, 37 of a 4h pin on a 15m chart and 12 on 5m, so an average of 20 4h candles fills on 15m and arms but stays empty on 5m: arm such alerts on a coarser chart. A fold of the chart's own bars has no pin to size the alert by: declare warmup() with the bars its window spans, or the alert can arm and stay empty (Alerts).

From htf(), request(), requestBars() and ltf()

If you have written chart scripts with these four functions, each call maps to one declaration here:

The functionHere
htf(src, "4h"), or request(sym, "4h") for another market: the last closed candleinput("h4", ohlcv.close, { interval: "4h" }), the confirmed view, with symbol and exchange for another market
htf(src, "4h", { mode: "developing" })view: "forming", or views: "forming" beside the confirmed reading
htf(src, "4h", { offset: N })offset: N
request(sym, "4h", { bars: N })bars: N on the pin
request(sym, "1W"), "1M", "1Q", "1Y"a 1d candles stream read through Periods(period.WEEK), MONTH, QUARTER or YEAR: confirmed(0) is the last closed period, confirmed(1) the one before (with keep 2), developing() the current one
request(sym, "1Q") and "1Y", value for valuethe same Periods fed a 1w stream and told so, new Periods(period.QUARTER, 4, period.WEEK): a week that straddles two periods counts in the one it opens in, as those two tokens count it
vwap(anchor="quarter") or "year"Periods(period.QUARTER) or YEAR over a 1d stream: developing().vwap(), plus the forming day's own bars for a live value
requestBars(sym, "1d", { bars: N + 1 })a 1d candles stream with bars: N, read through CandleList(N): the same rows without the last one, the forming candle, which a second 1d candles input declared view: "forming" carries on the live bar (or the forming view of a scalar pin)
plotMiniChartGrid(panels, title, position, x, y, ...) over requestBars(..., { bars: N, anchor: "latest" }) panelsdraw.minicharts({ name, frame, ... }) with the same look in snake case (position is anchor, panelWidth is panel_width), and per panel a candles stream read through CandleList(N) plus its view: "forming" twin, written with mc_leg (the live candle last) and sent with writeMiniCharts; the look's inputs are settings bound with "@<setting>" references (A grid of timeframes)
ltf("1m")input("m1", intrabar.cells, { interval: "1m", max_cells: 60 })
ltf("1m", "ohlcv", sym, exchange)the same with symbol and exchange