Eadem mutata resurgo - Logarithmic spiral in AmiBroker (Fibonacci, Elliott, Fischer...)

Sir Jacob Bernoulli chose a figure of a logarithmic spiral and the motto Eadem mutata resurgo (latin for "Changed and yet the same") for his gravestone.

Last chapter of Mr. Robert Fischer’s book Fibonacci Applications and Strategies for Traders states logarithmic spiral as the closest solution to any chance of linking human behavior (shown as price fluctuations in market instruments) with ever -existential Natural Laws seen in about everything.

Hi Everyone,

After reading Mr. Fischer’s book, I have been working on coding logarithmic spiral in AmiBroker. I am not a programmer and hold no profound knowledge in mathematics either. Thanks to the power of AmiBroker's simple approach to complex aspects, with the help of search engines, numerous self-explaining AFL samples scattered all around the internet; have been able to devise a first-hand code for the same but with several drawbacks.

The further I dive deep into the idiosyncrasies of trading, Fibonacci Numbers and Ratios appear more and more sacred to me. I find no other tool other than this spiral to consider time into account in tandem with price. However, as mentioned by Mr. Fischer the accuracy lies in finding the proper starting point of the spiral w.r.t. to a appropriate center. I believe to this problem Mr. R. N. Elliot's theory would come into rescue.

Having that said, on the contrary I lack experience in developing trading set-up too, hence writing this post to seek help from the creative minds of this helping community.

In his book, Mr. Fischer had mentioned three points (pg. 134):
i) Point B as the Center of the Spiral;
ii) Point A as the starting point, and
iii) Point C as any point where Price meets with the arc of the Spiral.

Objective:
com-optimize

Code:

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

//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//To Pixel Co-ordinates From x-axis Bars to y-axis Values
//Thank you @fxshrat, learned this beautiful way from your post (https://forum.amibroker.com/t/circle-through-three-points/1165/6)
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
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 );
}
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//Customized Text Out using GFX
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
procedure GfxCustomTextOut( txt, fnt, fntsize, fntweight, fntColor, txtBackColor, X, Y )
{
	 GfxSetBkMode( 2 );
	 GfxSetTextAlign( 0 | 0 );
	 GfxSetBkColor( txtBackColor );
	 GfxSetTextColor( fntColor );
	 GfxSelectFont( fnt, fntsize, fntweight, 0, 0, 0 );
	 GfxTextOut( txt, X, Y );
}
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//Customized Circle-shaped like Radio buttons using GFX
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
procedure DrawButtonSpi( x1, y1, x2, y2, RndRectBkClr, CirBkClr )
{
     GfxSetZOrder( 4 );
     GfxSelectPen( RndRectBkClr );
     GfxSelectSolidBrush( RndRectBkClr );
     GfxSetBkColor( RndRectBkClr );
     x3 = 15;
     y3 = 15;
     GfxRoundRect( x1, y1, x2, y2, x3, y3 );
     GfxSelectPen( CirBkClr );
     GfxSelectSolidBrush( CirBkClr );
     GfxCircle( ( x1 + x2 ) / 2, ( y1 + y2 ) / 2, 2 );
}
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//Logarithmic Spiral using GFX
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
procedure drawLogarithmicSpiral( direction, stepCount, DELTA, exponent, ROTATIONS, x, y, width, height )
{
	 GfxSetZOrder( 4 );
	 pi = 3.14159265359;

	 theta = 0.0;
	 _delta = 0.0;

	 P1X = x;
	 P1Y = y;

	 //GfxSetOverlayMode( 0 );
	 GfxSelectPen( colorDarkBlue, 2 );

	for( step = 0; step < stepCount; step++ )
	{
		 t = step / (stepCount - 1 );

		 P2X = P1X + cos( theta ) * _delta;
		 P2Y = P1Y + sin( theta ) * _delta;

		 GfxMoveTo( P1X, P1Y );
		 GfxLineTo( P2X, P2Y );

		 if( direction == 1 )
		 {
			 theta -= ( ROTATIONS * ( pi * 2 ) ) / stepCount;
		 }
		 else
		 {
			 theta += ( ROTATIONS * ( pi * 2 ) ) / stepCount;
		 }

		 //_delta = ( step * ( DELTA / ( stepCount / ROTATIONS ) ) ) ^ log(100000);
		 _delta = ( step * ( DELTA / ( stepCount / ROTATIONS ) ) ) ^ exponent;

		 P1X = P2X;
		 P1Y = P2Y;
	}
}
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx







