Correlation Matrix

@reds, this can be done by storing the corr. table to matrix at the end of exploration (using matrix functions). The sorted top N get picked from that matrix and output via Addrow.

It is a bit tricky (but not difficult). I'm saying tricky because you have to think a little bit.

Here is proof that it is possible to do that (output of sorted table (from largest to lowest together with standard correlation table below of it). As aside Addrow does not offer cell coloring (yet). I have output all but of course you can pick just a top N via MxGetBlock also.
230136

Here is another example just outputting overall top five pairs + corr.

174133

So as always endless possibilities of output.


BTW, instead of using Foreign for default correlation table it is much much faster using StaticVarAdd to store second array of Correlation function. In fact that method is lightning fast compared to using Foreign. Milliseconds per each exploration run.

3 Likes

@reds here is a possible way to do it (a bit unorthodox approach):

/****** Constants *****/
							// Change these as needed
MAX_ALLOWED_TICKERS = 1000; // Over this size probably it is too slow...
MAX_PAIRS_IN_TABLE = 250;   // To avoid using too many columns and hit a Windows limit

/***** Parameters *****/

// Number of the watchlist to use to create the correlation matrix
wlNumber = Param("Watchlist Number", 1, 0, 63, 1);         
// Number of columns pair to display in resulting table
maxColRes = Param("Number of (pair) columns in results (0 = max)", 5, 0, MAX_PAIRS_IN_TABLE, 1);  
// Daily correlation look-back period
corrLB = Param("Daily correlation lookback period", 252, 50, 300, 1);         
// Send trace messages for debugging purpose on progess
debugTrace = ParamToggle("Enable _TRACE()", "No|Yes", 1); 


/***** Functions *****/

function _TRACE_(msg) {
    if (debugTrace) 
		_TRACE(msg);
}		


/***** Exploration *****/

actionStatus = Status("action");
if (( actionStatus == actionExplore ) OR (actionStatus == actionBacktest)) {
	symlist = GetCategorySymbols( categoryWatchlist, wlNumber);
	Filter = 0;
	stockNum = Status("stocknum");
	if (stockNum == 0)
	{
		// Do this only ONCE
		if (symList != "") {
			EnableTextOutput( 0 ); 
			size = 1 + StrCount( symlist, ",");
			if (maxColRes >= size) {
				maxColRes = size-1;
			}
			if (maxColRes <= 0) {
				maxColRes = size-1;
			}
			if (maxColRes >= MAX_PAIRS_IN_TABLE) {
				maxColRes = MAX_PAIRS_IN_TABLE; // 
			}
			
			_TRACE_("Selected watchlist: " + WriteVal(wlNumber, 2.0) + " - Size: " + WriteVal(size, 3.0));
			_TRACE_("Symbols: " + symList);
			if (size < MAX_ALLOWED_TICKERS+1) {
				Filter = 0;
				SetOption("NoDefaultColumns", True );	
				AddColumn(Null, "Ticker", 1.2, -1, -1, 60);
				for (i = 1; i <= maxColRes; i++) {
					AddColumn(Null, "T" + WriteVal(i, 1.0), 1.2, -1, -1, 60);
					AddColumn(Null, "C" + WriteVal(i, 1.0), 1.2, -1, -1, 60);	
				}
				
				// Get Tickers and do calculations ONCE 
				_TRACE_("Parsing tickers");				
				for( i = 0; ( sym = StrExtract( symlist, i ) ) != ""; i++ ) 
				{
					VarSetText("T_" + i, sym);
					CFrgn = Foreign( sym, "C" );
					logCFrgn = log( CFrgn / Ref( CFrgn, -1 ) );
					VarSet("L_" + i, logCFrgn);					
				}	
				_TRACE_("Parsing tickers. Done");
				// Add top row used for ticker index 
				mx = Matrix(size+1, size, 0);		
				_TRACE_("Created matrix. " + WriteVal(size+1, 3.0) + "*" +  WriteVal(size, 3.0));						
				
				_TRACE_("Filling matrix with correlations.... ");
				// First row is reserved to ticker indexes
				for( col = 0; col < size; col++ )
				   mx[0][col] = col;
				
				// Filling a matrix with correlation values
				startCol = 0;
				for( row = 0; row < size; row++ ) 
				{
					symRow = VarGetText("T_" + row);
					// CRow = Foreign( symRow, "C" ); 
					// logCRow = log( CRow / Ref( CRow, -1 ) ); 
					if (row % 10 == 0) 
						_TRACE_("Row " + WriteVal(row, 3.0));
					for( col = startCol; col < size; col++ )
					{
						symCol = VarGetText("T_" + col);
						// _TRACE_("R " + WriteVal(row, 2.0) + " C " + WriteVal(col, 3.0) + " " + symRow + " * " + symCol);
						// CCol = Foreign( symCol, "C" ); // store Foreign to variable since it is called multiple times
						// LogCCol = log( CCol / Ref( CCol, -1 ) ); 
						// corrARray = Correlation( LogCRow, LogCCol, corrLB );
						// corr = LastValue(corrArray);
						corr = LastValue(Correlation( VarGet("L_" + row), VarGet("L_" + col), corrLB ));
						// corr is a columm 
						mx[col+1][row] = corr;
						// Filling the symmetrical cell
						mx[row+1][col] = corr;
					}
					startCol += 1; // reducing number of cells to fill thanks to to this matrix symmetry
				}    

				_TRACE_("Matrix done. Sorting - Ignoring header");

				
				_TRACE_("Coupling elements");		
				// Print result in the Analisys window using AddRow (to output too if needed)
				for( row = 0; row < size; row++ ) {

					// we need to transpose the matrix to sort by columns - then restore
					// each iteration we sort on a different row for each ticker (rows headers)
					// and row 0 will have the orders of correlated tickers (columns headers)
					mx2 = MxTranspose(mx); 
					mx3 = MxSortRows(mx2, False, row+1); // sort descending
					mx2 = MxTranspose(mx3);  
					symRow = VarGetText("T_" + row);				
					s = symRow + "\t";
					// Skip col 0 since it is 1.0 for the ticker own correlation.... 
					for( col = 1; col <= maxColRes; col++ )
					{
						symIdx = mx2[0][col]; // get the index from row 0
						symCol = VarGetText("T_" + symIdx);				
						corr = mx2[row+1][col];
						s = s + symCol + "\t" + WriteVal( corr, 3.3 ) + "\t";
					}		
					AddRow(s);
				}
				_TRACE_("Coupling elements/Add rows done");	
			} else {
				_TRACE_("Too many tickers. Skipping");	
			}
		} else {
			_TRACE_("Empty watchlist. Skipping");	
		}
	} 
	_TRACE_("Script completed for stock " + WriteVal(stockNum, 4.0));					
} else {
	_TRACE_("Script ignored. Status " + WriteVal(actionStatus, 1.0));					
}

w = 0; // used to set a breakpoint when using the debugger

I too used matrices and addRow().

(In this sample I applied the correlation logic used by @portfoliobuilder - using only the last value of the correlation arrays; you may want to change it as needed).

I use a matrix to store the correlations and then sort it multiple times; one for each ticker row - using the first row of my original unsorted matrix as an index for the tickers to display in the correlation table.

Seems to work well enough for medium/sized watchlists (I used a max of 500 tickers)

To display the resulting data in the Analysis window I used the AddRow() function with the filter set to 0 (search here in the forum other examples about its usage). Unfortunately as said also in the previous message no colors…

For the exploration, you have to choose a watchlist (selecting its corresponding number as a parameter).
In order to get the table a little faster, I suggest setting the analysis filter to “current” (it is actually ignored but the process will be done only once).

This sample raw code (where I left on purpose many commented lines to show how some code was developed/moved/replaced) is provided “as it is” (it is not fully tested); take it mainly as a starting point for your own further development.

(All suggestions to do it in a better way are welcome!)

9 Likes

P.S. Note that all the relevant code happens when the Status(“stocknum”) == 0 so it is run in a SINGLE THREAD (I did it on purpose to be able to easily follow the code logic tracing/debugging).

1 Like

Portfoliobuilder, fxshrat, and beppe…Thank you for all you recommendations and direction! I will consider all and move forward…Happy New Year! Best…Mike

Two questions about this matrix:

(1) Why are the correlation values on both sides of the diagonal sometimes different for the same pair of symbols? Which correlation is then the good one?

(2) If we want to do a correlation analysis using the weekly or monthly returns instead of the daily returns, would changing the "periodicity" in the settings window be the right way to do it? (Assuming that we also change the AFL number of bars lookback also obviously)

Thanks,

Oscar

How do i get Full Name of the ticker in the above matrix
i'm able to get in vertical lines but not on horizintal lines

Good-day Amibroker community,

I would like to seek your valuable help in resolving my challenge. I'm trying to sum up the correlation of each and every symbol for later use in a ranking scheme.
Having browsed through the entire forum it seems there is a discussion about this topic on the old yahoo group but I can't access it... I'm able to sum up the correlation in exploration mode but can't seem to be able to store them into a static variable which is pretty frustrating :slight_smile:

I attempted several ways but nothing I've tried seems to work.. the values I'm after are highlighted in yellow on the screenshot below.

Any help or pointer would be greatly appreciated.

Thanks in advance for your time on this -

ArnaudP.

image

@ArnaudP do you want to store each total in a single staticVar with a name based on the column header (for instance "CorrelationMatrixTotal_APPL", "CorrelationMatrixTotal_AXP", ..., "CorrelationMatrixTotal_TRV") or in some other ways?

Moreover, do you want to include in the totals also the "autocorrelation" value (1.0) as displayed in the screenshot, probably done using a AddSummaryRows(1, 1.2) line?

If you've calculated all the correlations and summed them up, then the hard word is already done. What issue are you having with Static Variables? Could you post some code to help us help you find your error?

@beppe & @mradtke. I appreciate that two legends of the amibroker community are looking into my issue :slight_smile:
@beppe , yes this is exactly what I intend to do, I would like to have the ability to retrieve the ColSum correlation of each asset to add this component to a bigger weighting scheme. Basically what I'm trying to achieve is the FAA (flexible asset allocation) scheme as detailed in the following SSRN paper: https://papers.ssrn.com/sol3/papers.cfm?abstract_id=2193735
While I have no problem coding it with R (as per this example: http://systematicinvestor.github.io/strategy/Strategy-FAA )
I encounter an issue when it comes to getting the sum of the correlation to include to my three components weighting scheme.
And yes you are right I used AddSummaryRows to obtain the top line on my screenshot.
@mradtke the code I generated is essentially the same as the one posted by portfolio builder in the same post, my only trouble is to generate & store the cummulated correlation values of each asset.
I know it may sound trivial but I'm only beginning my journey with AFL and facing my first roadblock -
I intend to share the full FAA implementation to this forum (which is quite close to GTAA by trendXplorer) once successfully succesffuly coded.
Thanks !
ArnaudP

We don't need the full FAA code at this point. Just a simplified version that shows how you're calculating the sum of correlations and attempting to store them in static variables. Without seeing your code, we really have no way to point you in the right direction other than to implement our own solution from scratch. That's not a good use of our time nor the best way for you to improve your AFL skills.

Here is code example

/// @link https://forum.amibroker.com/t/correlation-matrix/1184/4
/// @link https://forum.amibroker.com/t/correlation-matrix/1184/5
CorrLB = Param("Lookback", 252, 50, 300, 1); // daily correlation look-back period//

wlnumber = GetOption( "FilterIncludeWatchlist" ); // choose your WL in Analysis window
symlist = CategoryGetSymbols( categoryWatchlist, wlnumber );

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

	Filter = Status( "lastbarinrange" );
	SetSortColumns(1); 
	Tickers = Name();

	AddSummaryRows( 1, 1.5 );
	
	logC = log( C / Ref( C, -1 ) );// does not belong within loop

	/// added by fxshrat to remove stored values for summary rows
	if( Status( "stocknum" )  == 0 )
		StaticVarRemove( "cs_*" );

	cs = 0;/// added by fxshrat
	// iterate through symbols
	for( i = 0; ( sym = StrExtract( symlist, i ) ) != ""; i++ )
	{
		Frg = Foreign( sym, "C" ); 
		FrgLog = log( Frg / Ref( Frg, -1 ) );
		Corr = Correlation( logC, FrgLog, CorrLB );
		corrcond = Corr>0.6;
		tickercond = Tickers == sym;
		Clr = 32 + SelectedValue( Corr ) * 32;
		Clr = IIf( tickercond, colorBlack, Clr );
		fntcolor = IIf( tickercond, colorWhite, IIf(corrcond,colorWhite, -1 ));
		cellcolor = IIf( tickercond, colorDarkBlue, IIf(corrcond, colorGreen, -1) );
		AddColumn( Corr, sym, 1.3, fntcolor, cellcolor, width = 60 );   
		
		cs += SelectedValue(corr);  /// added by fxshrat
	}

	/// @ link https://forum.amibroker.com/t/correlation-matrix/1184/26
	/// added by fxshrat to store summary rows to matrix
	i = Status( "Stocknum" );
	StaticVarAdd( "cs_" + i, cs );
	mat = Matrix( 1, StrCount( symlist, "," ) + 1 );
	for( i = 0; i < MxGetSize(mat,1); i++ )
		mat[0][i] = StaticVarGet( "cs_" + i );	
	StaticVarSet( "SummaryRowsMat", mat );
}

_TRACE( symlist );

/// added by fxshrat to call "summary rows"-matrix
mat = StaticVarGet( "SummaryRowsMat" );
if( typeof(mat) == "matrix" ) {
	mstr = StrReplace(StrReplace(MxToString(mat), "{", ""), "}", "" );
	_TRACE( mstr );
}

418

10 Likes

@fxshrat Thanks a lot this is definitely very helpul. Hopefully it will be of some use for other users too. Cheers

Here is modification to move the whole matrix part outside of exploration since (I suppose) you want to use it elsewhere...

/// Correlation output and summary rows storage
/// @link https://forum.amibroker.com/t/correlation-matrix/1184/4
/// @link https://forum.amibroker.com/t/correlation-matrix/1184/5
/// @link https://forum.amibroker.com/t/correlation-matrix/1184/28
CorrLB = Param("Lookback", 252, 50, 300, 1); // daily correlation look-back period//

wlnumber = GetOption( "FilterIncludeWatchlist" ); // choose your WL in Analysis window
symlist = CategoryGetSymbols( categoryWatchlist, wlnumber );

stocknum = Status( "stocknum" );

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

	Filter = Status( "lastbarinrange" );
	SetSortColumns(1); 
	Tickers = Name();

	AddSummaryRows( 1, 1.5 );
	
	logC = log( C / Ref( C, -1 ) );// does not belong within loop

	/// added by fxshrat to remove stored values for summary rows
	if ( stocknum == 0 )
		StaticVarRemove( "cs_*" );

	cs = 0;/// added by fxshrat

	// iterate through symbols
	for ( i = 0; ( sym = StrExtract( symlist, i ) ) != ""; i++ )
	{
		Frg = Foreign( sym, "C" ); 
		FrgLog = log( Frg / Ref( Frg, -1 ) );
		Corr = Correlation( logC, FrgLog, CorrLB );
		corrcond = Corr>0.6;
		tickercond = Tickers == sym;
		Clr = 32 + SelectedValue( Corr ) * 32;
		Clr = IIf( tickercond, colorBlack, Clr );
		fntcolor = IIf( tickercond, colorWhite, IIf(corrcond,colorWhite, -1 ));
		cellcolor = IIf( tickercond, colorDarkBlue, IIf(corrcond, colorGreen, -1) );
		AddColumn( Corr, sym, 1.3, fntcolor, cellcolor, width = 60 );   
		
		cs += SelectedValue(corr);  /// added by fxshrat
	}

	/// @ link https://forum.amibroker.com/t/correlation-matrix/1184/28
	/// added by fxshrat to store correlation sum
	StaticVarAdd( "cs_" + stocknum, cs );
}


