Backtesting non time based chart

Hello everyone, and apologies for what is probably a numptie question :roll_eyes:
I have an afl that im using for a 3 line break chart, and have tried to backtest this against a simple MA crossover
the chart image shows the results, and the same periodicity of the MAs
resultofMAcross|463x445

clearly the crossover backtest isn't liking the results whereas the chart itself is happily overlaying the MAs against what it believes is the closing price

is Amibroker not able to backtest a non-time based chart of this nature, or if it is what might be the direction i need to take to achieve this?

many thanks everyone for any tips you can provide

Hi @JohnC, and welcome to the forum.

Here are a couple of links that you should start with:


Thanks for trying to attach an image. Unfortunately I can not access it (it could be just me...).

My suggestion is to post your code (Use code tags) so we can see what you are doing.

Also, try using an Exploration to see the numbers of your indicators (line breaks and MAs) and see if it is what you Expect. That might help you locate the problem in either your logic, or your code.

resultofMAcross

thanks for the heads up Snoopy. Hopefully the image is clearer
the chart is that of a 3line break chart, overlayed with 2 moving averages. 10 and 30
the backtest is as follows: (keeping it really simple as i get started!)

FastMA = MA( C, 10 ); 
SlowMA = MA( C, 30 ); 
Buy = Cross( FastMA, SlowMA ); 
Sell = Cross( SlowMA, FastMA ); 

using the plot arrows function of the analysis results onto the chart, i get the image where hopefully its clear the 10 is not crossing the 30.
so whereas the MA is using the close (according to the afl) correctly to plot the MAs, the backtest still thinks its backtesting the base periodicity. in this case being the weekly chart.
i can confirm this as the backtest result is exactly as if there was no afl.

so my question is, how can i get amibroker to backtest the non time based chart, ie the 3 line break?
so that the close is that of the afl rather than the underlying price.

one option might be to reflect all of the resultant data points in its own database, but these aren't visible and would be so time consuming so as to make the backtesting redundant anyway
the afl is below

hopefully this is making sense this time. again, thanks for the heads up

GraphXSpace = 7;
 
numline = Param( "Break", 2, 2, 10, 1 );
j = 0;
Line[j] = C[0];
Op[j] = C[0];
direction = 0;
up = 0; // count up lines
down = 0;// count down lines
Count = 1; // count lines
 
for ( i = 1; i < BarCount; i++ )
{
 
    if ( direction[j] == 0 ) // continues down
    {
        if ( C[i] < Line[j] )
        {
            j++;
            direction[j] = 0;
            Line[j] = C[i];
            Op[j] = Line[j-1];
            down++;
            up = 0;
            Count++;
        }
        else
        {
            if ( Count == 1 &&  C[i] > OP[j] )// I-st reverse
            {
                j++;
                direction[j] = 1;
                Line[j] = C[i];
                Op[j] = Op[j-1];
                up++;
                down = 0;
                Count++;
            }
 
            if ( Count > 1 && down  == 1 && C[i] > Line[j-1] )//  reverse after 1 down line
            {
                j++;
                direction[j] = 1;
                Line[j] = C[i];
                Op[j] = Op[j-1];
                up++;
                down = 0;
                Count++;
            }
 
            if ( Count > 1  && down  > 1 && down < numline && C[i] > Op[j-1] )//simple reverse
            {
                j++;
                direction[j] = 1;
                Line[j] = C[i];
                Op[j] = Op[j-1];
                up++;
                down = 0;
                Count++;
            }
 
            if (  down >= numline && C[i] > Op[j-( numline -1 )] ) // white trunaround line
            {
                j++;
                direction[j] = 1;
                Line[j] = C[i];
                Op[j] = Op[j-1];
                up++;
                down = 0;
                Count++;
            }
        }
 
 
 
    }
    else // continues up
    {
        if ( C[i] > Line[j] )
        {
            j++;
            direction[j] = 1;
            Line[j] = C[i];
            op[j] = Line[j-1];
            up++;
            down = 0;
            Count++;
        }
        else
        {
            if ( Count == 1  && C[i] < OP[j] )// I-st reverse
            {
                j++;
                direction[j] = 0;
                Line[j] = C[i];
                Op[j] = Op[j-1];
                down++;
                up = 0;
                Count++;
 
            }
 
            if ( Count > 1 && up  == 1 && C[i] < Line[j-1] )//  reverse after 1 up line
            {
                j++;
                direction[j] = 0;
                Line[j] = C[i];
                Op[j] = Op[j-1];
                down++;
                up = 0;
                Count++;
            }
 
            if ( Count > 1 && up  > 1 && up < numline && C[i] < Op[j-1] ) //simple reverse
            {
                j++;
                direction[j] = 0;
                Line[j] = C[i];
                Op[j] = Op[j-1];
                down++;
                up = 0;
                Count++;
            }
 
            if ( up >= numline && C[i] < Op[j-( numline -1 )] )//black trunaround line
 
            {
                j++;
                direction[j] = 0;
                Line[j] = C[i];
                Op[j] = Op[j-1];
                down++;
                up = 0;
                Count++;
            }
        }
    }
}
 
