Skip to content
Level 5 · Real-Time AmiBroker UserLessonPart 25 · page 1 of 330 min
30Minutes
8AFL functions
8Sources
StandardRequires
AFL functions taught here8

Alert Mechanisms and AlertIf()

There is exactly one AFL function for raising an alert, it has six arguments, and the official function reference introduces it with an unusual warning: “AlertIf is not mindless function, it contains internal logic (aka. finite state machine)”. That sentence is doing a lot of work. It means the function will sometimes decline to do what you told it, for reasons that are documented but not obvious, and that debugging a missing alert is a different exercise from debugging a wrong number.

By the end of this page you should be able to write the call correctly from memory, say what each of the four command forms does and what it costs, open the window the alerts land in, name the three places in Preferences that can silence your alert entirely, and choose between AlertIf() and the three simpler functions that sit underneath it.

Fragment — not a complete formula

AlertIf( BOOLEAN_EXPRESSION, command, text, type = 0, flags = 1+2+4+8, lookback = 1 );

It returns nothing. That is the first thing to internalise: there is no return value to test, so AFL cannot tell you whether an alert fired. Every verification technique in the next lesson is a way around that absence.

Argument Default What it is
BOOLEAN_EXPRESSION The condition. Non-zero triggers, zero does not. Only the lookback most recent bars are examined, not the whole array.
command Which action to take: empty string, SOUND, EMAIL or EXEC.
text The line written to the Alert Output window, the e-mail body, or the trailing argument to the EXEC target.
type 0 The alert type. Also the key the repeat-suppression logic uses.
flags 1+2+4+8 A bit field controlling output, beeping and de-duplication.
lookback 1 How many recent bars are checked.

The condition, and how little of it is read

Section titled “The condition, and how little of it is read”

lookback defaults to 1, so by default AmiBroker looks at the most recent bar only. Everything earlier in the array is ignored, however true it was. This is why an alert formula applied to a chart with ten years of crossovers on it does not produce ten years of alert lines: nine years and 364 days of them were never considered.

Raising lookback widens the window. It is occasionally useful — a scan you run once a week might want lookback = 5 so a Tuesday signal is not missed by a Friday scan — and it is frequently a mistake, because it resurrects old signals every time the formula runs.

It is also the argument you have to change the moment you adopt the completed-bar guard, and the reason is worth sitting with for a moment. That guard, which the next lesson covers properly, excludes the newest bar from the condition. lookback = 1 examines the newest bar and nothing else. Put those two together and the condition presented to AlertIf() is false by construction, every time. The two documented defaults cancel each other out, so a formula using the guard needs lookback of at least 2 to have anything left to look at. The next lesson returns to this with a way to prove it on your own installation.

command string What happens
"" (empty) The text is displayed in the Alert Output window.
"SOUND path-to-wav" The WAV file is played once.
"EMAIL" An e-mail is sent to the account configured in Preferences.
"EXEC path-or-URL optional-args" An external application, file or URL is launched. Any optional arguments are appended after the file name, and text is appended at the end.

The e-mail format is fixed and documented: the subject is Alert type_name ( type ) Ticker on Date/Time, and the body is your text. You do not get to change the subject line from AFL. If you want a subject you control, use SendEmail() instead — see below.

type Name
0 default
1 buy
2 sell
3 short
4 cover
5 and above “other”

Higher values are legal and are all named “other”. The type is not decoration. The documented internal state machine “prevents repeated signals of the same type from occurring”, and it stores the type of the last alert per symbol — so each symbol carries its own memory of what it last told you.

flags is a sum of four independent bits, all on by default.

Bit Effect when set
1 Display text in the Alert Output window
2 Beep via the computer speaker
4 Do not display repeated alerts having the same type
8 Do not display repeated alerts having the same date/time

Bits 4 and 8 are the de-duplication. The tutorial gives one explicit reason to turn them off — experimentation, where you want the same scan to keep reporting the same signal so you can see it working:

Fragment — not a complete formula

// Suppression off: every run reports the signal again.
AlertIf( Condition, "", "Text", 1, 1+2 );

Clearing bit 1 while leaving the others is also useful: the Alert Output window stays quiet while the e-mail, sound or external action still fires.

What happens between your condition and your ears

  1. The formula executesA chart refresh, a Scan, an Exploration, an Interpretation update
  2. The last `lookback` bars are readEverything older in the array is ignored
  3. Is the source enabled?Tools -> Preferences -> Alerts, "Enable alerts from". If not, nothing happens and nothing is reported
  4. The state machine decidesPer symbol, keyed on alert type. Bits 4 and 8 of flags apply here
  5. The action runsWindow text, beep, WAV, e-mail or external program
Three of these five steps can swallow an alert without producing an error.

