Code that uses REMAP function

little gift. Code uses REMAP function. It looks for a similar pattern in the past data. Best to play around with it first with EOD data because of speed. If you use it with intraday data set it to "Trigger Mode" in the Param window and then press the "Calculation Trigger" because else it is too slow. Trigger mode only calculates it 1 time when you push the calculation trigger. You can "Lock Pattern In Place" so you can scroll back to see which patterns fitted the best. If the code crashes the pattern is probably locked and you do reset all or clear all. I added 4 different patterns. Pattern 3 uses Bollinger bands which looks promising. I also made an intraday version to forecast intraday futures. Possibly will post that later. I added 2 methods of normalization, see "Normalization Mode". Forecast still pretty unreliable so code is just for educational purposes :slight_smile: I was just playing around with the REMAP function.

So best to use EOD data first and play with it, see also the Parameter window. Hope you like it.

Chart shows ES EOD data with 1 one the best fits visible in the chart. It is the 3rd best fit it found in default mode.

SetBarsRequired( sbrAll, sbrAll );

per = Param( "Pattern Number of Bars", 10, 1, 50, 1 );
npred = Param( "Number of Bars to Predict", 10, 1, 50, 1 );
nfits = Param( "Number of Fits", 3, 1, 50, 1 );
calculationMode = ParamList( "Calculation Mode", "Continuous Mode|Trigger Mode", 0 ); // continuous gets real time updates
calculationTrigger = ParamTrigger( "Calculation Trigger", "Press Here" ); // works when calculation mode is in Trigger Mode
patternType = ParamList( "Pattern Type", "Pattern 1|Pattern 2|Pattern 3|Pattern 4", 0 );
displayType = ParamList( "Display Type", "Show Average Fit Plus Forcasts|Show Average Fit Standard Deviation Cone", 1 );
normalizationmode = ParamList( "Normalization mode", "Percentage|0 to 1", 1 );
showbarnumbering = ParamToggle( "Show Bar Numbering", "Not Show|Show", 1 );
ft = ParamList( "Font Type", "Tahoma|Arial Black|Verdana|Courier New|Times New Roman|Open Sans|Segoe UI|DejaVu Sans", 1 );
sz = Param( "Font Size", 10, 8, 50, 1 );
lockToggle = ParamToggle( "Lock Pattern in Place", "Off|On", 0 );
clearAll = ParamTrigger( "Clear All", "Clear All" );
sintest = ParamToggle( "SIN TEST", "Not Show|Show", 0 );

if( clearAll )
{
    StaticVarRemove( "*" );
    lockToggle = 0;
}

// initialisations
bi = BarIndex();
distance = 1e12;
patternText = "";
hper = lper = Null;
bestFitsStorageArray = 0;
idx0 = ss = shift = 0;
topcone = botcone = Null;
yymin = Status( "axisminy" );
yymax = Status( "axismaxy" );
// Pattern arrays
patO = cpatO = 0;
patH = cpatH = 0;
patL = cpatL = 0;
patC = cpatC = 0;
patBBOT = cpatBBOT = 0;
patBTOP = cpatBTOP = 0;
patMA1 = cpatMA1 = 0;
patMA2 = cpatMA2 = 0;
patMA3 = cpatMA3 = 0;
bbot = btop = Null;
ma1 = ma2 = ma3 = Null;

// lock pattern in place
if( lockToggle == 0 )
{
    idx0 = Nz( SelectedValue( BarIndex() ) );
    StaticVarSet( "idx0", idx0 );
}

if( lockToggle == 1 )
{
    idx0 = Nz( StaticVarGet( "idx0" ) );
}

////////////////////////////////////////////////////
// For testing purposes
pi = 3.14159265359;
function sinfunc( wavelength, phase )
{
    sinusFunctionArray = 2 * sin( 2 * pi * Cum( 1 ) / wavelength  + phase )
                         + 1 * sin( 2 * pi * Cum( 1 ) / ( trunc( wavelength / 2 ) )  + phase / 2 )
                         + 0.5 * sin( 2 * pi * Cum( 1 ) / ( trunc( wavelength / 4 ) )  + phase / 4 )
                         + 1 * sin( 2 * pi * Cum( 1 ) / ( trunc( wavelength * 50 ) )  + phase / 8 );
    //+ 0.75 * mtRandomA( seed = 1 );
    return sinusFunctionArray;
}

if( sintest )
{
    O = H = L = C = sinfunc( 30, pi );
}

////////////////////////////////////////////////////

if( patternType == "Pattern 1" )
{
    patternText = "1 variable C";
    hper = HHV( C, per );
    lper = LLV( C, per );
}

if( patternType == "Pattern 2" )
{
    patternText = "4 variables O, H, L, C";
    hper = HHV( H, per );
    lper = LLV( L, per );
}

if( patternType == "Pattern 3" )
{
    patternText = "3 variables, btop, bbot, and C";
    btop = BBandTop( C, 20, 2 );
    bbot = BBandBot( C, 20, 2 );
    hper = Max( HHV( C, per ), Max( HHV( btop, per ), HHV( bbot, per ) ) );
    lper = Min( LLV( C, per ), Min( LLV( btop, per ), LLV( bbot, per ) ) );
}

if( patternType == "Pattern 4" )
{
    patternText = "4 variables, ma1, ma2, ma3 and C";
    ma1 = MA( C, 20 );
    ma2 = MA( C, 50 );
    ma3 = MA( C, 200 );
    hper = Max( HHV( C, per ), Max( HHV( ma1, per ), Max( HHV( ma2, per ), HHV( ma3, per ) ) ) );
    lper = Min( LLV( C, per ), Min( LLV( ma1, per ), Min( LLV( ma2, per ), LLV( ma3, per ) ) ) );
}

function patternDefinition1()
{
    for( z = 0; z < per; z++ )
    {
        x = idx0 - per + z + 1;

        if( normalizationmode == "0 to 1" )
        {
            patC[z] = Remap( C[x], lper[idx0], hper[idx0], 0, 1 );
        }

        if( normalizationmode == "Percentage" )
        {
            patC[z] = Remap( C[x], lper[idx0], hper[idx0], 0, SafeDivide( hper[idx0] - lper[idx0], lper[idx0] ) * 100 );
        }

        if( showbarnumbering )
            PlotTextSetFont( "" + z, ft, sz, x, yymin, colorBlack, colorWhite, 0 );
    }

    for( i = 2 * per; i < idx0 - per; i++ )
    {
        dis = 0;

        for( z = 0; z < per; z++ )
        {
            x = i - per + z + 1;

            if( normalizationmode == "0 to 1" )
            {
                cpatC[z] = Remap( C[x], lper[i], hper[i], 0, 1 );
            }

            if( normalizationmode == "Percentage" )
            {
                cpatC[z] = Remap( C[x], lper[i], hper[i], 0, SafeDivide( hper[i] - lper[i], lper[i] ) * 100 );
            }

            // avoid parts with bad data
            if( lper[x] == hper[x] )
            {
                dis = 1e12;
            }
            else
            {
                dis = sqrt( ( cpatC[z] - patC[z] ) ^ 2 ) + dis;
            }
        }

        distance[i] = SafeDivide( dis, per ); // average distance per bar
    }

    StaticVarSet( "distance", distance );
}

function patternDefinition2()
{
    for( z = 0; z < per; z++ )
    {
        x = idx0 - per + z + 1;

        if( normalizationmode == "0 to 1" )
        {
            patO[z] = Remap( O[x], lper[idx0], hper[idx0], 0, 1 );
            patH[z] = Remap( H[x], lper[idx0], hper[idx0], 0, 1 );
            patL[z] = Remap( L[x], lper[idx0], hper[idx0], 0, 1 );
            patC[z] = Remap( C[x], lper[idx0], hper[idx0], 0, 1 );
        }

        if( normalizationmode == "Percentage" )
        {
            hh = SafeDivide( hper[idx0] - lper[idx0], lper[idx0] ) * 100;
            patO[z] = Remap( O[x], lper[idx0], hper[idx0], 0, hh );
            patH[z] = Remap( H[x], lper[idx0], hper[idx0], 0, hh );
            patL[z] = Remap( L[x], lper[idx0], hper[idx0], 0, hh );
            patC[z] = Remap( C[x], lper[idx0], hper[idx0], 0, hh );
        }

        if( showbarnumbering )
            PlotTextSetFont( "" + z, ft, sz, x, yymin, colorBlack, colorWhite, 0 );
    }

    for( i = 2 * per; i < idx0 - per; i++ )
    {
        dis = 0;

        for( z = 0; z < per; z++ )
        {
            x = i - per + z + 1;

            if( normalizationmode == "0 to 1" )
            {
                cpatO[z] = Remap( O[x], lper[i], hper[i], 0, 1 );
                cpatH[z] = Remap( H[x], lper[i], hper[i], 0, 1 );
                cpatL[z] = Remap( L[x], lper[i], hper[i], 0, 1 );
                cpatC[z] = Remap( C[x], lper[i], hper[i], 0, 1 );
            }

            if( normalizationmode == "Percentage" )
            {
                hh = SafeDivide( hper[i] - lper[i], lper[i] ) * 100;
                cpatO[z] = Remap( O[x], lper[i], hper[i], 0, hh );
                cpatH[z] = Remap( H[x], lper[i], hper[i], 0, hh );
                cpatL[z] = Remap( L[x], lper[i], hper[i], 0, hh );
                cpatC[z] = Remap( C[x], lper[i], hper[i], 0, hh );
            }

            // avoid parts with bad data
            if( lper[x] == hper[x] )
            {
                dis = 1e12;
            }
            else
            {
                dis = sqrt( ( cpatO[z] - patO[z] ) ^ 2
                            + ( cpatH[z] - patH[z] ) ^ 2
                            + ( cpatL[z] - patL[z] ) ^ 2
                            + ( cpatC[z] - patC[z] ) ^ 2 ) + dis;
            }
        }

        distance[i] = SafeDivide( dis, per ); // average distance per bar
    }

    StaticVarSet( "distance", distance );
}