/*
OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
																			Section Begin for Logarithmic Spiral
OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
*/
_SECTION_BEGIN( "Logarithmic spiral" );
	 _log = 1; //For GFX to read the pixel co-ordinates precisely in log_scale too
	 
	 MouseState = GetCursorMouseButtons();
	 MouseHover = MouseState == 0;
	 LButtonTrigger = MouseState == 9;
	 MousePx  = Nz( GetCursorXPosition( 1 ) );
	 MousePy  = Nz( GetCursorYPosition( 1 ) );
	 
	 xMouseBar = Nz( GetCursorXPosition( 0 ) );
	 yMouseValue = Nz( GetCursorYPosition( 0 ) );
	 
	 ResetLogSpi = ParamTrigger( "Reset Coordinates", "RESET" );
	 RectSize = 8; 

	 xCntr = fvb;
	 yCntr = ( Maxy - Miny ) / 2.5;
	 
	 xSpiCntr = Nz( Lookup( bi, StaticVarGet( "#xSpiCntr" + Name() ), 0 ), xCntr );
	 ySpiCntr = Nz( StaticVarGet( "#ySpiCntr" + Name() ), Miny + yCntr );
	 RoundRectBackClrCntr = Nz( StaticVarGet( "#RoundRectBackClrCntr" + Name() ), ColorRGB( 191, 206, 230 ) );
	 CirBackClrCntr = Nz( StaticVarGet( "#CirBackClrCntr" + Name() ), colorWhite );
	 
	 xSpiStrt = Nz( Lookup( bi, StaticVarGet( "#xSpiStrt" + Name() ), 0 ), xCntr );
	 ySpiStrt = Nz( StaticVarGet( "#ySpiStrt" + Name() ), Maxy - yCntr );
	 RoundRectBackClrStrt = Nz( StaticVarGet( "#RoundRectBackClrStrt" + Name() ), ColorRGB( 191, 206, 230 ) );
	 CirBackClrStrt = Nz( StaticVarGet( "#CirBackClrStrt" + Name() ), colorYellow );
	 

	 if( ResetLogSpi )
	 {
		 StaticVarSet( "#xSpiCntr" + Name(), Null, False );
		 StaticVarSet( "#ySpiCntr" + Name(), Null, False );
		 
		 StaticVarSet( "#xSpiStrt" + Name(), Null, False );
		 StaticVarSet( "#ySpiStrt" + Name(), Null, False );
		 
		 StaticVarSet( "#MoveInProgressCntr" + Name(), False, False );
		 StaticVarSet( "#MoveInProgressStrt" + Name(), False, False );
	 
	 	 StaticVarSet( "#RoundRectBackClrCntr" + Name(), Null, False );
		 StaticVarSet( "#RoundRectBackClrStrt" + Name(), Null, False );
		 
		 StaticVarSet( "#CirBackClrCntr" + Name(), Null, False );
		 StaticVarSet( "#CirBackClrStrt" + Name(), Null, False );
	 }
	 
	 /*
	 ------------------------------------------------------------------------------
								 Spiral's Center
	 ------------------------------------------------------------------------------
	 */
	 pxBtnCntr = GfxConvertBarToPixelX( xSpiCntr );
	 pyBtnCntr = GfxConvertValueToPixelY( ySpiCntr, _log );

	 x1Cntr = pxBtnCntr - RectSize;
	 y1Cntr = pyBtnCntr - RectSize;

	 x2Cntr = pxBtnCntr + RectSize;
	 y2Cntr = pyBtnCntr + RectSize;

	 CursorInBtnCntr = MousePx > x1Cntr AND MousePx < x2Cntr AND MousePy > y1Cntr AND MousePy < y2Cntr;

	 MoveInProgressCntr = Nz( StaticVarGet( "#MoveInProgressCntr" + Name() ) );
	 if( NOT MoveInProgressCntr )
	 {
		 if( CursorInBtnCntr & MouseHover )
		 {
			 RoundRectBackClrCntr = colorWhite;
		 }
		 if ( LButtonTrigger AND CursorInBtnCntr )
		 {
			 StaticVarSet( "#RoundRectBackClrCntr" + Name(), ColorRGB( 13, 47, 109 ), False );
			 StaticVarSet( "#CirBackClrCntr" + Name(), colorWhite, False );
			 StaticVarSet( "#MoveInProgressCntr" + Name(), True, False );
		 }
	 }
	 else
	 {
		 if( LButtonTrigger )
		 {
			 StaticVarSet( "#RoundRectBackClrCntr" + Name(), ColorRGB( 191, 206, 230 ), False );
			 StaticVarSet( "#CirBackClrCntr" + Name(), colorWhite, False );
	
			 StaticVarSet( "#xSpiCntr" + Name(), xMouseBar, False );
			 StaticVarSet( "#ySpiCntr" + Name(), yMouseValue, False );

			 StaticVarSet( "#MoveInProgressCntr" + Name(), False, False );
		 }
	 }
	 /*
	 -------------------------------------------------------------------------------
	 */


	 /*
	 ------------------------------------------------------------------------------
						??????? Unfunctional Spiral Start ?????? Ready Yellow Button but unable to give it THE purpose!
	 ------------------------------------------------------------------------------
	 */
	 pxBtnStrt = GfxConvertBarToPixelX( xSpiStrt );
	 pyBtnStrt = GfxConvertValueToPixelY( ySpiStrt, _log );

	 x1Strt = pxBtnStrt - RectSize;
	 y1Strt = pyBtnStrt - RectSize;

	 x2Strt = pxBtnStrt + RectSize;
	 y2Strt = pyBtnStrt + RectSize;

	 CursorInBtnStrt = MousePx > x1Strt AND MousePx < x2Strt AND MousePy > y1Strt AND MousePy < y2Strt;

	 MoveInProgressStrt = Nz( StaticVarGet( "#MoveInProgressStrt" + Name() ) );
	 if( NOT MoveInProgressStrt )
	 {
		 if( CursorInBtnStrt & MouseHover )
		 {
			 RoundRectBackClrStrt = colorYellow;
		 }
		 if ( LButtonTrigger AND CursorInBtnStrt )
		 {
			 StaticVarSet( "#RoundRectBackClrStrt" + Name(), ColorRGB( 13, 47, 109 ), False );
			 StaticVarSet( "#CirBackClrStrt" + Name(), colorYellow, False );
			 StaticVarSet( "#MoveInProgressStrt" + Name(), True, False );
		 }
	 }
	 else
	 {
		 if( LButtonTrigger )
		 {
			 StaticVarSet( "#RoundRectBackClrStrt" + Name(), ColorRGB( 191, 206, 230 ), False );
			 StaticVarSet( "#CirBackClrStrt" + Name(), colorYellow, False );
	
			 StaticVarSet( "#xSpiStrt" + Name(), xMouseBar, False );
			 StaticVarSet( "#ySpiStrt" + Name(), yMouseValue, False );

			 StaticVarSet( "#MoveInProgressStrt" + Name(), False, False );
		 }
	 }
	 /*
	 -------------------------------------------------------------------------------
	 */

	 DrawButtonSpi( x1Cntr, y1Cntr, x2Cntr, y2Cntr, RoundRectBackClrCntr, CirBackClrCntr ); //Center Button
	 DrawButtonSpi( x1Strt, y1Strt, x2Strt, y2Strt, RoundRectBackClrStrt, CirBackClrStrt ); //Start Button. FOR NOW UNFUCTIONAL! ONLY CENTRE BUTTON WORKS.

	 dir = ParamToggle( "Direction", "ClockWise|CounterClockWise", 0 );
	 stepCnt = Param( "stepCount", 1024, 1, 10240, 1 );
	 DEL = Param( "DELTA", 0.618, 0.1, 10, 0.01 );
	 expnt = Param( "exponent", 2.71828, 0.1, 10, 0.1 ); //e
	 RTNS = Param( "ROTATIONS", 11, 1, 100, 1 );

	 X = pxBtnCntr;
	 Y = pyBtnCntr;
	 wid = pxchartwidth;
	 hght = pxchartheight;

	 drawLogarithmicSpiral( dir, stepCnt, DEL, expnt, RTNS, X, Y, wid, hght );

	 RequestTimedRefresh( 1 );
	 SetChartOptions( 0, chartShowArrows | chartShowDates );
_SECTION_END();
/*
OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
																			Section End for Logarithmic Spiral
OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
*/

/*
OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
																				Section Begin for Price Chart
OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
*/
_SECTION_BEGIN( "Price Chart" );
	 Title = "";
	 PrChrtStyle = ParamToggle( "Price Chart Style", "styleLine|styleCandle", 1 );
	 Plot( C, "Close", colorDefault, styleNoTitle | styleThick | IIf( PrChrtStyle == 0, styleLine, styleCandle ), Null, Null, 0, 5, 3 );
_SECTION_END();
/*
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
																				Section End for Price Chart
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
*/

/*
OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
																			Section Begin for displaying Chart-Title
OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
*/
_SECTION_BEGIN( "Chart-Title" );
	 Font = "Courier New";
	 FontSize = 9;
	 FontWeight = 700;
	 FontColor = colorWhite;
	 TextBkColor = ColorRGB( 9, 30, 70 );

	 x1 = 3;		y1 = 3;
	 Text1 =
	 StrFormat
	 (
		 DateTimeToStr( SelectedValue( DateTime() ) ) +
		 " O %g,
		 H %g,
		 L %g,
		 C %g,
		 V %0.0f",
		 O,
		 H,
		 L,
		 C,
		 V
	 );
	 GfxCustomTextOut( Text1, Font, FontSize, FontWeight, FontColor, TextBkColor, x1, y1 );
_SECTION_END();
/*
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
																			Section End for displaying Chart-Title
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
*/

Output of the above code in AmiBroker Chart:
com-resize

Challenges:
i) Unable to specify Point A as a starting point on the spiral arc w.r.t. its Center Point B;
ii) Devising a proper set of argument values for drawLogarithmicSpiral() function, namely, direction, stepCount, DELTA, exponent, ROTATIONS, x, y, width, height;
iii) Scaling issues of the spiral w.r.t. the price chart;
iv) Other issues that my inexperience is not able to figure out.

Would highly appreciate your 2 cents for the code and be ever grateful for bringing in a new finer elegant perspective. Requesting everybody to hop in for a discussion.

Immensely thank you for your time!

P.S.
The objective chart is recorded from a freeware called Fibotrader (a successor of Mr. Fisher’s WinPhi software) for demonstration of the goal only. IMO, this 3rd party application is not recommended for trading as it lacks user-friendliness.

The procedure drawLogarithmicSpiral() in the code was depicted from a JavaScript code originating from a Github’s OpenSource initial commit by Mr. Ikaros Kappler.

The Bar-Value pixel conversion method in the code was learned from @fxshrat post in this forum which is based on @Tomasz ’s code.

The beautiful technique for moving a GFX Object using the negation of a StaticVar’s Boolean value – if( NOT MoveInProgress ) - was learned from Herman / Al Venosa’s code.

11 Likes

at request I have made a translation from the book from BASIC code. !!!! you need latest beta version to run it.

