Plotting a chart using gfx

Below an example of how I would plot a single price array in gfx. The problem is that it executes ~1000 slower than the build in Plot(). This is expected of course - hence this post.

  1. Putting all other considerations aside, my question is whether there is a way to size the build-in plot() function, remove the Y-Axis, and overlay this on a gfx layout. A simple yes or no will suffice. I don’t know everything and, imo, asking a dumb question is better than asking none.

  2. Am I doing something terribly wrong in the code below that results in the 1000x slower execution. If so please give me a hint.

I think it would be great if we could somehow use the Plot() function to plot on a full-window gfx layout.

Best regards,
Herman

function gfxPlotVLine( XPixels, Color )
{
    global pxheight;
    GfxSelectPen( Color ) ;
    GfxMoveTo( XPixels, 0 );
    GfxLineTo( XPixels, pxheight );
}

function GetVisualBarIndex( )
{
    lvb = Status( "lastvisiblebar" );
    fvb = Status( "firstvisiblebar" );
    bi = BarIndex();
    StaticVarSet( "NumberbarsVisible", Lvb - fvb + 1 );
    return bi - bi[ 0 ] - fvb;
}

function GetYPixels( Y )
{
    global PixelsPerPrice, pxTopArea, MaxY;
    return ( MaxY - Y ) * PixelsPerPrice;
}

function GetXPixels( X )
{
    global PixelsPerBar, pxLeftArea;
    return X * PixelsPerBar;
}

BeginExecutionTime = GetPerformanceCounter( False );
FilePathName = GetFormulaPath();
SetChartOptions( 0, chartHideQuoteMarker );
RequestTimedRefresh( 1 );

pxwidth = Status( "pxwidth" );
pxheight = Status( "pxheight" );

SetChartOptions( 3, chartShowDates );
PxL = Status( "pxchartright" ); // Cover origional chart frame vertical line
gfxPlotVLine( PxL, colorblack );

Plot( C, "", colorgreen, styleNoLabel | stylenoline ); // needed to give the min and max values
Miny = Status( "axisminy" );
Maxy = Status( "axismaxy" );
YRange = MaxY - MinY;
VisBarIndex =  GetVisualBarIndex( );
NumBarsVisible = StaticVarGet( "NumberbarsVisible" );

// calculate conversion factors
PixelsPerBar     = pxwidth / NumBarsVisible;
PixelsPerPrice = pxHeight / ( YRange + 10 ^ -20 );

// Plot pixel plot
GfxSelectPen( colorYellow );
FVB = Status( "firstvisiblebar" );
LVB = Status( "lastvisiblebar" );

for( b = FVB + 1; b <= LVB AND b < BarCount; b++ )
{
    PrevPixelY 	= GetYPixels( C[b - 1] );
    PixelY 		= GetYPixels( C[b] );

    PrevPixelX 	= GetXPixels( VisBarIndex[b - 1] );
    PixelX 		= GetXPixels( VisBarIndex[b] );

    GfxMoveTo( PrevPixelX, PrevPixelY );
    GfxLineTo( PixelX, PixelY );
}

EndExecutionTime = GetPerformanceCounter( False );
ExecutionTime = EndExecutionTime - BeginExecutionTime;
StaticvarSet( "ExecutionTime", ExecutionTime );
Title =
    "FormulaName: " + FilePathName + "\n" +
    " Exec. Time: " + StaticVarGet( "ExecutionTime" ) + " msec";

Hello,
I managed to speed up the formula about 10x by moving some of the calculations outside the loop (code below). But it is still 100x slower than Plot(). Anyone see anything else I can improve?

...I wish there was a type of gfxPlotArray( ... ) instead of having to call GfxMoveTo() 2000000 times... (using 1M Tick db)

Best regards,
Herman

function gfxPlotVLine( XPixels, Color )
{
    global pxheight;
    GfxSelectPen( Color ) ;
    GfxMoveTo( XPixels, 0 );
    GfxLineTo( XPixels, pxheight );
}

function GetVisualBarIndex( )
{
    lvb = Status( "lastvisiblebar" );
    fvb = Status( "firstvisiblebar" );
    bi = BarIndex();
    StaticVarSet( "NumberbarsVisible", Lvb - fvb + 1 );
    return bi - bi[ 0 ] - fvb;
}