/// added by fxshrat to store corr sum to "summary row" matrix vector
/// called outside after exploration being finished, for further use cases...
//if ( stocknum == 0 ) 
{
	_TRACE( symlist );	

	mat = Matrix( 1, StrCount( symlist, "," ) + 1 );
	for ( i = 0; i < MxGetSize(mat,1); i++ )
		mat[0][i] = StaticVarGet( "cs_" + i );	
	mstr = StrReplace(StrReplace(MxToString(mat), "{", ""), "}", "" );
	_TRACE( mstr );
}
6 Likes

After contacting @ArnaudP in private, he asked me to share the FAA code I have for monthly data.

Please do comment on possible improvements. I am especially interested how to modify the code to work with daily data while (still) observing the exact monthly endpoints (not being i.e. 4 x 21).

Importantly: this code is not to correct the matrix approach shared by @fxshrat. It's just another way, originating from the era when AmiBroker did not have build-in matrix functions.

In the below code the correlation matrix is calculated in two different ways. For Explorations, the code uses a loop in similar fashion to the earlier shared approached. For the rotational calculations to determine the monthly position sizes, the code uses a custom function CalcPfC( symbol, tickerlist, length ).

Acknowledgement goes to the old Yahoo board member SanzProphet, who kindly helped me along with this code back in 2014.