function patternDefinition3()
{
    for( z = 0; z < per; z++ )
    {
        x = idx0 - per + z + 1;

        if( normalizationmode == "0 to 1" )
        {
            patC[z] = Remap( C[x], lper[idx0], hper[idx0], 0, 1 );
            patBBOT[z] = Remap( bbot[x], lper[idx0], hper[idx0], 0, 1 );
            patBTOP[z] = Remap( btop[x], lper[idx0], hper[idx0], 0, 1 );
        }

        if( normalizationmode == "Percentage" )
        {
            hh = SafeDivide( hper[idx0] - lper[idx0], lper[idx0] ) * 100;
            patC[z] = Remap( C[x], lper[idx0], hper[idx0], 0, hh );
            patBBOT[z] = Remap( bbot[x], lper[idx0], hper[idx0], 0, hh );
            patBTOP[z] = Remap( btop[x], lper[idx0], hper[idx0], 0, hh );
        }

        if( showbarnumbering )
            PlotTextSetFont( "" + z, ft, sz, x, yymin, colorBlack, colorWhite, 0 );
    }

    for( i = ( 2 * per + 20 ); i < idx0 - per; i++ )
    {
        dis = 0;

        for( z = 0; z < per; z++ )
        {
            x = i - per + z + 1;

            if( normalizationmode == "0 to 1" )
            {
                cpatC[z] = Remap( C[x], lper[i], hper[i], 0, 1 );
                cpatBBOT[z] = Remap( bbot[x], lper[i], hper[i], 0, 1 );
                cpatBTOP[z] = Remap( btop[x], lper[i], hper[i], 0, 1 );
            }

            if( normalizationmode == "Percentage" )
            {
                hh = SafeDivide( hper[i] - lper[i], lper[i] ) * 100;
                cpatC[z] = Remap( C[x], lper[i], hper[i], 0, hh );
                cpatBBOT[z] = Remap( bbot[x], lper[i], hper[i], 0, hh );
                cpatBTOP[z] = Remap( btop[x], lper[i], hper[i], 0, hh );
            }

            // avoid parts with bad data
            if( lper[x] == hper[x] )
            {
                dis = 1e12;
            }
            else
            {
                dis = sqrt( ( cpatC[z] - patC[z] ) ^ 2
                            + ( cpatBBOT[z] - patBBOT[z] ) ^ 2
                            + ( cpatBTOP[z] - patBTOP[z] ) ^ 2 ) + dis;
            }
        }

        distance[i] = SafeDivide( dis, per ); // average distance per bar
    }

    StaticVarSet( "distance", distance );
}

function patternDefinition4()
{
    for( z = 0; z < per; z++ )
    {
        x = idx0 - per + z + 1;

        if( normalizationmode == "0 to 1" )
        {
            patMA1[z] = Remap( ma1[x], lper[idx0], hper[idx0], 0, 1 );
            patMA2[z] = Remap( ma2[x], lper[idx0], hper[idx0], 0, 1 );
            patMA3[z] = Remap( ma3[x], lper[idx0], hper[idx0], 0, 1 );
            patC[z] = Remap( C[x], lper[idx0], hper[idx0], 0, 1 );
        }

        if( normalizationmode == "Percentage" )
        {
            hh = SafeDivide( hper[idx0] - lper[idx0], lper[idx0] ) * 100;
            patMA1[z] = Remap( ma1[x], lper[idx0], hper[idx0], 0, hh );
            patMA2[z] = Remap( ma2[x], lper[idx0], hper[idx0], 0, hh );
            patMA3[z] = Remap( ma3[x], lper[idx0], hper[idx0], 0, hh );
            patC[z] = Remap( C[x], lper[idx0], hper[idx0], 0, hh );
        }

        if( showbarnumbering )
            PlotTextSetFont( "" + z, ft, sz, x, yymin, colorBlack, colorWhite, 0 );
    }

    for( i = 300; i < idx0 - per; i++ )
    {
        dis = 0;

        for( z = 0; z < per; z++ )
        {
            x = i - per + z + 1;

            if( normalizationmode == "0 to 1" )
            {
                cpatMA1[z] = Remap( ma1[x], lper[i], hper[i], 0, 1 );
                cpatMA2[z] = Remap( ma2[x], lper[i], hper[i], 0, 1 );
                cpatMA3[z] = Remap( ma3[x], lper[i], hper[i], 0, 1 );
                cpatC[z] = Remap( C[x], lper[i], hper[i], 0, 1 );
            }

            if( normalizationmode == "Percentage" )
            {
                hh = SafeDivide( hper[i] - lper[i], lper[i] ) * 100;
                cpatMA1[z] = Remap( ma1[x], lper[i], hper[i], 0, hh );
                cpatMA2[z] = Remap( ma2[x], lper[i], hper[i], 0, hh );
                cpatMA3[z] = Remap( ma3[x], lper[i], hper[i], 0, hh );
                cpatC[z] = Remap( C[x], lper[i], hper[i], 0, hh );
            }

            // avoid parts with bad data
            if( lper[x] == hper[x] )
            {
                dis = 1e12;
            }
            else
            {
                dis = sqrt( ( cpatMA1[z] - patMA1[z] ) ^ 2
                            + ( cpatMA2[z] - patMA2[z] ) ^ 2
                            + ( cpatMA3[z] - patMA3[z] ) ^ 2
                            + ( cpatC[z] - patC[z] ) ^ 2 ) + dis;
            }
        }

        distance[i] = SafeDivide( dis, per ); // average distance per bar
    }

    StaticVarSet( "distance", distance );
}

if( calculationMode == "Continuous Mode" )
{
    if( patternType == "Pattern 1" )
    {
        patternDefinition1();
    }

    if( patternType == "Pattern 2" )
    {
        patternDefinition2();
    }

    if( patternType == "Pattern 3" )
    {
        patternDefinition3();
    }

    if( patternType == "Pattern 4" )
    {
        patternDefinition4();
    }
}

if( calculationMode == "Trigger Mode" )
{
    if( calculationTrigger )
    {
        Say( "Calculate Fit" );

        if( patternType == "Pattern 1" )
        {
            patternDefinition1();
        }

        if( patternType == "Pattern 2" )
        {
            patternDefinition2();
        }

        if( patternType == "Pattern 3" )
        {
            patternDefinition3();
        }

        if( patternType == "Pattern 4" )
        {
            patternDefinition4();
        }

        Say( "Finished" );
    }
}

distance = Nz( StaticVarGet( "distance" ) );
sdistance = Sort( distance, 0, -1, False ); // return sorted value
sdistancei = Sort( distance, 0, -1, True ); // return the index corresponding to the sorted value

SetChartBkColor( colorBlack );
SetChartOptions( 0, chartShowDates );
Plot( C, "", colorWhite, stylecandle, Null, Null, 0, 0, 1 );
Plot( btop, "", colorRed, styleLine | styleNoLabel | styleNoRescale, Null, Null, 0, -2, 1 );
Plot( bbot, "", colorGreen, styleLine | styleNoLabel | styleNoRescale, Null, Null, 0, -2, 1 );
Plot( ma1, "", colorRed, styleLine | styleNoLabel | styleNoRescale, Null, Null, 0, -2, 1 );
Plot( ma2, "", colorGreen, styleLine | styleNoLabel | styleNoRescale, Null, Null, 0, -2, 1 );
Plot( ma3, "", colorBlue, styleLine | styleNoLabel | styleNoRescale, Null, Null, 0, -2, 1 );

