Skip to content
Level 5 · Real-Time AmiBroker UserProjectPart 24 · page 3 of 360 min Professional edition Live feed
60Minutes
16AFL functions
11Sources
Professional + live feedRequires

This page needs the Professional edition and a real-time data feed. Every gated part of this course ships a Level A path that uses Bar Replay and historical data instead — look for it below.

AFL functions taught here16

Project: Intraday Breakout Scanner

You are going to build a working intraday scanner: a formula that examines a universe of symbols on an intraday interval, applies a session-aware opening-range breakout definition with a volume-pace gate and a daily trend gate, and produces a timestamped candidate list that refreshes on a timer. It is a substantial piece of AFL and it exercises everything in this part.

Before you write a line of it, read the next paragraph, because it is the most important thing on this page.

The pieces, and where each one comes from

  1. Session geometryMinutes since midnight, session open detection, opening-range window
  2. Opening rangeRunning extremes frozen with ValueWhen once the window closes
  3. Context gatesDaily trend and volume baseline from completed daily bars only
  4. TriggerCross above the range high plus a buffer, once per session via ExRem
  5. ReportOne row per candidate, carrying its own age and the definition that produced it
  6. RepeatAuto repeat Scan/Explore on an interval chosen from a measurement

Two files. The scanner produces the candidate list. A second, deliberately verbose audit formula reports every in-session bar with the state of each gate, and exists so that you can answer “why did this symbol not appear?” and, more importantly, so that you can run the look-ahead test in the validation section.

Time budget: about twenty minutes to get the scanner running, twenty on the validation procedure, and twenty on the extensions and the closing section. The validation is not optional garnish — it is the part of the project with the most transferable value.

Design decisions, and which of them are arbitrary

Section titled “Design decisions, and which of them are arbitrary”

Every scanner is a stack of choices. Writing them down before you code is what separates a definition from a preference you have forgotten you have.

Decision Value used Where it came from
Session 09:30 to 16:00, database clock A common equity session; must match your data
Opening range First 30 minutes Round number. Not tested
Breakout buffer 10% of the opening range Round number, chosen so a single tick through the level does not qualify
Volume pace gate 1.20 Round number. See the fragility discussion below
Daily trend Close above the 50-day moving average, previous completed day Round number, and the most conventional possible choice
Volume baseline 20 completed days Round number
Liquidity floor Previous day close x volume above 5,000,000 Round number, in the quote currency
Opening-range size band 0.30% to 6.00% of price Rejects ranges too tight to be meaningful and too wide to be orderly. Untested
Latest candidate 15:00 Avoids reporting candidates with almost no session left
One report per session ExRem against SessionOpen A design choice: the first crossing, not every crossing

Nine of those ten are round numbers. That is not laziness — it is the correct starting point for a technical exercise, because a threshold you have not measured should look obviously unmeasured. The failure mode to avoid is picking 1.17 instead of 1.20 because it produced a nicer list yesterday, and thereby fitting the scanner to a single day without noticing.

Session and time-zone handling, before you run anything

Section titled “Session and time-zone handling, before you run anything”

Four checks. Each takes a minute and each one, skipped, produces a scanner that runs cleanly and reports nonsense.

1. Which clock are your bar timestamps in? Open an intraday chart of a symbol you know and look at the first bar of a normal session. Does it carry the exchange’s opening time? If it does not, your database has a time shift, and the session constants in the formula must be written in your database’s clock rather than the exchange’s. Status("timeshift") reports the shift in seconds if you want it numerically.

2. Start or end stamping? Tools → Preferences → Intraday sets whether a bar carries the start or the end time of its interval. The formula assumes the documented default, START. If yours is set to END, every time comparison is one bar out and your opening range is computed over a window shifted by one bar.

3. Does your scan interval divide the opening-range length? Thirty minutes divides cleanly by 1, 5, 10 and 15 minutes. It does not divide by 7 or by 20. If it does not divide, the range window ends part way through a bar and the level you get is not the level you defined.