In Exploration View the result looks like:
FAA%20Exploration

Running the code on the N10 investment universe and the separate out of market fund as shown on the screenshot with TopSize=10, results in the following long term performance chart:
FAA

Point to note: next to the filterlist with both all investment assets as well as the out of market fund, the code needs a dedicated watchlist with only the investment assets. Otherwise the correlation calculations are off.

////////////////////////////////////////////////////////////////////////////////////////
//
// --- FAA_Dual_Monthly_v1.1.afl ---
//
// --- introduction ---
//
// based on: 
// "Generalized Momentum and Flexible Asset Allocation (FAA), An Heuristic Approach"
// Keller and Van Putten, 2012
// FAA Electronic copy available at: http://ssrn.com/abstract=2193735
//
// FAA strategy rules:
// M: Calculate 4 months relative momentum on monthly returns, then rank (highest is best)
// V: Calculate 4 months standard deviation on monthly returns, then rank  (lowest is best)
// C: Calculate 4 month average correlation on monthly prices, then rank (lowest is best)
// Select top 3 assets based on final MVC ranking: 100% M + 50% V + 50% C
// From the top 3 replace asset with M <= 0 for cash (instead of FAA cash proxy: VFISX)
// Rebalance monthly
//
// --- afl-version ---
//
// AmiBroker implementation by TrendXplorer: www.trendxplorer.info
//        in collaboration with SanzProphet: sanzprophet.blogspot.com
// original version: Feb. 18, 2014, modified for BacktestRotational: Aug. 18, 2018
//
// STRATEGY SETUP:
// 1 - ADJUSTING START AND END DATES
// 2 - USE BACKTESTER SETTINGS FOR MONTHLY PERIODICITY
// 3 - SELECT FILTERLIST
// 4 - START BACKTEST
//
//
////////////////////////////////////////////////////////////////////////////////////////

