HomeKnowledge Base

How to browse charts in selected date range

In order to select certain range of dates in the historical chart, then scroll through the history of tickers, we can mark the range of dates we interested in using Range Markers (double-click on the chart or mark the bars and hit F12, SHIFT+F12).

Range markers

Then, use View->Zoom->Range menu to zoom the chart to our range selection.

Zoom to range

We can also assign a keyboard shortcut to View->Zoom->Range command or create a custom toolbar button to have it easily accessible (e.g. CTRL+SHIFT+R). This can be done in Tools->Customize (

After we switch the symbol, in situations when the chart shifts e.g. due to different history length in those symbols – all we have to do is to hit CTRL+SHIFT+R to bring back the view to the range we wanted.

Browsing through the list of symbols can be automated further with scripts. Below is a sample auto-play script that will automatically browse through the list of symbols and will set the zoom to the dates we set in the code. All we need is just to start the script and watch it all switching automatically, without any manual actions required.

Here is a sample script that will browse through the list of symbols and set the zoom to show year 2010:

iWatchList 0/* you can define watch list number here */

AB = new ActiveXObject"Broker.Application" );
Qty AB.Stocks.Count;

for ( 
0Qtyi++ )
Stk AB.Stocks);

    if ( 
iWatchList 32 )
        if ( 
Stk.WatchListBits & ( << iWatchList ) )
AB.ActiveDocument.Name Stk.Ticker;
AW AB.ActiveWindow;
AW.ZoomToRange"2010-01-01""2011-01-01" );
WScript.Sleep2000 ); // 2 seconds delay
        if ( 
Stk.WatchListBits2 & ( << ( iWatchList 32 ) ) )
AB.ActiveDocument.Name Stk.Ticker;
AW AB.ActiveWindow;
AW.ZoomToRange"2010-01-01""2011-01-01" );
WScript.Sleep2000 ); // 2 seconds delay

To use above code follow these steps:

  1. Open Notepad
  2. Copy-paste above the code
  3. Save the file with .JS extension (which means that system will treat this as JScript code)
  4. Make sure that AmiBroker is running with desired chart as active one
  5. Double click on .JS file to execute the JScript code

IMPORTANT: if you are running 64-bit Windows and have BOTH 32-bit and 64-bit versions of AmiBroker installed the OLE scripts by default would only talk to 64-bit instance. To use 32-bit version instead you would need to follow advice given in this article:

It is worth noting that going through series of charts is not the only way to compare performance of various symbols over time. There are other dedicated built tools for comparing several securities (without switching symbols, but within one chart window), including Price (Foreign) and Relative Performance indicator. These functionalities are shown in the following video:

Positioning area plots behind the grid lines

When we want to paint the background with custom colors to indicate certain states or conditions – we can use area plots style for this purpose. The code example presented below shows green background when Close stays above 50-period moving average and red when below MA-50.

PlotMA50 MA(Close,50), "MA50"colorBluestyleThick);

color IIfClose MA50colorGreencolorRed );

However – by default both grid lines and the selector line would get covered by the area plot:

area chart

There is an easy fix for that – AmiBroker allows to specify the Z-axis position too, so we can shift the visibility and order of plots (including their position against grids and other elements) by means of Z-order argument of Plot function.

If we specify the Z-order argument to -1 that means we move the particular plot one level behind and this would also be located below the grids.

PlotMA50 MA(Close,50), "MA50"colorBluestyleThick);

color IIfClose MA50colorGreencolorRed );
Plot1""colorstyleArea|styleOwnScale,0,1,0,-); // the 8th argument specifies z-orde

fixed z-order area chart

More information about use of Z-order can be found in the User’s Guide:

Symbol selection when PositionScore is not defined

AmiBroker’s portfolio backtester allows to define stock ranking and selection criteria by means of PositionScore variable. This is explained in details in the following tutorial chapter:

If PositionScore is not defined or it has the same value for two or more symbols, then AmiBroker will use the following rules:

  1. transaction with greater PositionSize is preferred – the comparison method depends on the position sizing approach used in our code:
    • If we use SetPositionSize( dollarvalue, spsValue) – then $ value is compared.
    • If we use SetPositionSize( shares, spsShares) – then number of shares is used for comparison.
    • If we use SetPositionSize( perc, spsPercentOfEquity) – then % equity matters.
  2. alphabetical order
  3. long trades rather than short trades, if both occur at the same time for the same symbol.

