Skip to content
Level 5 · Real-Time AmiBroker UserChallengePart 19 · page 4 of 540 min Professional edition Live feed
40Minutes
10AFL functions
7Sources
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 here10

Challenge: The History That Is Not All There

An intraday database that is missing a third of its history looks exactly like one that is complete. The chart draws. The indicators compute. The exploration returns rows. Nothing is red, nothing is blank, and nothing warns you.

This page is a diagnostic exercise. You are handed a database with seven observable symptoms, and your job is to work out which of them share a cause, which are separate faults, and which are not faults at all.

A reader has built an intraday database and used it for about three months.

  • Base time interval 1-minute. Data source: a real-time plug-in with local data storage enabled.
  • Number of bars to load: 30,000.
  • 40 United States equities, added in three batches: 25 at the start, 10 about six weeks ago, 5 last week.
  • Session filtering set to Show day session only, with day-session trading hours entered as 09:30 to 16:00.
  • The reader wants to test an opening-range rule: define the range as the high and low of the first fifteen minutes, then trade the first one-minute close outside it.
  1. The chart stops at a different date for each symbol. Press End, then scroll back. One symbol runs out around fifteen weeks ago; another around nine weeks; the five newest run out five days ago.
  2. The opening-range rule produces nothing on roughly one day in five, and which days differ by symbol.
  3. Some 15-minute bars begin at 09:35 instead of 09:30. Not many. Not always the same symbol.
  4. Two symbols the reader believes should be near-identical in coverage report very different bar counts over the same “All quotations” range.
  5. The five newest symbols have far less history than the rest, which the reader expected — but raising Number of bars to load from 30,000 to 100,000 and refreshing the charts changed nothing for the other 35.
  6. A handful of sessions contain roughly 200 bars instead of roughly 390.
  7. A backtest set to run from “1 January” produces its earliest trade in mid-March, and the equity curve starts there too.

Before forming a theory, gather the following. Each item is here because it distinguishes between at least two candidate causes.

Evidence Where What it settles
Base time interval, Data source, Local data storage, Number of bars to load, and the days-equivalent the dialog prints beside the bar count File → Database Settings Whether the ceiling is yours or the vendor’s
Filtering mode, day/night trading hours, Time shift, Allow mixed EOD/Intraday data File → Database Settings → Intraday settings Whether bars are hidden or absent
Whether any group has Group uses own intraday settings ticked Symbol → Categories → Groups Whether a subset of symbols has different session hours
One suspect symbol, one suspect session, opened in the Quote Editor Symbol → Quote Editor Hidden versus missing. The Quote Editor always shows every stored bar
Plug-in status light colour, and the plug-in’s documented backfill depth Status area, bottom right; vendor documentation Whether the vendor ever offered the history you want
Per-symbol first bar, last bar, bar count, bars per session The history depth report from the previous lesson Whether the database is uniform
Per-session bar counts, start times, end times and internal gaps The completeness check below Which sessions are wrong, and in which of four ways
Whether Bar Replay is running or paused Tools → Bar Replay, and GetPlaybackDateTime() in a formula Whether the whole database is being truncated globally
Whether the Analysis run had Wait for backfill ticked Analysis window Settings split-button menu Whether the run analysed arrays that had not arrived yet

Work out, for each of the seven symptoms:

  1. Which layer it lives in — display, storage, vendor, or program state.
  2. Which other symptoms share its cause, and which do not.
  3. What single observation would prove or disprove your explanation.

Then produce two artefacts:

  • A written statement of the database’s actual coverage: the earliest date on which every symbol has data, which is the earliest date any multi-symbol study over this database can honestly start from.
  • A repeatable completeness check you can run weekly.

Give yourself the full forty minutes and write your answers down before reading the hints. The value of this exercise is in the ordering of your reasoning, not in the answer.

You will need this for the task, and you will keep it afterwards.

Produce one row per symbol per session that separates four questions an incomplete database constantly conflates: are there holes inside the session, did it start late, did it end early, and is the whole session short? Do it without a holiday calendar and without a hard-coded bar count, so that the same formula runs on any exchange, any instrument and any time-based base interval.

Complete runnable AFL

