Equity Curve Analysis
The statistics page compresses several years of an account’s life into about forty numbers. The equity curve does not compress it, and that is its value: it shows the order in which things happened, which every summary statistic throws away.
Order matters because you experience it. A 22 per cent drawdown that arrives in the third
month and a 22 per cent drawdown that arrives in the eighth year produce the same
Max. system % drawdown and completely different decisions. By the end of this lesson you
will be able to read the curve for what the table cannot show, compute the one thing the
report never gives you, and hold a suspiciously good-looking curve up to the light.
Where the curve comes from
Section titled “Where the curve comes from”Two places, for two different purposes.
The report’s charts page. Report charts are ordinary AFL formulas living in the Charts tree’s Report Charts folder, each rendered to a bitmap of fixed dimensions and embedded in the report HTML. They are what you look at while reading a report.
The ~~~EQUITY symbol. After a portfolio backtest completes, AmiBroker writes the
portfolio equity values into a special symbol called ~~~EQUITY, which you can read from
any formula with Foreign( "~~~EQUITY", "C" ). This is what the built-in portfolio equity
chart does, and it is what lets you compute things the report does not report.
Walk-forward testing produces two further composite symbols, ~~~ISEQUITY and
~~~OSEQUITY, holding the concatenated in-sample and out-of-sample equity. Those belong to
Part 32.
Reading the shape
Section titled “Reading the shape”Four questions, in order.
Is the slope roughly constant, and if not, where did it change? A curve that is flat for years and then rises steeply has not demonstrated a persistent effect; it has demonstrated one favourable period. Note the dates of the change and ask what was happening in the market then. If the rise coincides with one sustained trend, you have learned that the rule participates in trends, not that it works.
Where did the money come from? Follow the curve’s biggest single steps back to the trade list. If four trades produced most of the height, the summary statistics are averages over a distribution dominated by four events.
How long are the flat and declining stretches? This is the next section.
Does the curve’s texture match the market’s? A rule trading volatile instruments with meaningful position sizes should produce a curve with visible texture. One that does not is telling you something, and the last section of this lesson is about what.
Flat periods, and the two things they can mean
Section titled “Flat periods, and the two things they can mean”A flat stretch on an equity curve has two completely different causes, and the curve alone cannot distinguish them:
- No trades. The rule found nothing to do. Capital was idle. This shows up in
Exposure %and in the gap between entries in the trade list. - Trades that netted to nothing. The rule traded continuously and the wins and losses cancelled. Capital was at risk the entire time, and every round trip paid costs.
These are not remotely the same proposition. The first is a system waiting; the second is a system paying to stand still. The trade list tells you which one you are looking at, and it is worth checking every time, because the second case is a good candidate for the rule having stopped working in that period.
Time underwater is the thing you live through
Section titled “Time underwater is the thing you live through”A drawdown ends on the chart when equity makes a new high. It ends for a person somewhere else entirely — when they stop taking the signals.
As the risk metrics lesson established, AmiBroker reports how deep the worst decline was
and does not report how long any decline lasted. There is no row for it and no metric
string for it. Ulcer Index responds to duration implicitly, by summing squared drawdowns
over every bar, but it does not report a duration in units you can act on.
So compute it.
The underwater curve
Section titled “The underwater curve”Draw portfolio equity, the running peak that equity has ever reached, and the distance below that peak on every bar — then report the deepest decline and, the number the report does not contain, the longest stretch the account spent below a previous peak.
This is the shape the formula draws. The top pane holds the two curves; the bottom pane holds the third, which is simply the vertical distance between them:
Equity, its running peak, and the distance between them
- Portfolio equity
- Running peak
The formula
Section titled “The formula”Complete runnable AFL
// underwater-curve.afl// Part 29 - Understanding Backtest Results//// Draws the portfolio equity of the LAST completed portfolio backtest, the// running peak of that equity, and the underwater curve - how far below the// previous peak the account stood on every single bar - and reports the// deepest decline and the longest stretch spent below a previous peak.//// The backtest report tells you how deep the worst drawdown was. It does not// report how long any drawdown lasted. This formula supplies the number the// report leaves out, because duration is the part a person actually lives// through.//// ------------------------------------------------------------------------// ASSUMPTIONS// ------------------------------------------------------------------------// - Run a PORTFOLIO backtest first. AmiBroker writes portfolio equity into// the special ~~~EQUITY symbol only after a backtest completes, so this// chart shows the last run, not the current one.// - Apply it as a chart pane. Do NOT paste it inside a backtest formula:// during a run, Foreign( "~~~EQUITY", "C" ) returns the PREVIOUS// backtest's equity, because the Analysis window keeps a private copy// until the run has finished.// - Depth is measured bar by bar on the equity CLOSE, which is the basis the// portfolio backtester itself uses. Intra-bar excursions are not in it.// - Duration is counted in BARS. On daily data one bar is one trading day,// so roughly 21 bars is a month and roughly 252 bars is a year. Those are// conversions you are doing, not figures AmiBroker reports.// - The chart covers whatever range the chart is showing. To see the whole// test, zoom out to all bars.// ------------------------------------------------------------------------
_SECTION_BEGIN( "Underwater curve" );
// Highest() is a running maximum measured from the FIRST bar in the array, so// this formula must see every bar. QuickAFL would otherwise hand the chart a// truncated array and the "previous peak" would start part-way through the// test. sbrAll (-2) turns QuickAFL off for this pane.SetBarsRequired( sbrAll, sbrAll );
SetChartOptions( 0, chartShowDates | chartWrapTitle );
EquitySymbol = "~~~EQUITY";
// Nz() turns the Nulls outside the tested range into zeros so that a missing// backtest fails visibly rather than propagating Null through everything.PortEquity = Nz( Foreign( EquitySymbol, "C" ) );
// Highest() is a running maximum from the first bar in the database, which is// exactly the "previous peak" an underwater plot needs.PeakEquity = Highest( PortEquity );
// Zero at every new peak, negative everywhere else.Underwater = IIf( PeakEquity > 0, 100 * ( PortEquity - PeakEquity ) / PeakEquity, 0 );
// A bar is at a peak when equity equals the running maximum, so the comparison// has to be >=, not >.AtPeak = PortEquity >= PeakEquity;BarsBelowPeak = BarsSince( AtPeak );
DeepestPercent = LastValue( Lowest( Underwater ) );LongestBars = LastValue( Highest( BarsBelowPeak ) );CurrentPercent = LastValue( Underwater );CurrentBars = LastValue( BarsBelowPeak );
Plot( PortEquity, "Portfolio equity", colorBlue, styleLine | styleThick );Plot( PeakEquity, "Previous peak", colorLightGrey, styleLine | styleDashed );Plot( Underwater, "Underwater %", colorRed, styleArea | styleOwnScale | styleNoLabel );
if ( LastValue( PeakEquity ) <= 0 ){ Title = "No portfolio equity found in " + EquitySymbol + ". Run a portfolio backtest first, then refresh this chart.";}else{ Title = StrFormat( "Portfolio equity %g\n" + "Deepest decline from a previous peak %.2f%%\n" + "Longest stretch below a previous peak %.0f bars\n" + "Right now %.2f%% below the peak, %.0f bars below it", LastValue( PortEquity ), DeepestPercent, LongestBars, CurrentPercent, CurrentBars );}
_SECTION_END();How it works
Section titled “How it works”The formula has four logical sections.
It reads the equity of the last completed backtest from ~~~EQUITY through Foreign,
wrapping it in Nz so that bars outside the tested range become zero rather than Null. A
missing backtest then fails visibly instead of propagating Null through every later
calculation.
It builds the running peak with Highest, which returns the highest value seen from
the first bar in the array up to and including each bar. That is exactly the definition of
“the previous peak” that an underwater plot needs.
It computes depth and duration. Depth is the percentage distance from the peak, guarded
so a zero peak cannot cause a division by zero. Duration comes from BarsSince( AtPeak ),
where a bar counts as being at a peak when equity equals the running maximum — which is
why the comparison is >= and not >. The running maximum of that bar count is the
longest stretch spent below a previous peak.
It reports, plotting equity and the peak on the main scale with the underwater series as a filled area on its own scale, and putting the four summary figures in the title.
The SetBarsRequired( sbrAll, sbrAll ) call at the top matters more than it looks. Under
QuickAFL, AmiBroker may hand a chart formula a truncated array to save time. Highest would
then compute a running maximum starting part-way through the test, and every underwater
figure would be wrong — quietly, and in the flattering direction.
Key functions
Section titled “Key functions”| Function | What it does here |
|---|---|
Foreign( ticker, "C" ) |
Reads the close series of another symbol — here, portfolio equity |
Nz( array ) |
Replaces Null with zero, so a missing backtest fails loudly |
Highest( array ) |
Running maximum from the first bar of the array: the previous peak |
Lowest( array ) |
Running minimum: used to find the deepest point of the underwater series |
BarsSince( condition ) |
Bars elapsed since the condition was last true: the time underwater |
LastValue( array ) |
The value at the final bar, used to pull the summary numbers out |
SetBarsRequired( sbrAll, sbrAll ) |
Turns QuickAFL off so the running maximum sees every bar |
Expected result
Section titled “Expected result”Applied as a chart pane after a portfolio backtest, you should see the equity line, a dashed step-shaped line above it that only ever rises, and a red area hanging below zero on its own scale. The title should read something like:
Portfolio equity 127520Deepest decline from a previous peak -22.40%Longest stretch below a previous peak 318 barsRight now -1.85% below the peak, 12 bars below itThe dashed peak line should be flat wherever the red area is non-zero, and should step up only at the moments the red area touches zero. If it does anything else, something is wrong.
Test it
Section titled “Test it”Three checks that would catch this being wrong:
- The deepest decline should match the report. Compare the title’s figure with
Max. system % drawdownon the statistics page. They are computed on the same basis — close prices, portfolio equity — so they should agree closely. A large discrepancy usually means QuickAFL truncated the array or the chart is not showing all bars. - The red area should be exactly zero at every new high, and nowhere else. Zoom in on a peak and confirm.
- Run it on a symbol other than
~~~EQUITY. The output should be identical, becauseForeignreads the equity symbol regardless of which symbol the chart is on. If it changes, you have edited the wrong line.
Common errors
Section titled “Common errors”| Symptom | Cause |
|---|---|
| Title says no portfolio equity was found | No portfolio backtest has been run in this database, or it was an Individual rather than a Portfolio backtest |
| Numbers do not match the report | The chart is not showing all bars, or SetBarsRequired was removed and QuickAFL truncated the array |
| Equity looks like the previous run | The formula was pasted into a backtest formula rather than used as a chart; ~~~EQUITY is only updated after a run completes |
| The underwater area is invisible | It is plotted on its own scale; if the drawdowns are tiny the area is a thin line at the bottom of the pane, which is itself informative |
| Everything is zero | Nz converted a Null equity series to zeros, which is the intended visible failure |
Extension
Section titled “Extension”Convert the bar counts into calendar time and print both. On daily data, BarsSince counts
trading days, so roughly 21 bars is a month; a more honest version reads the actual dates
with DateTime() and reports elapsed calendar days, which is what a person experiences. A
second extension is to report the number of separate stretches spent more than, say, 10
per cent below a peak — because three separate 15 per cent drawdowns and one 15 per cent
drawdown are different experiences that share a Max. system % drawdown.
What one trade can do to a curve
Section titled “What one trade can do to a curve”Take the illustrative report from the trade statistics lesson: 214 trades, Net Profit
27,520, Avg. Profit/Loss 128.60, Payoff Ratio 1.903, Profit Factor 1.355. Suppose the
largest single win was 9,400.
Largest win as a share of Net Profit = 9,400 / 27,520 = 34.2%Now recompute the report without that one trade:
| Figure | With the trade | Without it |
|---|---|---|
| Trades | 214 | 213 |
Net Profit |
27,520 | 18,120 |
Avg. Profit/Loss |
128.60 | 85.07 |
Payoff Ratio |
1.903 | 1.753 |
Profit Factor |
1.355 | 1.234 |
One trade out of 214 supplied a third of the profit, and removing it cuts the expectancy by a third. That is not a reason to remove it — deleting your best result is not a method, and outliers are a genuine feature of trend-following returns rather than a contaminant. It is a reason to ask a specific question: is this outlier a repeatable property of the rule, or an accident of one instrument on one date?
The trade list answers the first half. Look the trade up. Was it a merger announcement, a one-off gap, a symbol that no longer exists? If the rule’s logic gives no reason to expect such trades to recur, the summary statistics are describing a sample the future is unlikely to resemble. Part 33 turns this into a systematic procedure by resampling the trade sequence rather than deleting single trades by hand.
Comparing against buy and hold, fairly
Section titled “Comparing against buy and hold, fairly”Version 6.00 added detailed buy-and-hold benchmark statistics to the backtest report
automatically. The exact row labels of that block are not spelled out in the User’s Guide,
so read them off your own report rather than trusting a list from anywhere — including this
page. The old backtester’s labels, which you will meet in older material, were
Buy-and-hold profit, Buy-and-hold % return, Annual B&H % return, Max. B&H drawdown,
Max. B&H % drawdown and System to buy-and-hold index.
A fair comparison requires five things, and most published comparisons are missing at least two:
- The same period. Including start and end dates, because both figures are annualised over calendar days.
- Costs on both sides. Buy and hold pays a spread and a commission twice, not never. The difference is small over twenty years and it should still be there.
- A stated benchmark. “Buy and hold” of what? The same universe, equally weighted and rebalanced how often? An index? The index including dividends, or excluding them? These produce materially different numbers and the choice must be recorded.
- The same survivorship treatment. If your universe excludes delisted companies, so does your benchmark, and both are flattered by the same amount.
- An answer to the exposure question. A system at 30 per cent exposure that matches a fully-invested benchmark is doing something different, not obviously something better, and the other 70 per cent of the capital has to be doing something in real life.
Why a smooth curve is a warning as often as a comfort
Section titled “Why a smooth curve is a warning as often as a comfort”AmiBroker reports three metrics that reward a smooth equity line:
| Metric | Definition | Direction |
|---|---|---|
Standard Error |
Choppiness of the equity line, measured about its linear regression | Lower is better |
Risk-Reward Ratio |
Slope of the equity line, meaning expected annual return, divided by its standard error | Higher is better |
K-Ratio |
Detects inconsistency in returns; the regression slope scaled by the standard error and the bar count; should be 1.0 or more | Higher is better |
Now the uncomfortable part. Markets are not smooth. A rule trading volatile instruments with meaningful position sizes produces a curve with visible texture, because the underlying returns have visible texture. When a backtest equity curve is much smoother than the thing it is trading, something absorbed the volatility, and the candidates are mostly defects:
- Look-ahead. A rule that can see the future does not experience the future’s uncertainty. Part 30’s first lesson is entirely about this.
- Survivorship. A universe containing only companies that survived has had its worst outcomes deleted.
- A fill that was not available. Exiting at the exact stop price through a gap, or buying at a close that was only known after the close, removes precisely the events that create roughness.
- Costs set to zero or near it. Costs are the one component that is certain, and omitting them smooths every curve.
- Position sizes so small the curve is mostly cash. Check
Exposure %. A curve that is 90 per cent deposit account is smooth for reasons that have nothing to do with the rule. - An in-sample fit. A parameter set chosen because it produced a smooth curve on this data will produce a smooth curve on this data. Parts 31 and 32 exist for this.
- Repeated closes in the data. A symbol whose data source fills non-trading days by repeating the previous close contributes stretches of exactly zero change. That is a data defect being read as stability.
The hazard compounds when smoothness becomes an optimization target. Optimise on
K-Ratio, Risk-Reward Ratio or Standard Error and the search will find the parameter
set whose in-sample curve is straightest — which, among a few thousand candidates, is very
often the most over-fitted one rather than the most robust one.
The checks a suspiciously smooth curve deserves
Section titled “The checks a suspiciously smooth curve deserves”- Count the trades. Smooth curves on few trades are almost always an artefact.
- Check
Exposure %. If it is small, most of the smoothness is cash. - Compare
Max. trade % drawdownwithMax. system % drawdown. If individual trades went badly wrong and the system did not, find out what absorbed it, and confirm that the thing that absorbed it was diversification rather than an assumption. - Sample ten trades from the list. Is every fill price inside its bar’s range? Is every exit at a price that was knowable at the time?
- Re-run on a different date range and a different universe. A curve that is smooth on both is more interesting than one that is smooth on one.
- Set the costs to a pessimistic figure and run it again. If the smoothness survives doubling the cost assumption, it is more likely to be real.
The equity curve carries the ordering information that every summary statistic discards.
Flat stretches have two causes that only the trade list can separate, and the length of the
declining stretches is the number the report omits entirely — which is why this lesson
built a formula that computes it from the ~~~EQUITY symbol. A single outlier trade can
supply a third of the profit, and finding out whether it is a property of the rule or an
accident of one date is a question the trade list answers. Buy-and-hold comparisons need
the same period, the same costs, a stated benchmark, the same survivorship treatment and an
answer to the exposure question. And a curve much smoother than the market it trades is a
prompt to look for the assumption that made it so.
That closes Part 29. You can now read the report in AmiBroker’s own vocabulary, know which numbers are ratios of what, and know which popular statistics are not in it at all. Part 30 takes the next step and catalogues the ways a backtest can be wrong long before its report is printed.
Check your understanding
Sources for this lesson
6 verified · checked 2026-08-31
- 01AmiBroker User's Guide — Equity function and portfolio equity§ Portfolio Equity special symbolamibroker.com/guide/a_equity.html2026-08-31
- 02AmiBroker User's Guide — System test report window§ New backtester reportamibroker.com/guide/w_report.html2026-08-31
- 03AmiBroker User's Guide — System test settings window§ Report tabamibroker.com/guide/w_settings.html2026-08-31
- 04AmiBroker User's Guide — Multithreading and performance§ Accessing the ~~~Equity symbolamibroker.com/guide/h_multithreading.html2026-08-31
- 05AmiBroker User's Guide — What's new§ Highlights of version 6.00amibroker.com/guide/whatsnew.html2026-08-31
- 06AFL Function Reference — SetBarsRequiredamibroker.com/guide/afl/setbarsrequired.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.