A ranking is a relationship between a set of items such that, for any two items, the first is either 'ranked higher than', 'ranked lower than' or 'ranked equal to' the second. The simplest way to obtain the rank is to sort items by 'value' or 'score'. For example, you can take a 100-bar rate of change for symbols - it will be your item 'score' or 'value'. Then sort the results by it so you will get a symbol list where the first one is the best performing (highest rate of change) and the last one is the worst performing.
AmiBroker allows users to perform/use three different kinds of rankings
The first kind of ranking is performed automatically if your trading system formula defines PositionScore variable. You can use PositionScore variable to decide which trades should be entered if there are more entry signals on different securities than the maximum allowable number of open positions or available funds. In such a case, AmiBroker will use the absolute value of PositionScore variable to decide which trades are preferred. For details about ranking functionality during backtesting, see the Portfolio Backtester tutorial.
The second kind of ranking is simply assigning a number (rank) to each line of exploration output. The rank column is added to the exploration output just by calling the AddRankColumn function after performing a sort using the SetSortColumns function. You can call SetSortColumns multiple times and you can call AddRankColumn multiple times to achieve many different ranks based on multiple columns. See example below:
Filter = 1;
AddColumn( Close, "Close" );
AddColumn( Volume, "BI" );
AddSummaryRows( 31 + 32, 1.5 );
AddRankColumn(); //
without prior sorting AddRankColumn just adds line number
SetSortColumns( -4 );
AddRankColumn(); //
rank according to 4th column (descending)
SetSortColumns( -3 );
AddRankColumn(); //
rank according to 3rd column (ascending)
A third kind of ranking is general-purpose, bar-by-bar ranking that is performed using static variables. It is the most resource-hungry (computationally intensive) but also gives the most possibilities.
Generally, the process involves creating static variables with values to be used for sorting/ranking, i.e., "scores" and then calling a special function (StaticVarGenerateRanks) that generates a new set of static variables that hold calculated ranks.
NOTE: This function is NOT intended to replace backtester's built-in ranking via PositionScore. Just the opposite: whenever you can, you should use PositionScore as it is a far faster and less memory-consuming way to perform backtests with ranking.
StaticVarGenerateRanks is generally intended to be used for tasks OTHER than backtesting, such as explorations or indicators that may require ranking functionality, but of course it can also be used for backtesting when/where PositionScore alone does not allow you to implement what you need in your trading system.
WARNING: This function is computationally and memory intensive. It takes about 20ms per 15K bars and 7 symbols. Try to call it just once per scan/exploration/backtest using if( Status("stocknum")==0) or better yet, use a separate scan just once to pre-calculate ranks and use it later (like composite creation scan). If you fail to do so and call StaticVarGenerateRanks for every symbol, performance would drop significantly as this function not only needs a lot of time to compute but it also has to lock access to shared memory used by static variables so other threads trying to access static variables would wait until this function completes.
StaticVarGenerateRanks function
StaticVarGenerateRanks( "outputprefix", "inputprefix", topranks, tiemode ) is a core element of the general-purpose ranking system. It takes 4 parameters: "outputprefix" - the prefix appended to output static variables that hold the ranks, "inputprefix" - the prefix of static variables holding scores (input), topranks - which defines how many top/bottom-ranking symbols should be included in the generated rank set, and tiemode that defines how ties (equal ranks) should be resolved.
The "inputprefix" is a prefix that defines the names of static variables that will be used as input for ranking. AmiBroker will search for all static variables that begin with that prefix and assume that the remaining part of the variable name is a stock symbol. Say you want to rank stocks by ROC (rate of change). All you need to do is to store values into static variables. Let us say that we will use static variable names like "ItemScoreAPPL", "ItemScoreMSFT", and so on.
To fill input static variables you can use this loop:
for (
i = 0; ( sym = StrExtract(
symlist, i ) ) != "";
i++ )
{
SetForeign(
sym );
Value = ROC( C, 10 );
RestorePriceArrays();
StaticVarSet( "ItemScore" +
sym, Value );
}
Now you are ready to perform sorting/ranking. There are two modes, normal ranking mode and Top/Bottom Rank mode. Normal ranking mode is performed when the toprank argument is set to zero.
StaticVarGenerateRanks( "rank", "ItemScore", 0, 1224 );
In this case, StaticVarGenerateRanks call would generate a set of static variables starting with the prefix defined by the 2nd argument; each variable holding the rank of a particular symbol. So in this case, RankItemScoreMSFT will hold the ranking of MSFT, and RankItemScoreAAPL will hold the ranking of AAPL. Note that in AmiBroker, rank counts start from ONE.
The third argument (topranks) is zero in normal ranking mode. Fourth argument (tiemode) defines how ties are ranked. Supported modes are 1234 and 1224. In 1224 mode, ties are numbered with an equal rank.
Example code for normal ranking mode (everything is done in one pass, can be used in indicator):
symlist = "C,CAT,DD,GE,IBM,INTC,MSFT";
// delete static variables
StaticVarRemove( "ItemScore*" );
// fill input static arrays
for ( i = 0;
( sym = StrExtract( symlist,
i ) ) != ""; i++ )
{
SetForeign(
sym );
Value = ROC( C, 10 );
RestorePriceArrays();
StaticVarSet( "ItemScore" +
sym, Value );
}
// perform ranking
StaticVarGenerateRanks( "rank", "ItemScore", 0, 1224 ); //
normal rank mode
// read ranking
for ( i = 0;
( sym = StrExtract( symlist,
i ) ) != ""; i++ )
{
Plot( StaticVarGet( "RankItemScore" +
sym ), sym, colorCustom10 +
i );
}
Top/bottom ranking mode (that generates top/bottom-ranking tables that
hold indexes to top-ranking values. When topranks > 0, top-ranked values
are used; when topranks < 0, bottom-ranked values are used.
The values are stored in variables that have the format: OutputprefixInputprefixN where N is a number 1, 2, 3, representing top/bottom ranks. Let us assume that
OutputPrefix parameter is "Top" and the Inputprefix parameter is ROC.
In such a case, variable TopROC1 would hold the index of the top-rated value. TopROC2
would hold the second top-rated value, and so on.
StaticVarGenerateRanks function uses rank numbering that starts from ONE.
In top ranking mode, StaticVarGenerateRanks will also prepare a static variable
that contains a comma-separated list of variable names that can be used to
find out which index refers to which symbol. So if TopROC1 holds 1, you would
look up the first substring in the TopROCSymbols variable to find out what variable
(symbol)
ranked at the top.
Additionally, StaticVarGetRankedSymbols gives an easy-to-use method to retrieve a
comma-separated list of ranked symbols for a particular datetime.
Example code for top ranking mode:
symlist = "C,CAT,DD,GE,IBM,INTC,MSFT";
// delete static variables
StaticVarRemove( "ItemScore*" );
// fill input static arrays
for ( i = 0;
( sym = StrExtract( symlist,
i ) ) != ""; i++ )
{
SetForeign(
sym );
Value = ROC( C, 10 );
RestorePriceArrays();
StaticVarSet( "ItemScore" +
sym, Value );
}
// perform ranking
StaticVarGenerateRanks( "rank", "ItemScore", 0, 1224 ); //
normal rank mode
StaticVarGenerateRanks( "top", "ItemScore", 3, 1224 ); //
top-N mode
StaticVarGenerateRanks( "bot", "ItemScore",
-3, 1224 ); //
bottom-N mode
// read ranking
for ( i = 0;
( sym = StrExtract( symlist,
i ) ) != ""; i++ )
{
Plot( StaticVarGet( "RankItemScore" +
sym ), sym, colorCustom10 +
i );
}
sdt = SelectedValue( DateTime()
);
Title = "{{NAME}}
-{{DATE}} - {{VALUES}} TOP: " + StaticVarGetRankedSymbols( "top", "ItemScore",
sdt ) +
" BOT: " + StaticVarGetRankedSymbols( "bot", "ItemScore",
sdt ) ;
How to use StaticVarGenerateRanks in Analysis window
Since ranking is a resource-hungry process, it should be performed just once per Analysis run, not for every symbol. You can achieve it either by running a separate ranking-generation formula once by hand prior to running Analysis, or by using the Status("stocknum") == 0 statement that would ensure that ranking process is done only for the very first symbol from the watchlist under analysis.
Here is an example code for exploration that takes the currently active watchlist or an all-symbol list and performs ranking.
if ( GetOption( "ApplyTo" )
== 2 )
{
wlnum = GetOption( "FilterIncludeWatchlist" );
List = CategoryGetSymbols( categoryWatchlist,
wlnum ) ;
}
else
if ( GetOption( "ApplyTo" )
== 0 )
{
List = CategoryGetSymbols( categoryAll, 0 );
}
else
{
Error( "The
formula works fine if your ApplyTo setting is 'Filter' or 'All' " );
}
if ( Status("stocknum")
== 0 ) //
GENERATE RANKING WHEN WE ARE ON VERY FIRST SYMBOL
{
StaticVarRemove( "values*" );
for (
n = 0; ( Symbol = StrExtract(
List, n ) ) != ""; n++ )
{
SetForeign (
symbol );
values = RSI();
RestorePriceArrays();
StaticVarSet ( "values" + symbol,
values );
_TRACE(
symbol );
}
StaticVarGenerateRanks( "rank", "values", 0, 1224 );
}
symbol = Name();
values = StaticVarGet ( "values" + symbol
);
rank = StaticVarGet ( "rankvalues" + symbol
);
AddColumn ( values, "values" );
AddColumn ( rank, "rank" );
Filter = 1;
SetSortColumns( 2, 4 );