Distance travelled without intersection

I am trying to calculate longest distance that can reached High to High and Low to Low. My code (complete, runnable) below does the job well. But due to the nested loop it is slow. Is there a way to speed this up?
I keep hearing of faster running c++ code that can written and converted to dll. Please let me know if that is relevant here.

I have also attached a data file of 10 minutes interval.

_TRACE( "!CLEAR!" );
SetBarsRequired( sbrAll, sbrAll );
_SECTION_BEGIN( "Price" );
SetChartOptions( 0, chartShowArrows | chartShowDates | chartWrapTitle );
_N( Title = StrFormat( "{{NAME}} - {{INTERVAL}} {{DATE}} Open %g, Hi %g, Lo %g, Close %g (%.1f%%) {{VALUES}} ", O, H, L, C, SelectedValue( ROC( C, 1 ) ) ) );
PlotOHLC( O, H, L, C, "Close", colorRed, styleBar , Null, Null, 0, 1, 1 );
_SECTION_END();

constScoreTypeLL=1;constScoreTypeHH=2;
intersectionPrice = intersectionBar = 0;
function findIntersection( priceA, barIndxA, priceB, barIndxB, priceC, barIndxC, priceD, barIndxD )
{

    a0 = b0 = c0 = a1 = b1 = c1 = det = Null;
    // Line AB represented as a1x + b1y = c1
    a0 =  priceB - priceA;
    b0 =  barIndxA - barIndxB;
    c0 =  a0 * barIndxA + b0 * priceA;
    // Line CD represented as a2x + b2y = c2
    a1 = priceD - priceC;
    b1 = barIndxC - barIndxD;
    c1 = a1 * ( barIndxC ) + b1 * ( priceC );
    det = a0 * b1 - a1 * b0;

    intersectionBar = iif( det == 0, null, ( b1 * c0 - b0 * c1 ) / ( det + 1e-9 ) ) ;
    intersectionPrice =  iif( det == 0, null, ( ( a0 * c1 - a1 * c0 ) / ( det + 1e-9 ) ) );
                
     linesAreIntersecting = ( ( intersectionPrice > priceD && intersectionPrice < priceC || intersectionPrice < priceD && intersectionPrice > priceC )
                  && ( intersectionPrice > priceA && intersectionPrice < priceB || intersectionPrice < priceA && intersectionPrice > priceB ) )
                && ( ( intersectionBar < barIndxD || intersectionBar > barIndxC ) || ( intersectionBar > barIndxA || intersectionBar < barIndxB ) );

    intersectionPrice = IIf(linesAreIntersecting, intersectionPrice, Null);

    intersectionBar = IIf(linesAreIntersecting, int( intersectionBar ), Null);
}


function getBarScore(forwardSearchDistanceArray, type)
{
 //max search Distance is 100
barScore = 0;

for( i = 2; i < 102; i++ )
{
    intresectionReached = 0;

    for( j = 1; j < i; j++ ) //is there a way to get rid of this nested loop?
    {
        ReachedLastBar=Ref(BarIndex(), j+1)==Ref(BarIndex(), j);
        if(type==constScoreTypeHH){
        findIntersection( H, BarIndex(), IIf( Ref( H, i ) == H, Ref( H, i ) + 1e-3, Ref( H, i ) ), Ref( BarIndex(), i ), Ref( H, j ), Ref( BarIndex(), j ), Ref( L, j ) * 0.50, Ref( BarIndex(), j ));
        intresectionReached = IIf( intresectionReached, 1, !IsNull( intersectionBar ) );
        barScore = IIf( IsNull( intersectionBar ) && !intresectionReached && !ReachedLastBar && i<forwardSearchDistanceArray+2, IIf( j > barScore, j, barScore ), barScore );
        }
        else
        {
        findIntersection( L, BarIndex(), IIf( Ref( L, i ) == L, Ref( L, i ) - 1e-3, Ref( L, i ) ), Ref( BarIndex(), i ), Ref( H, j )*1.5, Ref( BarIndex(), j ), Ref( L, j ) , Ref( BarIndex(), j ));
        intresectionReached = IIf( intresectionReached, 1, !IsNull( intersectionBar ) );
        barScore = IIf( IsNull( intersectionBar ) && !intresectionReached && !ReachedLastBar && i<forwardSearchDistanceArray+2, IIf( j > barScore, j, barScore ), barScore );
        }
        //if(i==12)
		//_TRACE("i="+i+", j="+j+", isnull(intersectionBar)="+isnull(intersectionBar)+", intersectionBar="+intersectionBar+", isnull(intersectionPrice)="+isnull(intersectionPrice)+", intersectionPrice="+intersectionPrice);
    }
}
return(barScore);
}