intraday-completeness-check.afl
// ===========================================================================
// Intraday completeness check
// One row per symbol per trading session, answering four separate questions
// that an incomplete intraday database confuses with each other:
//
// 1. Are there holes INSIDE the session? (Missing column)
// 2. Did the session start late? (Late start column)
// 3. Did the session end early? (Early end column)
// 4. Is the whole session shorter than this
// symbol's own normal session? (Short column)
//
// It needs no holiday calendar and no hard-coded bar count, because every
// session is measured against (a) its own elapsed time and (b) the fullest
// session this symbol actually has. That is what makes it portable: it runs
// unchanged on any intraday database, local or plug-in fed, at any
// time-based base interval, on any exchange.
//
// HOW TO RUN
// Analysis window: Apply to = All symbols (or a watch list),
// Range = All quotations.
// Set the chart interval to the database's base interval first - the "i"
// toolbar button - or you will be auditing compressed bars rather than
// stored ones.
// Press Explore. Leave "Show only suspect sessions" on for a first pass.
//
// WHAT EACH COLUMN MEANS
// Bars bars stored in that session
// Start/End first and last bar time, as TimeNum() codes:
// 10000*hour + 100*minute + second, so 09:35:00 reads 93500
// Span min minutes between the first and the last bar of the session
// Expected Span divided by the bar interval, plus one
// Missing Expected minus Bars: bar slots inside the session with no bar
// Max gap min the longest interval between two consecutive bars
// % fullest Bars as a percentage of this symbol's fullest session
//
// ASSUMPTIONS, STATED SO THEY CAN BE CHECKED
// - A missing bar is not necessarily missing DATA. In an intraday database
// a minute in which nothing traded produces no bar at all, so an illiquid
// symbol will show Missing > 0 permanently and correctly. Compare a
// symbol against itself over time, not against a liquid neighbour.
// - Half-day sessions before public holidays are genuinely short. "Short"
// flags them, and it is right to: the check cannot tell a holiday from a
// truncated backfill, and neither should it pretend to. That is what the
// exchange calendar is for.
// - Late start and Early end are measured against the earliest start and
// the latest end this symbol has anywhere in the loaded range. If the
// loaded range contains only truncated sessions, everything looks normal.
// Widen the range before trusting a clean result.
// - The check reads what AFL was GIVEN, which may be less than what is
// stored: an Analysis range, QuickAFL or an active Bar Replay all shorten
// it. Confirm with the Quote Editor, which always shows every stored bar.
// - Tick and other non-time-based intervals leave Expected, Missing and
// Max gap empty by design, because "one bar per interval" has no meaning
// when bars are not time based.
// ===========================================================================
ShortThreshold = Param( "Short session threshold %", 90, 10, 100, 1 );
OnlySuspect = ParamToggle( "Show only suspect sessions", "No|Yes", 1 );
SetOption( "NoDefaultColumns", True );
BarNumber = Cum( 1 );
// Cum(1) == 1 forces the first bar to open a session, because Ref() has no
// previous bar there to compare against.
NewSession = BarNumber == 1 OR Day() != Ref( Day(), -1 );
// The last bar of a session is the bar before the next session opens. Ref()
// with a positive shift looks one bar into the future, which is legitimate in
// a data audit and would not be in a trading rule. On the final bar of the
// array there is no next bar, so the analysis-range flag catches it.
EndOfSession = Nz( Ref( NewSession, 1 ) ) OR Status( "lastbarinrange" );
SessionStartBar = ValueWhen( NewSession, BarNumber );
SessionStartDateTime = ValueWhen( NewSession, DateTime() );
SessionStartTime = ValueWhen( NewSession, TimeNum() );
BarsInSession = BarNumber - SessionStartBar + 1;
// How long the session ran, in seconds, measured from its own first bar.
SpanSeconds = DateTimeDiff( DateTime(), SessionStartDateTime );
// Interval() is the interval of the CHART, in seconds. Tick charts return 0
// and daily or longer returns 86400 or more; in both cases the bar-slot
// arithmetic below is meaningless, so the divisor becomes Null and the
// derived columns come out empty rather than wrong.
IntervalSeconds = Interval();
BarSlotSeconds = IIf( IntervalSeconds > 0 AND IntervalSeconds < inDaily,
IntervalSeconds, Null );
ExpectedBars = SpanSeconds / BarSlotSeconds + 1;
MissingBars = ExpectedBars - BarsInSession;
// Gap between this bar and the previous one, reset at every session boundary
// so that the overnight gap is never counted as a hole.
GapSeconds = Nz( DateTimeDiff( DateTime(), Ref( DateTime(), -1 ) ) );
GapWithinSession = IIf( NewSession, 0, GapSeconds );
LargestGap = HighestSince( NewSession, GapWithinSession );
// This symbol's own reference session: the fullest one in the loaded range,
// and the earliest open and latest close it has ever recorded. 999999 is a
// sentinel above any possible TimeNum() value, which caps out at 235959.
SessionTotal = IIf( EndOfSession, BarsInSession, 0 );
FullestSession = LastValue( Highest( SessionTotal ) );
EarliestStart = LastValue( Lowest( IIf( NewSession, TimeNum(), 999999 ) ) );
LatestEnd = LastValue( Highest( IIf( EndOfSession, TimeNum(), 0 ) ) );
PercentOfFullest = IIf( FullestSession > 0,
100 * BarsInSession / FullestSession, Null );
HasHoles = MissingBars >= 1;
StartsLate = SessionStartTime > EarliestStart;
EndsEarly = TimeNum() < LatestEnd;
IsShort = PercentOfFullest < ShortThreshold;
Suspect = HasHoles OR StartsLate OR EndsEarly OR IsShort;
Filter = EndOfSession AND ( OnlySuspect == 0 OR Suspect );
AddTextColumn( Name(), "Symbol", 1.0, colorDefault, colorDefault, 80 );
AddColumn( DateTime(), "Session", formatDateTime );
AddColumn( BarsInSession, "Bars", 1.0 );
AddColumn( SessionStartTime, "Start", 1.0 );
AddColumn( TimeNum(), "End", 1.0 );
AddColumn( SpanSeconds / 60, "Span min", 1.0 );
AddColumn( ExpectedBars, "Expected", 1.0 );
AddColumn( MissingBars, "Missing", 1.0 );
AddColumn( LargestGap / 60, "Max gap min", 1.1 );
AddColumn( PercentOfFullest, "% fullest", 1.1 );
// Every flag is readable as text, so the report survives being printed in
// black and white, read by a screen reader or pasted into a spreadsheet.
AddTextColumn( WriteIf( HasHoles, "holes", "-" ), "Holes", 1.0,
colorDefault, colorDefault, 60 );
AddTextColumn( WriteIf( StartsLate, "late", "-" ), "Late start", 1.0,
colorDefault, colorDefault, 70 );
AddTextColumn( WriteIf( EndsEarly, "early", "-" ), "Early end", 1.0,
colorDefault, colorDefault, 70 );
AddTextColumn( WriteIf( IsShort, "short", "-" ), "Short", 1.0,
colorDefault, colorDefault, 60 );
// Worst first: most missing bars, then longest internal gap.
SetSortColumns( -8, -9 );