By default every alert generates text, and that text goes to the Alert Output window. It is the only durable record you get, so treat the text argument as a log line rather than a notification: put the symbol, the interval and the bar’s timestamp in it, because in three hours “breakout” on its own will tell you nothing.

The window carries a column identifying which part of AmiBroker raised each line — Automatic Analysis, Commentary, or one of your custom indicators. When you have several formulas alerting at once, that column is how you find the guilty one.

Two separate mechanisms make noise. Flag bit 2 produces a beep through the computer speaker and needs nothing configured. The SOUND command plays a WAV file once, and takes the path inline in the command string:

Fragment — not a complete formula

AlertIf( Sell, "SOUND C:\\Windows\\Media\\Ding.wav", "Audio alert", 2 );

Note the doubled backslashes. AFL string literals escape backslashes, so a single one will not survive. The alerts tutorial shows the escaped form correctly; the AlertIf function reference page shows the same example with the backslashes stripped out entirely by its own HTML rendering, which has misled a great many people into pasting a broken path. Only .WAV is documented — there is no MP3 support.

EMAIL sends to the account set under Tools -> Preferences -> Alerts. The documented authentication schemes are AUTH LOGIN, POP3-before-SMTP, CRAM-MD5 and LOGIN PLAIN. SSL — the scheme every major mail provider now requires — is not built in: since version 5.30 it needs a separate SSL add-on, downloaded and run before ticking the SSL box in Preferences.

A more robust pattern, if e-mail matters to you: send the alert to a local file or a messaging tool you already run, via EXEC, and let that tool handle delivery.

External actions, and where this course stops

Section titled “External actions, and where this course stops”

EXEC launches a program, a document or a URL. It uses the Windows ShellExecute mechanism, which is why URLs work and not only executables. The text argument is appended to the end of the command line, so the launched program receives your alert message as an argument.

This is the most powerful and the most dangerous of the four commands, for three reasons worth stating plainly. It runs with your Windows user’s privileges. It runs once per triggering execution, so a formula on a two-second refresh that mis-fires will launch a program repeatedly. And there is no AFL-visible result — AlertIf() returns nothing, so a failed launch is indistinguishable from a suppressed alert.

This is the section that saves the most time, because a silenced alert produces no error.

Alert generation is enabled per subsystem under Tools -> Preferences -> Alerts, in the “Enable alerts from” group. There are three checkboxes: Automatic Analysis, Commentary/Interpretation, and custom indicators. If the box for the context you are running in is clear, AlertIf() produces nothing at all — no line, no beep, no error, no clue.

That maps onto where you would actually deploy an alert:

  • A chart pane is a custom indicator. This is the natural home for a single-symbol, continuously refreshing alert, and it is what the project at the end of this part uses.
  • The Analysis window, running a Scan or an Exploration, is Automatic Analysis. This is the natural home for a many-symbol alert. The Analysis window has an “Auto repeat Scan/Explore” option with an interval field, where a plain number means minutes and 5sec or 5s means seconds — that is how a scan becomes a monitor.
  • Commentary and Interpretation can alert too, and this catches people out: an Interpretation window left open on a symbol will keep re-executing the formula and keep raising alerts you thought were coming from somewhere else.

Two contexts where alerting is a bad idea rather than impossible: a Backtest, where the formula runs over all history and your alert has nothing useful to say, and an Optimization, where it runs hundreds of times.

When you want an action without the state machine, the tutorial points at three direct functions. Each does one thing, unconditionally, every time the line executes.

Function Signature The catch
SendEmail() SendEmail( subject, message, ShowUI = False ) Asynchronous — it returns before the mail is sent, and returns nothing, so you cannot detect failure from AFL. No de-duplication.
PlaySound() PlaySound( filename ) Returns 1 on success, 0 on failure. .WAV only. No de-duplication.
ShellExecute() ShellExecute( filepath, arguments, parameters, showcmd = 1 ) Success is a return value greater than 32, per the Win32 convention. Testing if( ShellExecute(...) ) is true for error codes too.

“No de-duplication” is the whole trade. Put PlaySound() in a chart formula refreshing every second and it plays every second. Use these when you are managing repeat suppression yourself — with static variables, say — and use AlertIf() when you want AmiBroker’s.

Goal. Produce a working alert you can watch land in the Alert Output window within two minutes, on data you already have, so that the rest of this part has something concrete to argue about. It alerts on a moving-average crossover in both directions, with the two directions correctly separated by type.

Complete runnable AFL

