Knowing Where You Are: Status() and Execution Context
The same .afl file can be drawn as a chart, sent to a scan, used as an Exploration,
backtested, optimised, checked for syntax errors, profiled, rendered into a tooltip and
turned into interpretation text. AmiBroker does not ask your permission before doing any of
those things, and by default your formula has no idea which one is happening.
That is fine until the formula does something that only makes sense in one of them. Then
you need Status( "action" ), and you need to know what it does not tell you.
The problem, made concrete
Section titled “The problem, made concrete”Consider a formula that plots price, sets Buy and Sell, fills an Exploration table and
raises an alert when a level is crossed. Written without a context test, here is what
happens to it:
- In a chart pane it draws, and — if you have added a timed refresh — it re-executes every few seconds, all day. The alert fires on every one of those executions unless something stops it.
- In a scan the drawing calls are wasted work on every symbol in the universe. If the scan runs over three thousand tickers, so does the expensive per-bar loop you wrote because it was fast enough on one chart.
- In an optimisation the formula runs once per parameter combination per symbol. An unguarded alert or e-mail in that path will produce thousands of them, and an unguarded static-variable write will produce a race between threads.
- In a syntax check AmiBroker executes the formula to verify it. Anything with a side effect happens then too, at the moment you press the Verify button in the editor.
None of those is exotic. The last one catches people who put an order-placement call in a formula and discover the editor’s syntax check has run it.
One file, many callers
- Chart paneOne symbol, repeated execution, drawing allowed
- ScanMany symbols, reads Buy / Sell / Short / Cover
- ExplorationMany symbols, reads Filter and the AddColumn calls
- Backtest and optimisationMany symbols, many parameter sets, no user watching
- Syntax check, profile, tooltip, interpretationExecuted without you asking for a result
Status(“action”) and its six codes
Section titled “Status(“action”) and its six codes”Status( "statuscode" ) returns run-time information about the analysis engine. It has
been in AFL since version 3.650, and the "action" code is the one that answers “who is
asking”.
| Value | Constant | Meaning |
|---|---|---|
| 1 | actionIndicator |
An indicator or chart pane is being repainted |
| 2 | actionCommentary |
Commentary |
| 3 | actionScan |
An Analysis scan |
| 4 | actionExplore |
An Analysis exploration |
| 5 | actionBacktest |
Backtest — and optimisation, code check and profile |
| 6 | actionPortfolio |
The portfolio phase of a portfolio backtest |
Always compare against the named constant, not the number. The numbers appear here so that
you can read other people’s code, not so that you can write if( Status( "action" ) == 3 ).
Row 5 is the one to remember. The reference page states that the value of actionBacktest
is used in other contexts as well, “like code check and profile”, and that the codes were
deliberately left unchanged for backward compatibility with formulas already written. So a
branch guarded by actionBacktest runs during a syntax check. Keep that branch free of
anything you would not want to happen when you press Verify.
Status(“ActionEx”), and the trap inside it
Section titled “Status(“ActionEx”), and the trap inside it”Version 5.20 added Status( "ActionEx" ) for cases where five overloaded values are not
enough. Its first five codes match Status( "action" ) but with a narrower meaning:
actionCommentary there means commentary only, not interpretation and not a tooltip; and
actionBacktest there means a backtest only, with optimisation given its own codes.
The documented list, in the order the page prints it, continues past the first six with three reserved slots, then codes for the “Show arrows” command, the Parameters dialog, the editor’s syntax check, optimisation setup, an optimisation backtest, an optimisation portfolio phase, a tooltip, interpretation, and a reserved initialisation code.
Why a chart and a scan need different code
Section titled “Why a chart and a scan need different code”It is tempting to treat the context test as a tidiness feature. It is not. Four things genuinely differ, and each of them changes what correct code looks like.
Cardinality. A chart pane is one symbol. A scan is the whole universe. Work that costs three milliseconds is invisible on a chart and adds nine seconds to a three-thousand-symbol scan — before threading, and threading is capped at two per Analysis window in the Standard edition against thirty-two in Professional.
Output surface. A chart reads Plot(), Title, Tooltip and the low-level graphics
calls. An Exploration reads Filter and the AddColumn() family. A scan reads Buy,
Sell, Short and Cover. A backtest reads those four plus the price arrays and the
position-sizing variables. Setting a variable the current context does not read is not an
error; it is just work nobody will look at.
Repetition. A chart pane with a timed refresh executes on a schedule for as long as it is visible. A scan executes when you press Scan, or on the Analysis window’s repeat interval. An optimisation executes once per parameter combination. Any side effect — an alert, an e-mail, a sound, a static variable, a file — needs to know which of those it is living inside.
What “the last bar” means. Intraday, the last bar is still forming. A condition can go true, then false, inside a single bar as the price moves. On a chart that is honest: you are watching it happen. In a scan it produces a signal that later disappears, and the alerts tutorial gives the standard remedy — test only completed bars:
Fragment — not a complete formula
// True on every bar except the one that is still being built.BarComplete = BarIndex() < LastValue( BarIndex() );
Buy = BarComplete AND Cross( Close, MA( Close, 20 ) );This does not change the definition of the signal. It changes whether an unconfirmed one is reported, which is a different thing and belongs in the context that reports.
Other Status() keys worth having
Section titled “Other Status() keys worth having”The "action" code is the headline, but the same function answers a dozen other useful
questions. These are the ones that come up most in real-time and Analysis work.
| Code | What it returns |
|---|---|
"redrawaction" |
0 for a regular refresh, 1 for one triggered by RequestTimedRefresh() |
"stocknum" |
Ordinal number of the symbol currently being analysed |
"barinrange" |
1 when the current bar is inside the Analysis From–To range |
"firstbarinrange", "lastbarinrange" |
True on the first and last bar of that range |
"firstbarintest", "lastbarintest" |
The same, but from the last backtest or optimisation, unaffected by intermediate scans |
"rangefromdate", "rangetodate" |
The Analysis range as DateNums |
"barvisible" |
Indicator mode only: 1 when the bar is visible in the current view |
"firstvisiblebar", "lastvisiblebar" |
Indicator mode only; may include blank future bars |
"pxwidth", "pxheight" |
Pixel size of the chart pane, for low-level graphics |
"axisminy", "axismaxy" |
Bottom and top of the Y axis |
"timeshift" |
Database timeshift in seconds (version 5.60) |
"lastbarend" |
DateTime of the end of the last bar; a 5-minute bar at 9:00 ends 9:04:59 |
"lastbartimeleft" |
Seconds until the current last bar completes |
"lastbartimeleftrt" |
The same, measured from the most recent real-time stream update rather than from Now() |
"lastrtupdate" |
DateTime of the last update sent by the real-time plugin |
"ThreadID" |
The thread this execution is running on |
Three caveats travel with the timing codes, all stated on the same page. "lastbarend" and
"lastbartimeleft" work for time-based bars only — they are meaningless on tick,
n-volume or n-tick charts. "lastbartimeleft" needs the database timeshift set correctly,
because otherwise it produces a countdown that looks plausible and is wrong. And the two
real-time codes depend on the plugin sending correct update stamps: the page notes that most
data sources send odd timestamps at weekends, and that the IQFeed plugin sends update stamps
only inside regular trading hours, so a countdown built on them misbehaves outside those
hours.
"redrawaction" deserves its own sentence, because it is the pairing that makes timed
refresh safe. It answers “did the timer cause this execution, or did I?”, which is exactly
the question a side effect needs answered. The RequestTimedRefresh() page offers it as a
hint for that purpose, and the next lesson uses it.
A formula that knows where it is
Section titled “A formula that knows where it is”A scaffold you can start from: one file that draws a sensible chart, produces a sensible Exploration table, reports scan signals on completed bars only, and stays inert during a syntax check — with the trading logic written exactly once, so that no two contexts can disagree about it.
The complete formula
Section titled “The complete formula”Complete runnable AFL
// context-aware-template.afl// Part 23 - Knowing Where You Are: Status() and Execution Context//// One file, four jobs. The same formula draws a chart, fills an Exploration// table, reports scan signals and behaves itself in a backtest, because it// asks Status("action") which of those it is doing before it does anything// context-specific.//// The rule it follows: compute the SIGNALS once, in code that is identical// everywhere, then branch only on OUTPUT and on the small number of// adjustments a context genuinely requires. A formula that computes different// numbers in a chart and in a scan is a formula whose chart is lying to you.//// Assumptions:// - Any database, any interval, no data feed required. The trading logic is// a deliberately plain moving-average crossover so that nothing distracts// from the context handling. It is a scaffold, not a strategy.// - Status("action") returns 5 for a backtest, an optimisation, a syntax// check and a profiling run alike. That is documented, and it is why the// backtest branch below does nothing that would be harmful to do during a// syntax check.// - Plot() is called only when Status("action") == actionIndicator, which is// the check the Status() reference page explicitly sanctions. Do not// gate Plot() on Status("actionex") - the same page warns against it.
_SECTION_BEGIN("Context-aware template");
FastPeriod = Param( "Fast MA period", 20, 2, 200, 1 );SlowPeriod = Param( "Slow MA period", 50, 5, 400, 1 );
//----------------------------------------------------------------------------// 1. Shared logic. Byte-for-byte identical in every context, by construction.//----------------------------------------------------------------------------FastLine = MA( Close, FastPeriod );SlowLine = MA( Close, SlowPeriod );
TrendUp = FastLine > SlowLine;EntryBar = Cross( FastLine, SlowLine );ExitBar = Cross( SlowLine, FastLine );
Buy = EntryBar;Sell = ExitBar;
//----------------------------------------------------------------------------// 2. Which context is this?//----------------------------------------------------------------------------Context = Status( "action" );
// Human-readable, for the chart title and for the Exploration table. Keeping// this here rather than inside a branch means every context can report it.if( Context == actionIndicator ) ContextName = "chart pane";else if( Context == actionCommentary ) ContextName = "commentary";else if( Context == actionScan ) ContextName = "scan";else if( Context == actionExplore ) ContextName = "exploration";else if( Context == actionPortfolio ) ContextName = "portfolio phase";else ContextName = "backtest, optimisation, syntax check or profile";
//----------------------------------------------------------------------------// 3. Output. Only this part depends on the context.//----------------------------------------------------------------------------if( Context == actionIndicator ){ // One symbol, a person looking at it, drawing allowed, and the pane may be // re-executed many times a minute. Plot( Close, "Close", colorDefault, styleCandle ); Plot( FastLine, StrFormat( "MA(%g)", FastPeriod ), colorBlue, styleLine ); Plot( SlowLine, StrFormat( "MA(%g)", SlowPeriod ), colorRed, styleLine );
PlotShapes( EntryBar * shapeUpArrow, colorBrightGreen, 0, Low, -18 ); PlotShapes( ExitBar * shapeDownArrow, colorRed, 0, High, 18 );
// Status("redrawaction") separates a refresh you caused from one the timer // caused. Anything with a side effect belongs behind this test. if( Status( "redrawaction" ) == 1 ) RedrawSource = "timer"; else RedrawSource = "user or data";
_N( Title = Name() + " - " + Interval( 2 ) + "\nContext: " + ContextName + ", redraw from " + RedrawSource + StrFormat( "\nFast %g, slow %g, trend up: %g", FastPeriod, SlowPeriod, LastValue( TrendUp ) ) );}
if( Context == actionScan ){ // A scan reads Buy/Sell/Short/Cover and reports the symbols where they are // true. The one adjustment worth making is intraday: the last bar is still // forming, so a condition can go true and false again inside it. Report // completed bars only. The DEFINITION of the signal has not changed - the // scan has simply refused to report an unconfirmed one. BarComplete = BarIndex() < LastValue( BarIndex() );
Buy = Buy AND BarComplete; Sell = Sell AND BarComplete;}
if( Context == actionExplore ){ // Many symbols, a table, no drawing. Filter decides which rows appear. Filter = EntryBar OR ExitBar OR Status( "lastbarinrange" );
AddTextColumn( ContextName, "Context", 1.0, colorDefault, colorDefault, 120 ); AddColumn( Close, "Close", 1.2 ); AddColumn( FastLine, "Fast", 1.2 ); AddColumn( SlowLine, "Slow", 1.2 ); AddColumn( IIf( TrendUp, 1, 0 ), "Trend up", 1.0 ); AddColumn( IIf( EntryBar, 1, IIf( ExitBar, -1, 0 ) ), "Signal", 1.0 ); AddColumn( Status( "stocknum" ), "Symbol number", 1.0 );}
if( Context == actionBacktest OR Context == actionPortfolio ){ // A backtest, an optimisation, a syntax check and a profiling run all // arrive here, because Status("action") returns the same code for all of // them. So this branch stays free of side effects: no alerts, no e-mail, // no writes to static variables, nothing that would fire thousands of // times during an optimisation. BuyPrice = Close; SellPrice = Close;
SetOption( "InitialEquity", 100000 );}
_SECTION_END();How it works
Section titled “How it works”The file is in three parts and the order matters.
The shared logic comes first, above any context test. Two moving averages, a trend
state, and the two crossover events, assigned to Buy and Sell. Nothing in this block
knows or cares what is going to be done with it. That is the point: whatever context runs
the formula, these values are computed by the same lines.
The context test is a single call, stored in a variable. Calling Status( "action" )
once and reusing it is a small thing, but it also means the branch conditions all read
against the same value rather than against several separate calls.
The output branches follow. The chart branch draws and sets a title, and it is the only
branch that calls Plot() — guarded, as the documentation requires, by
Status( "action" ) == actionIndicator rather than by any ActionEx value. The scan branch
makes exactly one adjustment, restricting reporting to completed bars, and says in a comment
why that is a reporting decision and not a change of definition. The Exploration branch sets
Filter and builds columns, including the symbol’s ordinal number from "stocknum" so you
can see the universe being walked. The backtest branch sets execution prices and one option,
and deliberately contains nothing with a side effect, because a syntax check lands there too.
Key functions
Section titled “Key functions”Status( "action" ) returns the context code. Status( "redrawaction" ) distinguishes a
timer refresh from a user refresh, and appears here in the chart title so you can watch it
change. Status( "lastbarinrange" ) is used in the Exploration filter so that a symbol with
no signal still produces one row, which makes an empty result distinguishable from a broken
formula. BarIndex() with LastValue() gives the completed-bar test. SetOption() sets an
Analysis setting from inside the formula.
Expected result
Section titled “Expected result”Applied to a chart pane, you get candles with two averages, arrows on crossovers, and a title that names the context as “chart pane” and reports whether the redraw came from a timer or from you. Sent to Analysis as an Exploration, you get one row per symbol with a context column reading “exploration”. Run as a scan, you get the symbols where a crossover occurred on a completed bar. Nothing draws in the Analysis contexts, and nothing is computed differently between them.
Test it
Section titled “Test it”The test that matters is that the contexts agree. Pick one symbol and one date on which the chart shows an up arrow. Run the Exploration over that symbol with a range covering that date and confirm the signal column reads 1 on the same bar. Then run the scan over the same symbol and range and confirm the same date appears in the results.
Then break it deliberately: move the two MA() lines inside the chart branch and re-run the
Exploration. The columns go empty, because FastLine and SlowLine no longer exist outside
the chart context. That failure is loud and easy to spot. The dangerous version — computing
a different average inside each branch — fails silently, and the two contexts would simply
disagree with no error anywhere.
Common errors
Section titled “Common errors”Gating Plot() on Status( "actionex" ) produces a chart that blanks when the
Interpretation window opens, and it is explicitly warned against. Putting an alert or an
e-mail inside the backtest branch means it fires during a syntax check. Assuming
actionBacktest means “a backtest” leads to code that misbehaves during optimisation and
profiling, because all three share the value. Testing against the bare numbers instead of the
constants produces code that no one, including you in six months, can read.
Extension
Section titled “Extension”Add a fifth branch for actionCommentary that writes a plain-language summary of the
current state, and confirm from the Interpretation window that the commentary text and the
chart title agree. Then try to make them disagree by computing the trend separately in each
branch — and notice how much easier that was to do by accident than the correct version was
to write.
Running this without a feed
Section titled “Running this without a feed”Everything on this page works on an end-of-day database in the Standard edition.
Status() is not edition-restricted, the template needs no streaming data, and the four
contexts it distinguishes are the same four whether your bars arrive from a plugin or from a
CSV file you imported last year.
That is worth stating rather than assuming, because context handling is usually taught as a real-time subject. It is not. It becomes urgent in real time — a pane executing every second punishes careless side effects quickly — but the correctness argument applies to a purely historical workflow just as strongly.
Status( "action" ) tells a formula which part of AmiBroker is executing it, using six
documented codes with named constants. Code 5 covers backtest, optimisation, code check and
profile together, which is why a backtest branch must stay free of side effects.
Status( "ActionEx" ) gives finer detail, with the standing warning never to gate Plot()
on it. The same function also reports the redraw source, the Analysis range, the visible
bars, the pane’s pixel geometry, the database timeshift, several real-time timing values and
the current thread. The discipline that makes all of it useful is to compute the analysis
once and branch only on what is reported.
Next: how often a chart pane should re-run, and how to find out what one execution costs.
Check your understanding
Sources for this lesson
5 verified · checked 2026-08-31
- 01AmiBroker AFL Function Reference — Statusamibroker.com/guide/afl/status.html2026-08-31
- 02AmiBroker AFL Function Reference — RequestTimedRefreshamibroker.com/guide/afl/requesttimedrefresh.html2026-08-31
- 03AmiBroker User's Guide — Multi-threadingamibroker.com/guide/h_multithreading.html2026-08-31
- 04AmiBroker User's Guide — Using formula-based alertsamibroker.com/guide/h_alerts.html2026-08-31
- 05AmiBroker AFL Function Reference — GetRTDataamibroker.com/guide/afl/getrtdata.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.