Skip to content
Level 5 · Real-Time AmiBroker UserLabPart 19 · page 5 of 550 min Professional edition Live feed
50Minutes
5AFL 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 here5

Lab: Build an Intraday Market Database

By the end of this lab you will have an intraday database you can defend line by line: a base interval you chose for a stated reason, settings you understand, real bars in it, and three verification passes that prove what is actually there.

There are two routes. Route A uses a real-time plug-in and needs a subscription. Route B builds the same thing from historical intraday files and needs nothing beyond AmiBroker and one CSV file. Route B is not a lesser version: it produces a genuine intraday database with the same base interval, the same settings, the same session behaviour and the same maintenance problems. Everything after the “Verify” heading applies to both.

Before you start: three decisions, written down

Section titled “Before you start: three decisions, written down”

Open the text file you started in Part 3 and answer these before you touch a dialog. Guessing here is what the whole part has been arguing against.

  1. What is the finest measurement your rules will make? Not the finest chart you might look at. If the answer is “the first one-minute close beyond the opening range”, your base interval is 1-minute. If it is “the fifteen-minute trend”, 5-minute is ample and costs a fifth of the storage.
  2. How many symbols, and which ones? An intraday database should be small and deliberate. Ten to forty instruments you actually watch. Not a market.
  3. How much history do you need, in sessions? Say the number. “Sixty sessions” is a plan; “as much as possible” is how databases end up at 2 GB and too slow to work in.

Follow this only if you have a working subscription and the vendor’s client software installed and connected. Otherwise skip to Route B.

A1. Create the database. File → New → Database. Type or browse to a new folder — put it outside the AmiBroker program folder so that reinstalling never touches it — and click Create. The Data source half of the dialog becomes usable only after you click Create.

A2. Choose the data source. Pick your plug-in from the Data source combo. The list printed in the User’s Guide is dated and omits several real plug-ins, so trust the combo in front of you rather than any list.

A3. Local data storage: Enable. Documented as required for most real-time sources, and it is what makes the bars persist between sessions.

A4. Base time interval. Set it to the value you decided above. This is the decision this part exists for. On the Standard edition your floor is 1-minute; tick and N-second intervals require Professional.

A5. Number of bars to load. Take your target session count, multiply by a realistic bars-per-session figure for your feed, add about twenty per cent, and enter that. Then read the days-equivalent AmiBroker prints beside the field. If it disagrees with your target, one of your assumptions is wrong — find out which before continuing.

A6. Intraday settings. Set the filtering mode and the day-session trading hours in your own local time zone, set the time shift if your bar timestamps need moving, and decide about Allow mixed EOD/Intraday data. If your plug-in supports mixed mode and you want daily bars in the same database, tick it now: the IQFeed walkthrough ticks it during setup rather than afterwards.

A7. Configure the plug-in. Configure opens the vendor-specific dialog — credentials, symbol counts, backfill options. Set the symbol count to your actual subscription entitlement, not higher.

A8. Add symbols. Symbol → New accepts a comma-separated list. Nothing is downloaded yet: backfill happens on first access.

A9. Backfill everything. In the Formula Editor write Buy = 1;, press Send to Analysis, set Apply to to All symbols, Range to 1 recent bar, open the Settings split-button menu and tick Wait for backfill, then press Scan. On Interactive Brokers use the different documented route instead: add the symbols to the Real-Time Quote window, choose a Backfill length from the plug-in status context menu, then Backfill All RT quote window symbols.

Then go to Verify.

Route B: an intraday database from historical files

Section titled “Route B: an intraday database from historical files”

This route needs one thing the course cannot give you: a file of intraday bars. Everything else is in AmiBroker already.

Be realistic about scope. One symbol and five trading sessions is enough to complete every step of this lab — that is roughly 1,950 rows at one-minute bars. You do not need a market, and you do not need years.

Places such a file legitimately comes from:

  • A platform you already use. Most broker and charting platforms let account holders export intraday bars to CSV. This is the most common route and the only one this course can be confident about, because it depends on your own arrangements rather than on a third party’s current terms.
  • AmiQuote’s own sources. AmiQuote ships with the AmiBroker installer and is licensed separately. Its official Read Me lists historical end-of-day and intraday FOREX from FinAm among the built-in sources; that intraday source is noted as available to the registered version.
  • A user-defined AmiQuote source. AmiQuote 4.x can be pointed at any source that returns CSV over HTTP, and example definitions ship in the DataSources subfolder — including Tiingo Intraday IEX and AlphaVantage. Both need your own API key from the vendor.