Download intraday-completeness-check.afl142 lines

Every session is measured against two references that come from the data itself.

The first is its own elapsed time. DateTimeDiff gives the seconds between the session’s first and last bar; dividing by Interval() — the bar length in seconds — and adding one gives how many bar slots that span contains. Subtract the bars actually present and you have Missing: bar slots inside the session with no bar in them. This measure does not care what time the session started or how long it ran, so a half-day and a full day are judged by the same rule.

The second is the symbol’s own fullest session. Highest() is a running maximum over everything seen so far, so LastValue(Highest(SessionTotal)) is the longest session this symbol has anywhere in the loaded range. % fullest compares each session with it. The same trick with Lowest() over session start times gives the earliest open the symbol has ever recorded, and with Highest() over session end times gives the latest close — which is what Late start and Early end compare against.

Internal gaps use HighestSince( NewSession, GapWithinSession ), which resets at each session boundary so that the overnight gap is never counted as a hole.

Two guards matter. EndOfSession looks one bar into the future at NewSession, which is legitimate in a data audit and would be look-ahead bias in a trading rule; on the final bar of the array there is no next bar, so Status("lastbarinrange") catches the newest session. And when Interval() returns zero (tick bars) or 86,400 and above (daily and longer), the bar-slot divisor becomes Null and the derived columns come out empty rather than wrong.

  • Interval() — the current chart’s bar interval in seconds; zero for tick bars.
  • TimeNum() — bar time as 10000 × hour + 100 × minute + second, so 09:35:00 reads as 93500. That is why the Start and End columns are integers.
  • DateTimeDiff( a, b ) — seconds between two DateTime values.
  • HighestSince( condition, array ) — running maximum since the condition was last true.
  • WriteIf( condition, "yes", "no" ) — a text column, so every flag is readable without relying on colour.