How to populate Matrix from a text file

AmiBroker 6.00 has introduced support for matrices. After we create a matrics with Matrix function call:

my_var_name Matrixrowscolsinitvalue)

then in order to access matrix elements, we need to use:
my_var_namerow ][ col ]

However – if we want to populate a relatively large matrix with values generated in other programs, then it may not be very practical to do it by hand in the AFL code assigning individual elements like this:

A][ ] = 1A][ ] = 4A][ ] = 6

What we can do in such case is to store the values in a text file that we could use as input, then read through the file using fgets function and populate Matrix elements using a looping code. A sample formula showing how to perform such task is presented below.

A sample text file for this example can be found here:

// the input file path
file "C:\\samplematrix.txt";

// define the size of the desired matrix
rows 16;
cols 16;

// create matrix
myMatrix Matrixrowscols);

// open file
fh fopenfile"r" );

fh )

// iterate through the lines of input file
for( 0; ! feoffh ) AND rowsi++ ) 
// read a line of text
line fgetsfh ); 

line == "" )
Error("Too few rows in the data file or an empty row found");
// iterate through the elements of the line
for( 0; ( item StrExtractline) ) != "" AND colsj++ ) 
// assign matrix element
myMatrix][ ] = StrToNumitem );
cols )
Error("Too few columns in data file");
fclosefh );
Error"ERROR: file can not be opened" );

// spot check selected element
Title "spot check M[ 2 ][ 3 ]: " NumToStrMyMatrix][ ] )

How to add symbol labels to Relative Performance chart

The built-in Relative Performance chart displays symbol names colored respectively in the chart Title. However – it may be practical to display the symbol label next to the plotted line for easier identification. This can easily be done with PlotText or PlotTextSetFont function.

A sample code showing such modification is presented below. Font size of the labels can be changed through Parameters dialog.

_NTickerList ParamStr"Tickers""^DJI,MSFT,GE" ) );
fontsize Param("Label font size"10430);
fvb Status"firstvisiblebar" );

0; ( symbol StrExtractName() + "," TickerList) ) != ""i++ )
fc Foreignsymbol"C" );

    if( ! 
IsNullfc] ) )
relP 100 * ( fc fcfvb ] ) / fcfvb ];
PlotrelP symbolcolor colorLightOrange + ( ( ) % 15 ), styleLine );

LastValueBarIndex() ) + 1;
LastValuerelP );

PlotTextSetFontsymbol"Arial"fontsizexyGetChartBkColor(), color, -fontsize/);

PlotGrid0colorYellow );
_NTitle "{{NAME}} - Relative Performance [%]: {{VALUES}}" )

Relative Performance with labels

Drawing line extensions on future bars using AFL

AmiBroker allows to display the AFL-based chart output on the future blank bars area with use of XSHIFT argument of the Plot function. This functionality allows to move the particular chart by certain number of bars and place the output within the blank bars area (provided that we use positive value for XSHIFT, i.e. we are shifting the chart to the right).

The following code shows price chart with 20-period MA overlay and additionally – with the same 20-period MA shifted to the right.

PlotClose"Close"colorDefaultstyleBar );

PlotMAClose20 ), "MA-20"colorRedstyledashed );
PlotMAClose20 ), "MA-shift"colorRedstyleThickNullNull10)

Chart with XShift

However – there may be some situations where we not only want to shift the chart position, but actually calculate the position of the line on the blank bars, for example if we are producing an extension of the existing indicator line.

Let us consider a simple example, which draws a line connecting the last record of the input array with the value 50-bars ago, using LineArray() function.

The code is the following:

inputArray Close;
PlotinputArray"input"colorDefaultstyleDots );

bi BarIndex();
lvbi LastValuebi );
x0 lvbi 50;
x1 lvbi;
y0 inputArraylvbi 50 ];
y1 inputArraylvbi ];

PlotLineArrayx0y0x1y1True True ), "line"colorRedstyleThick )

and the output looks like this:
Chart with XShift

LineArray function allows to calculate the extension automatically if we set EXTEND argument to True, however – all the calculations in AFL language are performed within the ‘real bars’ area, i.e. on the available elements of the array, between array item 0 until array item (Barcount-1).