BeginExecutionTime = GetPerformanceCounter( False );
FilePathName = GetFormulaPath();
SetChartOptions( 0, chartHideQuoteMarker );
RequestTimedRefresh( 1 );

pxwidth = Status( "pxwidth" );
pxheight = Status( "pxheight" );

SetChartOptions( 3, chartShowDates );
PxL = Status( "pxchartright" ); // Cover origional chart frame vertical line
gfxPlotVLine( PxL, colorblack );

Plot( C, "", colorgreen, styleNoLabel | stylenoline ); // needed to give the min and max values
Miny = Status( "axisminy" );
Maxy = Status( "axismaxy" );
YRange = MaxY - MinY;
VisBarIndex =  GetVisualBarIndex( );
NumBarsVisible = StaticVarGet( "NumberbarsVisible" );

// calculate conversion factors
PixelsPerBar     = pxwidth / NumBarsVisible;
PixelsPerPrice = pxHeight / ( YRange + 10 ^ -20 );

// Plot pixel plot
GfxSelectPen( colorYellow );
FVB = Status( "firstvisiblebar" );
LVB = Status( "lastvisiblebar" );

PrevPixelY 	= ( MaxY - Ref(C,-1) ) * PixelsPerPrice;
PixelY 		= ( MaxY - C ) * PixelsPerPrice;
PrevPixelX 	= Ref( VisBarIndex, - 1) * PixelsPerBar;
PixelX 		= VisBarIndex * PixelsPerBar;

for( b = FVB + 1; b <= LVB AND b < BarCount; b++ )
{
    GfxMoveTo( PrevPixelX[b], PrevPixelY[b] );
    GfxLineTo( PixelX[b], PixelY[b] );
}

EndExecutionTime = GetPerformanceCounter( False );
ExecutionTime = EndExecutionTime - BeginExecutionTime;
StaticvarSet( "ExecutionTime", ExecutionTime );
Title =
    "FormulaName: " + FilePathName + "\n" +
    " Exec. Time: " + StaticVarGet( "ExecutionTime" ) + " msec";

hello
Until some one answer to you. can you check below link.

You are using the oldest version function. Have you had a look at the new functions that exists.
i just found a different sample for your loop in here

https://www.amibroker.com/guide/afl/gfxsetcoordsmode.html

And If you like:
Go to Main menu / tools / preferences / misceleneous / “and temporary” click the Display chart timing. instead using GetPerformanceCounter()

Thank you for you very much Panos, good link!

I haven't coded gfx for some years and will check out the newer functions.

Using GfxSetCoordsMode() would clean up the code but using it I can't seem
to make it plot over the y-axis area. It is about 20% faster - but it is
still 100x slower than the plot().

After minimizing the code it looks like I am out of luck.

Thanks, and happy coding,
Herman

// Code derived from:
https://www.amibroker.com/guide/afl/gfxsetcoordsmode.html
BeginExecutionTime = GetPerformanceCounter( False );

Plot( C, "Price", colorDefault, styleLine | stylenoline);
GfxSetOverlayMode( 1 );
GfxSetCoordsMode( 1 ); // bar/price mode (instead of pixel)
GfxSelectPen( colorRed );
GfxSelectPen( Colorwhite );

bi = BarIndex();
start = FirstVisibleValue( bi );
end = LastVisibleValue( bi );

for ( i = start+1; i <= end; i++ )
{
     GfxMoveTo( i, C[ i-1 ] );
     GfxLineTo( i, C[ i ] );
}

FilePathName = GetFormulaPath();
EndExecutionTime = GetPerformanceCounter( False );
ExecutionTime = EndExecutionTime - BeginExecutionTime;
StaticvarSet( "ExecutionTime", ExecutionTime );
Title =
"https://www.amibroker.com/guide/afl/gfxsetcoordsmode.html\n"+
    "FormulaName: " + FilePathName + "\n" +
    " Exec. Time: " + StaticVarGet( "ExecutionTime" ) + " msec";

You won’t get anywhere near to native Plot() speed no matter what you try because AmiBroker’s Plot() (and all built-in functions) use pure magic coding :slight_smile:

I can give you one hint though: you can remove GfxMoveTo completely. It is NOT needed except for bar 0

