TimeFrameSet() and TimeFrameRestore()
TimeFrameSet( inWeekly ) is one line long and it changes the meaning of every line after
it. By the end of this lesson you will know precisely which arrays it replaces, what
happens to everything it does not replace, why the numbers it produces are attached to the
wrong bars until you do something about it, and which restrictions the official
documentation places on what you may do inside the block.
The switch
Section titled “The switch”Fragment — not a complete formula
TimeFrameSet( inWeekly ); // returns NOTHING; every price array is now weeklyWeeklyAverage = MA( Close, 10 );TimeFrameRestore();The documented syntax is TimeFrameSet( interval ), and the argument is an interval in
seconds. Sixty means one minute. Everything else follows from that: the named constants
are just readable spellings of second counts.
| Constant | Documented value | Notes |
|---|---|---|
in1Minute |
60 | |
in5Minute |
5 × 60 | |
in15Minute |
15 × 60 | |
inHourly |
3600 | |
inDaily |
24 × 3600 | 86,400 |
inWeekly |
5 × 24 × 3600 + 1 | 432,001 — note the +1 |
inMonthly |
25 × 24 × 3600 + 1 | 2,160,001 — note the +1 |
inQuarterly |
not published | constant exists; the guide does not print its value |
inYearly |
not published | constant exists; the guide does not print its value |
There is no in30Minute and no in4Hour in the documented constant list. For anything not
named, the documented idiom is an integer multiple: 3 * in1Minute for three-minute bars,
4 * inHourly for four-hour bars, 3 * inDaily for three-day bars.
Two other forms are documented. A negative interval means N-tick bars — TimeFrameSet( -133 )
is 133-tick compression — and works correctly only when the database’s base time interval is
set to tick in File → Database Settings. Positive values can also be reinterpreted as tick,
volume or range bars by a prior call to TimeFrameMode(); that belongs with intraday work
and is covered where intraday databases are, in Part 19.
What changes inside the block, and what does not
Section titled “What changes inside the block, and what does not”This is where most multi-timeframe bugs are born, so it is worth being pedantic.
TimeFrameSet() replaces exactly seven built-in arrays: Open, High, Low, Close, Volume,
OpenInt and Avg. That is the documented list, and it is the complete list. Nothing else
in your formula is touched by the call itself.
But because those seven arrays are the input to almost everything, the consequence is
sweeping: every function you call after the switch operates on weekly bars, so every result
you compute after the switch is a weekly result. MA( Close, 10 ) inside the block is a
ten-week average of weekly closes. It is not a two-week-long average of daily closes,
and it is not the same series as MA( Close, 50 ) on daily bars. They answer different
questions and they turn on different days.
Variables you created before the call keep the frame they were made in. The reference page states this directly, and it is the mechanism that lets one formula mix any number of intervals: compute your daily things first, switch, compute your weekly things, restore.
Fragment — not a complete formula
DailyAverage = MA( Close, 50 ); // daily: made before the switch, stays daily
TimeFrameSet( inWeekly );WeeklyAverage = MA( Close, 10 ); // weekly AND time-compressedTimeFrameRestore();The internal layout, and why it matters
Section titled “The internal layout, and why it matters”The User’s Guide is unusually explicit about the implementation, and understanding it removes most of the mystery from the rest of this part.
Timeframe functions do not change BarCount. The array you get back has exactly as
many elements as the base-interval array had. AmiBroker squeezes the compressed values into
it by filling the first N elements with Null and packing the real values at the end.
Ten daily bars, compressed to two weekly bars
| Bar | Mon | Tue | Wed | Thu | Fri | Mon | Tue | Wed | Thu | Fri |
|---|---|---|---|---|---|---|---|---|---|---|
Close (daily) | 10.0 | 10.4 | 10.2 | 10.6 | 10.5 | 10.8 | 11.2 | 11.0 | 11.4 | 11.3 |
Close inside the blockcompressed: packed at the end | Null | Null | Null | Null | Null | Null | Null | Null | 10.5 | 11.3 |
TimeFrameExpand(…, inWeekly)back on the right bars | Null | Null | Null | Null | 10.5 | 10.5 | 10.5 | 10.5 | 10.5 | 11.3 |
Look at the middle row. The two weekly closes are sitting on the last two daily bars,
which are Thursday and Friday of week 2. If you plotted that row, or put it in an
Exploration column, or compared it with Close, you would be reading week 1’s close
against Thursday of week 2. Nothing would warn you. This is the whole reason the next
lesson exists, and why the guide says flatly that expanding is required for any formula
that uses the TimeFrame functions.
TimeFrameRestore()
Section titled “TimeFrameRestore()”The documented syntax is TimeFrameRestore( tradeprices = False ), and the reference page
says plainly that the tradeprices argument should be set to false. Leave it alone.
The critical sentence is what it restores: only OHLC, Volume, OpenInt and Avg. Every other variable created while the block was open stays compressed. Your weekly average, your weekly Boolean condition, your weekly high — none of them come back on their own. They remain compressed arrays with the layout in the diagram above, and they must be expanded before use.
The documented restrictions inside a block
Section titled “The documented restrictions inside a block”Five rules, all of them from the official pages rather than from folklore.
One block at a time. “Before calling TimeFrameSet again in the same formula with different interval you have to restore original timeframe first using TimeFrameRestore.” There is no stack and no nesting. Treat set and restore as a strictly flat pair.
Fragment — not a complete formula
// Wrong: a second switch without restoring the firstTimeFrameSet( inWeekly );TimeFrameSet( inMonthly );
// RightTimeFrameSet( inWeekly );WeeklyValue = MA( Close, 10 );TimeFrameRestore();
TimeFrameSet( inMonthly );MonthlyValue = MA( Close, 6 );TimeFrameRestore();Compression goes up only. From one-minute data you can build 2, 3, 5 or N-minute bars. From 15-minute data you cannot get one-minute bars, and from an end-of-day database you cannot reach any intraday interval at all. The data to build the shorter bars is not there, and no function will invent it.
Loops need help; array functions do not. AmiBroker’s built-in array functions check for
Nulls at the beginning of a series and skip them. A for or while loop does not: starting
at bar 0 inside a compressed block reads Null and poisons everything downstream. The
documented pattern uses NullCount(), which returns the number of consecutive Nulls at the
start of an array.
Fragment — not a complete formula
TimeFrameSet( inWeekly );Start = NullCount( Close ); // where the real weekly data beginsfor( i = Start + 1; i < BarCount; i++ ){ // seed element Start with a real value before the loop, then work forward}TimeFrameRestore();Note that BarCount is unchanged inside the block, so only the start index moves.
Everything leaving the block must be expanded. Skipping it raises Warning 509, whose
official wording is that TimeFrameExpand() is absolutely required for any formula using
the TimeFrame functions, and that without it your timestamps will be wrong. The Common
Coding Mistakes chapter gives the canonical pair:
Fragment — not a complete formula
TimeFrameSet( inWeekly );MA14_Weekly = MA( Close, 14 );TimeFrameRestore();
Buy = Cross( Close, MA14_Weekly ); // WRONGBuy = Cross( Close, TimeFrameExpand( MA14_Weekly, inWeekly ) ); // CORRECTMind QuickAFL. The official QuickAFL article lists cases where its estimate of how many
bars a formula needs may not produce identical results, and names “TimeFrame functions with
much higher intervals than base interval” among them. A weekly indicator built from daily
bars is exactly that case. The practical symptom is a formula that gives different numbers
zoomed in than zoomed out, or different backtest results over a sub-range than over all
quotations. The documented fix is SetBarsRequired() at the top of the formula — a
specific figure such as SetBarsRequired( 1000, 0 ), or SetBarsRequired( sbrAll, sbrAll )
to switch QuickAFL off entirely.
TimeFrameGetPrice(): the one-call alternative
Section titled “TimeFrameGetPrice(): the one-call alternative”If all you want is another interval’s open, high, low, close, volume or open interest, the
whole set-compute-restore-expand sequence is unnecessary. TimeFrameGetPrice() does it in
one call and the guide records it as about twice as fast as the equivalent nested calls.
Fragment — not a complete formula
PreviousWeekHigh = TimeFrameGetPrice( "H", inWeekly, -1 );PreviousDayHigh = TimeFrameGetPrice( "H", inDaily, -1 ); // on intraday barsThe documented signature is TimeFrameGetPrice( pricefield, interval, shift = 0, mode = expandFirst ).
Three things about it catch people out:
pricefieldis a quoted string, one of"O","H","L","C","V"or"I". Passing the bare arrayHighis not the same thing.shiftcounts higher-timeframe bars, not base bars. On daily data withinWeekly,-3means three weeks ago. It behaves likeRef()applied to the compressed series.- Its defaults point the wrong way for trading rules. Both
shift = 0andmode = expandFirstare the risky choices, and the reference page says so: withshift = 0, “compressed data may look into the future ( weekly high can be known on monday )”, and it instructs you to reference past data with a negative shift when writing a trading system.
That last point is the subject of the next lesson. For now, take the documented advice at face value: in a decision, use a negative shift.
Seeing it happen
Section titled “Seeing it happen”Reading about a leading-Null prefix is not the same as watching one appear in a table against real dates. This Exploration puts the daily close, the raw compressed weekly close and the expanded weekly close in neighbouring columns, so the layout stops being an abstraction.
Produce direct evidence, on your own data, of three claims made above: that compression fills the front of the array with Nulls and packs values at the end; that expansion puts them back on the right bars; and that a variable created before the switch is untouched by it.
Complete formula
Section titled “Complete formula”Complete runnable AFL
// ===========================================================================// Time frame anatomy// Makes visible what TimeFrameSet() actually does to an array. The official// User's Guide describes it plainly: time-frame functions do not change// BarCount, they squeeze the array so the first N slots hold Null and the// compressed values sit at the END of the array. Until you expand them back,// those values are attached to the wrong bars.//// This Exploration puts the raw compressed array and the expanded array in// neighbouring columns so you can see the difference rather than believe it.//// HOW TO RUN// Analysis window -> Apply to: Current symbol (any liquid share with a few// years of history). Range: All quotations.// Analysis -> Settings -> Periodicity: Daily.// Read the table from the FIRST row downwards. The interesting damage is at// the top of the table, not at the bottom.//// WHAT TO LOOK FOR// 1. "Weekly close (compressed)" is empty for roughly the first four fifths// of the rows, then prints numbers that have nothing to do with the date// on the same row. That is the unexpanded array.// 2. "Weekly close (expanded)" carries a plausible weekly value on every// row, and changes only on the last trading day of each week.// 3. "Daily MA(20) built before the switch" is untouched by TimeFrameSet:// variables assigned before the call stay in the frame they were made in.//// ASSUMPTIONS, STATED SO THEY CAN BE CHECKED// - The database base time interval is daily or shorter. Compression only// goes upwards; from end-of-day data no intraday frame is reachable.// - QuickAFL is switched off inside the formula with SetBarsRequired(),// because time-frame functions with an interval much higher than the base// interval are one of the documented cases where the QuickAFL estimate// can change the numbers you see.// ===========================================================================
SetBarsRequired( sbrAll, sbrAll );
// ---------------------------------------------------------------------------// Built BEFORE the switch. These stay in the daily frame.// ---------------------------------------------------------------------------DailyClose = Close;DailyMA20 = MA( Close, 20 );
// ---------------------------------------------------------------------------// Inside the block every built-in price array is weekly, so every result// computed here is weekly AND time-compressed.// ---------------------------------------------------------------------------TimeFrameSet( inWeekly );
WeeklyCloseRaw = Close;WeeklyMA10Raw = MA( Close, 10 );
TimeFrameRestore();
// TimeFrameRestore() puts back Open, High, Low, Close, Volume, OpenInt and Avg,// and nothing else. The two variables above are still compressed, so they must// be expanded before they can be displayed or compared with daily data.// The interval argument names the frame the data CAME FROM, not the frame we// are going to.WeeklyClose = TimeFrameExpand( WeeklyCloseRaw, inWeekly );WeeklyMA10 = TimeFrameExpand( WeeklyMA10Raw, inWeekly );
// Where the real values begin inside the compressed array.LeadingNulls = NullCount( WeeklyCloseRaw );
// Day names, so the alignment can be read without counting rows.DayName = WriteIf( DayOfWeek() == 1, "Mon", WriteIf( DayOfWeek() == 2, "Tue", WriteIf( DayOfWeek() == 3, "Wed", WriteIf( DayOfWeek() == 4, "Thu", WriteIf( DayOfWeek() == 5, "Fri", "other" ) ) ) ) );
Filter = 1;SetOption( "NoDefaultColumns", True );
AddColumn( DateTime(), "Date", formatDateTimeISO, colorDefault, colorDefault, 110 );AddTextColumn( DayName, "Day", 1.0, colorDefault, colorDefault, 50 );AddColumn( Interval(), "Base interval (seconds)", 1.0 );AddColumn( LeadingNulls, "Leading Nulls in compressed array", 1.0 );AddColumn( DailyClose, "Daily close", 1.2 );AddColumn( WeeklyCloseRaw, "Weekly close COMPRESSED (unusable)", 1.2 );AddColumn( WeeklyClose, "Weekly close expanded", 1.2 );AddColumn( Close, "Close after restore", 1.2 );AddColumn( DailyMA20, "Daily MA(20) built before the switch", 1.2 );AddColumn( WeeklyMA10, "Weekly MA(10) expanded", 1.2 );How it works
Section titled “How it works”The formula has four movements. First it builds two daily series — the close itself and a
20-bar average — before any switch, which is what makes them stay daily. Second it opens
the weekly block and computes two weekly series. Third it restores, which brings back only
the seven built-in arrays, and expands the two weekly series explicitly. Fourth it prints
everything side by side, including the raw compressed array that you would normally never
display, plus NullCount() so you can see how many leading Nulls there are without
counting rows yourself.
SetBarsRequired( sbrAll, sbrAll ) sits at the top for the reason given above: without it,
the numbers can depend on the analysis range, and an example whose output changes with the
range teaches the wrong lesson.
Key functions
Section titled “Key functions”NullCount( array, mode = 1 )— counts consecutive Nulls. Mode 1, the default, counts them at the beginning; 2 at the end; 3 from both ends; 0 counts every Null in the array including non-consecutive ones.Interval( format = 0 )— returns the interval the formula is currently running at, in seconds by default. It is here as a self-check: if that column does not read 86,400 you are not running daily and the rest of the table means something else. Compare it numerically, never against the text fromInterval( 2 ), because those names are translated in localised builds.DayOfWeek()— 0 for Sunday through 6 for Saturday. Used to label the rows so that weekly alignment can be read at a glance.
Expected result
Section titled “Expected result”Test it
Section titled “Test it”Change inWeekly to inMonthly in the TimeFrameSet() call and in both
TimeFrameExpand() calls, then re-run. The leading-Null count should grow by roughly a
factor of four, because there are about four times fewer monthly bars than weekly ones, and
the expanded column should now change once a month. If you change only the TimeFrameSet()
call and leave the expansions on inWeekly, the expanded column becomes nonsense with no
error message — which is a useful failure to have seen once, deliberately, rather than for
the first time in a live formula.
Common errors
Section titled “Common errors”- Weekly columns entirely empty. The Analysis Periodicity is already weekly or longer.
You cannot compress weekly data to weekly bars in a way that leaves anything to show;
check the
Interval()column. - “Weekly close expanded” changes on a Monday rather than a Friday. Someone has passed
an explicit
expandFirsttoTimeFrameExpand(), or the database’s first-day-of-week setting has been changed. The next lesson is about exactly this. - The numbers differ between two runs over different ranges.
SetBarsRequired()has been removed or QuickAFL is intervening. Put it back before drawing any conclusion.
Extension
Section titled “Extension”Add a column that shows TimeFrameGetPrice( "C", inWeekly, -1 ) next to the expanded
weekly close. That is the previous completed week’s close, and comparing the two columns
row by row is the fastest way to build an intuition for how far apart “the most recent
completed weekly value” and “the value from one week before that” really are.
What changes for you
Section titled “What changes for you”You can now read any multi-timeframe formula and answer three questions about it: which arrays it switched, which of its variables are still compressed, and whether it obeyed the documented restrictions on nesting, direction and loops. You also know that the compressed form is not a display format but a genuinely different arrangement of the array, which is why the next step is not optional.
What remains is the argument the whole part is built around: once you have a compressed weekly value, which base bar should it appear on? Get that wrong and everything above still runs, still produces plausible numbers, and quietly reads the future.
Check your understanding
Sources for this lesson
8 verified · checked 2026-08-31
- 01AFL Function Reference — TimeFrameSetamibroker.com/guide/afl/timeframeset.html2026-08-31
- 02AFL Function Reference — TimeFrameRestoreamibroker.com/guide/afl/timeframerestore.html2026-08-31
- 03AFL Function Reference — TimeFrameGetPriceamibroker.com/guide/afl/timeframegetprice.html2026-08-31
- 04AFL Function Reference — NullCountamibroker.com/guide/afl/nullcount.html2026-08-31
- 05AFL Function Reference — SetBarsRequiredamibroker.com/guide/afl/setbarsrequired.html2026-08-31
- 06AmiBroker User's Guide — Multiple Time Frame Support in AFL§ How does it work internallyamibroker.com/guide/h_timeframe.html2026-08-31
- 07AmiBroker User's Guide — Common coding mistakes§ TimeFrameExpand() is required to match data with original time frameamibroker.com/guide/a_mistakes.html2026-08-31
- 08AmiBroker Knowledge Base — QuickAFL factsamibroker.com/kb/2008/07/03/quickafl2026-08-31
Every technical claim on this page was checked against the official AmiBroker documentation on the date shown. Where the course disagrees with folklore, the source is how you can tell which one to trust.