Your file needs, per row: a ticker (or a file named after the ticker), a date, a time, open, high, low, close and volume. Something like this, which is the shape the format definition below expects:

AAA,2026-08-24,09:30,20.00,20.08,19.97,20.06,4200
AAA,2026-08-24,09:31,20.06,20.14,20.03,20.11,3100
AAA,2026-08-24,09:32,20.11,20.12,20.02,20.04,2700

Those three rows are synthetic, and they exist only to show the column order.

File → New → Database. Choose a folder outside the AmiBroker program directory and click Create. Leave Data source as (local).

B2. Set the base interval and the intraday settings

Section titled “B2. Set the base interval and the intraday settings”

Base time interval: the value you decided at the top of this lab, and it must be no coarser than your file. One-minute bars cannot be imported into a five-minute database; five-minute bars import perfectly well into a one-minute database, because they are simply bars stamped five minutes apart.

Then Intraday settings:

  • Filtering: start with Show 24 hours trading (no filtering). Filtering hides bars, and while you are learning what is in the file you want to see everything. Tighten it later.
  • Trading hours: enter the session your instrument actually trades, in your own local time zone.
  • Time shift: leave it at zero for now. If your file’s timestamps are in the exchange’s time zone and you want them shown in yours — or the reverse — you have two ways to deal with it, and choosing between them is step B4.

The Import Wizard (File → Import Wizard) is the friendly route, and for intraday work it is the wrong one. The documented list of field types it offers is Ticker, YMD, DMY, MDY, Open, High, Low, Close and Volume — with no time field. The wizard is described in the guide as offering a subset of the importer’s features, “for novice users only”. Intraday import needs the full ASCII importer and a format definition file.

Create a plain text file named intraday-1min.format in the Formats subdirectory of your AmiBroker working directory, containing:

; intraday-1min.format
; Ticker, ISO date, HH:MM time, OHLC, volume, comma separated, one header row.
$FORMAT Ticker, Date_YMD, Time, Open, High, Low, Close, Volume
$SEPARATOR ,
$SKIPLINES 1
$AUTOADD 1
$CONT 1
$DEBUG 1

What each line does, and why it is there:

  • $FORMAT declares the order and type of the fields. Time accepts HH:MM:SS, HH:MM, HHMM or HHMMSS. Use Skip for any column you want ignored — there is no positional “omit”.
  • $SEPARATOR , is not optional. The importer’s default separator is a space, and forgetting this line is the classic first failure.
  • $SKIPLINES 1 discards a header row. Set it to 0 if your file has none.
  • $AUTOADD 1 creates symbols that do not yet exist. The default is 0, which silently imports nothing into an empty database.
  • $CONT 1 marks newly added symbols as continuously quoted, which is what enables candlestick charts. Without it the symbol is treated as price-fixing.
  • $DEBUG 1 logs errors to import.log in AmiBroker’s main directory. Turn it on for the first import of any new file shape.

If your timestamps need shifting, $TIMESHIFT <hours> moves them at import time and accepts fractional and negative values — $TIMESHIFT -11.5 shifts eleven and a half hours backwards. That is a different mechanism from the Time shift in Intraday Settings, which shifts on display. Pick one and record which; using both is how people end up two hours out and unable to say why.

File → Import ASCII. Your format now appears in the Files of type combo under its description. Select your file — Ctrl and Shift multi-select works — and import.

If nothing appears, work through these in order:

  • import.log in the AmiBroker main directory, if you set $DEBUG 1.
  • The separator. Space is the default; a comma-separated file without $SEPARATOR , produces a file of one unparseable column.
  • $AUTOADD. Without it, unknown tickers are skipped rather than created.
  • The ticker. If no field in $FORMAT names the ticker, the file name without path or extension is used instead. download1.csv creates a symbol called DOWNLOAD1.
  • Silent price repair. Without $ALLOWNEG 1, the importer quietly fixes rows where the OHLC relationships do not hold — a zero open becomes the close, a high below the body is raised, a zero low becomes the lower of open and close. If your imported prices differ from your file, this is usually why.

