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 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 symbol list where first one is best performing (highest rate of change) and the last one is worst performing one.
AmiBroker allows user to perform/use three different kind 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 maximum allowable number of open positions or available funds. In such case AmiBroker will use the absolute value of PositionScore variable to decide which trades are preferred. For the details about ranking functionality during backtesting see Portfolio Backtester tutorial.
Second kind of ranking is simply assigning a number (rank) to the line of exploration output. The rank column is added to the exploration output just by calling AddRankColumn function after performing a sort using 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 most resource hungry (computationally intensive) but also gives 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 new set of static variables that hold calculated ranks.
NOTE: This function is NOT intended to replace bakctester's built-in ranking via PositionScore. Just the opposite: whenever you can, you should use PositionScore as it is way way 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 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 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 lots of time to compute but it also has to lock the access to shared memory used by static variables so other threads trying to access static variables would wait until this function completes.
StaticVarGenerateRanks function
StaticVarGenarateRanks( "outputprefix", "inputprefix", topranks, tiemode ) is a core element of 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 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 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 toprank argument is set to zero.
StaticVarGenerateRanks( "rank", "ItemScore", 0, 1224 );
In this case StaticVarGenerateRanks call would generate set of static variables starting with prefix defined by 2nd argument each variable holding the rank of particular symbol, so in this case RankItemScoreMSFT will hold ranking of MSFT, RankItemScoreAAPL will hold ranking of AAPL. Note that in AmiBroker rank count start from ONE.
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 equal rank.
Example code for normal ranking mode (everything done 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 then bottom ranked values are used.
The values are stored in variables that have format of:
OutputprefixInputprefixN where N is a number 1, 2, 3 representing top/bottom
ranks. Let us assume that
OutputPrefix parameter is "Top" and Inputprefix parameter is ROC.
In such case variable TopROC1 would hold the index of top rated value. TopROC2
would hold second top rated value, and so on.
StaticVarGenerateRanks function uses rank numbering that starts from ONE.
In top ranking mode StaticVarGenerateRanks will also prepare static variable
that contains 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
lookup first substring in TopROCSymbols variable to find out what variable
(symbol)
ranked at the top.
Additionally StaticVarGetRankedSymbols gives easy-to-use method to retrieve
comma separated list of ranked symbols for 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 resource hungry process, it should be performed just once per Analysis run, not for every symbol. You can achieve it either by running separate ranking-generation formula once by hand prior to running Analysis or using Status("stocknum") == 0 statement that would ensure that ranking process is done only for the very first symbol from the watch list under analysis.
Here is an example code for exploration that takes currently active watch list or 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 );