4. What is in your session? If pre-market and after-hours bars are present and not filtered out by File → Database Settings → Intraday Settings, they will be inside the daily compression that produces your trend gate and volume baseline, even though the InSession test excludes them from the opening range. That is not necessarily wrong, but it must be a decision rather than an accident.

Given a universe and an intraday interval, list the symbols that have crossed above the high of their session opening range by a defined margin, while a daily trend gate and a session-volume-pace gate were satisfied, reporting each candidate once per session with enough context to judge it and enough metadata to audit it later.

Complete runnable AFL

intraday-breakout-scanner.afl
// intraday-breakout-scanner.afl
// Part 24 - Project: Intraday Breakout Scanner
//
// WHAT THIS IS
// A repeatable intraday exploration that lists symbols whose price has crossed
// above the high of their session opening range, while a daily trend gate and
// a session volume-pace gate were both satisfied.
//
// WHAT THIS IS NOT
// Evidence of anything. Every threshold below was chosen because it is round,
// not because it was measured. There is no exit rule, no position sizing, no
// cost model and no test. A list of symbols that satisfied a definition is a
// list of symbols that satisfied a definition. Parts 27 to 31 are where a
// rule set is turned into something that can be tested and can fail.
//
// HOW TO RUN IT
// Formula Editor -> paste -> name it -> Send to Analysis
// Apply to: Filter, and choose a watch list you can actually stream
// Range: 1 recent day(s) (so today's whole candidate list rebuilds)
// Settings: Periodicity = the intraday interval you intend to scan
// Press Explore. To repeat it, open the Settings drop-down, tick
// "Auto repeat Scan/Explore" and set an interval (a plain number is minutes;
// type 5s or 5sec for seconds).
//
// With no live feed: run exactly the same thing over a historical intraday
// database with Range = From-To covering one past session, and then again
// under Tools -> Bar Replay. The two must agree. See the lesson.
//
// ASSUMPTIONS DECLARED UP FRONT
// - Intraday database; the scan periodicity divides ORMinutes exactly.
// - Bars stamped with the START of the interval (AmiBroker's default).
// - Session times are in the database's own clock, not necessarily exchange
// time. A configured time shift moves every threshold below.
// - Daily values are the daily compression of this database and inherit its
// Intraday Settings.
// - Volume is present and non-zero. On a symbol with no volume every pace
// test below is meaningless rather than merely wrong.
SetBarsRequired( sbrAll, sbrAll );
// ---------------------------------------------------------------------------
// Definition. Every number here is an assumption you are asked to change.
// ---------------------------------------------------------------------------
StartHour = 9; // session start, hour
StartMinute = 30; // session start, minute
EndHour = 16; // session end, hour
EndMinute = 0; // session end, minute
ORMinutes = 30; // length of the opening range
LastEntryHour = 15; // no candidate reported after this time
LastEntryMin = 0;
BufferFrac = 0.10; // breakout must clear the OR high by this fraction of
// the OR range, so that one tick through does not count
MinVolumePace = 1.20; // session volume pace required (see the lesson: this is
// NOT "20% above average volume")
TrendLen = 50; // daily moving average used as the trend gate
VolDays = 20; // completed days in the volume baseline
MinTurnover = 5000000; // previous day close x volume, in the quote currency
MinORRangePct = 0.30; // reject a range too tight to mean anything
MaxORRangePct = 6.00; // reject a range so wide the symbol is in disarray
// ---------------------------------------------------------------------------
// Session geometry, in minutes since midnight. TimeNum() is decimal-packed and
// cannot be used for arithmetic; Hour() and Minute() can.
// ---------------------------------------------------------------------------
BarLengthMin = Interval() / 60;
SessionStartMin = 60 * StartHour + StartMinute;
SessionEndMin = 60 * EndHour + EndMinute;
CutoffMin = 60 * LastEntryHour + LastEntryMin;
SessionMinutes = SessionEndMin - SessionStartMin;
BarMin = 60 * Hour() + Minute();
InSession = BarMin >= SessionStartMin AND BarMin < SessionEndMin;
PrevInSess = Nz( Ref( InSession, -1 ) );
SessionOpen = InSession AND NOT PrevInSess;
InOR = InSession AND BarMin < SessionStartMin + ORMinutes;
AfterOR = InSession AND BarMin >= SessionStartMin + ORMinutes;
// ---------------------------------------------------------------------------
// Opening range, frozen with past bars only.
// ---------------------------------------------------------------------------
ORHigh = ValueWhen( InOR, HighestSince( SessionOpen, High ) );
ORLow = ValueWhen( InOR, LowestSince( SessionOpen, Low ) );
ORRange = ORHigh - ORLow;
ORRangePct = IIf( Close > 0, 100 * ORRange / Close, Null );
// ORHigh carries yesterday's value until today's range has closed, so nothing
// below may look at it unless AfterOR is true.
ORUsable = AfterOR AND NOT IsNull( ORHigh ) AND ORRange > 0 AND
ORRangePct >= MinORRangePct AND ORRangePct <= MaxORRangePct;
// ---------------------------------------------------------------------------
// Context gates, all built from COMPLETED daily bars.
// ---------------------------------------------------------------------------
SessionVolume = SumSince( SessionOpen, Volume, True );
ElapsedMin = BarMin - SessionStartMin + BarLengthMin;
TimeFrameSet( inDaily );
BaselineVolume = Ref( MA( Volume, VolDays ), -1 );
DailyTrendUp = Ref( Close > MA( Close, TrendLen ), -1 );
TimeFrameRestore();
BaselineVolume = TimeFrameExpand( BaselineVolume, inDaily, expandFirst );
DailyTrendUp = TimeFrameExpand( DailyTrendUp, inDaily, expandFirst );
ExpectedVolume = BaselineVolume * ElapsedMin / SessionMinutes;
VolumePace = IIf( ExpectedVolume > 0, SessionVolume / ExpectedVolume, Null );
// Negative shift, so both of these are the previous COMPLETED daily bar.
PrevDayClose = TimeFrameGetPrice( "C", inDaily, -1 );
PrevDayVolume = TimeFrameGetPrice( "V", inDaily, -1 );
PrevTurnover = PrevDayClose * PrevDayVolume;
// ---------------------------------------------------------------------------
// Setup, trigger, and one report per session.
// ---------------------------------------------------------------------------
Setup = ORUsable AND
Nz( DailyTrendUp ) AND
Nz( VolumePace ) >= MinVolumePace AND
Nz( PrevTurnover ) >= MinTurnover AND
BarMin <= CutoffMin;
BreakLevel = ORHigh + BufferFrac * ORRange;
Trigger = Setup AND Cross( Close, BreakLevel );
// One report per symbol per session: ExRem suppresses further triggers until the
// next session opens.
Trigger = ExRem( Trigger, SessionOpen );
// Setting Buy lets the same file run under the Scan action, which is what Part 25
// attaches an alert to. There is deliberately no Sell, no stop and no exit: this
// file is not a strategy and must not be sent to the back-tester as one.
Buy = Trigger;
// ---------------------------------------------------------------------------
// Exploration output.
// ---------------------------------------------------------------------------
AboveLevelPct = IIf( ORHigh > 0, 100 * ( Close - ORHigh ) / ORHigh, Null );
MinutesIn = BarMin - SessionStartMin;
SignalAgeSec = DateTimeDiff( Now( 5 ), DateTime() );
DefinitionTag = "OR" + NumToStr( ORMinutes, 1.0, False ) + "m from " +
NumToStr( StartHour, 1.0, False ) + ":" +
NumToStr( StartMinute, 1.0, False ) + " buf " +
NumToStr( 100 * BufferFrac, 1.0, False ) + "% pace>=" +
NumToStr( MinVolumePace, 1.2, False ) + " MA" +
NumToStr( TrendLen, 1.0, False ) + "d";
Filter = Trigger;
AddColumn( Close, "Close", 1.2 );
AddColumn( ORHigh, "OR high", 1.2 );
AddColumn( ORLow, "OR low", 1.2 );
AddColumn( ORRangePct, "OR range %", 1.2 );
AddColumn( AboveLevelPct, "Above OR high %", 1.2 );
AddColumn( VolumePace, "Volume pace", 1.2 );
AddColumn( PrevTurnover / 1000000, "Prev turnover (m)", 1.1 );
AddColumn( MinutesIn, "Minutes into session", 1.0 );
AddColumn( SignalAgeSec, "Signal age (s)", 1.0 );
AddTextColumn( DefinitionTag, "Definition" );
// Column 11 is "Signal age (s)". Ascending puts the freshest row first - which
// is the only ordering that does not quietly encourage you to act on the oldest
// signal in the list.
SetSortColumns( 11 );
// COUNT only, on the first numeric column: how many candidates this run produced.
AddSummaryRows( 16, 1.0, 3 );