Few things I defined differently:

  1. the equation for CON is different
  2. for A0 I just used atan2( DY/DX )

so maybe someone can check if it is correct.

Also this version is in pixel mode. When scrolling the chart the spiral does not move yet with the data. I will make an updated version later. Or maybe someone else can make that if you like. It can be made much nicer. This is just a rough version. If it has any value for trading is up for debate :slight_smile:

// AFL by E.M.Pottasch, 5/2018
// Translated from code posted in: Fibonacci Applications and Strategies for Traders (Robert Fischer, 1993)
// https://forum.amibroker.com/t/eadem-mutata-resurgo-logarithmic-spiral-in-amibroker-fibonacci-elliott-fischer/5764

dir = ParamToggle( "Direction", "ClockWise|CounterClockWise", 0 );
stepCount = Param( "stepCount", 512, 1, 1024, 1 );
incr = Param( "Increment (degrees)", 0.1, 0.01, 1, 0.01 );

if( dir == 0 )
{
    DIR = 1;
}
else
{
    DIR = -1;
}

GfxSetZOrder( 5 );
GfxSetCoordsMode( 0 );

bi = BarIndex();
x1 = y1 = x2 = y2 = 0;
PI = 3.141593;
PHI = ( sqrt( 5 ) + 1 ) / 2; // Golden ratio
CON = ( 1 / ( 2 * PI ) ) * Log( phi );

b = GetCursorMouseButtons();
x = GetCursorXPosition( 1 );
y = GetCursorYPosition( 1 );

if( b & 8 )
{
    if( b & 1 )
    {
        RequestMouseMoveRefresh();
        //x = LastValue( ValueWhen( x == DateTime(), bi ) ); 
        StaticVarSet( "x1", x );
        StaticVarSet( "y1", y );
    }
}
else
{
    if( b == 1 )
    {
        RequestMouseMoveRefresh();
        //x = LastValue( ValueWhen( x == DateTime(), bi ) );
        StaticVarSet( "x2", x );
        StaticVarSet( "y2", y );
    }
}

SetChartOptions( 0, chartShowDates );
SetChartBkColor( ColorRGB( 0, 0, 0 ) );
Plot( C, "", colorWhite, styleCandle, Null, Null, 0, 0, 1 );

// center point A
P1X = StaticVarGet( "x1" );
P2X = StaticVarGet( "x2" );
// start point B
P1Y = StaticVarGet( "y1" );
P2Y = StaticVarGet( "y2" );

GfxSelectPen( colorGreen, 2 );
GfxMoveTo( P1X, P1Y );
GfxLineTo( P2X, P2Y );

DX = P2X - P1X;
DY = P2Y - P1Y;
R0 = sqrt( DX ^ 2 + DY ^ 2 );
A0 = atan2( DY, DX );

GfxSelectPen( colorDarkBlue, 2 );
A = 0;
// start point B
TP1X = P2X;
TP1Y = P2Y;
GfxMoveTo( TP1X, TP1Y );

if( R0 > 0 || !IsEmpty( R0 ) )
{
    for( step = 0; step < stepCount; step++ )
    {
        A = A + incr;
        R = R0 * exp( CON * A );
        TA = A * DIR + A0;

        TP2X = cos( TA ) * R + P1X;
        TP2Y = sin( TA ) * R + P1Y;

        GfxLineTo( TP2X, TP2Y );

        TP1X = TP2X;
        TP1Y = TP2Y;
    }
}
3 Likes

Thanks a ton Ed,

What intrigued my curiosity is the author's promise that the spiral can attempt to forecast time with increased degree of accuracy provided the correct center is chosen along with an appropriate spiral start point. Have gone through few books on trading - mostly they speak about indicators based on price, volume, some even enter into the territory of calculating market cycles, seasonality, etc.

But with Mr. Fischer in his book he clearly claims (along with Chart images and thorough explanation) that TIME can also be anticipated in order to forecast major price movements (or events). Although his book is more than 25 years old, but it was a new concept for me. Unless backtested, it is hard to ascertain his claims.

Ed, no words for your level of skill and proficiency. Many lessons leaned from this piece of code. I know you had to compromise your schedule for this thing.

I THANK YOU and wish the very best of Health & Wealth for you!

2 Likes

thanks, will post some updates later

here is a version where you can scroll the chart.

when you have your spiral pattern in place you can press the Middle Mouse Button. Then the pattern will be locked in place. You can now play around with the pattern locked in place.

To unlock press the button "Free Spiral". You need again the latest Beta for this.

// AFL by E.M.Pottasch, 5/2018
// Translated from code posted in: Fibonacci Applications and Strategies for Traders (Robert Fischer, 1993)
// https://forum.amibroker.com/t/eadem-mutata-resurgo-logarithmic-spiral-in-amibroker-fibonacci-elliott-fischer/5764

dir = ParamToggle( "Direction", "ClockWise|CounterClockWise", 0 );
stepCount = Param( "stepCount", 512, 1, 1024, 1 );
incr = Param( "Increment (Radians)", 0.1, 0.01, 1, 0.01 );
SetBarsRequired( sbrAll );
RequestTimedRefresh( 1 );
GfxSetZOrder( 5 );

if( dir == 0 )
{
    DIR = 1;
}
else
{
    DIR = -1;
}

function ConvertBarToPixelX( bar )
{
    lvb = Status( "lastvisiblebar" );
    fvb = Status( "firstvisiblebar" );
    pxchartleft = Status( "pxchartleft" );
    pxchartwidth = Status( "pxchartwidth" );

    if( ( lvb - fvb ) != 0 )
        rr = pxchartleft + ( ( bar - fvb ) / ( lvb - fvb ) ) * pxchartwidth;
    else
        rr = 0;

    return rr;
}

function ConvertValueToPixelY( Value )
{
    Miny = Status( "axisminy" );
    Maxy = Status( "axismaxy" );
    pxchartbottom = Status( "pxchartbottom" );
    pxchartheight = Status( "pxchartheight" );

    if( ( Maxy - Miny ) != 0 )
        rr = pxchartbottom - floor( 0.5 + ( Value - Miny ) * pxchartheight / ( Maxy - Miny ) );
    else
        rr = 0;

    return rr;
}

GuiSetFont( "Lucida Console", 10 );
GuiButton( "Free Spiral", 1, 10, 50, 120, 30, 7 );
GuiSetColors( 1, 1, 2, colorRed, colorBlack, colorRed, colorWhite, colorBlue, colorYellow, colorRed, colorBlack, colorYellow );

id = GuiGetEvent( 0, 0 );
event = GuiGetEvent( 0, 1 );

if( id == 1 && event == 1 )
{
    Say( "Free Spiral Pattern" );
    StaticVarSet( "LockSpiral", 0 );
    StaticVarSet( "x1", 0 );
    StaticVarSet( "y1", 0 );
    StaticVarSet( "x2", 0 );
    StaticVarSet( "y2", 0 );
}

bi = BarIndex();
x1 = y1 = x2 = y2 = 0;
PI = 3.141593;
PHI = ( sqrt( 5 ) + 1 ) / 2; // Golden ratio
CON = ( 1 / ( 2 * PI ) ) * Log( phi );

b = GetCursorMouseButtons();
x = GetCursorXPosition( 0 );
y = GetCursorYPosition( 0 );

if( Nz( StaticVarGet( "LockSpiral" ) ) == 0 )
{
    if( b & 8 )
    {
        if( b & 1 )
        {
            RequestMouseMoveRefresh();
            x = LastValue( ValueWhen( x == DateTime(), bi ) );
            StaticVarSet( "x1", x );
            StaticVarSet( "y1", y );
        }

        if( b & 4 ) // middle mouse button
        {
            StaticVarSet( "LockSpiral", 1 );
            Say( "Lock Spiral Pattern" );
        }
    }
    else
    {
        if( b == 1 )
        {
            RequestMouseMoveRefresh();
            x = LastValue( ValueWhen( x == DateTime(), bi ) );
            StaticVarSet( "x2", x );
            StaticVarSet( "y2", y );
        }
    }
}

