Help me understand PeakBars/TroughBars

buytrigger = LLV( C + ( C / 100 ) * pr, BarsSince( pk ) );
shorttrigger = HHV( C - ( C / 100 ) * pr, BarsSince( tr ) );
//buytrigger = LowestSince( pk,  C + ( C / 100 ) * pr );
//shorttrigger= HighestSince( tr, C - ( C / 100 ) * pr );

I do not see signals disappear when using the LowestSince/HighestSince and it gives better results than LLV/HHV.

I am watching in live market. So, could you please let me know exactly when/where the signals disappears so that I can watch more carefully to confirm.

I do not rely on bar replay and hence prefer to test in live market only.

they should give the same result but I noticed that when scrolling the chart the signals flicker on/off when using LowestSince/HighestSince. This has something to do with the number of bars used in the calculation. If you use setbarsrequired( sbrall, sbrall ) then you will not see this flickering but then the code becomes real slow.

So there is no problem using LowestSince/HighestSince only when you scroll the chart but for trading there is no problem.

You also mentioned "repainting" earlier. I think you meant the ZigZag line itself, right? You could avoid that by plotting the zig-line differently.

Good Morning!

To deal further with our last code in order to minimize noise to some extent can we implement the below idea:

Along with all the present condition a new Buy should generate if C is greater than the previous peak and similarly a new Short should generate if C is lower than the previous trough.

Ffirst advise if it is logical at all.

yes that is also a method many use. Problem is the many false breakouts, I was never successful making a profitable system out of it.

but in my code the most recent High is in the array ph1, and the most recent low is in the array tl1. So breakout signals would be:

Buy = Cross( C, ph1 );
Short = Cross( tl1, C );

or do you mean breakouts inside the the uptrend and dntrend?

Inside the dntrend a new trough is defined as:

trn = LowestSince( pk, L ) == L && dntrend;

and inside the uptrend a new peak is defined as:

pkn = HighestSince( tr, H ) == H && uptrend;

so if you want to buy inside an uptrend when the most recent pkn is broker you write:

Buy = Cross( H, Valuewhen( pkn, H ) ) AND uptrend;

Yes, I mean breakouts inside the the uptrend and dntrend.

But , I am confused where to add the below lines as in our last code uptrend & dntrend is defined after pk & tr code.

yes you first define the uptrend and dntrend using pk and tr and below that you can define the other sets of peaks and troughs.

But for the code to give correct buy/sell signals it has to be rewritten a bit again. It is something you should practice yourself, else you will never learn. I can help a bit further on this but send me an private email since else we take up too much space on the forum, since probably we are the only 2 reading this thread.

Only thing I will add to this thread is an alternative peak/through setup using ATR. I have posted this before but this one has the same setup as the earlier Percent ZigZag code. Nice thing about ATR is that you can switch to another time frame and the peaks and troughs look similar since they use ATR for their calculation.

Also this simple setup can be profitable although not fantastic ...

// E.M.Pottasch, 3/2018
// ATR ZigZag
// http://forum.amibroker.com/t/help-me-understand-peakbars-troughbars/5072

// OptimizerSetEngine( "cmae" );
SetTradeDelays( 0, 0, 0, 0 );
SetOption( "CommissionMode", 3 );
SetOption( "CommissionAmount", 2.32 );
SetOption( "FuturesMode", True );
NumContracts = 1;
PositionSize = NumContracts * MarginDeposit;
SetOption( "MaxOpenPositions", 4 );

function ParamOptimize( pname, defaultval, minv, maxv, step )
{
    return Optimize( pname,
                     Param( pname, defaultval, minv, maxv, step ),
                     minv, maxv, step );
}

// perBull = Param( "perBull", 50, 1, 300, 1 );
perBull = perBear = ParamOptimize( "perBear", 50, 1, 300, 1 );
// multBull = ParamOptimize( "multBull", 10, 1, 20, 0.1 );
multBull = multBear = ParamOptimize( "multBear", 10, 1, 20, 0.1 );
shiftLabels = Param( "Shift Labels", 12, -20, 100, 1 );
labelsswitch = ParamToggle( "Show Labels", "Off|On", 1 );
sz = Param( "Font Size", 8, 4, 16, 1 );

upColor = ColorRGB( 0, 250, 250 );
dnColor = ColorRGB( 250, 250, 0 );

bi = BarIndex();
fvb = FirstVisibleValue( bi );
lvb = LastVisibleValue( bi );

