Auto horizontal lines -- Coordinate 'y1'?

Hello everyone.

Since last 8 months, struggling to write a code to draw simple auto horizontal-lines on chart. Unable to write a user-function or loop due to a specific problem. My problem is the position of the ‘y1’ coordinate of the line. No problem with the x0, x1, y0 coordinates. The problem is that ‘y1’ coordinate is available in terms of open, high, low and close price only. And if I choose them, then most of the time, line will not be a perfect horizontal-line, because its not possible that these values will always happen to be horizontal in reference to ‘x1’ coordinate.

Initially, I was using ‘plot()’ function in my attempts. Later, after researching the AmiBroker’s help-guide and the various posts in forum, I learned that the use of ‘plot()’ function is not efficient in such cases. Struggled so many ways that now it’s difficult for me, even to determine what should be the right approach and from where I should start. If anyone have any sample-code or idea to deal with such a condition, then kindly share it. For help I am specifically looking forward to @Milosz and @fxshrat. Kindly guide.

Take a look at the attached snapshots. Below are the conditions for the line are below:

A). The line is horizontal-line.

B). The maximum length of the line is not more than 100 periods.

C). All the lines are displayed within the range of visible-chart area only.

D). The end of line is always when:
(Left-side) Peak’s high-price <= Right-side price
Or
(Left-side) Trough’s low-price >= Right-side price

E). The line’s start is always:
(Left-side) Peak’s high-price
Or
(Left-side) Trough’s low-price

F). The line is drawn only when the end-point for the line becomes available, not before.

G). The line is displayed immediately even if the end-point is available from an un-finished current bar.

H). The number of bars that the line has covered are counted and shown at the middle of the line’s length. Also, the position of the count is above the line, if the starting point was a Peak, and below the line, if the line’s starting point was a Trough.

@LotusHeart, if you already have written some code, especially when using a simple loop over the visible bars, I wonder why you cannot simply assign y1 the same value as y0 ?

I suggest you post your code and maybe someone here can help you fix it.

2 Likes

Hello @beppe

First of all thanks for responding.

I have written very many codes but the thing is that due to problem of 'y' cordinate they were not complete and thereby not debuggable also. Further after realizing that using the plot() function is not efficient. I deleted all of them. I am not asking you to write a code for me. However if possible, I will be greatful. Anyway, as mentioned already my original intention is to have some sample code for my analysis or just some idea to overcome the problem. If I use the end-point bar's any O-H-L-C value then line is no more horizontal. Kindly check the snapshot.

I usually avoid loops, and prefer array based solution. But now I am so much desprate for the solution that even loops will do.

There already exists code

You just have to change arguments / variables
Targetarray being trough or peak and Short signal e.g. being L == targetarray.
Of course for peaks you have to use H array not L array also within function.

Plot shapes and Plot line of the code can be removed. They are not required anyway.

So for troughs it would look like this

Helo @fxshrat

Thanks for paying attention and responding. This much is more than enough for me. I am very happy that you left some homework for by providing code for only trough-side lines. I will deligently debug it to understand the logic and working behind the code. Also I already read the post you shared and tried to tweak it as per my need, but I failed many times. That's why I specifically asked for help from you. Just one more thing I want to ask. The count of periods on the left side of the line is covering the candlesticks. How can I make the count appear in the middle and below the line? Please check the very first snapshot to visually see what I mean. Very grateful to you and @beppe also for considering and responding to my question.

Middle of line is (x1+x2)*0.5

Ok understood. Thanks a lot. :smile:

Edit percent_change to your needs.

