Help with calculation of CCI using loops

I want to chart the Commodity Channel Index in a separate pane and have it apply to a Relative Strength indicator instead of the normal Price Field. Since the built-in CCI function can only be applied to the Price Field, I have tried to code an alternate using loops.

Moderator comment: The original poster is wrong, built-in CCIa works with ANY array. All that is needed is reading the manual: see AFL Function Reference - CCI

I was able to duplicate Amibroker's function in a tedious way (the first part of the code below) by taking the absolute difference between the current period Moving Average of the specified lookback period and the sum of the differences between that and the Close price over the same time frame. However, my alternate calculation using loops to do this (so the lookback period could be easily varied) has not been successful as it does not correctly sum up the differences over the lookback period. I have tried umpteen ways to get it to work using _Trace, Explorations, searching the Forum, etc without achieving the correct result. Any help that someone could give pointing out what is wrong with my code below would be much appreciated.

Russell

///////////////////////////////////////////////////////////////////////////////////
//                   Calculate Raw CCI             
///////////////////////////////////////////////////////////////////////////////////
cciPer = 5;
tp = Close;	//(High+Low+Close)/3;
cciMA = MA( tp, cciPer );
///////////////////////////////////////////////////////////////////////////////////
//    Manual calculation used to verify Amibroker calculation
///////////////////////////////////////////////////////////////////////////////////
md1 = abs( cciMA - Close ) + abs( cciMA - Ref( C, -1 ) ) + abs( cciMA - Ref( C, -2 ) ) + abs( cciMA - Ref( C, -3 ) ) + abs( cciMA - Ref( C, -4 ) );
meanDev = md1 / cciPer;
cciRaw = ( tp - cciMA ) / ( .015 * meanDev );
///////////////////////////////////////////////////////////////////////////////////
//            My alternate calculation using loops
///////////////////////////////////////////////////////////////////////////////////
for( i = 0; i < BarCount; i++ )
{
    z = zum = 0;
    for( j = 0; j < cciPer; j++ )
    {
        printf( "i =: %g and j =: %g\n", i, j );
        printf( "Close =: %g\n", Ref( Close, j ) );
        printf( "cciMA =: %g\n", cciMA[i] );
        z = abs( cciMA[i] - Ref( Close, j ) );
        printf( "z =: %g\n", z );
        zum = Sum( z, cciPer );
        printf( "zum =: %g\n", zum );
        _TRACE( "zum =: " + zum );
    }
}
///////////////////////////////////////////////////////////////////////////////////
//               Specify Columns for Exploration
///////////////////////////////////////////////////////////////////////////////////
Filter = 1;
{
    if( Status( "actionex" ) == actionExplore )
        AddColumn( tp, "tp", 1.4, colorDarkYellow, -1 );

    AddColumn( cciMA, "cciMA", 1.4 );
    AddColumn( C, "close", 1.4 );
    AddColumn( md1, "md1", 1.4 );
    AddColumn( meanDev, "meanD", 1.2 );
    AddColumn( cciRaw, "CCI Raw", 1.4 );
    AddColumn( z, "z", 1.4 );
    AddColumn( zum, "zum", 1.4 );
}

@russman, I did not examine your code, but I wonder if you tried to use directly the CCIa() variant of the indicator that allows you to use your own "price" array overriding the default Avg - i.e. (H + L + C ) /3 field, as shown in the documentation examples.

5 Likes