sup = C - multBull * ATR( perBull );
res = C + multBear * ATR( perBear );

trailArray = Null;
trailstop = 0;

for( i = 1; i < BarCount; i++ )
{
    if( C[ i ] > trailstop AND C[ i - 1 ] > trailstop )
        trailstop = Max( trailstop, sup[ i ] );
    else
        if( C[ i ] < trailstop AND C[ i - 1 ] < trailstop )
            trailstop = Min( trailstop, res[ i ] );
        else
            trailstop = IIf( C[ i ] > trailstop, sup[ i ], res[ i ] );

    trailArray[ i ] = trailstop;
}

dntrend = ts = IIf( trailArray > C, trailArray, Null ); // dntrend
uptrend = tl = IIf( trailArray < C, trailArray, Null ); // uptrend

lll = LLV( L, BarsSince( !IsEmpty( tl ) ) );
//lll = LowestSince( !IsEmpty( tl ), L );
lll = IIf( ts, lll, Null );
trm = ts AND L == lll;

hhh = HHV( H, BarsSince( !IsEmpty( ts ) ) );
//hhh = HighestSince( !IsEmpty( ts ), H );
hhh = IIf( tl, hhh, Null );
pkm = tl AND H == hhh;

tr = ExRem( Reverse( trm ), Reverse( pkm ) );
pk = ExRem( Reverse( pkm ), Reverse( trm ) );

tr = Reverse( tr );
pk = Reverse( pk );

for( i = 0; i < 3; i++ )
{
    VarSet( "px" + i, ValueWhen( pk, bi, i ) );
    VarSet( "tx" + i, ValueWhen( tr, bi, i ) );
    VarSet( "ph" + i, ValueWhen( pk, H, i ) );
    VarSet( "tl" + i, ValueWhen( tr, L, i ) );
}

ll = tr AND tl1 < tl2;
hl = tr AND tl1 > tl2;
hh = pk AND ph1 > ph2;
lh = pk AND ph1 < ph2;
dt = pk AND ph1 == ph2;
db = tr AND tl1 == tl2;

trn = LowestSince( pk, L ) == L && dntrend;
pkn = HighestSince( tr, H ) == H && uptrend;

Buy = uptrend && IsEmpty( dntrend );
BuyPrice = C;
Short = dntrend && IsEmpty( uptrend );
ShortPrice = C;
Buy = ExRem( Buy, Short );
Short = ExRem( Short, Buy );

Sell = Short;
SellPrice = C;
Cover = Buy;
CoverPrice = C;

BuyPrice = ValueWhen( Buy, BuyPrice );
ShortPrice = ValueWhen( Short, ShortPrice );
CoverPrice = ValueWhen( Cover, CoverPrice );
SellPrice = ValueWhen( Sell, SellPrice );

longResult = Prec( IIf( Sell, SellPrice - ValueWhen( Buy, BuyPrice ), 0 ), 2 );
shortResult = Prec( IIf( Cover, ValueWhen( Short, ShortPrice ) - CoverPrice, 0 ), 2 );

GraphGridZOrder = 0;
SetChartBkColor( ColorRGB( 0, 0, 0 ) );
SetChartOptions( 0, chartShowArrows | chartShowDates );
Plot( C, "C", colorWhite, styleCandle, Null, Null, 0, 0, 1 );
Plot( ts, "Buy Trigger", upColor, styleDashed | styleStaircase | styleNoRescale | styleNoLabel, Null, Null, 0, 1, 1 );
Plot( tl, "Short Trigger", dnColor, styleDashed | styleStaircase | styleNoRescale | styleNoLabel, Null, Null, 0, 1, 1 );
Plot( 1, "", IIf( uptrend, ColorRGB( 0, 0, 20 ), ColorRGB( 20, 0, 0 ) ), styleArea | styleOwnScale | styleNoLabel | styleNoRescale, 0, 1, 0, -2, 5 );
PlotShapes( shapeSmallCircle * trn, ColorRGB( 0, 60, 0 ), 0, L, -10 );
PlotShapes( shapeSmallCircle * pkn, ColorRGB( 80, 0, 0 ), 0, H, 10 );
PlotShapes( shapeSmallCircle * tr, ColorRGB( 0, 255, 0 ), 0, L, -10 );
PlotShapes( shapeSmallCircle * pk, ColorRGB( 255, 0, 0 ), 0, H, 10 );
PlotShapes( IIf( Buy, shapeUpArrow, shapeNone ), upColor, 0, L, -15 );
PlotShapes( IIf( Buy, shapeSmallCircle, shapeNone ), upColor, 0, BuyPrice, 0 );
PlotShapes( IIf( Short, shapeDownArrow, shapeNone ), dnColor , 0, H, -15 );
PlotShapes( IIf( Short, shapeSmallCircle, shapeNone ), dnColor, 0, ShortPrice, 0 );
PlotShapes( IIf( Cover, shapeUpArrow, shapeNone ), upColor, 0, L, -15 );
PlotShapes( IIf( Cover, shapeSmallCircle, shapeNone ), upColor, 0, CoverPrice, 0 );
PlotShapes( IIf( Sell, shapeDownArrow, shapeNone ), dnColor , 0, H, -15 );
PlotShapes( IIf( Sell, shapeSmallCircle, shapeNone ), dnColor, 0, SellPrice, 0 );

