Passing a metrics to StaticVarGenerateRanks

Hi I am having a problem with passing my metrics to StaticVarGenerateRanks.

Part of my code (my metrics, is just n example to shown that I need a loop to calculate it):

//This sets the Static Metrics
for (y=ymin; y<=ymax; y++) 
 { 	StaticVarSet( "Rocs"+Name() + y, IIf( var == y, Rocs, 0 ),persistent =True );// ROC at each  variable Var
	count= Cum(IIF(var ==y AND NumOfBars>0, 1,0)) ;// Number of occurrency per each case
	StaticVarSet("Count"+Name()+y, count,persistent =True);
	for( i = 1; i < BarCount; i++ ) 
	{ if (var[i]==y AND NumOfBars[i]>=1+lag) 
		eq[i]=eq[i-1] *(1+Rocs[i]/100);
		else
		eq[i]= eq[i-1]; 
	}
	StaticVarSet("EQ"+Name()+y, eq,persistent =True);
	geteq = StaticVarGet( "EQ"+Name()+y);
	FValueEQ= LastValue(ValueWhen (fbr==1,geteq,1));
	LValueEQ= geteq;
	getcount=StaticVarGet("Count"+Name()+y);
	addval=1e-20; // 
	CAR=((LValueEQ/FValueEQ)^(1/(getcount/12+addval))-1)*100;//*100;
	StaticVarSet( "CAR"+Name()+y, CAR,persistent =True);
	GETCAR=StaticVarGet( "CAR"+Name()+y);
	for (i=1; i<BarCount; i++) 
	{ if(refvar[i]==y) 
		{ AGGCAR[i]=  GETCAR[i];
		  StaticVarSet( "AGGCAR"+Name(), AGGCAR,persistent =True);
		}
	}
}

generates AGGCAR and stores it in a Static Variable which should then be used with StaticVarGenerateRanks.

My problem is that AGGCAR is not passes to StaticVarGenerateRanks because StaticVarGenerateRanks is computed if ( Status( "stocknum" ) == 0 )

When I run explorer (see attached file) AGGCAR is calculated correctly (AGGCAR column) but not passed to the StaticVarGenerateRanks calculation (RankMet column) . If I run the exploration twice I can see that (RankMet column) gets the right value from AGGCAR.
Rotational_with_rank_light_v1.afl (14.3 KB)

I guess the problem lays with the order of execution, basically I suspect that AGGCAR is calculated at each bar for each ticker, while StaticVarGenerateRanks (RankMet column) is generated when Status( "stocknum" ) == 0 ) only, therefore it misses all the AGGCAR because at Status( "stocknum" ) == 0 ) RankMet is not know for all tickers > 0.

I ve been trying to solve this issue for a while and cannot find a solution.

Any help would be very welcome.

JB

the whole code

//==========================================================================
// Source: Amibroker forum, from various sorces and my own contribution
//==========================================================================
// Settings and Options
SetOption("ExtraColumnsLocation",2);
// Watchlist // Backtester Settings //
wlnumber  = GetOption( "FilterIncludeWatchlist" );
watchlist = GetCategorySymbols( categoryWatchlist, wlnumber );
NumTickers = StrCount( watchlist, "," ) + 1;
SetOption( "MaxOpenPositions", NumTickers );
SetOption( "CommissionMode", 1 ); // 1 = PeTickerRocent 0 = Dollars
SetOption("InitialEquity", 10000 ); // Initial Portfolio Equity
SetTradeDelays(0,0,0,0);
SetOption("AllowSameBarExit", True ); 
//=======================================================
lag=1;
Index= Foreign( ".SPX","C");
Var=IIf (Index>MA(C,12),1,0);

_SECTION_BEGIN("Action Type and Time Series");
ExploreFilter = ParamToggle("Explore","Last_Bar|All_Bars",1);
if ( ExploreFilter ) Filter = 1;
else Filter = Status( "lastbarinrange" );
// Run on all times or on First Visible Bar FVB
TimeSeriesSel=ParamList("Time Series From","All Series|First Visible Bar",0);
//DebugToggle=ParamToggle("Debug","NO|YES",1);
//HideToggle= ParamToggle("Hide","NO|YES",0);
_SECTION_END();
//=======================================================
//
_SECTION_BEGIN("Positions");
// Number of max open positions

