How to imitate IBD Relative Strength Percentile Ranking of Stocks

Hello,
thanks for the reply :slight_smile:
I manage to do it using
Title = Name() + " " + Rank;

but there is an error using it. If the chart time frame is daily, then the Rank would be wrong, because it should show rank in monthly, not change rank every day in daily chart.
I tried to use timeframeset(monthly) for the rank calculation, but it a complete mess without right result, hahaha

So, my question now is how can we plot the rank in the chart, (monthly, weekly, or daily) and still have the correct rank calculation? can you help

thanks

Hello,

thanks for the reply
here is my setting, is it correct?

Capture

@sikatgigi: Those settings look fine for the Backtester Settings.

But now you are asking about showing the Monthly Rank on a Daily chart. For this you will need as a Minimum to learn about the "TimeFrame" functions. Might also need/want static variables.

READ the Manual on the functions, and SEARCH the forum to start learning how to implement. THEN post your code with specific questions (use code blocks to properly format code so we can copy it and try it out).

Hello @snoopy.pa30

thanks for the reply, i am still on it now, I am like a turtle when comes to coding, learning by copying and did a lot of mistakes, for days or months.....

TimeFrame and static variables, thanks for the hint :slight_smile:

Here is the code that I've been recklessly force the set timeframe function, but it didnt work out.....

	// Generate the raw RS score for every stock and store in a static var
	for (n = 0; (Symbol = StrExtract(List, n)) != "";  n++) 
	{ 
		SetForeign (Symbol,0); 
		RSraw = 0;	
		TimeFrameSet(inMonthly);
		// relative strength IBD style
		ThreeMthRS  = 0.4*(C/Ref(C,-3));
		SixMthRS    = 0.2*(C/Ref(C,-6));
		NineMthRS   = 0.2*(C/Ref(C,-9));
		TwelveMthRS = 0.2*(C/Ref(C,-12));
		TimeFrameRestore(); // restore time frame to original 

		RSraw = ThreeMthRS + SixMthRS + NineMthRS + TwelveMthRS; 
		
		RestorePriceArrays(); 
		StaticVarSet("RSraw_"  +  Symbol, RSraw); 
	} 

@sikatgigi, so what did not work out?

Good posting of your code, but what is not working? How are you trying to debug it?

I am away for next couple of days, so not ignoring you when I don't respond.... :smile:

hello @snoopy.pa30,

when i click on the daily chart, the value of the rank still change each day, it suppose to change rank only at the beginning of new month, am I right?

I still dont know where to put the timeframeset correctly....

The TimeFrameRestore() function just works on the built-in arrays like Open, Close, etc. It doesn't affect the variables that you defined like ThreeMthRS, SixMthRS, etc. You will need to use TimeFrameExpand() to get those variables back to your base timeframe.

3 Likes

Hello
thanks for the tip. I am beginning to feel embarrased, because I think it should be something that really simple
I tried different places to put the timeframe command but no correct result.... here is one of the place I put the timeframe command....

	// Generate the raw RS score for every stock and store in a static var
	for (n = 0; (Symbol = StrExtract(List, n)) != "";  n++) 
	{ 

		SetForeign (Symbol,0); 
		RSraw = 0;	
TimeFrameSet(inMonthly);
		// relative strength IBD style
		ThreeMthRS  = 0.4*(C/Ref(C,-3));
		SixMthRS    = 0.2*(C/Ref(C,-6));
		NineMthRS   = 0.2*(C/Ref(C,-9));
		TwelveMthRS = 0.2*(C/Ref(C,-12));
TimeFrameRestore(); // restore time frame to original 

		
		RSraw = TimeFrameExpand (ThreeMthRS + SixMthRS + NineMthRS + TwelveMthRS,inMonthly);
		
		RestorePriceArrays(); 
		StaticVarSet("RSraw_"  +  Symbol, RSraw); 
	} 

Hello,
I search the forum and tried several code that have similar problem with timeframeset + rank function like this link for instance Help for using TimeFrameSet with Static Variables
but find that my stock rank is still change in each candle in daily timeframe, it should changed after the month change in daily timeframe

Here is my attempt based on fxshrat code , can someone direct me where is the wrong is?