With Show only suspect sessions left on, a healthy database returns very few rows. What you should see on a database with the fault in this challenge is clusters: a run of sessions with late on one symbol, a scattering of holes across illiquid symbols, and a small number of short sessions shared by every symbol on the same dates.

That last pattern is the signature of a real half-day session, and it is the one result the check cannot interpret for you.

Pick a symbol and a session you know is complete. Delete three bars from the middle of it with the Quote Editor, on a copy of the database. Re-run: that session should now report Missing 3 and a Max gap of four bar lengths. Undo by re-importing or by restoring the copy. If the numbers do not move, the chart is not on the base interval, or the Analysis range excludes that session.

Running it on a compressed interval audits compressed bars, not stored ones — set the chart to the base interval with the i toolbar button first. Reading Missing > 0 as proof of a defect is the second error: in an intraday database a minute in which nothing traded produces no bar, so an illiquid symbol reports missing bars permanently and correctly. And a clean result over a narrow Analysis range proves only that the narrow range is clean.

Add a column holding the previous session’s bar count, using ValueWhen on the session-end bars, so that each row carries its own neighbour for comparison. Sessions that are short relative to the day before are much more interesting than sessions that are short in absolute terms.

Read one at a time. Stop as soon as you have a theory you can test.

Two of the seven symptoms are about what is displayed, and at least one is about what the program is doing right now rather than about data at all. Which single window in AmiBroker is documented to show every stored bar regardless of any filter — and what does it tell you about symptom 3?

Symptom 5 contains its own answer if you read the previous lesson’s warning about what happens after you increase a setting. Raising a limit does not retrospectively fetch anything.

Number of bars to load counts bars, not days, backwards from the newest bar. Two symbols with the same setting can therefore reach back different distances in calendar time. Under what circumstances would one symbol consume its 30,000-bar budget faster than another? There are at least two independent mechanisms, and both are visible in the Bars/session column of the history depth report.

Symptom 7 is not about the database. Something in the program can truncate every symbol at once, affects charts and Analysis alike, leaves the Quote Editor untouched, writes nothing to disk, and stays in effect until you press a particular button. What does GetPlaybackDateTime() return while it is active?

Symptom 6 may not be a fault. Before you conclude that 200 bars in a 390-bar session means missing data, ask what the exchange was doing that afternoon. Which column of the completeness check distinguishes “the session was genuinely short” from “the session had holes in it”?

Sorting seven symptoms into four causes

  1. Symptoms 1 and 4 → the bar-count ceilingA cap in bars, consumed at different rates per symbol
  2. Symptoms 2, 3 and part of 4 → partial sessionsBackfill that began mid-session, so the session has no opening bars
  3. Symptom 5 → a raised limit with no forced backfillPlus, behind it, the vendor’s own backfill depth
  4. Symptom 6 → probably not a fault at allHalf-day sessions are short by design
  5. Symptom 7 → Bar Replay still activeGlobal truncation of every symbol, in charts and Analysis alike
Three real faults, one program-state error and one non-fault.

Symptom 1 — the chart stops at a different date per symbol. The 30,000-bar cap is consumed from the newest bar backwards. A symbol whose feed stores 390 bars per session reaches back about 77 sessions; a symbol for which the feed also stores pre-market and after-hours bars might store 550 or 600 per session and reach back only 50. Same setting, different dates. Confirm it in the Bars/session column of the history depth report: if it varies across symbols, this is your mechanism.

Symptom 2 — the opening-range rule produces nothing one day in five. The rule needs the first fifteen minutes. If the session’s first stored bar is 09:41, there is no first fifteen minutes to measure, so the range is undefined and the rule cannot fire. The completeness check flags these as late. The reason a session starts late is almost always that the backfill for that symbol began partway through it — on the day the symbol was added, or after a reconnection.