An intraday database built from files grows by importing more files. Imports at the same bar interval are additive, so overlapping files repair gaps rather than duplicating bars. If you are downloading daily with AmiQuote, note that its settings distinguish historical files, which are overwritten from daily files, which are appended — appending daily files is precisely how an intraday history accumulates over weeks.

Three passes, in order. Do not skip to the third.

Verification, cheapest first

  1. Pass 1: what is this database?Interval, time shift, bars loaded, sessions, Bar Replay state
  2. Pass 2: is it uniform across symbols?First bar, last bar, bars per session, per symbol
  3. Pass 3: is each session complete?Holes, late starts, early ends, short sessions
  4. Write the coverage statementThe earliest date on which every symbol has complete data

Set a chart to the base interval — the intraday toolbar button marked i — and apply this indicator.

Complete runnable AFL

database-settings-readout.afl
// ===========================================================================
// Database settings readout
// Prints, in the chart title, what this database and this chart pane are
// actually configured to do - as opposed to what you believe you configured
// when you created them. Nearly every intraday problem in Part 19 is a
// mismatch between those two things.
//
// HOW TO RUN
// Formula Editor -> Apply Indicator, in its own small pane.
// To read the database's BASE interval rather than some other interval,
// switch the chart to the intraday toolbar button marked "i", which means
// "the base intraday interval set in File -> Database Settings".
//
// WHAT IT REPORTS
// Chart interval the interval of THIS pane, in seconds and by name
// Time shift the database time shift, from Status("timeshift")
// Bars loaded how many bars AFL actually received for this symbol
// First / Last bar the ends of what was loaded, not of what is stored
// Calendar span days between the first and the last bar
// Sessions how many distinct calendar days those bars cover
// Bars per session loaded bars divided by sessions
// Bar Replay whether playback is truncating the data right now
//
// ASSUMPTIONS, STATED SO THEY CAN BE CHECKED
// - "Bars loaded" is not "bars stored". AmiBroker may hand a formula fewer
// bars than the database holds, because of the Analysis range, because of
// QuickAFL, or because Bar Replay is active. The Quote Editor is the only
// window documented to always show every stored bar.
// - A "session" here means a distinct calendar date, which is not the same
// as an exchange session for instruments that trade overnight. On a
// 24-hour instrument one exchange session can straddle two dates.
// - Interval() returns the interval of the chart, never the base interval
// of the database. They are the same number only when the chart is set to
// the base interval.
// ===========================================================================
_SECTION_BEGIN( "Database settings readout" );
Plot( Close, "Close", colorDefault, styleCandle );
IntervalSeconds = Interval();
IntervalName = Interval( 2 );
// Status("timeshift") returns the database time shift in seconds (AmiBroker
// 5.60 and later). It is the setting from Database Settings -> Intraday
// settings -> Time shift, expressed as seconds rather than hours.
TimeShiftSeconds = Status( "timeshift" );
TimeShiftHours = TimeShiftSeconds / 3600;
BarNumber = Cum( 1 );
BarsLoaded = LastValue( BarNumber );
FirstBarDateTime = LastValue( ValueWhen( BarNumber == 1, DateTime() ) );
LastBarDateTime = LastValue( DateTime() );
// DateTimeDiff returns seconds, positive when the first argument is later.
SpanDays = DateTimeDiff( LastBarDateTime, FirstBarDateTime ) / 86400;
// A new calendar date starts wherever the day number changes. Cum(1) == 1
// forces the very first bar to count, because Ref() has no previous bar there.
NewSession = BarNumber == 1 OR Day() != Ref( Day(), -1 );
SessionCount = LastValue( Cum( NewSession ) );
BarsPerSession = IIf( SessionCount > 0, BarsLoaded / SessionCount, Null );
// GetPlaybackDateTime() returns the Bar Replay position, or zero when replay
// is not active. A forgotten Bar Replay truncates every chart and every
// Analysis run in the program, so it is worth showing permanently.
PlaybackPosition = GetPlaybackDateTime();
ReplayLine = "Bar Replay: not active";
if ( PlaybackPosition != 0 )
{
ReplayLine = "BAR REPLAY ACTIVE - data truncated at "
+ DateTimeToStr( PlaybackPosition );
}
IntervalLine = "Chart interval: " + IntervalName
+ " (" + NumToStr( IntervalSeconds, 1.0 ) + " s)";
if ( IntervalSeconds >= 86400 )
{
IntervalLine = IntervalLine
+ " - this pane is NOT intraday";
}
Title = "DATABASE READOUT - " + Name() + "\n"
+ IntervalLine + "\n"
+ "Time shift: " + NumToStr( TimeShiftHours, 1.2 ) + " h"
+ " (" + NumToStr( TimeShiftSeconds, 1.0 ) + " s)\n"
+ "Bars loaded: " + NumToStr( BarsLoaded, 1.0 )
+ " Sessions: " + NumToStr( SessionCount, 1.0 )
+ " Bars per session: " + NumToStr( LastValue( BarsPerSession ), 1.1 ) + "\n"
+ "First bar: " + DateTimeToStr( FirstBarDateTime )
+ " Last bar: " + DateTimeToStr( LastBarDateTime )
+ " Span: " + NumToStr( SpanDays, 1.1 ) + " days\n"
+ ReplayLine;
_SECTION_END();

