Text Anchored to the Right of the last Bar

I use the GfxTextOut(""+FullName(), 3205 , 0 ); function to place text on my chart - in this particular example to place the Full Name of the stock in the top right hand corner of the chart. The text is placed at a fixed location on each chart using this function but I was wondering if it is possible to have the text close to the right of the last bar on a particular chart? When cycling through multiple charts in a watchlist this limits the amount of eye movement that is necessary to take in price and ticker information and speeds up manual scanning of the watchlist for particular trading setups.

https://forum.amibroker.com/t/add-color-to-closing-price-in-title/31856/2?u=yogyatrader

Thanks - I will take a look and adjust parameters to see if it can fit my needs.

@trevorchorn2, I'm not sure what your goal is, but you could try the following example.

The "Parameters" dialog allows you to test 2 different ways to place the 'fullname'.

The "dynamic" one is a bit convoluted as I want to place the text a little above the H of the last visible bar.
To do this (using GfxCoordmode(1) I have to estimate how much to shift up the text (actually increasing the price used for the y coordinate).
For this, I use the Status() function to retrieve the chart height (in pixles) and 'axisminy' and 'axismaxy' (which are expressed in price) to perform some calculations.
Unfortunately, when I only change the ticker displayed these two latter values ​​are not immediately updated as it happens regularly every time you scroll through the chart or click anywhere on it.1).

To avoid this extra action, I was forced to include some logic to "force" a graph refresh.

So, if you want to use the dynamic mode, before applying the formula, take care to set the required registry key to use a "hiRes" refresh timer (as per the link included at the top of the code).

// Enable hiRes timer refresh in the registry if using dynamic y positioning (yPosition param == -1)
// See: https://forum.amibroker.com/t/sub-second-timed-refresh-via-registry/14455/11

yPosition = Param( "Fixed y coordinate (-1 == above plot last bar H)", -1, -1, 2160, 1 );
fontSize = Param( "Font Size", 20, 10, 100, 1 );
fontColor = ParamColor("Font color", colorOrange);
debug = ParamToggle("Enable _TRACE() to debug", "No|Yes", 0); 

Version(6.20); // required to use GfxSetCoordsMode( 3 );

if (debug) {
	redrawAction = Status( "redrawaction" );
	if (redrawAction == 1 ) {
		_TRACEF( "------------ TIMED REFRESH - %s", Now());
	} else {
		_TRACEF( "============ REDRAW ACTION: %2.0f - %s", redrawAction, Now());
	}
}

GraphXSpace = 20; // allow some room to draw the fullname text


fn = StrTrim( FullName(), " " );
bi = BarIndex();
GfxSetBkMode( 1 );
// GfxSetOverlayMode( 1 ); // Uncomment to draw text under the chart
GfxSelectFont( "Tahoma", fontSize, 700 );
GfxSetTextColor( fontColor );
GfxSetTextAlign( 2 ); // TA_RIGHT

if( yPosition == -1 ) {
	// draw fullname() above the price of the last visible bar
    refName = StaticVarGetText( "ActiveName__" + GetChartID());

	// get chart data to calculate an offset based on the price
    minPrice = Status( "axisminy" );
    maxPrice = Status( "axismaxy" );
    paneH = Status( "pxchartheight" );
    priceRange = maxPrice - minPrice;

    if( priceRange == 0 ) {
        if (debug) _TRACE( "Forcing refresh on price range == 0" );
        RequestTimedRefresh( 0.1 );
    } else  {
        if( Name() != refName ) {
			if (debug) _TRACE( "Forcing refresh on changed ticker" );
            StaticVarSetText( "ActiveName__" + GetChartID(), Name() );
            RequestTimedRefresh( 0.1 ); // Forcing a refresh to get correct price range (yMin and yMax)
        } else {
			pricePerPixel = priceRange / paneH;
			if (debug) _TRACEF( "%s - yMin: %g - yMax: %g - Pane/Chart H: %g - PriceRange: %g - PixelUnit: %g", Name(), minPrice, maxPrice, paneH, priceRange, pricePerPixel );

			yOffset = ( fontSize * 1.5 ) * pricePerPixel;
			GfxSetCoordsMode( 1 ); //  X is expressed in bar index and Y is expressed in price
			x = LastVisibleValue( bi );
			y = LastVisibleValue( H );
			yy = y + yOffset; // Moving the text a bit over the last bar H
			GfxTextOut( fn, x, yy );
			if (debug) _TRACEF( "y: %g - yOffset: %g - y+yOffset: %g", y, yOffset, yy);
			
			// cancel unneeded forced refresh
			RequestTimedRefresh( 0 );
		}
    }
} else {
	// draw fullname() at y as set in param dialog
    paneH = Status( "pxchartheight" );
    x = LastVisibleValue( bi );
    y = yPosition;
	// do not allow drawing outside of bottom of the chart
    if( y > paneH )
        y = paneH - ( fontSize * 1.5 );

    GfxSetCoordsMode( 3 ); // X coordinate is bar index, Y is pixel
    
    GfxTextOut( fn, x, y );
    if (debug) _TRACEF( "x: %g - y: %g", x, y );
}