`// Relative Strength ranking of stocks
// This code must be run in Analysis Engine with Monthly timeframe. 



// watchlist should contain all symbols included in the test
wlnum = GetOption( "FilterIncludeWatchlist" );
List = CategoryGetSymbols( categoryWatchlist, wlnum ) ;
ListQty = StrCount(List, ",") + 1; 
// Determine the quantity of stocks to rank
StaticVarSet("UniListTotal", ListQty, True);
 
if( Status( "stocknum" ) == 0 )
{
	// Clear the static vars from last run
	StaticVarRemove( "RS_*" );
	StaticVarRemove("RSraw_*");
	StaticVarRemove("RankRSraw*_"); 

	// Generate the raw RS score for every stock and store in a static var
	for (n = 0; (Symbol = StrExtract(List, n)) != "";  n++) 
	{ 
		SetForeign (Symbol,0); 
		RSraw = 0;
		TimeFrameSet(inMonthly);
		// relative strength IBD style
		ThreeMthRS  = 0.4*(C/Ref(C,-3));
		SixMthRS    = 0.2*(C/Ref(C,-6));
		NineMthRS   = 0.2*(C/Ref(C,-9));
		TwelveMthRS = 0.2*(C/Ref(C,-12));
		
		RSraw = ThreeMthRS + SixMthRS + NineMthRS + TwelveMthRS; 
		TimeFrameRestore();	
		RSraw = TimeFrameExpand(RSraw, inMonthly, expandfirst);
		RestorePriceArrays(); 
		StaticVarSet("RSraw_"  +  Symbol, RSraw); 
	} 

	// rank the stocks using this extremely useful function!
	StaticVarGenerateRanks("Rank", "RSraw_", 0, 1224); 

	/**** If you want to automaticlly stores the result in watch list, uncomment the following lines. ***/
	//Create result watchlist based on market of first symbol on list. Default is US market
	//firstSym=StrExtract(List, 0);
	//marketSym=StrExtract(firstSym, 1, '.');
	//resultwl = -1;

	//if ((marketSym == "SZ") OR (marketSym == "SS"))
	//	resultwl = CategoryCreate( "Top Rank China Stocks", categoryWatchlist ); 
	//else if (marketSym == "HK")
	//	resultwl = CategoryCreate( "Top Rank HK Stocks", categoryWatchlist );
	//else
	//	resultwl = CategoryCreate( "Top Rank US Stocks", categoryWatchlist ); 

	// Convert the static var ranks generated into a percentile score.
	for (n = 0; (Symbol = StrExtract(List, n))  != "";  n++) 
	{
		
		Rank  = StaticVarGet ("RankRSraw_" +  Symbol); 
		
		TimeFrameSet(inMonthly);
		RSpctile = 100 - 100*Rank/ListQty;
		TimeFrameRestore();	
		RSpctile = TimeFrameExpand(RSpctile, inMonthly, expandfirst);
		StaticVarSet("RS_" + Symbol, RSpctile, True);
	
		/**** If you want to automaticlly stores the result in watch list, uncomment the following lines. ***/
		// use this opportunity to store the highest ranking stocks in a watchlist.
		//if (LastValue(RSpctile) >= 95)
		//	CategoryAddSymbol(Symbol, categoryWatchlist, resultwl);
		//else 
		//	CategoryRemoveSymbol(Symbol, categoryWatchlist, resultwl);
	}

}

Rank  = StaticVarGet ("RS_" +  Name()); 




function GetRS(Symbol)
{
	// Retrieve relative strength static var
	return StaticVarGet ( "RS_" +  Symbol ); 
}



		
Filter= 1;
AddColumn( Close, "Close" );
rank = PercentRank( Close, 100 );
Color = ColorHSB( rank * 64/100, 255, 255 );
AddColumn( rank, "100-day percent rank", 1.2, colorDefault, Color, -1, rank );

if( Status( "Action" ) == actionExplore ) SetSortColumns(-4,1);


sdt = SelectedValue( DateTime() ); 

Title = "{{NAME}} -{{DATE}} - {{VALUES}} TOP: " + Rank+ 
        " BOT: " + StaticVarGetRankedSymbols( "bot", "ValuesToSort", sdt ) ; 
        
GfxSetOverlayMode(0);
GfxSetZOrder(-4);
GfxSelectFont("Tahoma", Status("pxheight")/5 );
GfxSetTextAlign( 6 );// center alignment
GfxSetTextColor( ColorRGB( 200, 200, 200 ) );
GfxSetBkMode(1); // transparent
GfxTextOut( WriteVal(rank,1,1), Status("pxwidth")/2, Status("pxheight")/12 );`

Hi guys, first off thanks for all of your contributions here and thanks to @kzliao for sharing the concept. I have been trying to rework this slightly so that I can create and exploration output similar to the one in the code, but instead just reflect the top 15 ranked tickers in my watchlist.

Eventually, the idea is to reduce a large watchlist to the top X ranked positions and then build trading signals around just those tickers. Clearly I would like to backtest the idea and rules so I need to reassess the top X ranked tickers daily.

In any case, as a starting point I have made some small adjustments to the code below but the Top Ranking mode (i.e. 'topranks' in the StaticVarGenerateRanks function) does not seem to be working properly. If I filter based on that score I suddenly receive no exploration output and even if I filter based on Rank and add a column to see the TopRank value no value appears.

Any thoughts or advice would be appreciated!

// Relative Strength ranking of stocks

wlnum = 3;//GetOption( "FilterIncludeWatchlist" );
List = CategoryGetSymbols( categoryWatchlist, wlnum ) ;
ListQty = StrCount(List, ",") + 1; 

// Determine the quantity of stocks to rank
StaticVarSet("UniListTotal", ListQty, True);
 