PosQtyOptTog= ParamList( "PosQtyOpt:","NO|YES", Defaultval=0); 
if (PosQtyOptTog=="NO")
PosQty = Param( "Number of Positions", 3, 1, NumTickers, 1) ;
if (PosQtyOptTog=="YES")
{ 	PosQtyMin = Param("PosQtyMin",1, 1, NumTickers, 1);
	PosQtyMax = Param("PosQtyMax",NumTickers, 1, NumTickers, 1);
	PosQtyOpt = Optimize( "Pos Qty Optimaser", 1, PosQtyMin, PosQtyMax, 1); 
	PosQty = PosQtyOpt;
}
URankNum = 1; // rank has to be be >URankNum
LRankNum= PosQty;  // rank has to be be <=LRankNum 
ExcTopRankTog= ParamList( "Exc Top Rank Optimiser:","NO|YES", Defaultval=0); 
if (ExcTopRankTog=="YES")
{ 	ExcMin = Param("ExcTopPosMin",0, 1, NumTickers, 1);
	ExcMax = Param("ExcTopPosMax",5, 1, NumTickers, 1);
	ExcMaxRank = Optimize( "ExcPosQtyOpt", 1, ExcMin, ExcMax, 1); 
	URankNum = ExcMaxRank +1; // rank has to be be >=URankNum
	LRankNum= URankNum + PosQty -1;
}  // rank has to be be <= LRankNum 
InEquity=Param("Initial Equity",1000,1,100,1000,10); 
MinBars= Param("Min Bars",1,1,100,1);
CommPct=Param("% Commission", 0,0,5,0.01); // This set the value for the commissions
SetOption( "CommissionAmount", CommPct);
SetOption("HoldMinBars", MinBars );
_SECTION_END();
//=======================================================
_SECTION_BEGIN("In/Out market and cash");
// Choose if you want an In/Out Market filter 
InOutTog=ParamToggle( "In/Out Market","NO|YES",Defaultval=1);
// If In/Out Market filter is (NO)
if( InOutTog==0) 
{	InOut= 1; // This means that the Market Filter is always OFF 
	SPX=999; SPXMA=888; 
} 
// If In/Out Market filter is (YES)
if(InOutTog==1)// Filter ON
{ // Select a Ticker if In/Out Market filter is (YES)
	MarInd= ParamList( "In/Out Market Ticker:",".SPX|Name()",Defaultval=0);
	SPX=Foreign(MarInd,"C");
	/// Choseee if Optimaze or Not the MA for the Ticker of In/Out Market filter
	MarIndMAOptTog=ParamToggle( "In/Out MA Optimizer:","NO|YES",Defaultval=0);
	// If Optimaze MA is (NO) the select teh MA Periods Manually
	if(MarIndMAOptTog==0)
	{ 	SPXMAPDS= Param("In/out MA periods",12, 1, 100, 1);
		SPXMA=MA(SPX,SpxMAPds);
		InOut= SPX>=SPXMA ;
	}
	// If Optimaze MA is (YES) this optimaze the MA Periods
	if(MarIndMAOptTog==1)
	{ // If Optimaze MA is (YES) this selects Min and Max MA Periods
		SpxMaMin= Param("MA Min Pds",6, 1, 100, 1); //choose Min
		SpxMaMax= Param("MA Max Pds",18, 1, 500, 1); //choose Max
		SpxMaSte= Param("MA Ste Pds",1, 1, 100, 1); //choose Steps
		SPXMAPds= Optimize("MA Pds",1,SpxMaMin, SpxMaMax,SpxMaSte); // optimizer
		SPXMA=MA(SPX,SpxMAPds);
		InOut= SPX>=SPXMA; 
	}
}
TickerMaTog=ParamToggle( "Ticker Moving Average","NO|YES",Defaultval=0);
if(TickerMaTog==0) 
TickerMa=C;  UpDown = 1;
if(TickerMaTog==1)
{ TickerMaOptTog=ParamToggle( "Opt Ticker Moving Av.","NO|YES",Defaultval=0);
	if(TickerMaOptTog==0) 
	{ 	TickerMaPds= Param("Ticker Ma",12, 1, 100, 1);//Choose Moving Average
		TickerMa=MA(C,TickerMaPds); 
	} 
	if(TickerMaOptTog==1)
	{ 	TickerMaMin= Param("Min Ticker Ma",1, 1, 100, 1); //choose Min
		TickerMaMax= Param("Max Ticker Ma",18, 1, 500, 1); //choose Max
		TickerMaSte= Param("Step Ticker Ma",1, 1, 100, 1); //choose Steps
		TickerMaPds= Optimize( "Ticker Ma Pds", 1, TickerMaMin, TickerMaMax, TickerMaSte); //Optimize Moving Average
		TickerMa=MA(C,TickerMaPds); 
	}
}
CashTR = ParamList( "Cash Ticker:","$CASHTR|.SPX|#IND",Defaultval=0); // Ticker for cash
_SECTION_END();
//=======================================================
_SECTION_BEGIN("System Optimizer and Reports");
// This code sets if the system optimazer is OFF or ON (default=OFF)
OptTog= Paramtoggle("System Optimizer:", "OFF|ON",Defaultval=0);
if( OptTog==1 )
{ OptimizerSetEngine("cmae");  OptimizerSetOption("MaxEval", 10000000 );  }
// Report to be switched ON because of portfolio report 
Report= ParamToggle("Report:","OFF|ON",DefaultVal=1);// 0 suppresses report; 1 foTickerRoces generation of full report;
SetOption("GenerateReport", report);
// This code sets if the type of report
ReportModetog=ParamList( "Report Mode:","Trade list|Detailed log|Summary|No output", Defaultval=2); 
if(ReportModeTog=="Trade list") ReportMode= 0; if(ReportModeTog=="Detailed log") ReportMode= 1;
if(ReportModeTog=="Summary") ReportMode= 2; if(ReportModeTog=="No output") ReportMode= 3;
SetOption("PortfolioReportMode",ReportMode);
_SECTION_END();
//=========================================================
// ALL METRICS HERE
//=========================================================
eq=InEquity; // this comes from above settings
Rocs = ROC( C, 1 );
RefVar = Ref(Var,-lag); 
YMin= 0;//LastValue( Lowest(Var) ); // Minimum for IND/Y/M/Other
YMax= 1;// LastValue( Highest(Var) ); // Maximum for IND/Y/M/Other
Car =InCount = PosSize = AGGCAR=0;
BI=BarIndex();
fbr = IIf(Status( "FirstBarInRange" ),1,Null) ; 
lbr = IIf(Status( "LastBarInRange" ),1,Null) ;
NumOfBars= BI-ValueWhen (fbr==1,BI,1);
BuyPrice=Close; SellPrice=Close;
// Remove StaticVar at first symbol
if ( Status( "stocknum" ) == 0 )  
{StaticVarRemove( "Rocs*"  );  
StaticVarRemove( "Count*"  ); StaticVarRemove( "EQ*"  ); 
}
//This sets the Static Metrics
for (y=ymin; y<=ymax; y++) if(y!=0)
 { 	StaticVarSet( "Rocs"+Name() + y, IIf( var == y, Rocs, 0 ),persistent =True );// ROC at each  variable Var
	count= Cum(IIF(var ==y AND NumOfBars>0, 1,0)) ;// Number of occurrency per each case
	StaticVarSet("Count"+Name()+y, count,persistent =True);
	for( i = 1; i < BarCount; i++ ) 
	{ if (var[i]==y AND NumOfBars[i]>=1+lag) 
		eq[i]=eq[i-1] *(1+Rocs[i]/100);
		else
		eq[i]= eq[i-1]; 
	}
	StaticVarSet("EQ"+Name()+y, eq,persistent =True);
	geteq = StaticVarGet( "EQ"+Name()+y);
	FValueEQ= LastValue(ValueWhen (fbr==1,geteq,1));
	LValueEQ= geteq;
	getcount=StaticVarGet("Count"+Name()+y);
	addval=1e-20; // 
	CAR=((LValueEQ/FValueEQ)^(1/(getcount/12+addval))-1)*100;//*100;
	StaticVarSet( "CAR"+Name()+y, CAR,persistent =True);
	GETCAR=StaticVarGet( "CAR"+Name()+y);
	for (i=1; i<BarCount; i++) 
	{ if(refvar[i]==y) 
		{ AGGCAR[i]=  GETCAR[i];
		  StaticVarSet( "AGGCAR"+Name(), AGGCAR,persistent =True);
		}
	}
}
//=========================================================
// Scenarios Metrics END
//=========================================================
// --- metrics ranking routine ---
if ( Status( "stocknum" ) == 0 )
{   
StaticVarRemove( "RankMet*"); StaticVarRemove( "ActMet*");
StaticVarRemove( "RankRank*"); StaticVarRemove( "PosSize*"); 
StaticVarRemove( "UpDown*"); 
//This sets the Static Metrics
    for ( i = 0; ( symbol = StrExtract( watchlist, i ) )  != "";  i++ )
    {   SetForeign( symbol );
		////==============================================
        //// Metrics for ticker ranking values STARTS HERE
        UpDown = 1; //       
		CARG=StaticVarGet( "AGGCAR"+Symbol);
		RankMet = CARG ;// Actual value used for the ranking !!
        ////==============================================
        //// Metrics for ticker ranking values ENDS HERE
        if ( symbol == CashTR ) 
        {RankMet = -999;} // force lowest rank for cash ticker
        if(TickerMaTog==1) 
        UpDown = C>= MA( C, TickerMaPds );
        ActMet= RankMet; // This is the Actual Metrics before * UpDown
        // Used for Ranking, If UpDown=0 the RankMet=0 
        RestorePriceArrays();
        StaticVarSet( "RankMet"  + symbol , RankMet,persistent =True);
        StaticVarSet( "ActMet" + symbol , ActMet ,persistent =True);
        StaticVarSet( "UpDown" + symbol , UpDown,persistent =True );
    }
    // This generates rank for "RankMet" the actual Metrics used for the Ranking
    StaticVarGenerateRanks( "Rank", "RankMet", 0, 1224 );  
    //This gets the Static Metrics Variables
    for ( i = 0; ( symbol = StrExtract( watchlist, i ) )  != "";  i++ )
    {   if(TickerMaTog==1) 
		{UpDown = StaticVarGet( "UpDown" + symbol); 
        RankNum = StaticVarGet( "RankRankMet" + symbol  ) ; 
        // n. top ranked symbols above their MA filter
        InCount = IIf( RankNum <= PosQty AND UpDown==1, InCount + 1, InCount );}         
        if(TickerMaTog==0) 
		{ RankNum = StaticVarGet( "RankRankMet" + symbol ); 
        // n. top ranked symbols above their MA filter
        InCount = IIf( RankNum <= PosQty AND UpDown==1, InCount + 1, InCount );}
    }
    //This sets the PosSize
    for ( i = 0; ( symbol = StrExtract( watchlist, i ) )  != "";  i++ )
    {   RankNum = StaticVarGet( "RankRankMet" + symbol );
        UpDown    = StaticVarGet( "UpDown"    + symbol);
        if(symbol==CashTR) // this apply to cash
        { PosSize= IIF(INOUT==0,100,(PosQty-InCount) * 100 / PosQty);
        }
        else // this apply to any other symbol
        {  PosSize  = IIf( RankNum <= LRankNum  AND RankNum >= URankNum AND UpDown==1 AND INOUT==1, 100 / PosQty, 0 );
        }  
        StaticVarSet( "PosSize" + symbol, PosSize ,persistent =True);   
    }
}
// --- get values and ranks for Momentum ---
symbol   =  Name();
RankMet = StaticVarGet( "RankMet" + symbol );
ActMet = StaticVarGet( "ActMet" + symbol );
RankNum  = StaticVarGet( "RankRankMet" + symbol );
PosSize = StaticVarGet( "PosSize"  + symbol );
UpDown   = StaticVarGet( "UpDown"    + symbol );
// --- re-balance at the end/close of every month ---
//M=Month(); LDM= M!=Ref(M,1); // This is the last day of the month 
Buy = PosSize > 0 ;
Sell = 1;
// --- set position sizes ---
PositionSize = -PosSize; // "-" sign for spsPercentOfEquity
// --- set stop in number of bars ---