SetChartOptions( 0, chartShowDates );
SetChartBkColor( ColorRGB( 0, 0, 0 ) );
Plot( C, "", colorWhite, styleCandle, Null, Null, 0, 0, 1 );

// center point A
P1X = ConvertBarToPixelX( StaticVarGet( "x1" ) );
P1Y = ConvertValueToPixelY( StaticVarGet( "y1" ) );
GfxSelectFont( "Tahoma", 10, 700 );
GfxSetBkMode( 1 );
GfxSetTextColor( colorYellow );
GfxTextOut( "Center", P1X, P1Y );
// start point B
P2X = ConvertBarToPixelX( StaticVarGet( "x2" ) );
P2Y = ConvertValueToPixelY( StaticVarGet( "y2" ) );
GfxTextOut( "Start", P2X, P2Y );

GfxSelectPen( colorGreen, 2 );
GfxMoveTo( P1X, P1Y );
GfxLineTo( P2X, P2Y );

DX = P2X - P1X;
DY = P2Y - P1Y;
R0 = sqrt( DX ^ 2 + DY ^ 2 );
A0 = atan2( DY, DX );

GfxSelectPen( colorDarkBlue, 2 );
A = 0;
// start point B
TP1X = P2X;
TP1Y = P2Y;
GfxMoveTo( TP1X, TP1Y );

if( R0 > 0 || !IsEmpty( R0 ) )
{
    for( step = 0; step < stepCount; step++ )
    {
        A = A + incr;
        R = R0 * exp( CON * A );
        TA = A * DIR + A0;

        TP2X = cos( TA ) * R + P1X;
        TP2Y = sin( TA ) * R + P1Y;

        GfxLineTo( TP2X, TP2Y );

        TP1X = TP2X;
        TP1Y = TP2Y;
    }
}
4 Likes

there is still a problem with it. Something I did wrong with the scaling or conversion to pixels. When a pattern is locked and you zoom in or out or scroll the chart it becomes apparent ...

1 Like

Wow Ed !

Yes the scrolling and scaling issues were the original challenges too. :expressionless:

I think, since the X and the Y-axis are of different units, it makes the spiral lines get fixed to the bars procuring the scrolling and scaling irrelevant.

I was hoping somebody else would have solved it by now :slight_smile:

yes it is due to the fact that I transform real coordinated to pixels. When I use this instead:

P1X = StaticVarGet( "x1" );
P1Y = StaticVarGet( "y1" );
P2X = StaticVarGet( "x2" );
P2Y = StaticVarGet( "y2" );

//P1X = ConvertBarToPixelX( P1X );
//P1Y = ConvertValueToPixelY( P1Y );
//P2X = ConvertBarToPixelX( P2X );
//P2Y = ConvertValueToPixelY( P2Y );

DX = P2X - P1X;
DY = P2Y - P1Y;
R0 = sqrt( DX ^ 2 + DY ^ 2 );
A0 = atan2( DY, DX );

GfxSetCoordsMode( 1 ); 

.... so using actual coordinates, then zooming and scrolling is allowed and the pattern remains unchanged, however, the pattern is off the charts. So not sure how to solve it. Have to look into how Robert Fischer did this

1 Like

Dear Ed and others passing by,

This spiral is everywhere!

Bacteria's growth
Ferns
Pine cones
Sea horses
Snail shells
Animal horns
Ocean waves
Spider web spins
Sunflower seeds
Daisies
Hurricane clouds
The tail of a comet curving away from the sun
The Galaxies of outer space
On cover of Frost & Prechter's book "Elliott Wave Principle"

The core of a logarithmic spiral seen through a microscope would have the same look as its widest viewable reach from light years away (truly Eadem mutata Resurgo).

For last 7 hours trying to decipher the meaning of First-order differential linear equations found in MIT Open Courseware Lecture Notes and Readings. I am totally bewildered and tired!

Famous quote of Carl Sagan, "If you wish to make an apple pie from scratch, you must first invent the universe".

Thank you!

I am not sure if it can be successfully used in trading.

with respect to the problem with zooming in and scrolling the chart when one previously put a spiral pattern on the chart: when scrolling or zooming in the length of R0 and the angle A0 change. Even though P1X, P1Y, etc are initially stored in static variables as normal chart coordinates, before calculating R0 and A0 I transformed P1X, P1Y etc to pixels. The pattern will therefor also change.

If you do not transform P1X, P1Y to pixels and keep the chart coordinates (bar, price) then R0 and A0 will remain the same when zooming/scrolling. However, the pattern itself is off the chart.

So you want to use the pixel mode but somehow calculate a scaling factor when scrolling/zooming. That I did not manage so far. I found some mq4 code, google for "fx5_fibospiral_v1.0.mq4". Here the coder seems to use some scaling in his DrawLine() function.

So if someone else can figure it out that would be good :slight_smile:

I add the code again with an additional parameter to toggle between "real coordinate" and "pixel" modes.

So you first make the pattern by:

  1. clicking the left mouse button on the center point
  2. keeping mouse button down moving to the start point
  3. clicking middle mouse button to lock the pattern

then you can use the param window to toggle between pix mode and real mode.

// AFL by E.M.Pottasch, 5/2018
// Translated from code posted in: Fibonacci Applications and Strategies for Traders (Robert Fischer, 1993)
// https://forum.amibroker.com/t/eadem-mutata-resurgo-logarithmic-spiral-in-amibroker-fibonacci-elliott-fischer/5764

dir = ParamToggle( "Direction", "ClockWise|Counter ClockWise", 0 );
stepCount = Param( "stepCount", 512, 1, 1024, 1 );
incr = Param( "Increment (Radians)", 0.1, 0.01, 1, 0.01 );
pixmode = ParamToggle( "Chart Mode", "Real Coordinates Mode|Pixel Mode", 1 );
SetBarsRequired( sbrAll );
RequestTimedRefresh( 1 );
GfxSetZOrder( 5 );

if( dir == 0 )
{
    DIR = 1;
}
else
{
    DIR = -1;
}

function ConvertBarToPixelX( bar )
{
    lvb = Status( "lastvisiblebar" );
    fvb = Status( "firstvisiblebar" );
    pxchartleft = Status( "pxchartleft" );
    pxchartwidth = Status( "pxchartwidth" );

    if( ( lvb - fvb ) != 0 )
        rr = pxchartleft + ( ( bar - fvb ) / ( lvb - fvb ) ) * pxchartwidth;
    else
        rr = 0;

    return rr;
}

function ConvertValueToPixelY( Value )
{
    Miny = Status( "axisminy" );
    Maxy = Status( "axismaxy" );
    pxchartbottom = Status( "pxchartbottom" );
    pxchartheight = Status( "pxchartheight" );

    if( ( Maxy - Miny ) != 0 )
        rr = pxchartbottom - floor( 0.5 + ( Value - Miny ) * pxchartheight / ( Maxy - Miny ) );
    else
        rr = 0;

    return rr;
}

GuiSetFont( "Lucida Console", 10 );
GuiButton( "Free Spiral", 1, 10, 50, 120, 30, 7 );
GuiSetColors( 1, 1, 2, colorRed, colorBlack, colorRed, colorWhite, colorBlue, colorYellow, colorRed, colorBlack, colorYellow );

id = GuiGetEvent( 0, 0 );
event = GuiGetEvent( 0, 1 );

if( id == 1 && event == 1 )
{
    Say( "Free Spiral Pattern" );
    StaticVarSet( "LockSpiral", 0 );
    StaticVarSet( "x1", 0 );
    StaticVarSet( "y1", 0 );
    StaticVarSet( "x2", 0 );
    StaticVarSet( "y2", 0 );
}