Symptom 3 — 15-minute bars beginning at 09:35. Compression builds from what is stored. If the earliest stored bar in that session is 09:35, the first compressed 15-minute bar starts there, because there is nothing earlier to include. This is the same fault as symptom 2 seen through a different lens, which is exactly why you check the Quote Editor: if the 09:30 to 09:34 bars are absent there too, they are genuinely missing rather than filtered.

Symptom 4 — different bar counts for comparable symbols. Three mechanisms add up here, and separating them is the point of the exercise. Different history depth (symptom 1), partial sessions (symptom 2), and the ordinary fact that a minute in which nothing traded produces no bar at all — so a less active symbol legitimately has fewer bars in the same session. The completeness check separates the third from the first two: genuine no-trade minutes appear as Missing spread thinly across many sessions, whereas a truncated session appears as late, early or short on specific dates.

Symptom 5 — raising the bar count changed nothing. Two layers. The immediate one: symbols already backfilled at 30,000 do not gain the extra bars by themselves; the guide is explicit that a Force backfill is required after enlarging the setting. The deeper one: even after forcing it, you only get what the vendor is willing to send. If your feed’s documented one-minute depth is shorter than 100,000 bars’ worth, raising your own ceiling changes nothing at all, and the ceiling that binds was never yours.

Symptom 6 — about 200 bars instead of 390. A regular 09:30 to 16:00 session is 390 minutes; a shortened session ending at 13:00 is 210. If the short sessions fall on the same dates for every symbol, that is an exchange half-day and your data is right. If they fall on different dates per symbol, it is truncation. The check gives you both readings: a genuinely short session shows short with Missing near zero, whereas a holed session shows Missing well above zero.

Symptom 7 — the backtest starts in mid-March. Bar Replay was left paused. Playback truncates data for all symbols at the playback position and affects charts and Analysis alike; the Quote Editor is the documented exception. Nothing is written to disk, so no damage is done, and pressing Stop or closing the window restores everything. GetPlaybackDateTime() returns the playback position while replay is active and zero when it is not, which is why the readout formula from the settings lesson keeps a permanent line for it.

The deliverable is one sentence, and it is the most useful sentence in this part:

The earliest date on which every symbol in this database has complete session data is D. Any multi-symbol study run over this database before D is studying a shrinking universe, not a market.

You find D by sorting the history depth report by first bar date, descending, and reading the top row — then advancing it past any late or short sessions the completeness check flags in the days that follow.

Base interval. Determines what the stored grain is and therefore how many bars a session contains at all. Wrong here and none of the other three can be fixed: you cannot audit for missing one-minute bars in a database that stores five-minute bars.

Number of bars to load. A ceiling expressed in bars, per symbol, consumed from the newest bar backwards, and therefore reaching back different distances in calendar time for different symbols. Raising it requires a forced backfill to take effect on symbols already filled.

Provider backfill limits. The vendor’s own depth, which is frequently the binding constraint and which AmiBroker’s documentation records in pages that date themselves — one of which contradicts another about the same feed. Verify with the vendor.

Partial sessions. Backfill that began mid-session leaves a session with a late start; a disconnection leaves one with an early end; a no-trade minute leaves a hole that is not a fault. These are the ones that survive every other fix, because they are invisible on a chart unless you happen to scroll to that exact day.

And the fifth thing, which is not a cause but is mistaken for all four of them: display filtering and program state. Session filtering hides bars without deleting them, a per-group override can countermand the database-wide session times, and an active Bar Replay truncates everything at once. All three are free to fix and none of them is a data problem.

Reproducing it deliberately, without a feed

Section titled “Reproducing it deliberately, without a feed”

You can build every one of these faults on a local database from files, which is the most efficient way to learn to recognise them.

  1. Create a local database with a 1-minute base interval, and import a one-minute file for two or three symbols. The lab on the next page gives the import definition.
  2. Make a partial session. Take one symbol’s file, delete the first twenty rows of one trading day, and re-import into a fresh database. Run the check: that session should report late.
  3. Make holes. Delete every fifth row of one session. The check should report Missing around 20 per cent of the session and a Max gap of two bar lengths.
  4. Make an early end. Delete the last thirty rows of a session and re-import. The check should report early.
  5. Make a short session. Import a file for a genuine half-day and confirm the check reports short with Missing at or near zero — the pattern that means “not a fault”.
  6. Reproduce symptom 7. Open Tools → Bar Replay, set a start date in the middle of your data, press Pause, and re-run the history depth report. Every symbol shortens together. Press Stop and re-run: everything returns.

