Rolling Mean/SD function, for log Returns, looping code

Hi,

I take the MA & StDev functions in AB for granted.
So I thought it would be a good exercise to try and code it using looping code in afl, (well that’s why it’s there :slight_smile:)

Unfortunately, I am having difficulties.
The afl code below is based on
http://www.wisestocktrader.com/indicators/1477-arnaud-legoux-moving-average
http://www.cs.mtu.edu/~shene/COURSES/cs201/NOTES/chap08/mov-avg.html
( I googled moving average/SD in C/C++ – various code made no sense to me.)

I would be grateful if someone could correct my code below for rolling mean, and hence/otherwise adapt it for a rolling SD, both on log Returns. I’m sure it’s very simple.

many thanks,
Amarjit


// Based on
/// @link http://www.wisestocktrader.com/indicators/1477-arnaud-legoux-moving-average
/// @link http://www.cs.mtu.edu/~shene/COURSES/cs201/NOTES/chap08/mov-avg.html


//===============================
window = 20;


// function rolling SD's of RETURNS
// --------------------------------

function ALMA(C, window)
{

	// set logRets to Null's
	w = Null;
	

	// logRets
	for(i = 1; i < Barcount; i++)
	{
	
		w[i] = ln(C[i]/C[i-1]);
		
	}


	// set output and intermim vectors to Null's
	outalma = Null;


	// loop through data
	for(i = 1; i < BarCount - Window + 1; i++)
	{

		a1Sum = 0;			
		
        
        if(i < Window)
        {
            outalma[i] = Null;
        }
        
        else
        
        {

		
			// ROLLING MEAN 
			for(j = i; j < i + Window - 1  ; j++)
			{
				   alSum = a1Sum + w[j];
			}
	 
			outalma[i] = alSum/Window;
	
		}

	
	}
	
		
	return outalma;	

		
}

	
ac_alma = ALMA(C, window);



//===============================
// Explore

logRet  = ln( C / Ref(C, -1) );

Filter = 1;
AddColumn(C, "C", 1.6);
AddColumn(logRet, "logRet", 1.6);
AddColumn(MA( logRet , window ),  "tjMA", 1.6);
AddColumn(StDev( logRet , window ),  "tjStDev", 1.6);

AddColumn(ac_alma, "ac_alma", 1.6);

Your primary error is that your secondary loop (the one that uses j as the loop counter) is moving forward through the data, not back. For example, if today was April 15 and the window size was 10, you should be finding the average of the bars from approximately April 2-15 (assuming some weekends in there). Instead, you are finding the average of the bars from April 15-28.

I did not fix this error and run your code to see if that resolves all issues. I assume you’re doing this just to learn how to write loops, so continuing to debug your own work will be a good exercise. :slight_smile:

  • Matt

Hi,

Thanks for the reply.
Here’s A solution.
Unfortunately there’s a “fudge” to output the SD result.
If anyone has a more elegant method I’d be interested.

There’s a way to return multiple values, rather than parameters list
http://www.amibroker.com/kb/2014/09/21/a-function-with-multiple-return-values/

My background is in FORTRAN.



// Based on
/// @link http://www.wisestocktrader.com/indicators/1477-arnaud-legoux-moving-average


//===============================
// function muSig for rolling MEAN's & SD's of LOG RETURNS
// -------------------------------------------------------


// Set parameters
window = Param("window", 10, 2, 1000, 1);
muOrSig = ParamList( "mu Or Sig:", "mu|Sig" );


price = C;



function muSig(price, window)
{

	// set logRets to Null's
	w = Null;
	

	// logRets
	for(i = 1; i < Barcount; i++)
	{
		w[i] = ln( price[i] / price[i-1] );	
	}


	// set outputs to Null's
	res0 = Null;
	res2 = Null;


	// loop through the Data
	for(i = 1; i < BarCount; i++)
	{

        
        // set leading output data to Null's
        if(i < window)
        {
        
            res0[i] = Null;
            res2[i] = Null;
            
        }
        
        
        else
        
        {

			// initialize counters to zero
			alSum = 0;		
			res1 = 0;	
			
			
			
			// ROLLING MEAN 
			for(j = 0; j < window; j++)
			{
			
				   // sum logRets over the window
				   alSum += w[i - window + j + 1];
				   
			}
	 
			// mean is res0
			res0[i] = alSum/window;
			
			
			
			
			
			// ROLLING SD POPULATION
			for(j = 0; j < window; j++)
			{
					
				   // sum squared deviations from mean over the window
				   res1 += ( w[i - window + j + 1] - res0[i] ) ^ 2;
				   
			}
	 			
			
			// sd population is res2
			res2[i] = sqrt(res1/window);
			
			// fudge adjust for leading point
			res2[window] = Null;
			
			
	
		}

	
	}
	
	
	// choose/swap res0 for MA and
	//             res2 for SD

	if ( muOrSig == "mu" )
	{
		return1 = res0;	
	}

	if ( muOrSig == "Sig" )
	{
		return1 = res2;	
	}


	return return1; 


		
}


// set function ready for explore	
ac_muSig = muSig(C, window);



//===============================
// Explore

logRet  = ln( C / Ref(C, -1) );

Filter = 1;
AddColumn(C, "C", 1.6);
AddColumn(logRet, "logRet", 1.6);
AddColumn(MA(logRet,window),  "tjMA", 1.6);
AddColumn(StDev(logRet,window),  "tjStDev", 1.6);

AddColumn(ac_muSig, "ac_muSig", 1.6);

To confirm, you’re writing this code simply as an exercise to get comfortable with looping code, correct? The most efficient solution by far is to use the array functions already built into AmiBroker, as these will run at the speed of compiled code, i.e. many times faster than any looping code you can write in AFL. Make sure that you understand which functions are array functions. For example, you don’t have to call ln from within a loop, because you can pass an entire array to it and calculate all bars at once. If that’s not obvious to you yet, then spend some more time in the tutorials and the help file. It will be time well spent!!

Matt

Yes, that’s right I am writing it simply as an exercise to get comfortable with looping code within AB.

Thanks for the recommendations & advice.

Amarjit