@Russman, if you still want to do it with loops, here is a version of your code with some corrections (no attempt to improve it).
When you use a loop, you should access the array elements with the [ ] bracket operator (do not include array operations in loops).
(Results are slightly different from the native CCI, probably due to AmiBroker's internal calculations).

///////////////////////////////////////////////////////////////////////////////////
//                   Calculate Raw CCI             
///////////////////////////////////////////////////////////////////////////////////
cciPer = 5;
tp = Avg; // (High+Low+Close)/3;
cciMA = MA( tp, cciPer );
///////////////////////////////////////////////////////////////////////////////////
//    Manual calculation used to verify Amibroker calculation
///////////////////////////////////////////////////////////////////////////////////
md1 = abs( cciMA - tp ) + abs( cciMA - Ref( tp, -1 ) ) + abs( cciMA - Ref( tp, -2 ) ) + abs( cciMA - Ref( tp, -3 ) ) + abs( cciMA - Ref( tp, -4 ) );
meanDev = md1 / cciPer;
cciRaw = ( tp - cciMA ) / ( .015 * meanDev );
///////////////////////////////////////////////////////////////////////////////////
//            My alternate calculation using loops
///////////////////////////////////////////////////////////////////////////////////
zum = z = md2 = cciLp = null;
// for( i = cciPer; i < BarCount; i++ ) // to start as AB
for( i = cciPer-1; i < BarCount; i++ )  // calculates 1 bar more at the start of data
{
	zum[i] = 0;
    for( j = 0;  j < cciPer; j++)
    {
		idx = i-j;
        // _TRACEF( "i =: %g and j =: %g =: idx =: %g", i, j, idx );
        // _TRACEF( "TP =: %g", tp[idx]);
        // _TRACEF( "cciMA =: %g", cciMA[i]);
        z[idx] = abs( cciMA[i] - tp[idx] );
        // _TRACEF( "z =: %g", z[idx]);
        zum[i] += z[idx];
        // _TRACEF( "zum =: %g", zum[i] );
    }
    md2[i] = zum[i] / cciPer;
    // _TRACEF( "md =: %g", md2[i]);
    cciLp[i] = ( tp[i] - cciMA[i] ) / ( .015 * md2[i] );
    // _TRACEF( "cciLp =: %g", cciLp[i]);
    
}

CCI_AB = CCI(cciPer); 
///////////////////////////////////////////////////////////////////////////////////
//               Specify Columns for Exploration
///////////////////////////////////////////////////////////////////////////////////
if( Status( "actionex" ) == actionExplore ) {
	Filter = 1;
	AddColumn(CCI_AB, "AB CCI", 1.4);
    AddColumn( tp, "tp", 1.4);
	// AddColumn( C, "close", 1.4 );
    AddColumn( md1, "md1", 1.4 );
    AddColumn( meanDev, "meanD", 1.4 );
    AddColumn( cciRaw, "CCI Raw", 1.4 );
    AddColumn( z, "z Loop", 1.4 );
    AddColumn( md2, "md Loop", 1.4 );
    AddColumn( zum, "zum Loop ", 1.4 );
    AddColumn( cciLp, "CCI loop", 1.4 );
    SetSortColumns(-2);
}

// Plot to visually check
Plot(CCI_AB, "AB CCI", colorRed, styleLine, Null, Null, 0, 0, 7);
Plot(CCIRaw, "CCI Raw", colorBlue, styleLine, Null, Null, 0, 1, 5);
Plot(CCILp, "CCI Loop", colorAqua, styleLine, Null, Null, 0, 2, 1);

As I said in my previous message, you probably DO NOT need this at all; when possible, you should avoid nested loops, but I hope it will help in other similar situations in the future.

4 Likes

https://forum.amibroker.com/u/beppe,
Thank you for pointing out the CCIa() option. It is indeed a simple solution to accomplish what I wanted to do. In all the hours spent on this situation I did not discover this more flexible version of CCI.

Also, thank you for taking the time to re-write looping code and correct my errors so that the output matches the Amibroker CCI function. This forum is a wonderful place to share ideas and receive assistance and your willingness to help a struggling coder has given me a looping template that I will study and try to learn from so that I can take one more step towards becoming more capable with AFL.

Russman

1 Like

@Russman, thanks for your nice words.

Please keep in mind that the loop I provided is far from optimal (as was kindly indicated to me by another user's direct message) and could be significantly improved.

Indeed, I wrote in such way, since your code included many printf/trace statements, and I interpreted this as your attempt to follow step by step the various "individual" element assignments (also of "not strictly needed" intermediate values). So the focus was principally on the use of the bracket [ ] operator, which can also be used to create intermediate (in this case redundant) arrays (like I did with z).

I hope other users will find the opportunity to show the correct / best way to achieve the desired result. Still, I expect that seeing how to access individual elements of the arrays was a helpful example.

@beppe

I too would like to have another forum member take the time to illustrate a best or better way of writing this looping code.

In the meantime, I have much to learn from the helpful example that you provided.

@beppe,

I wanted to reply to you by private email but couldn't locate an address. Thank you for sharing the additional examples, they are brilliant. You have such a higher level of Amibroker knowledge than I do; it gives me something to aspire to.

Russman

@Russman, believe me: I still have a lot to learn too.

Fortunately, there are far more experienced users in this forum than me who very generously answer many users' questions with tips and brilliant code examples that are worth studying carefully to keep improving.

I prefer to avoid mentioning names so as not to forget anyone but just scroll through some past posts to understand who you need to follow carefully!

Thank you I too was looking to use cci in array of rsi with value when fn.

I got the hint

Regards

1 Like

Rather do like this:

///////////////////////////////////////////////////////////////////////////////////
//                   Calculate Raw CCI             
///////////////////////////////////////////////////////////////////////////////////
cciPer = 5;
tp = Avg; // (High+Low+Close)/3;
cciMA = MA( tp, cciPer);
///////////////////////////////////////////////////////////////////////////////////

//            My alternate calculation using loops
///////////////////////////////////////////////////////////////////////////////////
zum = Null;
// for( i = cciPer; i < BarCount; i++ ) // to start as AB
for ( i = cciPer-1; i < BarCount; i++ ) // calculates 1 bar more at the start of data
{	
	myma = cciMA[i];
    for ( j = 0, z = 0; j < cciPer; j++) {
        z += abs(myma - tp[i-j]);     
    }
    zum[i] = z;
    // _TRACEF( "zum =: %g", zum[i]);    
}

md2 = zum / cciPer;
cciLp = (tp - cciMA) / (.015 * md2);

So you can move several variables out of each part of nested loop because they are simply not being required to be in there.


BUT.....

There is way way simpler way if you want to use loops.
You do not need to iterate Barcount. Just use arrays.

///////////////////////////////////////////////////////////////////////////////////
cciPer = 5;
tp = Avg; // (High+Low+Close)/3;
cciMA = MA( tp, cciPer);
///////////////////////////////////////////////////////////////////////////////////

for ( j = 0, zum = 0; j < cciPer; j++) {
	zum += abs(cciMA - Ref(tp,-j));     
}
md2 = zum / cciPer;
cciLp = (tp - cciMA) / (.015 * md2);

CCI_AB = CCI(cciPer);// in-built CCI for comparison

// Plot to visually check
Plot(CCI_AB, "AB CCI", colorRed, styleLine, Null, Null, 0, 0, 7);
Plot(CCILp, "CCI Loop", colorAqua, styleLine, Null, Null, 0, 2, 1);

So loop needs just single line.

Still, as I wrote in my first post, the recommended approach is to avoid any loop and use the existing CCia function (all in a single line).

It's about the actual written loop(s) in this thread.
"Everyone" (who would look into manual) knows that you do not need loop for resulting CCI.
So it's no secret information having been found out just now.

Also, even if there was no CCIa variant, one could calculate ANY indicator that uses OHLC field indirectly using CUSTOM data by simply overriding (local copy) of OHLC data:

Open = your data
High = your data
Low = your data
Close = your data
custom_macd = MACD(12, 26 ); // ANY indicator here, will use custom data !

For this reason, re-inventing the wheel is NEVER needed.

1 Like

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