delta = BarCount - j - 1;
 
direction = Ref( direction, - delta );
C = Ref( Line, - delta );
O = ( Ref( Op, -delta ) );
H = L = C;
 
 
Plot( C, "", 39, styleCandle );
 
Title = Name() + "  Three Line Break" + "\n" +
        "Value = " + SelectedValue( C )  + "\n" +
        "Break = " + numline;

@JohnC, the chart is showing now.

Since you are calculating the values for your 3 Line Break plotting, you should be able to simply add the code for the MA into your code and plot them directly.

I would avoid the redefining of the OHLC values and create new ones (TLBO, TLBC ...). That way you can reference the original values in any later code that you add. Then have the MA reference the TLBC value.
FastMA = MA(TLBC,10);

Hope that helps.

Thanks snoopy, really appreciated
Its not quite there, by changing the final C in the 3linebreak afl to TLBC possibly because C is being referenced throughout. but one to think about and dig into further. if i change the numline parameter for example which then changes the MA, again the backtest doesn't change. so its picking up something different for sure, just dont know what at this point

direction = Ref( direction, - delta );
TLBC = Ref( Line, - delta );
O = ( Ref( Op, -delta ) );
H = L = TLBC;
 
 
Plot( TLBC, "", 39, styleCandle );
 
Title = Name() + "  Three Line Break" + "\n" +
        "Value = " + SelectedValue( C )  + "\n" +
        "Break = " + numline;
        
FastMA = MA( TLBC, 10 ); 
SlowMA = MA( TLBC, 30 ); 
Buy = Cross( FastMA, SlowMA ); 
Sell = Cross( SlowMA, FastMA ); 

but thats probably because of my lack of technical ability which no doubt will change over time!

@JohnC

Here is the way I did it for plotting.

 direction = Ref( direction, - delta );
TLBC = Ref( Line, - delta );
TLBO = ( Ref( Op, -delta ) );
TLBH = TLBL = TLBC;
 
 
PlotOHLC( TLBO,TLBH, TLBL, TLBC,  "", colorgreen, styleCandle );
FastMA = MA( TLBC, 10 ); 
SlowMA = MA( TLBC, 30 ); 
Buy = Cross( FastMA, SlowMA ); 
Sell = Cross( SlowMA, FastMA ); 
Plot(fastma, "MA10", colorgreen, styleline);
Plot(slowma, "MA30", colorred, styleline);

Have not tried the backtest.

Hey Snoopy, I did the same as you. it plots ok, however the backtest is quite suspect

the attached chart is where the line break parameter is 2 (line 3 of the code)

GraphXSpace = 7;
 
numline = Param( "Break", 2);
j = 0;
Line[j] = C[0];

image
I've marked where the cross of the MA occurs for just long
this coincides with the backtest result..7 long trades
image