Download database-settings-readout.afl100 lines

Check three things. Chart interval matches the base interval you chose. Time shift matches what you set, or is zero if you shifted at import time instead. Bar Replay: not active — because if it is active, every number in passes 2 and 3 will be wrong in the same direction and you will spend an hour on it.

Write down the Bars per session figure. That is your real conversion factor between bars and days, and it is the number Route A’s step A5 was guessing at.

Analysis window, Apply to = All symbols, Range = All quotations, Explore.

Complete runnable AFL

history-depth-report.afl
// ===========================================================================
// History depth report
// One row per symbol, answering the question a backfill never answers by
// itself: how much history did each symbol actually end up with? Symbols in
// the same database, added on different days, from the same feed, routinely
// hold very different amounts of history - and nothing on a chart says so
// until you scroll back far enough to fall off the end of the data.
//
// HOW TO RUN
// Analysis window: Apply to = All symbols (or a watch list),
// Range = All quotations, then Explore.
// Run it on an intraday database with the chart interval set to the base
// interval, so that "Bars" means base-interval bars.
//
// WHAT EACH COLUMN MEANS
// Bars bars this symbol supplied inside the range
// First / Last the ends of the loaded history
// Cal.days calendar days between the first and the last bar
// Sessions distinct calendar dates covered
// Bars/session Bars divided by Sessions
// Fullest bars in the busiest single session
// Thinnest bars in the emptiest single session
// Last bar age h hours between the newest bar and your computer clock
//
// ASSUMPTIONS, STATED SO THEY CAN BE CHECKED
// - A short history is not automatically a fault. A symbol listed last
// month cannot have a year of bars, and a vendor's backfill depth is a
// limit you were told about in advance. This report tells you what you
// have; deciding whether that is wrong is your job.
// - A session here is a distinct calendar date. For instruments that trade
// overnight, one exchange session spans two dates and the session count
// will be roughly double what a trader would say.
// - "Last bar age" compares a bar timestamp against your local clock. If
// the database time shift is set so that bar times are exchange times
// rather than local times, the age is off by the shift. Read the shift
// with Status("timeshift") before drawing conclusions from this column.
// - Thinnest sessions are frequently real: half-day sessions before public
// holidays are short by design, and an illiquid symbol simply prints no
// bar in a minute in which nothing traded.
// ===========================================================================
Filter = Status( "lastbarinrange" );
SetOption( "NoDefaultColumns", True );
BarNumber = Cum( 1 );
BarsInRange = LastValue( BarNumber );
FirstBarDateTime = ValueWhen( BarNumber == 1, DateTime() );
// Cum(1) == 1 forces the first bar to open a session, because Ref() has no
// previous bar to compare against there.
NewSession = BarNumber == 1 OR Day() != Ref( Day(), -1 );
SessionCount = Cum( NewSession );
// Bars elapsed since the session opened, counted on every bar.
SessionStartBar = ValueWhen( NewSession, BarNumber );
BarsInSession = BarNumber - SessionStartBar + 1;
// The last bar of a session is the bar before the next session opens. On the
// final bar of the array there is no next bar, so the analysis-range flag is
// used to make sure the newest session is still measured.
EndOfSession = Nz( Ref( NewSession, 1 ) ) OR Status( "lastbarinrange" );
SessionTotal = IIf( EndOfSession, BarsInSession, Null );
// Highest() and Lowest() are running extremes over everything seen so far, so
// LastValue() of them is the extreme over the whole loaded history.
FullestSession = LastValue( Highest( Nz( SessionTotal ) ) );
ThinnestSession = LastValue( Lowest( IIf( EndOfSession, BarsInSession, 999999 ) ) );
CalendarDays = DateTimeDiff( DateTime(), FirstBarDateTime ) / 86400;
// Now(5) returns the current date and time as a DateTime number.
LastBarAgeHours = DateTimeDiff( Now( 5 ), DateTime() ) / 3600;
AddTextColumn( Name(), "Symbol", 1.0, colorDefault, colorDefault, 80 );
AddColumn( BarsInRange, "Bars", 1.0 );
AddColumn( FirstBarDateTime, "First", formatDateTime );
AddColumn( DateTime(), "Last", formatDateTime );
AddColumn( CalendarDays, "Cal.days", 1.1 );
AddColumn( SessionCount, "Sessions", 1.0 );
AddColumn( IIf( SessionCount > 0, BarsInRange / SessionCount, Null ),
"Bars/session", 1.1 );
AddColumn( FullestSession, "Fullest", 1.0 );
AddColumn( ThinnestSession, "Thinnest", 1.0 );
AddColumn( LastBarAgeHours, "Last bar age h", 1.1 );
// Newest first bar at the top: the symbols with the least history are the ones
// that will silently shorten every study you run across this database.
SetSortColumns( -3 );

