Porfolio Backtester Interface Reference Guide
(Updated January 22nd, 2005 to cover enhancements and additions introduced
in AmiBroker 4.68.0 BETA)
Basics
AmiBroker version 4.67.0 exposes new object-oriented interface to porfolio
backtester allowing to control 2nd phase of the backtest. This allows multitude
of applications including, but not limited to:
- position sizing based on portfolio-level equity
- implementing advanced
rotational systems (you have now access to ranking arrays and can
decide what trades to take after knowing which symbols
scores best on bar-by-bar basis)
- adding your custom metrics to backtest
and optimization statistics
- implementing custom formulas for slippage
control
- advanced scaling-in/-out based on portfolio equity and other
run-time stats
- advanded trading systems that use portfolio-level statistics
evaluated on bar-by-bar basis to decide which trades
to take
This document describes all objects, methods and properties exposed by portfolio
interface.
Requirements
To use new interface the user needs AmiBroker 4.67.0 or higher
and needs to have AFL coding skills including understanding the terms: an
object, method and property.
Various approaches for various applications
The porfolio backtester interface supports various approaches to customization
of backtest process that suit different applications.
- high-level approach (the easiest)
- using Backtest() method and it runs
default backtest
procedure (as in old versions) - allows simple implementation of custom metrics
- mid-level approach
-
using PreProcess()/ProcessTradeSignal()/PostProcess()
methods - allows to modify signals, query open positions (good for advanced
position sizing)
- low-level approach (the most complex)
- using PreProcess()/EnterTrade()/ExitTrade()/ScaleTrade()/UpdateStats()/HandleStops()/PostProcess()
methods - provides full control over entire backtest process for hard-code
programmers only
Getting access to the interface
To access new portfolio backtester interface you need to:
- enable custom backtesting procedure by calling:
SetOption("UseCustomBacktestProc", True );
or calling
SetCustomBacktestProc( "C:\\MyPath\\MyCustomBacktest.afl" );
in your formula
or by enabling it in Automatic Analysis->Settings window, "Portfolio" tab
and specifying external custom procedure file.
- get access to backtester object by calling GetBacktesterObject() method.
Note that GetBacktester method should only be called when Status("action")
returns actionPortfolio:
if( Status("action")==
actionPortfolio )
{
// retrieve the interface
to portfolio backtester
bo = GetBacktesterObject();
...here is your custom backtest formula.
}
When using external custom procedure file you don't need to check for actionPortfolio,
because external backtest procedures are called exclusively in actionPortfolio
mode.
Typing Conventions
- bool - italic represents the type of parameter/variable/return
value (Trade, Signal, Stats - represent the type of object returned)
- AddSymbols - bold represents function / method / property
name
- SymbolList - underline type face represents formal parameter
- [optional] - denotes optional parameter (that does not need to
be supplied)
- variant - represent variable type that can be either string
or a number
Despite the fact that interface handles integer data type such as long,
short, bool and two different floating point types: float and double, the
AFL itself converts all those data types to float because AFL treats all numbers
as floats (32-bit IEEE floating point numbers).
Objects
The interface exposes the following objects:
- Backtester object
- Signal object
- Trade object
- Stats object
The only object directly accessible from AFL is Backtester object, all other
objects are accessible by calling Backtester object methods as shown in the
picture below.