//=========================================================
// Exploration //
//=========================================================
if ( Status( "actionex" ) == actionExplore )
{   
SetSortColumns( 2,3);
    // --- columns for exploration ---
    SetOption( "NoDefaultColumns", True ); 
    AddTextColumn( Name()   , "Ticker" );
	AddColumn( DateTime(), "Date", formatDateTime );
    ColorRank = IIf( RankNum <= PosQty AND PosSize, ColorGold, colorWhite );
    ColorPos  = IIf( PosSize > 0, IIf( Name() == CashTR, colorYellow, colorGold ), colorWhite );
    ColorInOut= IIF( InOut ==0, colorYellow, colorWhite);
    ColorUpDown= IIF( UpDown ==0, coloryellow, colorWhite);
    ColorInCount=IIF( InCount >0, ColorGold, colorWhite);
    AddColumn( RankNum , "RankNum"     , 1.0, 1, ColorRank );
    AddColumn( RankMet   , "RankMet"  , 3.3 );
    AddColumn( ActMet   , "ActMet"  , 3.3 );
    AddColumn( PosSize, "PosSize(%)", 3.3, 1, ColorPos  );
    AddColumn( InCount, "InCount", 3.0, 1, ColorInCount );
    AddColumn( C , "Close"   , 1.2);
	AddColumn( TickerMa , " Ticker MA"   , 1.2);
    AddColumn( UpDown  , "UpDown"   , 1.0,1 ,ColorUpDown);  
	AddColumn( InOut  , "InOut"   , 1.0,1, ColorInOut);
	AddTextColumn( MarInd  , "MarInd");
	AddColumn( SPX  , "SPX"   , 1.2);
	AddColumn( SPXMA  , "SPXMA"   , 1.2);
	AddColumn( NumOfBars, "Bars",1);
	AddColumn(AGGCAR,"AGGCAR",1.3);	
for (y=ymin; y<=ymax; y++) 
		{ gettroc = StaticVarGet( "Rocs"+Name()+y);
		  geteq= StaticVarGet( "EQ"+Name()+y);
		  getcar= StaticVarGet( "CAR"+Name()+y);
		  getcount= StaticVarGet( "count"+Name()+y);
			AddColumn(IIf(Var==y,getTRoc,Null), "Roc_"+y ,1.2);
			AddColumn(IIf(Var==y,geteq,Null) , "EQ_"+y ,1.2);
			AddColumn(IIf(Var==y,getcar,Null) , "CAR_"+y ,1.2);
			AddColumn(IIf(Var==y,getcount,Null) , "count_"+y ,1);
		}	
}
//=========================================================
// Backtesting  Stats
//=========================================================
function GetFormulaName()
{  return StrExtract( StrExtract( GetFormulaPath(), -1, '\\' ), -2, '.' ); } 
FileName=GetFormulaName();
SetCustomBacktestProc(""); // Now custom-backtest procedure follows 
if( Status("action") == actionPortfolio )
{ 
bo = GetBacktesterObject();
bo.Backtest(); // run default backtest procedure
st = bo.GetPerformanceStats(0); // get stats for all trades
// Here we add custom metric to backtest report
//Weigthed Reward/Risk Ratio
WRR = st.GetValue("WinnersAvgProfitPercent")*st.GetValue("WinnersPercent")/
abs(st.GetValue("LosersAvgLossPercent")*st.GetValue("LosersPercent"));
bo.AddCustomMetric( "W R/R ", WRR ); 
e = bo.EquityArray(); 
StaticVarset( "EQ", e, persistent = True );
ModelType=StaticVarGetText("Model");
AddToComposite( e, "~~EQ_"+FileName + ModelType, "X", atcFlagDefaults |atcFlagEnableInPortfolio); 
// Here we add custom metric at trade level
SumMAE = SumMFE= NumTrades = 0; 
   // iterate through closed trades first 
   for( trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade() ) 
   {  // here we sum up maximum adverse excursions
       SumMFE=  SumMFE + trade.GetMFE();
       SumMAE = SumMAE + trade.GetMAE();
       NumTrades++; 
   } 
   // iterate through eventually still open positions 
   for( trade = bo.GetFirstOpenPos(); trade; trade = bo.GetNextOpenPos() ) 
   { 
       SumMFE = SumMFE + trade.GetMFE();
       SumMAE = SumMAE + trade.GetMAE();
       NumTrades++; 
   } 
   averageMFE = SumMFE / NumTrades; 
   bo.AddCustomMetric( "Av.MFE   ", averageMFE );
   averageMAE = SumMAE / NumTrades;
   bo.AddCustomMetric( "Av.MAE   ", averageMAE );
   MFEMAE=abs(averageMFE)/abs(averageMAE);
   bo.AddCustomMetric( "MFE/MAE", MFEMAE );  
}
// --- end of code ---