alert-output-basics.afl
// alert-output-basics.afl
// Part 25 - Alert Mechanisms and AlertIf()
//
// A minimal but complete alerting formula: two events, two alert types, one
// completed-bar guard, and alert text you can still make sense of six hours
// later. Apply it to a chart pane, open Window -> Alert Output, and watch.
//
// Assumptions declared up front:
// - Any interval, including daily. No real-time feed and no Professional
// edition are needed to run it: AlertIf() carries no edition restriction
// and works perfectly well on end-of-day history.
// - Alerts reach the Alert Output window only if the context you run this
// from is ticked under Tools -> Preferences -> Alerts, in the
// "Enable alerts from" group. A chart pane counts as a custom indicator.
// - The Alert Output line is the only record of the alert that you will
// have, so the text carries the symbol, the interval and the bar stamp.
_SECTION_BEGIN( "Alert Output Basics" );
FastPeriod = Param( "Fast average", 20, 2, 200, 1 );
SlowPeriod = Param( "Slow average", 50, 5, 400, 1 );
FastAvg = MA( Close, FastPeriod );
SlowAvg = MA( Close, SlowPeriod );
// Events, not states. Cross() is true on the single bar where the relationship
// changed. FastAvg > SlowAvg would be true for every bar of the whole run.
GoLong = Cross( FastAvg, SlowAvg );
GoFlat = Cross( SlowAvg, FastAvg );
// The newest bar of a live or replayed chart is still being built, so a
// condition can turn true inside it and then turn false again before the bar
// closes. This is the guard the User's Guide gives for alerting only on a bar
// that can no longer change.
BarComplete = BarIndex() < LastValue( BarIndex() );
// DateTimeToStr() prints the BAR's own timestamp. Now() would print the moment
// the formula happened to execute, which is a different and less useful fact.
StampText = DateTimeToStr( LastValue( DateTime() ) );
WhereText = Name() + " " + Interval( 2 ) + " @ " + StampText;
// type 1 = buy, type 2 = sell. The two types MUST differ. AmiBroker's built-in
// repeat suppression keys on the alert type, so giving both events the same
// type would let one of them silence the other.
//
// The sixth argument is the lookback, and it is passed explicitly at 2 rather
// than left at its default of 1. AlertIf examines only the lookback most recent
// bars - and with a lookback of 1 that is precisely the bar BarComplete has
// just excluded, so nothing would ever fire. A lookback of 2 puts the most
// recent COMPLETED bar inside the examined window. The lesson explains why the
// two documented defaults conflict, and how to verify this on your own build.
//
// Flags stay at the default 1+2+4+8. Bit 8 - "do not display repeated alerts
// having the same date/time" - is what stops the same completed-bar signal
// being reported again on every refresh during the bar that follows it.
AlertIf( BarComplete AND GoLong, "", "LONG cross " + WhereText, 1, 1+2+4+8, 2 );
AlertIf( BarComplete AND GoFlat, "", "FLAT cross " + WhereText, 2, 1+2+4+8, 2 );
Plot( Close, "Close", colorDefault, styleCandle );
Plot( FastAvg, "MA fast", colorBlue, styleLine );
Plot( SlowAvg, "MA slow", colorRed, styleLine );
PlotShapes( GoLong * shapeUpArrow, colorGreen, 0, Low, -15 );
PlotShapes( GoFlat * shapeDownArrow, colorRed, 0, High, 15 );
Title = WhereText
+ "\nLong crosses in history: " + NumToStr( LastValue( Cum( GoLong ) ), 1.0 )
+ " Flat crosses: " + NumToStr( LastValue( Cum( GoFlat ) ), 1.0 )
+ "\nAlerts fire on completed bars only, so the newest bar never raises one.";
_SECTION_END();

Download alert-output-basics.afl71 lines

How it works. Four short sections. Two moving averages are computed. Cross() turns each of them into an event — true on the single bar the relationship changed — rather than the state FastAvg > SlowAvg, which would be true for the entire run. BarComplete excludes the newest bar, because on a live or replayed chart that bar is still forming. Finally, two AlertIf() calls fire with types 1 and 2, and text built from the symbol name, the interval and the bar’s own timestamp.

Key functions. BarIndex() returns the bar number as an array, so BarIndex() < LastValue( BarIndex() ) is true everywhere except the final bar — this exact expression is the completed-bar guard the User’s Guide gives. DateTimeToStr() converts a DateTime value into readable text; applied to LastValue( DateTime() ) it gives the stamp of the most recent bar. Interval( 2 ) returns the interval’s name as a string, which is useful in alert text but must never be compared as text, because it is translated in localised builds.

Expected result. Green up arrows and red down arrows on the crossovers, two moving average lines, and a title reporting how many of each occurred over the loaded history. The Alert Output window gains at most one line per run, because the lookback window is two bars wide and only the more recent completed one of those can satisfy the guard. The count in the title, which covers all history, will be far larger than the number of alert lines. That discrepancy is not a fault; it is the lookback window doing its job.

Test it. Three checks, in this order, on a symbol with plenty of history. Set the fast average to 5 and the slow to 10 so crossovers are frequent, and open Window -> Alert Output.