bi = BarIndex();
x1 = y1 = x2 = y2 = 0;
PI = 3.141593;
PHI = ( sqrt( 5 ) + 1 ) / 2; // Golden ratio
CON = ( 1 / ( 2 * PI ) ) * Log( phi );

b = GetCursorMouseButtons();
x = GetCursorXPosition( 0 );
y = GetCursorYPosition( 0 );

if( Nz( StaticVarGet( "LockSpiral" ) ) == 0 )
{
    if( b & 8 )
    {
        if( b & 1 )
        {
            RequestMouseMoveRefresh();
            x = LastValue( ValueWhen( x == DateTime(), bi ) );
            StaticVarSet( "x1", x );
            StaticVarSet( "y1", y );
        }

        if( b & 4 ) // middle mouse button
        {
            StaticVarSet( "LockSpiral", 1 );
            Say( "Lock Spiral Pattern" );
        }
    }
    else
    {
        if( b == 1 )
        {
            RequestMouseMoveRefresh();
            x = LastValue( ValueWhen( x == DateTime(), bi ) );
            StaticVarSet( "x2", x );
            StaticVarSet( "y2", y );
        }
    }
}

SetChartOptions( 0, chartShowDates );
SetChartBkColor( ColorRGB( 0, 0, 0 ) );
Plot( C, "", colorWhite, styleCandle, Null, Null, 0, 0, 1 );

P1X = StaticVarGet( "x1" );
P1Y = StaticVarGet( "y1" );
P2X = StaticVarGet( "x2" );
P2Y = StaticVarGet( "y2" );

if( pixmode )
{
    P1X = ConvertBarToPixelX( P1X );
    P1Y = ConvertValueToPixelY( P1Y );
    P2X = ConvertBarToPixelX( P2X );
    P2Y = ConvertValueToPixelY( P2Y );
}

DX = P2X - P1X;
DY = P2Y - P1Y;
R0 = sqrt( DX ^ 2 + DY ^ 2 );
A0 = atan2( DY, DX );

if( !pixmode )
{
    GfxSetCoordsMode( 1 );
}

GfxSelectFont( "Tahoma", 10, 700 );
GfxSetBkMode( 1 );
GfxSetTextColor( colorYellow );
GfxTextOut( "Center", P1X, P1Y );
GfxTextOut( "Start", P2X, P2Y );

GfxSelectPen( colorGreen, 2 );
GfxMoveTo( P1X, P1Y );
GfxLineTo( P2X, P2Y );

GfxSelectPen( colorDarkBlue, 2 );
A = 0;
// start point B
TP1X = P2X;
TP1Y = P2Y;
GfxMoveTo( TP1X, TP1Y );

if( R0 > 0 || !IsEmpty( R0 ) )
{
    for( step = 0; step < stepCount; step++ )
    {
        A = A + incr;
        R = R0 * exp( CON * A );
        TA = A * DIR + A0;

        TP2X = cos( TA ) * R + P1X;
        TP2Y = sin( TA ) * R + P1Y;

        GfxLineTo( TP2X, TP2Y );

        TP1X = TP2X;
        TP1Y = TP2Y;
    }
}

@empottasch, @Lennon I didn't have much time to work on the code (so it might need further improvements), but decided to play a little with Edward's code (thanks for the main part of the code :+1: ) and here is the result. My version is shorter. Here are the main differences:

  1. My version doesn't require using All bars
  2. I don't enforce any regular refreshes
  3. I don't use Gui Buttons.
  4. You can scroll and zoom in/out without any issues

To draw a spiral you need to press and hold Ctrl key and keeping left mouse button down (and Ctrl key pressed) move to the start point - than release mouse button. To clear the spiral just press and hold Ctrl key and click middle mouse button.


Because this code uses old screen pixel mode GfxSetCoordsMode( mode = 0) - it needs to do conversion between bars/price and pixels, extra refresh when Y scale changes is required. In all my new codes I use GfxSetCoordsMode( mode = 1) which doesn't require conversion and additional refreshes - and for that reason it is much better solution, but in this case - I wasn't able to calculate and draw the spiral in the newer mode. Because I don't like using any regular refreshes (unless they are absolutelly necessary) some time ago, I've come up with a solution which might not be ideal - but it works for my older codes - providing additional refreshes only when needed. It is used here. I've tried many different variations of such code - for example using Status( "axismaxy" ) or Status( "axisminy" ) but only below code works for me:

ChId = GetChartID();
qfdb = Status( "quickaflfirstdatabar" );
qfdbBefore = Nz( StaticVarGet( "qfdbBefore" + ChId));

if( qfdb != qfdbBefore )
{
    RequestTimedRefresh( 0.2 ); 
    StaticVarSet( "qfdbBefore" + ChId, qfdb );
}

I remember, that @Tomasz wrote some time ago, that he might implement some internal solution to provide an additional refresh in such cases (to avoid enforcing regular refreshes or some other strange solutions :wink: similar to mine ), but unfortunatelly until now it hasn't been implemented.


A solution allowing to work on a subset of array instead of all bars with the use of Status( "quickaflfirstdatabar" ) was inspired by @fxshrat work --> Link

Spiral%203

Here is the main code:

// AFL by E.M.Pottasch, 5/2018
// Translated from code posted in: Fibonacci Applications and Strategies for Traders (Robert Fischer, 1993)
// https://forum.amibroker.com/t/eadem-mutata-resurgo-logarithmic-spiral-in-amibroker-fibonacci-elliott-fischer/5764
// Version modified by Milosz Mazurkiewicz

Title = " Ctrl + Left Mouse Button --> START,   Ctrl + Middle Mouse Button --> CLEAR";
dir = ParamToggle( "Direction", "ClockWise|CounterClockWise", 0 );
stepCount = Param( "stepCount", 512, 1, 1024, 1 );
incr = Param( "Increment (Radians)", 0.1, 0.01, 1, 0.01 );
ChId = GetChartID();

if( dir == 0 ) DIR = 1;
else  DIR = -1;

qfdb = Status( "quickaflfirstdatabar" );
bi = qfdb + BarIndex(); // to avoid using all bars

// Additional refreshes - only when needed
qfdbBefore = Nz( StaticVarGet( "qfdbBefore" + ChId ) );

if( qfdb != qfdbBefore )
{
    if( StaticVarGet( "x1" ) != 0 ) RequestTimedRefresh( 0.2 );

    StaticVarSet( "qfdbBefore" + ChId, qfdb );
}


function ConvertBarToPixelX( bar )
{
    lvb = Status( "lastvisiblebar" ) + qfdb;
    fvb = Status( "firstvisiblebar" ) + qfdb;
    pxchartleft = Status( "pxchartleft" );
    pxchartwidth = Status( "pxchartwidth" );

    if( ( lvb - fvb ) != 0 ) rr = pxchartleft + ( ( bar - fvb ) / ( lvb - fvb ) ) * pxchartwidth;
    else  rr = 0;

    return rr;
}

function ConvertValueToPixelY( Value )
{
    Miny = Status( "axisminy" );
    Maxy = Status( "axismaxy" );
    pxchartbottom = Status( "pxchartbottom" );
    pxchartheight = Status( "pxchartheight" );

    if( ( Maxy - Miny ) != 0 )  rr = pxchartbottom - floor( 0.5 + ( Value - Miny ) * pxchartheight / ( Maxy - Miny ) );
    else rr = 0;

    return rr;
}

x1 = y1 = x2 = y2 = 0;
PI = 3.141593;
PHI = ( sqrt( 5 ) + 1 ) / 2; // Golden ratio
CON = ( 1 / ( 2 * PI ) ) * Log( phi );