scoreLL=getBarScore(50, constScoreTypeLL);
scoreHH=getBarScore(40, constScoreTypeHH);


Atr5 = ATR( 5 );

for( i = 0; i < BarCount; i++ ){
PlotTextSetFont( "" + scoreHH[i], "windings", 7, i , H[i] + Atr5[i] / 3, colorYellow );
PlotTextSetFont( "" + scoreLL[i], "windings", 7, i , L[i] - Atr5[i] / 3, colorYellow );
}

It is better to describe the GOAL/METHOD in human language than to try to "decipher" somebody's else code and "optimize" existing code. Please follow this advice: How to ask a good question

If you are using loops and definitely double nested loops it is very inefficient to call array functions from inside of double nested loop. General rule is:

Use array processing when possible and do it OUTSIDE THE LOOP.

If you need a loop and array processing at the same time make sure you are using SINGLE-nested loop.

If you are doing double nested loop and call array functions from inside - you are doing things wrong (because essentially you are doing triple nested loop O(N^3) complexity).

If you really need double nested loop, the only thing that you should do inside, is to use direct subscripts like array [ i + j]

Longest distance that can be reached without intersection by intervening price bar.

array [ i + j ] it has to called inside a i=1 to barcount-1 loop which i don't want to do.

You have to be precise and define the terms you are using. Nobody knows what you mean with "Itervening price bar" or "without intersection". Intersection with what?

Try to conceptualize the algorithm on piece of paper first. Graphical "thinking" gives better insight in what you are trying to do. Then think how many nested loops you really need. In 99% of cases you don't need anything more complex than double nested loop O(N^2)

see this image below. The number on top of the bar indicates distance that the line drawn from it can travel forward [High to High] without intersection. The number at the bottom of the bar indicates Low to Low figure for the same.

Yes. I draw everything on paper and come up with algorithm first. Only then i code. I tried doing it with 1 loop but not succeeded yet.

What are EXACT RULES that you use do you draw those trend lines, because there is nothing is said about that.
Instead of red line one could draw lines that are marked in turquoise color and then the results would be different:

If algorithm iterates into the future it is totally unreliable, as few days after there can be upward trend and then all previously found distances are simply wrong since there is a new high that does not cause intersection anymore.

I am trying to calculate longest distance without intersection like it states in the first line of this thread.

But you can't calculate that reliably. As new bars are added, there can be new high and all your previous calculations are wrong, since there is a new line to a new high

Basically it is terrible future leak and I don't know any use of that. It is worse than trading zig zag.

If algorithm iterates into the future it is totally unreliable, as few days after there can be upward trend and then all previously found distances are simply wrong since there is a new high that does not cause intersection anymore.

You are right and that's why we have forwardSearchDistanceArray. First the rough contours of the pattern are found and then this function is used to validate the legs of the pattern. The forwardSearchDistanceArray is bound by (patternFound Barindex minus current bar barindex() ). So this is future proof.

As I wrote - the idea has terrible future leak and I would not base any trading on it.
Regarding AFL - you can write that without calling any functions, something along these lines.