// function helps to avoid selecting 2 patterns within a distance of "per" bars
function testValidity( aa, disi, per )
{
    val = 1;

    for( i = 0; i < 50; i++ )
    {
        if( aa[i] == 0 AND i == 0 )
        {
            val = 1;
            break;
        }
        else
            if( abs( aa[i] - disi ) < per )
            {
                val = 0;
                break;
            }
            else
                if( disi <= per )
                {
                    val = 0;
                    break;
                }
    }

    return val;
}

cnt = 0;

for( i = 0; i < BarCount; i++ )
{
    fitdummyY = 0;
    forcastdummyY = 0;

    // avoid selecting 2 patterns within a distance of per bars, use testValidity
    if( testValidity( bestFitsStorageArray, sdistancei[i], per ) )
    {
        bestFitsStorageArray[cnt] = idx = sdistancei[i];
        shift = npred - Min( BarCount - 1 - idx0, npred );

        for( j = 0; j < per; j++ )
        {
            x = idx0 + j - per + 1;
            x1 = idx + j - per + 1;
            //_TRACE( "x1: " + x1 );
            y = Remap( C[x1], lper[idx], hper[idx], lper[idx0], hper[idx0] );
            fitdummyY[x] = y;
        }

        StaticVarSet( "fit" + cnt, fitdummyY );

        for( j = per; j < per + npred; j++ )
        {
            x = idx0 + j - per + 1 - shift;
            x1 = idx + j - per + 1;
            y = Remap( C[x1], lper[idx], hper[idx], lper[idx0], hper[idx0] );
            forcastdummyY[x] = y;
        }

        StaticVarSet( "forcast" + cnt, forcastdummyY );
        cnt = cnt + 1;
    }

    if( cnt > nfits )
    {
        StaticVarSet( "bestFitsStorageArray", bestFitsStorageArray );
        break;
    }
}

if( displayType == "Show Average Fit Plus Forcasts" )
{
    totfit = 0;
    totforcast = 0;

    for( i = 0; i < nfits; i++ )
    {
        ff = StaticVarGet( "fit" + i );
        Plot( IIf( ff, ff, Null ), "", colorAqua, styleDashed | styleNoRescale | styleNoLabel, Null, Null, 0, 0, 1 );
        totfit = totfit + StaticVarGet( "fit" + i );
        ff = StaticVarGet( "forcast" + i );
        Plot( IIf( ff, ff, Null ), "", colorViolet, styleDashed | styleNoRescale | styleNoLabel, Null, Null, shift, 0, 1 );
        totforcast = totforcast + StaticVarGet( "forcast" + i );
        PlotTextSetFont( "" + ( i + 1 ), ft, sz, idx0 + 1 + npred, ff[idx0 + npred - shift], colorBlack, colorWhite, 0 );
    }

    // average fit
    totfit = totfit / nfits;
    Plot( IIf( totfit, totfit, Null ), "", colorAqua, styleDots | styleNoRescale | styleNoLabel, Null, Null, 0, 1, 3 );

    // average forcast
    totforcast = totforcast / nfits;
    Plot( IIf( totforcast, totforcast, Null ), "", colorViolet, styleDots | styleNoRescale | styleNoLabel, Null, Null, shift, 1, 3 );
}

if( displayType == "Show Average Fit Standard Deviation Cone" )
{
    totfit = 0;
    totforcast = 0;

    for( i = 0; i < nfits; i++ )
    {
        ff = StaticVarGet( "fit" + i );
        totfit = totfit + StaticVarGet( "fit" + i );
        ff = StaticVarGet( "forcast" + i );
        totforcast = totforcast + StaticVarGet( "forcast" + i );
    }

    // average fit
    totfit = totfit / nfits;
    Plot( IIf( totfit, totfit, Null ), "", colorAqua, styleDots | styleNoRescale | styleNoLabel, Null, Null, 0, 1, 3 );

    // average forcast
    totforcast = totforcast / nfits;
    Plot( IIf( totforcast, totforcast, Null ), "", colorViolet, styleDots | styleNoRescale | styleNoLabel, Null, Null, shift, 1, 3 );

    // error cone forecast
    for( i = 0; i < npred; i++ )
    {
        ss = 0;
        x = idx0 + 1 + i;
        shift = npred - Min( BarCount - 1 - idx0, npred );

        for( j = 0; j < nfits; j++ )
        {
            ff = StaticVarGet( "forcast" + j );
            ss = ( ff[x - shift] - totforcast[x - shift] ) ^ 2 + ss;
        }

        topcone[x - shift] = totforcast[x - shift] + sqrt( SafeDivide( ss, nfits ) );
        botcone[x - shift] = totforcast[x - shift] - sqrt( SafeDivide( ss, nfits ) );
    }

    topcone = IIf( 0, Null, topcone );
    botcone = IIf( 0, Null, botcone );
    PlotOHLC( topcone, topcone, botcone, botcone, "", ColorRGB( 0, 0, 60 ), styleCloud | styleNoLabel | styleNoRescale, Null, Null, shift, -10, 1 );
    Plot( topcone, "", colorblue, styleLine | styleNoLabel | styleNoRescale, Null, Null, shift, -10, 3 );
    Plot( botcone, "", colorblue, styleLine | styleNoLabel | styleNoRescale, Null, Null, shift, -10, 3 );

    // error cone data fits
    topcone = botcone = Null;

    for( i = 0; i < per; i++ )
    {
        ss = 0;
        x = idx0 - per + i + 1;
        //shift = npred - Min( BarCount - 1 - idx0, npred );
        shift = 0;

        for( j = 0; j < nfits; j++ )
        {
            ff = StaticVarGet( "fit" + j );
            ss = ( ff[x - shift] - totfit[x - shift] ) ^ 2 + ss;
        }

        topcone[x - shift] = totfit[x - shift] + sqrt( SafeDivide( ss, nfits ) );
        botcone[x - shift] = totfit[x - shift] - sqrt( SafeDivide( ss, nfits ) );
    }

    topcone = IIf( 0, Null, topcone );
    botcone = IIf( 0, Null, botcone );
    PlotOHLC( topcone, topcone, botcone, botcone, "", ColorRGB( 0, 0, 60 ), styleCloud | styleNoLabel | styleNoRescale, Null, Null, shift, -10, 1 );
    Plot( topcone, "", colorblue, styleLine | styleNoLabel | styleNoRescale, Null, Null, shift, -10, 3 );
    Plot( botcone, "", colorblue, styleLine | styleNoLabel | styleNoRescale, Null, Null, shift, -10, 3 );
}

// draw box around pattern that needs to be fitted
pat = IIf( idx0 == BarIndex(), 1, 0 );
Plot( pat, "", colorYellow, styleDashed | styleHistogram | styleOwnScale | styleNoLabel, 0, 1, 0, -3, 1 );
GfxSetZOrder( -2 );
GfxSetCoordsMode( 1 );
GfxSelectSolidBrush( ColorRGB( 50, 50, 10 ) );
GfxSelectPen( ColorYellow, 3, 0 );

if( !IsEmpty( hper[idx0] ) AND !IsEmpty( lper[idx0] ) )
    GfxPolyline( idx0, hper[idx0], idx0, lper[idx0], idx0 - per + 1, lper[idx0], idx0 - per + 1, hper[idx0], idx0, hper[idx0] );

PlotTextSetFont( "" + patternText, ft, sz, idx0 + 1, hper[idx0], colorBlack, colorWhite, 0 );

// show boxes around the best fits
GfxSelectPen( colorAqua, 3, 0 );

for( i = 0; i < nfits; i++ )
{
    idx0 = bestFitsStorageArray[i];

    if( !IsEmpty( hper[idx0] ) AND !IsEmpty( lper[idx0] ) )
    {
        GfxPolyline( idx0, hper[idx0], idx0, lper[idx0], idx0 - per + 1, lper[idx0], idx0 - per + 1, hper[idx0], idx0, hper[idx0] );
        PlotTextSetFont( "" + ( i + 1 ) + " best", ft, sz, idx0, hper[idx0], colorBlack, colorWhite, 0 );
        pat = IIf( idx0 == BarIndex(), 1, 0 );
        Plot( pat, "", colorAqua, styleDashed | styleHistogram | styleOwnScale | styleNoLabel, 0, 1, 0, -3, 1 );
    }
}

Title = EncodeColor( colorAqua ) + "Number of Fits: " + nfits + " | "
        + EncodeColor( colorYellow ) + " Pattern Size: " + per + " | "
        + EncodeColor( colorBrightGreen ) + " Number of bars forcasted: " + npred + " | "
        + EncodeColor( colorPink ) + " Pattern Type: " + patterntype + " | "
        + EncodeColor( colorOrange ) + " Normalization Mode: " + normalizationmode + " | ";
13 Likes

Very generous, looks like a lot of work on your part. Will take a closer look. :cowboy_hat_face:

Seems like it could be used in so many ways, as you could implement other contexts, setups and view patterns in other contexts and within larger pasterns.

Have a great weekend.

1 Like