function fxMxStopLines(sig, targetarray, visi_chart) {
  /// @link https://forum.amibroker.com/t/plot-horizontal-line-until-crossed/10802/32
  /// by fxshrat@gmail.com
  bir = Status("barvisible");// chart area array  
  if ( visi_chart ) {  
    Cum_s = Cum(IIf(bir, sig, 0));
    lastcum = LastVisibleValue(Cum_s);
  } else {          
    Cum_s = Cum(sig);
    lastcum = LastValue(Cum_s, 1);
  }
  stop_mat = Matrix(3, Max(1,lastcum), 0);
  colnum = MxGetSize(stop_mat, 1);
  draw_range = IIf( visi_chart, bir, 1);
  lastbar = BarCount-1;
  for ( i = 0, n = 0; i < BarCount; i++ ) {
    if ( draw_range[i] ) { // if visible chart
      if ( sig[i] ) {  // if signal      
        if ( n < colnum ) { // ensure not exceeding mat size
          stop_mat[0][n] = i; // start bar
          stop_mat[1][n] = lastbar; // end bar
          stop_mat[2][n] = targetarray[i]; // level        
          for ( j = i+1, k = 0; j < BarCount; j++ ) {
            if ( L[j] < stop_mat[2][n] ) {
              stop_mat[1][n] = j; // end bar
              break;
            }
          }
        }
        n++;
      }
    }
  }    
  return stop_mat;
}

Version(6.0);

//RequestTimedRefresh( 1 );
SetChartOptions( 0, chartShowDates | chartShowArrows | chartWrapTitle );
_N( Title = StrFormat( "{{NAME}} - {{INTERVAL}} - {{DATE}} Open %g, Hi %g, Lo %g, Close %g (%.1f%%), Vol %g {{VALUES}}",
                           O, H, L, C, SelectedValue( ROC( C, 1 ) ), V ) );
Plot( C, "", colorDefault, styleBar );

GfxSetZOrder( 0 );
GfxSetCoordsMode( 1 );
GfxSetBkMode(1);
GfxSetTextAlign(0|0);
GfxSelectFont("Arial", 8, 500 );  
GfxSetTextColor( colorLightYellow );
GfxSetBkColor(colorRed);
GfxSelectPen(colorRed, 1, 2);

percent_change = 2; //edit percent change

targetarray = Trough(L, percent_change);//tlas-stopvalue;
max_bars = 100; // edit maximum number of periods of line to be drawn
draw_all_lines = True;
draw_label = True;
visichart = true;
stop_mat = fxMxStopLines(L == targetarray, targetarray, visichart);

lastbar = BarCount-1;
colnum =  MxGetSize(stop_mat, 1);
for ( n = 0; n < colnum; n++ ) {
  x2 = stop_mat[1][n];
  if ( x2 == lastbar OR draw_all_lines ) {
    x1 = stop_mat[0][n];
    if ( x2-x1 <= max_bars ) {
      y = stop_mat[2][n];      
      GfxMoveTo(x1, y);
      GfxLineTo(x2, y); 
      if ( draw_label) {
        GfxTextOut(StrFormat("%g", y), x1, y ); 
        GfxTextOut(StrFormat("%g bars", x2-x1), (x1+x2)*0.5, y );  
      }
    }
  }  
}
/// ....

printf( "\n%s", MxToString(stop_mat));
printf( "\n#lines: %g, BarCount: %g", MxGetSize(stop_mat,1), BarCount );
1 Like

Sorry to bother you again. My chat's scale is not in alignment with your code. Everything has compressed towards the upperside of chart. Please check the code and snapshot. Neither I have been able to find the place to insert the zigzag's pk (peak) and tr (trough) values.

_SECTION_BEGIN( "Empottasch-ZiZag" );


nbar = Param( "Swing number of Bars", 10, 1, 50, 1 );
priceswitch = ParamToggle( "Use Close or High and Low price", "Use Close|Use High and Low", 1 );
showTrailline = ParamToggle( "Show Trail Line", "Off|On", 0 );
showprimarypivotlabels = ParamToggle( "Show Primary Pivot Labels", "Off|On", 0 );
// this toggle is for when in H/L mode the high is above res and the low below sup. There are 2 ways to handle this.
handleBothOutside = ParamToggle( "Long AND Short signal at the SAME bar", "Change Trend|Stay with current Trend", 0 );