Download intraday-breakout-scanner.afl171 lines

The formula is five sections, in dependency order.

Definition. Every threshold is a named constant at the top with a comment, so that the scanner’s behaviour can be read without reading its logic. Nothing below re-derives a number; they all reference these.

Session geometry. Interval() supplies the bar length in seconds, so the formula learns its own interval rather than being told. Everything else is minutes since midnight, derived from Hour() and Minute(), because TimeNum() is decimal-packed and cannot be used arithmetically. SessionOpen, InOR and AfterOR follow from that one array.

Opening range. The running session extremes are frozen with ValueWhen, and ORUsable gates every later use of the levels behind three requirements at once: the range window has actually closed today, the level is not Null, and the range is neither degenerate nor absurd relative to price. That single flag is what stops yesterday’s frozen level being read at 09:35 this morning.

Context gates. Session volume accumulates with SumSince from the session’s first bar. The baseline and the trend flag are both computed inside a daily TimeFrameSet block with a Ref( ..., -1 ), so they describe completed days, and are expanded back down with expandFirst — safe precisely because of that shift. Liquidity uses TimeFrameGetPrice with an explicit negative shift for the same reason.

Trigger and report. Setup is the conjunction of every gate. Cross( Close, BreakLevel ) turns the state into an event on the bar where the crossing happened. ExRem( Trigger, SessionOpen ) suppresses further triggers until the next session begins, which is what makes the report one row per symbol per session rather than one per bar above the level. Filter = Trigger reports exactly those bars; Buy = Trigger lets the same file run under the Scan action, which is what Part 25 attaches an alert to.