for( i = 0; i < BarCount - 1; i++ )
{
  b = high[ i ];

   for( j = BarCount -1; j > i; j-- )
   {
        // the trendline is given by equation
       //        y = ax + b;
       //  where x would be (j - x)
       //             b would be high[ i ]
       //             a would be (high[ j ] - high[ i ] ) / (j - i );
       a = (high[ j ] - high[ i ] ) / (j - i );
       // intersection check
       for( k = i; k < j; k++ ) 
       {  
            y = a * ( k - i ) + b;
            if( high[ k ] <  y )
            { 
               // intersection found
            }
      }
   }
}
1 Like

Thanks, Your code has 3 nested loops. I will check it out anyway. All that matters is better performance. I will post back the code profile results Once i test this code.

Regarding future leak. There is none. no future leak. because the search distance is limited to the last bar of the pattern. A pattern is found, provincially, after its last bar is formed and then i go back in time and run this function with forwardSearchDistanceArray limited to the last bar of the pattern. SO NO THERE IS NO FUTURE LEAK WHATSOEVER IN HOW I USE IT.

Yes the skeleton code of course uses 3 nested loops because:

  1. For illustration purposes it calculates trendlines for ALL BARS (and my understanding is that in reality outmost loop would be removed and you would call it from actual highs on charts (such as peaks)
  2. You asked about possiblity to write in C++ and I provided you the code that would work with C++ as C++ has NO AFL FUNCTIONS
  3. It does NOT call array functions inside double nested loop and inner loop is much smaller effectively leading to LESS calculation and possibly more efficient code. There is a whole world of difference between running a loop for few elements only as opposed to processing entire array. In most cases the inner loop data is totally in Level-1 processor cache. It gains tremendously from locality of reference especially if you would put that code in C++.

The purpose of the code was to show solution WITHOUT calling functions from nested loops. The code I presented would very efficiently run in C++. And that is what you asked for in original post, as far as I remember?

You should learn how to express GRATITUDE when someone spends part of their lifetime (even if it is half an hour) helping you for free.

And yes, there is a future leak.

Caveat for ANYBODY trying the same thing: DO NOT USE "patterns" that look into the future for trading. You will lose money.

I did thank you. That was the first word of my last post. What more do you want?

Yes after testing I can see that your code is 10 times faster than mine. see below
perf1

_TRACE( "!CLEAR!" );
SetBarsRequired( sbrAll, sbrAll );
_SECTION_BEGIN( "Price" );
SetChartOptions( 0, chartShowArrows | chartShowDates | chartWrapTitle );
_N( Title = StrFormat( "{{NAME}} - {{INTERVAL}} {{DATE}} Open %g, Hi %g, Lo %g, Close %g (%.1f%%) {{VALUES}} ", O, H, L, C, SelectedValue( ROC( C, 1 ) ) ) );
PlotOHLC( O, H, L, C, "Close", colorRed, styleBar , Null, Null, 0, 1, 1 );
_SECTION_END();

barScore = barScore10=barScore11=barScore12=barScore13=barScore14=barScore35=barScore36=barScore37=barScore38=barScore39=intersectionFound = 0;

//maxDistance=29;
for( i = 0; i < BarCount - 1; i++ ) //source bar
{
    b = high[ i ];
    maxLookup = Min( i + 40, BarCount - 1 );
    m = 0;

    for( j = i + 2; j <= maxLookup ; j++ ) //destination bar
    {
        // the trendline is given by equation
        //        y = ax + b;
        //  where x would be (j - x)
        //             b would be high[ i ]
        //             a would be (high[ j ] - high[ i ] ) / (j - i );
        a = ( high[ j ] - high[ i ] ) / ( j - i );
        intersectionFound[i] = m = 0;

        // intersection check
        for( k = i + 1; k < j; k++ ) //intervening price bar between source and destination
        {

            y = a * ( k - i ) + b;

            if( high[ k ] >  y )
            {
                // intersection found
                intersectionFound[i] = 1;
                break;
            }


        }

        if( intersectionFound[i] == 0 )
        {
            barScore[i] = j - i - 1;
            //_TRACE("i="+i+", j="+j+", j-i="+(j-i)+", barScore[i]="+barScore[i]);
            barScore10[i]=IIf((j - i)<=10 && barScore[i]>barScore10[i], barScore[i], barScore10[i]);
            barScore11[i]=IIf((j - i)<=11 && barScore[i]>barScore11[i], barScore[i], barScore11[i]);
            barScore12[i]=IIf((j - i)<=12 && barScore[i]>barScore12[i], barScore[i], barScore12[i]);
            barScore13[i]=IIf((j - i)<=13 && barScore[i]>barScore13[i], barScore[i], barScore13[i]);
            barScore14[i]=IIf((j - i)<=14 && barScore[i]>barScore14[i], barScore[i], barScore14[i]);
            
            barScore35[i]=IIf((j - i)<=35 && barScore[i]>barScore35[i], barScore[i], barScore35[i]);
            barScore36[i]=IIf((j - i)<=36 && barScore[i]>barScore36[i], barScore[i], barScore36[i]);
            barScore37[i]=IIf((j - i)<=37 && barScore[i]>barScore37[i], barScore[i], barScore37[i]);
            barScore38[i]=IIf((j - i)<=38 && barScore[i]>barScore38[i], barScore[i], barScore38[i]);
            barScore39[i]=IIf((j - i)<=39 && barScore[i]>barScore39[i], barScore[i], barScore39[i]);
             //VarSet("barScore"+(j-i), IIf(BarIndex()==i && barScore>VarGet("barScore"+(j-i)), barScore, VarGet("barScore"+(j-i))));
             


        }

    }
}

_TRACE( "barScore=" + barScore + ", barScore35=" + barScore35 + ", barScore36=" + barScore36+ ", barScore37=" + barScore37+ ", barScore38=" + barScore38+ ", barScore39=" + barScore39+ ", barindex()=" + BarIndex() + ", i=" + i + ", barcount=" + barcount );
_TRACE( "barScore10=" + barScore10 + ", barScore11=" + barScore11+ ", barScore12=" + barScore12+ ", barScore13=" + barScore13+ ", barScore14=" + barScore14 );

I have a question. now that this code is not running as a function I would have to capture the barScore for all possible ForwardSearchDistances and then use them as and when needed in my afl.

        barScore10[i]=IIf((j - i)<=10 && barScore[i]>barScore10[i], barScore[i], barScore10[i]);
            barScore11[i]=IIf((j - i)<=11 && barScore[i]>barScore11[i], barScore[i], barScore11[i]);
            barScore12[i]=IIf((j - i)<=12 && barScore[i]>barScore12[i], barScore[i], barScore12[i]);
            barScore13[i]=IIf((j - i)<=13 && barScore[i]>barScore13[i], barScore[i], barScore13[i]);
            barScore14[i]=IIf((j - i)<=14 && barScore[i]>barScore14[i], barScore[i], barScore14[i]);

I have captured for a few like barScore10, barScore11, barScore12 etc. barScore10 will have the score for that bar for a search distance equal to 10, barscore11 will be for search distance 11 so on and so forth. My question is there a ore elegant way to write this like using a varSet("barScore"+i, ...) ?

perf2
This the code profile for my code

Yes you could use VarSet()/VarGet() to get dynamic variable names. You could also use matrix. Matrix would probably look more elegant. Matrices are described in the AFL Reference Manual

I am looking at matrix but it seems it is scalar in nature as in one matrix per chart. Here i need barScore2 to barScoreN stored for every bar as every bar will have different value.
Can you please give me an example.

No matrix is not scalar. Matrix is two-dimensional.