Multi-timeframe
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
intervalpin (a secondaryohlcvinput, andfundingoroion 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
candlesstream (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/candleskit 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);
}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:
| Token | Bucket index | UTC 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 + civilMonth | the first of the month |
"1Q" | civilYear * 4 + (civilMonth - 1) / 3 | Jan / Apr / Jul / Oct 1 |
"1Y" | civilYear | Jan 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);
}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:
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 0forming 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:
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 oneN 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
| Input | Interval pin | Market pin |
|---|---|---|
a secondary ohlcv input | coarser than the chart and a whole multiple of its interval | symbol + exchange, at the chart's interval or with a coarser one |
funding (rate_close), oi | coarser, on the chart's own market (bucketed from their own rows) | refused |
odds | coarser, confirmed and is_new_period views only | the market's condition id in symbol, always |
trades, liquidations, implied_, skew, etf_flow, time | refused | refused |
candles (celled) | required: any interval word, finer than, equal to or coarser than the chart's | symbol + exchange |
intrabar (celled) | required: finer than the chart's and dividing it evenly | symbol + exchange |
other celled inputs (book, volume_profile, options_chain) | refused | refused |
| the first input | refused: it follows the chart's interval | refused: 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:
| Chart | Pin | Why |
|---|---|---|
45m | 1d works, 1h is refused | 45m divides a day (32 bars, restarting at midnight UTC) but not an hour |
1d | 3d works | three whole days; a 3d bar opens every third day from 1970-01-01, so its weekday drifts |
3d | 1w is refused | three days never divide a week, whose bars open on Mondays |
2h | 6h, 8h, 12h and 1d work, 45m is refused | 45m 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:
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_msis the candle's open minus the bar's open, negative for history, so the candle opened atbar.time() + offset_ms / 1000epoch 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.
intervalis required and may be any interval word: coarser than the chart's (daily candles on a 1h chart), equal to it, or finer.symbolandexchangetogether read another market.bars(1 to 5000) sets the depth: the stream starts at leastbarscandles before the forming one (earlier when the chart has loaded more), so by the newest bar the indicator has seen at least the newestbarsclosed candles. Without it the stream starts one or two candles before the first bar the indicator runs.max_cellsmay be left out: the editor then declares the larger ofbarsand 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 for1mcandles 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 (itsoffset_msis 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 nobars, andmax_cells1 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 readNaN. 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, nobars, andmax_cells1 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());
}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);
}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
inputline 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; ...). Putinput("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_cellsand stop the run: raisemax_cellsto hold a weekend of candles, or, for candles finer than the chart's, read them throughintrabar, which slices each bar instead.
Lower timeframes
The intrabar celled source attaches the finer bars inside each chart
bar as cells:
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 function | Here |
|---|---|
htf(src, "4h"), or request(sym, "4h") for another market: the last closed candle | input("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(, 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 value | the same Periods fed a 1w stream and told so, new Periods(: a week that straddles two periods counts in the one it opens in, as those two tokens count it |
vwap( or "year" | Periods( or YEAR over a 1d stream: developing(, plus the forming day's own bars for a live value |
request | 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) |
plot over request panels | draw. 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 |