1 Like

@jbenfeld, I don't have time to review all of your code in detail, but I think you are on the right track with your observation that the problem is tied to order of execution. One potential way to solve this problem would be to run a "preliminary exploration" that generates the AGGCAR values and stores them in Static Variables. At the conclusion of the preliminary exploration, do not remove your static vars.

Immediately after running the preliminary exploration, run the exploration or back test that performs the StaticVarGenerateRanks() logic inside the "if ( Status( "stocknum" ) == 0 )" block. All of your Static Vars should still be defined from the running of the preliminary exploration.

Thanks mradtke,
that is exactly the problem I am facing an stuck in.

I CAN"T run a "preliminary exploration" that generates the AGGCAR (I am not capable of, due to my limited coding skills)

I do NOT expect the solution from the forum, but if anyone has an hint, than I can wortk of it.
e.g. would #include work? because I have tried it and didn't for me.

@jbenfeld perhaps my terminology was confusing. When I said "preliminary exploration", I simply meant a regular exploration that you will need to run before you execute the code with the StaticVarGenerateRanks. So as a rough, untested outline, you could do something like this:

idExplore = Param("Explore ID", 1, 1, 2, 1);
...
if (idExplore == 1)
{
     // Your logic to generate AGGCAR and save static vars using StaticVarSet()
}
else if (idExplore == 2)
{
     // Your logic to use StaticVarGet() and call StaticVarGenerateRanks()
}