Two output details are deliberate. The signal-age column exists so that the freshness of a row is visible without arithmetic, and the initial sort is ascending on it so the newest candidate is at the top. The definition column stamps every row with the thresholds that produced it, using AddTextColumn — which cannot vary per bar, and does not need to here, because the definition is constant for the run. Export the results to CSV a month from now and the file still says what produced them.

ExRem( ARRAY1, ARRAY2 ) removes excessive signals: it keeps the first true value of the first array and suppresses the rest until the second array becomes true. Cross( ARRAY1, ARRAY2 ) is true only on the bar where the first rises through the second, which is the difference between a state and an event. AddTextColumn( string, name ) adds a text column whose value cannot vary per bar. SetSortColumns( col ) sets the initial sort with one-based column numbers that count the default Ticker and Date/Time columns, so the first AddColumn is column 3. AddSummaryRows( flags, format, onlycols... ) places summary rows at the top of the list; flag 16 is COUNT.

Run it over one historical session first, with Apply to: Filter on a watch list and Range: From-To covering that session. Expect a short list rather than a long one: four gates applied in conjunction remove most of a universe, and that is the intended behaviour. Each row is stamped with the time of the crossing, the frozen range levels, how far above the level the close was, and the pace at that moment.

Run it live, or under replay, and the same list rebuilds on every repetition, growing through the session as candidates trigger. Rows do not disappear when the condition stops being true — they record that a crossing occurred, not that it is still occurring. That is intentional, and it is one of the things the closing section asks you to think carefully about.

