Generate relative strength rank for previous quarter

I have a working code to get annual relative strength ranking, i want to generate similar ranking for quarterly time frame.

When i explore, rank for quarterly column is blank.
Below is the code:

// https://forum.amibroker.com/t/how-to-imitate-ibd-relative-strength-percentile-ranking-of-stocks/6068/15
// Relative Strength ranking of stocks similar to Marketsmith India
// watchlist should contain all symbols included in the test

GetOption("PadAndAlignToReference");
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( "QRS_*" );

	// 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); // Restoring SetForeign as without it, all stocks are getting an RS of 100.
		RSraw = 0;
		QRSraw = 0;
		// relative strength IBD style
		ThreeMthRS  = 2*(C /Ref(C,-63));
		SixMthRS    = (C /Ref(C,-126));
		NineMthRS   = (C /Ref(C,-189));
		TwelveMthRS = (C /Ref(C,-252));
		RSraw = ThreeMthRS + SixMthRS + NineMthRS + TwelveMthRS;
		QRSraw =  ThreeMthRS/2;
		
		// Making changes to show RS performance of last 3 months only
		//ThreeMthRS  = (C /Ref(C,-63));
		//RSraw = ThreeMthRS ; 
		RestorePriceArrays(); 
		StaticVarSet("RSraw_"  +  Symbol, RSraw);
		StaticVarSet("QRSraw_"  +  Symbol, QRSraw);  
	} 

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

for( n = 0; ( Symbol = StrExtract( List, n ) )  != "";  n++ )
{
    Rank  = StaticVarGet( "RankRSraw_" +  Symbol );
    RSpctile = 100 - 100 * Rank / ListQty;
    StaticVarSet( "RS_" + Symbol, RSpctile, True );
    
    QRank  = StaticVarGet( "RankQRSraw_" +  Symbol );
	QRSpctile = 100 - 100 * QRank / ListQty;
	StaticVarSet( "QRS_" + Symbol, QRSpctile, True );
    // use this opportunity to store the highest ranking stocks in a watchlist.
    //if( LastValue( RSpctile ) >= 60 )
    //    CategoryAddSymbol( Symbol, categoryWatchlist, 35 );
    //else
    //    CategoryRemoveSymbol( Symbol, categoryWatchlist, 35 );
}

}

Filter = 1;
Rank  = StaticVarGet ("RS_" +  Name()); 
QRank  = StaticVarGet ("QRS_" +  Name()); 

// exploration code for verification
//AddColumn(Close, "Close", 1.2);
//AddColumn(Volume, "Volume", 1.0);
AddColumn(Rank, "Rank",1);
AddColumn(QRank, "QRank",1);
SetSortColumns(-3);
StaticVarRemove("RSraw_*");
StaticVarRemove("RankRSraw*_");
StaticVarRemove("QRSraw_*");
StaticVarRemove("QRankRSraw*_");  

For this purpose do not hard code time periods like this:

Instead approach in this manner:

bi = BarIndex();
monthMultList = "3,6,9,12"; // a comma separeted list of month multipliers
noOfMonthMults = StrCount( monthMultList, "," );

Filter = 1;

for( i = 0; i <= noOfMonthMults; ++i ) {
	monthMult = StrExtract( monthMultList, i );
	
	tf = inMonthly * StrToNum( monthMult );
	
	firstBi = TimeFrameExpand(
		TimeFrameCompress( bi, tf, compressOpen ),
		tf,
		expandFirst
	);
	firstC = ValueWhen( bi == firstBi, C );
	
	lastBi = TimeFrameExpand(
		TimeFrameCompress( bi, tf, compressLast ),
		tf,
		expandFirst
	);
	lastC = ValueWhen( bi == lastBi, C );
	
	rs = lastC / firstC;

	AddColumn( rs, "RS" + monthMult, 1.2 );
}
1 Like

Thank you!!, your code is very elegant and is faster than my code. I tried modifying your code for my need but i could not.

I want to add:

  • Percentile ranking so that greatest value is 100 or 99
  • Add static variable to save ranks of at least 1 month and 12 months, i use static variable to display data in my chart title.

Apologies for asking, the complexity of the challenge is beyond my skill level.

It would've been nicer if you would've continued the original thread:


Please do not apologise, does not suit here!

Developing skill is not a hard thing to achieve, provided, one patiently intends to understand.

Instead of copy-pasting, if you would've studied, by now you would know what to do.

Still would be happy to help, if you show some of your "own" authentic efforts!

1 Like

Sorry, but no, supplied code using TimeFrameExpand/TimeFrameCompress functions is NOT better. It is just way too resource heavy. Ref() or ValueWhen is way more efficient.

What if Ref( arr, -252 ) is not "precisely" 12 months prior?

First of all Ref supports variable periods. Besides there is a ValueWhen then you can use to calculate rate of change since month or quarter boundary

Quarter = floor( ( Month() - 1 ) / 3 ); // gives quarter 0..3

LastBarOfQuarter = Quarter != Ref( Quarter, 1 );
FirstBarOfQuarter = Quarter != Ref( Quarter, -1 );

ChangeSinceQuaterEnd = Close - ValueWhen( LastBarOfQuarter, Close );

This is all without doing any TimeFrame calls. Ref() and ValueWhen are much faster.

Also, if you really must use timeframe funcitons is them wisely. Instead of calling them zillions of times, call them ONCE.

Just get QUARTERLY data ONCE.

QuarterlyClose = TimeFrameCompress( Close, inQuarterly );

OneQuarterBack = Ref( QuarterlyClose, -1 );
TwoQuartersBack = Ref( QuarterlyClose, -2 );
ThreeQuartersBack = Ref( QuarterlyClose, -3 );`

// later do all calculations within Quaterly timeframe 
// and only TimeFrameExpand when needed AT THE END

One call to TimeFrameCompress.

2 Likes

We are in Q2 this year, as today is 20-Jun-23, tomorrow would be 21-Jun-23, so on...

For all dates of this Q2, started on 01-Apr-23 till 31-Jun-23, the prior quarter's (Q1) start date would remain constant at 01-Jan-23 and prior quarter's (Q1) end date would remain constant at 31-Mar-23.

Using Ref(), how to capture prior Quarter's both Start bar and End bar?

TimeFrameExpand/TimeFrameCompress must not be used at all.

Thanks

I gave you the code already in previous reply. ValueWhen is the function you should use if you want "date to remain constant" for entire quarter. And it does not require any TimeFrame calls.