Run your code once with idExplore set to 1, and then run it again with idExplore set to 2. Alternatively you could create  two separate AFL files, and run them sequentially, perhaps even using an AmiBroker batch file if you wanted the sequential execution to be more automatic. There are lots of ways to solve the problem.

Thanks a lot this looks a really good starting point.
Thanks for you time to answer my post.

I think I managed to solve (with someonelse help) this issue by running the loop at the first simbol as shown in the code below and in the attached file. Most likely is possible to to have one instance of

if ( Status( "stocknum" ) == 0 ) 

but I had to write two of them, but this is is enought for me. Not perfect but is working.

//==========================================================================
//==========================================================================
// Settings and Options
SetOption("ExtraColumnsLocation",2);
// Watchlist // Backtester Settings //
wlnumber  = GetOption( "FilterIncludeWatchlist" );
watchlist = GetCategorySymbols( categoryWatchlist, wlnumber );
NumTickers = StrCount( watchlist, "," ) + 1;
SetOption( "MaxOpenPositions", NumTickers );
SetOption( "CommissionMode", 1 ); // 1 = PeTickerRocent 0 = Dollars
SetOption("InitialEquity", 10000 ); // Initial Portfolio Equity
SetTradeDelays(0,0,0,0);
SetOption("AllowSameBarExit", True ); 
//=======================================================
lag=1;
Index= Foreign( ".SPX","C");
Var=IIf (Index>MA(C,12),1,0);