Four tests, in increasing order of what they can catch.

  1. The level is right. Put the panel formula from the previous lesson on a chart of a symbol the scanner reported, at the same interval, with the same session settings. The frozen opening-range high on the chart must equal the OR high column in the scan row, and the reported time must be a bar where the close crossed the level plus the buffer.
  2. The count is right. Run the audit formula over the same session and symbol. The bars where its TRIGGER column is 1 must be exactly the bars the scanner reported.
  3. The suppression works. In the audit output, find a session where price crossed the level more than once. TRIGGER must be 1 only on the first of them.
  4. It does not read the future. That is the next section.

Reading the level before the range has closed. Symptom: candidates reported at 09:35 with an opening-range high that belongs to yesterday. Cause: consulting ORHigh without the ORUsable gate.

QuickAFL changing the trend gate. Symptom: the scanner produces different results for Range: 1 recent day(s) than for All quotations, with no other change. Cause: partial evaluation with a timeframe ratio the documentation names as an edge case. Fix: SetBarsRequired( sbrAll, sbrAll ) at the top, which the formula already has — remove it and watch the symptom appear.

Sorting by attractiveness. Symptom: you keep acting on the same stale row. Cause: clicking the “Above OR high %” header, which re-sorts the list and puts the oldest, most-extended candidate on top. The formula sorts by age for a reason.

Volume of zero. Symptom: a symbol never appears, or appears constantly. Cause: an instrument whose feed does not carry meaningful volume. Every pace test is undefined for it. Exclude such symbols from the universe rather than special-casing them in the formula.

A stale Bar Replay. Symptom: the scanner stops finding anything after lunch, every day, including on live data. Cause: a Bar Replay session left active. It is global, and it truncates Analysis as well as charts.

Three, in increasing difficulty.

Add a downside symmetry: a crossing below the opening-range low with the daily trend gate inverted, reported in the same list with a direction column. This is more instructive than it sounds, because it forces you to notice how many of your assumptions were directional.

Replace the flat-rate pace with a time-of-day baseline, comparing today’s cumulative session volume against the same clock time on previous sessions. Keep every reference negative. Then compare the two candidate lists for the same session: the difference between them is the size of the flat-rate approximation, measured rather than asserted.

Add a session-quality gate that rejects any symbol whose current session has fewer bars than expected by this time of day, or whose session started at an unexpected minute. That single gate defends against half days, late opens and daylight-saving shifts at once, and it is the most useful thing you can add to any intraday scanner.

The Analysis window settings, in order:

  • Formula: the scanner, sent from the Formula Editor.
  • Apply to: Filter, with a watch list you can actually stream. Compare its size against your subscription’s symbol allowance before you start — the previous lesson explains what happens when you exceed it, and it is not a polite failure.
  • Range: 1 recent day(s), so each repetition rebuilds the whole of today’s candidate list rather than only the newest bar.
  • Periodicity (in Settings): the intraday interval you intend to scan, which must divide the opening-range length.
  • Settings drop-down: tick Auto repeat Scan/Explore, and set Auto repeat interval. A plain number is minutes; type 5s for five seconds.

Choose that interval by measuring, not by preference: run the probe formula from the first lesson over the same universe, note the pass duration, and set the repeat interval comfortably above it. If you are on a plugin-fed database and enable Wait for backfill, remember it is documented as a Professional-edition feature and that it makes your run duration depend on your vendor’s response times rather than on your CPU.

Here is the part worth keeping long after you have discarded this particular scanner.