// --- begin of code ---

// --- inputs ---
frequency      = ParamList( "Rebalance Frequency:", "Monthly|Bi-Monthly|Quarterly|Annually", 0 );

// --- detect invest universe ---
wln_market     = Param( "WatchListNumber for Market Assets:", 11, 0, 150, 1 );
marketlist      = GetCategorySymbols( categoryWatchlist, wln_market );
NoM             = StrCount( marketlist, "," ) + 1;

TopSize        = Param( "Number of Positions",  3, 1, NoM, 1 );

MomLength      = Param( "Momentum Period"    ,  4, 1, 12, 1 );
VolLength      = Param( "Volatility Period"  ,  4, 1, 12, 1 );
CorLength      = Param( "Correlation Period" ,  4, 1, 12, 1 );

wM             = Param( "Momentum Weight"    , 1.0, 0.0, 1.0, 0.1 );  // weight of momentum
wV             = Param( "Volatility Weight"  , 0.5, 0.0, 1.0, 0.1 );  // weight of volatility
wC             = Param( "Correlation Weight" , 0.5, 0.0, 1.0, 0.1 );  // weight of correlation

_Cashticker    = ParamStr( "Cash Proxy", "SHY" );

showCorTable   = ParamToggle( "Show Correlation Table:", "No|Yes", 1 );

