HomeKnowledge Base

Choosing first day of the week for weekly compression

AmiBroker allows to define, what days to use for weekly compression. By default weekly compression uses Monday-Sunday week (i.e. Monday being the first day of the week), we can change it however to any custom day of the week to match our local calendar.

To choose the day to be used as the first day of the week, we need to go to File->Database Settings->Intraday Settings and choose the appropriate day in “First day of week” field.

Database Settings

The above example shows custom-defined week, which starts on Sunday and ends on Saturday (i.e. working Sunday-Thursday week, as in many Middle East countries).

The following Wikipedia article specifies the working week for a number of countries in the world:

Customizing chart titles

When we create custom indicators in AmiBroker, then by default the program will automatically create chart title line based on the selected ticker and the information we have provided in Plot function calls.

If we use the following formula:
PlotClose"Close"colorDefaultstyleBar );
PlotMAClose20), "MA-20"colorRed );
PlotMAClose50), "MA-50"colorBlue )

Then the auto-generated chart title line will contain:

Chart title

First item is the symbol name selected for that particular chart window (BA in this case), then the output is based on the plot names (provided in the 2nd argument of Plot function calls within the code) and colors will also match the respective plot colors. Number of decimal places depends on the settings in Tools->Preferences->Miscellaneous: Decimal places in chart titles/tools

If we do not want certain plot to affect the chart titles, we can use styleNoTitle chart style, for example changing the 3rd line of the above code into:

PlotMAClose50), "MA-50"colorBluestyleNoTitle )

Would result in removing MA-50 values from the title output presented above, even though MA-50 values are still presented in the chart.

We can customize the chart title output even further by means of dedicated Title variable. If we define Title string within the formula, it will override the automatic title generation. Therefore, starting with most basic example, defining an empty string with use of:

Title ""

would hide the title output completely, while using statement like this:

Title "Close Price: " +NumToStrClose1.2 )

would generate default-color output such as the following (the price is formatted to use 2 decimal places by using NumToStr function in this case):

Chart title

It is also possible to define chart color by using EncodeColor() function, so changing the above line into:

Title "Close Price: " +EncodeColorcolorRed ) + NumToStrClose1.2 )

would result in displaying the price value (and other text that follows the EncodeColor call) in red color.

Now let us analyse the Title definition included in the built-in Price chart.

_N(Title StrFormat("{{NAME}} - " +FullName() +
" - {{INTERVAL}} {{DATE}} Open %g, Hi %g, Lo %g, Close %g (%.1f%%) Vol " +
WriteValV1.0 ) +" {{VALUES}}"OHLCSelectedValueROCC)) ))

Among other elements shown above, the Title definition uses templates like {{NAME}} or {{VALUES}} etc. in the defined string.

These are special tokens that are replaced by appropriate values at run-time:

  1. {{VALUES}} inside Title string will be replaced by automatic-values generated by Plot function calls
  2. {{NAME}} will be replaced by the ticker symbol
  3. {{DATE}} will be replaced by selected date
  4. {{INTERVAL}} will be replaced by the name of the interval
  5. {{OHLCX}} will be replaced at runtime by string “Open …, Hi …. Lo … Close (…%)” showing current price

The built-in title definition uses also StrFormat function. This function allows us to specify the string followed by the list of arguments that will be inserted into the string in places, where %f, %g or %e specifications are entered.

Chart title

If we use the code like the one above, it would produce the following output:

Chart title

Those specifications allow us to format the output string accordingly.

  • %.0f will output a number without decimal places (with rounding if necessary), such as 393
  • %.1f will output a number with 1 decimal place, such as 392.7
  • %.2f will output a number with 2 decimal places, such as 392.65
  • %.3f will output a number with 3 decimal places, such as 392.651
  • %.4f will output a number with 4 decimal places, such as 392.6510
  • %e will output scientific notation (mantissa/exponent), such as 3.92651e+2 (3.92651 * 10 ^ 2 )
  • %g uses automatic formatting and displays as many decimal places as required to show full precision of given number (392.651)