GfxMoveTo( i, C[ start] );
for ( i = start+1; i <= end; i++ ) GfxLineTo( i, C[ i ] );
2 Likes

Thank you everyone,

I agree that TJ's low-level hand-coding cannot be improved on but, with a
little trickery, we can reduce the plotting time from 320 to 16 mSec and
that is only 4x as slow as the native plot(). See plots below. Green =
pxwidth loop, Yellow = loop all bars, Red = native plot().

Since there is no practical reason to display more points than our pixwidth
I limit the loop to pxwidth and adjust the gfx-line segment to give a
continuous line segments. You could compare this to Quick-afl and call it
"Quick-gfx":slight_smile:

The result is not perfect yet as data points separate when zooming in and
the number of bars falls below pxwidth, but that should be fixable.

I show the new loop below, if you can improve on it please jump in!

Happy coding,
Herman

bi = BarIndex();
FVB = FirstVisibleValue( bi );
LVB = LastVisibleValue( bi );
ScreenResolution = (pxwidth+10^-20); // Small fix to prevent divide by zero
PixIncr = LastValue( BI)/Screenresolution;
for( b = FVB + 1; b <= LVB AND b < BarCount-PixIncr; b=b+PixIncr )
{
    GfxMoveTo( PrevPixelX[b], PrevPixelY[b] );
    GfxLineTo( PixelX[b], PixelY[b+PixIncr] );
}
1 Like

Just note that you may skip some high/lows that way if they happen in-between plotted bars.

Thanks you Tomasz,

still working on implementing you hint…

Herman

Thanks TJ,

Your single-line loop hint solved my zoomed-in disconnects nicely ! Its
looking good now.

Yes, high and lows, however I think I can handle that outside the loop. I
think I am good now.

Thanks everyone, some discussion always loosens up the grey matter. Here is
the latest loop, its come a long way:

GfxMoveTo( PrevPixelX[FVB], PrevPixelY[FVB] );
for( b = FVB + 1; b <= LVB AND b < BarCount-PixIncr; b=b+PixIncr )
{
    GfxLineTo( PixelX[b], PixelY[b+PixIncr] );
}

Happy coding,
Herman

2 Likes

thank you Herman
just remember to put the line FilePathName UNDER the line EndExecutionTime

EndExecutionTime = GetPerformanceCounter( False );
ExecutionTime = EndExecutionTime - BeginExecutionTime;
FilePathName = GetFormulaPath();

Hi @Beaver @SpinTrader,

Thanks for bringing up this topic, it happens to be late on my side to find it.

I read the article Creating GFX Chart-Overlays (v3) previously and played with it to write a simple code:

pxchartwidth = Status( "pxchartwidth" );
pxchartheight = Status( "pxchartheight" );
lvb = Status( "lastvisiblebar" );
fvb = Status( "firstvisiblebar" );
pxchartleft = Status( "pxchartleft" );
pxchartbottom = Status( "pxchartbottom" );
Miny = Status( "axisminy" );
Maxy = Status( "axismaxy" );

//***************************************************************************************************************
//From fxshrat https://forum.amibroker.com/t/circle-through-three-points/1165/6
//***************************************************************************************************************
function GfxConvertBarToPixelX( bar )
{
	 local bardiff, barnum, relbar, px;
     // based on http://www.amibroker.com/kb/2009/03/30/how-to-convert-from-bar-value-to-pixel-co-ordinates/
     barnum = lvb - fvb + 1;
     bardiff = bar - fvb;
	 relbar = bardiff / barnum;
	 px = relbar * pxchartwidth + pxchartleft;
     return Nz( px );
}
function GfxConvertValueToPixelY( Value, logscale )
{ 
	 local logMiny, logMinMax, pxy;
     //based on http://www.amibroker.com/kb/2009/03/30/how-to-convert-from-bar-value-to-pixel-co-ordinates/
     if( logscale )
     {
         logMiny = log( Miny );
         logMinMax = log( Maxy ) - logMiny;
         pxy = pxchartbottom - floor( 0.5 + ( log( Value ) - logMiny ) * pxchartheight / logMinMax );
     }
     else
     {
         pxy = pxchartbottom - floor( 0.5 + ( Value - Miny ) * pxchartheight / ( Maxy - Miny + 1e-9 ) );
     }
     return Nz( pxy );
}
//***************************************************************************************************************

