Converting Ehler's CGO From Loop to Array Formula

I understand that array-based formulas should be much faster than those using loops, but I am struggling to come up with any code that could accomplish that for Ehler's Center of Gravity Oscillator. How should I go about converting the following to an array-based solution?

function CGOscillator( Price, Length )
{
    Result = 0;
    for( i = length; i < BarCount; i++ )
    {
        Num = 0;
        Denom = 0;
        for( j = 0; j < Length; j++ )
        {
            Num = Num + ( 1 + j ) * Price[i - j];
            Denom = Denom + Price[i - j];
        }
        if( Denom != 0 )
            Result[i] = 100.0 * ( ( -Num / Denom ) + ( Length + 1 ) / 2 );
    }
    return Result;
}
function CGOscillator( Price, Length )
{ 
    /// @link https://tinyurl.com/y8t2chur
    Num = 0;
    for ( j = 0; j < Length; j++ )
        Num += (1 + j) * Ref(Price, -j);
    Denom = Sum(Price, Length);
    Result = 100 * (-Num / (Denom+1e-9) + (Length + 1) / 2);
    return Result;
}

x = CGOscillator( C, 20 );
Plot( x, "CGOscillator", colorRed );
8 Likes

This is awesome, thanks!

This one change cut my optimization time down by 53% (!!!)

Try this one. :slightly_smiling_face:

function CGOscillator( Price, Length )
{
	a = Length -1 ;
	Result = -a*50 + ((WMA(Price, a)*(a+a^2)*50) / (Sum(Price, Length) + 1e-9));
	return Result;
}

It took me a while to figure it out...
You need to understand that you can replace Num by

Sum(Price, Length)*Length - WMA(Price, a)*(a+a^2)*0.5;

It would be even faster if it were possible to weight the Price without recalculating the number of used Prices of the WMA.... please correct me if I'm wrong here :slight_smile:

2 Likes

... and can be simplefied to just

Result = (WMA(Price, a) / (Sum(Price, Length)+1e-9)*Length-1)*a*50;

For better readability

function CGOscillator( Price, Length )
{
	a = Length-1;
	num = WMA(Price, a)*Length;
	denom = Sum(Price, Length)+1e-9;
	result = (num / denom-1)*a*50;
	return result;
}

So bit similar to this one

5 Likes

This is outstanding. Thanks again. This is a further 10% faster than the original solution that you posted (AFL portion itself dropped from ~2200 in original looping code down to ~35 in the WMA version.)

Here is the original time summary:

Completed in 84.24 seconds. Number of rows: 10
( Timings: data: 61.22, setup: 0.14, afl: 2206.52, job: 101.63, lock: 50.85, pbt: 0.04, UI thread: 61.41, worker threads: 2308.15/2257.31 )

And here is with the final code:

Completed in 33.35 seconds. Number of rows: 10
( Timings: data: 31.96, setup: 0.06, afl: 37.46, job: 187.40, lock: 155.26, pbt: 0.05, UI thread: 32.07, worker threads: 224.86/69.60 )

Interestingly, my CPU usage during the optimization also dropped from ~98% with the looping code to just ~15% using the WMA.

Well. You can go even further:

function CGOscillator( Price, Length )
{
	a = Length-1;
	num = WMA(Price, a);
	denom = MA(Price, Length)+1e-9;
	result = (num / denom-1)*a*50;
	return result;
}

Theoretically you can even use "CGOscillator(Price, a)" and delete the top line.
You would get a little more speed, but must remember every time, that your oscillator has Length-1 as length (==a) ...

1 Like

OK, let's ride a dead horse and instead of few percent let's get some real further speed improvement.

Following version is 2 times faster then upper two ones.

function CGOscillator3( Price, Length )
{
    a = Length - 1;
    cnt = Min(BarCount,a);
    for (i = 0; i < cnt; i++) coeff[ i ] = i+1;
    num = FIR( Price, coeff, a );
    denom = MA( Price, Length ) + 1e-9;
    result = (num / denom - 1) * a * 50;
    return result;
}

14

4 Likes

@fxshrat Thanks for your code! :slight_smile:
I've tried it with AMA2 but didn't have any speed improvements (as far as I tried).

I wasn't aware that FIR is that much faster... I thought WMA is the more specalized function, thus should be at least run at equal speed as FIR...
The only reason as far as I see is, that FIR has a third (the second) specified argument.

But isn't the for-loop (or Cum(1) instead - shorter, but a little bit slower) (much) slower than the same internal loop?!
So my guess would be, that the WMA should be at least as fast as the FIR.
Can someone tell me why this isn't the case? :slight_smile:
I don't get it yet. Is the WMA implemented in another way? But if, then why?
(Interestingly the replacement of e.g. the MA with FIR doesn't give any further speed improvements.)