The documentation of the StrFormat function is available in the manual

In the above example the whole title definition is enclosed with _N() function. This just prevents from displaying the title string within the Interpretation window, so using _N() does not really affect the output within the chart.

How to manage overlapping entry/exit signals in portfolio test

When we run the portfolio-test and use default backtesting procedure – on each bar AmiBroker will first process exit signals, then use entry signals to open new trades.

There may be some strategies however, where this approach may not be enough. For example – if we simulate entries with limit price (so they occur somewhere in the middle of the day), but exits on Close – then if we do not use any margin loan, the funds from exit signals can only be used on subsequent days.

Since trading prices (BuyPrice, SellPrice, ShortPrice, CoverPrice arrays) don’t carry any timing information, but only the information about price level for given trade – then we need to delay release of funds by one day to get correct results. This can be done with the following command:


The unsettled cash is reported in the Detailed Log:

Detailed log

We need to remember that this option works on Days (not bars) and it may be better to use it with backtestRegularRaw instead of backtestRegular, otherwise some trades may not be entered because funds are not settled immediately – so we may need to be able to enter not on first but subsequent buy signals (all would really depend on the particular trading rules) – the behaviour of backtestRegular mode and processing raw signals is explained here:

How to re-import the same data without downloading them again

If we have already downloaded several years of historical data using AmiQuote and for any reason we need to re-import this data once again (e.g. into a new database) – there is a way to avoid re-downloading the whole history.

AmiQuote stores all downloaded data files inside of the dedicated destination folder. The exact location can be checked from AmiQuote: Tools->Settings menu:

AmiQuote settings

All recently downloaded files are stored in this folder and we can use ASCII importer in AmiBroker to pick these files manually for import. To do that please follow the steps below:

  1. Select File->Import ASCII
  2. In the open file dialog navigate to Download folder
  3. From Files of type field pick correct import definition to match the source used in AmiQuote:
    Import ASCII

  4. mark the .AQH files we want to import (to select all, it is enough to click on any of the files and hit CTRL+A, multiple file selection is also possible by clicking with CTRL or SHIFT keys pressed)
  5. press Open button to start import procedure

It is important to remember that by default each new download via AmiQuote overwrites the earlier files, so the AQH files will contain quotes from the very last download for given symbol.

More information about using ASCII importer can be found here:

Checking relationship between multiple moving averages

When we compare the positions of several lines against one another, we need to remember about using correct operators, so our AFL statement return correct results. Let us consider a set of moving averages using 10, 20, 30 and 40 periods, drawn with the following code:

MA10 MAClose10 );
MA20 MAClose20 );
MA30 MAClose30 );
MA40 MAClose40 );

PlotClose"Close"colorDefaultstyleBar );
PlotMA10"MA10"colorRed );
PlotMA20"MA10"colorBlue );
PlotMA30"MA10"colorGreen );
PlotMA40"MA10"colorgrey40 )

Multiple Moving averages

When we want to specify a condition where MA10 is highest of all of the lines, above MA20, which is above MA30 and with MA40 on the very bottom – we can NOT simply write:

condition MA10 MA20 MA30 MA40// WRONG - NOT what we really want, but no syntax erro

It may seem strange that such statement is accepted without an error but it is actually syntactically correct. This is because of the fact that True and False are represented by numbers 1 and 0 respectively, so all comparisons actually have numerical value that allows such statement to be evaluated and yield numeric result. The above statement is evaluated from left to right and would be an equivalent of:

condition = ( ( MA10 MA20 ) > MA30 ) > MA40// again WRON

Using > operator will return an array of True or False values (1 or 0). Therefore – the result of MA10 > MA20 comparison (which is True, that is equal to 1) would be then compared to MA30, resulting in checking 1 > MA30, then if such condition returns False (i.e. 0 ), we would end up with 0 > MA40 comparison that would return False (0) as the final output. This is of course not what we want to get.