_log = ParamToggle("Log Scale", "Off|On", 1 );
SetChartOptions( IIf( _log, 2, 3 ), chartLogarithmic );
Plot( C, "", ParamColor( "C Dot Co-Ordinates color", colorGrey50 ), styleDots | styleNoLine, Null, Null, 0, 5, 2 );

// For verification: Overlay pixel on price plot
GfxSetZOrder( 4 );
GfxSelectPen( colorWhite, 3 );
GfxSetCoordsMode( 0 );
for( i = fvb + 1; i <= BarCount - 1; i++ )
{
	 PrevPixelY = GfxConvertValueToPixelY( C[ i - 1 ], _log );
	 PixelY = GfxConvertValueToPixelY( C[ i ], _log );
	 
	 PrevPixelX = GfxConvertBarToPixelX( i - 1 );
	 PixelX = GfxConvertBarToPixelX( i );
	 
	 GfxMoveTo( PrevPixelX, PrevPixelY );
	 GfxLineTo( PixelX, PixelY );
}

RequestTimedRefresh( 1 );

If the for-loop is written to move and draw lines when the GfxSetCoordsMode is set to 0 then why when we comment out // Plot(), the lines drawn pixel-to-pixel disappear? Whereas Plot() is independent.

Alternately, if we do not want the Plot() to be displayed then the only way is to use styleNoDraw
Plot( C, "", ParamColor( "C Dot Co-Ordinates color", colorGrey50 ), styleDots | styleNoDraw, Null, Null, 0, 5, 2 );

The question is very basic might be stupid to some extent, but, would ask for better understanding of GFX in AFL. Why the GFX attributes are dependent on the Plot() function, here?

Your code requires at least one Plot() statement as chart axisminy and axismaxy are calculated based on what you pass as array argument in Plot() call and you are using those values in your code.

Thank you @Tomasz,

Understood! so the Plot() function calculates the chart's axisminy and axismaxy on the basis of the array we are plotting. Now since by default the GfxSetOverlayMode() is set to 0, the GFX controls are being overlaid on the Plot() or vice versa as the chartist chooses.

Doubt! then how to factor the auto-scale or scrolling of Plot() in GfxSetOverlayMode( 2 )?

For instance in the below code:

pxchartwidth = Status( "pxchartwidth" );
pxchartheight = Status( "pxchartheight" );
lvb = Status( "lastvisiblebar" );
fvb = Status( "firstvisiblebar" );
pxchartleft = Status( "pxchartleft" );
pxchartbottom = Status( "pxchartbottom" );
Miny = Status( "axisminy" );
Maxy = Status( "axismaxy" );

function GfxConvertBarToPixelX( bar )
{
	 local bardiff, barnum, relbar, px;
     // based on http://www.amibroker.com/kb/2009/03/30/how-to-convert-from-bar-value-to-pixel-co-ordinates/
     barnum = lvb - fvb + 1;
     bardiff = bar - fvb;
	 relbar = bardiff / barnum;
	 px = relbar * pxchartwidth + pxchartleft;
     return Nz( px );
}
function GfxConvertValueToPixelY( Value, logscale )
{ 
	 local logMiny, logMinMax, pxy;
     //based on http://www.amibroker.com/kb/2009/03/30/how-to-convert-from-bar-value-to-pixel-co-ordinates/
     if( logscale )
     {
         logMiny = log( Miny );
         logMinMax = log( Maxy ) - logMiny;
         pxy = pxchartbottom - floor( 0.5 + ( log( Value ) - logMiny ) * pxchartheight / logMinMax );
     }
     else
     {
         pxy = pxchartbottom - floor( 0.5 + ( Value - Miny ) * pxchartheight / ( Maxy - Miny + 1e-9 ) );
     }
     return Nz( pxy );
}

_log = ParamToggle( "Log Scale", "Off|On", 1 );
SetChartOptions( IIf( _log, 2, 3 ), chartLogarithmic );

//dispCoodC = ParamToggle( "Show Coord. Candles", "No|Yes", 0 );
//Plot( C, "", ParamColor( "C Dot Co-Ordinates color", colorDefault ),  styleNoLine | styleNoLabel | IIf( dispCoodC, styleCandle, styleNoDraw ), Null, Null, 0, 4, 2 );
//NumBars = BarCount - fvb;
//pxPerBar = GfxConvertBarToPixelX( NumBars ) / NumBars;