Is the path live? Temporarily change the GoLong call’s flags argument from 1+2+4+8 to 1+2, which switches the built-in suppression off, and refresh the chart a few times while the bar before the newest one is a long crossover. A line should appear on every refresh. If nothing appears, nothing later in this part can be interpreted, so stop and fix it here.

Is the suppression working? Restore 1+2+4+8 and refresh several more times. One line, then silence. That is bits 4 and 8 doing their job.

What does a silenced alert look like? Clear the “custom indicators” box under Tools -> Preferences -> Alerts, refresh again, and observe that absolutely nothing happens — no line, no beep, no error, no clue. Restore the box. Do this once deliberately and you will recognise the symptom instantly for the rest of your life with this program.

Common errors. Leaving lookback at its default alongside the completed-bar guard, which produces permanent silence. A path with single backslashes in a SOUND command, which plays nothing. An alert whose text carries no symbol name, which is useless in a multi-symbol log. An AlertIf() placed inside an if() block that is false in the context you are running in, which produces nothing and looks like a fault in AmiBroker.

Extension. Add a third alert of type 5 — which the documentation names “other” — that fires when the gap between the two averages, as a percentage of price, exceeds a Param() you add. You now have three types in play and can watch the per-type suppression behave independently.

AlertIf() is one function with six arguments, and four of them decide whether anything happens at all. lookback limits how much of the array is even read, and defaults to just the newest bar. type is not a label — it is the key the built-in suppression uses, stored per symbol. flags controls output, beeping and two kinds of de-duplication. The command string selects between the Alert Output window, a WAV file, e-mail and an external program, each with its own configuration burden and its own failure mode. And three checkboxes in Preferences can make the whole thing produce nothing, without an error.

None of this needs a subscription or the Professional edition. What it does need is the discipline of the next page: an alert that is correct in principle and fires forty times is not a working alert.

Check your understanding

Question 1. A chart formula contains `AlertIf( Close > MA( Close, 50 ), "", "Above average", 1 );` and the symbol has been above its 50-bar average for the last 30 bars. You refresh the chart once. How many bars does AmiBroker examine?
AlertIf( Close > MA( Close, 50 ), "", "Above average", 1 );
Show the answer and why

Answer: The most recent bar only, because lookback defaults to 1

The sixth argument, lookback, defaults to 1, so only the newest bar is considered. That is a separate mechanism from the repeat suppression in flags — it limits what is read, not what is reported. It is also why this alert would still be wrong: on every subsequent refresh the newest bar is still true.

Question 2. Which of these will stop an AlertIf() call from producing anything, with no error message? Select all that apply.
Show the answer and why

Answer: The relevant box is clear under Tools -> Preferences -> Alerts, "Enable alerts from", flags is set to 1+2+4+8 and an alert of the same type already fired for that symbol, The condition array is Null on the most recent bar

The first three all produce silence. A syntax error does not — AmiBroker reports it. That asymmetry is the point: alert failures are usually silent, so you need a positive test that the alert fired, not an absence of errors.

Question 3. You want a WAV file played but you do not want the line cluttering the Alert Output window. What do you change?
Show the answer and why

Answer: Clear bit 1 from flags, e.g. pass 2+4+8

Bit 1 is what routes text to the Alert Output window. Clearing it silences the window while the sound, e-mail or EXEC action still runs. Passing empty text would still produce an empty line, and lookback has nothing to do with output.

Question 4. Why can AFL not simply check whether an alert was actually raised?
Show the answer and why

Answer: Because AlertIf() returns nothing at all

AlertIf() is documented as returning nothing, so there is no value to test. The Alert Output window is indeed not readable from AFL either, but the absence of a return value is the direct answer, and it is why the next lesson verifies alert behaviour by counting signals in an Exploration rather than by asking the alert.

Sources for this lesson

8 verified · checked 2026-08-31

  1. 01AmiBroker User's Guide — Using formula-based alertsamibroker.com/guide/h_alerts.html2026-08-31
  2. 02AFL Function Reference — AlertIfamibroker.com/guide/afl/alertif.html2026-08-31
  3. 03AFL Function Reference — SendEmailamibroker.com/guide/afl/sendemail.html2026-08-31
  4. 04AFL Function Reference — PlaySoundamibroker.com/guide/afl/playsound.html2026-08-31
  5. 05AFL Function Reference — ShellExecuteamibroker.com/guide/afl/shellexecute.html2026-08-31
  6. 06AmiBroker User's Guide — Easy Alerts windowamibroker.com/guide/w_easyalerts.html2026-08-31
  7. 07AmiBroker User's Guide — Preferences§ Alerts tabamibroker.com/guide/w_preferences.html2026-08-31
  8. 08AmiBroker — Standard versus Professional edition comparisonamibroker.com/guide/versions.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.