// --- detect tradelist ---
wlnumber       = GetOption( "FilterIncludeWatchlist" );
filterlist     = GetCategorySymbols( categoryWatchlist, wlnumber );

// --- backtester settings ---
SetBacktestMode( backtestRegular );
SetOption( "CommissionAmount", 0.00 );
SetOption( "InitialEquity", 100000 );
SetTradeDelays( 0, 0, 0, 0 ); 
SetOption( "MaxOpenLong", TopSize );
SetOption( "MaxOpenPositions", TopSize );
SetOption( "AllowPositionShrinking", True );
SetOption( "AllowSameBarExit", True ); 
SetOption( "ReverseSignalForcesExit", False ); 
SetOption( "HoldMinBars", 1 );
SetOption("ExtraColumnsLocation", 11 ); 
RoundLotSize = 0.0001;

// --- detect period ends ---
MonthEnd    = Month() != Ref( Month(), 1 );
BiMonthEnd  = Month()%2  == 0 AND MonthEnd;
QuarterEnd  = Month()%3  == 0 AND MonthEnd;
YearEnd     = Month()%12 == 0 AND MonthEnd;

// --- init rebalancing frequency ---
if ( frequency == "Monthly"    ) Rebalance = MonthEnd;
if ( frequency == "Bi-Monthly" ) Rebalance = BiMonthEnd;
if ( frequency == "Quarterly"  ) Rebalance = QuarterEnd;
if ( frequency == "Annually"   ) Rebalance = YearEnd;
Rebalance = Rebalance OR Status( "LastBarInTest" );

// --- function for portfolio calculation provided by SanzProphet ---
function CalcPfC( symbol, tickerlist, length )
{
    // This function takes a symbol from a watchlist 
    // and calculates the average correlation of the
    // symbol to the other symbols
	
    CorSum = 0;
    if ( tickerlist != "" )
    {
        for ( j = 0; ( ticker = StrExtract( tickerlist, j ) ) != ""; j++ )
        {
            data1   = Foreign( ticker, "C" );
            data2   = Foreign( symbol, "C" );
            log1    = log( data1 / Ref( data1, -1 ) );
            log2    = log( data2 / Ref( data2, -1 ) );
            CorSum  = CorSum + Correlation( log1, log2, Length );
        }
    }
    // take out the diagonal of the correlation matrix and average
    PfC = ( CorSum - 1 ) / ( j - 1 );
    return ( PfC );
}


// --- init ---
SumPosSize = 0;

// --- asset selection and capital allocation routine ---
if ( ( Status( "stocknum" ) == 0 OR Status("stocknum") == -1 ) )
{
    StaticVarRemove( "Mom*" );
    StaticVarRemove( "Vol*" );
    StaticVarRemove( "Cor*" );
    StaticVarRemove( "MVC*" );
    StaticVarRemove( "Pos*" );
    StaticVarRemove( "Sum*" );
    StaticVarRemove( "num*" );
    StaticVarRemove( "cas*" );

    for ( i = 0; ( symbol = StrExtract( marketlist, i ) )  != "";  i++ )
    {
        SetForeign ( symbol );

			Mom =  100 * ( Close / Ref( Close, -MomLength ) - 1 );                           		// highest is best
			Vol = -100 * StDev( ( Close / Ref( Close, -1 ) - 1 ), VolLength, False ) * sqrt( 12 );	// lowest is best

        RestorePriceArrays();
        
        Cor = -CalcPfC( symbol, marketlist, CorLength ); // lowest is best
        
		if ( symbol == _Cashticker )
		{
			
			CashMom = Mom;
			Mom     = Vol = Cor = Null;
			
			StaticVarSet( "CashMom", CashMom );
		}
       
        StaticVarSet( "Mom"    + symbol, Mom   );
        StaticVarSet( "Vol"    + symbol, Vol   );
        StaticVarSet( "Cor"    + symbol, Cor   );
        StaticVarSet( "number" + symbol, i + 1 );
   }

    // generate ranks for M, V and C
    StaticVarGenerateRanks( "Rank", "Mom", 0, 1224 );
    StaticVarGenerateRanks( "Rank", "Vol", 0, 1224 );
    StaticVarGenerateRanks( "Rank", "Cor", 0, 1224 );

    for ( i = 0; ( symbol = StrExtract( marketlist, i ) )  != "";  i++ )
    {
        RankMom  = StaticVarGet( "RankMom"  +  symbol );
        RankVol  = StaticVarGet( "RankVol"  +  symbol );
        RankCor  = StaticVarGet( "RankCor"  +  symbol );
        
        // in case of tie, RankMom is decisive through + 0.001       
        MVC = ( wM + 0.001 ) * RankMom + wV * RankVol + wC * RankCor;
        
        StaticVarSet( "MVC" + symbol, -MVC );
    }
    
    // generate master ranking
    StaticVarGenerateRanks( "Rank", "MVC", 0, 1224 );
    
    for ( i = 0; ( symbol = StrExtract( marketlist, i ) )  != "";  i++ )
    {
    	// --- retrieve stored values ---    	
    	RankMVC    = StaticVarGet( "RankMVC" + symbol );
    	Mom        = StaticVarGet( "Mom"     + symbol );
    	
		PosSize    = IIf( RankMVC <= TopSize AND Mom > 0, 100 / TopSize, 0 );
		
		SumPosSize = SumPosSize + PosSize;
		
		// --- store position size percentages ---
		StaticVarSet( "PosSize" + symbol, PosSize );
	}
       
    // --- calculate allocation for out-of-market "cash" fund ---
    PosSizeCash = 100 - SumPosSize; 
    
    // --- store value ---
    StaticVarSet( "SumPosSize" , SumPosSize  );
    StaticVarSet( "PosSizeCash", PosSizeCash );
    
    for ( i = 0; ( symbol = StrExtract( filterlist, i ) )  != "";  i++ )
    {	
		if ( symbol == _Cashticker ) 
		{
			// --- check if cash symbol is part of investment universe too ---
			PosSizeCash = StaticVarGet( "PosSizeCash"      );
			PosSize     = StaticVarGet( "PosSize" + symbol );
			PosSize     = IIf( !IsNull( PosSize ), PosSizeCash + PosSize, PosSizeCash );
			
			SetForeign ( symbol );

				Mom =  100 * ( Close / Ref( Close, -MomLength ) - 1 );

			RestorePriceArrays();
			
			StaticVarSet( "Mom" + symbol, Mom );
			
			StaticVarSet( "number" + symbol, NoM + 1 );

			StaticVarSet( "PosSize" + symbol, PosSize );
			
			StaticVarSet( "RankMom" + symbol, Null );
			StaticVarSet( "RankVol" + symbol, Null );
			StaticVarSet( "RankCor" + symbol, Null );
			StaticVarSet( "MVC"     + symbol, Null );
			StaticVarSet( "RankMVC" + symbol, Null );
		}
	}
}