bi = BarIndex();
fvb = FirstVisibleValue( bi );
lvb = LastVisibleValue( bi );
pk = tr = 0;
ZigZag = line1 = Null;

if( priceswitch )
{
    prch = H;
    prcl = L;
    mode = "High/Low price";
}
else
{
    prch = C;
    prcl = C;
    mode = "Close price";
}

function calculateZigZag()
{
    zigup = Flip( tr, pk );
    zigupLow = ValueWhen( tr, prcl, 1 );
    zigupHigh = ValueWhen( pk, prch, 0 );
    zigupLowIndex = ValueWhen( tr, bi, 1 );
    zigupHighIndex = ValueWhen( pk, bi, 0 );
    slopeup = IIf( zigup, ( zigupHigh - zigupLow ) / ( zigupHighIndex - zigupLowIndex ) , Null );
    zigupLine = IIf( zigup, zigupLow + slopeup * BarsSince( tr ), Null );

    zigdn = Flip( pk, tr );
    zigdnLow = ValueWhen( tr, prcl, 0 );
    zigdnHigh = ValueWhen( pk, prch, 1 );
    zigdnLowIndex = ValueWhen( tr, bi, 0 );
    zigdnHighIndex = ValueWhen( pk, bi, 1 );
    slopedn = IIf( zigdn, ( zigdnLow - zigdnHigh ) / ( zigdnLowIndex - zigdnHighIndex ) , Null );
    zigdnLine = IIf( zigdn, zigdnHigh + slopedn * BarsSince( pk ), Null );

    ZigZag = IIf( zigup, zigupLine, IIf( zigdn, zigdnLine, Null ) );
    ZigZag = IIf( bi > Max( LastValue( ValueWhen( tr, bi ) ), LastValue( ValueWhen( pk, bi ) ) ), Null, ZigZag );
}
function calculateLastSegments()
{
    lastidxpk = LastValue( ValueWhen( pk, bi ) );
    lastidxtr = LastValue( ValueWhen( tr, bi ) );
    lastvalpk = LastValue( ValueWhen( pk, prch ) );
    lastvaltr = LastValue( ValueWhen( tr, prcl ) );
    valpk = LastValue( HighestSince( Ref( tr, -1 ), prch , 1 ) );
    idxpk = LastValue( ValueWhen( prch == valpk, bi ) );
    valtr = LastValue( LowestSince( Ref( pk, -1 ), prcl, 1 ) );
    idxtr = LastValue( ValueWhen( prcl == valtr, bi ) );

    if( lastidxpk > lastidxtr )
    {
        x0 = lastidxpk;
        y0 = lastvalpk;
        x1 = idxtr;
        y1 = valtr;
        line1 = linedn = LineArray( x0, y0, x1, y1 );
        tr[idxtr] = 1;
        valpk = LastValue( HighestSince( Ref( tr, -1 ), prch, 1 ) );
        idxpk = LastValue( ValueWhen( prch == valpk, bi ) );
    }

    if( lastidxpk < lastidxtr )
    {
        x0 = lastidxtr;
        y0 = lastvaltr;
        x1 = idxpk;
        y1 = valpk;
        line1 = lineup = LineArray( x0, y0, x1, y1 );
        pk[idxpk] = 1;
        valtr = LastValue( LowestSince( Ref( pk, -1 ), prcl, 1 ) );
        idxtr = LastValue( ValueWhen( prcl == valtr, bi ) );
    }

    ZigZag = IIf( !IsEmpty( line1 ), line1, ZigZag );
}

// find trend
res = HHV( prch, nbar );
sup = LLV( prcl, nbar );
crossup = prch > Ref( res, -1 );
crossdn = prcl < Ref( sup, -1 );