To calculate and display the values that extend past the very last bar available in the array we will use technique explained below:

  1. first we shift the input back (to the left) by N bars, so the real input data would occupy earlier part of the array and we would have extra bars at the end for the calculation of extended arrays
  2. now we calculate the position of arrays on such shifted
  3. shift the displayed output forwards with XSHIFT functionality of Plot function (so the calculated extensions would get aligned onto the blank bars as a result).

If our original input array contains 200 bars and the line was drawn between bar 150 and the bar 200, then our aim is to shift the array that way, so the line would occupy the bars between bar 140 and bar 190, while the remaining 10 bars at the end of the array could be used for calculating the extended part of the line. Then – using XSHIFT we could place the array back into its original position.

inputArray Close;
PlotinputArray"input"colorDefaultstyleDots );

// here we shift the input array to the left
shift 10;
inputArray RefinputArrayshift );

// calculations of the x-y coordinates of the LineArray
// take into account the fact that array has been shifted
bi BarIndex();
lvbi LastValuebi );
x0 lvbi 50 shift;
x1 lvbi shift;
y0 inputArraylvbi 50 shift ];
y1 inputArraylvbi shift ];

// lineArray shifted back to original (correct) position with XSHIFT
PlotLineArrayx0y0x1y1TrueTrue ), "line"colorRedstyleThickNullNullshift );

//positions that were used for calclations before using xshift
PlotinputArray"input shifted"colorLightGreystyleDashed );
PlotLineArrayx0y0x1y1TrueTrue ), "line shifted"colorRedstyleDashed )

Chart with XShift

Dashed lines in the above chart show the shifted positions of the input array and calculated LineArray before using XSHIFT (i.e. on the bars that were used for actual calculations)

How to display interest gains in the backtest report

The default backtest report shows total Net Profit figure, which includes both trading profits and interest earnings. With Custom Backtest procedure we can easily isolate these components by summing up profits and loses from individual trades, then subtracting trading gains from the Net Profit and report them as separate metrics.

SetCustomBacktestProc"" );

if ( 
Status"action" ) == actionPortfolio )
bo GetBacktesterObject();
bo.Backtest(); // run default backtest procedure

    // read Net Profit, Winners and Losers profits from the report
st bo.GetPerformanceStats);
netProfit st.GetValue"NetProfit" );
tradeProfits st.GetValue("WinnersTotalProfit") + st.GetValue("LosersTotalLoss");

bo.AddCustomMetric"Trading profits"tradeProfits );
bo.AddCustomMetric"Interest earnings"netProfit tradeProfits );


// trading rules here
Buy CrossMACD(), Signal() );
Sell CrossSignal(), MACD() )

After backtest is run, we can see our custom metrics in the backtest report.

More information about creating custom metrics can be found in the manual:

Using optimum parameter values in backtesting

After Optimization process has found optimum values for parameters of our trading system, typically we want to use optimum values in subsequent backtesting or explorations. In order to achieve that, we need to manually update default_val (second) argument of Optimize function with the values obtained from the optimization report.

The arguments of Optimize function are shown below (note second parameter marked in dark red color – this is the default value parameter we will be changing after optimization run):

some_var = Optimize( "description", default_val, min_val , max_val, step );

Let us consider the following example formula used for optimization process:

periods Optimize"Periods"2550); // note that default value is 2
Level Optimize"Level"22150); // note that default value is 2

Buy CrossCCIperiods ), -Level );
Sell CrossLevelCCIperiods ) )

If we perform Optimization process and check the results (for this example we use Net Profit as the optimization target), we can see that the best results use Periods = 6 and Level = 126.

Optimization result

Now in order to run backtest and obtain exactly the same results as in the respective line of the above Optimization results, we need to enter the values into default argument, so the modified code will look like this:

periods Optimize"Periods"6550); // we changed default value to 6
Level Optimize"Level"1262150); // we changed default value to 126

Buy CrossCCIperiods ), -Level );
Sell CrossLevelCCIperiods ) )

Now we can use the code with modes other than Optimization and the formula will use optimized values we retrieved from the results.

Drawing indicators on a subset of visible bars

By default, the Plot function draws the graph for all visible bars. In some situations however, we may want to draw some selected bars, leaving remaining chart space unaffected.

