Skip to content
Level 4 · Trading System ResearcherLessonPart 29 · page 1 of 628 min
28Minutes
10AFL functions
5Sources
StandardRequires
AFL functions taught here10

Reading the Backtest Report

A backtest report is a long document, and almost nobody reads it as one. People scroll to the number they were hoping for, feel something about it, and then read the rest of the report in the light of that feeling. If the number was good, the trade list becomes confirmation; if it was bad, the settings become suspect. The report has not changed. The reader has.

The defence is procedural rather than intellectual. Decide the order in which you will look at things before you look at any of them, put the numbers that can invalidate the test in front of the numbers that can excite you, and keep the same written record every time so that two runs a month apart are actually comparable. By the end of this lesson you will have that order, that record, and a clear picture of what the report contains.

Run a backtest from the Analysis window and click the Report button. That opens the report for the run you have just done.

The drop-down arrow on the same Report button opens the Report Explorer, which lists every report AmiBroker has stored, not only the most recent one. This is more useful than it sounds. Six weeks into developing a strategy you will want to know what the fourth variation did, and the Report Explorer is the only place that still knows.

AmiBroker’s own documentation names two pages of the report explicitly — the statistics page, where the metrics live, and the Monte Carlo page, which appears when Monte Carlo simulation is enabled in the settings. It separately documents the other contents: the charts, the trade list, the formula and the settings.

This is the table of metrics, and it is what the next three lessons are about. Two structural facts matter before any individual number does.

First, the modern portfolio backtester reports statistics in three columns: All trades, Long trades and Short trades. Where a system takes no short trades, the short column reads N/A. The old, pre-portfolio backtester only ever produced an all-trades column, which is one quick way to tell whether a forum post you are reading describes the engine you are using.

Second, hovering the mouse over a field name shows a tooltip with that metric’s short description — the same text the User’s Guide carries. When you cannot remember whether Recovery Factor divides by the currency drawdown or the percentage one, the answer is already on your screen.

The report’s charts are ordinary AFL formulas that live in the Charts tree’s Report Charts folder. Each is rendered to a bitmap of fixed dimensions and embedded in the HTML, which explains the classic complaint that the profit table is unreadable: the numbers are too big for the picture. The fix is Settings → Report → Chart dimensions (in pixels), and it only affects reports generated after the change. Reports already stored keep the picture they were built with.

Two further facts about this page. You can add your own report charts by putting an AFL file in that folder. And EnableTextOutput( 3 ) lets a report chart emit HTML instead of a bitmap, which is how AmiBroker’s own profit table stopped being a scaling problem.

Settings → Report has a check box, Include trade list in the report, on by default, and a radio group, Result list shows:, with three choices that change what the Analysis window’s result list contains:

Setting What you get When you want it
Trade list One row per trade, ordered by exit date by default Normal reading, sampling trades
Detailed log One row per data bar, showing scores, positions, and the reason a trade could or could not be opened Debugging position sizing, ranking, and “why did nothing happen?”
Summary One row per backtest, containing the summary statistics One-line-per-run output, optimizations

The columns visible in AmiBroker’s own documentation screenshot of a trade list include the position value, cumulative profit, # bars, Profit/bar, MAE and MFE, plus any per-trade custom metrics appended on the right. MAE and MFE are percentages — the maximum adverse and maximum favourable excursion of each trade.

The guide’s own warning is worth repeating: trade lists “may be huge and consume quite a bit of disk space”. Turn the option off before a large optimization, and back on when you are reading a single run.

Settings has two check boxes, Formula and Settings, which embed the exact AFL source and the exact backtester settings into the stored report. Both live under the settings dialog’s Old tab, which is a genuinely confusing place for them, because they control the content of the report you actually read.

Turn both on and leave them on. A stored report that contains its own formula and its own settings is reproducible six months later by someone who has forgotten everything, including you. A stored report that contains only numbers is an anecdote.

Every lesson in this part refers to the report produced by one formula. It is an unremarkable long-only breakout, and its rules are not the point — the assumption block at the top is.

Complete runnable AFL

