Hi,
I'm trying to calculate RSCMansfield on daily and weekly from beppe code.
Calculations don't match when assigning a StaticVar variable.
Can someone help me?
Thank you,
Carlos
@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 );
///////// CHANGE Roc( Close, 10 ) FOR THIS:
Ind = "SPX-IDX"; // Index Sp500
RelS = RelStrength(Ind); // Calculate relative strength
values = 10*((RelS/MA(RelS,52))-1); // Calculate RSCMansfield --> 252 Daily - 52 Weekly
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
AddColumn( values, "RSC M", 1.4 );
AddColumn( rank, "Rank", 1 );
AddTextColumn( GicsID( 1 ), "Gics" );
AddTextColumn( GicsID( 0 ), "Gics ID" );
///////// NEW TO COMPARE StaticVar WITH A NORMAL VARIABLE
Ind1 = "SPX-IDX"; // Index Sp500
RelS1 = RelStrength(Ind1); // Calculate relative strength
values1 = 10*((RelS1/MA(RelS1,52))-1);// Calculate RSCMansfield --> 252 Daily - 52 Weekly
AddColumn( values1, "RSC M", 1.4 );
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 );
@carlosrodrgueznieto, I'm able to replicate the problem of some random values you are reporting.
It seems that you found an "issue" in Amibroker (or, as it probably happens, we are missing something that @Tomasz will explain to us regarding the use of RelStrength() in combination with SetForeign()).
In any case, to temporarily bypass it, you can try to use the following changes.
At the top of formula define your index:
Ind = "SPX-IDX"; // Index Sp500
Then, inside the conditional block if( Status( "stocknum" ) == 0 ), before the initial watchlist loop initialize a new StaticVar with the values of your index like:
SetForeign(ind);
StaticVarSet( "___INDEX_VALUES___", C );
RestorePriceArrays();
In the loop, replace your code RelS = RelStrength(Ind); with the following lines that will approximate the same calculation of the native AB indicator:
idxClose = StaticVarGet( "___INDEX_VALUES___" );
RelS = (C / idxClose) * 1000; // Calculate Relative Strengh on saved Index Close price
// do your own RSCMansfield stuff
At the end, outside of the loop, leave your verification code as it is now, using the native AmiBroker RelStrenght() function to compare the values and see if it works as expected.
P.S. I noticed that the "bad" values are somewhat random. Some lines report the correct values and some others do not. FWIW, adding to the top of the formula a #pragma maxthreads 1 changes the way the bad values are distributed.
I have already tried it and it's perfect.
I had been testing for several days and it didn't work for me.
Thank you very much @beppe.
Best regards,
Carlos
RelStrength should NOT be used with SetForeign. These two do not mix. Instead do the right thing and calculate relative strength YOURSELF using simple division Foreign("A","C")/Foreign("B", "C")
General note is to AVOID calling SetForeign if you don't really need it. This is heavy weight function. I really hate formulas that call SetForeign in a loop. Such codes are bad. It is infinitely better and faster to run NORMAL scan/exploration for symbols in a watch list without SetForeign at all. Batch window allows to sequence explorations and one exploration would produce ranking and second would utilize it. This way you would benefit from multithreading. SetForeign in a loop is bad, as it can't use multiple threads.
Future versions of AmIBroker will detect using of SetForeign in a loop and issue a warning about bad coding.
I suggest you to update the manual/help to point out such incompatibility:
RelStrength should NOT be used with SetForeign. These two do not mix.
The code for this 'derived' formula is the one posted above, and it includes the discouraged "SetForeign in a loop".
Maybe you should also put a note in the original KB article, ideally providing also a new detailed KB example explaining the current ideal approach for this kind of explorations (using the Batch functionality).
Personally, although I understand that for many users speed is a critical factor, for my explorations with few symbols and relatively few bars, I still prefer the old "one-pass" suboptimal approach.
Yes I know it is derived from KB and I did not like that code in KB from the beginning. It was bad from the start, it was written as a quickie just to answer some of repeated questions asked over support email. I did not know that this particular code would make such a "career". It was written way before Batch was available in AmiBroker.
As for RelStrength and SetForeign - this is complex threading / caching stuff. For this combination to work reliably you have to have EVERYTHING in in-memory (RAM) cache. No 3rd party plugin or file should be touched. The minimum requirement for that is that: a) in-memory cache is set in preferences to be large enough to hold all symbols under scan b) in-memory cache must be already prefilled with data (it means that particular symbol data must be accessed once already).