That is why we should use AND operator instead, because we want to check several conditions being met at the same time, that is:

MA10 is above MA20
AND // must use AND/OR to combine multiple conditions
MA20 is above MA30
AND // must use AND/OR to combine multiple conditions
MA30 is above MA40

Therefore, we should write the AFL statement the following way:

condition MA10 MA20 AND MA20 MA30 AND MA30 MA40// correct way of checking multiple condition

So as a general guideline – if you have multiple boolean (yes/no) conditions that you want to combine into single rule, you need to use AND operator between conditions if you want True result when all conditions are met at the same time.

In a similar way, if you want a True result when any one (or more) of multiple conditions is met, then you need to use OR operator.

condition MA10 MA20 OR MA20 MA30// this will give True result if any one condition (or both) is me

How to show date axis in a newly created chart pane

When we double-click on the selected indicator in Charts window to display it in a new chart pane or apply our custom code as indicator, by default most of the indicator formulas will not show the date-axis, unless we specified such requirement in our formula.

In order to display the date axis, we need to click on the chart with the right mouse button, choose Parameters->Axes&Grid tab, then set Show date axis option to YES.


There is also a way to pre-code such condition in our custom indicator formula. To enable data-axis display when we apply the new indicator formula, we just need to add relevant SetChartOptions() function call:

SetChartOptions0chartShowDates );

// sample custom indicator
PlotRSI(), "RSI"colorRed )

More information about SetChartOptions function can be found in the AFL reference:

Using IB controller (auto-trading interface) with 64-bit version of AmiBroker

AmiBroker allows to automate orders execution through Interactive Brokers account. In order to use the auto-trading capabilities it is necessary to install special component that communicates with IB TWS – the interface installer and documentation is available at:

By default the IB controller is installed to 32-bit release installation folder. So – that is the following location in 64-bit Windows system:

C:\Program Files (x86)\AmiBroker

If we are using 64-bit version, then it is necessary need to manually copy BrokerIB.exe and BrokerIB.xml files to the installation folder of 64-bit version, by default that is:

C:\Program Files\AmiBroker

Using Exclude statement to skip unwanted optimization steps

Sometimes when we optimize our system, we may want to use only a subset of all parameter permutations for our analysis and ignore the others that do not meet our requirements.

For example – if we test a simple trend-following strategy, where we enter long position when short MA crosses above long MA using code such as:

shortPeriods Optimize("Short MA"1011001);
longPeriods Optimize("long MA"5011001);

Buy CrossMACloseshortPeriods), MACloselongPeriods) );
Sell CrossMACloselongPeriods), MACloseshortPeriods) )

Then, shortPeriods parameter value should remain smaller than longPeriods, otherwise the trading rules would work against the main principle of the tested strategy.

There is an easy way to ignore the unwanted sets of parameters by using Exclude statement in our code. If the variable is true – the backtester will not calculate any statistics for that particular run:

shortPeriods Optimize("Short MA"1011001);
longPeriods Optimize("long MA"5011001);

Buy CrossMACloseshortPeriods), MACloselongPeriods) );
Sell CrossMACloselongPeriods), MACloseshortPeriods) );

Exclude shortPeriods >= longPeriods

The information from Info tab of Analysis window shows the difference between first execution (all 10000 backtest runs) and second one using Exclude statement. Note reduced number of steps and reduced optimization time.

Exclude results

How to add MAE / MFE dates to the backtest report

If we want to identify dates, when MAE and MFE levels have been reached during the trade lifetime – we can use the code example presented below.

The formula will process the trades one-by-one, read BarsInTrade property to know how many bars it took since trade entry till exit, then use HHVBars / LLVBars functions to identify how many bars have passed since lowest low or highest high within trade length.

With the information that highest or lowest value was observed N-bars ago – it will shift Date/Time array accordingly – so with use of Lookup() function pointing at the exitbar – we can read the date when HHV/LLV was observed within trade lifetime (BarsInTrade).

