# PositionScore using combined ranks

I'm interested in combining various formulas into a single positionscore in order to perform a complex ranking. When looking for examples, I've only encountered simple rankings such as:

PositionScore = ROC( Close, 252 );

Lets say I wanted to combine several ranking methods:

Rank1 = ROC( Close, 252 );
Rank2 = // something else

PositionScore = // some combination of Rank1 and Rank2

I'm thinking I could perform a weighted sum of the normalized values of Rank1 and Rank2. I expect that I would have to normalize each rank over all the tickers going into my backtest over the backtest period. This seems like it requires a pass through the data ahead of time.

Does anyone know the AFL code for finding the min/max values given some formula over a range of ticker symbols and a time range?

Are there other ways to perform the combination of ranks (perhaps using some built-in functions)?

You can perform your multiple rankings using `StaticVarGenerateRanks()` and then combine them to produce final `PositionScore`.

Example usage is presented here:
http://www.amibroker.com/kb/2016/01/30/separate-ranks-for-categories-that-can-be-used-in-backtesting/

3 Likes

Also, if you are after normalization of `ROC()` keep in mind that `RSI()` is essentially normalized `ROC()` (uses ‘period’ high-low range to do the normalization). So you could simply use `RSIa( Close, 252 )` for your Rank1. Once normalized it is easy to combine in weighted sum.

3 Likes

I am using EnableRotationalTrading mode. But PositionScore only allow one score’s ranking.
I want to do PositionScore = rank(RSI(3))+rank(RSI(6)).(rank all stock’s RSI value)
Anyone know how to do this?

Thank you so much.

You have only provided limited information, so I am not certain what you have coded. But have you tried,

``````Score = (RSI(3) + RSI(6))/2;
PositionScore = Score;
``````

Forum members need to see more of your code to understand where you may be making mistakes and to understand what you are attempting to do.

I'm just playing around with some code here but if your code is more complex and you are using StaticVarGenerateRanks then perhaps something like this would help

``````wlnumber  = GetOption( "FilterIncludeWatchlist" );
watchlist = GetCategorySymbols( categoryWatchlist, wlnumber );
category = categoryWatchlist;

Tickerlist = CategoryGetSymbols( category, wlnumber );

if( Status( "stocknum" ) == 0 ) // generate ranking when on first symbol
{
StaticVarRemove( "values*" );

for( n = 0; ( Symbol = StrExtract( Tickerlist, n ) )  != "";  n++ )
{
SetForeign( symbol );
values = ( RSI( 3 ) + RSI( 6 ) ) / 2;
RestorePriceArrays();
StaticVarSet( "values"  +  symbol, values );
}

StaticVarGenerateRanks( "rank", "values", 0, 1224 );
}

symbol = Name();

values = StaticVarGet( "values" +  symbol );
rank = StaticVarGet( "rankvalues" +  symbol );

Filter = 1;
SetSortColumns( -5 );
AddColumn( RSI( 3 ), "RSI(3)", 1.2 );
AddColumn( RSI( 6 ), "RSI(6)", 1.2 );

``````

Running an Explore on today's DJIA to see what the code generates looks something like this,

10 Likes

Thanks for merging the threads Tomasz. Sorry I missed that and actually considered just posting links to similar discussions on the old Yahoo! forum as there is still useful information there.

For example a discussion on combining ranking of Momentum, Volatility and Correlation, https://groups.yahoo.com/neo/groups/amibroker/conversations/topics/180605

2 Likes

Tomasz or if someone else can confirm as there may be a workaround for my question that I am not seeing...

Is there a way to substitute the GicsID versus the SectorID in the code you referenced here... http://www.amibroker.com/kb/2016/01/30/separate-ranks-for-categories-that-can-be-used-in-backtesting/

It appears that the Gics modes are stored as text(?) so it shuts down the program when I substitute it for the SectorID. My database is set up using the Sectors for Bloomberg Mutual Fund and ETF categories and I use the GICS for equities so I was hoping to modify this ranking but ran into this issue.

