StaticVarAdd: unable to get correct count of stocks

Hi, i am unable to get the correct count of stocks, where i'm i going wrong. Attached apx file at the end, just in case.

// @https://forum.amibroker.com/t/create-my-own-composite/9372/2 
// @https://forum.amibroker.com/t/correct-use-of-staticvaradd/29697 
// Initialize local variables (optional, for clarity)
Closeabv20ma = 0;
SymbolCount = 0;

// Get watchlist name (currently unused, kept for completeness)
List = CategoryGetName(categoryWatchlist, 2);

// Reset static variables only for the first stock
if( Status("stocknum") == 0 )
{
    StaticVarRemove("~Closeabv20ma");
    StaticVarRemove("~SymbolCount");
}

// Accumulate counts across all stocks
StaticVarAdd("~Closeabv20ma", Close > MA(Close, 20));
StaticVarAdd("~SymbolCount", C != 0);

// Retrieve the accumulated values
Closeabv20ma = StaticVarGet("~Closeabv20ma");
SymbolCount = StaticVarGet("~SymbolCount");

// Calculate the composite percentage
CompositePcClose = Nz(Closeabv20ma / (SymbolCount ) * 100);

// Exploration
Filter = Status("stocknum") == 0;
SetOption("NoDefaultColumns", True);
AddColumn(DateTime(), "Date", formatDateTime);
AddColumn(Closeabv20ma, "Stocks Above 20MA", 1.0);
AddColumn(SymbolCount, "Total Stocks", 1.0);
AddColumn(CompositePcClose, "Percent Above 20MA", 1.2);

Analysis1.apx (8.6 KB)

here

AFL will run only for 1st symbol, you have to run on whole watchlist.

OR

the slow performance way, is to keep the Filter and iterate through your WL which is currently unused and "kept for completeness"

Changed the filter to 1; seeing below output, why are there so many rows for one date? What should be the filter value.

Not very proficient at coding, apologies if asking a very basic query

yes, it is very basic and also you need to understand how AFL runs.
use chatgpt or gemini, they will answer it for you.

if you are using Filter = Status("stocknum") == 0;
There is no point using it, might as well keep only one symbol for AA Explore and use For loop with Foreign() like shown in below manual link.

AFL Function Reference - STATICVARGENERATERANKS
then see how you access all symbols through a loop

OR

I risk getting trolled as it is not conventional, but you could use the multithread Exploration and Filter at the end for the last symbol,
your problem is you are filtering the first symbol instead of the last one

// your code

// use correct WL number to get correct count
WL_count = StrCount( CategoryGetSymbols( categoryWatchlist, 15 ), ",");

// ensure the last thread to complete is the last symbol
if ( Status("stocknum") == WL_count ) {
ThreadSleep( 50 );
}

Filter = Status("stocknum") == WL_count; // at the end

Overall, one can write better code instead of hacking your way through, but you need to understand AFL and profile your code.
Some functions like StaticVarAdd() use a critical section, so your code profile will tell you whether the first option is better or 2nd. Usually, if you have more thread intensive computation, the 2nd would be better.

@erukumk the example you linked demonstrates the correct approach.

htps://forum.amibroker.com/t/correct-use-of-staticvaradd/29697

As was alredy discussed multiple times in the forum, you should use the #pragma sequence(scan, explore) directive to run a scan followed by an exploration.

Below is a revised version of your original code. It works correctly only if your watchlist contains symbols that were actively traded during the entire date range of your analysis.
It uses your condition C != 0 to filter out stocks that are no longer/no yet trading, preventing them from being included in the count. (Note: if your watchlist doesn’t include all tickers that were active at the beginning of the selected date range, older dates will report fewer stocks than recent ones.)

Alternatively, if you're using Norgate data, I’ve left two commented lines in the code.
You can uncomment them when working with a "Current & Past" type watchlist. In that case, make sure to:

  • Enable Pad & Align with a symbol that has a full price history (e.g., &SPX)
  • Replace the index symbol (e.g., $NDX) with the one that corresponds to your selected watchlist ($SPX, $RUA, etc.)

Here’s the AFL code:

#pragma sequence(scan,explore)
  
Version( 6.40 );

//#include_once "Formulas\Norgate Data\Norgate Data Functions.afl"

if( Status( "action" ) == actionScan )
{

    if( status( "stocknum" ) == 0 )
    {
        // remove any previous composite values
        StaticVarRemove( "~Closeabv20ma" );
        StaticVarRemove( "~SymbolCount" );
    }

    isValid = C != 0; // Check only stocks with a valid price 
    // isValid = NorgateIndexConstituentTimeSeries( "$NDX" ); // Change the constituency symbol in this line to refer to the used watchlist ($NDX, $SPX, $RUA, etc.)

    StaticVarAdd( "~CloseAbv20ma", iif( isValid, Close > MA( Close, 20 ), 0 ) );
    StaticVarAdd( "~SymbolCount", isValid );
    _exit(); // quick exit in scan mode
}

Filter = 1;

// Retrieve the accumulated values
Closeabv20ma = StaticVarGet( "~Closeabv20ma" );
SymbolCount = StaticVarGet( "~SymbolCount" );
// Calculate the composite percentage
CompositePcClose = Nz( Closeabv20ma / ( SymbolCount ) * 100 );

// Exploration
Filter = Status( "stocknum" ) == 0; // without P&A will work properly only if the first stock in the WL is still traded!
SetOption( "NoDefaultColumns", True );
AddColumn( DateTime(), "Date", formatDateTime );
AddColumn( Closeabv20ma, "Stocks Above 20MA", 1 );
AddColumn( SymbolCount, "Total Stocks", 1 );
// AddColumn( CompositePcClose, "Percent Above 20MA", 1.2 );
AddColumn( CompositePcClose, "%", 1.2,  colorWhite, colorLightBlue, 200, CompositePcClose );

if( Status( "action" ) == actionExplore )
    SetSortColumns( -1 );
3 Likes

@beppe
This is what I meant in my previous post :slight_smile:

The second pragma sequence, ie Explore, will spawn thread for each symbol, and there is no need for it as scan has already passed over all symbols.

if you want to check, just add this after Filter =1; line.

Filter = 1;
ThreadSleep( 500 ); // Explore will slow down significantly, testing.

To optimize your code, you just need the first thread, like this:

Filter = 1;

if ( Status( "stocknum" ) != 0 )
{
	Filter = 0;
	_exit();
}

ThreadSleep( 500 );  // just for testing
// no effect as all threads > 1 exited above

That is why, instead of 2 #pragma sequence passes over AFL and all symbols, i would rather delay only the last thread and run a single pass,
ie. Explore and get the result using multi-thread and not Foreign() in first stocknum=0.
Test with 50-100 symbols to see the lag.