SetCustomBacktestProc"" );

processTradetrade )
dt DateTime();

SetForeigntrade.Symbol );

llvDate LookupRefdt, - LLVBarsLowtrade.BarsInTrade ) ), trade.ExitDateTime );
hhvDate LookupRefdt, - HHVBarsHightrade.BarsInTrade ) ), trade.ExitDateTime );

    if ( 
trade.IsLong() )
maeDate llvDate;
mfeDate hhvDate;
maeDate hhvDate;
mfeDate llvDate;


trade.AddCustomMetric"MFE Date"DateTimeToStrmfeDate ) );
trade.AddCustomMetric"MAE Date"DateTimeToStrmaeDate ) );

if ( 
Status"action" ) == actionPortfolio )
bo GetBacktesterObject();

bo.Backtest); // run default backtest procedure

for ( trade bo.GetFirstTrade(); tradetrade bo.GetNextTrade() )
processTradetrade );


    for ( 
trade bo.GetFirstOpenPos(); tradetrade bo.GetNextOpenPos() )
processTradetrade );


Buy CrossMACD(), Signal() );
Sell CrossSignal(), MACD() )

When and how often AFL code is executed?

All analysis in AmiBroker including charting, Analysis window or commentaries is based on underlying AFL code, which is being executed by the program to produce the required output. Therefore – any changes we see in the charts or analysis results (for example – chart updated with new ticks) mean that the program has received some input, then based on this information has recalculated the formula and presented the updated results.

These refreshes / formula recalculations depend on several factors:

  1. In a local database, which is not updated by a realtime plugin, the formula would get refreshed if we perform any actions, such as clicking, scrolling, zooming, changing parameters, choosing another symbol or interval, importing new data etc.
  2. In addition to the actions listed in (1), if we are running a plugin-based realtime feed, then the chart is refreshed based on “Intraday refresh interval” defined in Tools –> Preferences –> Intraday. If we enter 0 into this field, then it will result in chart being refreshed with every new tick (up to 10-times per sec).
  3. It is also possible to force certain refresh rate using dedicated RequestTimedRefresh() function in the AFL code:
  4. It is also possible to manually refresh chart (or all chart and windows) using View->Refresh (or View->Refresh All) menu.

It is worth noting that chart formulas are refreshed only when they are placed on the active chart sheets. Non-active sheets just ‘don’t exist’, they are only created when you click on a bottom tab (sheet tab) to make them visible and destroyed immediately when other sheet becomes active. This ensures that precious CPU resources are not wasted on invisible chart sheets.

Additionally – by default charts in minimized chart windows or when multiple MDI windows are open and one is maximized, the windows in background that are completely obscured by others and/or minimized windows are not redrawn/refreshed during normal RT refresh. We can however call RequestTimedRefresh function with onlyvisible argument set to False and that will force regular refreshes in such windows as well.

RequestTimedRefreshFalse ); // force refresh for minimized MDI window or obscured MDI windo

With regards to Analysis window – in general the formula is executed when we run e.g. Scan, Exploration, Backtest etc. Analysis window executes the formulas in multiple threads running in parallel (this tutorial explains multi-threading aspects:

Repeated execution (to keep the code running over and over) in Analysis window can be also enabled with “Auto-repeat” option, the following Knowledge Base article explains it in details:

Last but definitely not least, we need to remember that AmiBroker may and will perform some executions internally for its own purposes such as:

  1. during AFL Syntax Check that happens when applying the chart, or sending the code to Analysis window, or updating existing formula
  2. when it is about to display Parameters window for the first time for given chart or during Parameters’ “Reset All” operation
  3. at the beginning of Optimization when it reads Optimize() statements to configure the optimization process and/or smart optimization engines
  4. at the beginning of each In-sample walk-forward step again to setup optimization parameters

Bottom line: we should never assume that certain formula will only be executed e.g. N-times during certain time-frame, because all really depends on the above factors, our actions and changing input.

Next Page »