ft = "Helvetica";
clr = ColorRGB( 10, 10, 10 );//colorDefault;
GfxSetZOrder( 0 );
GfxSetCoordsMode( 1 );

if( labelsswitch )
{
    for( i = fvb; i <= lvb; i++ )
    {
        if( Buy[i] && Cover[i] )
        {
            str = "Buy: " + BuyPrice[i];
            PlotTextSetFont( str, ft, sz, i, L[i], upColor, clr, -35 - shiftLabels );
            str = "Cover: " + CoverPrice[i] + " (" + shortResult[i] + ")";
            PlotTextSetFont( str, ft, sz, i, L[i], upColor, clr, -50 - shiftLabels );
        }

        if( !Buy[i] && Cover[i] )
        {
            str = "Cover: " + CoverPrice[i] + " (" + shortResult[i] + ")";
            PlotTextSetFont( str, ft, sz, i, L[i], upColor, clr, -35 - shiftLabels );
        }

        if( Buy[i] && !Cover[i] )
        {
            str = "Buy: " + BuyPrice[i];
            PlotTextSetFont( str, ft, sz, i, L[i], upColor, clr, -35 - shiftLabels );
        }

        if( Short[i] && Sell[i] )
        {
            str = "Short: " + ShortPrice[i];
            PlotTextSetFont( str, ft, sz, i, H[i], dnColor, clr, 25 + shiftLabels );
            str = "Sell: " + SellPrice[i] + " (" + longResult[i] + ")";
            PlotTextSetFont( str, ft, sz, i, H[i], dnColor, clr, 40 + shiftLabels );
        }

        if( !Short[i] && Sell[i] )
        {
            str = "Sell: " + SellPrice[i] + " (" + longResult[i] + ")";
            PlotTextSetFont( str, ft, sz, i, H[i], dnColor, clr, 25 + shiftLabels );
        }

        if( Short[i] && !Sell[i] )
        {
            str = "Short: " + ShortPrice[i];
            PlotTextSetFont( str, ft, sz, i, H[i], dnColor, clr, 25 + shiftLabels );
        }

        if( ll[i] )
        {
            str = "LL";
            PlotTextSetFont( str, ft, sz, i, L[i], upColor, clr, -30 );
        }

        if( hl[i] )
        {
            str = "HL";
            PlotTextSetFont( str, ft, sz, i, L[i], upColor, clr, -30 );
        }

        if( db[i] )
        {
            str = "DB";
            PlotTextSetFont( str, ft, sz, i, L[i], upColor, clr, -30 );
        }

        if( hh[i] )
        {
            str = "HH";
            PlotTextSetFont( str, ft, sz, i, H[i], dnColor, clr, 20 );
        }

        if( lh[i] )
        {
            str = "LH";
            PlotTextSetFont( str, ft, sz, i, H[i], dnColor, clr, 20 );
        }

        if( dt[i] )
        {
            str = "DT";
            PlotTextSetFont( str, ft, sz, i, H[i], dnColor, clr, 20 );
        }

        if( tr[i] )
        {
            GfxSelectPen( dnColor, 1 );
            GfxMoveTo( tx1[i], tl1[i] );
            GfxLineTo( px1[i], ph1[i] );
        }

        if( pk[i] )
        {
            GfxSelectPen( upColor, 1 );
            GfxMoveTo( px1[i], ph1[i] );
            GfxLineTo( tx1[i], tl1[i] );
        }
    }
}
4 Likes

Thanks for your contribution, what a wonderfull code.