thanks. If you have ideas let me know. The idea is that patterns repeat. Problem is that they might repeat but not exactly. For intraday forecasting I tried using yesterdays High and Low as the rectangle of normalization. These levels are often used as support/resistance. So far looking for the exact same price movements does not seem to work well in forecasting. The price makes similar moves but not exactly the same.

But I was playing around with the remap because when using machine learning the data you put in is very important. Often these examples I find on the internet take ALL the data and normalize the entire array at once. This will not work.

Here 2 charts of 30min intraday fitting of Oil (CL), using 1500 days. First chart is when all data is known. Second chart shows how the forecast looks like if 50% of the data is known. 1 of the 3 best fits makes a good prediction. At this point this is still too unreliable.


5 Likes

God, well the problem is that ideas and possible refinements are endless. So is your context/refinement/setup/filter, one where prices are still in prior days range? These type of specific "Contexts/setups..." might be a fertile place to investigate. Also, I'm not one for batting average of predictions, as I am of expectation, which would involve the trade etc. If you were able to get some predictable "Area", perhaps testing for expectation could involve option spreads, covering a price range rather than a specific predictable price point. Just a thought.

yes the code I posted uses the data within the borders of the yellow box to predict data that comes after the box. For fitting only data before the yellow box is used.

Yes I just meant with ideas things to fit. For instance in this code I only look for patterns that are within the same "scale" or same frame or like in my chart in the same box. Like the price and bollinger bands they are fitted inside the same box. This means I can normalize them within the same frame. But if I would also want to use for instance RSI or stochastics or delta volume then this should be fitted within a separate frame.

Did not try this yet. Possibly if I use a separate frame for the RSI I can just add the results of other frames as long as I scale the frames using the same method (like between 0 and 1). Not sure. Will play around with it. Similar problems are with machine learning where they also use for instance moving averages and RSI mixed and they just scale everything between 0 and 1. It gives lousy results.

1 Like

Gotcha.