@Duket here below is the linked formula with minimal modifications to use the GicsID as a string containing the GICS code alone such as "15103020", instead of the original used SectorID:

``````// Code adapted from:
// http://www.amibroker.com/kb/2016/01/30/separate-ranks-for-categories-that-can-be-used-in-backtesting/
// In anwser to forum request:
// https://forum.amibroker.com/t/positionscore-using-combined-ranks/576/7

// watchlist should contain all symbols included in the test
wlnum = GetOption( "FilterIncludeWatchlist" );
List = CategoryGetSymbols( categoryWatchlist, wlnum ) ;

if( Status( "stocknum" ) == 0 )
{
// cleanup variables created in previous runs (if any)
StaticVarRemove( "rank*" );
StaticVarRemove( "values*" );
categoryList = ",";

for( n = 0; ( Symbol = StrExtract( List, n ) )  != "";  n++ )
{
SetForeign( symbol );

// use Gics numbers for ranking
category = GicsID( 0 );

//// Modify this hard-coded fake Gics ID as needed or remove the
//// conditional block (next 2 lines) if ALL the symbols are GICS classified
if( category == "" )
category = "01"; // It is mandatory to handle also "unclassified" tickers using a non existing (fake) GicsID to avoid an empty "" category

// add the GicsID string to the list
if( ! StrFind( categoryList, "," + category + "," ) ) categoryList += category + ",";

// write our ranking criteria to a variable
// in this example we will use 10-bar rate-of-change
values = Roc( Close, 10 );

RestorePriceArrays();

// write ranked values to a static variable
StaticVarSet( "values" + category + "_" + symbol, values );

}

// generate separate ranks for each category (GicsID string) from the list
for( i = 1; ( category = StrExtract( categoryList, i ) ) != ""; i++ )
{
StaticVarGenerateRanks( "rank", "values" + category + "_", 0, 1224 );
}
}

category = GicsID( 0 );
//// if (category == "")
////    category = "01"; // optionally shows also unclassified tickers that were ranked using a non existing Gics Id
symbol = Name();
m = Month();

values = StaticVarGet( "values" + category + "_" + symbol );
rank = StaticVarGet( "rank" + "values" + category + "_" + symbol );

// exploration code for verification
AddTextColumn( GicsID( 1 ), "Gics" );
AddTextColumn( GicsID( 0 ), "Gics ID" );
Filter = rank <= 2;

if( Status( "Action" ) == actionExplore )
SetSortColumns( 2, 5, 4 );

// sample backtesting rules
SetBacktestMode( backtestRotational );
score = IIf( rank <= 2, values, 0 );
// switch symbols at the beginning of the month only
PositionScore = IIf( m != Ref( m, -1 ), score, scoreNoRotate );
SetPositionSize( 1, spsPercentOfEquity );
``````

Note that in the code (marked with a //// comment) I added a conditional that will take care of any ticker that may not have a GicsID code (probably in your database, if all the symbols are classified, it is not needed).

6 Likes

Beppe, First, Thank you for taking a look at the issue and providing a solution. I can see now I was tackling the idea incorrectly as I was trying to convert the string (and failed to account for non-classified tickers) and wasn't getting anywhere as when I ran my attempt it would literally shut down Amibroker which is not an easy feat as Tomasz has built a pretty solid platform! Again, thank you for your time and help.

2 Likes

I am interested in the discussion but can’t access this link, any chance to download it here?

Thanks,

Oscar

@osinv, old Yahoo groups are no longer accessible (although some content may have been saved and made available by other sites).

The author of that post was @TrendXplorer and it was about his initial Amibroker implementation of the FAA strategy.

You can find an excellent post of his in this past thread where you can also see information about his blog where he discusses in detail some other asset allocation strategies.

2 Likes

Thank you @beppe, I'll take a look at these.

how do then ask amibroker to buy the top 5 in the rank (BA, NKE, DD, CAT and MCD) ?