now if i change the parameter to 3, the chart displays the MAs fine resulting now in 4 crossovers
(displayed manually as the backtester can't plot the trade arrows)
image

the backtest results however still shows 7. so its clearly doing something right..and yet then doesn't recognise that things have changed. making the backtest results unreliable
image

the other more concerning element is the price itself
image
the MA crosses twice at almost identical prices..99

the back test result doesn't come close to 99..suggesting the price at the time was 121 and then 133
image

I did pass this to support, but as its afl declined to comment so that we can all learn from my misery :slight_smile:

If you change parameters of Param functions in chart they do not change to same value in analysis automatically!

Besides Param* functions have other pitfalls in analysis


Now, if you want to set params in chart and then apply same setting in analysis then use this method storing to static variables.


"Your" three line break bars are not real price data. It is artificial/synthetic compressed bars.

Compare to real close price line (red line) being overlaid.

1

Add this at the end of "your" code to see price difference on chart:

// restoring real price data        
RestorePriceArrays();

// plotting real close prices
Plot( C, "real close price", colorRed, styleLine);

2 Likes

thanks fxsrat.
for the parameter, i kind of expected it wasn't changing, so I amended the code each time (its not mine btw), saving it and then running the analysis again each time. with the same results.
so the MA is able to change, with its closing price being displayed correctly, the backtest still isn't recognising this
what im trying to say is, im not changing the parameter through the chart itself and then running the analysis, but changing the afl itself, and rerunning

I've tried adding the buyprice, setting it to TLBC which you've alluded to..again, no change to the results

Buy = Cross( FastMA, SlowMA ); 
BuyPrice=TLBC;
Sell = Cross( SlowMA, FastMA );  
SellPrice=TLBC;

I appreciate the price of the underlying is different which really comes down to the original question. as this is a nontime based chart, and the time is not really that particular date but that of the date and price of the base periodicity, can amibroker really backtest a non-time based chart?

I'll look at a reference renko chart, to see if this has actually been done before and work backwards. i can't be the first and renko is by far more popular than 3 line breaks.
or of course, just pay for someone to code it properly that does allow you to then backtest the results

There is absolutely no difference in backtesting time based on non timebased interval.
Compression happens BEFORE backtesting. Backtester does not care what timestamps bars have and it does not really matter for calculation except things like CAR that use (which are calculated correctly anyway).

@JohnC,

there aren't no differences between chart and analysis signals.

If you get different number of "trades" then you are doing something wrong on your end.
Check detailed log in analysis settings and investigate.

40

Using this code

GraphXSpace = 7;

SetPositionSize( 1, spsShares);
 
numline = 4;//Param( "Break", 2, 2, 10, 1 );

j = 0;
Line[j] = C[0];
Op[j] = C[0];
direction = 0;
up = 0; // count up lines
down = 0;// count down lines
Count = 1; // count lines
 
for ( i = 1; i < BarCount; i++ )
{
 
    if ( direction[j] == 0 ) // continues down
    {
        if ( C[i] < Line[j] )
        {
            j++;
            direction[j] = 0;
            Line[j] = C[i];
            Op[j] = Line[j-1];
            down++;
            up = 0;
            Count++;
        }
        else
        {
            if ( Count == 1 &&  C[i] > OP[j] )// I-st reverse
            {
                j++;
                direction[j] = 1;
                Line[j] = C[i];
                Op[j] = Op[j-1];
                up++;
                down = 0;
                Count++;
            }
 
            if ( Count > 1 && down  == 1 && C[i] > Line[j-1] )//  reverse after 1 down line
            {
                j++;
                direction[j] = 1;
                Line[j] = C[i];
                Op[j] = Op[j-1];
                up++;
                down = 0;
                Count++;
            }
 
            if ( Count > 1  && down  > 1 && down < numline && C[i] > Op[j-1] )//simple reverse
            {
                j++;
                direction[j] = 1;
                Line[j] = C[i];
                Op[j] = Op[j-1];
                up++;
                down = 0;
                Count++;
            }
 
            if (  down >= numline && C[i] > Op[j-( numline -1 )] ) // white trunaround line
            {
                j++;
                direction[j] = 1;
                Line[j] = C[i];
                Op[j] = Op[j-1];
                up++;
                down = 0;
                Count++;
            }
        }
 
 
 
    }
    else // continues up
    {
        if ( C[i] > Line[j] )
        {
            j++;
            direction[j] = 1;
            Line[j] = C[i];
            op[j] = Line[j-1];
            up++;
            down = 0;
            Count++;
        }
        else
        {
            if ( Count == 1  && C[i] < OP[j] )// I-st reverse
            {
                j++;
                direction[j] = 0;
                Line[j] = C[i];
                Op[j] = Op[j-1];
                down++;
                up = 0;
                Count++;
 
            }
 
            if ( Count > 1 && up  == 1 && C[i] < Line[j-1] )//  reverse after 1 up line
            {
                j++;
                direction[j] = 0;
                Line[j] = C[i];
                Op[j] = Op[j-1];
                down++;
                up = 0;
                Count++;
            }
 
            if ( Count > 1 && up  > 1 && up < numline && C[i] < Op[j-1] ) //simple reverse
            {
                j++;
                direction[j] = 0;
                Line[j] = C[i];
                Op[j] = Op[j-1];
                down++;
                up = 0;
                Count++;
            }
 
            if ( up >= numline && C[i] < Op[j-( numline -1 )] )//black trunaround line
 
            {
                j++;
                direction[j] = 0;
                Line[j] = C[i];
                Op[j] = Op[j-1];
                down++;
                up = 0;
                Count++;
            }
        }
    }
}
 
delta = BarCount - j - 1;
 
direction = Ref( direction, - delta );
C = Ref( Line, - delta );
O = ( Ref( Op, -delta ) );
H = L = C;
 
FastMA = MA( C, 10 ); 
SlowMA = MA( C, 30 ); 
Buy = Cross( FastMA, SlowMA ); 
Sell = Cross( SlowMA, FastMA ); 

BuyPrice = SellPrice = C;

Short = Cover = 0;

SetChartOptions( 0, chartShowDates | chartShowArrows | chartWrapTitle );
Plot( C, "", 39, styleCandle );
Plot(fastma, "MA10", colorgreen, styleline);
Plot(slowma, "MA30", colorred, styleline);
 
Title = Name() + "  Three Line Break" + "\n" +
        "Value = " + SelectedValue( C )  + "\n" +
        "Break = " + numline;        

N = 2:

50

N = 4;

49

2 Likes

@fxshrat
comparing your code and the one i was using the difference was coding out the parameter setting

numline = 3; //Param( "Break", 3);

and

BuyPrice = SellPrice = TLBC;

thank you so, so much! and @snoopy.pa30 @Tomasz for the guidance