report-reference-system.afl
// report-reference-system.afl
// Part 29 - Understanding Backtest Results
//
// The single backtest whose report every lesson in this part reads.
//
// The rules are deliberately ordinary - a long-only breakout taken only in an
// established uptrend, on a liquid universe, with one maximum-loss stop. The
// point of this file is not the rules. The point is that every assumption the
// report depends on is written down in one place, so that when you read a
// number you can say what produced it.
//
// No result is claimed for this system. Nothing here is a recommendation.
//
// ------------------------------------------------------------------------
// ASSUMPTIONS - a backtest report without these attached is not evidence
// ------------------------------------------------------------------------
// Universe Whatever watch list you apply it to, further filtered below
// by average traded value. If your list was built from today's
// index membership, it is survivorship-biased and the report
// describes a past that never existed. Part 30 covers that.
// Periodicity Daily bars. Set Analysis -> Settings -> General -> Periodicity
// to Daily. Every metric in the report changes if you do not.
// Timing Signals are computed on the close of the signal bar and acted
// on at the NEXT bar's open: SetTradeDelays( 1, 1, 1, 1 ) with
// the trade prices taken from Open. Nothing in this formula is
// allowed to read a price it could not have known.
// Slippage A fixed percentage moved against you on every fill. Crude,
// but stated. PriceBoundChecking is left on, so AmiBroker keeps
// the adjusted price inside the bar's High-Low range.
// Commission 0.10 per cent of trade value per side (CommissionMode 1).
// Interest Uninvested cash earns nothing. This is set deliberately: a
// non-zero Settings interest rate quietly credits a low-exposure
// system with income it did not trade for, and that income lands
// in Net Profit and in Annual Return %.
// Leverage None. AccountMargin 100 is a cash account.
// Stops One maximum-loss stop, checked against the High-Low range but
// exited at the NEXT bar's open (ExitAtStop = 2). Exiting at the
// exact stop price assumes a fill that a gap does not offer.
// Capacity NOT modelled. The liquidity filter is a proxy; the backtester
// still fills the whole position at a single price. Every figure
// in the report inherits that assumption.
// ------------------------------------------------------------------------
// ---- Account and portfolio ---------------------------------------------
MaxPositions = 10;
SetOption( "InitialEquity", 100000 );
SetOption( "MaxOpenPositions", MaxPositions );
SetOption( "AccountMargin", 100 ); // 100 = cash account
SetOption( "InterestRate", 0 ); // idle cash earns nothing
SetOption( "AllowPositionShrinking", False ); // no half-sized entries
SetOption( "MinPosValue", 2000 ); // skip trades too small to be real
SetOption( "PriceBoundChecking", True ); // keep fills inside the bar
// Equal weight across the maximum number of simultaneous positions.
SetPositionSize( 100 / MaxPositions, spsPercentOfEquity );
// ---- Costs --------------------------------------------------------------
SetOption( "CommissionMode", 1 ); // 1 = percent of trade value
SetOption( "CommissionAmount", 0.10 ); // 0.10% per side
SlippagePercent = Param( "Slippage per side (%)", 0.05, 0, 0.50, 0.01 );
// ---- Execution timing ---------------------------------------------------
SetTradeDelays( 1, 1, 1, 1 );
BuyPrice = Open * ( 1 + SlippagePercent / 100 );
SellPrice = Open * ( 1 - SlippagePercent / 100 );
// Stops are executed before regular signals, so cash freed by a stop is
// available on the same bar. Turn this on if you want the opposite.
SetOption( "ActivateStopsImmediately", False );
// ---- Universe filter ----------------------------------------------------
// Traded value, not share volume: a 3.00 share and a 300.00 share trading the
// same money are equally tradeable, and their share counts are not comparable.
MinTurnover = Param( "Min. avg daily turnover", 2000000, 0, 50000000, 100000 );
TurnoverPeriod = Param( "Turnover average (bars)", 50, 5, 250, 5 );
AvgTurnover = MA( Close * Volume, TurnoverPeriod );
LiquidEnough = AvgTurnover > MinTurnover;
// ---- Rules --------------------------------------------------------------
TrendPeriod = Param( "Trend average", 200, 50, 400, 10 );
EntryPeriod = Param( "Breakout lookback", 50, 10, 200, 5 );
ExitPeriod = Param( "Exit lookback", 25, 5, 100, 5 );
MaxLossPct = Param( "Max. loss stop (%)", 12, 2, 40, 1 );
Uptrend = Close > MA( Close, TrendPeriod );
// Ref( ..., -1 ) so that today's own high is not part of the level it must
// exceed. Comparing HHV with the bar that produced it is a silent no-op.
BreakoutLevel = Ref( HHV( Close, EntryPeriod ), -1 );
ExitLevel = Ref( LLV( Close, ExitPeriod ), -1 );
Buy = Uptrend AND LiquidEnough AND Close > BreakoutLevel;
Sell = Close < ExitLevel;
Short = False;
Cover = False;
// When more symbols signal than there are free position slots, the ranking
// decides which ones are taken. Ranking on longer-term momentum is a choice;
// record it, because it is part of the system, not a detail.
//
// AmiBroker ranks on the ABSOLUTE value of PositionScore unless you turn on
// SeparateLongShortRank, so a score that can go negative silently promotes the
// worst candidates. ROC() cannot fall below -100 for a positive price, so
// shifting it by 100 keeps the score non-negative and the ordering intact.
PositionScore = 100 + ROC( Close, 100 );
// ExitAtStop = 2: the High-Low range is checked, but the exit is taken at the
// next bar's open rather than at the exact stop price.
ApplyStop( stopTypeLoss, stopModePercent, MaxLossPct, 2 );