Doing all six takes about twenty minutes and gives you a permanent mental signature for each fault. It is also the only version of this exercise where you know the right answer in advance.

The habit this challenge is trying to install is ordering. When intraday history looks wrong, check in this sequence, because each step is cheaper than the next and can eliminate the ones after it:

  1. Program state. Is Bar Replay active? Is a Bar Replay window open and paused? Did the Analysis run wait for backfill?
  2. Display. Does the Quote Editor show bars the chart does not? Is there a per-group session override?
  3. Your own ceilings. Number of bars to load, and its days-equivalent for your measured bars per session. Did you force a backfill after raising it?
  4. The vendor’s ceiling. What does the vendor document today, not what does AmiBroker’s copy of their documentation say?
  5. The data itself. Only now run the completeness check and start deleting and re-importing.

Most people start at step five, which is why most people re-download history they already had.

Check your understanding

Question 1. Every symbol in the database suddenly appears to end six weeks early, simultaneously, and the Quote Editor still shows the missing bars. What is the first thing to check?
Show the answer and why

Answer: Whether Bar Replay is active or paused

Two clues point the same way: every symbol changed together, and the Quote Editor is unaffected. Bar Replay truncates data globally for charts and Analysis while leaving the Quote Editor alone and writing nothing to disk. A vendor limit or a bar-count ceiling would bite unevenly across symbols.

Question 2. A session reports Bars 210, Missing 0, and "short". The same date is short for every symbol in the database. What is the most reasonable conclusion?
Show the answer and why

Answer: It was a shortened exchange session and the data is correct

Missing 0 means the session has no holes inside its own span: every bar slot between the first and last bar is filled. Short plus complete, on the same date across every symbol, is the signature of a half-day. The check deliberately cannot tell you this by itself, which is why it reports both numbers.

Question 3. Which of these can independently cause two symbols with identical settings to hold different amounts of calendar history? Select all that apply.
Show the answer and why

Answer: One symbol’s feed stores extended-hours bars and the other’s does not, The symbols were added to the database on different dates, One symbol is far less liquid, so many minutes print no bar

The bar-count ceiling is consumed in bars, so anything that changes bars per day changes how far back the cap reaches; and backfill depth is measured from first access, so add dates matter. The last option is impossible: the base interval is a property of the database, not of a symbol.

Question 4. You raise Number of bars to load and the older symbols gain nothing. What are the two candidate explanations, in the order you should test them?
Show the answer and why

Answer: You did not force a backfill for already-backfilled symbols, then the vendor does not offer that much depth

Force backfill is free and takes seconds, so test it first. If the history still does not extend, the binding limit is the vendor’s, not yours — and no setting on your side will change it. Testing in the other order wastes a conversation with the vendor.

Question 5. Why does the completeness check compare each session against the symbol’s own fullest session rather than against a fixed number such as 390?
Show the answer and why

Answer: Because a fixed number is wrong for every exchange, instrument, base interval and extended-hours setting except one

A hard-coded bar count encodes one exchange’s regular session at one interval with one extended-hours policy. Measuring a session against the same symbol’s fullest session, and against its own elapsed time, makes the check portable to any database — which is the property that makes it worth keeping.

Sources for this lesson

7 verified · checked 2026-08-31

  1. 01AmiBroker User's Guide — Working with real-time data sourcesamibroker.com/guide/h_rtsource.html2026-08-31
  2. 02AmiBroker User's Guide — Database Settings windowamibroker.com/guide/w_dbsettings.html2026-08-31
  3. 03AmiBroker User's Guide — Bar Replayamibroker.com/guide/w_barreplay.html2026-08-31
  4. 04AmiBroker User's Guide — Performance tuning tipsamibroker.com/guide/x_performance.html2026-08-31
  5. 05AFL Function Reference — TimeNumamibroker.com/guide/afl/timenum.html2026-08-31
  6. 06AFL Function Reference — Intervalamibroker.com/guide/afl/interval.html2026-08-31
  7. 07AFL Function Reference — HighestSinceamibroker.com/guide/afl/highestsince.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.