I tried to run it and did some optimizing on Nasdaq stocks. I have the impression that adding additional conditions can make a good system of this.

However, sometimes I get this error.
Invalid argument values for Reverse() function. The function expects that first<last (ln79 col25)

Any idea why that happens? It sure has to do with the start and end date of the analysis but
I did not find any logic yet.

I did not test the code extensively and tested only on futures. I just tried on nasdaq stocks but did not see the error. What number does the error have? ( http://www.amibroker.com/guide/errors/ ).

The error description you give suggests that I use incorrect first and last arguments for the Reverse function but I use default values.

also when testing on Nasdaq stocks do you use something like this code at the top?

SetOption( "InitialEquity", 200000 );
SetOption( "MinShares", 10 );
SetOption( "CommissionMode", 3 );
SetOption( "CommissionAmount", 0.006 );
SetOption( "MarginRequirement", 100 );
SetTradeDelays( 0, 0, 0, 0 );
Qty = 20;
SetOption( "maxopenpositions", Qty );
SetOption( "AllowSameBarExit", True );
PositionSize = -100 / Qty;
PositionScore = mtRandom();

Does the error occur for a certain stock? Maybe 1 with few data points?

When I use parameter values that are out of range with the data then I still did not get this error, I only get -nan and -inf values in the optimization.

When I find the error myself will get back on it.

WOW!!!

Others also started reading this now.

Anyway,just to bring it to your notice ---- I sent you an email and waiting for your comments.

its works outstanding in daily trades, thanks to the developer mr. santy and mr. edward pottasch,i have been a great fan of him, i have read few of his awesome codes online, most of them work really well in real trading

All credit goes to the Legend---@empottasch.

I just imagined and thought some ideas and those ideas could not have been realized with out the superb coding from ---@empottasch.

i agree, he has been one of the most outstanding exponent of amibroker afl, we are indeed lucky to have him in the forum guiding young people!!

yeah I am a Legend in search of the Holy Grail :stuck_out_tongue_winking_eye:

i by responded to your question already early this morning (via email)

1 Like

Yes, I got it.

I will test tonight and get back to you then.

Thanks for your response.
It is error 52. I try to isolate it.

I added your portfolio settings but that made no difference.

If I run an analysis on Nasdaq100 stocks for example 6-6-2015 till 6-6-2017 it shows error 52.
I can't find any single ticker that causes the problem if I run them all separate as a single ticker.

If I run from 6-6-2015 till 12-12-2018 it doesn't show error 52 which is strange.
If I use all data it also doesn't show error 52
It doesn't matter what timeframe (5min,hour, day) I choose with these different
start and end data, if the error 52 shows up it will do so with any timeframe.

If I test with all Nasdaq ETF's (about 1800) Amibroker crashes everytime (no error 52), so it
could be a Amibroker issue.
This is the message I get on a big database of 1800 etf's

AmiBroker version 6.28.0.6280
( 64-bit, cooltool.dll 6.28.0, mfc42.dll 6.28.0, msvcrt.dll 7.0.16299 )

Microsoft Windows 10 version 10.0 (Build 16299)
Service Pack 0.0, Common Controls: 6.16

Unhandled exception
Type: CSysException
Code: c0000005
Description: ACCESS VIOLATION
Address: 00007FF7EF6ECD64

hhh = IIf( tl, hhh, Null );
pkm = tl AND H == hhh;

tr = ExRem( Reverse( trm
------------------------^
File: 'Formulas\Custom\help-me-understand-peakbars-troughbars.afl', Ln: 115, Col: 25
Error 47.
Exception occurred during AFL formula execution at address: 00007FF7EF6ECD64, code: C0000005
Call Stack:
00007FF7EF6ECD64 Broker, (function-name not available)() +0 byte(s)
00007FF7EF6BF7EA Broker, (function-name not available)() +0 byte(s)
00007FF7EF6BEA39 Broker, (function-name not available)() +0 byte(s)
00007FF7EF6BD8F6 Broker, (function-name not available)() +0 byte(s)
00007FF7EF6BE984 Broker, (function-name not available)() +0 byte(s)
00007FF7EF6BE733 Broker, (function-name not available)() +0 byte(s)
00007FF7EF6BD802 Broker, (function-name not available)() +0 byte(s)
00007FF7EF6AFA09 Broker, (function-name not available)() +0 byte(s)
00007FF7EF6F135F Broker, (function-name not available)() +0 byte(s)
00007FF7EF6B217E Broker, (function-name not available)() +0 byte(s)
00007FF7EF6D0D91 Broker, (function-name not available)() +0 byte(s)
00007FFF95BFD986 mfc140, (function-name not available)() +0 byte(s)
00007FFFD2B9DC05 ucrtbase, iswascii() +165 byte(s)
00007FFFD4CB1FE4 KERNEL32, BaseThreadInitThunk() +20 byte(s)
00007FFFD67EEFC1 ntdll, RtlUserThreadStart() +33 byte(s)

Additional information:

Multi-threaded charts - ENABLED

Number of stock loaded: 2068
Currently selected stock: TQQQ
Number of quotes (current stock): 9215

Workspace:
Data source = (default), Data local mode = 2, NumBars = 1

Preferences:
Data source = (local), Data local mode = 1, NumBars = 10000

Command history:
3393 - Creates new Analysis--New Analysis
3408 - New document--New Tab
7913 - Open this database

Cache manager stats:
Number of list elements: 790
Number of map elements: 790
Hash table size: 16001

Memory status:
MemoryLoad: 30 %
TotalPhys: 16717268K AvailPhys: 11574956K
TotalPageFile: 24057300K AvailPageFile: 18542076K
TotalVirtual: 4294967168K AvailVirtual: 4294512504K

Log:
Logging started 2018-03-21 20:09:36
1.52 ms : Enabling low frag heap (0.06 ms)
1.58 ms : Launching splash screen (1.95 ms)
3.53 ms : Waiting for splash screen (13.91 ms)

too bad I can't make the error happen, but last year when I was working on a plugin using the Reverse() function I had similar issues and I ended up writing my own Reverse function.

I like to use the Reverse() function because it looks so elegant. But I can also solve it differently. Could you maybe try to replace these equivalent lines of code with this?

//lll = LLV( L, BarsSince( !IsEmpty( tl ) ) );
lll = LowestSince( !IsEmpty( tl ), L );
lll = IIf( ts, lll, Null );
trm = ts AND L == lll;

//hhh = HHV( H, BarsSince( !IsEmpty( ts ) ) );
hhh = HighestSince( !IsEmpty( ts ), H );
hhh = IIf( tl, hhh, Null );
pkm = tl AND H == hhh;

rtrm = Reverse( trm, 0, -1 );
rpkm = Reverse( pkm, 0, -1 );

tr = ExRem( rtrm, rpkm );
pk = ExRem( rpkm, rtrm );

tr = Reverse( tr, 0, -1 );
pk = Reverse( pk, 0, -1 );

should not make a difference. If that doesn't work I'll post an alternative

Thanks for your help and code.

Unfortunately it makes no difference, the error 52 stays when running on the same data.

i also have an alternative way but maybe you can first try my own reverse function and see if that solves the problem.

function Reverse1( rrin )
{
    rrout = 0;

    for( i = 0; i < BarCount; i++ )
    {
        rrout[BarCount - 1 - i] = rrin[i];
    }

    return rrout;
}

rtrm = Reverse1( trm );
rpkm = Reverse1( pkm );

tr = ExRem( rtrm, rpkm );
pk = ExRem( rpkm, rtrm );

tr = Reverse1( tr );
pk = Reverse1( pk );

Thanks for the code.

When backtesting a portfolio now it runs without error 52.

However when backtesting a portfolio the progress bar goes upto 99% but then it stops progressing,
when pressing the red button (cancel) it shows a few trades.
The optimize function on a portfolio stays in an endless loop

When backtesting a single ticker it runs without an error but only shows one trade.
The date of the trade is always between the start and end date of the database.
And when optimizing on a single ticker all generated systems also only have one trade.

//tr = ExRem( Reverse( trm ), Reverse( pkm ) );
//pk = ExRem( Reverse( pkm ), Reverse( trm ) );

//tr = Reverse( tr );
//pk = Reverse( pk );

function Reverse1( rrin )
{
    rrout = 0;

    for( i = 0; i < BarCount; i++ )
    {
        rrout[BarCount - 1 - i] = rrin[i];
    }

    return rrout;
}

rtrm = Reverse1( trm );
rpkm = Reverse1( pkm );

tr = ExRem( rtrm, rpkm );
pk = ExRem( rpkm, rtrm );

tr = Reverse1( tr );
pk = Reverse1( pk );



for( i = 0; i < 3; i++ )