To achieve that – we simply assign Null value for the bars that we want to skip. Our graph will just be drawn for the non-null bars.

This simple example draws candlesticks only on Mondays and leaves empty all the other days.

IsMonday DayOfWeek() == 1;
// assign Close for Mondays, otherwise assign Null
Data IIfIsMondayCloseNull );
// plot the data
PlotData"Chart of Mondays"colorDefaultstyleCandle )

The following example shows how to restrict the visibility to last N bars. The code defines a custom function, which can be called later on for the arrays we want to show only partially.

// custom function definition
function LastNBars( array, bars )
bi BarIndex();
lvbi LastValuebi );

// use Null value for bars other than last N
return IIfbi lvbi bars, array, Null );

// price plot
PlotClose"Close"colorDefaultstyleBar );

// MA-50 restricted to last 10-bars only
line MAClose50 );
PlotLastNBarsline10 ), "last 10 bars"colorRed );

// shaded area
PlotLastNbarsTrue10 ), ""colorYellowstyleArea|styleOwnScale|styleNoLabel010, -)

Draw chart only for last N bars

In the above chart both Moving average (red line) and yellow shading area have been restricted to last 10-bars only.

In a similar way we can restrict the visibility to most recent day only in intraday chart:

// custom function definition
function ShowLastDay( array )
dn datenum();
lastDay dn == LastValuedn );

IIflastDay, array, Null );

// price plot
PlotClose"Close"colorDefaultstyleBar );

// daily high / low on last day only
dailyH TimeFrameGetPrice("H"inDaily );
dailyL TimeFrameGetPrice("L"inDaily );
PlotShowLastDaydailyH ), "dailyH"colorGreenstyleThick  );
PlotShowLastDaydailyL ), "dailyL"colorRedstyleThick  );

// shaded area
colorPaleYellow ColorBlend(colorWhitecolorYellow0.1);
style styleArea styleOwnScale styleNoLabel;
PlotShowLastDay), ""colorPaleYellowstyle010, -)

Draw chart only for last day

Other practical implementations of such technique is presented in these formulas:

Using multiple watchlists as a filter in the Analysis

The Filter window in the Analysis screen allows us to define a filter for symbols according to category assignments, for example watchlist members (or a result of mutliple criteria search).

The filter allows us to select one watch list for “inclusion” and one for “exclusion”. To include members of more than one watchlist, we can not simply pick them both in the Filter window – we need to combine these symbols together in another dedicated watchlist storing symbols from both lists.

Let us say we want to run a test on members of List 1 and List 2. To combine these watchlists together we need to follow the instructions below.

  1. Click on List 1, then in the bottom part of the Symbols window mark all tickers. A multiple selection is done by clicking on first and last item in the list while holding down the Shift key. We may also select all symbols by clicking on any symbol and pressing Ctrl+A key.

    Select symbols from watch list

  2. Now click on the selection with right mouse button and choose Watch list->Add selected symbol(s)

    Add symbols to watch list

  3. Pick an empty watchlist that we will use to combine our tickers (e.g. List 5 ) and confirm to add multiple symbols:

    Confirm adding multiple symbols

  4. Repeat the above steps 1-3 with List 2 members
  5. Now we can pick List 5 in the Filter window and run the test on all the tickers

    Create new watch list

An alternative solution to this is to filter out unwanted symbols in the code. In this case AmiBroker would need to run analysis for all tickers (so Apply to would need to be set to All symbols) and apply filtering while executing your formula. To do so you may use code like this for backtesting (filtering Buy signals):

Buy /* your regular trading rules here */;

watchlistCheck InWatchList) OR InWatchList);
Buy watchlistCheck  AND Buy// combine watch list filter with your rule

or code like this in exploration (adding extra condition to Filter variable):

Filter /* your regular exploration filter here */;
watchlistCheck InWatchList) OR InWatchList);
Filter watchlistCheck AND Filter// combine watch list filter with your rule

Please keep in mind that filtering in the code is significantly slower. Using this method AmiBroker needs to read the data for all tickers, prepare arrays, then evaluate the formula and verify the condition – so using Filter window and the first approach will be faster, as the filtering is done before the formula execution, saving lots of time required for data retrieval and AFL execution.

« Previous PageNext Page »