_SECTION_BEGIN("Action Type and Time Series");
ExploreFilter = ParamToggle("Explore","Last_Bar|All_Bars",1);
if ( ExploreFilter ) Filter = 1;
else Filter = Status( "lastbarinrange" );
// Run on all times or on First Visible Bar FVB
TimeSeriesSel=ParamList("Time Series From","All Series|First Visible Bar",0);
_SECTION_END();
//=======================================================
_SECTION_BEGIN("Positions");
// Number of max open positions
PosQtyOptTog= ParamList( "PosQtyOpt:","NO|YES", Defaultval=0); 
if (PosQtyOptTog=="NO")
PosQty = Param( "Number of Positions", 3, 1, NumTickers, 1) ;
if (PosQtyOptTog=="YES")
{ 	PosQtyMin = Param("PosQtyMin",1, 1, NumTickers, 1);
	PosQtyMax = Param("PosQtyMax",NumTickers, 1, NumTickers, 1);
	PosQtyOpt = Optimize( "Pos Qty Optimaser", 1, PosQtyMin, PosQtyMax, 1); 
	PosQty = PosQtyOpt;
}
URankNum = 1; // rank has to be be >URankNum
LRankNum= PosQty;  // rank has to be be <=LRankNum 
ExcTopRankTog= ParamList( "Exc Top Rank Optimiser:","NO|YES", Defaultval=0); 
if (ExcTopRankTog=="YES")
{ 	ExcMin = Param("ExcTopPosMin",0, 1, NumTickers, 1);
	ExcMax = Param("ExcTopPosMax",5, 1, NumTickers, 1);
	ExcMaxRank = Optimize( "ExcPosQtyOpt", 1, ExcMin, ExcMax, 1); 
	URankNum = ExcMaxRank +1; // rank has to be be >=URankNum
	LRankNum= URankNum + PosQty -1;
}  // rank has to be be <= LRankNum 
InEquity=Param("Initial Equity",1000,1,100,1000,10); 
MinBars= Param("Min Bars",1,1,100,1);
CommPct=Param("% Commission", 0,0,5,0.01); // This set the value for the commissions
SetOption( "CommissionAmount", CommPct);
SetOption("HoldMinBars", MinBars );
_SECTION_END();
//=======================================================
_SECTION_BEGIN("In/Out market and cash");
// Choose if you want an In/Out Market filter 
InOutTog=ParamToggle( "In/Out Market","NO|YES",Defaultval=1);
// If In/Out Market filter is (NO)
if( InOutTog==0) 
{	InOut= 1; // This means that the Market Filter is always OFF 
	SPX=999; SPXMA=888; 
} 
// If In/Out Market filter is (YES)
if(InOutTog==1)// Filter ON
{ // Select a Ticker if In/Out Market filter is (YES)
	MarInd= ParamList( "In/Out Market Ticker:",".SPX|Name()",Defaultval=0);
	SPX=Foreign(MarInd,"C");
	/// Choseee if Optimaze or Not the MA for the Ticker of In/Out Market filter
	MarIndMAOptTog=ParamToggle( "In/Out MA Optimizer:","NO|YES",Defaultval=0);
	// If Optimaze MA is (NO) the select teh MA Periods Manually
	if(MarIndMAOptTog==0)
	{ 	SPXMAPDS= Param("In/out MA periods",12, 1, 100, 1);
		SPXMA=MA(SPX,SpxMAPds);
		InOut= SPX>=SPXMA ;
	}
	// If Optimaze MA is (YES) this optimaze the MA Periods
	if(MarIndMAOptTog==1)
	{ // If Optimaze MA is (YES) this selects Min and Max MA Periods
		SpxMaMin= Param("MA Min Pds",6, 1, 100, 1); //choose Min
		SpxMaMax= Param("MA Max Pds",18, 1, 500, 1); //choose Max
		SpxMaSte= Param("MA Ste Pds",1, 1, 100, 1); //choose Steps
		SPXMAPds= Optimize("MA Pds",1,SpxMaMin, SpxMaMax,SpxMaSte); // optimizer
		SPXMA=MA(SPX,SpxMAPds);
		InOut= SPX>=SPXMA; 
	}
}
TickerMaTog=ParamToggle( "Ticker Moving Average","NO|YES",Defaultval=0);
if(TickerMaTog==0) 
TickerMa=C;  UpDown = 1;
if(TickerMaTog==1)
{ TickerMaOptTog=ParamToggle( "Opt Ticker Moving Av.","NO|YES",Defaultval=0);
	if(TickerMaOptTog==0) 
	{ 	TickerMaPds= Param("Ticker Ma",12, 1, 100, 1);//Choose Moving Average
		TickerMa=MA(C,TickerMaPds); 
	} 
	if(TickerMaOptTog==1)
	{ 	TickerMaMin= Param("Min Ticker Ma",1, 1, 100, 1); //choose Min
		TickerMaMax= Param("Max Ticker Ma",18, 1, 500, 1); //choose Max
		TickerMaSte= Param("Step Ticker Ma",1, 1, 100, 1); //choose Steps
		TickerMaPds= Optimize( "Ticker Ma Pds", 1, TickerMaMin, TickerMaMax, TickerMaSte); //Optimize Moving Average
		TickerMa=MA(C,TickerMaPds); 
	}
}
CashTR = ParamList( "Cash Ticker:","$CASHTR|.SPX|#IND",Defaultval=0); // Ticker for cash
_SECTION_END();
//=======================================================
_SECTION_BEGIN("System Optimizer and Reports");
// This code sets if the system optimazer is OFF or ON (default=OFF)
OptTog= Paramtoggle("System Optimizer:", "OFF|ON",Defaultval=0);
if( OptTog==1 )
{ OptimizerSetEngine("cmae");  OptimizerSetOption("MaxEval", 10000000 );  }
// Report to be switched ON because of portfolio report 
Report= ParamToggle("Report:","OFF|ON",DefaultVal=1);// 0 suppresses report; 1 foTickerRoces generation of full report;
SetOption("GenerateReport", report);
// This code sets if the type of report
ReportModetog=ParamList( "Report Mode:","Trade list|Detailed log|Summary|No output", Defaultval=2); 
if(ReportModeTog=="Trade list") ReportMode= 0; if(ReportModeTog=="Detailed log") ReportMode= 1;
if(ReportModeTog=="Summary") ReportMode= 2; if(ReportModeTog=="No output") ReportMode= 3;
SetOption("PortfolioReportMode",ReportMode);
_SECTION_END();
//=========================================================
// ALL METRICS HERE
//=========================================================
eq=InEquity; // this comes from above settings
Rocs = ROC( C, 1 );
RefVar = Ref(Var,-lag); 
YMin= 0;//LastValue( Lowest(Var) ); // Minimum for IND/Y/M/Other
YMax= 1;// LastValue( Highest(Var) ); // Maximum for IND/Y/M/Other
Car =InCount = PosSize = AGGCAR=0;
BI=BarIndex();
fbr = IIf(Status( "FirstBarInRange" ),1,Null) ; 
lbr = IIf(Status( "LastBarInRange" ),1,Null) ;
NumOfBars= BI-ValueWhen (fbr==1,BI,1);
BuyPrice=Close; SellPrice=Close;
// Remove StaticVar at first symbol
if ( Status( "stocknum" ) == 0 )  
{StaticVarRemove( "Rocs*"  );  
StaticVarRemove( "Count*"  ); StaticVarRemove( "EQ*"  ); 
}
//This sets the Static Metrics
// --- metrics ranking routine ---
if ( Status( "stocknum" ) == 0 ) 
{ StaticVarRemove( "*");
	{	for ( j = 0; ( symbol = StrExtract( watchlist, j ) )  != "";  j++ )
		{ SetForeign( symbol );
				{for (y=ymin; y<=ymax; y++) 
			// This creates the stativ var for Rocs and Count		
				{	rocs= IIf(var== y, ROC( C, 1 ),0);
					StaticVarSet( "Rocs"+ Symbol + y, rocs, persistent =True );
					// Number of occurrency at each case
					count= Cum(IIF(var ==y AND NumOfBars>0, 1,0)) ;
					StaticVarSet("Count"+ Symbol +y, count,persistent =True);		
					// This creates the Equity value for each case
					{	for( i = 1; i < BarCount; i++ ) 
						{ 	if (var[i]==y AND NumOfBars[i]>=1+lag) 
							eq[i]=eq[i-1] *(1+Rocs[i]/100);
							else
							eq[i]= eq[i-1]; 
						}
						StaticVarSet("EQ"+ Symbol +y, eq ,persistent =True);
					}
					geteq = StaticVarGet( "EQ"+ Symbol +y);
					FValueEQ= LastValue(ValueWhen (fbr==1,geteq,1));
					LValueEQ= geteq;
					getcount=StaticVarGet("Count"+ Symbol +y);
					addval=1e-20; // 
					CAR=((LValueEQ/FValueEQ)^(1/(getcount/12+addval))-1)*100;//*100;
					StaticVarSet( "CAR"+ Symbol +y, CAR,persistent =True);
					GETCAR=StaticVarGet( "CAR"+Name()+y);
					for (i=1; i<BarCount; i++) 
					{ if(refvar[i]==y) 
						{ AGGCAR[i]=  GETCAR[i];
						  StaticVarSet( "AGGCAR"+ symbol , AGGCAR,persistent =True);
						}
					}
				}
			}
			RestorePriceArrays();
		}	
	}
}
//=========================================================
//=========================================================
// --- metrics ranking routine ---
if ( Status( "stocknum" ) == 0 )
{   
StaticVarRemove( "RankMet*"); StaticVarRemove( "ActMet*");
StaticVarRemove( "RankRank*"); StaticVarRemove( "PosSize*"); 
StaticVarRemove( "UpDown*"); 
//This sets the Static Metrics
    for ( i = 0; ( symbol = StrExtract( watchlist, i ) )  != "";  i++ )
    {   SetForeign( symbol );
		////==============================================
        //// Metrics for ticker ranking values STARTS HERE
        UpDown = 1; //       
		CARG=StaticVarGet( "AGGCAR"+Symbol);
		RankMet = CARG ;// Actual value used for the ranking !!
        ////==============================================
        //// Metrics for ticker ranking values ENDS HERE
        if ( symbol == CashTR ) 
        {RankMet = -999;} // force lowest rank for cash ticker
        if(TickerMaTog==1) 
        UpDown = C>= MA( C, TickerMaPds );
        ActMet= RankMet; // This is the Actual Metrics before * UpDown
        // Used for Ranking, If UpDown=0 the RankMet=0 
        RestorePriceArrays();
        StaticVarSet( "RankMet"  + symbol , RankMet,persistent =True);
        StaticVarSet( "ActMet" + symbol , ActMet ,persistent =True);
        StaticVarSet( "UpDown" + symbol , UpDown,persistent =True );
    }
    // This generates rank for "RankMet" the actual Metrics used for the Ranking
    StaticVarGenerateRanks( "Rank", "RankMet", 0, 1224 );  
    //This gets the Static Metrics Variables
    for ( i = 0; ( symbol = StrExtract( watchlist, i ) )  != "";  i++ )
    {   if(TickerMaTog==1) 
		{UpDown = StaticVarGet( "UpDown" + symbol); 
        RankNum = StaticVarGet( "RankRankMet" + symbol  ) ; 
        // n. top ranked symbols above their MA filter
        InCount = IIf( RankNum <= PosQty AND UpDown==1, InCount + 1, InCount );}         
        if(TickerMaTog==0) 
		{ RankNum = StaticVarGet( "RankRankMet" + symbol ); 
        // n. top ranked symbols above their MA filter
        InCount = IIf( RankNum <= PosQty AND UpDown==1, InCount + 1, InCount );}
    }
    //This sets the PosSize
    for ( i = 0; ( symbol = StrExtract( watchlist, i ) )  != "";  i++ )
    {   RankNum = StaticVarGet( "RankRankMet" + symbol );
        UpDown    = StaticVarGet( "UpDown"    + symbol);
        if(symbol==CashTR) // this apply to cash
        { PosSize= IIF(INOUT==0,100,(PosQty-InCount) * 100 / PosQty);
        }
        else // this apply to any other symbol
        {  PosSize  = IIf( RankNum <= LRankNum  AND RankNum >= URankNum AND UpDown==1 AND INOUT==1, 100 / PosQty, 0 );
        }  
        StaticVarSet( "PosSize" + symbol, PosSize ,persistent =True);   
    }
}
// --- get values and ranks for Momentum ---
symbol   =  Name();
RankMet = StaticVarGet( "RankMet" + symbol );
ActMet = StaticVarGet( "ActMet" + symbol );
RankNum  = StaticVarGet( "RankRankMet" + symbol );
PosSize = StaticVarGet( "PosSize"  + symbol );
UpDown   = StaticVarGet( "UpDown"    + symbol );
// --- re-balance at the end/close of every month ---
//M=Month(); LDM= M!=Ref(M,1); // This is the last day of the month 
Buy = PosSize > 0 ;
Sell = 1;
// --- set position sizes ---
PositionSize = -PosSize; // "-" sign for spsPercentOfEquity
// --- set stop in number of bars ---