Bar Replay plays historical data back at a chosen speed, and it is documented as global: it plays back data for all symbols at once, every symbol’s data ends at the current playback position, and this affects all formulas, in charts and in Analysis alike. Pressing Play or Pause enters playback mode and truncates the data; pressing Stop, or closing the dialog, restores it. Nothing is written to disk.

That gives you a test that a live feed cannot provide, because a live feed only ever runs the formula once, forwards.

  1. Choose one past session and one symbol that the scanner reported on that session.
  2. Full-history run. With Bar Replay stopped, run the audit formula with Apply to: Current symbol and Range: From-To covering that session. Record every bar where TRIGGER is 1. Export the result to CSV through File → Export HTML/CSV in the main window — the menu entry appears only while an Analysis window is active.
  3. Replay run. Open Tools → Bar Replay. Set Start and End to bracket the same session, set Step interval to the database’s base interval, and set Speed to 1. Press Play.
  4. In a second Analysis window, run the scanner with Auto repeat on a short interval while replay advances. Let it run to the end of the session.
  5. Compare. The set of trigger times the scanner produced under replay must equal the set the audit produced over full history. Press Stop in Bar Replay to restore the data before you do anything else.

They match. The formula’s references are causal for this session. That is evidence about the formula, not about the rule — it says the scanner is honest, not that its definition is useful.

Replay found fewer. Usually a warm-up problem rather than a look-ahead one: at the start of playback, less history is available, so a 50-day daily average or a 20-day volume baseline may be undefined. Begin the replay window a couple of months before the session you care about and re-run.

Replay found more, or found them earlier. This is the interesting failure. Something in the formula behaves differently when the array ends at the current bar than when it continues past it — a positive Ref, an expandFirst without the shift, a HHV window that includes bars ahead, or a LastValue() on an array that means something different mid-session. Find it before you use the formula for anything.

Everything in this section runs on historical intraday data with no subscription, no entitlements and no vendor. You need an intraday database with at least a few months of history for one universe — Part 19 builds one, and Part 18 covers the zero-cost sources — and nothing else. The scanner, the repeat mechanism, the timing measurements, the session handling and the validation are all fully exercisable offline.

What you genuinely cannot do offline is experience the operational reality: a feed dropping mid-session, a backfill arriving late, a vendor rotating symbols under you, or the specific discomfort of watching a candidate list update while you are still deciding about the previous row. Those are worth knowing about, which is why the first lesson documents them. They are not worth paying for in order to complete this course.

Why this scanner is not evidence of an edge

Section titled “Why this scanner is not evidence of an edge”

You now have a scanner that runs, that has been shown to use only past data, and that produces a short list of candidates during a session. It is tempting to treat the list as a finding. It is not one, for six separate reasons, and they do not overlap.

The thresholds were never measured. Ten decisions, nine round numbers. Nothing in this project compared 1.20 against 1.15, or thirty minutes against fifteen, against any outcome — because no outcome was ever measured.

There is no outcome at all. The scanner reports that a crossing happened. It never asks what happened next. Without a defined exit, a holding period, or any measurement of subsequent returns, there is no result to be right or wrong about. A signal is not a trade.

There is no base rate. Even with an outcome measured, the number that matters is not “how often did the price rise after a signal” but “how much more often than after an arbitrary bar in the same symbols over the same period”. Part 7’s pattern work makes this point on daily data, and the arithmetic is unchanged intraday.

There are no costs. Intraday breakouts trade at moments of unusual activity, which is precisely when spreads widen and slippage is largest. A rule that looks marginally positive before costs is routinely negative after them, and intraday rules are the most vulnerable kind because the cost per unit of expected move is highest.

The sample is smaller than it looks. An opening-range setup produces at most one observation per symbol per session. Two years of five-minute data on 500 symbols is an enormous number of bars and roughly 250,000 candidate sessions before any gate is applied — which sounds large until you notice that sessions across a universe are highly correlated, so the number of genuinely independent observations is far smaller than the row count.