GfxSetOverlayMode( 2 );

for( i = fvb; i <= BarCount - 1; i++ )
{
	 OpPixelY = GfxConvertValueToPixelY( O[ i ], _log );
	 HiPixelY = GfxConvertValueToPixelY( H[ i ], _log );
	 LoPixelY = GfxConvertValueToPixelY( L[ i ], _log );
	 ClPixelY = GfxConvertValueToPixelY( C[ i ], _log );
	 
	 PixelX = GfxConvertBarToPixelX( i );
	 
	 if( C[ i ] >= O[ i ] )
	 {
		 BarColor = ColorRGB( 1, 254, 1 );
	 }
	 else if( C[ i ] < O[ i ] )
	 {
		 BarColor = ColorRGB( 255, 0, 0 );
	 }
	
	 GfxSelectPen( BarColor, 2 );
	 GfxMoveTo( PixelX, HiPixelY );
	 GfxLineTo( PixelX, LoPixelY );
}

GfxSelectPen( colorWhite, 2 );
GfxMoveTo( 0, GfxConvertValueToPixelY( Maxy, _log ) );
GfxLineTo( pxchartwidth, GfxConvertValueToPixelY( Maxy, _log ) );

GfxMoveTo( 0, GfxConvertValueToPixelY( Miny, _log ) );
GfxLineTo( pxchartwidth, GfxConvertValueToPixelY( Miny, _log ) );

GfxMoveTo( pxchartwidth, GfxConvertValueToPixelY( Maxy, _log ) );
GfxLineTo( pxchartwidth, GfxConvertValueToPixelY( Miny, _log ) );

RequestTimedRefresh( 1 );
1 Like

Apologies I mentioned the wrong for-loop wanted to bring @SpinTrader 's code:

How to factor the auto-scale or scrolling of Plot() in GfxSetOverlayMode( 2 )?

Here is the one:

pxchartwidth = Status( "pxchartwidth" );
pxchartheight = Status( "pxchartheight" );
lvb = Status( "lastvisiblebar" );
fvb = Status( "firstvisiblebar" );
pxchartleft = Status( "pxchartleft" );
pxchartbottom = Status( "pxchartbottom" );
Miny = Status( "axisminy" );
Maxy = Status( "axismaxy" );

function GfxConvertBarToPixelX( bar )
{
	 local bardiff, barnum, relbar, px;
     // based on http://www.amibroker.com/kb/2009/03/30/how-to-convert-from-bar-value-to-pixel-co-ordinates/
     barnum = lvb - fvb + 1;
     bardiff = bar - fvb;
	 relbar = bardiff / barnum;
	 px = relbar * pxchartwidth + pxchartleft;
     return Nz( px );
}
function GfxConvertValueToPixelY( Value, logscale )
{ 
	 local logMiny, logMinMax, pxy;
     //based on http://www.amibroker.com/kb/2009/03/30/how-to-convert-from-bar-value-to-pixel-co-ordinates/
     if( logscale )
     {
         logMiny = log( Miny );
         logMinMax = log( Maxy ) - logMiny;
         pxy = pxchartbottom - floor( 0.5 + ( log( Value ) - logMiny ) * pxchartheight / logMinMax );
     }
     else
     {
         pxy = pxchartbottom - floor( 0.5 + ( Value - Miny ) * pxchartheight / ( Maxy - Miny + 1e-9 ) );
     }
     return Nz( pxy );
}

_log = ParamToggle( "Log Scale", "Off|On", 1 );
SetChartOptions( IIf( _log, 2, 3 ), chartLogarithmic );

//dispCoodC = ParamToggle( "Show Coord. Candles", "No|Yes", 0 );
//Plot( C, "", ParamColor( "C Dot Co-Ordinates color", colorDefault ),  styleNoLine | styleNoLabel | IIf( dispCoodC, styleCandle, styleNoDraw ), Null, Null, 0, 4, 2 );
//NumBars = BarCount - fvb;
//pxPerBar = GfxConvertBarToPixelX( NumBars ) / NumBars;

GfxSetOverlayMode( 2 );