if( handleBothOutside )
{
    // if in H/L mode both crossup and crossdn at the same bar may occur.
    // if handleBothOutside is true then it will stay in the current trend
    // if handleBothOutside is false it will change the trend.
    crossup = IIf( ( crossup AND crossdn ) AND( BarsSince( Ref( crossup, -1 ) ) > BarsSince( Ref( crossdn, -1 ) ) ), 0, crossup );
    crossdn = IIf( ( crossup AND crossdn ) AND( BarsSince( Ref( crossdn, -1 ) ) > BarsSince( Ref( crossup, -1 ) ) ), 0, crossdn );
}

crossup = ExRem( crossup, crossdn );
crossdn = ExRem( crossdn, crossup );
trendup = Flip( crossup, crossdn );
trenddn = Flip( crossdn, crossup );

// find pivots
rtrenddn = Reverse( trenddn );
rtrendup = Reverse( trendup );
l1 = LowestSince( trenddn AND Ref( trendup, -1 ), prcl );
rL = Reverse( prcl );
l2 = LowestSince( rtrenddn AND Ref( rtrendup, -1 ), rL );
rl2 = Reverse( l2 );
h1 = HighestSince( trendup AND Ref( trenddn, -1 ), prch );
rH = Reverse( prch );
h2 = HighestSince( rtrendup AND Ref( rtrenddn, -1 ), rH );
rh2 = Reverse( h2 );
tr = l1 == rl2 AND trenddn;
pk = h1 == rh2 AND trendup;
rpk = Reverse( pk );
rtr = Reverse( tr );
rpk = ExRem( rpk, rtr );
rtr = ExRem( rtr, rpk );
pk = Reverse( rpk );
tr = Reverse( rtr );

calculateZigZag();
calculateLastSegments();



if( showtrailline )
{
    if( priceswitch )
    {
        bp = Max( O, Ref( res, -1 ) );
        sp = Min( O, Ref( sup, -1 ) );
    }
    else
    {
        bp = C;
        sp = C;
    }

    Plot( IIf( trendup, sup, Null ), "", colorGreen, styleStaircase | styleNoRescale, Null, Null, 0, 0, 1 );
    Plot( IIf( trenddn, res, Null ), "", colorRed, styleStaircase | styleNoRescale, Null, Null, 0, 0, 1 );
    PlotShapes( IIf( trendup AND Ref( trenddn, -1 ), shapeSmallCircle, shapeNone ), colorWhite, 0, bp, 0 );
    PlotShapes( IIf( trenddn AND Ref( trendup, -1 ), shapeSmallCircle, shapeNone ), colorWhite, 0, sp, 0 );
}

Plot( zigzag, "ZigZag", colorAqua, styleLine | styleNoLabel | styleNoRescale, Null, Null, 0, 1, 2 );
//PlotShapes( IIf( tr, shapeSmallCircle, shapeNone ), colorGreen, 0, L, -10 );
//PlotShapes( IIf( pk, shapeSmallCircle, shapeNone ), colorRed, 0, H, 10 );


_SECTION_END();

//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


_SECTION_BEGIN( "Fxshrat-Lines" );


fxshrat_Line_Zorder = Param( "fxshrat's Line Zorder", 1, -5, 5, 1 );


function fxMxStopLines(sig, targetarray, visi_chart) {
  /// @link https://forum.amibroker.com/t/plot-horizontal-line-until-crossed/10802/32
  /// by fxshrat@gmail.com
  bir = Status("barvisible");// chart area array  
  if ( visi_chart ) {  
    Cum_s = Cum(IIf(bir, sig, 0));
    lastcum = LastVisibleValue(Cum_s);
  } else {          
    Cum_s = Cum(sig);
    lastcum = LastValue(Cum_s, 1);
  }
  stop_mat = Matrix(3, Max(1,lastcum), 0);
  colnum = MxGetSize(stop_mat, 1);
  draw_range = IIf( visi_chart, bir, 1);
  lastbar = BarCount-1;
  for ( i = 0, n = 0; i < BarCount; i++ ) {
    if ( draw_range[i] ) { // if visible chart
      if ( sig[i] ) {  // if signal      
        if ( n < colnum ) { // ensure not exceeding mat size
          stop_mat[0][n] = i; // start bar
          stop_mat[1][n] = lastbar; // end bar
          stop_mat[2][n] = targetarray[i]; // level        
          for ( j = i+1, k = 0; j < BarCount; j++ ) {
            if ( L[j] < stop_mat[2][n] ) {
              stop_mat[1][n] = j; // end bar
              break;
            }
          }
        }
        n++;
      }
    }
  }    
  return stop_mat;
}

