RequestTimedRefresh() and Real-Time Performance
A chart pane runs your formula when you apply it, when you change a parameter, when you
scroll, and when new data arrives. RequestTimedRefresh() adds a fifth trigger: a clock.
One line turns a static pane into one that re-executes on a schedule, and the schedule is
yours to choose.
The line is easy. Choosing the number is the lesson. Every refresh re-runs the whole formula, every pane runs on its own thread, and a layout with a dozen panes all asking for one-second refreshes is a straightforward way to saturate a processor with work nobody looks at.
What the function does
Section titled “What the function does”The reference page gives the signature as RequestTimedRefresh( interval, onlyvisible = True ),
filed under Indicators, added in AmiBroker 4.90, returning nothing. It causes the indicator
window containing it to refresh automatically every interval seconds — and the wording of
the next clause is the important one — “regardless of data source used or connection state”.
That clause is why this lesson carries no Professional badge and needs no subscription. Timed refresh is not a real-time feature that happens to work offline. It is a charting feature that works everywhere, and real-time work happens to depend on it.
AmiBroker aligns refreshes to second boundaries. RequestTimedRefresh( 5 ) fires at second
0, 5, 10 and so on through 55 of each minute, rather than five seconds after whenever the
pane happened to load.
Since version 5.30.3 the interval can go below one second, down to 0.1 s — but only when
enabled through a registry setting, HKCU/Software/TJP/Broker/Settings/EnableHiresRTR, as a
DWORD value of 1. Treat that as a documented capability rather than a recommendation. Sub-second
refresh multiplies every cost on this page by up to ten, and the accuracy caveat above does not
improve to match.
The onlyvisible argument
Section titled “The onlyvisible argument”The second argument defaults to True, meaning refreshes are triggered only for visible,
non-minimised windows. This applies to the main AmiBroker window as well: minimise
AmiBroker and, by default, charts stop refreshing. Passing False keeps them refreshing
while minimised.
Fragment — not a complete formula
RequestTimedRefresh( 5 ); // stops when the window is minimisedRequestTimedRefresh( 5, False ); // keeps running when minimisedThe visibility rule is narrower than it first appears. It covers the minimised state, and the case where a chart has been dragged beyond the edge of the physical screen so it is open but not visible to an eye.
What one refresh actually costs
Section titled “What one refresh actually costs”Every timed refresh re-executes the entire formula for that pane. Not the changed part: all of it, from the first line.
The multi-threading chapter supplies the other half of the arithmetic. Its core rule is that one operation on one symbol is one thread, where an operation is displaying a single chart pane, or running a scan, exploration, backtest or optimisation. A chart window with three panes therefore uses three threads on a single symbol. Thread limits are edition-dependent: the Standard edition is limited to two threads per Analysis window and the Professional edition allows thirty-two, and AmiBroker additionally caps threads at the number of logical processors Windows reports.
What sits between the timer and the number on screen
- Timer firesAligned to the second boundary, accurate to about ±55 ms
- Pane thread wakesOne chart pane is one thread2 per Analysis window on Standard, 32 on Professional
- Quotations loadedAs many bars as the pane is configured to use
- Your whole formula executesEvery line, from the top
- Pane repaintsPlot output and low-level graphics
The consequence is easy to work out and easy to forget:
| Panes asking for refresh | Interval | Whole-formula executions per minute |
|---|---|---|
| 1 | 5 s | 12 |
| 4 | 5 s | 48 |
| 4 | 1 s | 240 |
| 12 | 1 s | 720 |
| 12 | 0.5 s | 1,440 |
Multiply the right-hand column by the milliseconds one execution takes, and you have the processor time your layout consumes doing nothing but redrawing itself. Twelve panes at one second, each taking 40 ms, is 28.8 seconds of formula execution per minute — spread across threads, but real work all the same, before AmiBroker has painted anything.
There is a second refresh source underneath all of this. Preferences, on the Intraday tab,
sets a real-time chart refresh interval whose documented default is three seconds. Setting
that field to zero requests a refresh on every arriving trade — and the manual states plainly
that the Standard edition will not allow it, making that a Professional-only capability. With
zero set, AmiBroker refreshes on every new trade provided the formulas execute fast enough,
and if they do not it dynamically adjusts the rate to hold average processor use at no more
than fifty per cent. Part 21 covers how that interacts with the feed itself.
RequestTimedRefresh() is an additional, formula-driven trigger on top of it, not a
replacement for it.
Measuring instead of guessing
Section titled “Measuring instead of guessing”GetPerformanceCounter( bReset = False ) reads the Windows high-resolution performance
counter and returns milliseconds, with a resolution documented as up to 0.001 ms. Added in
4.90, available in both editions.
Three documented facts shape how you use it.
Reset before you measure. Without a reset the value is milliseconds since the machine booted, which is a very large number, and AFL carries about seven significant digits. The page recommends resetting at the start of the block being measured so that floating-point resolution does not eat the answer.
A call costs about 0.015 ms. The page’s own example says so. Two calls bracket every measurement, so anything under roughly 0.05 ms is mostly the measurement apparatus.
Counters are per formula. Resetting in one formula does not affect the counters in another, so two panes can measure themselves independently without interfering.
There is also a hardware caveat printed on the page: the function relies on the Windows
QueryPerformanceCounter API and the processor’s timestamp counter, and may give inaccurate
results on multi-core processors with clock-stepping technologies enabled in BIOS. If your
readings swing wildly with no change in workload, suspect that before suspecting your code.
A pane that measures itself
Section titled “A pane that measures itself”A chart pane that reports, in its own title, how long it took to execute, its worst case and mean since you last reset it, how many times it has run, and roughly what share of one processor core that implies at the interval you chose — plus a dial for making it deliberately more expensive so you can watch the numbers move.
The complete formula
Section titled “The complete formula”Complete runnable AFL
// refresh-cost-meter.afl// Part 23 - RequestTimedRefresh() and Real-Time Performance//// Measures what this chart pane costs every time it runs, and reports the// answer inside the pane, so that a refresh interval can be chosen from// evidence instead of from optimism.//// It reports:// - milliseconds taken by one execution of the measured block;// - a running worst case and a running mean since the counters were reset;// - how many times the pane has executed;// - roughly what share of one CPU core that implies at the chosen interval;// - whether this particular execution came from the timer or from you.//// Assumptions and honest limits:// - No data feed of any kind is required. RequestTimedRefresh() is// documented to refresh "regardless of data source used or connection// state", so this runs perfectly well on an end-of-day database.// - GetPerformanceCounter() is documented to cost about 0.015 ms per call,// so readings below roughly 0.05 ms are mostly measurement noise.// - Its reference page also warns that the underlying counter can be// inaccurate on machines with CPU clock-stepping enabled in BIOS. If your// numbers jump around wildly with no change in workload, suspect that// before suspecting AmiBroker.// - The duty-cycle figure is your formula's own cost only. It excludes// everything AmiBroker does around the formula - loading quotations,// painting the pane, the data plugin's own work.// - Statistics live in static variables keyed by GetChartID(), so two copies// of this pane keep separate counters instead of corrupting each other's.
_SECTION_BEGIN("Refresh cost meter");
RefreshSeconds = Param( "Refresh interval (seconds)", 1, 1, 60, 1 );ExtraPasses = Param( "Extra passes over the data", 0, 0, 200, 1 );ResetPressed = ParamTrigger( "Statistics", "Reset counters" );
RequestTimedRefresh( RefreshSeconds );
//----------------------------------------------------------------------------// The measured block. Everything between the reset and the read is timed.//----------------------------------------------------------------------------
// Reset first. The counter otherwise reports milliseconds since the machine// booted, which is a very large number, and AFL carries about seven// significant digits - so an unreset measurement of a short block loses most// of its precision. The reference page recommends this explicitly.GetPerformanceCounter( True );
FastLine = MA( Close, 20 );SlowLine = MA( Close, 50 );AtrValue = Max( ATR( 14 ), 0.000001 );Score = ( FastLine - SlowLine ) / AtrValue;
// A dial for making the formula genuinely more expensive. Each extra pass is// real array work over the whole loaded history, which is how real formulas// get slow: more indicators, more lookbacks, more bars.for( i = 1; i <= ExtraPasses; i++ ){ Score = Score + ( MA( Close, 20 + i ) - SlowLine ) / AtrValue;}
ElapsedMs = GetPerformanceCounter();
//----------------------------------------------------------------------------// Running statistics, kept per chart pane.//----------------------------------------------------------------------------StatKey = "rtcost" + NumToStr( GetChartID(), 1.0 ) + "_";
// Static variables start as Null, so the first read of each one has to be// guarded. Nz() does that without pretending a missing value was ever zero// anywhere else.RunCount = Nz( StaticVarGet( StatKey + "runs" ) );TotalMs = Nz( StaticVarGet( StatKey + "total" ) );WorstMs = Nz( StaticVarGet( StatKey + "worst" ) );
if( ResetPressed ){ RunCount = 0; TotalMs = 0; WorstMs = 0;}
RunCount = RunCount + 1;TotalMs = TotalMs + ElapsedMs;WorstMs = Max( WorstMs, ElapsedMs );
StaticVarSet( StatKey + "runs", RunCount );StaticVarSet( StatKey + "total", TotalMs );StaticVarSet( StatKey + "worst", WorstMs );
MeanMs = TotalMs / Max( RunCount, 1 );
// One pane executing for MeanMs every RefreshSeconds occupies this fraction of// one core. Multiply by the number of panes doing the same thing to get the// number that actually matters.DutyPercent = 100 * ( MeanMs / 1000 ) / RefreshSeconds;
if( Status( "redrawaction" ) == 1 ) RedrawSource = "timer";else RedrawSource = "user action or new data";
//----------------------------------------------------------------------------// Report.//----------------------------------------------------------------------------Plot( Score, "Fast-slow distance in ATR units", colorBlueGrey, styleLine | styleThick );PlotGrid( 0, colorLightGrey );
_N( Title = Name() + " - " + Interval( 2 ) + " - refresh cost meter\n" + StrFormat( "This execution: %.3f ms (redraw from ", ElapsedMs ) + RedrawSource + ")\n" + StrFormat( "Mean %.3f ms, worst %.3f ms, over %g executions\n", MeanMs, WorstMs, RunCount ) + StrFormat( "Refresh every %g s implies about %.2f%% of one core for this pane alone\n", RefreshSeconds, DutyPercent ) + StrFormat( "Bars given to this formula: %g Extra passes: %g\n", BarCount, ExtraPasses ) + "Thread " + NumToStr( Status( "ThreadID" ), 1.0 ) );
_SECTION_END();How it works
Section titled “How it works”The measured block is bracketed by two calls: GetPerformanceCounter( True ) to reset, and
GetPerformanceCounter() to read. Everything between them is timed, and nothing else is.
The block contains ordinary array work — two moving averages and an ATR — plus a loop whose
count comes from a parameter. Each extra pass computes another moving average over the whole
loaded history, which is how real formulas actually become slow: more indicators, longer
lookbacks, more bars, not clever tricks.
The statistics survive between executions in static variables, which is the only mechanism
that does. Their names are prefixed with GetChartID(), so two copies of the pane on the
same chart keep separate counters instead of trampling each other. Every read is wrapped in
Nz(), because a static variable that has never been set reads as Null.
The duty-cycle figure is the mean execution time divided by the refresh interval, expressed as a percentage. It is deliberately understated: it counts your formula only, not the quotation loading or the repaint around it. Treat it as a floor.
Status( "redrawaction" ) reports whether this particular execution came from the timer or
from you, and Status( "ThreadID" ) names the thread it ran on — useful when several panes
are running and you want to confirm they really are separate.
Key functions
Section titled “Key functions”GetPerformanceCounter( True ) resets the counter; the same call without an argument reads
it. GetChartID() returns the chart’s identifier, which is what makes the static-variable
names unique per pane. ParamTrigger() puts a button in the Parameters dialog: it returns 1
for the single execution following a press and 0 afterwards, which is exactly the semantics a
reset needs. StaticVarSet() and StaticVarGet() carry the running totals across
executions.
Expected result
Section titled “Expected result”Apply the pane to any chart on any database. The title updates on the interval you set, the execution time reads as a fraction of a millisecond with the extra-pass dial at zero, and the run count climbs steadily. Turn the dial up and both the per-execution time and the duty percentage rise with it. Press the reset button in the Parameters dialog and the run count returns to one.
Test it
Section titled “Test it”Set the extra passes to zero, note the mean over a minute, then set them to fifty and note it again. The difference is the cost of fifty additional moving averages over your loaded history, and it should scale roughly linearly with the dial. If it does not, either your bar count is small enough that overhead dominates, or the counter caveat above applies to your machine.
Then open a second copy of the pane on the same chart. Both should keep independent counts, and both should report different thread identifiers.
Common errors
Section titled “Common errors”Reading GetPerformanceCounter() without resetting first produces a number in the millions
with no useful precision in its low digits. Measuring a block that takes less than about
0.05 ms measures the two calls rather than the block. Reusing one static-variable name across
panes produces statistics that jump between panes. And forgetting Nz() on the first read
gives Null totals that propagate silently through every subsequent calculation.
Extension
Section titled “Extension”Add a second measured block around only the loop, so the title reports the fixed cost and the variable cost separately. Then reduce the pane’s bar count in the chart settings and watch how much of the cost was the number of bars rather than the number of indicators — which is usually the answer nobody expects.
Distinguishing a timer refresh from any other
Section titled “Distinguishing a timer refresh from any other”Status( "redrawaction" ) returns 0 for a regular refresh and 1 for one triggered by
RequestTimedRefresh(). The reference page offers this specifically as the way to tell them
apart, and it is the guard that makes side effects survivable in a self-refreshing pane.
Fragment — not a complete formula
RequestTimedRefresh( 1 );
// Only act on the timer's schedule. A mouse-driven repaint, a scroll or a// parameter change must not be allowed to look like the passage of time.if( Status( "redrawaction" ) == 1 ){ _TRACE( "timer refresh at " + Now() );}A related function, RequestMouseMoveRefresh(), added in 6.30, re-executes the formula when
the cursor moves over the chart area. It carries no example and no performance note of its
own, but the cost model is identical: every mouse movement re-runs the whole formula. It
belongs in hover readouts and crosshair panels, not in anything that polls for data.
When not to use timed refresh
Section titled “When not to use timed refresh”- On a chart of end-of-day data that changes once a day. The refresh will find nothing new and cost the same as one that does.
- On a heavy formula, at a short interval. Measure first. A formula taking 200 ms cannot usefully be asked to run every 100 ms.
- On a pane that lives on a sheet you do not display. It will not run at all, and the silence looks like working code.
- As a substitute for repeat scanning. If the job is to watch a universe rather than a symbol, the Analysis window’s repeat facility is the right tool, and Part 24 covers its costs.
- To drive anything that must happen at a precise moment. The timer is documented as approximate. Build on completed bars, not on wall-clock precision.
- On more panes than you actually read. The cheapest optimisation available is deleting a pane.
Doing this without a feed
Section titled “Doing this without a feed”There is nothing to work around here. RequestTimedRefresh() is documented to work
irrespective of the data source and the connection state, GetPerformanceCounter() and
Status() are available in both editions, and the cost meter above measures a formula, not a
feed.
That makes this the best place in the whole real-time track to do your tuning. Build the pane on your end-of-day database, measure it honestly, choose an interval you can defend, and only then point it at live data. Doing it the other way round means tuning a formula while the market is moving, which is the worst possible time to learn that your layout needs 30 seconds of processor time per minute.
If you want the pane to update against changing data without a subscription, run Bar Replay underneath it: replay advances the visible data for every symbol, and the pane will redraw against a moving series exactly as it would against a feed. Part 26 covers what that simulation does and does not reproduce.
RequestTimedRefresh( interval, onlyvisible ) re-executes an indicator pane on a schedule,
independently of any data source, aligned to second boundaries and accurate to roughly a
twentieth of a second. The onlyvisible default stops refreshes for minimised windows and
cannot rescue panes on inactive sheets, which do not exist until shown. Each refresh runs the
entire formula on its own thread, so the cost of a layout is panes multiplied by frequency
multiplied by execution time. GetPerformanceCounter() measures that execution time in
milliseconds, provided you reset it first and do not try to time anything shorter than its own
overhead. Status( "redrawaction" ) separates timer refreshes from every other kind, which is
what lets a self-refreshing pane hold a side effect safely.
Next, the three functions come together into a panel that has to behave correctly whether or not anything is streaming.
Check your understanding
Sources for this lesson
6 verified · checked 2026-08-31
- 01AmiBroker AFL Function Reference — RequestTimedRefreshamibroker.com/guide/afl/requesttimedrefresh.html2026-08-31
- 02AmiBroker AFL Function Reference — RequestMouseMoveRefreshamibroker.com/guide/afl/requestmousemoverefresh.html2026-08-31
- 03AmiBroker AFL Function Reference — GetPerformanceCounteramibroker.com/guide/afl/getperformancecounter.html2026-08-31
- 04AmiBroker AFL Function Reference — Statusamibroker.com/guide/afl/status.html2026-08-31
- 05AmiBroker User's Guide — Multi-threadingamibroker.com/guide/h_multithreading.html2026-08-31
- 06AmiBroker User's Guide — Preferences§ Intradayamibroker.com/guide/w_preferences.html2026-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.