Download report-reference-system.afl114 lines

Run it once on a watch list of your own, over a range of your own, with Periodicity set to Daily, and keep the report open as you read the rest of this part.

A reading order that resists wishful thinking

Section titled “A reading order that resists wishful thinking”

The order below is deliberately hostile to enthusiasm. Everything that could tell you the test is not worth reading comes before anything that could tell you the test went well.

Read the report in this order

  1. 1. Settings and formulaWhat was actually run, before any number
  2. 2. Number of tradesIs there enough evidence to read at all?
  3. 3. Exposure %How much of the time was money at work?
  4. 4. Drawdown and the equity curveWhat would holding this have felt like?
  5. 5. Annual Return % and CAR/MaxDDOnly now, the headline
  6. 6. Trade distributionPayoff Ratio, Profit Factor, largest win
  7. 7. The trade listSample it. Do the trades look possible?
  8. 8. Write it downThe record, not the report, is the artefact
Invalidating information first; flattering information last.

Each step earns its place:

  1. Settings and formula. Wrong periodicity, wrong date range, a commission of zero or trade delays of zero make every subsequent number meaningless. Checking this first costs thirty seconds and saves whole afternoons.
  2. Number of trades. Thirty trades cannot support a conclusion about a win rate, a Profit Factor or anything else. Establish the sample size before you form an opinion that the sample size cannot carry.
  3. Exposure %. This changes what every return figure means, and it is the denominator of two of them. A system that is in the market 8 per cent of the time and one that is in it 95 per cent of the time are not doing comparable things, whatever their returns.
  4. Drawdown and the equity curve. Look at the worst decline and the shape of the curve before the return, because the drawdown is the part you would actually have to survive in order to collect the return.
  5. The headline. Annual Return % and CAR/MaxDD, now that you know what they are a return on and a ratio of.
  6. Trade distribution. Payoff Ratio, Profit Factor, the largest win. If one trade supplied most of the profit, the summary statistics are describing an accident.
  7. The trade list. Sample ten trades at random and ask whether each one could have been taken: was the symbol liquid on that date, was the fill price inside the bar, did the exit happen at a price that was knowable at the time?
  8. The record. Which is the next section.

The report is disposable. The record is not. Keep it in the same shape for every run, in a plain text file or a spreadsheet, so that two runs six weeks apart can be compared without archaeology.