if( Status( "stocknum" ) == 0 )
{
	// Clear the static vars from last run
	StaticVarRemove( "MomoScore*" );
	StaticVarRemove("RankMomoScore*");
	StaticVarRemove("TopMomoScore*");


	// Generate the Momentum score for every stock and store in a static var
	for (n = 0; (Symbol = StrExtract(List, n)) != "";  n++) 
	{ 
		SetForeign (Symbol,0); 
		Momentum = 0;
		PSPer = Param ("Lookback for PositionScore",20, 2, 30, 2);
		Momentum = ROC(C,PSPer);
		RestorePriceArrays(); 
		StaticVarSet("MomoScore"  +  Symbol, Momentum); 
	} 
	// rank the stocks using this extremely useful function!
	StaticVarGenerateRanks("Rank", "MomoScore", 0, 1234); 
	StaticVarGenerateRanks("Top", "MomoScore", 15, 1234); 
}

Rank  = StaticVarGet ("RankMomoScore" +  Name()); 
TopRank  = StaticVarGet ("TopMomoScore" +  Name()); 

PSPer = Param ("Lookback for PositionScore",160, 2, 30, 2);
Momentum = ROC(C,PSPer);

// Exploration
//Avoid penny stock, and has at least two years worth of data and monthly traded vol at least 1M
Filter = (Rank >= 0) AND (BarCount >= PSPer) AND (C > 1.0) AND (V > 1000000);
AddColumn(Rank,"Rank",1.0);
AddColumn(Momentum,"Momentum Score",1.2);
AddColumn(TopRank,"Top Rank",1.2);
if( Status( "Action" ) == actionExplore ) SetSortColumns(3,1);
TopRank  = StaticVarGet ("TopMomoScore" +  Name()); 

This line is incorrect because this variable is generated by Numeric Rank starting from 1 and in top case will have positive integers going up. (For Bottom, its also +ve with Bottom prefix variable)

// Variables are something like this
TopMomoScore1, TopMomoScore2, TopMomoScore3 // … and so on...

More explanation for example with
TopMomoScore1
This is an Array corresponding to the Datetime at each Bar will hold the Index of the Symbol that was Rank 1 at that time.
Index of the symbol will be from your variable List which starts from 0 btw.

2 Likes

An update to my post bcos i mixed up in a hurry.
The string in which you can search the symbol will be
a substring in TopMomoScoreSymbols static variable instead of List user defined one.
It's a comma separated string of all the ranked symbols.

The index returned from Top...1 2 3 etc indexes this string

2 Likes

Thanks for your help with this @travick. So just to ensure I understand, basically for the top and bottom rank functions options you can't generate a list like you can with the normal rank version (i.e. 0 for the staticvargenerateranks function). Makes sense why its not working for me then. I will just need to think about how to frame the problem a little bit differently but shouldn't be a problem. Thanks again for you help!

Just give it a try with actual code and it will be very clear :slight_smile:
List = "S1,S2,S3,S4,S5";

Rank static Variables:
RankMomoScoreS1, RankMomoScoreS2, RankMomoScoreS3, RankMomoScoreS4, RankMomoScoreS5
Each variables has ranks for that specific Symbol for each bar.

After doing Generating Top Ranks:

// Generate top 2 Ranks
StaticVarGenerateRanks( "top", "MomoScore", 2, 1224 );
// topMomoScoreSymbols

This Static Var is specifically created and may have something like this:

tvss = StaticVarGet( "topMomoScoreSymbols" );
printf(" %s ", tvss );

//OUTPUT is a comma separated string which maybe like this
S3,S2,S5

This part is something to ponder over.
The order of this string has nothing to do with List it just means that over the entire period, only these three symbols got into the top two ranks, because earlier we defined Two in GenerateRanks.
It doesn't care about the omitted symbols.

Top Rank Variables

TopMomoScore1, TopMomoScore2

so for example

bc = BarCount -1;
getTop1 = TopMomoScore1[ bc ];

This will return either 0,1 or 2.
This is the index of the Symbol which was at Top Rank 1 for the last bar.
so if getTop1 is equal to 0, this means S3 from the topMomoScoreSymbols string "S3,S2,S5" was Top Rank 1 for the last bar.

You would extract Symbol name in the same way

sym = StrExtract( tvss, getTop1 );
2 Likes

Tried running this in Exploration as is and ran out of virtual memory...Im sure its a hardware issue. Running on an old HP 620. Disappointing...I was very interested in this one!

1 Like

I'm sorry! I have 16GB of RAM so I never noticed anything except that it takes time (2nd gen i7). Would be interesting to see how much memory this indeed takes to run and try and optimize it. This is pretty brute force and not too clever in terms of resource management.

Take care
Mike

To be fair, I stopped using this because eTables from IBD is pretty cheap, so now I just download their tables and convert their data to static vars with a scraper formula:

Using eTables with Amibroker

1 Like

Do you have an updated code for this?
Thanks!

Nope, it's the same as before!