// add your own title.... or 
if (debug) Title = "ChartID: " + GetChartID();

Plot( Close, "Close", colorDefault, styleCandle );

I also left some "debug" _TRACE() instructions, if you would like to understand better how it works...

1) I contacted AmiBroker's support about this behavior and indeed I got the confirmation that this is by design: "The reason for this is that formula execution happens BEFORE finding scale for entire chart (before rendering), so the values you get from Status() function actually represent PREVIOUS execution.

@trevorchorn2, an alternative way do to the above, avoiding the extra refresh is as follows (visual result is slightly different).

yPosition = Param( "Fixed y coordinate (-1 == above plot last bar H)", -1, -1, 2160, 1 );
fontSize = Param( "Font Size", 20, 10, 100, 1 );
fontColor = ParamColor("Font color", colorOrange);
debug = ParamToggle("Enable _TRACE() to debug", "No|Yes", 0); 

Version(6.20); // required to use GfxSetCoordsMode( 3 );

GraphXSpace = 20; // allow some room to draw the fullname text

fn = StrTrim( FullName(), " " );
bi = BarIndex();
GfxSetBkMode( 1 );
// GfxSetOverlayMode( 1 ); // Uncomment to draw text under the chart
GfxSelectFont( "Tahoma", fontSize, 700 );
GfxSetTextColor( fontColor );
GfxSetTextAlign( 2 ); // TA_RIGHT

if( yPosition == -1 ) {
	// draw fullname() above the price of the last visible bar
	// get chart data to calculate an offset based on the price
    minPrice = LowestVisibleValue(L);
    maxPrice = HighestVisibleValue(H);
    paneH = Status( "pxchartheight" );
    priceRange = maxPrice - minPrice;

	if( priceRange != 0 ) {
		pricePerPixel = priceRange / paneH;
		yOffset = ( fontSize * 1.75 ) * pricePerPixel;
	}	
	else
		yOffset = 0;
	GfxSetCoordsMode( 1 ); //  X is expressed in bar index and Y is expressed in price
	x = LastVisibleValue( bi );
	y = LastVisibleValue( H );
	yy = y + yOffset; // Moving the text a bit over the last bar H
	GfxTextOut( fn, x, yy );
} else {
	// draw fullname() at y as set in param dialog
    paneH = Status( "pxchartheight" );
    x = LastVisibleValue( bi );
    y = yPosition;
	// do not allow drawing outside of bottom of the chart
    if( y > paneH )
        y = paneH - ( fontSize * 1.5 );

    GfxSetCoordsMode( 3 ); // X coordinate is bar index, Y is pixel
    GfxTextOut( fn, x, y );
}

// add your own title.... or 
if (debug) Title = "NO REFRESH VERSION - ChartID: " + GetChartID();

Plot( Close, "Close", colorDefault, styleCandle );

In this case, (as it was kindly suggested to me by AmiBroker's support) instead of using the Status() function to get the Y axis min/max values, I used the LowestVisibleValue(L) and HighestVisibleValue(H) to estimate the Y offset I use to place the title a little bit above the last bar H.

(The 1.5 or 1.75 multipliers used in the formulas are empirical values to take care of the actual line height of the font used - Unfortunately, at present, there is no way to finely control typographic layouts).

Thanks beppe for your post which does provide some measure for what I am trying to do. Subsequent to my initial post question, I looked at what others had posted on this Forum and came across this:

which is drawing a horizontal line on the last bar and then annotating the line with text. I don't need the line for my use, and so I modified some of the lines in the above AFL to print the Full Name to the right of the last bar on the chart. The modified code is shown below:

M1 = LastVisibleValue(HHV(H, 1)); // Example
N1 = LastVisibleValue(LLV(0.99*H, 1)); // Example
LVBI = LastVisibleValue(BarIndex());

//PlotOHLC(M1, M1, N1, N1, "", colorLightYellow, styleCloud|styleNoLabel, Null, Null, 0, -5, 1);
//Plot( M1, "M1", colorLightBlue, styleLine|styleThick|styleNoRescale );
//Plot( N1, "N1", colorLightBlue, styleLine|styleThick|styleNoRescale );

//PlotTextSetFont("   M1   ", "Arial black", 10,  LVBI - 5, M1, colorGreen, colorLightGrey, -5);
PlotTextSetFont(   FullName() , "Ariel", 7,  LVBI + 1.5, N1, colorBlack, colorWhite, -5);

And a screenshot of the result is shown below:

image

It is a workaround but it is effective in achieving what I want. So as I scroll through a watchlist I can look at the price action around the last bar and also in the same "frame" I can see the Full Name of the stock with minimal eye movement necessary. I need to do this for several hundred tickers a night and so the less my eyes need to move around the screen the quicker I can complete the task - that was the motivation for my original post.

I hope this is useful for others.

1 Like

This topic was automatically closed 100 days after the last reply. New replies are no longer allowed.