Exporting Results and Building a Daily Workflow
A screen that lives inside AmiBroker’s result list is a screen you run when you happen to remember. A screen with a route out — into a file, a watch list, a journal — is the beginning of a routine.
This lesson is about that route, and about a discipline that matters more than any of the mechanics: keeping a record of what you actually ran, so that a candidate list you saved three months ago can still be explained.
Getting the result list out
Section titled “Getting the result list out”The menu is not where people look for it.
To export data to a CSV file or an HTML file, use the File → Export HTML/CSV menu (from the main window). Note that these menu items appear only if you have the New Analysis window active.
That last sentence is the whole difficulty. The command lives on the main window’s File menu, and it only appears while the Analysis window is the focused window. Click into the Analysis window, then open File, and it is there.
File → Import HTML… reads a previously exported HTML file back in, which is occasionally
useful for comparing today’s list against last week’s.
Designing an exploration for export rather than for reading
Section titled “Designing an exploration for export rather than for reading”An exploration you read and an exploration you export want different things. On screen you want readability. In a file you want predictability — fixed column positions, unambiguous dates, and no formatting that a parser has to undo.
Three settings do most of the work.
SetOption( "NoDefaultColumns", True ) removes the automatic Ticker and Date/Time columns.
That sounds like a loss and is a gain: the automatic columns’ presence depends on a setting
somebody could change, so leaving them in means your column numbers are not yours to control.
Turn them off, then add exactly the columns you want, in the order you want them.
formatDateTimeISO is the format constant to use for any exported date. NumToStr’s
documentation introduces it in 6.20: it produces YYYY-MM-DD HH:MM:SS, an international format,
rather than formatDateTime, which follows Windows regional settings. A file whose date column
means something different on a colleague’s machine is a file you will eventually misread.
SetSortColumns( n ) takes one-based column numbers; a negative number sorts descending.
For an export that will be appended to over time, sort ascending by date so the accumulated file
stays chronological.
The export formula
Section titled “The export formula”Complete runnable AFL
// signal-export.afl// Part 12 - Exporting Results and Building a Daily Workflow//// An exploration whose output is not meant to be looked at. Every column is// chosen so that the exported CSV can be read by another program without any// cleaning step: no locale-dependent dates, no thousands separators in the// wrong place, no automatic columns whose position depends on a setting.//// This is the shape of the official "signal file" recipe, extended with the// two columns a downstream tool almost always needs: the symbol and a// reference price.//// Assumptions:// - Daily bars.// - The consumer of this file treats it as a record of what the rules said// on a given bar, not as an instruction. Nothing here models slippage,// costs, position size or whether the price was reachable.//// TO EXPORT: run it with Explore, then use the MAIN window's// File -> Export HTML/CSV menu. That menu entry only appears while the// Analysis window is the active window, which is why people cannot find it.
FastPeriod = 20;SlowPeriod = 50;
Fast = MA( Close, FastPeriod );Slow = MA( Close, SlowPeriod );
Buy = Cross( Fast, Slow );Sell = Cross( Slow, Fast );
// The two automatic columns are switched off so that column positions are// fixed and predictable. Everything the file needs is then added explicitly,// in the order the consumer expects. Remember that this renumbering also// shifts every column number used by SetSortColumns and AddSummaryRows.SetOption( "NoDefaultColumns", True );
Filter = Buy OR Sell;
AddTextColumn( Name(), "Symbol", 16 );AddColumn( DateTime(), "Date", formatDateTimeISO );
// formatChar prints the single ASCII character whose code is in the array:// 66 is "B", 83 is "S". One character is easier for a downstream parser than// a word whose spelling might change.AddColumn( IIf( Buy, 66, 83 ), "Side", formatChar );
AddColumn( Close, "RefPrice", 1.4 );AddColumn( Volume, "Volume", 1.0 );
// Oldest first, so that appending a later export to the same file keeps the// rows in chronological order. Column 2 is the date because the automatic// columns are gone.SetSortColumns( 2 );Notice the Side column:
Fragment — not a complete formula
// 66 is "B", 83 is "S". formatChar prints the ASCII character// whose code is in the array.AddColumn( IIf( Buy, 66, 83 ), "Side", formatChar );AddColumn() takes a numeric array, so text cannot be put in it directly. formatChar prints
the single character whose ASCII code the array holds, which gives a one-character side flag —
easier for a downstream parser than a word, and impossible to get wrong through a spelling
change.
Route two: writing to a watch list
Section titled “Route two: writing to a watch list”Sometimes you do not want a file — you want the candidates to appear in AmiBroker’s own symbol tree so you can page through their charts.
CategoryAddSymbol( symbol, category, number ) adds a symbol to a category.
For categoryWatchlist the number is the watch list number, and the documentation notes that
watch lists — unlike markets, groups and sectors — allow a symbol to belong to more than one.
An empty symbol string means the current symbol.
Fragment — not a complete formula
// Inside a Scan or Exploration, on the last bar of the range only.CandidateToday = Status( "lastbarinrange" ) AND Liquid AND UpTrend AND Setup;
if( LastValue( CandidateToday ) ) CategoryAddSymbol( "", categoryWatchlist, 31 );Two cautions, both practical rather than exotic.
Adding is not replacing. Run this daily and watch list 31 accumulates. Clear it first — right-click the watch list in the symbol tree — or accept that it is a running log rather than today’s list.
Pick an empty watch list number and write it down. Watch lists are stored as .TLS text
files in the Watchlists folder of the database, with index.txt defining the order, so a
mistake here is recoverable — but it is far easier not to overwrite something you care about.
Route three: chaining steps
Section titled “Route three: chaining steps”Two mechanisms exist, and they are for different sizes of job.
#pragma sequence — quick chaining inside one Analysis window
Section titled “#pragma sequence — quick chaining inside one Analysis window”AmiBroker 6.40 added a Run Sequence toolbar button in the Analysis window, driven by a preprocessor directive:
Fragment — not a complete formula
#pragma sequence( scan, explore )
if( Status( "action" ) == actionScan ){ AddToComposite( /* ... */ ); _exit(); // nothing else to do in the scan pass}
Filter = /* ... */; // the exploration pass, which can useAddColumn( /* ... */ ); // whatever the scan pass just builtThe documented use case is exactly this shape: a first pass that builds something —
composites, static variables — and a second pass that consumes it, both from one button.
Status( "action" ) is what lets one file behave differently in each pass, and _exit() ends
the pass early once its work is done.
The official documentation is explicit that this is not a replacement for batches. It is for “quick hacking” — chaining two or three steps you are actively developing.
The Batch window — the real automation
Section titled “The Batch window — the real automation”Introduced in version 6.20, the Batch window automates sequences that previously needed OLE
programming. It works on .APX files — Analysis Project files — and that is the key idea:
an .APX is self-contained and holds the formula, all the options and settings, and the apply-to
and range selections.
Documented batch step types include:
- load an
.APXproject - optionally set the current symbol
- run a Scan, Exploration, Backtest, Optimization or Walk-forward
- export results to CSV or HTML
- notify with a sound file or spoken text
- write text to a log file
- load or save the database
- execute an external program and wait for it
- run a vendor-specific data-plugin command
- import quotation data from ASCII files
The part that matters most: a record of what you ran
Section titled “The part that matters most: a record of what you ran”Here is the situation this section exists to prevent. You find a folder of exported candidate lists from six months ago. You cannot reproduce them. Was the liquidity threshold a million or five? Which watch list? Which date range? Was that before or after you changed the trend filter?
The formula was in the file. The settings were not — they were in a dialog.
A screen that journals itself
Section titled “A screen that journals itself”Complete runnable AFL
// screen-log.afl// Part 12 - Exporting Results and Building a Daily Workflow//// A screen that keeps a journal of itself. Every run appends one line to a CSV// file recording what was actually run: when, which formula, which universe// setting, which watch list, which range, and the thresholds in force. Six// months later that journal is the only thing that can tell you whether a// candidate list you saved came from the rules you think it did.//// The journal records the INPUTS of the run. It cannot record how many rows// came back, because it is written before the run finishes; the exported// result file is the record of the output. Keeping the two side by side, named// with the same date, is the whole discipline.//// Assumptions:// - Daily bars.// - The folder in LogFolder already exists. AmiBroker will not create it,// and fopen() simply returns 0 if it cannot open the file.// - Backslashes in AFL string literals must be doubled.
LogFolder = "C:\\AmiBrokerCourse\\";LogPath = LogFolder + "screen-journal.csv";
MinTurnover = 1000000;LiquidityPeriod = 50;TrendPeriod = 200;SetupPeriod = 10;
// Status("stocknum") == 0 is a recognised special form: AmiBroker detects this// exact statement, runs the first symbol on a single thread and waits for it// before starting the others. That makes it the sanctioned place for// once-per-run work such as this. It must not be moved into an #include file,// or the detection - and the single-threaded first pass - does not happen.if( Status( "stocknum" ) == 0 ){ // shared = True asks fopen to open the file in share-aware mode and to // retry on a sharing violation. In a multi-threaded Analysis run that is // not optional politeness; without it two threads can corrupt the file. LogHandle = fopen( LogPath, "a", True );
if( LogHandle ) { JournalLine = Now( 0 ) + "," + GetFormulaPath() + "," + "applyto=" + NumToStr( GetOption( "ApplyTo" ), 1.0 ) + "," + "watchlist=" + NumToStr( GetOption( "FilterIncludeWatchlist" ), 1.0 ) + "," + "rangefrom=" + NumToStr( Status( "rangefromdate" ), 1.0 ) + "," + "rangeto=" + NumToStr( Status( "rangetodate" ), 1.0 ) + "," + "minturnover=" + NumToStr( MinTurnover, 1.0 ) + "," + "trendperiod=" + NumToStr( TrendPeriod, 1.0 ) + "\n";
fputs( JournalLine, LogHandle ); fclose( LogHandle ); }}
AvgTurnover = MA( Close * Volume, LiquidityPeriod );Trend = MA( Close, TrendPeriod );
Liquid = AvgTurnover > MinTurnover;UpTrend = Close > Trend;Setup = Close >= HHV( Close, SetupPeriod );
Filter = Status( "lastbarinrange" ) AND IsTrue( Liquid AND UpTrend AND Setup );
AddTextColumn( FullName(), "Name", 34 );AddColumn( Close, "Close", 1.2 );AddColumn( AvgTurnover, "Turnover 50d", 1.0 );AddColumn( 100 * ( Close - Trend ) / Trend, "% from MA200", 1.1 );AddColumn( DateTime(), "As of", formatDateTimeISO );
AddSummaryRows( 16, 1.0, 4 );Every run appends one line to a CSV recording when it ran, which formula file, the Apply To setting, the watch list filter, the date range and the thresholds in force.
The retrieval functions are all documented:
| Call | What it records |
|---|---|
Now( 0 ) |
when the run happened |
GetFormulaPath() |
the full path of the formula being executed |
GetOption( "ApplyTo" ) |
0 = all symbols, 1 = current symbol, 2 = filter |
GetOption( "FilterIncludeWatchlist" ) |
−1 if no watch list filter, otherwise its index |
Status( "rangefromdate" ) / Status( "rangetodate" ) |
the range actually used |
The thresholds are written from the formula’s own constants, so the journal cannot disagree with the run.
Why it is wrapped in Status( "stocknum" ) == 0
Section titled “Why it is wrapped in Status( "stocknum" ) == 0”An Analysis run executes the formula once per symbol, in parallel across threads. Without a guard, a journal line would be written once per symbol.
if( Status( "stocknum" ) == 0 ) is a recognised special form. AmiBroker detects this exact
statement, runs the first symbol on a single thread and waits for it to finish before starting
the others — which makes it the sanctioned place for once-per-run setup work.
Note what the journal deliberately does not record: how many rows came back. It is written before the run finishes. The exported result file is the record of the output; the journal is the record of the inputs. Naming both with the same date is the whole discipline.
A ten-minute daily routine
Section titled “A ten-minute daily routine”A repeatable end-of-day routine
- Update dataAmiQuote or your feed. Two minutes, and it is the step whose failure is least visible — check the last bar date on a symbol you know.
- Run the screenOne Analysis window, one .APX, Run. The journal line is written automatically.
- Export the result listFile -> Export HTML/CSV from the MAIN window, with the Analysis window active. Name it with today's date.
- Look at the chartsPage through the candidates. This is the only step that cannot be automated, and it is the one that catches data errors.
- Write down what you decidedIncluding "nothing". A routine that only records action days cannot tell you how selective you actually were.
Two observations about this routine that are worth more than the routine itself.
The chart review is not optional and not automatable. A screen cannot tell you that a symbol gapped 40% on a takeover, that the “breakout” is a bad print, or that the instrument is a delisted shell. Ten seconds a chart on twenty candidates is three minutes, and it is where data problems get caught.
Recording the no-action days is what makes the record honest. A file that contains only the days you did something cannot tell you how often the screen fired, and a screen whose base rate you do not know is a screen you cannot evaluate. This is the same argument the reality-check lessons make about base rates, applied to your own behaviour.
Exporting is a main-window File menu that only appears while the Analysis window is active.
An exploration built for export wants NoDefaultColumns on, ISO dates, fixed column positions,
and column numbers that you own — remembering that turning the default columns off renumbers
everything SetSortColumns() and AddSummaryRows() refer to. CategoryAddSymbol() puts
candidates in a watch list; it adds rather than replaces. #pragma sequence chains a couple of
steps behind one button; the Batch window automates real sequences via .APX project files.
And the piece nobody builds until they have been burned: a journal line per run, written once
under Status( "stocknum" ) == 0 with fopen( …, "a", True ), recording the settings that live
in dialogs rather than in the formula.
Check your understanding
Sources for this lesson
10 verified · checked 2026-08-31
- 01AmiBroker User's Guide — New Analysis window§ Exporting and Importing the Result List; Running a sequence of actionsamibroker.com/guide/h_newanalysis.html2026-08-31
- 02AmiBroker User's Guide — Using Batch windowamibroker.com/guide/h_batch.html2026-08-31
- 03AmiBroker User's Guide — Working with watch listsamibroker.com/guide/h_watchlist.html2026-08-31
- 04AFL Function Reference — "#pragma"amibroker.com/guide/afl/_pragma.html2026-08-31
- 05AFL Function Reference — fopen§ shared parameter (new in 5.80)amibroker.com/guide/afl/fopen.html2026-08-31
- 06AFL Function Reference — GetOptionamibroker.com/guide/afl/getoption.html2026-08-31
- 07AFL Function Reference — SetSortColumnsamibroker.com/guide/afl/setsortcolumns.html2026-08-31
- 08AFL Function Reference — AddSummaryRowsamibroker.com/guide/afl/addsummaryrows.html2026-08-31
- 09AFL Function Reference — CategoryAddSymbolamibroker.com/guide/afl/categoryaddsymbol.html2026-08-31
- 10AmiBroker User's Guide — Multithreading§ Status("stocknum") == 0amibroker.com/guide/h_multithreading.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.