Field Example of what to record
Date run, AmiBroker version 2026-09-01, 7.00.1
Database and data source Local EOD database, vendor and download date
Universe, and how it was built Watch list name, the rule that built it, and whether delisted symbols are in it
Date range and periodicity From-To dates as set in the Analysis window; Daily
Initial equity 100,000
Position size rule Equal weight, 10 per cent of equity, max 10 open positions
Trade delays and trade prices 1 bar all four; entries and exits at the open
Commission mode and amount Percent of trade value, 0.10 per side
Slippage assumption 0.05 per cent per side, applied to the fill price
Stops in force Maximum loss 12 per cent, ExitAtStop mode 2
Rate settings on the Report tab Whatever Settings → Report shows; the Risk-free rates pair is not a constant and two metrics move with it
The question you were asking “Does the trend filter earn its place?”
The metrics you will compare on Chosen before the run, not after

That last pair of rows does most of the work. Choosing the comparison metric before the run is the difference between testing an idea and shopping for a number, and writing down the question stops a run drifting into “let me see what happens if…” without anyone noticing.

You know where the report and the Report Explorer are, and that the Explorer keeps every past run. You know the documented contents: a statistics page with All, Long and Short columns, a charts page built from AFL formulas in the Report Charts folder, a trade list whose format is controlled by a radio group in Settings, and — if you turn them on — the formula and settings that produced it all. You know that the guide does not enumerate the tab strip, so you check that on your own installation rather than taking anyone’s word for it.

More importantly, you have an order to read in and a record to keep. The next lesson starts on the statistics page itself, with the numbers most people read first and this course reads fifth.

Check your understanding

Question 1. A backtest produces no trades at all. Which setting is the documented first diagnostic?
Show the answer and why

Answer: Set Result list shows to Detailed log

Detailed log produces one row per data bar showing scores, positions and the reason a trade could or could not be opened. The usual causes — initial equity too small for the requested position size, a minimum shares or minimum position value veto, wrong periodicity, account margin — are all visible there and none of them are visible in the entry rule.

Question 2. Why does this lesson put Exposure % ahead of Annual Return % in the reading order?
Show the answer and why

Answer: Because Exposure % changes what a return figure means, and is the denominator of two other reported metrics

Exposure % is the market exposure of the system computed bar by bar. Risk Adjusted Return % is Annual Return % divided by it, and Net Risk Adjusted Return % is Net Profit % divided by it. Reading the return before you know the exposure means reading a numerator without its denominator.

Question 3. Which statements about the trade list are correct? Select all that apply.
Show the answer and why

Answer: It is ordered by exit date by default, MAE and MFE in the trade list are expressed in percent, Per-trade custom metrics appear as extra columns on the right

The trade list is ordered by exit date by default, MAE and MFE are percentages, and per-trade custom metrics are appended as columns. The guide warns that trade lists may be huge and consume a lot of disk space, so the recommendation for large optimizations is the opposite: turn the trade list off.

Question 4. You want a stored report to remain interpretable a year from now. What matters most?
Show the answer and why

Answer: Enabling the Formula and Settings check boxes so the report embeds what produced it

The Report Explorer stores every report automatically, but a stored report that contains only numbers cannot be reproduced. The Formula and Settings check boxes embed the exact AFL and the exact backtester settings, which is what makes the run reconstructable. They are found under the settings dialog’s Old tab, which is an unhelpful place for them.

Sources for this lesson

5 verified · checked 2026-08-31

  1. 01AmiBroker User's Guide — System test report windowamibroker.com/guide/w_report.html2026-08-31
  2. 02AmiBroker User's Guide — Using New Analysis window§ Viewing Reports / Running the Report Exploreramibroker.com/guide/h_newanalysis.html2026-08-31
  3. 03AmiBroker User's Guide — System test settings window§ Report tabamibroker.com/guide/w_settings.html2026-08-31
  4. 04AmiBroker User's Guide — How to add user-defined metricsamibroker.com/guide/a_custommetrics.html2026-08-31
  5. 05AmiBroker User's Guide — Portfolio Backtester Interface Reference§ Trade objectamibroker.com/guide/a_custombacktest.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.