Just off the top of my head, I would suggest you define a more restrictive "Context" or "Environment" to train on and apply to. Just as an example, if prices are trading above 10-day or 3-day EMA of daily opens (like this because it doesn't change during the current intra-day session), then I would match that only with training a "like" environment, i.e., only train from past data that was above that EMA and predict to the same situation. Likewise train and predict for below it etc. You could even restrict it further, like count what number bar it was in that environment, i.e., how old is the defined trend, day(1), day(2), day(n) (above/below) and train from just those etc. Combinations of environments could get more complex and restrictive, but enough data to be statistically significant from enough training instances. Could include other instruments and base research on robustness.

Twins are home from College and mind is slipping to images of roasted turkey. :open_mouth:

thanks, i will think about it. First I am going to implement or include in the fit derivatives of the price like for instance RSI. Eventually i want to move back to machine learning. This is practice how to prepare the data (normalization) before feeding it to the NN algorithms

bon appétit :poultry_leg:

1 Like

i realized a more general way to do this. One can treat every variable separately. So the Close, Open, Bollinger Band Top, RSI etc can all be fitted separately and then you can assemble the pattern by choosing which variables you want to use. Then when you do the forecast you choose the variable that you want to forecast, for instance the Close price.

Got a question in PM about forecasting/fitting the RSI. You should not forget to also use the RSI when doing the forecast. So code below shows just the fit for the RSI. Will make more generalized code later

SetBarsRequired( sbrAll, sbrAll );

per = Param( "Pattern Number of Bars", 10, 1, 50, 1 );
npred = Param( "Number of Bars to Predict", 10, 1, 50, 1 );
nfits = Param( "Number of Fits", 3, 1, 50, 1 );
calculationMode = ParamList( "Calculation Mode", "Continuous Mode|Trigger Mode", 0 ); // continuous gets real time updates
calculationTrigger = ParamTrigger( "Calculation Trigger", "Press Here" ); // works when calculation mode is in Trigger Mode
patternType = ParamList( "Pattern Type", "Pattern 1", 0 );
displayType = ParamList( "Display Type", "Show Average Fit Plus Forcasts|Show Average Fit Standard Deviation Cone", 1 );
normalizationmode = ParamList( "Normalization mode", "Percentage|0 to 1", 1 );
showbarnumbering = ParamToggle( "Show Bar Numbering", "Not Show|Show", 1 );
ft = ParamList( "Font Type", "Tahoma|Arial Black|Verdana|Courier New|Times New Roman|Open Sans|Segoe UI|DejaVu Sans", 1 );
sz = Param( "Font Size", 10, 8, 50, 1 );
clearAll = ParamTrigger( "Clear All", "Clear All" );
sintest = ParamToggle( "SIN TEST", "Not Show|Show", 0 );

// do not forcast more bars then pattern length
npred = Min( per, npred );

// initialisations
bi = BarIndex();
fvb = FirstVisibleValue( bi );
lvb = LastVisibleValue( bi );
distance = 1e12;
patternText = "";
hper = lper = Null;
bestFitsStorageArray = 0;
idx0 = ss = shift = 0;
topcone = botcone = Null;
yymin = Status( "axisminy" );
yymax = Status( "axismaxy" );
// Pattern arrays
f1 = 0;
patRSI = cpatRSI = 0;

GuiToggle( StaticVarGetText( "LockOnOffText" ), 1, 5, 20, 130, 20,  1 );
guicheck = GuiGetCheck( 1 );
GuiSetColors( 1, 1, 1,
              ColorRGB( 0, 0, 0 ), StaticVarGet( "LockOnOffColor" ), ColorRGB( 50, 50, 50 ), 	// text/back/border
              ColorRGB( 0, 0, 0 ), StaticVarGet( "LockOnOffColor" ), ColorRGB( 50, 50, 50 ), 	// selected (text/back/border)
              colorBlue, StaticVarGet( "LockOnOffColor" ), colorYellow, 						// hover
              ColorRGB( 100, 100, 100 ), ColorRGB( 100, 100, 100 ), ColorRGB( 100, 100, 100 ) );	// disabled

function SayOnce( text )
{
   if( StaticVarGetText("lastsaidtext") != text )
   {
      Say( text );
      StaticVarSetText("lastsaidtext", text );
   }
}
    
// lock pattern in place
if( guicheck == 0 )
{
	idx0 = Nz( SelectedValue( BarIndex() ) );
    StaticVarSet( "idx0", idx0 );
    StaticVarSetText( "LockOnOffText","PATTERN LOCK OFF" );
    StaticVarSet( "LockOnOffColor",colorGreen );
    SayOnce( "Lock Off" );
}

if( guicheck == 1 )
{
	StaticVarSetText( "LockOnOffText","PATTERN LOCK ON" );
	StaticVarSet( "LockOnOffColor",colorRed );
    idx0 = Nz( StaticVarGet( "idx0" ) );
    SayOnce( "Lock On" );
}

if( clearAll )
{
    StaticVarRemove( "*" );
    GuiSetCheck( 1, 0 );
    StaticVarSetText( "LockOnOffText","PATTERN LOCK OFF" );
    StaticVarSet( "LockOnOffColor",colorGreen );    
}

////////////////////////////////////////////////////
// For testing purposes
pi = 3.14159265359;
function sinfunc( wavelength, phase )
{
    sinusFunctionArray = 2 * sin( 2 * pi * Cum( 1 ) / wavelength  + phase )
                         + 1 * sin( 2 * pi * Cum( 1 ) / ( trunc( wavelength / 2 ) )  + phase / 2 )
                         + 0.5 * sin( 2 * pi * Cum( 1 ) / ( trunc( wavelength / 4 ) )  + phase / 4 )
                         + 1 * sin( 2 * pi * Cum( 1 ) / ( trunc( wavelength * 50 ) )  + phase / 8 );
    //+ 0.75 * mtRandomA( seed = 1 );
    return sinusFunctionArray;
}

if( sintest )
{
    O = H = L = C = sinfunc( 30, pi );
}

////////////////////////////////////////////////////

if( patternType == "Pattern 1" )
{
	f1 = RSI(20);
    patternText = "1 variable RSI(20)";
    hper = HHV( f1, per );
    lper = LLV( f1, per );
}

function patternDefinition1()
{
    for( z = 0; z < per; z++ )
    {
        x = idx0 - per + z + 1;

        if( normalizationmode == "0 to 1" )
        {
            patRSI[z] = Remap( f1[x], lper[idx0], hper[idx0], 0, 1 );
        }

        if( normalizationmode == "Percentage" )
        {
            patRSI[z] = Remap( f1[x], lper[idx0], hper[idx0], 0, SafeDivide( hper[idx0] - lper[idx0], lper[idx0] ) * 100 );
        }

        if( showbarnumbering )
            PlotTextSetFont( "" + z, ft, sz, x, yymin, colorBlack, colorWhite, 0 );
    }

    for( i = 2 * per + 20; i < idx0 - per; i++ )
    {
        dis = 0;

        for( z = 0; z < per; z++ )
        {
            x = i - per + z + 1;

            if( normalizationmode == "0 to 1" )
            {
                cpatRSI[z] = Remap( f1[x], lper[i], hper[i], 0, 1 );
            }

            if( normalizationmode == "Percentage" )
            {
                cpatRSI[z] = Remap( f1[x], lper[i], hper[i], 0, SafeDivide( hper[i] - lper[i], lper[i] ) * 100 );
            }

            // avoid parts with bad data
            if( lper[x] == hper[x] )
            {
                dis = 1e12;
            }
            else
            {
                dis = sqrt( ( cpatRSI[z] - patRSI[z] ) ^ 2 ) + dis;
            }
        }

        distance[i] = SafeDivide( dis, per ); // average distance per bar
    }

    StaticVarSet( "distance", distance );
}

if( calculationMode == "Continuous Mode" )
{
    if( patternType == "Pattern 1" )
    {
        patternDefinition1();
    }
}

if( calculationMode == "Trigger Mode" )
{
    if( calculationTrigger )
    {
        Say( "Calculate Fit" );

        if( patternType == "Pattern 1" )
        {
            patternDefinition1();
        }

        Say( "Finished" );
    }
}

distance = Nz( StaticVarGet( "distance" ) );
sdistance = Sort( distance, 0, -1, False ); // return sorted value
sdistancei = Sort( distance, 0, -1, True ); // return the index corresponding to the sorted value

SetChartBkColor( colorBlack );
SetChartOptions( 0, chartShowDates );
Plot( f1, "", colorWhite, styleLine, Null, Null, 0, 0, 1 );

// function helps to avoid selecting 2 patterns within a distance of "per" bars
function testValidity( aa, disi, per )
{
    val = 1;

    for( i = 0; i < 50; i++ )
    {
        if( aa[i] == 0 AND i == 0 )
        {
            val = 1;
            break;
        }
        else
            if( abs( aa[i] - disi ) < per )
            {
                val = 0;
                break;
            }
            else
                if( disi <= per )
                {
                    val = 0;
                    break;
                }
    }

    return val;
}

cnt = 0;

for( i = 0; i < BarCount; i++ )
{
    fitdummyY = 0;
    forcastdummyY = 0;

    // avoid selecting 2 patterns within a distance of per bars, use testValidity
    if( testValidity( bestFitsStorageArray, sdistancei[i], per ) )
    {
        bestFitsStorageArray[cnt] = idx = sdistancei[i];
        //_TRACE( "idx: " + idx0 );
        shift = npred - Min( BarCount - 1 - idx0, npred );

        for( j = 0; j < per; j++ )
        {
            x = Max( 0, idx0 + j - per + 1 );
            x1 = Max( 0, idx + j - per + 1 );
            //_TRACE( "x1: " + x1 );
            y = Remap( f1[x1], lper[idx], hper[idx], lper[idx0], hper[idx0] );
            fitdummyY[x] = y;
        }

        StaticVarSet( "fit" + cnt, fitdummyY );

        for( j = per; j < per + npred; j++ )
        {
            x = idx0 + j - per + 1 - shift;
            x1 = idx + j - per + 1;
            //_TRACE( "x1: " + x1 );
            y = Remap( f1[x1], lper[idx], hper[idx], lper[idx0], hper[idx0] );
            forcastdummyY[x] = y;
        }

        StaticVarSet( "forcast" + cnt, forcastdummyY );
        cnt = cnt + 1;
    }

    if( cnt > nfits )
    {
        StaticVarSet( "bestFitsStorageArray", bestFitsStorageArray );
        break;
    }
}

if( displayType == "Show Average Fit Plus Forcasts" )
{
    totfit = 0;
    totforcast = 0;

    for( i = 0; i < nfits; i++ )
    {
        ff = StaticVarGet( "fit" + i );
        Plot( IIf( ff, ff, Null ), "", colorAqua, styleDashed | styleNoRescale | styleNoLabel, Null, Null, 0, 0, 1 );
        totfit = totfit + StaticVarGet( "fit" + i );
        ff = StaticVarGet( "forcast" + i );
        Plot( IIf( ff, ff, Null ), "", colorViolet, styleDashed | styleNoRescale | styleNoLabel, Null, Null, shift, 0, 1 );
        totforcast = totforcast + StaticVarGet( "forcast" + i );
        PlotTextSetFont( "" + ( i + 1 ), ft, sz, idx0 + 1 + npred, ff[idx0 + npred - shift], colorBlack, colorWhite, 0 );
    }

    // average fit
    totfit = totfit / nfits;
    Plot( IIf( totfit, totfit, Null ), "", colorAqua, styleDots | styleNoRescale | styleNoLabel, Null, Null, 0, 1, 3 );

    // average forcast
    totforcast = totforcast / nfits;
    Plot( IIf( totforcast, totforcast, Null ), "", colorViolet, styleDots | styleNoRescale | styleNoLabel, Null, Null, shift, 1, 3 );
}

if( displayType == "Show Average Fit Standard Deviation Cone" )
{
    totfit = 0;
    totforcast = 0;

    for( i = 0; i < nfits; i++ )
    {
        ff = StaticVarGet( "fit" + i );
        totfit = totfit + StaticVarGet( "fit" + i );
        ff = StaticVarGet( "forcast" + i );
        totforcast = totforcast + StaticVarGet( "forcast" + i );
    }

    // average fit
    totfit = totfit / nfits;
    Plot( IIf( totfit, totfit, Null ), "", colorAqua, styleDots | styleNoRescale | styleNoLabel, Null, Null, 0, 1, 3 );

    // average forcast
    totforcast = totforcast / nfits;
    Plot( IIf( totforcast, totforcast, Null ), "", colorViolet, styleDots | styleNoRescale | styleNoLabel, Null, Null, shift, 1, 3 );

    // error cone forecast
    for( i = 0; i < npred; i++ )
    {
        ss = 0;
        x = idx0 + 1 + i;
        shift = npred - Min( BarCount - 1 - idx0, npred );

        for( j = 0; j < nfits; j++ )
        {
            ff = StaticVarGet( "forcast" + j );
            ss = ( ff[x - shift] - totforcast[x - shift] ) ^ 2 + ss;
        }

        topcone[x - shift] = totforcast[x - shift] + sqrt( SafeDivide( ss, nfits ) );
        botcone[x - shift] = totforcast[x - shift] - sqrt( SafeDivide( ss, nfits ) );
    }

    topcone = IIf( 0, Null, topcone );
    botcone = IIf( 0, Null, botcone );
    PlotOHLC( topcone, topcone, botcone, botcone, "", ColorRGB( 0, 0, 60 ), styleCloud | styleNoLabel | styleNoRescale, Null, Null, shift, -10, 1 );
    Plot( topcone, "", colorblue, styleLine | styleNoLabel | styleNoRescale, Null, Null, shift, -10, 3 );
    Plot( botcone, "", colorblue, styleLine | styleNoLabel | styleNoRescale, Null, Null, shift, -10, 3 );

    // error cone data fits
    topcone = botcone = Null;

    for( i = 0; i < per; i++ )
    {
        ss = 0;
        x = Max( 0, idx0 - per + i + 1 );
        shift = 0;

        for( j = 0; j < nfits; j++ )
        {
            ff = StaticVarGet( "fit" + j );
            ss = ( ff[x - shift] - totfit[x - shift] ) ^ 2 + ss;
        }

        topcone[x - shift] = totfit[x - shift] + sqrt( SafeDivide( ss, nfits ) );
        botcone[x - shift] = totfit[x - shift] - sqrt( SafeDivide( ss, nfits ) );
    }

    topcone = IIf( 0, Null, topcone );
    botcone = IIf( 0, Null, botcone );
    PlotOHLC( topcone, topcone, botcone, botcone, "", ColorRGB( 0, 0, 60 ), styleCloud | styleNoLabel | styleNoRescale, Null, Null, shift, -10, 1 );
    Plot( topcone, "", colorblue, styleLine | styleNoLabel | styleNoRescale, Null, Null, shift, -10, 3 );
    Plot( botcone, "", colorblue, styleLine | styleNoLabel | styleNoRescale, Null, Null, shift, -10, 3 );
}

// draw box around pattern that needs to be fitted
pat = IIf( idx0 == BarIndex(), 1, 0 );
Plot( pat, "", colorYellow, styleDashed | styleHistogram | styleOwnScale | styleNoLabel, 0, 1, 0, -3, 1 );
GfxSetZOrder( -2 );
GfxSetCoordsMode( 1 );
GfxSelectSolidBrush( ColorRGB( 50, 50, 10 ) );
GfxSelectPen( ColorYellow, 3, 0 );

if( !IsEmpty( hper[idx0] ) AND !IsEmpty( lper[idx0] ) )
    GfxPolyline( idx0, hper[idx0], idx0, lper[idx0], idx0 - per + 1, lper[idx0], idx0 - per + 1, hper[idx0], idx0, hper[idx0] );

PlotTextSetFont( "" + patternText, ft, sz, idx0 + 1, hper[idx0], colorBlack, colorWhite, 0 );

// show boxes around the best fits
GfxSelectPen( colorAqua, 3, 0 );

for( i = 0; i < nfits; i++ )
{
    idx0 = bestFitsStorageArray[i];

    if( !IsEmpty( hper[idx0] ) AND !IsEmpty( lper[idx0] ) )
    {
        GfxPolyline( idx0, hper[idx0], idx0, lper[idx0], idx0 - per + 1, lper[idx0], idx0 - per + 1, hper[idx0], idx0, hper[idx0] );
        PlotTextSetFont( "" + ( i + 1 ) + " best", ft, sz, idx0, hper[idx0], colorBlack, colorWhite, 0 );
        pat = IIf( idx0 == BarIndex(), 1, 0 );
        Plot( pat, "", colorAqua, styleDashed | styleHistogram | styleOwnScale | styleNoLabel, 0, 1, 0, -3, 1 );
    }
}

Title = EncodeColor( colorAqua ) + "Number of Fits: " + nfits + " | "
        + EncodeColor( colorYellow ) + " Pattern Size: " + per + " | "
        + EncodeColor( colorBrightGreen ) + " Number of bars forcasted: " + npred + " | "
        + EncodeColor( colorPink ) + " Pattern Type: " + patterntype + " | "
        + EncodeColor( colorOrange ) + " Normalization Mode: " + normalizationmode + " | ";
        
4 Likes

this is an updated version where you can assemble the to be fitted pattern by adding the functions separately. In total I added 10 functions but it is slow because I calculate them for each separately. So this can probably be programmed more intelligently. But you can also put it in "Trigger Mode" and calculate it just once. Also use EOD data else it will take for ever.

And the forecasts are not very good but possibly it can be used for later work

SetBarsRequired( sbrAll, sbrAll );
SetChartBkColor( colorBlack );
SetChartOptions( 0, chartShowDates );

per = Param( "Pattern Number of Bars", 10, 1, 50, 1 );
npred = Param( "Number of Bars to Predict", 10, 1, 50, 1 );
nfits = Param( "Number of Fits", 3, 1, 50, 1 );
patf1 = ParamToggle( "Function f1  C, ID 1", "Off|On", 1 );
VarSet( "pf1", patf1 ); // Close
patf2 = ParamToggle( "Function f2  H, ID 2", "Off|On", 0 );
VarSet( "pf2", patf2 ); // H
patf3 = ParamToggle( "Function f3  O, ID 3", "Off|On", 0 );
VarSet( "pf3", patf3 ); // O
patf4 = ParamToggle( "Function f4  L, ID 4", "Off|On", 0 );
VarSet( "pf4", patf4 ); // L
patf5 = ParamToggle( "Function f5  RSI, ID 5", "Off|On", 0 );
VarSet( "pf5", patf5 ); // RSI
patf6 = ParamToggle( "Function f6  BTOP, ID 6", "Off|On", 0 );
VarSet( "pf6", patf6 ); // BTOP
patf7 = ParamToggle( "Function f7  BBOT, ID 7", "Off|On", 0 );
VarSet( "pf7", patf7 ); // BBOT
patf8 = ParamToggle( "Function f8  MA(20), ID 8", "Off|On", 0 );
VarSet( "pf8", patf8 ); // MA(20)
patf9 = ParamToggle( "Function f9  MA(50), ID 9", "Off|On", 0 );
VarSet( "pf9", patf9 ); // MA(50)
patf10 = ParamToggle( "Function f10 MA(150), ID 10", "Off|On", 0 );
VarSet( "pf10", patf10 ); // MA(150)
displayFunction = ParamList( "Forecasted Function ID", "1|2|3|4|5|6|7|8|9|10", 0 );
calculationMode = ParamList( "Calculation Mode", "Continuous Mode|Trigger Mode", 0 ); // continuous gets real time updates
calculationTrigger = ParamTrigger( "Calculation Trigger", "Press Here" ); // works when calculation mode is in Trigger Mode
displayType = ParamList( "Display Type", "Show Average Fit Plus Forcasts|Show Average Fit Standard Deviation Cone", 1 );
normalizationmode = ParamList( "Normalization mode", "Percentage|0 to 1", 1 );
showbarnumbering = ParamToggle( "Show Bar Numbering", "Not Show|Show", 1 );
ft = ParamList( "Font Type", "Tahoma|Arial Black|Verdana|Courier New|Times New Roman|Open Sans|Segoe UI|DejaVu Sans", 1 );
sz = Param( "Font Size", 10, 8, 50, 1 );
clearAll = ParamTrigger( "Clear All", "Clear All" );
sintest = ParamToggle( "SIN TEST", "Not Show|Show", 0 );
totpat = 10; // total number of patterns

// initialisations
bi = BarIndex();
fvb = FirstVisibleValue( bi );
lvb = LastVisibleValue( bi );
distance = 0;
distancecounter = 0;
VarSetText( "patternText", "" );
hper = lper = Null;
bestFitsStorageArray = 0;
idx0 = ss = shift = 0;
topcone = botcone = Null;
yymin = Status( "axisminy" );
yymax = Status( "axismaxy" );
// Pattern arrays
f1 = 0;
pat = cpat = 0;

// button code
GuiToggle( StaticVarGetText( "LockOnOffText" ), 1, 5, 20, 130, 20,  1 );
guicheck = GuiGetCheck( 1 );
GuiSetColors( 1, 1, 1,
              ColorRGB( 0, 0, 0 ), StaticVarGet( "LockOnOffColor" ), ColorRGB( 50, 50, 50 ), 	// text/back/border
              ColorRGB( 0, 0, 0 ), StaticVarGet( "LockOnOffColor" ), ColorRGB( 50, 50, 50 ), 	// selected (text/back/border)
              colorBlue, StaticVarGet( "LockOnOffColor" ), colorYellow, 						// hover
              ColorRGB( 100, 100, 100 ), ColorRGB( 100, 100, 100 ), ColorRGB( 100, 100, 100 ) );	// disabled

function SayOnce( text )
{
    if( StaticVarGetText( "lastsaidtext" ) != text )
    {
        Say( text );
        StaticVarSetText( "lastsaidtext", text );
    }
}

// lock pattern in place
if( guicheck == 0 )
{
    idx0 = Nz( SelectedValue( BarIndex() ) );
    StaticVarSet( "idx0", idx0 );
    StaticVarSetText( "LockOnOffText", "PATTERN LOCK OFF" );
    StaticVarSet( "LockOnOffColor", colorGreen );
    SayOnce( "Lock Off" );
}

if( guicheck == 1 )
{
    StaticVarSetText( "LockOnOffText", "PATTERN LOCK ON" );
    StaticVarSet( "LockOnOffColor", colorRed );
    idx0 = Nz( StaticVarGet( "idx0" ) );
    SayOnce( "Lock On" );
}

if( clearAll )
{
    SayOnce( "Clear All" );
    StaticVarRemove( "*" );
    GuiSetCheck( 1, 0 );
    StaticVarSetText( "LockOnOffText", "PATTERN LOCK OFF" );
    StaticVarSet( "LockOnOffColor", colorGreen );
}

////////////////////////////////////////////////////
// For testing purposes
pi = 3.14159265359;
function sinfunc( wavelength, phase )
{
    sinusFunctionArray = 2 * sin( 2 * pi * Cum( 1 ) / wavelength  + phase )
                         + 1 * sin( 2 * pi * Cum( 1 ) / ( trunc( wavelength / 2 ) )  + phase / 2 )
                         + 0.5 * sin( 2 * pi * Cum( 1 ) / ( trunc( wavelength / 4 ) )  + phase / 4 )
                         + 1 * sin( 2 * pi * Cum( 1 ) / ( trunc( wavelength * 50 ) )  + phase / 8 );
    //+ 0.75 * mtRandomA( seed = 1 );
    return sinusFunctionArray;
}

if( sintest )
{
    O = H = L = C = sinfunc( 30, pi );
}

////////////////////////////////////////////////////

// function 1, the Close price
VarSetText( "ptext1", "C, " );
VarSet( "f1", C );
VarSet( "hper1", HHV( f1, per ) );
VarSet( "lper1", LLV( f1, per ) );
VarSet( "startpoint1", per * 2 );

// function 2, the High price
VarSetText( "ptext2", "H, " );
VarSet( "f2", H );
VarSet( "hper2", HHV( f2, per ) );
VarSet( "lper2", LLV( f2, per ) );
VarSet( "startpoint2", per * 2 );

// function 3, the Open price
VarSetText( "ptext3", "O, " );
VarSet( "f3", O );
VarSet( "hper3", HHV( f3, per ) );
VarSet( "lper3", LLV( f3, per ) );
VarSet( "startpoint3", per * 2 );

// function 4, the Low price
VarSetText( "ptext4", "L, " );
VarSet( "f4", L );
VarSet( "hper4", HHV( f4, per ) );
VarSet( "lper4", LLV( f4, per ) );
VarSet( "startpoint4", per * 2 );

// function 5, RSI
VarSetText( "ptext5", "RSI, " );
VarSet( "f5", RSI( 20 ) );
VarSet( "hper5", HHV( f5, per ) );
VarSet( "lper5", LLV( f5, per ) );
VarSet( "startpoint5", per * 2 + 20 );

// function 6, BTOP
VarSetText( "ptext6", "BBTOP, " );
VarSet( "f6", BBandTop( C, 20, 2 ) );
VarSet( "hper6", HHV( f6, per ) );
VarSet( "lper6", LLV( f6, per ) );
VarSet( "startpoint6", per * 2 + 20 );

// function 7, BBOT
VarSetText( "ptext7", "BBBOT, " );
VarSet( "f7", BBandBot( C, 20, 2 ) );
VarSet( "hper7", HHV( f7, per ) );
VarSet( "lper7", LLV( f7, per ) );
VarSet( "startpoint7", per * 2 + 20 );

// function 8, MA(20)
VarSetText( "ptext8", "MA(20), " );
VarSet( "f8", MA( C, 20 ) );
VarSet( "hper8", HHV( f8, per ) );
VarSet( "lper8", LLV( f8, per ) );
VarSet( "startpoint8", per * 2 + 20 );

// function 9, MA(50)
VarSetText( "ptext9", "MA(50), " );
VarSet( "f9", MA( C, 50 ) );
VarSet( "hper9", HHV( f9, per ) );
VarSet( "lper9", LLV( f9, per ) );
VarSet( "startpoint9", per * 2 + 50 );

// function 10, MA(150)
VarSetText( "ptext10", "MA(150), " );
VarSet( "f10", MA( C, 150 ) );
VarSet( "hper10", HHV( f10, per ) );
VarSet( "lper10", LLV( f10, per ) );
VarSet( "startpoint10", per * 2 + 150 );

function patternDefinition()
{
    distance = 0;
    cnt = 0;

    for( npat = 1; npat <= totpat; npat++ )
    {
        if( VarGet( "pf" + npat ) )
        {
            cnt = cnt + 1;
            //_TRACE("in: " + npat );
            VarSetText( "patternText",  VarGetText( "patternText" ) + VarGetText( "ptext" + npat ) );

            ff = VarGet( "f" + npat );
            hper = VarGet( "hper" + npat );
            lper = VarGet( "lper" + npat );

            for( z = 0; z < per; z++ )
            {
                x = idx0 - per + z + 1;

                if( normalizationmode == "0 to 1" )
                {
                    pat[z] = Remap( ff[x], lper[idx0], hper[idx0], 0, 1 );
                }

                if( normalizationmode == "Percentage" )
                {
                    pat[z] = Remap( ff[x], lper[idx0], hper[idx0], 0, SafeDivide( hper[idx0] - lper[idx0], lper[idx0] ) * 100 );
                }

                if( showbarnumbering )
                    PlotTextSetFont( "" + z, ft, sz, x, yymin, colorBlack, colorWhite, 0 );
            }

            for( i = VarGet( "startpoint" + npat ); i < idx0 - per; i++ )
            {
                dis = 0;

                for( z = 0; z < per; z++ )
                {
                    x = i - per + z + 1;

                    if( normalizationmode == "0 to 1" )
                    {
                        cpat[z] = Remap( ff[x], lper[i], hper[i], 0, 1 );
                    }

                    if( normalizationmode == "Percentage" )
                    {
                        cpat[z] = Remap( ff[x], lper[i], hper[i], 0, SafeDivide( hper[i] - lper[i], lper[i] ) * 100 );
                    }

                    // avoid parts with bad data
                    if( lper[x] == hper[x] )
                    {
                        dis = 1e12;
                    }
                    else
                    {
                        dis = sqrt( ( cpat[z] - pat[z] ) ^ 2 ) + dis;
                    }
                }

                distance[i] = distance[i] + SafeDivide( dis, per ); // average distance per bar
                distancecounter[i] = distancecounter[i] + 1;
            }

            StaticVarSet( "distance", distance );
            StaticVarSet( "distancecounter", distancecounter );
        }
    }

    if( cnt == 0 )
    {
        StaticVarSet( "distance", 0 );
    }
}

if( calculationMode == "Continuous Mode" )
{
    patternDefinition();
}

if( calculationMode == "Trigger Mode" )
{
    if( calculationTrigger )
    {
        Say( "Calculate Fit" );
        patternDefinition();
        Say( "Finished" );
    }
}

function plt()
{
    Plot( VarGet( "f6" ), "", colorRed, styleDashed | styleNoLabel | styleNoRescale, Null, Null, 0, -2, 1 );
    Plot( VarGet( "f7" ), "", colorGreen, styleDashed | styleNoLabel | styleNoRescale, Null, Null, 0, -2, 1 );
    Plot( VarGet( "f8" ), "", colorRed, styleLine | styleNoLabel | styleNoRescale, Null, Null, 0, -2, 1 );
    Plot( VarGet( "f9" ), "", colorGreen, styleLine | styleNoLabel | styleNoRescale, Null, Null, 0, -2, 1 );
    Plot( VarGet( "f10" ), "", colorBlue, styleLine | styleNoLabel | styleNoRescale, Null, Null, 0, -2, 1 );
}

// handle the distances of the fits
distance = Nz( StaticVarGet( "distance" ) );
distancecounter = Nz( StaticVarGet( "distancecounter" ) );
distance = SafeDivide( distance, distancecounter );
distance = IIf( distance == 0, 1e12, distance ); // distance initially needs to be set to 0, so remove zeros after fitting
sdistance = Sort( distance, 0, -1, False ); // return sorted value
sdistancei = Sort( distance, 0, -1, True ); // return the index corresponding to the sorted value

// function to forcast
ff_disp = VarGet( "f" + displayFunction );
hper = VarGet( "hper" + displayFunction );
lper = VarGet( "lper" + displayFunction );

if( displayFunction == "1" )
{
    displayFunctionText = "Close Price";
    Plot( ff_disp, "", colorWhite, styleCandle, Null, Null, 0, 0, 1 );
    //plt();
}

if( displayFunction == "2" )
{
    displayFunctionText = "High Price";
    Plot( ff_disp, "", colorWhite, styleDots, Null, Null, 0, 0, 1 );
}

if( displayFunction == "3" )
{
    displayFunctionText = "Open Price";
    Plot( ff_disp, "", colorWhite, styleDots, Null, Null, 0, 0, 1 );
}

if( displayFunction == "4" )
{
    displayFunctionText = "Low Price";
    Plot( ff_disp, "", colorWhite, styleDots, Null, Null, 0, 0, 1 );
}

if( displayFunction == "5" )
{
    displayFunctionText = "RSI";
    Plot( ff_disp, "", colorWhite, styleDots, Null, Null, 0, 0, 1 );
}

if( displayFunction == "6" )
{
    displayFunctionText = "BTOP";
    Plot( ff_disp, "", colorWhite, styleDots, Null, Null, 0, 0, 1 );
}

if( displayFunction == "7" )
{
    displayFunctionText = "BBOT";
    Plot( ff_disp, "", colorWhite, styleDots, Null, Null, 0, 0, 1 );
}

if( displayFunction == "8" )
{
    displayFunctionText = "MA(C,20)";
    Plot( ff_disp, "", colorWhite, styleDots, Null, Null, 0, 0, 1 );
}

if( displayFunction == "9" )
{
    displayFunctionText = "MA(C,50)";
    Plot( ff_disp, "", colorWhite, styleDots, Null, Null, 0, 0, 1 );
}

if( displayFunction == "10" )
{
    displayFunctionText = "MA(C,150)";
    Plot( ff_disp, "", colorWhite, styleDots, Null, Null, 0, 0, 1 );
}

// function helps to avoid selecting 2 patterns within a distance of "per" bars
function testValidity( aa, disi, per, i0 )
{
    val = 1;

    for( i = 0; i < 50; i++ )
    {
        // first element, no storage elements yet
        if( aa[i] == 0 AND i == 0 )
        {
            val = 1;
            break;
        }
        else

            // distance to new best fit disi less than per then ignore disi
            if( abs( aa[i] - disi ) < per )
            {
                val = 0;
                break;
            }
            else
                if( i0 - disi < npred )
                {
                    val = 0;
                    break;
                }
                else
                    if( disi <= per )
                    {
                        val = 0;
                        break;
                    }
    }

    return val;
}

cnt = 0;

for( i = 0; i < BarCount; i++ )
{
    fitdummyY = 0;
    forcastdummyY = 0;

    // avoid selecting 2 patterns within a distance of per bars, use testValidity
    if( testValidity( bestFitsStorageArray, sdistancei[i], ( per + npred ), idx0 ) )
    {
        bestFitsStorageArray[cnt] = idx = sdistancei[i];
        //_TRACE( "idx: " + idx0 );
        shift = npred - Min( BarCount - 1 - idx0, npred );

        // fit
        for( j = 0; j < per; j++ )
        {
            x = Max( 0, idx0 + j - per + 1 );
            x1 = Max( 0, idx + j - per + 1 );
            y = Remap( ff_disp[x1], lper[idx], hper[idx], lper[idx0], hper[idx0] );
            fitdummyY[x] = y;
        }

        StaticVarSet( "fit" + cnt, fitdummyY );

        // forecast
        for( j = per; j < per + npred; j++ )
        {
            x = Min( BarCount - 1, idx0 + j - per + 1 - shift );
            x1 = Min( BarCount - 1, idx + j - per + 1 );
            y = Remap( ff_disp[x1], lper[idx], hper[idx], lper[idx0], hper[idx0] );
            forcastdummyY[x] = y;
        }

        StaticVarSet( "forcast" + cnt, forcastdummyY );
        cnt = cnt + 1;
    }

    if( cnt > nfits )
    {
        StaticVarSet( "bestFitsStorageArray", bestFitsStorageArray );
        break;
    }
}

if( displayType == "Show Average Fit Plus Forcasts" )
{
    totfit = 0;
    totforcast = 0;

    for( i = 0; i < nfits; i++ )
    {
        ff = StaticVarGet( "fit" + i );
        Plot( IIf( ff, ff, Null ), "", colorAqua, styleDashed | styleNoRescale | styleNoLabel, Null, Null, 0, 0, 1 );
        totfit = totfit + StaticVarGet( "fit" + i );
        ff = StaticVarGet( "forcast" + i );
        Plot( IIf( ff, ff, Null ), "", colorViolet, styleDashed | styleNoRescale | styleNoLabel, Null, Null, shift, 0, 1 );
        totforcast = totforcast + StaticVarGet( "forcast" + i );
        PlotTextSetFont( "" + ( i + 1 ), ft, sz, idx0 + 1 + npred, ff[idx0 + npred - shift], colorBlack, colorWhite, 0 );
    }

    // average fit
    totfit = totfit / nfits;
    Plot( IIf( totfit, totfit, Null ), "", colorAqua, styleDots | styleNoRescale | styleNoLabel, Null, Null, 0, 1, 3 );

    // average forcast
    totforcast = totforcast / nfits;
    Plot( IIf( totforcast, totforcast, Null ), "", colorViolet, styleDots | styleNoRescale | styleNoLabel, Null, Null, shift, 1, 3 );
}

if( displayType == "Show Average Fit Standard Deviation Cone" )
{
    totfit = 0;
    totforcast = 0;

    for( i = 0; i < nfits; i++ )
    {
        ff = StaticVarGet( "fit" + i );
        totfit = totfit + StaticVarGet( "fit" + i );
        ff = StaticVarGet( "forcast" + i );
        totforcast = totforcast + StaticVarGet( "forcast" + i );
    }

    // average fit
    totfit = totfit / nfits;
    Plot( IIf( totfit, totfit, Null ), "", colorYellow, styleDots | styleNoRescale | styleNoLabel, Null, Null, 0, 1, 2 );

    // average forcast
    totforcast = totforcast / nfits;
    Plot( IIf( totforcast, totforcast, Null ), "", colorViolet, styleDots | styleNoRescale | styleNoLabel, Null, Null, shift, 1, 3 );

    // error cone forecast
    for( i = 0; i < npred; i++ )
    {
        ss = 0;
        x = idx0 + 1 + i;
        shift = npred - Min( BarCount - 1 - idx0, npred );

        for( j = 0; j < nfits; j++ )
        {
            ff = StaticVarGet( "forcast" + j );
            ss = ( ff[x - shift] - totforcast[x - shift] ) ^ 2 + ss;
        }

        topcone[x - shift] = totforcast[x - shift] + sqrt( SafeDivide( ss, nfits ) );
        botcone[x - shift] = totforcast[x - shift] - sqrt( SafeDivide( ss, nfits ) );
    }

    topcone = IIf( 0, Null, topcone );
    botcone = IIf( 0, Null, botcone );
    PlotOHLC( topcone, topcone, botcone, botcone, "", ColorRGB( 0, 0, 60 ), styleCloud | styleNoLabel | styleNoRescale, Null, Null, shift, -10, 1 );
    Plot( topcone, "", colorblue, styleLine | styleNoLabel | styleNoRescale, Null, Null, shift, -10, 3 );
    Plot( botcone, "", colorblue, styleLine | styleNoLabel | styleNoRescale, Null, Null, shift, -10, 3 );

    // error cone data fits
    topcone = botcone = Null;

    for( i = 0; i < per; i++ )
    {
        ss = 0;
        x = Max( 0, idx0 - per + i + 1 );
        shift = 0;

        for( j = 0; j < nfits; j++ )
        {
            ff = StaticVarGet( "fit" + j );
            ss = ( ff[x - shift] - totfit[x - shift] ) ^ 2 + ss;
        }

        topcone[x - shift] = totfit[x - shift] + sqrt( SafeDivide( ss, nfits ) );
        botcone[x - shift] = totfit[x - shift] - sqrt( SafeDivide( ss, nfits ) );
    }

    topcone = IIf( 0, Null, topcone );
    botcone = IIf( 0, Null, botcone );
    PlotOHLC( topcone, topcone, botcone, botcone, "", ColorRGB( 0, 0, 60 ), styleCloud | styleNoLabel | styleNoRescale, Null, Null, shift, -10, 1 );
    Plot( topcone, "", colorblue, styleLine | styleNoLabel | styleNoRescale, Null, Null, shift, -10, 3 );
    Plot( botcone, "", colorblue, styleLine | styleNoLabel | styleNoRescale, Null, Null, shift, -10, 3 );
}

// draw box around pattern that needs to be fitted
pat = IIf( idx0 == BarIndex(), 1, 0 );
Plot( pat, "", colorYellow, styleDashed | styleHistogram | styleOwnScale | styleNoLabel, 0, 1, 0, -3, 1 );
GfxSetZOrder( -2 );
GfxSetCoordsMode( 1 );
GfxSelectSolidBrush( ColorRGB( 50, 50, 10 ) );
GfxSelectPen( ColorYellow, 3, 0 );

if( !IsEmpty( hper[idx0] ) AND !IsEmpty( lper[idx0] ) )
    GfxPolyline( idx0, hper[idx0], idx0, lper[idx0], idx0 - per + 1, lper[idx0], idx0 - per + 1, hper[idx0], idx0, hper[idx0] );

PlotTextSetFont( "" + VarGetText( "patternText" ), ft, sz, idx0 + 1, hper[idx0], colorBlack, colorWhite, 0 );

// show boxes around the best fits
GfxSelectPen( colorAqua, 3, 0 );

for( i = 0; i < nfits; i++ )
{
    idx0 = bestFitsStorageArray[i];

    idx_fit = 0;

    if( !IsEmpty( hper[idx0] ) AND !IsEmpty( lper[idx0] ) )
    {
        GfxPolyline( idx0, hper[idx0], idx0, lper[idx0], idx0 - per + 1, lper[idx0], idx0 - per + 1, hper[idx0], idx0, hper[idx0] );
        PlotTextSetFont( "" + ( i + 1 ) + " best", ft, sz, idx0, hper[idx0], colorBlack, colorWhite, 0 );
        pat = IIf( idx0 == BarIndex(), 1, 0 );
        Plot( pat, "", colorAqua, styleDashed | styleHistogram | styleOwnScale | styleNoLabel, 0, 1, 0, -3, 1 );

        for( j = 0; j < per; j++ )
        {
            x = Max( 0, idx0 - per + j + 1 );
            idx_fit[x] = ff_disp[x];
        }

        Plot( IIf( idx_fit, idx_fit, Null ), "", colorAqua, styleDots | styleNoRescale | styleNoLabel, Null, Null, 0, 1, 2 );
    }
}

Title = EncodeColor( colorAqua ) + "Number of Fits: " + nfits + " | "
        + EncodeColor( colorYellow ) + " Pattern Size: " + per + " | "
        + EncodeColor( colorBrightGreen ) + " Number of bars forcasted: " + npred + " | "
        + EncodeColor( colorPink ) + " Fitted Functions: " + VarGetText( "patternText" ) + " | "
        + EncodeColor( colorLightBlue ) + " Forcasted Function: " + displayFunctionText + " | "
        + EncodeColor( colorOrange ) + " Normalization Mode: " + normalizationmode + " | ";
3 Likes