MouseButtonPressed = GetCursorMouseButtons();
CtrlPressed =  GetAsyncKeyState( 17 ) < 0 ;
x = GetCursorXPosition( 0 );
y = GetCursorYPosition( 0 );

if( CtrlPressed )

{

    x = LastValue( ValueWhen( x == DateTime(), bi ) );

    if( MouseButtonPressed & 8 ) // Center point coordinates
    {
        StaticVarSet( "x1" + ChId, x );
        StaticVarSet( "y1" + ChId, y );
    }

    if( MouseButtonPressed & 9 ) // Start point coordinates
    {
        StaticVarSet( "x2" + ChId, x ) + ChId;
        StaticVarSet( "y2" + ChId, y ) + ChId;
    }

    if( MouseButtonPressed & 4 ) // Reset spiral pattern
    {
        StaticVarSet( "x1" + ChId, 0 );
        StaticVarSet( "y1" + ChId, 0 );
        StaticVarSet( "x2" + ChId, 0 );
        StaticVarSet( "y2" + ChId, 0 );
    }

    RequestMouseMoveRefresh();
}

Plot( C, "", colorWhite, styleCandle, Null, Null, 0, 0, 1 );

// center point A
P1X = ConvertBarToPixelX( StaticVarGet( "x1" + ChId ) );
P1Y = ConvertValueToPixelY( StaticVarGet( "y1" + ChId ) );
GfxSetTextColor( colorLightGrey );
GfxCircle( P1X, P1Y, 4 );
// start point B
P2X = ConvertBarToPixelX( StaticVarGet( "x2" + ChId ) );
P2Y = ConvertValueToPixelY( StaticVarGet( "y2" + ChId ) );
GfxCircle( P2X, P2Y, 4 );

GfxSelectPen( colorLightGrey, 2 );
GfxMoveTo( P1X, P1Y );
GfxLineTo( P2X, P2Y );

DX = P2X - P1X;
DY = P2Y - P1Y;
R0 = sqrt( DX ^ 2 + DY ^ 2 );
A0 = atan2( DY, DX );

GfxSelectPen( colorLightGrey, 2 );
A = 0;
// start point B
TP1X = P2X;
TP1Y = P2Y;
GfxMoveTo( TP1X, TP1Y );

if( R0 > 0 || !IsEmpty( R0 ) )
{
    for( step = 0; step < stepCount; step++ )
    {
        A = A + incr;
        R = R0 * exp( CON * A );
        TA = A * DIR + A0;

        TP2X = cos( TA ) * R + P1X;
        TP2Y = sin( TA ) * R + P1Y;

        GfxLineTo( TP2X, TP2Y );

        TP1X = TP2X;
        TP1Y = TP2Y;
    }
}

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

Regards

7 Likes

thanks for your effort, Milosz.

I will study it in more detail later. I like the mouse press solution and the qfdb.

However, on a first glance the issue I was talking about, when you zoom in/out or scroll the chart you can see that the position of the spiral rings changes with respect to the price. This is the same problem I did not fix yet.

It needs some additional scaling both in the x and y direction (imo).

3 Likes

Edward, yes I confirm - I haven't noticed that earlier, but when the chart is scrolled, the position of the spiral rings sometimes changes (usually a little bit). In that case, using the newer GfxSetCoordsMode( 1 ) which allows way easier overlays on top of existing charts and guarentees a perfect match, would be the best solution. Probably it is doable, but (at least to me), it seems much more difficult...

Personally I am not Elliott or Fibonacci enthusiast, and I don't use things like logarithmic spirals in my trading, but I wanted to help Lennon, because I liked his initial post :slight_smile:

2 Likes

neither do I, with respect to Fibs :slight_smile:

better stop now because I had some beer.

I did not solve the scaling bit but it is about capturing the chart conditions when the pattern is locked, like:

            // store chart conditions when locked
            StaticVarSet( "lvb0", Status( "lastvisiblebar" ) );
            StaticVarSet( "fvb0", Status( "firstvisiblebar" ) );
            StaticVarSet( "miny0", Status( "axisminy" ) );
            StaticVarSet( "maxy0", Status( "axismaxy" ) );

and then when you zoom or scroll the chart calculate scalings in the X and Y direction, something like:

    lvb = Status( "lastvisiblebar" );
    fvb = Status( "firstvisiblebar" );
    lvb0 = StaticVarGet( "lvb0" );
    fvb0 = StaticVarGet( "fvb0" );
    scaleX = ( lvb0 - fvb0 ) / ( lvb - fvb );

    miny = Status( "axisminy" );
    maxy = Status( "axismaxy" );
    miny0 = StaticVarGet( "miny0" );
    maxy0 = StaticVarGet( "maxy0" );
    scaleY = ( maxy0 - miny0 ) / ( maxy - miny );

but I did not manage to apply the scaling correctly yet,

I will look at it again mañana

1 Like

@empottasch @Milosz Thank you for being the kindest!

@Milosz Many thanks for sharing that technique from your arsenal.

I think the appropriate scaling solution lies within the inbuilt 4th argument of Plot() - StyleNoRescale, StyleOwnScale. When I first met Plot() function about 2 years back I was totally mesmerized by the amount of precision carried by one single function.

Is there a way to create an array of the (x, y) co-ordinates of the spiral and get that plotted using Plot()?

In the meantime would monkey around your codes to learn more without the beer (otherwise with beer no learning, only monkeying). I feel like a zygote in-front of you experts. :slight_smile:

1 Like

I am interest also about this post, I really like this Spiral tool.

As we do not have any native Spiral at this moment in Amibroker I would like to ask @Tomasz to tell us a Hint so we can finish the code.

This version is exactly the same as the previous one, and the only deferent is that we DONT need any staticVar as we need to use a TrendLine

@Milosz i have change little bit the Additional refreshes :wink:
@empottasch thank you :sunglasses:
@Lennon i am really happy that i saw, your coding skills are muchbetter than last year :face_with_raised_eyebrow:

// AFL by E.M.Pottasch, 5/2018
// Translated from code posted in: Fibonacci Applications and Strategies for Traders (Robert Fischer, 1993)
// https://forum.amibroker.com/t/eadem-mutata-resurgo-logarithmic-spiral-in-amibroker-fibonacci-elliott-fischer/5764
// Version modified by Milosz Mazurkiewicz

// Version modified by Panos.  A TrendLine Version. So we dont need any staticVar  02/05/2018
//  Draw a trend line and TYPE in a Study ID = SP

dir = ParamToggle( "Direction", "ClockWise|CounterClockWise", 0 );
stepCount = Param( "stepCount", 512, 1, 1024, 1 );
incr = Param( "Increment (Radians)", 0.1, 0.01, 1, 0.01 );
ChId = GetChartID();

if( dir == 0 ) DIR = 1;
else  DIR = -1;

qfdb = Status( "quickaflfirstdatabar" );  // printf("qfdb " + qfdb);
bi = qfdb + BarIndex(); // to avoid using all bars

// Additional refreshes - only when needed
qfdbBefore = Nz( StaticVarGet( "qfdbBefore" + ChId ) ); // printf("\nqfdbBefore " + qfdbBefore);

if( qfdb != qfdbBefore )
{
    RequestTimedRefresh( 0.1 );
    StaticVarSet( "qfdbBefore" + ChId, qfdb );
}


function ConvertBarToPixelX( bar )
{
    lvb = Status( "lastvisiblebar" ) + qfdb;
    fvb = Status( "firstvisiblebar" ) + qfdb;
    pxchartleft = Status( "pxchartleft" );
    pxchartwidth = Status( "pxchartwidth" );

    if( ( lvb - fvb ) != 0 ) rr = pxchartleft + ( ( bar - fvb ) / ( lvb - fvb ) ) * pxchartwidth;
    else  rr = 0;

    return rr;
}