And the definition is fragile in the way the previous lesson described. Half days, daylight-saving transitions, missing bars, auction prints and extended-hours settings all change what these conditions mean, and none of them announces itself. A scanner validated on well-behaved sessions has been validated on the easy cases.

So what is this project for? Three things, all real. You now have a piece of infrastructure that can express an intraday idea precisely enough to be tested. You have a procedure — the replay equality test — that catches the most damaging class of intraday bug in any formula you write from now on. And you have a written record of ten assumptions, which means that when a future version of this scanner disappoints you, you can find out which assumption was carrying the weight.

That is what a technical exercise is supposed to produce. Carry the tooling forward into Part 27, where the idea gets turned into something that can fail a test.

Check your understanding

Question 1. A formula produces a different set of trigger times under Bar Replay than it does over the full database for the same session, with replay finding signals earlier. What has most likely happened?
Show the answer and why

Answer: The formula reads bars ahead of the current one, so hiding the future changes its answers

Bar Replay only hides quotations after the playback position. A formula using past and current bars only must give identical answers either way, so a difference locates a forward reference.

Question 2. What does `ExRem( Trigger, SessionOpen )` do in this scanner?
Trigger = ExRem( Trigger, SessionOpen );
Show the answer and why

Answer: Keeps the first trigger and suppresses further ones until the next session begins

ExRem removes excessive signals from the first array until the second becomes true. With SessionOpen as the reset, the result is one report per symbol per session.

Question 3. Your scanner returns an empty list for a whole morning. Which is the correct first step?
Show the answer and why

Answer: Run the audit formula on a couple of symbols from that session and see which gate is false

The audit reports every in-session bar with each gate’s state, so it answers "why not" directly. Loosening thresholds until the list looks right is how a definition gets fitted to a single day.

Question 4. Which of these would make the candidate list evidence about the market rather than about the definition? Select all that apply.
Show the answer and why

Answer: Measuring what happened after each signal over a long period and wide universe, Comparing that against the base rate for arbitrary bars in the same symbols and period, Subtracting realistic spread and slippage costs

Outcome measurement, a base rate and costs are three of the requirements. Watching a live list and remembering the good rows is a recipe for selection bias, not a test.

Question 5. Why does the scanner sort its result list ascending by signal age?
Show the answer and why

Answer: So the freshest candidate is at the top, rather than the most extended one

Sorting by how attractive a candidate looks quietly puts the oldest, most-extended row on top. Sorting by age keeps the staleness of each row visible, which is the whole theme of this part.

Sources for this lesson

11 verified · checked 2026-08-31

  1. 01AmiBroker User's Guide — New Analysis windowamibroker.com/guide/h_newanalysis.html2026-08-31
  2. 02AmiBroker User's Guide — Exploration tutorialamibroker.com/guide/h_exploration.html2026-08-31
  3. 03AmiBroker User's Guide — Bar Replayamibroker.com/guide/w_barreplay.html2026-08-31
  4. 04AmiBroker User's Guide — Multiple time frame supportamibroker.com/guide/h_timeframe.html2026-08-31
  5. 05AmiBroker AFL Function Reference — ExRemamibroker.com/guide/afl/exrem.html2026-08-31
  6. 06AmiBroker AFL Function Reference — SetSortColumnsamibroker.com/guide/afl/setsortcolumns.html2026-08-31
  7. 07AmiBroker AFL Function Reference — AddSummaryRowsamibroker.com/guide/afl/addsummaryrows.html2026-08-31
  8. 08AmiBroker AFL Function Reference — AddTextColumnamibroker.com/guide/afl/addtextcolumn.html2026-08-31
  9. 09AmiBroker AFL Function Reference — TimeFrameGetPriceamibroker.com/guide/afl/timeframegetprice.html2026-08-31
  10. 10AmiBroker User's Guide — Working with real-time data sourcesamibroker.com/guide/h_rtsource.html2026-08-31
  11. 11AmiBroker User's Guide — Database settingsamibroker.com/guide/w_dbsettings.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.