Backtester object
Backtester object allows to control backtest process (process signals, enter/exit/scale
trades) and get access to signal list, open position and trade list and to
performance statistics
object.
Methods:
Properties:
- double Cash
available funds (cash) in your portfolio
- double Equity
current portfolio-vele Equity (read-only property)
- double InitialEquity
funds that are available at the beginning of the backtest
- double MarginLoan
loan amount (only if you are using margin account)
(read-only property)
Signal object
Signal object represents trading signal (buy/sell/short/cover) or ranking
array element generated by AmiBroker during first phase of backtest when your
formula is executed on every symbol under test. During this first phase scan
AmiBroker collects data from buy/sell/short/cover signal, price, position size
and score arrays, performs sorting of signals and put top-ranked entry signals
and all scale and exit signals into the list. Separate list of trading
signals is maintaned for every bar. Signal list is sorted so first entry signals
appear
(top ranked first) and after that scaling and exit signals follow. To conserve
memory AmiBroker stores only (2*MaxOpenPositons) top-ranked entry signals per
bar. It keeps however all exit and scaling signals. Once first phase is completed
and backtester enters 2nd phase (real backtest) it
iterates
through
bars and
through
all signals
within given bar and executes trades based on this signals.
To iterate through signal list you should use GetFirstSignal() / GetNextSignal()
methods of Backtester object, as shown below:
//
retrieve the interface to portfolio backtester
bo = GetBacktesterObject();
for(
sig = bo.GetFirstSignal(); sig; sig = bo.GetNextSignal() )
{
if(
sig.IsEntry() )
{
//
handle entry signal
....
}
}
Methods:
- bool IsEntry()
True if this is entry signal, False otherwise
- bool IsExit()
True if this is exit signal, False otherwise
- bool IsLong()
True if this is long entry (buy) or long exit (sell) or scale-in signal,
False otherwise
- bool IsScale()
True if this is scale-in or scale-out signal, False otherwise
Properties:
- float MarginDeposit
margin deposit (for futures)
- float PointValue
point value (for futures, currencies)
- float PosScore
position score
- float PosSize
requested position size (positive numbers mean dollar value, negative values
mean percent of portfolio equity)
- float Price
entry/exit/scale price
- short int Reason
this specifies reason of exit ( 0 - regular exit, 1 - max.
loss, 2 - profit, 3 - trail, 4 - N-bar, 5 - ruin )
- float RoundLotSize
round lot size
- string Symbol
symbol of security
- float TickSize
tick size (minimum price change)
- short int Type
this specifies signal type ( 0 - rank (rotational systems only), 1
- buy, 2 - sell, 3 - short, 4 - cover, 5 - scale-in, 6 - scale-out )
Trade object
Trade object represents either currently open position (open trade) or closed
trade. AmiBroker maintains 2 lists of trades: open position list (accessible
using GetFirstOpenPos/GetNextOpenPos methods of backtester object) and closed
trade lists (accessible using GetFirstTrade/GetNextTrade methods of the backtester
objects). Once open position is closed by the backtester it is automatically
moved from open position list to trade list. When backtest is completed (after
PostProcess call) AmiBroker closes out all open positions, so trade list includes
all trades. You can access both lists any time during backtest, you can also
access trade list after completion to generate trade-related stats.
To iterate through open position list you should use GetFirstOpenPos()
/ GetNextOpenPos()
methods of Backtester object, as shown below:
//
'bo' variable holds Backtester object retrieved earlier
for(
openpos = bo.GetFirstOpenPos(); openpos; openpos = bo.GetNextOpenPos() )
{
//
openpos variable now holds Trade object
}
To iterate through closed trade list you should use GetFirstTrade()
/ GetNextTrade()
methods of Backtester object, as shown below:
for(
trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade()
)
{
//
trade variable now holds Trade object
}
Methods:
- long AddCustomMetric( string Title,
variant Value )
(Version 4.68.0 BETA and above) This method adds PER-TRADE custom
metric to the trade list only. Title is a name of the metric to be displayed
in
the
report,
Value
is the
value
of the metric. When using this function you have to ensure that
you the same metrics in the same order to every trade. Otherwise output
may be messed up. Note that in contrast to Backtester.AddCustomMetric method
that is usually called after PostProcess, the Trade.AddCustomMetric should
be called before PostProcess call because PostProcess lists
trades. Using
Trade.AddCustomMetric after PostProcess gives
no result, because trades are already listed.
Also if you are using Backtester.Backtest() method
you should call it with NoTradeList parameter set to True, add your per-trade
metrics and then call ListTrades
to allow your custom metrics to be included in the output.
- float GetCommission( [optional] bool InclExit )
retrieves commission paid for that trade (includes all scale in/out commissions).
Depending on InclExit parameter the function returns commission including (True,
default) or exluding (False) exit commission.
- double GetEntryValue()
retrieves dollar entry value of the trade
- float GetMAE()
retrieves trade's Maximum Adverse Excursion in percent
- float GetMFE()
retrieves trade's Maximum Favorable Excursion in percent
- double GetPercentProfit()
retrieves current percent profit of the trade
- double GetPositionValue( )
retrieves current dollar value of the position.
- float GetPrice( long Bar, string Field )
(Version 4.68.0 BETA and above) provides quick access to price arrays of
open positions. Bar parameter
represents the data bar to query price for, Field
parameter specifies which price field you want to get, allowable values are:
"O" (Open)
"H" (High)
"L" (Low)
"C" (Close)
"F" (Fx currency rate)
NOTES:
1. GetPrice method is available for OPEN POSITIONS only, when called on
closed trade returns Null value
2. Open Interest field is NOT available via GetPrice
3. Bar must be between
0..BarCount-1, otherwise exception will occur
- double GetProfit()
retrieves current dollar (point) profit of the trade
Properties:
- long BarsInTrade
bars spent in trade (counting starts from 0)
Note however that the value of zero is available only when trade
is just opened in "low-level" approach, so normally you would see numbers >=
1 (all other reporting in AB remains as it was, so enter today and exit tommorrow
counts as 2-bar trade)
- float EntryDateTime
entry date/time in internal AmiBroker format (the same as used by AFL function
DateTime())
- float EntryFxRate
entry foreign exchange currency rate, if any scaling-in occurred this holds
average entry fx rate
- float EntryPrice
entry price, if any scaling-in occurred this holds average entry price
- float ExitDateTime
exit date/time in internal AmiBroker format (the same as used by AFL function
DateTime())
- float ExitFxRate
exit foreign exchange currency rate, if any scaling-out occurred this holds average
exit fx rate
- float ExitPrice
exit price, if any scaling-out occurred this holds average exit
price
- double Handle
internal handle value that allows to uniquely identify and manage (for example
exit or scale in/out) multiple trades open
on the same symbol at the same time. It can be passed to ExitTrade / ScaleTrade
instead of the symbol.
- bool IsLong
True if trade is long, False otherwise
- bool IsOpen
True if trade is open, False otherwise
- float MarginDeposit
initial margin deposit
- double MarginLoan
loan amount used for this trade
- float PointValue
point value (for futures / currencies)
- float RoundLotSize
round lot size
- float Score
entry score
- float Shares
number of shares / contracts
- string Symbol
symbol of the security
- float TickSize
tick size (minimum price change)
Stats object
Stats object provides the access to built-in backtester statistics and metrics.
Metrics are usually calculated once backtest is completed but it is also possible
to calculate metrics during backtest. To calculate current metrics and get
the access to them simply call GetPerformanceStats method of Backtester object.
Please note that if you calculate statistics in the middle of the backtest
they will include only closed trades.
To calculate and access stats use the following code:
// 'bo' variable holds
Backtester object retrieved earlier
stats = bo.GetPerformanceStats( 0 );
Methods:
- double GetValue( string MetricName )
retrieves the value of a metric, MetricName can be one of the following:
"InitialCapital" ,
"EndingCapital"
"NetProfit"
"NetProfitPercent"
"ExposurePercent"
"NetRAR"
"CAR"
"RAR"
"AllQty"
"AllPercent"
"AllAvgProfitLoss"
"AllAvgProfitLossPercent"
"AllAvgBarsHeld"
"WinnersQty"
"WinnersPercent"
"WinnersTotalProfit"
"WinnersAvgProfit"
"WinnersAvgProfitPercent"
"WinnersAvgBarsHeld"
"WinnersMaxConsecutive"
"WinnersLargestWin"
"WinnersLargestWinBars"
"LosersQty"
"LosersPercent"
"LosersTotalLoss"
"LosersAvgLoss"
"LosersAvgLossPercent"
"LosersAvgBarsHeld" ,
"LosersMaxConsecutive"
"LosersLargestLoss"
"LosersLargestLossBars"
"MaxTradeDrawdown"
"MaxTradeDrawdownPercent"
"MaxSystemDrawdown"
"MaxSystemDrawdownPercent"
"RecoveryFactor"
"CAR/MDD"
"RAR/MDD"
"ProfitFactor"
"PayoffRatio"
"StandardError"
"RRR"
"UlcerIndex"
"UlcerPerformanceIndex"
"SharpeRatio"
"KRatio"
Properties:
-none-
Further information
Examples and more documentation
can be found in this
Houston
presentation covering custom backtester interface (300 KB PDF format) and the
Knowledge
Base: http://www.amibroker.com/kb/category/afl/custom-backtest/