// --- get values and ranks for M, V and C ---
symbol  =  Name();
Mom     =  StaticVarGet( "Mom"     +  symbol );
Vol     = -StaticVarGet( "Vol"     +  symbol );
Cor     = -StaticVarGet( "Cor"     +  symbol );
MVC     = -StaticVarGet( "MVC"     +  symbol );
RankMom =  StaticVarGet( "RankMom" +  symbol );
RankVol =  StaticVarGet( "RankVol" +  symbol );
RankCor =  StaticVarGet( "RankCor" +  symbol );
RankMVC =  StaticVarGet( "RankMVC" +  symbol );
CashMom =  StaticVarGet( "CashMom" );

PosSize    = StaticVarGet( "PosSize" + symbol );
SumPosSize = StaticVarGet( "SumPosSize"        );
number     = StaticVarGet( "number"  + symbol  );

// --- set position sizes ---
SetPositionSize( PosSize, spsPercentOfEquity );

// --- re-balance at the end/close of every month ---
Buy          = Rebalance AND PosSize > 0;
Sell         = Rebalance;
Short        = Cover = 0;
BuyPrice     = Close;
SellPrice    = Close;

// --- exploration filter ---
ExploreFilter = ParamToggle( "ExploreFilter", "LastBarInTest|All", 1 );
if ( ExploreFilter )
	Filter = 1;
else
	Filter = Status( "LastBarInTest" );

// --- sort for exploration only (not on backtest) ---
if ( Status( "actionex" ) == actionExplore ) 
{
	// if ( showCorTable ) SetSortColumns( 2, 3 );
	// else				SetSortColumns( 2, -11, 10 );
	SetSortColumns( 2, 3 );

	// --- columns for exploration ---
	ColorMom  = IIf( Mom > 0    , colorBrightGreen, colorRed );	
	ColorCash = IIf( CashMom > 0, colorBrightGreen, colorRed );	
	PosColor  = IIf( symbol == _Cashticker, IIf( PosSize > 0, colorYellow, colorWhite ), IIf( PosSize > 0, colorGold, colorWhite ) );

	AddColumn( number, "Symbol#", 1.0 );

	
	// --- calculate correlation matrix for exploration ---
	if ( showCorTable )
	{
		ticker = Name();
				
		for ( j = 0; ( symbol = StrExtract( marketlist, j ) )  != "";  j++ )
		{
			data  = Foreign( symbol, "Close" );
			logData = log( data / Ref( data, -1 ) );
			logClose = log( Close / Ref( Close, -1 ) );
			CorXY = Correlation( logClose, logData, CorLength );
			Color = IIf( CorXY > 0, ColorRGB( 0, 204, 0 ), IIf( CorXY < 0, colorLightOrange, colorWhite ) );
			Color = IIf( ticker == symbol, colorBlack, Color );
			AddColumn( CorXY, symbol, 2.3, 1, Color );
		}
	}
	
	if ( symbol == _CashTicker )
	{
		AddColumn( CashMom, "Momentum", 3.3, 1, ColorMom );
	}
	else
	{
		AddColumn( Mom    , "Momentum", 3.3, 1, ColorMom );
	}
	AddColumn( Vol    , "Volatility" , 1.3                  );
	AddColumn( Cor    , "Portf.Cor"  , 1.3                  );

	AddColumn( RankMom, "RankMom"    , 1.0                  );
	AddColumn( RankVol, "RankVol"    , 1.0                  );
	AddColumn( RankCor, "RankCor"    , 1.0                  );
	AddColumn( MVC    , "MVC   "     , 3.3                  );
	AddColumn( RankMVC, "RankMVC"    , 1.0                  );
	
	AddColumn( PosSize, "PosSize (%)", 3.3, 1, PosColor     );		
}