Download history-depth-report.afl89 lines

Sort by the First column. On Route A, expect variation — symbols added at different times reach back different distances. On Route B, expect uniformity, because every symbol’s history is exactly the file you imported. Either way, the top row of the descending sort is the constraint on every multi-symbol study you will ever run against this database.

Same Analysis settings, with Show only suspect sessions left on for the first run.

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

Interpret it in the order the previous page established: a short session with Missing near zero on the same date for every symbol is a half-day and is fine. Missing spread thinly across many sessions on one symbol is illiquidity, not damage. late or early clustered on specific dates is truncated data, and it is what you go and fix.

Section titled “Pass 4 (optional but recommended): the Bar Replay smoke test”

Tools → Bar Replay. Set Start to a date in the middle of your data, set Step interval to your database’s base interval — which is the guide’s own recommendation — and press Play at speed 1.

Two things should happen. The chart should advance one bar at a time, and the readout formula’s last line should show the replay warning. Press Stop and everything returns; nothing was written to disk. You have just confirmed that your database can drive the whole of Part 26 without a live feed.

Do not call the lab finished until every line has an answer.

# Check Where Pass condition
1 Base time interval is the value you chose File → Database Settings Matches your written decision
2 Intraday chart intervals are enabled View → Intraday Not greyed out
3 Chart interval on the i button equals the base interval Readout, line 1 Matches check 1
4 Time shift is what you intended Readout, line 2 Matches your note, or zero by design
5 Bar Replay is not active Readout, last line “not active”
6 Bars per session measured, not assumed Readout, line 3 A number you wrote down
7 Days-equivalent read before accepting the bar count (Route A) File → Database Settings Consistent with your session target
8 Every symbol has bars History depth report No zero-bar rows
9 First-bar dates understood History depth report You can explain the spread
10 Coverage statement written Your notes One sentence, with a date in it
11 Suspect sessions triaged Completeness check Each cluster classified as fault or not
12 Quote Editor cross-check on one suspect session Symbol → Quote Editor Confirms hidden versus missing
13 Candlesticks draw Any chart Continuous quotations on
14 Database folder backed up Your file system A copy exists elsewhere
15 Settings recorded Your notes Five lines, listed below

