HomeKnowledge Base

How to combine data from multiple input files

Sometimes it is useful to update existing database with quotes from other source and sometimes we just want to update one data field, leaving other unaffected. For example we may want to import data into Aux1 and Aux2 fields leaving regular OHLC prices untouched.

This post will show how to use $HYBRID mode of ASCII importer to combine data from multiple input files.

When we use Import Wizard / Import ASCII features to import quotations, by default the importer expects Close prices to be present in the imported data. There are some situations however when it would be convenient to import data just to one field (e.g. Aux1) if we have some additional values that we want to store in the database.

Of course, one way would be to combine all these quotes in a single file before the import and process in one run – but ASCII importer allows also to use special $HYBRID mode to import such partial data.

As an example, let us consider a situation where we already have adjusted OHLC quotes imported into the database and we have a file containing unadjusted quotes in the following format:


In order to import those quotes into Aux1 field and combine with existing data, go to File->Import Wizard menu, pick the file and define format the following way:

ASCII import wizard

First we need to define columns so they match the format of input data (in this case set first three columns to Ticker, DMY, Aux1 and the rest to Skip).

To activate special hybrid mode we need to type:


into Additional Commands box and since we are not importing regular prices that include Close array, we also need to mark Allow negative prices.

If instead of using the wizard we create import definition file manually, then it should contain:


Now we can verify the results in Symbol->Quote Editor. The OHLC fields still contain adjusted values, while additional data has been properly stored in Aux1 field without affecting the other fields.

Quote Editor

For more information about Import Wizard and ASCII importer please check the following parts of the User’s Guide:

Per-symbol profit/loss in a portfolio backtest

Backtesting engine in AmiBroker allows to add custom metrics to the report, both in the summary report and in the trade list. This is possible with Custom Backtester Interface, which allows to modify the execution of portfolio-level phase of the test and (among many other features) adjust report generation.

The example presented below shows how to retrieve individual profit/loss figures for each traded symbol in a portfolio test and add the results as custom metrics to the report. The code performs backtest, then iterates through the list of trades and stores each symbol profit in separate variables. Variables are created with VarSet function, which allows to build variable names dynamically, based on the symbol name. There are 2 variables generated per symbol, one holding profit for long trades and one for short trades. In the last part the code reads the created variables and adds input into the backtest report.

function ProcessTradetrade )
symbol trade.Symbol;
if( ! StrFindtradedSymbols"," symbol "," ) )
tradedSymbols += symbol ",";
  // HINT: you may replace it with GetPercentProfit if you wish
profit trade.GetProfit();
if( trade.IsLong() )
varname "long_" symbol;
VarSetvarnameNzVarGetvarname ) ) + profit );
varname "short_" symbol;
VarSetvarnameNzVarGetvarname ) ) + profit );
SetCustomBacktestProc"" );
/* Now custom-backtest procedure follows */
if ( Status"action" ) == actionPortfolio )
bo GetBacktesterObject();
bo.Backtest(); // run default backtest procedure
tradedSymbols ",";
    //iterate through closed trades
for ( trade bo.GetFirstTrade( ); tradetrade bo.GetNextTrade( ) )
ProcessTradetrade );
    //iterate through open positions
for ( trade bo.GetFirstOpenPos( ); tradetrade bo.GetNextOpenPos( ) )
ProcessTradetrade );
    //iterate through the list of traded symbols and generate custom metrics
for ( 1; ( sym StrExtracttradedSymbols) ) != ""i++ )
longprofit VarGet"long_" sym );
shortprofit VarGet"short_" sym );
allprofit Nzlongprofit ) + Nzshortprofit );
// metric uses 2 decimal points and
        // 3 (calculate sum) as a "combine method" for walk forward out-of-sample
bo.AddCustomMetric"Profit for " symallprofitlongprofitshortprofit2);
SetOption"MaxOpenPositions"10 );
Buy CrossMACD(), Signal() );
Sell CrossSignal(), MACD() );
Short Sell;
Cover Buy;

Once we run the Backtest, we will get the following output in the report, showing individual profit/loss figures for each symbol in test.

Per-symbol profit

If you prefer percent profits instead of dollar profits, just replace GetProfit() call with GetPercentProfit().

Position sizing based on risk