////////////////////////////////////////////////////////////////////////////////////////
//
// --- end of code ---
//
////////////////////////////////////////////////////////////////////////////////////////
6 Likes

@TrendXplorer thanks for sharing your code JW. I was uncertain what you were looking for with this line,

I thought perhaps you were looking for some examples of using different time frame data (in this example daily/weekly/monthly) together. So here is a small example with a couple of different methods of using the longer timeframe for display on the shorter time frame.

// lets compare the 126 day vs 26 week vs 6 month ROC

TimeFrameSet(inWeekly); 
WeeklyROC26=ROC(C, 26);
TimeFrameRestore(); // restore time frame to original


TimeFrameSet( inMonthly); 
MonthlyROC6= ROC(C,6);
TimeFrameRestore(); // restore time frame to original


DailyROC126= ROC(C, 126);
Weekly2DailyROC26 = TimeFrameExpand( WeeklyROC26, inWeekly ); // expand for display on Daily data
Monthly2DailyROC6= TimeFrameExpand( MonthlyROC6, inMonthly); // expand for display on Daily data 

///  for Explore Display with Nulls  ///
Weekly2DailyROC26pt = TimeFrameExpand( WeeklyROC26, inWeekly, expandPoint ); // expand for display on Daily data
Monthly2DailyROC6pt= TimeFrameExpand( MonthlyROC6, inMonthly, expandPoint); // expand for display on Daily data

// for display purposes
eow = DayOfWeek() > Ref(DayOfWeek(), 1); // end of week
eom = Month() != Ref(Month() , 1); // end of month (looks into future)

// Explore to debug our variables and illustrate what the calculations are doing
Filter=1;
dynamic_fg_Color = IIf(eow, colorWhite, colorDefault);
dynamic_Bk_Color = IIf(eow, colorLightBlue, colorDefault);
dynamic_fg_ColorM = IIf(eom, colorWhite, colorDefault);
dynamic_Bk_ColorM = IIf(eom, colorBlue, colorDefault);
AddColumn(eow,"End Of Week", 1.0, dynamic_fg_Color, dynamic_Bk_Color);
AddColumn(eom,"End Of Month", 1.0, dynamic_fg_ColorM, dynamic_Bk_ColorM);

AddColumn(DailyROC126, "ROC(126) daily");
AddColumn(Weekly2DailyROC26, "ROC(26) Weekly");
AddColumn(Monthly2DailyROC6, "ROC(6) Monthly");

AddColumn(Weekly2DailyROC26pt, "ROC(26) W");
AddColumn(Monthly2DailyROC6pt, "ROC(6) M");

Plot( DailyROC126, "ROC(126) daily", colorRed, styleLine ); 
Plot( Weekly2DailyROC26, "ROC(26) Weekly", colorBlue, styleStaircase );
Plot( Monthly2DailyROC6, "ROC(6) Monthly", colorAqua, styleStaircase );

image

I am not sure that is what you were interested in but I hope it helps.

3 Likes

While thinking about how to explain to @portfoliobuilder what I actually was looking for, I managed to find a partial solution (ROC solved below, but Volatility and Correlation not: array to number issue).

For running strategies like FAA with monthly lookback settings on the daily time frame I wanted to get same end-of-the-month readings when comparing daily ROC's against monthly ROC's. Since the number of trading days typically changes each month, using i.e. 6-months times 21 days for a ROC(126), the reading won't always match with a 6-month ROC. This is what I meant with "preserving monthly endpoints".

AAPL

length           = Param( "Length in Months", 12, 1, 12, 1 );

MonthEnd         = Month() != Ref( Month(), 1 );
CountDays        = Cum( 1 );
LastDayCount     = ValueWhen( MonthEnd, CountDays, 1 );
FirstDayCount    = ValueWhen( MonthEnd, CountDays, length + 1 );
DaysInPeriod     = LastDayCount - FirstDayCount;

ChangeOverDays   = 100 * ( Close / Ref( Close, -DaysInPeriod ) - 1 );
ChangeOverMonths = 100 * ( ValueWhen( MonthEnd, Close, 1 ) / ValueWhen( MonthEnd, Close, length + 1 ) - 1 );

match            = ChangeOverDays == ChangeOverMonths;

Filter           = MonthEnd;

matchColor       = IIf( match, colorGreen, colorRed );

AddColumn( ChangeOverDays  , "Daily ROC% over "   + NumToStr( length, 1.0 ) + " months", 1.3 );
AddColumn( DaysInPeriod    , "Days in " + NumToStr( length, 1.0 ) + "-month period"    , 1.0 );
AddColumn( ChangeOverMonths, "Monthly ROC% over " + NumToStr( length, 1.0 ) + " months", 1.3 );
AddColumn( match           , "Match", 1.0, 1, matchColor                                     );