//=========================================================
// Exploration //
//=========================================================
if ( Status( "actionex" ) == actionExplore )
{   
SetSortColumns( 2,3);
    // --- columns for exploration ---
    SetOption( "NoDefaultColumns", True ); 
    AddTextColumn( Name()   , "Ticker" );
	AddColumn( DateTime(), "Date", formatDateTime );
    ColorRank = IIf( RankNum <= PosQty AND PosSize, ColorGold, colorWhite );
    ColorPos  = IIf( PosSize > 0, IIf( Name() == CashTR, colorYellow, colorGold ), colorWhite );
    ColorInOut= IIF( InOut ==0, colorYellow, colorWhite);
    ColorUpDown= IIF( UpDown ==0, coloryellow, colorWhite);
    ColorInCount=IIF( InCount >0, ColorGold, colorWhite);
    AddColumn( RankNum , "RankNum"     , 1.0, 1, ColorRank );
    AddColumn( RankMet   , "RankMet"  , 3.3 );
    AddColumn( ActMet   , "ActMet"  , 3.3 );
    AddColumn( PosSize, "PosSize(%)", 3.3, 1, ColorPos  );
    AddColumn( InCount, "InCount", 3.0, 1, ColorInCount );
    AddColumn( C , "Close"   , 1.2);
	AddColumn( TickerMa , " Ticker MA"   , 1.2);
    AddColumn( UpDown  , "UpDown"   , 1.0,1 ,ColorUpDown);  
	AddColumn( InOut  , "InOut"   , 1.0,1, ColorInOut);
	AddTextColumn( MarInd  , "MarInd");
	AddColumn( SPX  , "SPX"   , 1.2);
	AddColumn( SPXMA  , "SPXMA"   , 1.2);
	AddColumn( NumOfBars, "Bars",1);
	AddColumn(AGGCAR,"AGGCAR",1.3);	
for (y=ymin; y<=ymax; y++) 
		{ gettroc = StaticVarGet( "Rocs"+Name()+y);
		  geteq= StaticVarGet( "EQ"+Name()+y);
		  getcar= StaticVarGet( "CAR"+Name()+y);
		  getcount= StaticVarGet( "count"+Name()+y);
			AddColumn(IIf(Var==y,getTRoc,Null), "Roc_"+y ,1.2);
			AddColumn(IIf(Var==y,geteq,Null) , "EQ_"+y ,1.2);
			AddColumn(IIf(Var==y,getcar,Null) , "CAR_"+y ,1.2);
			AddColumn(IIf(Var==y,getcount,Null) , "count_"+y ,1);
		}	
}
//=========================================================
// Backtesting  Stats
//=========================================================
function GetFormulaName()
{  return StrExtract( StrExtract( GetFormulaPath(), -1, '\\' ), -2, '.' ); } 
FileName=GetFormulaName();
SetCustomBacktestProc(""); // Now custom-backtest procedure follows 
if( Status("action") == actionPortfolio )
{ 
bo = GetBacktesterObject();
bo.Backtest(); // run default backtest procedure
st = bo.GetPerformanceStats(0); // get stats for all trades
// Here we add custom metric to backtest report
//Weigthed Reward/Risk Ratio
WRR = st.GetValue("WinnersAvgProfitPercent")*st.GetValue("WinnersPercent")/
abs(st.GetValue("LosersAvgLossPercent")*st.GetValue("LosersPercent"));
bo.AddCustomMetric( "W R/R ", WRR ); 
e = bo.EquityArray(); 
StaticVarset( "EQ", e, persistent = True );
ModelType=StaticVarGetText("Model");
AddToComposite( e, "~~EQ_"+FileName + ModelType, "X", atcFlagDefaults |atcFlagEnableInPortfolio); 
// Here we add custom metric at trade level
SumMAE = SumMFE= NumTrades = 0; 
   // iterate through closed trades first 
   for( trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade() ) 
   {  // here we sum up maximum adverse excursions
       SumMFE=  SumMFE + trade.GetMFE();
       SumMAE = SumMAE + trade.GetMAE();
       NumTrades++; 
   } 
   // iterate through eventually still open positions 
   for( trade = bo.GetFirstOpenPos(); trade; trade = bo.GetNextOpenPos() ) 
   { 
       SumMFE = SumMFE + trade.GetMFE();
       SumMAE = SumMAE + trade.GetMAE();
       NumTrades++; 
   } 
   averageMFE = SumMFE / NumTrades; 
   bo.AddCustomMetric( "Av.MFE   ", averageMFE );
   averageMAE = SumMAE / NumTrades;
   bo.AddCustomMetric( "Av.MAE   ", averageMAE );
   MFEMAE=abs(averageMFE)/abs(averageMAE);
   bo.AddCustomMetric( "MFE/MAE", MFEMAE );  
}
// --- end of code ---