One of most popular position sizing techniques is Van Tharp risk-based method. Van Tharp defines risk as the maximum amount that can be lost in a trade. Typically you limit your loses by setting up a maximum loss stop.

The amount risked should not be confused with amount invested. If your stop is 15% away from entry price, in worst case you risk losing 15% of the position size (amount invested), not the entire amount. So risk practically means the amount of maximum loss stop.

Now, imagine that we only allow to lose 1% of entire portfolio equity in single trade. If our stop is placed 15% away, it means that to risk just 1% of entire equity we can put 1/15 part of our available equity into this trade. As we can see desired position size is inversely proportional to stop amount.

Buy CrossCMAC20 ) ); // some trading rules
Sell CrossMAC20 ), );
PositionRisk 1// how much (in percent of equity) we risk in single position
TradeRisk 15// trade risk in percent equals to max. loss percentage stop
PctSize 100 PositionRisk TradeRisk;
ApplyStopstopTypeLossstopModePercentTradeRiskTrue );
SetPositionSizePctSizespsPercentOfEquity )

Let us see how it works, say we have equity of $60,000, and we only want to risk 1% in single trade ($600). We set our protective stop to 15%. If stock entry price is say $20, we would put our protective stop at $20-15% = $17, so we will risk $3 per share. Given the fact that we want to risk only $600 in that trade, we could buy 200 shares (position risk 200 * $3 = $600). 200 shares @ $20 each gives position value of $4000. $4000 represents 6.667% of $60,000 and this is actual percentage position size we would open. As we can clearly see 6.667% is what we would get if we divide 1 (percent position risk) by 15 (percent loss amount): 1/15 = 6.667%

Instead of setting our stop as fixed percentage, we can use more sophisticated methods. For example we can adjust our maximum loss (so the risk) dynamically, using average true range, so it will get wider if stock is volatile and narrower if stock prices move in a narrow range. Say we want to set our stop to twice the amount of ATR( 20 ) at the entry bar and risk 3% of portfolio equity in a single trade:

Buy CrossCMAC20 ) ); // some trading rules
Sell CrossMAC20 ), );
RiskPerShare =  ATR20 );
ApplyStopstopTypeLossstopModePointRiskPerShareTrue );
// risk 3% of entire equity on single trade
PositionRisk 3;
// position size calculation
PctSize =  PositionRisk BuyPrice RiskPerShare;
SetPositionSizePctSizespsPercentOfEquity )

This time our maximum loss (so the risk per share) is expressed in dollars not in percents. Let us verify the above calculation. Assume that our equity is $90,000, stock price is $18, ATR(20) is $1. Now risk per share (the stop amount) equals 2 * $1 = $2, so our calculated position size (required % of equity) from the above formula would be:

PctSize = 3 * $18 / $2 = 27%

27% of $90,000 means trade size of $24,300, i.e. 1350 shares (@ $18 each). Since we risk $2 on each share, the total risk is $2 * 1350 shares = $2700, which is exactly 3% of our total equity ($90,000 * 3%).

In case of futures, we would need to take into account the fact that our position size depends on Margin Deposit, while the stop size (expressed in dollars) depends on the Point Value, so the position sizing formula would need to be modified.

Buy CrossCMAC20 ) ); // some trading rules
Sell CrossMAC20 ), );
RiskPerContract ATR20 );
ApplyStopstopTypeLossstopModePointRiskPerContractTrue );
// risk 1% of entire equity on single trade
PositionRisk 1;
PctSize =  PositionRisk MarginDeposit  / ( RiskPerContract PointValue );
SetPositionSizePctSizespsPercentOfEquity )

Let us assume that we are trading a contract with $5000 margin deposit, point value $50, our equity is 1 million and ATR(20) is equal 5 big points. Risk per contract is then 10 big points. Now the above formula would give us:

PctSize = 1% * $5000 / ( 10 * $50 ) = 10%

10% of our 1 million equity is $100K, which allows us to buy 20 contracts (20 * $5000 each). Since our risk is 10 big points and each big point has a value of $50 we are risking 10 * $50 = $500 per contract. We have 20 contracts so entire position represents a risk 20 * $500 = $10,000 which is 1% of our 1 million equity.

How to draw regression channel programatically

Built-in drawing tool allows to place regression channel on the chart manually and the study works on regular Close array as input. The power of AFL allows to automate this task and draw a customizable regression channel automatically in the chart or choose any custom array for calculation.

Here is a sample coding solution showing how to code Standard Deviation based channel. The Parameters dialog allows to control the array the channel is based upon, number of periods used for calculation, position and width of the channel.

lookback Param"Look back"201200);
shift Param"Shift"0020);
multiplier Param"Width"10.2550.25 );
color ParamColor"Color"colorRed );
style ParamStyle"Style"styleLine styleDots );
pricestyle ParamStyle"Price style"styleBar styleThickmaskPrice );
// price chart
PlotClose"Close"colorDefaultpricestyle );
array = ParamField"Price field", -);
BarIndex() + 1;
lastx LastValue) - shift;
// compute linear regression coefficients
aa LastValueRefLinRegIntercept( array, lookback ), -shift ) );
bb LastValueRefLinRegSlope( array, lookback ), -shift ) );
// the equation for straight line
Aa bb * ( - ( Lastx lookback ) );
width LastValueRefStDev( array, lookback ), -shift ) );
drawit > ( lastx lookback ) AND BarIndex() < Lastx;
// draw regression line...
PlotIIfdrawityNull ), "LinReg"colorstyle );
// ... and channel
PlotIIfdrawitwidth*multiplier Null ), "LinReg UP"colorstyle );
PlotIIfdrawitwidth*multiplier Null ), "LinReg DN"colorstyle )

Here is the picture that shows how it looks:

Regression in AFL

How to get support most efficiently

As a customer you want to have your questions and issues resolved most quickly and it is also our goal. In order to allow us to serve you the best possible answer in shortest possible time you need to provide some essential information that we can not collect without your help.

The most essential things you need to tell us when contacting support are:

  1. AmiBroker version you are running
  2. Data source you are using
  3. Exact error message you are getting, or screenshot if problem is of “visual” type
  4. The AFL formula that you are using (if question/issue is formula-related)
    and the settings (if you want help on backtest/optimization results)

  5. … and pretty please do not yell that you found a bug (there are 99.9% chances you did not).

If we counted the hours spent on e-mail exchange that was just asking for those details it would sum up to years. Please provide those in front. We really don’t know what you are running on your end.

There are countless examples when knowledge of these details would save hours of time (not only our but your time too). For example we write a formula for somebody, then he responds “it does not work” then we ask for details on what exactly does not not work and after some hours and e-mail exchanges it turns out that the customer is using old version of the software, while our formula used a function that was introduced lately.

Version numbers

When specifying AmiBroker version number please tell the number, not just “I am using latest version”, because latest means different thing for you and different for us. Your “latest” may mean latest official release and we may mean latest BETA that we just released a couple of minutes ago, so you are not even aware of it. So to remove doubt, please tell us exact number such as “I am running v5.85.0 32-bit”. You can find exact version number in the Help->About window.

Sometimes it also helps to know Windows version. For example we have seen cases when execution speed was different on XP and Windows 7 or when some feature did not work correctly on Windows XP because of Windows bug. So if you are reporting a problem/bug please include Windows version too. In this case you don’t need to tell us exact version, so “I am running Windows XP” or “Windows 7 64-bit” is perfectly fine.

The data source

Telling us a Data source is important when you are using plugin-driven data source. Pretty often 3rd party data sources that are plugin driven can cause bizzarre issues that can not be reproduced unless we know the data source.

Tell us what the error is or how it looks

It goes without saying that knowing the actual text of error message you get is essential for us. Please quote the message precisely as sometimes actual wording differs and there may be two similar messages yet they are different and resolution is also different. For any kind of “visual” problems it is best to send a screenshot. To make a screenshot, press PRINT SCREEN key on the keyboard (to make a short of entire screen), or ALT+PRTSCREEN (to make a screenshot of just active window). This puts a screenshot to the clipboard. Now you can paste it to MS Paint, MS Word, or any other graphic application. Some e-mail clients such as Thunderbird, allows pasting such image directly from clipboard into message (Edit->Paste, or Ctrl+V).

Sometimes it may not be obvious what is wrong on the screenshot so please describe what is wrong and/or annotate graphics. It helps tremendously.