However, for calculating i.e. the (exact) 6-month volatilities and correlations on daily periodicity, the non-stationary number of days in that period are required. Since both functions require a number instead of an array as period input, the above used DaysInPeriod array can't be used as such and returns an error:

error

Perhaps someone could show how to extract the appropriate (changing) number of days from the array into a scalar?

1 Like

@TrendXplorer for your volatility calculation I believe you are using Standard Deviation and that indicator can already accept an Array as well as a Scalar.
http://www.amibroker.com/guide/a_varperiods.html

You may consider creating a variable-period version of correlation,

//Variable period correlation
function VarCorrelation( x, y, number )
{
    nom = MA( x * y, number ) - MA( x, number ) * MA( y, number );
    denom = sqrt( MA( x ^ 2, number ) - MA( x, number ) ^ 2 ) *
            sqrt( MA( y ^ 2, number ) - MA( y, number ) ^ 2 );
    return nom / denom;
}

Discussed in this forum thread,

Otherwise, as you have probably read over many different posts in the forum, if you want to process things bar by bar, you need to write explicit loop and then loop counter gives your current bar inside the loop. A variable would return a number instead of array if outputting an element of the array.

I tried using the built-in functions returning element of an array -- SelectedValue(), LastValue(), BeginValue(), EndValue() , but unable to find a solution so far.

2 Likes

@portfoliobuilder Thanks Larry for pointing at to those examples provided by @Tomasz. The offered solution through a function is quite elegant.

exploration

Updated code:

function VarCorrelation( x, y, number )
{
    nom   = MA( x * y, number ) - MA( x, number ) * MA( y, number );
    denom = sqrt( MA( x ^ 2, number ) - MA( x, number ) ^ 2 ) *
            sqrt( MA( y ^ 2, number ) - MA( y, number ) ^ 2 );
    return nom / denom;
}

length           = Param( "Length in Months", 4, 1, 12, 1 );

// --- determine number of days in lookback period ---
MonthEnd         = Month() != Ref( Month(), 1 );
CountDays        = Cum( 1 );
LastDayCount     = ValueWhen( MonthEnd, CountDays, 1 );
FirstDayCount    = ValueWhen( MonthEnd, CountDays, length + 1 );
DaysInPeriod     = LastDayCount - FirstDayCount;

// --- calculate daily and monthly ROC
ChangeOverDays   = 100 * ( Close / Ref( Close, -DaysInPeriod ) - 1 );
ChangeOverMonths = 100 * ( ValueWhen( MonthEnd, Close, 1 ) / ValueWhen( MonthEnd, Close, length + 1 ) - 1 );

// --- plot as indicator ---
Plot( ChangeOverDays  , "ROC " + NumToStr( length, 1.0 ) + "m ( " + NumToStr( DaysInPeriod, 1.0 ) + " days)", colorBlue );
Plot( ChangeOverMonths, "ROC " + NumToStr( length, 1.0 ) + "m ( endpoints )", colorRed, styleHistogram|styleThick );

// --- calculate volatility ---
if ( Interval() == inDaily     )  { BarsInYear = 252; }
if ( Interval() == inWeekly    )  { BarsInYear =  52; } 
if ( Interval() == inMonthly   )  { BarsInYear =  12; }
if ( Interval() == inQuarterly )  { BarsInYear =   4; }

DailyReturn      = ROC( Close, 1 );
VolOverPeriod    = StDev( DailyReturn, DaysInPeriod ) * sqrt( BarsInYear );

// --- calculate correlation ---
symbol           = ParamStr( "Pairing symbol", "$SPX" );
logClose         = log( Close / Ref( Close, -1 ) );
ForeignClose     = Foreign( symbol, "C" );
logForeign       = log( ForeignClose / Ref( ForeignClose, -1 ) );
CorOverPeriod    = VarCorrelation( logClose, logForeign, DaysInPeriod );

// --- exploration ---
Filter           = MonthEnd;

AddColumn( DaysInPeriod    , "Days in " + NumToStr( length, 1.0 ) + "-month period"                                   , 1.0 );
AddColumn( ChangeOverDays  , "Daily ROC% over "   + NumToStr( length, 1.0 ) + " months"                               , 1.3 );
AddColumn( ChangeOverMonths, "Monthly ROC% over " + NumToStr( length, 1.0 ) + " months"                               , 1.3 );
AddColumn( VolOverPeriod   , "Volatility over "  + NumToStr( length, 1.0 )  + " months (Y%)"                          , 1.3 );
AddColumn( CorOverPeriod   , "Correlation(" + Name() + "," + symbol + ") over " + NumToStr( length, 1.0 )  + " months", 1.3 );
14 Likes

To answer non-Amibroker question number 2:
You always use returns, never price. Prices are non-stationary with a trend component (i.e. drift). Correlation refers to a joint probability distribution from which two variables are drawn. With non-stationary time series, successive values don’t come from the same distribution, so there’s no concept of correlation. By transforming it into returns, now you have a stationary time series and can do correlation analysis on it.

2 Likes