Version(6.0);

//RequestTimedRefresh( 1 );

SetChartOptions( 0, chartShowDates | chartShowArrows | chartWrapTitle );
_N( Title = StrFormat( "{{NAME}} - {{INTERVAL}} - {{DATE}} Open %g, Hi %g, Lo %g, Close %g (%.1f%%), Vol %g {{VALUES}}",
                           O, H, L, C, SelectedValue( ROC( C, 1 ) ), V ) );
Plot( C, "", colorDefault, styleBar );

 
GfxSetZOrder( fxshrat_Line_Zorder );
GfxSetCoordsMode( 1 );
GfxSetBkMode(1);
GfxSetTextAlign(0|0);
GfxSelectFont("Arial", 8, 500 );  
GfxSetTextColor( colorLightYellow );
GfxSetBkColor(colorRed);
GfxSelectPen(colorRed, 1, 2);

percent_change = 2; //edit percent change

targetarray = Trough(L, percent_change);//tlas-stopvalue;
max_bars = 100; // edit maximum number of periods of line to be drawn
draw_all_lines = True;
draw_label = True;
visichart = true;
stop_mat = fxMxStopLines(L == targetarray, targetarray, visichart);

lastbar = BarCount-1;
colnum =  MxGetSize(stop_mat, 1);
for ( n = 0; n < colnum; n++ ) {
  x2 = stop_mat[1][n];
  if ( x2 == lastbar OR draw_all_lines ) {
    x1 = stop_mat[0][n];
    if ( x2-x1 <= max_bars ) {
      y = stop_mat[2][n];      
      GfxMoveTo(x1, y);
      GfxLineTo(x2, y); 
      if ( draw_label) {
        GfxTextOut(StrFormat("%g", y), x1, y ); 
        GfxTextOut(StrFormat("%g bars", x2-x1), (x1+x2)*0.5, y );  
      }
    }
  }  
}
/// ....

printf( "\n%s", MxToString(stop_mat));
printf( "\n#lines: %g, BarCount: %g", MxGetSize(stop_mat,1), BarCount );



_SECTION_END();

//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Solved the chart alignment problem. But how can I use the zigzag's peaks and troughs as the input for your code ?

Found where to use zigzag's trough value. It was pretty simple but don't know why I wasn't able to see that in your code.

Simply Wonderful !

Thanks a lot. :smiley: :smile: :smiley:

I used to refer such lines by the name 'TimeSpace-Lines', whenever sharing my trading techniques with my friends. But now I named them 'Fxshrat-Lines', just to pay respect to you. Anyway following was the reason behind calling them 'TimeSpace-Lines':

TimeSpace is the space or number of bars (periods) a peak/trough’s penetration takes.

The word ‘Time’ in the term ‘TimeSpace’ is derived from the number of periods a peak/trough’s penetration takes. As ‘period’ is a unit of time, I used the word ‘Time’ in the term ‘TimeSpace’.

The word ‘Space’ in the term ‘TimeSpace’ is derived from the fact that when we eyeball those number of periods that the penetration of a peak/trough’s took, we usually don’t dwell into counting of the number of periods, instead we perceive that time-duration simply as a space on the chart. At first look, we usually perceiving them as space not as time, therefore I used the word ‘Space’ in term ‘TimeSpace. This is how I coined the term ‘Time’s Space’ or simply TimeSpace.

But now they are simply 'Fxshrat-Lines'. :smile: