amibroker

HomeKnowledge Base

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.

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

color IIfClose MA50colorGreencolorRed );
Plot1""colorstyleArea|styleOwnScale,0,);

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.

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

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

fixed z-order area chart

More information about use of Z-order can be found in the User’s Guide:
http://www.amibroker.com/guide/h_indbuilder2.html

“Invalid symbol” message for stocks traded on multiple exchanges (IB)

Some of the symbols available from Interactive Brokers data-feed may be traded on multiple exchanges. In such case, when we enter the symbol to the database as:

INTCGLDCSCO

then we may see “Invalid symbol” message displayed by the Interactive Brokers plugin, as symbol SMART-STK-USD default format is not enough to uniquely identify the symbol. Therefore we would need to specify the primary exchange for the given symbol.

The primary exchange can be specified in the symbol using -!EXCHANGE suffix, so we can write the symbol like this

INTC-!NASDAQ

and it will instruct Interactive Brokers plugin to send NASDAQ as Primary Exchange for INTC to TWS.

We can however maintain similar ticker naming for all symbols in our database and use short names for all US symbols. To make it possible there is special dictionary (or translation table) for IB symbols that both plugin and IB automated trading interface use. In AmiBroker main folder there is a IBDictionary.txt file, which we can edit with any plain text editor (like Notepad) and provide symbol translations. It describes how symbols are translated before plugin communicates with TWS API.

The format of this file is CSV (comma separated values):

InputSymbol,OutputSymbol, comment (optional)

For example:

INTC,INTC-!NASDAQ, This is because INTC is now traded on multiple exchanges, so we need to set primary exchangeCSCO,CSCO-!NASDAQ, The same as above MSFT,MSFT-!NASDAQGLD,GLD-!ARCA

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:

http://www.amibroker.com/guide/h_portfolio.html

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 handle delisted symbols in rotational test

This Knowledge Base article: http://www.amibroker.com/kb/2014/09/26/closing-trades-in-delisted-symbols/ explains how to close trades in delisted symbols in regular backtest (to avoid holding delisted stocks in the trade list and have our max symbol limit impacted by those positions).

In rotational test however we cannot use Sell variable, because trades are driven by symbols’ ranking by PositionScore values. Therefore we would need to assign zero to PositionScore variable for the exit bars respectively – this will force exiting any positions held in given stock.

EnableRotationalTrading();

bi BarIndex();
lastbi LastValuebi ) - Status("BuyDelay"); 
exitLastBar bi == lastbi;

score /*our regular positionScore*/;
PositionScore IIfexitLastBar 0score );

Note that we are adjusting the last bar index in case trade delays are set in the settings.

As in the regular test, we can also use DelistingDate information if we have it imported into Symbol ->Information window.

EnableRotationalTrading();
exitLastBar datetime() >= GetFnData("DelistingDate");

score /*our regular positionScore*/;
PositionScore IIfexitLastBar 0score );

Limit number of trades per day in a backtest

NOTE: The codes presented below are for intraday data only.

The scenario is as follows: we are intraday traders and we want to limit the number of trades made per day per symbol.

To simulate such scenario in a backtest, we need to count the signals and remove them accordingly after we reach our limit. There are several methods to do so and the choice depends on the signals that our system generates.

If our trading signals come in a sequence like Buy-Sell-Buy-Sell (without repeated signals in between), then we could just count BUY signals since the beginning of the day and allow first N of these signals, where N is the number of trades we allow. This can be achieved with Sum function:

// trades limit
2;

PlotClose"Close"colorDefaultstyleBar );

// identify new day
dn DateNum();
newDay dn != Refdn,-1);

// buy and sell signals
Buy CrossMACD(), Signal() );
Sell CrossSignal(), MACD() );

// visualize signals with yellow arrows
PlotShapes(Buy*shapeUpArrowcolorYellow0Low);
PlotShapes(Sell*shapeDownArrowcolorYellow0High);

// modify Buy array and allow only first N signals
Buy Buy AND SumBuyBarsSincenewDay) +) <= N;

// visualize modified signals with green triangles
PlotShapes(Buy*shapeUpTrianglecolorGreen0Low, -24);

Price chart

If the signals of the same type may get repeated and occur for example in sequence like Buy-Buy-Buy-Sell, then before counting the entry signals we would first need to remove redundant ones. This can be achieved with Equity( 1 ) function call, which will remove repeated signals the way backtester would handle them:

// trades limit
2;

PlotClose"Close"colorDefaultstyleBar );

// identify new day
dn DateNum();
newDay dn != Refdn, -);

// buy and sell signals
Buy =  MACD() > Signal(); // sample repeated signals
// exit on signal vs macd crossover or last bar of the day
Sell CrossSignal(), MACD() ) OR Refnewday); 

// visualize signals with yellow arrows
PlotShapesBuy*shapeUpArrowcolorYellow0Low );
PlotShapesSell*shapeDownArrowcolorred0High );

// remove redundant signals
Equity);

// modify Buy array and allow only first N signals
Buy Buy AND SumBuyBarsSincenewDay ) + ) <= N;

// visualize modified signals with green triangles
PlotShapesBuy*shapeUpTrianglecolorGreen0Low, -24 );

Price chart

When our trading system uses complex trading rules so we don’t know the order of signals, we can use a loop to process signals and count trades.

// trades limit
2;

PlotClose"Close"colorDefaultstyleBar );

// identify new day
dn DateNum();
newDay dn != Refdn, -);

// buy and sell signals
// sample repeated signals
Buy =  MACD() > Signal(); 
//exit on signal vs macd crossover or last bar of the day
Sell CrossSignal(), MACD() ) OR Refnewday); 

// visualize signals with yellow arrows
PlotShapesBuy*shapeUpArrowcolorYellow0Low );
PlotShapesSell*shapeDownArrowcolorred0High );

tradeCount onBuy 0;

for( 
0BarCounti++ )
{
    
// reset trade counter on the new day
    
if( newDay] ) tradeCount 0;

    
// keep buy signal if there is no trade and trade count did not hit the limit
    
if( Buy] AND tradeCount AND NOT onBuy )
    {
        
OnBuy 1;
        
TradeCount++;
    }
    else
        
Buy] = 0// ignore other buy signals


    
if( onBuy AND Sell] )
    {
        
onBuy 0// reset onBuy flag on exit
    
}
}

// visualize modified signals with green triangles
PlotShapesBuy*shapeUpTrianglecolorGreen0Low, -24 );

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: http://www.amibroker.com/kb/wp-content/uploads/2015/10/samplematrix.txt

// 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" );

if( 
fh )
{
    
0;

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

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

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