function ConvertValueToPixelY( Value )
{
    Miny = Status( "axisminy" );
    Maxy = Status( "axismaxy" );
    pxchartbottom = Status( "pxchartbottom" );
    pxchartheight = Status( "pxchartheight" );

    if( ( Maxy - Miny ) != 0 )  rr = pxchartbottom - floor( 0.5 + ( Value - Miny ) * pxchartheight / ( Maxy - Miny ) );
    else rr = 0;

    return rr;
}

PI = 3.141593;
PHI = ( sqrt( 5 ) + 1 ) / 2; // Golden ratio
CON = ( 1 / ( 2 * PI ) ) * Log( phi );


Plot( C, "", colorWhite, styleCandle, Null, Null, 0, 0, 1 );

trendline = Study("SP", GetChartID() );  
    X1 = StartX = LastValue( ValueWhen( ExRem( trendline, 0 ), bi ) );
    X2 = EndX = LastValue( ValueWhen( trendline, bi ) );			 //printf("X1 StartX  = %g,\n X2 EndX = %g\n", StartX, EndX );
    Y1 = StartY = LastValue( ValueWhen( ExRem( trendline, 0 ), trendline ) );
    Y2 = EndY = LastValue( ValueWhen( trendline, trendline ) );		 //printf("\nY1 StartY  = %g,\nY2  EndY = %g\n", StartY, EndY );

// center point A
P1X = ConvertBarToPixelX( X1 );
P1Y = ConvertValueToPixelY( Y1 );
GfxSelectFont( "Tahoma", 10, 700 );
GfxSetBkMode( 1 );
GfxSetTextColor( colorYellow );
GfxTextOut( "Center", P1X, P1Y );
// start point B
P2X = ConvertBarToPixelX( X2 );
P2Y = ConvertValueToPixelY( Y2 );
GfxTextOut( "Start", P2X, P2Y );


DX = P2X - P1X;
DY = P2Y - P1Y;
R0 = sqrt( DX ^ 2 + DY ^ 2 );
A0 = atan2( DY, DX );

GfxSelectPen( colorDarkBlue, 2 );
A = 0;
// start point B
TP1X = P2X;
TP1Y = P2Y;
GfxMoveTo( TP1X, TP1Y );

if( R0 > 0 || !IsEmpty( R0 ) )
{
    for( step = 0; step < stepCount; step++ )
    {
        A = A + incr;
        R = R0 * exp( CON * A );
        TA = A * DIR + A0;

        TP2X = cos( TA ) * R + P1X;
        TP2Y = sin( TA ) * R + P1Y;

        GfxLineTo( TP2X, TP2Y );

        TP1X = TP2X;
        TP1Y = TP2Y;
    }
}

Title = "Golden Spiral \nTrendLine Version";
if( x1 == 0  ) 	 { Title = "\n Draw a trendline, Click properties and insert StudyID 'SP' \n Study with ID=='SP' is not drawn yet";  } 

3 Likes

Glad to have your kind guidance! Thank you!

And this is another method of plotting the spiral.

TrendLine version maybe will better for me..... why? We can make a function

This is a Fibbo Function.
Take it as a present, do your homework, and came tomorrow with a Spiral Function like this one

_SECTION_BEGIN( "Fibo xGroup Color Lines" );
// How to get same Group Color Fibonacci Retracements parallel lines based on study() ? 
// By Panos Version 1   Jan - 2012  ( Ver 2 2014 PlotText added)

// i wrote this code to have a Multy Fibo Group Colors
// example = xFibo("F1",colorYellow ,extend=0 );


// Plot(C, "Price", colorWhite, styleCandle ); 
x=Cum(1)-1;  WriteVal(x ) +" x ";
LastX=LastValue(x);

function  xFibo( text,Color, extend )   //
{
su = Study(text, GetChartID() ); 	 isn = IsNull( su ); 

begpoint = NOT isn AND Ref( isn, -1 );   //su 
endpoint = NOT isn AND Ref( isn, 1 );  

X1=LastValue(ValueWhen(su ,x,1)) ; 						WriteVal(x1)+"  X1  end"; 
X13=LastValue(ValueWhen(endpoint ,BarIndex())) ; 		WriteVal(x13)+" X13   BI end "; 
X14=LastValue(ValueWhen(begpoint ,x,1)); 				WriteVal(x14)+"  X14  end test "; 
xo=ValueWhen(begpoint ,x,1) ;  							 WriteVal(xo) +"  X0 start" ;
// SU
dt = DateTime(); 
startdatetime = LastValue( ValueWhen( begpoint, dt ) );     //x0
enddatetime = LastValue( ValueWhen( endpoint, dt ) ); 	//X1
YO=startval = LastValue( ValueWhen( begpoint, su ) ); 		 // Y0
Y1=endval = LastValue( ValueWhen( endpoint, su ) ); 		// y1

// Y = ypsos
RightF=(Y1 +YO);
LeftF= (Y1 -YO);
y0=   YO+ 0*(LeftF);  y100= Y0+LeftF;   
y50=  (Y1 +YO)*0.50;  							WriteVal(y50,1.4) +"  ySU 50%" ;//
y618= YO+ 0.618*(LeftF);						WriteVal( (y618),1.4) +"   (y61)\n" ;//
y382= YO+ 0.382*(LeftF);
y236= YO+ 0.236*(LeftF);
y1236= YO+ 1.236*(LeftF);
y1382= YO+ 1.382*(LeftF);
y1618= YO+ 1.618*(LeftF);
y2618= YO+ 2.618*(LeftF);
y4236= YO+ 4.236*(LeftF);
//xStyle=styleThick|styleNoRescale|styleNoLabel;  // εμφάνιση γραμμων styleThick
xStyle=styleDashed|styleNoRescale|styleNoLabel;  // γραμμες με διακεκομενη εμφάνιση 
// Fibbo-------  LeftF-----------
Line0=LineArray( x14 , y0, x1, y0,  extend ) ;
Plot( Line0, "", Color ,xStyle  );
Line100=LineArray( x14 , y100, x1, y100,  extend ) ;
Plot( Line100, "", Color ,xStyle  );

Line50=LineArray( x14 , y50, x1, y50,  extend ) ;
Plot( Line50, "", Color ,xStyle  );
Line618=LineArray( x14 , y618, x1, y618,  extend ) ;
 Plot( Line618, "", Color ,xStyle );
Line382=LineArray( x14 , y382, x1, y382,  extend ) ;
Plot( Line382, "", Color ,xStyle);
Line236=LineArray( x14 , y236, x1, y236,  extend ) ;
Plot( Line236, "", Color ,xStyle );

Line1236=LineArray( x14 , y1236, x1, y1236,  extend ) ;		WriteVal( (y1236),1.4) +"   (y1236)" ;
Plot( Line1236, "", Color ,xStyle);
Line1382=LineArray( x14 , y1382, x1, y1382, extend ) ;		WriteVal( (y1382),1.4) +"   (y1382)" ;
Plot( Line1382, "", Color ,xStyle);
Line1618=LineArray( x14 , y1618, x1, y1618, extend ) ;		WriteVal( (y1618),1.4) +"   (y1618) \n" ;
Plot( Line1618, "", Color ,xStyle);

Line2618=LineArray( x14 , y2618, x1, y2618, extend ) ;		WriteVal( (y2618),1.4) +"   (y2618) \n" ;
Plot( Line2618, "", Color ,xStyle);
Line4236=LineArray( x14 , y4236, x1, y4236, extend ) ;		WriteVal( (y4236),1.4) +"   (y4236) \n" ;
Plot( Line4236, "", Color ,xStyle);

InfoPers= ParamToggle(" % ","No|Yes",0);
if (InfoPers)
{
PlotText("0.0% ",X14,y0,color);
PlotText("100.0% ",X14,y100,color);

PlotText("23.6% ",X14,y236,color);
PlotText("38.2% ",X14,y382,color);
PlotText("50% ",X14,y50,color);
PlotText("0.618% ",X14,y618,color);
PlotText("123.6% ",X14,y1236,color);
PlotText("138.2% ",X14,y1382,color);
PlotText("161.8%   ("+y1618+")",X14,y1618,color);
PlotText("261.8%   ("+y2618+")",X14,y2618,color);
PlotText("4.236%   ("+y4236+")",X14,y4236,color); // εμφανισει % και τιμες εδω
}
}