The magical formula

As far as AFL-related problems are considered, knowing the formula is essential. If we can paste your formula to editor and reproduce reported behaviour on our end helping you becomes much easier and faster. An enormous amount of time could be gained if formula in question is sent to us. If for some reason you can not send us actual formula that you are using, please try to write a small one that just demonstrates the same problematic behavior. It is even better to have such minimalistic formula as it is quicker to find problem area in shorter one. When you are reporing problem with the backtest or optimization please also include the settings. Instead of sending formula and settings separately, consider sending APX file (Analysis Project). The analysis project file has both settings and formula included and can be saved using File->Save As when Analysis window is open.

When something is bugging you

Now here it comes, the last but certainly not least hint. When you are having problems with our software, please do not claim you have found a bug unless you are very, very sure of your ground. Remember, there are many other users that are not experiencing your problem. Otherwise you would have learned about it while reading the documentation and searching the Web (you did do that before complaining, didn’t you?). This means that very probably it is you who are doing something wrong, not the software. In fact in 99.9% of past cases a suspected “bug” was a user mistake.

We put an enormous effort and engineering skills to make our software working as well as possible. If you claim you have found a bug, you will be impugning our competence, which will offend some of us even if you are correct. It is especially undiplomatic to yell bug in the Subject line.

When asking your question, it is best to write as though you assume you are doing something wrong, even if you are privately pretty sure you have found an actual bug. If there really is a bug, you will hear about it in the answer.

All of the above has one goal – enabling us to help you better and quicker. By following the guidelines above you will get your answers delivered faster and more efficiently. Thanks!

Relationship between chart panes

In order to explain possible relationships between chart panes in AmiBroker, let us first introduce two important attributes associated with each chart pane, these are Chart ID and File path.

To see these attributes, we need to right-click on the chart and choose Parameters, then Axes&Grid tab:

Parameters window

The File path defines physical AFL file on your hard-drive that contains the indicator code used to generate charts. The Chart ID is an index to AmiBroker’s internal chart table. The chart table holds chart settings such as those seen in Axes&Grid tab, including the File path. So knowing the Chart ID, AmiBroker knows everything about the chart. The Chart ID uniquely identifies the chart.

Chart ID plays important role in the following areas:

  1. Chart parameters are keyed by Chart ID so two different charts may have same names of parameters, yet parameters are independent if those charts have different ChartID.
  2. Hand-drawn studies are stored wih Chart ID assigned to it, so they “belong” to given chart and can be referenced from other formulas using Study() function
  3. Chart IDs are stored in the layout files so AmiBroker knows which charts should be loaded when layout is loaded.

When we compare two charts, then the following relations are possible:

  1. if both Chart IDs and File paths are different, then charts are completely independent. This will happen if we use Insert option from the Charts window to create a new chart pane or when we double-click on the given formula (double-click works as Insert), because AmiBroker then would create new Chart ID and independent copy of the formula as described in manual


  2. if Chart IDs are different in both chart panes but File Path is the same, then these are different charts, yet they share same formula file. In such case hand-drawn studies will not get transferred and parameters will not be shared, but drag&drop operations and changes in the formula will affect both charts. This situation will occur if we use Insert Linked option from the Charts window to create a new chart pane directly linked to the original formula.

    Insert linked

  3. if Chart ID is the same for both – then it is essentially the very same chart. It means that all the hand-drawn studies and parameters are common for both charts (changes in one chart pane will affect the other) and all the drag&drop operations or formula changes affect both charts – that is because as a result of dragging and dropping indicators the underlying code is being modified (new sections are added). Such relationship between chart panes will happen when we open another chart window that uses the same chart template as the previous window (e.g. using File->New->Default Chart) or save chart template and load it from the other window.

There is also a functionality in AmiBroker, which allows to copy-paste the selected chart pane and define which elements are pasted. To do that click on the pane you want to copy, choose Edit->Copy from the menu, then switch to the place where you want to paste it and choose Edit->Paste Special

Paste Special

Choosing first option creates new Chart ID, and creates a copy of formula, so resulted chart is independent from source chart. Choosing second option creates new Chart ID, but links it to same formula as original, so the formula is shared. Choosing third option inserts a pane that uses same Chart ID (identical chart, everything shared).