/*
for( i = fvb; i <= BarCount - 1; i++ )
{
	 OpPixelY = GfxConvertValueToPixelY( O[ i ], _log );
	 HiPixelY = GfxConvertValueToPixelY( H[ i ], _log );
	 LoPixelY = GfxConvertValueToPixelY( L[ i ], _log );
	 ClPixelY = GfxConvertValueToPixelY( C[ i ], _log );
	 
	 PixelX = GfxConvertBarToPixelX( i );
	 
	 if( C[ i ] >= O[ i ] )
	 {
		 BarColor = ColorRGB( 1, 254, 1 );
	 }
	 else if( C[ i ] < O[ i ] )
	 {
		 BarColor = ColorRGB( 255, 0, 0 );
	 }
	
	 GfxSelectPen( BarColor, 2 );
	 GfxMoveTo( PixelX, HiPixelY );
	 GfxLineTo( PixelX, LoPixelY );
}
*/

bi = BarIndex();
FVB = FirstVisibleValue( bi );
LVB = LastVisibleValue( bi );
ScreenResolution = (pxchartwidth+10^-20); // Small fix to prevent divide by zero
PixIncr = LastValue( BI)/Screenresolution;
for( i = FVB + 1; i <= LVB AND i < BarCount-PixIncr; i=i+PixIncr )
{
    OpPixelY = GfxConvertValueToPixelY( O[ i ], _log );
	 HiPixelY = GfxConvertValueToPixelY( H[ i ], _log );
	 LoPixelY = GfxConvertValueToPixelY( L[ i ], _log );
	 ClPixelY = GfxConvertValueToPixelY( C[ i ], _log );
	 
	 PixelX = GfxConvertBarToPixelX( i );
	 
	 if( C[ i ] >= O[ i ] )
	 {
		 BarColor = ColorRGB( 1, 254, 1 );
	 }
	 else if( C[ i ] < O[ i ] )
	 {
		 BarColor = ColorRGB( 255, 0, 0 );
	 }
	
	 GfxSelectPen( BarColor, 2 );
	 GfxMoveTo( PixelX, HiPixelY );
	 GfxLineTo( PixelX, LoPixelY );
}

GfxSelectPen( colorWhite, 2 );
GfxMoveTo( 0, GfxConvertValueToPixelY( Maxy, _log ) );
GfxLineTo( pxchartwidth, GfxConvertValueToPixelY( Maxy, _log ) );

GfxMoveTo( 0, GfxConvertValueToPixelY( Miny, _log ) );
GfxLineTo( pxchartwidth, GfxConvertValueToPixelY( Miny, _log ) );

GfxMoveTo( pxchartwidth, GfxConvertValueToPixelY( Maxy, _log ) );
GfxLineTo( pxchartwidth, GfxConvertValueToPixelY( Miny, _log ) );

RequestTimedRefresh( 1 );
1 Like

Can someone please tell me the most efficient way to plot trendlines with gfx? I want to be able to see my lines on a 1 minute TF and it's not happening. 30 minute TF is just possible, but it's very slow. My lines have about 3 conditions attached to them. My PC should be fast enough to handle this (corei5 + dedicated gfx processor), so I'm not sure what's happening.
Thanks.

Search the forum please, you'll find many examples. You can extract necessary section as per your need from the below mention and use GfxSetCoordsMode( 1 ) without using the pixel conversion functions.

Sorry don't understand what you mean!

Thanks. My problem is that AB needs to check conditions applied to the lines, and it does this using plot(). It necessitates 10's of thousands of calculations (apparently), so it grinds to a halt when using a small TF. My issue is one of speed, so that's why I asked about efficiency.

@C_M, quoting a sentence from a recent (unrelated) post by @Tomasz:

Either you want help and provide the exact code that REPRODUCES the problem or you don't provide the code and you don't receive any help.

A quote that I think fits perfectly here too.

3 Likes

one of the things you can check @C_M (if you have not already) is to make sure that the lines are being calculated/drawn only on the visible bars of the chart window, not all the bars that are in the array

3 Likes

Thanks v, got it.

@Tomasz, you're the master of efficient coding. Is there a general theme, process or technique which one should employ when attempting to draw multiple conditional trendlines on a 1 minute chart? I want to be able to scroll the date, backtest, explore etc without it hanging. If I know what's required to make it run, then I can ask a coder to do it for me. Thanks for the guidance.