//extend= Param("Extend Fibo Lines",0,0,3,1);
extend= ParamList("Extend Fibo Lines","without|Right|Left|Right & Left",0);
if (extend=="Right"){extend =1;}
else if (extend=="Left"){extend =2;}
else if (extend=="Right & Left"){extend =3;}
else extend =0;

xFibo("F1",colorYellow,extend);
xFibo("F2",colorBlue,extend);
xFibo("F3",colorAqua,extend);
xFibo("F4",colorBlueGrey,extend);
xFibo("F5",colorBrightGreen,extend);
xFibo("F6",colorLightGrey,extend);
xFibo("F7",colorAqua,extend);
xFibo("F8",colorBlue,extend);

_SECTION_END();
4 Likes

@PanoS I like your idea of using a TrendLine :slight_smile: I was thinking about a similar solution - allowing to move around the Start and the Center point programatically (which is not that difficult to implement), but using a TrendLine for that is much easier :+1: On the other hand, drawing the spiral by hand was a good excuse to use RequestMouseMoveRefresh() implemented in AB 6.25 (and improved in 6.26) and show, that we can achieve butter-smooth refreshes in AB chart window on demand (without forcing any regular refreshes which have negative impact on the performance).

@Lennon I'm afraid that is not the case here. We need a perfect match between the spiral and the chart (not the other way around) and that can be achieved in at least two different ways, but in my opinion GfxSetCoordsMode( 1 ), would be ideal for that. That would also address some other issues like the additional refreshes (not required in this mode) or checking if the spiral's rings have been approached/crossed ...

1 Like

Thank you for the clarification Milosz!

What I meant was to incorporate the auto-scaling factor of Plot() for the GFX controls used in the spiral.

Tried removing the Plot() from the code using GfxSetOverlayMode( 2 ) but the Spiral is still hit by scaling issues.

bi = BarIndex();
pxchartwidth = Status( "pxchartwidth" );
pxchartheight = Status( "pxchartheight" );
lvb = LastVisibleValue( bi );
fvb = FirstVisibleValue( bi );
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 );
}

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

	 GfxSetOverlayMode( 2 );

	 // ScreenResolution, PixIncr and the for-loop was taken from the code posted by SpinTrader @ https://forum.amibroker.com/t/plotting-a-chart-using-gfx/1674/10
	 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 ) );
_SECTION_END();


// AFL by E.M.Pottasch, 5/2018
// Translated from code posted in: Fibonacci Applications and Strategies for Traders (Robert Fischer, 1993)
// https://forum.amibroker.com/t/eadem-mutata-resurgo-logarithmic-spiral-in-amibroker-fibonacci-elliott-fischer/5764
_SECTION_BEGIN( "Log. Spiral" );
	 dir = ParamToggle( "Direction", "ClockWise|Counter ClockWise", 0 );
	 stepCount = Param( "stepCount", 512, 1, 1024, 1 );
	 incr = Param( "Increment (Radians)", 0.1, 0.01, 1, 0.01 );
	 SetBarsRequired( sbrAll );
	 RequestTimedRefresh( 1 );
	 
	 if( dir == 0 )
	 {
		 DIR = 1;
	 }
	 else
	 {
		 DIR = -1;
	 }
	 
	 GuiSetFont( "Courier New", 10 );
	 GuiButton( "Free Spiral", 1, 10, 50, 120, 30, 7 );
	 GuiSetColors( 1, 1, 2, colorRed, colorBlack, colorRed, colorWhite, colorBlue, colorYellow, colorRed, colorBlack, colorYellow );

	 id = GuiGetEvent( 0, 0 );
	 event = GuiGetEvent( 0, 1 );

	 if( id == 1 && event == 1 )
	 {
		 Say( "Free Spiral Pattern" );
		 StaticVarSet( "LockSpiral", 0 );
		 StaticVarSet( "x1", 0 );
		 StaticVarSet( "y1", 0 );
		 StaticVarSet( "x2", 0 );
		 StaticVarSet( "y2", 0 );
	 }

	 bi = BarIndex();
	 x1 = y1 = x2 = y2 = 0;
	 PI = 3.141593;
	 PHI = ( sqrt( 5 ) + 1 ) / 2; // Golden ratio
	 CON = ( 1 / ( 2 * PI ) ) * Log( phi );

	 b = GetCursorMouseButtons();
	 x = GetCursorXPosition( 0 );
	 y = GetCursorYPosition( 0 );

	 if( Nz( StaticVarGet( "LockSpiral" ) ) == 0 )
	 {
		 if( b & 8 )
		 {
			 if( b & 1 )
			 {
				 RequestMouseMoveRefresh();
				 x = LastValue( ValueWhen( x == DateTime(), bi ) );
				 StaticVarSet( "x1", x );
				 StaticVarSet( "y1", y );
			 }

			 if( b & 4 ) // middle mouse button
			 {
				 StaticVarSet( "LockSpiral", 1 );
				 Say( "Lock Spiral Pattern" );
			 }
		 }
		 else
		 {
			 if( b == 1 )
			 {
				 RequestMouseMoveRefresh();
				 x = LastValue( ValueWhen( x == DateTime(), bi ) );
				 StaticVarSet( "x2", x );
				 StaticVarSet( "y2", y );
			 }
		 }
	}

	 SetChartOptions( 0, chartShowDates );
	 SetChartBkColor( ColorRGB( 0, 0, 0 ) );
	 
	 P1X = StaticVarGet( "x1" );
	 P1Y = StaticVarGet( "y1" );
	 P2X = StaticVarGet( "x2" );
	 P2Y = StaticVarGet( "y2" );

	 P1X = GfxConvertBarToPixelX( P1X );
	 P1Y = GfxConvertValueToPixelY( P1Y, _log );
	 P2X = GfxConvertBarToPixelX( P2X );
	 P2Y = GfxConvertValueToPixelY( P2Y, _log );
	
	 DX = P2X - P1X;
	 DY = P2Y - P1Y;
	 R0 = sqrt( DX ^ 2 + DY ^ 2 );
	 A0 = atan2( DY, DX );

	 GfxSelectFont( "Courier New", 10, 700 );
	 GfxSetBkMode( 1 );
	 GfxSetTextColor( colorYellow );
	 GfxTextOut( "Center", P1X, P1Y );
	 GfxTextOut( "Start", P2X, P2Y );

	 GfxSelectPen( colorGreen, 2 );
	 GfxMoveTo( P1X, P1Y );
	 GfxLineTo( P2X, P2Y );

	 GfxSelectPen( colorWhite, 2 );
	 A = 0;
	 // start point B
	 TP1X = P2X;
	 TP1Y = P2Y;
	 GfxMoveTo( TP1X, TP1Y );

	 if( R0 > 0 || !IsEmpty( R0 ) )
	 {
		 for( step = 0; step < stepCount; step++ )
		 {
			 A = A + incr;
			 R = R0 * exp( CON * A );
			 TA = A * DIR + A0;

			 TP2X = cos( TA ) * R + P1X;
			 TP2Y = sin( TA ) * R + P1Y;

			 GfxLineTo( TP2X, TP2Y );

			 TP1X = TP2X;
			 TP1Y = TP2Y;
		 }
	 }
_SECTION_END();


RequestTimedRefresh( 1 );

Wondering if auto-scaling is possible in GfxSetOverlayMode( 2 )?

P.S.
Related post: Plotting a chart using gfx