Paste Special window

What are constants in AFL and how they work

The AFL language contains many pre-defined words like: shapeUpArrow, stopTypeTrailing, colorRed, styleThick, inDaily and many more. These are examples of constants. As written in AFL language specification ( Constants are tokens representing fixed numeric or character values.

To better explain what this means, let us consider example of PI constant, which equals 3.14159265358979….. PI is the name of constant we use this name in mathematical equations, because it is easier and more practical to use than using the numerical value each time. Constants in AFL serve the same purpose, each of these words represents certain value properly interpreted by the program in the context they are used.

That is why using the following statement in backtesting code:

ApplyStopstopTypeTrailingstopModePercent10 )

is much better to use than cryptic statement like:

ApplyStop2110 )

Both commands are equivalent, because value of stopTypeTrailing constant equals 2 and value of stopModePercent constant equals 1, yet the first version is much more understandable.

There is also another reason to use pre-defined constants rather than hard-coded numbers in the code. If for any reason the internal value of given constant changes due to development needs – all formulas using constants will continue to work properly (because new version would interpret them properly), while hard-coded numbers may change the code execution. For example – inWeekly and inMonthly constants have changed with introduction of N*inDaily timeframes, however if we always used:
TimeFrameSet( in Weekly ); in the code, then such internal change does not really affect our formulas at all.

There is one more example worth discussing – in the documentation of PlotShapes function we can find:

PlotClose"Price"colorBlackstyleCandle );
Buy CrossMACD(), Signal() );
Sell CrossSignal(), MACD() );
shape Buy shapeUpArrow Sell shapeDownArrow;
PlotShapesshapeIIfBuycolorGreencolorRed ), 0IIfBuyLowHigh ) )

So – what does the multiplication mean in the above context? If we remember that constants are in fact just numbers, and boolean True in AFL has numeric value of 1, while boolean False has numeric value of 0, then:

– if Buy is True (equals 1) and Sell is False (equals 0), then the result of such calculation will be

shape = 1 * shapeUpArrow + 0 * shapeDownArrow = shapeUpArrow

– if Buy is False (equals 0) and Sell is True (equals 1), then the result of such calculation will be:

shape = 0 * shapeUpArrow + 1 * shapeDownArrow = shapeDownArrow

The above approach is kind of shortcut that saves using conditional statements. It would work correctly only if Buy and Sell signals never occur on the same bar and only if we assign just 0 or 1 (False / True) to Buy and Sell arrays. Otherwise the result of calculations would be different. The internal value of shapeUpArrow is 1 and ShapeDownArrow is 2, so in situation, where both Buy and Sell signals were true, we would get

shape = 1 * shapeUpArrow + 1 * shapeDownArrow = shapeUpArrow + shapeDownArrow = 1 + 2 = 3

So – we would then pass number 3 to PlotShapes function and this is neither shapeUpArrow nor ShapeDownArrow, but a different shape. That is why in general case it is better to use conditional function IIf, like shown below:

shape IIfBuyshapeUpArrowIIfSellshapeDownArrowshapeNone ) )

This way we are always sure that returned value will be shapeUpArrow or shapeDownArrow or shapeNone.

How to setup automatic periodic scans & explorations

One of the most powerful features of AmiBroker is the ability of screening even hundreds of symbols in real-time and monitor the occurrence of trading signals, chart patterns and other market conditions we are looking for. This can be done in Analysis module with Scan or Exploration features.

The main difference between Scan and Exploration is that Exploration allows to customize the output shown in Analysis window (this is explained in details in the following tutorial chapter:, while Scan performs search for at least one of Buy, Sell, Short, Cover signals and displays predefined set of columns. Both these features allow for continuous screening of the database in real-time conditions.

The following procedure shows how to configure basic scan formula and generate alerts when conditions coded in the formula are met. We assume that AmiBroker is already configured to receive real-time data from one of realtime data vendors – the list of recommended datasources is available here:

We need to do the following:
– open Formula Editor window with Analysis->Formula Editor command from the menu
– in the editor window enter or paste the code below

// example trading signals defined here
Buy CrossMACD(), Signal() );
Sell CrossSignal(), MACD() );
// additional part of the formula which generates audio alerts when condition is detected
AlertIFBuy"SOUND C:\\Windows\\Media\\Ding.wav""Audio alert");
AlertIFSell"SOUND C:\\Windows\\Media\\Ding.wav""Audio alert")

