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.
The call
Section titled “The call”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.
The four commands
Section titled “The four commands”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.
The types AmiBroker documents
Section titled “The types AmiBroker documents”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.
The flags
Section titled “The flags”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
- The formula executesA chart refresh, a Scan, an Exploration, an Interpretation update
- The last `lookback` bars are readEverything older in the array is ignored
- Is the source enabled?Tools -> Preferences -> Alerts, "Enable alerts from". If not, nothing happens and nothing is reported
- The state machine decidesPer symbol, keyed on alert type. Bits 4 and 8 of flags apply here
- The action runsWindow text, beep, WAV, e-mail or external program
The Alert Output window
Section titled “The Alert Output window”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.
Sound, beeps and the escaping trap
Section titled “Sound, beeps and the escaping trap”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.
E-mail, and the part that is dated
Section titled “E-mail, and the part that is dated”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.
Where alerts can and cannot run
Section titled “Where alerts can and cannot run”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
5secor5smeans 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.
The three simpler functions underneath
Section titled “The three simpler functions underneath”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.
A formula you can run right now
Section titled “A formula you can run right now”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// 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();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
Sources for this lesson
8 verified · checked 2026-08-31
- 01AmiBroker User's Guide — Using formula-based alertsamibroker.com/guide/h_alerts.html2026-08-31
- 02AFL Function Reference — AlertIfamibroker.com/guide/afl/alertif.html2026-08-31
- 03AFL Function Reference — SendEmailamibroker.com/guide/afl/sendemail.html2026-08-31
- 04AFL Function Reference — PlaySoundamibroker.com/guide/afl/playsound.html2026-08-31
- 05AFL Function Reference — ShellExecuteamibroker.com/guide/afl/shellexecute.html2026-08-31
- 06AmiBroker User's Guide — Easy Alerts windowamibroker.com/guide/w_easyalerts.html2026-08-31
- 07AmiBroker User's Guide — Preferences§ Alerts tabamibroker.com/guide/w_preferences.html2026-08-31
- 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.