Different results for AFLs with PositionScore Vs StaticVarGenerateRanks

Hello,
I have two formulae that use the same logic to score and rank securities in the watchlist.
One uses PositionScore and another one uses StaticVarGenerateRanks.
I finally want to use the formula with StaticVarGenerateRanks as that is the one that allows using Internet* functions on Buy Sell signals. But the problem is its backtest results are very different than the one using PositionScore.
Definitely, there is some gap in the logic of these two formulae, can anyone help me find out why the results are so different?
The logic to rank securities is very simple, I am just taking the difference of fast-moving average and VWAP and then buy top N securities and short bottom N securities. Exit position when security falls below or rise-above a threshold rank. I am testing this on 15-minute data of last 5 years.

Here is the formula using PositionScore

#include <vwap.afl>
EnableRotationalTrading(); 

fastPeriod = 4;
rank = 8;
maxPositions = 4;

SetOption("worstrankheld", rank); 
SetOption("Maxopenpositions", maxPositions); 
SetOption("allowpositionshrinking", True);

PositionSize = -(100/maxPositions); 

// Difference of fast MA and VWAP
PositionScore = MA( Close, fastPeriod ) - vwapfunc( O, H, L, C, V );

Plot(PositionScore,"PositionScore",colorBlack,styleCandle);

Backtest Results:
UsingPositionScore

Below is the formula using StaticVarGenerateRanks:

#include <vwap.afl>
List = CategoryGetSymbols( categoryWatchlist, 0 );

maxPositions = 4;
// Mimic Worst rank held
exitRank = 8;
fast = 4;

SetOption("MaxOpenPositions", maxPositions);
SetOption("allowpositionshrinking", True);
SetPositionSize( (100/maxPositions) , spsPercentOfEquity );

total = 0;
if ( Status("stocknum") == 0 ) // GENERATE RANKING WHEN WE ARE ON VERY FIRST SYMBOL
{
    // delete static variables
    StaticVarRemove("ValuesToSort*");
    StaticVarRemove( "rank*" );

    for ( n = 0; ( symbol = StrExtract( List, n ) )  != "";  n++    )
    {
        total = total + 1;
        SetForeign ( symbol );
		
		values = MA( Close, fast ) - vwapfunc( O, H, L, C, V );;
		
        RestorePriceArrays();
        StaticVarSet( "ValuesToSort" + symbol, values );
    }
	// perform ranking
	StaticVarGenerateRanks( "rank", "ValuesToSort", 0, 1234 ); // normal rank mode    
}
sym = Name();
rank = StaticVarGet("rankValuesToSort" + sym);

Plot(rank, "rank", colorWhite, styleCandle);
fastMa = MA( Close, fast ); 
slow = vwapfunc( O, H, L, C, V );
// Place orders
thresholdRank = maxPositions/2;
// Enter short when rank is below threshold
Short  = fastMa-slow < 0 AND rank <= thresholdRank;

// Exit short when rank is above threshold+exitrank
Cover  = rank > thresholdRank + exitRank;

// Enter Long when rank is in top thresholdRank rank
Buy =  fastMa-slow > 0 AND rank >= (total - thresholdRank);

// Exit Long when rank falls below top (thresholdRank + exitRank)
Sell =  rank < thresholdRank + exitRank;

Here is the backtest result of above formula
UsingRankingMethod

So to summarize my question: What should I change in my second formula so that its backtest results are the same as that of the first formula?
Appreciate any help on this.
Regards.

Are you using data padding? If not, you should.

1 Like

Thanks for pointing it out. I have it turned on after reading the same suggestion on another topic in the forum. I had doubts if I should try turning it off but after your message its clear that its needed :slight_smile:

Now as for my own question, I got a solution for calling Alert / Internet* functions in the first AFL itself, from this AFL listed in the member area. So no more need to look at 2nd AFL using StaticVarGenerateRanks.

Replaced EnableRotationalTrading() with SetBacktestMode( backtestRegular ) and it allows to use Buy Sell signals.

Here is my updated code:

#include <vwap.afl>
SetBacktestMode( backtestRegular );

fastPeriod = Optimize("fast", 7, 3, 8, 1);
rank = Optimize("rank", 8, 5, 9, 1);
maxPositions = Optimize("pos", 4, 3, 6, 1);

// Fast moving average
f = MA( Close, fastPeriod );

SetOption("worstrankheld", rank); 
SetOption("Maxopenpositions", maxPositions); 
SetOption("allowpositionshrinking", True);

PositionSize = -(100/maxPositions); 

PositionScore = f - vwapfunc( O, H, L, C, V );

Plot(PositionScore,"PositionScore",colorGold,styleCandle);

Buy = Cover = Cross(f, vwapfunc( O, H, L, C, V ));
Sell = Short = Cross(vwapfunc( O, H, L, C, V ), f);

// remove excess buy/sell triggers
Buy = ExRem(Buy,Sell); 
Sell = ExRem(Sell,Buy);
Short = ExRem(Short,Cover); 
Cover = ExRem(Cover,Short);

sym =Name();
AlertIF( Cover, "", "Simple text alert cover" + sym  + " datetime:" + DateTime() , 4 );
AlertIF( Buy, "", "Simple text alert buy" + sym + " datetime:" + DateTime(), 4 );
AlertIF( Sell, "", "Simple text alert sell" + sym + " datetime:" + DateTime(), 4 );
AlertIF( Short, "", "Simple text alert short" + sym + " datetime:" + DateTime(), 4 );

The backtest results remain almost the same.

Regards,

Posted workaround here : How to act on actual entry exit signals instead of coded signals? - #7 by mbtrender

This topic was automatically closed 100 days after the last reply. New replies are no longer allowed.