After entering the code use Tools->Send to Analysis as shown below:

Send to Analysis

Then in the Analysis window select Apply To: All Symbols, Range: 1 Recent bar, this defines which symbols are included in the screening and what time-range will be shown in the results list.

Range setting

To enable continuous screening, mark Auto-repeat (AR) Scan/Explore option and enter the repeat interval. The interval can be specified in minutes or seconds (for example entering 10s means 10-seconds, while 5m means 5-minutes). The below example uses 15-second repeat interval:

Auto-repeat setting

NOTE: If that is the very first screening after launching the database and it may require filling the historical quotes, then it is also required to mark Wait for Backfill (applies to data sources, which support this feature, see: for more details).

Now press Scan button to initiate the screening process:


The results window will show the hits and generated alerts will also be logged in Alert Output window and the scan will be automatically repeated every 15 seconds in search for new signals.


More information about generating and configuration formula-based alerts is presented in this tutorial:

How to restore accidentially deleted price chart

When working with chart windows it sometimes may happen that we mistakenly close the chart we meant to keep displayed. Here are some suggestions showing how to quickly restore our working setup.

First situation happens when when we closed all chart windows and AmiBroker shows just an empty application window, what looks like this:

Empty chart space

In such situation the best way is to use File–>New–>Default Chart menu command, as a result will display a new chart window, which contains charts stored in default template.

Re-open default chart

Second situation is when we closed just the Price chart pane, so only indicators would remain in the chart window, looking like this:

No price pane

In order to bring the Price chart back, go to Charts window, unfold Basic Charts folder and double-click on Price (all in one) (if you want to get price chart with moving average and Bollinger bands overlays), or double-click Price (if you want to get price chart alone).

Insert price chart

It will be located at the bottom, below the other charts, but we can move it up to the top if we right-click on the chart, then choose Pane->Move Up from the context menu.

It is worth noting that AmiBroker allows to create multiple chart setups stored in Layouts, so if we have already created several layouts before, it is also possible to re-load one of the layouts from the Layouts window to restore the whole saved chart setup. This functionality is discussed in details in the following tutorial chapter:

Third party software “black list”

From time to time users face bizarre problems that after hours/days of investigation turn to be caused by 3rd party softwares that modify normal operation of Windows OS and cause troubles. As most problems are caused by badly written antiviruses, we recommend using Microsoft Security Essentials that is known to be OS friendly, working fast and without issues.

Here is a list of 3rd party software that are known to cause problems:

1. Avast Antivirus – specifically it’s Autosandbox feature messes up with operating system and effectively disallows application to write data to the disk. Which means that any of your changes to the data, watch lists, drawings, etc, will not be saved unless you turn off Autosandbox or uninstall Avast. Avast has its “heuristics” all wrong. They use Autosandbox even on legitimate, Microsoft Authenticode digitally signed applications like AmiBroker. Solution: uninstall Avast and install Microsoft Security Essentials instead.

2. Comodo Internet security – the same story as above – their “Sandbox” is messing up with OS and blocks applications from vital activity such as saving files or accessing registry. Their “sandbox” causes files to be stored in wrong places and become inaccessible. Either turn off sandbox or uninstall Comodo and install Microsoft Security Essentials instead.

3. Webroot Secure Anywhere – again it messes up with operating system causing problems with Windows Clipboard, preventing Edit->Paste from working in many applications including the new AFL editor. The solution is to go to Webroot Identity Protection settings and turn OFF Identity Shield. The issue affects many applications and is described in detail on their forum:

4. AMD Catalyst Software Suite, Desktop Desktop Manager – it may cause problems with saving window positions because it has a feature called “Dialog repositioning” that moves windows by itself somewhere else. AMD of course knows better where you want to have your windows. Solution: disable “Dialog repositioning” as shown below or turn off “Desktop Manager” feature. You may also uncheck “Preserve application position and size” box because AmiBroker remembers positions of all its windows by itself and does not need AMD’s help.


For the list of recommended hardware and software see:

« Previous Page