Add these five lines to the record you have been keeping since Part 3. Parts 20 to 26 all refer back to them, and reconstructing them from memory in the middle of a session-boundary problem is miserable.

  1. Database folder path, and whether the data source is (local) or a plug-in.
  2. Base time interval.
  3. Number of bars to load, and the days-equivalent AmiBroker displayed — or, for a local database, the date range of the files you imported.
  4. Session filtering mode and the day-session trading hours you entered.
  5. Time shift, and where it is applied: Intraday Settings, $TIMESHIFT at import, or nowhere.

Part 20 takes the session and time-shift settings you have just entered and shows what happens when they are subtly wrong — an opening range that moves twice a year is the classic symptom. Part 21 puts charts on this database. Part 24 scans it. Part 26 uses Bar Replay on it, which is why pass 4 is worth doing now rather than discovering a problem three parts later.

If you took Route B, none of that changes. The only thing you do not have is a stream of live quotes, and the course tells you plainly, in Parts 22 and 23, exactly which two lessons that limits and what you do instead.

Check your understanding

Question 1. Why does this lab use the ASCII importer with a .format file rather than the Import Wizard?
Show the answer and why

Answer: The wizard’s documented field types do not include a time field, so it cannot import intraday timestamps

The documented list of field types the wizard offers is Ticker, YMD, DMY, MDY, Open, High, Low, Close and Volume — no time. The guide describes the wizard as a subset of the importer intended for novice users. Intraday work needs the full importer.

Question 2. Your comma-separated file imports as a single unparseable column. What is the most likely cause?
$FORMAT Ticker, Date_YMD, Time, Open, High, Low, Close, Volume
Show the answer and why

Answer: The format definition is missing $SEPARATOR , and the importer default is a space

The ASCII importer’s default separator is a space, not a comma. Declaring the field order correctly does nothing if the importer cannot split the line in the first place. A header row would need $SKIPLINES, and $AUTOADD affects symbol creation rather than parsing.

Question 3. In a (local) intraday database, which of these actually affect how much history you have? Select all that apply.
Show the answer and why

Answer: The date range of the files you import, Limit number of saved quotations in Preferences → Data

Number of bars to load and Local data storage govern data pulled from an external source and are inert in a local database. The saved-quotations limit is a different setting and can genuinely discard intraday history, which is why the guide recommends at least 3,000 for intraday.

Question 4. You imported the same intraday file twice by mistake. What should you expect?
Show the answer and why

Answer: Bars at the same interval overwrite rather than duplicate, so the history is unchanged

Imports at the same bar interval are additive on a per-timestamp basis, which is exactly what makes overlapping files a repair mechanism for gaps. The destructive mode is $TICKMODE, which deletes existing quotes before importing and is only for tick data with duplicate timestamps.

Question 5. Pass 1 shows "Bar Replay: not active", the chart interval matches the base interval, and bars per session is 588 on a database you believed held only the regular session. What have you learnt?
Show the answer and why

Answer: The feed or the file is storing extended-hours bars, so your bar budget covers fewer calendar days than you assumed

A 09:30 to 16:00 session is 390 minutes, so 588 bars per session means bars outside it are being stored. Nothing is wrong — but every piece of sizing arithmetic based on 390 is now understated, which is precisely why the lab asks you to measure this number rather than assume it.

Sources for this lesson

7 verified · checked 2026-08-31

  1. 01AmiBroker User's Guide — Database Settings windowamibroker.com/guide/w_dbsettings.html2026-08-31
  2. 02AmiBroker User's Guide — ASCII importer and format definition filesamibroker.com/guide/d_ascii.html2026-08-31
  3. 03AmiBroker User's Guide — ASCII Import Wizardamibroker.com/guide/w_impwizard.html2026-08-31
  4. 04AmiBroker User's Guide — Working with real-time data sourcesamibroker.com/guide/h_rtsource.html2026-08-31
  5. 05AmiBroker User's Guide — Bar Replayamibroker.com/guide/w_barreplay.html2026-08-31
  6. 06AmiBroker User's Guide — Symbol Information windowamibroker.com/guide/w_information.html2026-08-31
  7. 07AmiQuote 4.00 Read Me — supported data sourcesamibroker.com/devlog/wp-content/uploads/2019/05/aqreadme4000.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.