1 Like

Your assumptions are incorrect.

Everything works fine. You do not need any pre-explorations. You just need to place the code that calculates ranks BEFORE the code that uses them.
The formula is executed sequentially from top to bottom. At the time when you call StaticVarGenerateRanks all ranks are calculated in-place and READY to be used as soon as the NEXT line in the code.

if ( Status( "stocknum" ) == 0 )
{
...  
   StaticVarGenerateRanks(...);

// ranks are ready to be used at that line and below (on first symbol)
// obviously for all other symbols ranks will be already in static variables from the very beginning
}

It does not really matter how many Status("stocknum") calls you have.

As soon as AmiBroker sees at least one such line it would execute the code for FIRST symbol only in single thread and only when it is finished, it will start parallel (multithread) execution of remaining symbols. This is specifically designed so, to guarantee that things like ranking or any kind of "first symbol only" processing are done BEFORE execution of all the other symbols. The decision to run it that way is done BEFORE execution. AmIBroker simply searches for such string in ENTIRE code BEFORE your code is run. So it does not really matter how many lines like that are found. If there is at least one, it will postpone processing of other symbols until first one is complete.

2 Likes

thanks, very clear now.

Very Good Morning Sir,
Thanks FOr good code.
Need Your Permission to use this.
Thanks In Advance,
Have a nice time sir.