Logic, Conditionals and IIf()
You now have Boolean arrays. This lesson joins them together, chooses values from them, and settles a question that costs people a surprising amount of time: whether AFL’s logical operators are the ones they are used to from other languages.
The short answer is no, and the documentation is unusually clear about it.
AND, OR and NOT
Section titled “AND, OR and NOT”AFL’s logical operators are the words AND, OR and NOT. They are documented in the
official operator tables, they work element-wise on arrays like everything else, and they
produce 1 and 0.
Fragment — not a complete formula
Trend = Close > MA( Close, 200 );Liquid = MA( Volume, 50 ) > 500000;
Candidate = Trend AND Liquid;Either = Trend OR Liquid;Excluded = NOT Liquid;AND is true where both operands are true on the same bar. OR is true where at least one
is. NOT is true where its operand is false. All three answer once per bar, and the result is
an array exactly as long as its inputs.
AND, OR and NOT, bar by bar
| Bar | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|---|
Trend | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 |
Liquid | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 0 |
Trend AND Liquid | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 0 |
Trend OR Liquid | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
NOT Liquid | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 |
What about &&, || and !?
Section titled “What about &&, || and !?”If you have written C, JavaScript, Java or a dozen other languages, your fingers will type
&& and || without asking you. Here is what an exhaustive check of the official
documentation establishes, and it is worth being precise because the internet is not.
AND,ORandNOTare the documented logical operators. They appear in the operator tables in the AFL Reference Manual with defined precedence. Use these.&and|are documented, but they are bit-wise operators, not logical ones. Their job in AFL is combining flag constants:styleLine | styleThick, and every stop-type and composite-flag combination you will meet later. They also sit at a different precedence level from the logical operators, which is a live trap in mixed expressions.&&appears in official sample code but in no official operator table. The backtesting tutorial’s custom-stop example containsif( priceatbuy == 0 && Buy[i] ). So the parser evidently accepts it. It is nevertheless undocumented, which means its precedence and its behaviour are not specified anywhere you can point at.||and a unary!appear nowhere in the User’s Guide, in the AFL language reference, or in any of the 448 function reference pages. There is no documented basis for using them.
The course’s position follows from that: write AND, OR and NOT. They are documented,
their precedence is published, and they read better in a language whose expressions describe
market conditions. Uptrend AND NOT Quiet says what it means; Uptrend && !Quiet does not
say it any better and rests on documentation that does not exist.
Precedence: the mistake AmiBroker documents itself
Section titled “Precedence: the mistake AmiBroker documents itself”AND, OR and NOT all bind looser than the comparison operators, which is convenient:
a > b AND c > d groups as ( a > b ) AND ( c > d ) with no help needed.
But AND binds tighter than OR, and that produces mistake number two on AmiBroker’s own
Common Coding Mistakes list. The guide even quotes the user’s complaint that led to it. The
intention was: buy when either the close is above its average or the close jumped ten per
cent, but only when volume is above its average.
Fragment — not a complete formula
// WRONG - and it compiles, runs, and produces plausible-looking signals.Buy = Close > MA( Close, 10 ) OR Close == 1.1 * Ref( Close, -1 ) AND Volume > MA( Volume, 10 );Because AND binds tighter, that reads as ( Close > MA( Close, 10 ) ) OR ( ( Close == 1.1 * Ref( Close, -1 ) ) AND ( Volume > MA( Volume, 10 ) ) ). The volume filter applies to only one
of the two branches, so the system buys whenever the first condition alone is true, volume
filter or no volume filter. The fix is parentheses:
Fragment — not a complete formula
// CORRECTBuy = ( Close > MA( Close, 10 ) OR Close == 1.1 * Ref( Close, -1 ) ) AND Volume > MA( Volume, 10 );Two more precedence facts worth carrying:
NOTbinds looser than every comparison, soNOT a > bmeansNOT ( a > b ). That is what an English reader expects, and the opposite of C, where!binds very tightly.&and|bind tighter thanNOT,ANDandORbut looser than==and!=. AFL’s ordering here differs from C’s. Never mix bit-wise and logical operators in one expression without parentheses.
IIf(): choosing a value per bar
Section titled “IIf(): choosing a value per bar”IIf( EXPRESSION, TRUE_PART, FALSE_PART ) is how you pick a different value on every bar
without a loop. It walks every bar, tests the condition there, and takes the matching element
of the second or third argument.
Result = IIf( Close <= BeginValue( Open ), Close, Open )
| Bar | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
Open | 1.23 | 1.24 | 1.21 | 1.26 | 1.24 | 1.29 | 1.33 | 1.32 | 1.35 | 1.37 |
Close | 1.22 | 1.26 | 1.23 | 1.28 | 1.25 | 1.25 | 1.31 | 1.30 | 1.32 | 1.28 |
Close <= BeginValue( Open ) | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Result | 1.22 | 1.24 | 1.23 | 1.26 | 1.24 | 1.29 | 1.33 | 1.32 | 1.35 | 1.37 |
Three properties of IIf() that its documentation states explicitly, and that each cause
trouble when forgotten.
It is a function, not flow control. It returns a value. You assign that value. This is mistake number three on the official list:
Fragment — not a complete formula
IIf( Close > 10, Result = 7, Result = 9 ); // WRONGResult = IIf( Close > 10, 7, 9 ); // CORRECTThe wrong form also triggers Warning 501, “Assignment within conditional”, because AmiBroker recognises the pattern.
It always evaluates both branches. Even though only one is returned, both the true part and the false part are computed for every bar. There is no short-circuit. If one branch is an expensive calculation you thought was being skipped, it is not, and if one branch has a side effect, that side effect happens.
It returns numbers and arrays, never strings. Mistake number four on the official list is
Variable = IIf( Condition, "Text 1", "Text 2" );. For text you want WriteIf(), which
returns a string — but note that WriteIf() returns a single string, evaluated at the
selected bar, not one string per bar. Despite the name it does not write anything anywhere;
AmiBroker’s author describes it as “just TextIIF”.
Nesting IIf()
Section titled “Nesting IIf()”Two outcomes come free. For three or more, nest the next IIf() in the false part:
Fragment — not a complete formula
PriceColour = IIf( Uptrend, colorGreen, IIf( Downtrend, colorRed, colorLightGrey ) );Read it as a ladder: if uptrend, green; otherwise, if downtrend, red; otherwise grey. The
indentation above is worth copying — beyond about three levels a nested IIf() becomes
genuinely hard to read, and that is the point at which to reconsider whether the classification
should be built up from named arrays instead.
This is also the recommended alternative to arithmetic tricks. AmiBroker’s own Knowledge Base
shows Shape = Buy * shapeUpArrow + Sell * shapeDownArrow; as an example of what goes wrong —
on a bar where both are true, the two constants add to a third one — and gives the nested
IIf() as the safe form.
if/else is a different thing entirely
Section titled “if/else is a different thing entirely”AFL also has an if/else statement, and it is not a substitute for IIf(). It executes one
block or another once, for the whole formula run. Its condition must therefore be a single
value.
Fragment — not a complete formula
// WRONG - Close > Open is an array. Error 6.if( Close > Open ) PriceColour = colorGreen;else PriceColour = colorRed;
// CORRECT - one decision per bar, made by a function that works on arrays.PriceColour = IIf( Close > Open, colorGreen, colorRed );Error 6, “Condition in IF/WHILE/FOR must be Numeric or Boolean”, is what you get for the first version, and the official explanation is worth remembering because it makes the reason obvious rather than arbitrary: there is no way to decide whether to run the block “if for example the array was [True,True,False,…,False,True]”. A string condition gives Error 7.
So when is if right? When the decision genuinely applies to the whole run:
Fragment — not a complete formula
if( BarCount < 250 ){ _TRACE( "This symbol has too little history for the 250-bar study." );}BarCount is one number, so the question has one answer. That is the shape of every
legitimate if in ordinary AFL: checks on the amount of data, on which context the formula is
running in, on a parameter you set yourself.
| You want | Use |
|---|---|
| A different value on each bar | IIf() |
| A different string to display | WriteIf() |
| A decision that applies to the whole run | if / else |
| A decision based on one bar of an array | if( SomeArray[ i ] ) inside a loop |
When a real loop is justified
Section titled “When a real loop is justified”Lesson five made the case against loops on performance grounds: the official measurement is that they run between ten and fifty times slower than equivalent array code. That is the default. There is a genuine exception.
A loop is justified when a bar’s value depends on the value the same calculation produced for the previous bar, and no built-in function expresses the relationship. This is called a recursive or path-dependent calculation, and no combination of element-wise operators can express it, because element-wise operators cannot see their own output.
The official example is a variable-period exponential average. AmiBroker publishes both forms:
the array form uses AMA( array, factor ), which is recursive internally, and the loop form
looks like this:
Pseudocode — not valid AFL
set the first element of the result to the first close
for each bar from 1 to the last bar: factor = 2 / ( period at this bar + 1 ) result at this bar = factor * close at this bar + ( 1 - factor ) * result at the previous barNote what makes it irreducible: result at the previous bar is a value this same loop
computed a moment ago. Trailing stops that ratchet, position-state machines that must know
whether you are already in a trade, and anything that must “remember” across bars fall into
the same category. Part 9 shows how far the built-in functions get you before a loop becomes
necessary, and the answer is: surprisingly far.
If you do write one, four rules from the official performance guidance:
- Bound it with
BarCount, never a hard-coded number.for( i = 0; i < BarIndex(); i++ )is mistake number six on the official list —BarIndex()is an array and cannot control a loop. - Move every array calculation out of the loop. Calling
MA( C, 10 )inside the body recomputes the whole average once per bar. - Inside the loop, use scalars, or index single array elements with
[ ]. - Start the index at 1 if the body reads
[ i - 1 ], orC[ -1 ]will raise Error 10 on the first iteration.
A worked classification
Section titled “A worked classification”This formula does everything the lesson covers: builds three named Boolean arrays, combines
them with parentheses that matter, and uses a nested IIf() to colour every bar according to
which state it is in.
Complete runnable AFL
// regime-colour.afl// Part 8 - Logic, Conditionals and IIf()//// Purpose: combine several Boolean arrays with AND, OR and NOT, then use// IIf() to choose a different colour for every bar without writing// a single loop.// Assumes: any symbol and interval. The three states below are definitions// chosen for teaching, not recommendations - a different fast/slow// pair would classify the same chart differently.// Apply: Formula Editor -> Apply indicator.
_SECTION_BEGIN( "Regime Colour" );
// ---------------------------------------------------------------------------// Settings// ---------------------------------------------------------------------------
FastPeriod = 20;SlowPeriod = 100;QuietPercent = 1.5; // ATR below this percentage of price counts as "quiet"
// ---------------------------------------------------------------------------// Ingredients// ---------------------------------------------------------------------------
FastMA = MA( Close, FastPeriod );SlowMA = MA( Close, SlowPeriod );AtrPercent = 100 * ATR( 20 ) / Close;
// ---------------------------------------------------------------------------// Three named states, each a Boolean array - one answer per bar// ---------------------------------------------------------------------------
Uptrend = Close > FastMA AND FastMA > SlowMA;Downtrend = Close < FastMA AND FastMA < SlowMA;Quiet = AtrPercent < QuietPercent;
// The parentheses are load-bearing. AND binds tighter than OR, so without them// this would read as "Uptrend, or else (Downtrend and not quiet)", which is a// completely different rule that happens to compile perfectly.Tradeable = ( Uptrend OR Downtrend ) AND NOT Quiet;
// ---------------------------------------------------------------------------// Choosing a value per bar// ---------------------------------------------------------------------------
// IIf() walks every bar, tests the condition on that bar, and picks the// matching element. Nesting a second IIf() in the false part is how you get a// third outcome. Both branches are always evaluated, whatever the condition.PriceColour = IIf( Uptrend, colorGreen, IIf( Downtrend, colorRed, colorLightGrey ) );
// ---------------------------------------------------------------------------// Drawing// ---------------------------------------------------------------------------
Plot( Close, "Close", PriceColour, styleCandle );Plot( FastMA, "MA(" + FastPeriod + ")", colorBlue, styleLine );Plot( SlowMA, "MA(" + SlowPeriod + ")", colorDarkBlue, styleLine | styleThick );
// The shaded band marks the bars the combined rule accepted. Colour alone is// never the whole message, so the title states the same thing as numbers.Plot( Tradeable, "Tradeable", colorOrange, styleArea | styleOwnScale | styleNoLabel );
Title = StrFormat( "{{NAME}} {{DATE}} ATR %.2f%% of price Uptrend %g Downtrend %g Quiet %g Tradeable %g", AtrPercent, Uptrend, Downtrend, Quiet, Tradeable );
_SECTION_END();Goal. Classify every bar into one of three states — up, down, or neither — and mark separately which bars pass a combined rule that requires a direction and enough movement to be worth acting on. It is a classifier, not a trading system: it says what kind of bar this is, and nothing about what to do next.
How it works. Three ingredients are computed first: a fast average, a slow average, and
volatility expressed as a percentage of price, so that the same threshold means something
comparable on a five-unit share and a five-hundred-unit one. Three named Boolean arrays follow.
Tradeable combines them, and its parentheses are the whole point: without them the rule would
group differently and quietly stop applying the volatility filter to the up case. The nested
IIf() produces a colour array, which is then handed to Plot() as its third argument —
Plot() accepts either a single colour or a whole array of colours, one per bar.
Key functions. IIf( EXPRESSION, TRUE_PART, FALSE_PART ) selects per bar and always
evaluates both parts. ATR( period ) is average true range, covered properly in Part 6; here
it is only a stand-in for “how much this instrument moves”.
Test it. Pick a bar where the band is absent but the candle is green. The title should show
Uptrend 1, Quiet 1, Tradeable 0. That is the volatility filter doing its job, and
checking it on a specific bar is how you know the parentheses are grouping as intended. Then
delete the parentheses around ( Uptrend OR Downtrend ), re-apply, and find a bar where
Tradeable changed. That bar is the official mistake number two, live on your own chart.
Common errors. Bars coloured grey everywhere usually means the two average periods are too
close together on the interval you are using. An orange band covering every single bar means
QuietPercent is set below anything the instrument ever reaches — check the ATR percentage in
the title and pick a threshold from what you actually see. If applying the formula produces
Error 6, you have written if where you meant IIf.
Extension. Add a fourth state for “in transition” — bars where the fast average is above
the slow one but the close is below the fast one — and give it its own colour by adding one
more level to the nested IIf(). Then notice how much harder the nested version has become to
read, and consider whether a table of named conditions would serve better. That tension is
real, and Part 10 returns to it when the classifier becomes a proper indicator.
AND, OR and NOT are AFL’s documented logical operators and work element-wise like
everything else; & and | are bit-wise flag operators at a different precedence; &&
appears in one official example but in no operator table, and || and ! appear nowhere at
all. AND binds tighter than OR, which is AmiBroker’s own second-most-common mistake, and
naming each condition then combining the names is the habit that prevents it.
IIf() chooses a value per bar, returns arrays never strings, always evaluates both branches,
and is a function rather than flow control. if/else decides once for the whole run and
raises Error 6 when handed an array. And a loop is the right tool only when a bar’s value
depends on the previous bar’s result — a real category, but a much smaller one than it looks
from other languages.
The next lesson deals with the values that are neither true nor false.
Check your understanding
Sources for this lesson
8 verified · checked 2026-09-01
- 01AmiBroker User's Guide — AFL Reference Manual§ Logical operators and precedenceamibroker.com/guide/a_language.html2026-08-31
- 02AmiBroker User's Guide — Common Coding Mistakesamibroker.com/guide/a_mistakes.html2026-08-31
- 03AFL Function Reference — IIfamibroker.com/guide/afl/iif.html2026-08-31
- 04AFL Function Reference — WriteIfamibroker.com/guide/afl/writeif.html2026-08-31
- 05AmiBroker User's Guide — Error 6, condition must be numeric or booleanamibroker.com/guide/errors/6.html2026-09-01
- 06AmiBroker User's Guide — Performance tuningamibroker.com/guide/x_performance.html2026-08-31
- 07AmiBroker User's Guide — Understanding how AFL works§ New loopingamibroker.com/guide/h_understandafl.html2026-08-31
- 08AmiBroker User's Guide — AFL keywordsamibroker.com/guide/a_keywords.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.