AmiBroker Documentation
Item No. ab401.html
This is the part of AmiBroker
documentation. Copyright ©2005 Tomasz Janeczko. All rights reserved.
THIS
DOCUMENT IS OUTDATED. REFER TO AMIBROKER USER'S GUIDE FOR MORE UP TO
DATE INFORMATION
Porfolio Backtester Interface Reference Guide - preliminary
documentation
(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.
Note that this is PRELIMINARY DOCUMENTATION and all details described here
are subject to change.
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:
- bool AddCustomMetric( string Title,
variant Value, [optional] variant LongOnlyValue,
[optional] variant ShortOnlyValue )
This method adds custom metric to the backtest report, backtest "summary"
and optimization result list. Title is a name of the metric to be displayed
in the report, Value is the value of the metric, optional arguments LongOnlyValue,
ShortOnlyValue allow to provide values for additional long/short-only
columns in the backtest report
- bool Backtest( [optional] bool NoTradeList )
This high-level method performs default portfolio backtest procedure in single
call. It should be used if you just need to obtain custom metrics and do
not want
to change
the way
backtest is performed. (Version 4.68.0 and above): If optional parameter NoTradeList is
set to True, then trade list is not generated automatically. This is useful
if
you
want to add some per-trade metrics. Once you add them, you can generate trade
list with your metrics using ListTrades method.
- long EnterTrade( long Bar, string Symbol, bool bLong, float Price, float PosSize, [optional]
variant PosScore, [optional]
variant RoundLotSize, [optional] variant MarginDeposit,
[optional] variant TickSize, [optional] variant PointValue )
Low-level method that enters trade on any symbol. This allows to take trades
even on symbols that have no corresponding signals. If values for optional
parameters are not provided then AmiBroker uses values defined in Symbol->Information
window
- long ExitTrade( long Bar, string Symbol, float Price, [optional]
variant ExitType )
Low-level method that exits trade on any symbol. This method searches open
trade list and if there is no open trade on given symbol it does nothing. Optional
ExitType parameter specifies the reason for exit (0 - regular exit, 1 - max.
loss, 2 - profit, 3 - trail, 4 - N-bar, 5 - ruin)
- Trade FindOpenPos( string Symbol )
This method looks for the Symbol in open position list and returns
matching trade object if it finds one or returns null object otherwise.
- Trade GetFirstOpenPos()
This method returns first Trade object from open position list
- Signal GetFirstSignal( long Bar )
This method returns first trading Signal object for given Bar
- Trade GetFirstTrade()
This method returns first Trade object from closed trade
list
- Trade GetNextOpenPos()
This method returns next Trade object from open
positions list. You should call GetFirstOpenPos before calling this method
for the first time. Returns null object when no more open positions are found.
- Signal GetNextSignal( long Bar )
This method returns next Signal object from closed signal list of
given Bar. You should call GetFirstSignal before calling this method
for the first time.
Returns null object
when no more signals are found.
- Trade GetNextTrade()
This method returns next Trade object from closed trade
list. You should call GetFirstTrade before calling this method
for the first time. Returns
null object when no more trades are found.
- Stats GetPerformanceStats( long Type )
Calculates built-in statistics and metrics and returns Stats object.
Type parameter specifies what trades should be counted in. Type
= 0 means all trades, Type = 1 means long-only, Type = 2 means short-only.
- HandleStops( long Bar )
This low-level method handles automatic stops (applystops). This
method MUST NOT be used in high-level and mid-level approaches. In low-level mode you
should call this method once for each bar inside trading loop.
- ListTrades()
(Version 4.68.0 and above) This outputs trades to the result list of Automatic
Analysis window. Usually this function does NOT need to be called because Backtest()
method by default lists trades already. This function should only be used
when you disabled trade listing in Backtest method to add some custom per-trade
metrics.
- PostProcess()
This mid-level and low-level method performs final processing required to
complete backtest correctly. Among other things it frees price cache, closes
out any open trades and outputs trade list to the Automatic Analysis window.
It should
NOT be
used when you call Backtest() method because Backtest() already performs
necessary processing.
- PreProcess()
This mid-level and low-level method performs initial processing required
to perform backtest correctly. Among other things it initializes price cache
and sets up initial variables. It should NOT be
used when you call Backtest() method because Backtest() already performs
necessary processing.
- bool ProcessTradeSignals( long Bar )
This mid-level method processes all trading signals for given bar. It should
be called once per every bar in your custom backtesting loop.
- RawTextOutput( string Text )
(Version 4.68.0 and above) This method outputs any string to the Automatic Analysis result list at
the time of the call. The user can output text formatted in multiple columns
using
\t
(tab)
character.
- long ScaleTrade(long Bar, string Symbol, bool bIncrease,
float Price, float PosSize, [optional] variant Deposit)
Low-level method that scales trade on any symbol. This method searches open
trade list and if there is no open trade on given symbol it does nothing. Optional
Deposit parameter specifies margin deposit for futures, if not given
then Price parameter
is used.
- UpdateStats( long Bar, long TimeInsideBar )
Low-level method that updates equity, exposure, trade excursions (for MAE/MFE
calculations) and other internal variables required for correct calculation
of statistics. You must NOT use
this function in high-level and mid-level approaches. TimeInsideBar parameter
specifies intraday time position. TimeInsideBar = 0 means opening of the
bar, TimeInsideBar = 1 means middle
of the bar, TimeInsideBar = 2 means end of bar. As certain internal calculations
depend on end-of-bar calculations, this method must be called once and only
once with TimeInsideBar parameter
set to 2 at the end of processing of every bar inside in your custom backtesting
loop. May be called zero or more times for every bar inside backtesting loop
with TimeInsideBar parameter set to 0 (zero) or 1.
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 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
- 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
on this topic will appear in the AmiBroker Tips newsletter. In
case of any further questions, comments and suggestions please contact me
at:
support[at]amibroker.com.