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

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

hi guys,

about using GfxSetCoordsMode( 1 ) I already did this in this post:

where you can toggle between "Pixel Mode" and "Bar / Price Mode".

In "Bar / Price Mode" the spiral does not change its shape when scrolling or zooming but the shape of the spiral is elongated because the x and y units are so far apart in size.

Transforming real coordinates into pixels makes the dimensions in the x and y direction equal and therefor the spiral has a nice circular shape.

However, when you scroll the chart then the number of pixels per bar remains the same but the number of pixels per price unit will change. When zooming the chart both the number of pixels per bar and the number of pixels per price unit will change.

So this is the reason why the spiral in "Pixel Mode" changes its shape. It needs a correction factor. But I have a little bit of a fog in my head, I didn't manage yet.

Edward, yes I have already tested your solution and noticed the problem with the spiral's elongated shape, but if there is a way of overcoming this, it would be probably better and definitely lighter solution comparing to the codes which require multiple conversions between bars/price and pixels and extra refresh when Y scale changes ... Additionally, it would be easier to check if the spiral's rings have been approached/crossed. That is how I see it, maybe I'm wrong ...

... and it would be a much better and simpler solution, than creating the whole chart from scratch - using Gfx functions :wink:

Regards

yes "bar / price" mode is best.

what would be possible I think is:

  1. calculate the spiral in pixel mode
  2. during the calculation you transform the spiral you calculated in pixels into "bar price" points and save this inside 2 static arrays.
  3. then at every refresh or scroll, zoom etc. you just read these bar/price coordinates from the static arrays, which you then plot in the chart

that should work I think. Will try this afternoon

yes that works. Here is a version. I use the Ctrl Leftmouse button from @Milosz

but now the Spiral is calculated and stored

// 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
// Contributors:
// Milosz Mazurkiewicz

Title = " Ctrl + Left Mouse Button --> START";
stepCount = Param( "stepCount", 512, 1, 1024, 1 );
incr = Param( "Increment (Radians)", 0.1, 0.01, 1, 0.01 );
buttonX = Param( "ButtonX", 10, 0, 1024, 1 );
buttonY = Param( "ButtonY", 20, 0, 1024, 1 );

SetBarsRequired( sbrAll, sbrall );
GfxSetZOrder( -5 );
GfxSetCoordsMode( 1 );

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

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

    return pxchartleft + ( ( bar - fvb ) / ( lvb - fvb + 1e-9 ) ) * pxchartwidth;
}

function ConvertValueToPixelY( Value )
{
    miny = Status( "axisminy" );
    maxy = Status( "axismaxy" );
    pxchartbottom = Status( "pxchartbottom" );
    pxchartheight = Status( "pxchartheight" );

    return pxchartbottom - floor( 0.5 + ( Value - miny ) * pxchartheight / ( maxy - miny + 1e-9 ) );
}

function ConvertPixelToBarX( PixX )
{
    lvb = Status( "lastvisiblebar" );
    fvb = Status( "firstvisiblebar" );
    pxchartleft = Status( "pxchartleft" );
    pxchartwidth = Status( "pxchartwidth" );

    return fvb + round( ( PixX / ( pxchartwidth + 1e-9 ) ) * ( lvb - fvb ) );
}

function ConvertPixelToValueY( PixY )
{
    miny = Status( "axisminy" );
    maxy = Status( "axismaxy" );
    pxchartbottom = Status( "pxchartbottom" );
    pxchartheight = Status( "pxchartheight" );

    return maxy - ( PixY / ( pxchartheight + 1e-9 ) ) * ( maxy - miny );
}

function calculateSpiral()
{
    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 );

    A = 0;
    XCoordinate = YCoordinate = 0;

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

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

            XCoordinate[step] = ConvertPixelToBarX( TP2X );
            YCoordinate[step] = ConvertPixelToValueY( TP2Y );

            TP1X = TP2X;
            TP1Y = TP2Y;
        }
    }

    // store arrays
    StaticVarSet( "XCoordinateClockwise", XCoordinate );
    StaticVarSet( "YCoordinateClockwise", YCoordinate );

    A = 0;
    XCoordinate = YCoordinate = 0;

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

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

            XCoordinate[step] = ConvertPixelToBarX( TP2X );
            YCoordinate[step] = ConvertPixelToValueY( TP2Y );

            TP1X = TP2X;
            TP1Y = TP2Y;
        }
    }

    // store arrays
    StaticVarSet( "XCoordinateCounterClockwise", XCoordinate );
    StaticVarSet( "YCoordinateCounterClockwise", YCoordinate );
}

bdy = 35;
wdt = 150;
GuiSetFont( "Lucida Console", 10 );
GuiButton( "Remove Spiral", 1, buttonX, buttonY, wdt, 30, 7 );
GuiToggle( StaticVarGetText( "direction" ), 2, buttonX, buttonY + bdy, wdt, 30, 7 );
GuiSetColors( 1, 2, 2, colorRed, colorBlack, colorRed, colorRed, ColorBlack, colorRed, colorRed, colorBlack, colorYellow );

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

if( id == 1 && event == 1 )
{
    Say( "Remove Spiral Pattern" );
    StaticVarSet( "LockSpiral", 0 );
    StaticVarSet( "x1", 0 );
    StaticVarSet( "y1", 0 );
    StaticVarSet( "x2", 0 );
    StaticVarSet( "y2", 0 );
    StaticVarSet( "XCoordinateClockwise", Null );
    StaticVarSet( "YCoordinateClockwise", Null );
    StaticVarSet( "XCoordinateCounterClockwise", Null );
    StaticVarSet( "YCoordinateCounterClockwise", Null );
}

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", x );
        StaticVarSet( "y1", y );
    }

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

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

    RequestMouseMoveRefresh();
}

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" );

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

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

if( GuiGetCheck( 2 ) )
{
    StaticVarSetText( "direction", "ClockWise" );
    XCoordinate = StaticVarGet( "XCoordinateClockwise" );
    YCoordinate = StaticVarGet( "YCoordinateClockWise" );
}
else
{
    StaticVarSetText( "direction", "Counter ClockWise" );
    XCoordinate = StaticVarGet( "XCoordinateCounterClockwise" );
    YCoordinate = StaticVarGet( "YCoordinateCounterClockWise" );
}

GfxSelectPen( colorDarkBlue, 2 );
GfxMoveTo( XCoordinate[0], YCoordinate[0] );

for( i = 0; i < Min( BarCount, stepCount ); i++ )
{
    GfxLineTo( XCoordinate[i], YCoordinate[i] );
}
5 Likes

How to use it in real trading to get signals...?

well, I wouldn't waste my time on it. Because even though this solution works .... now the shape of the spiral depends on the chart conditions when you store the spiral. For instance if you store a spiral and then zoom in the chart, all it good. But then when you remove that spiral and use the same center + start point it will give you a different spiral because the chart is in a zoomed state compared to the earlier stored spiral.

so the only spiral that is reliable is the one that uses the "bar price" mode in this code:

and now I am done with it :slight_smile:

Edward, thanks for the code. Now the position of the spiral rings doesn't change with respect to the price when someone scrolls the chart - and that is a good thing!

Sometimes spiral's rings become elongated and it may look a little strange :wink: but that is because of a different scaling depending on the zoom factor etc. That confirms, that one should be very careful when comparing candlesticks or looking for some patterns, because they may look different (much longer or shorter bars) depending on the zoom factor and some scaling issues...

I've noticed, that although you are using a special GuiButton to remove the spiral, it can still be removed by pressing (and holding) a Ctrl key and clicking middle mouse button (as it was in my code).

Now @Lennon can choose between three slightly different approaches to his subject: Yours, mine and @PanoS, when he will be working on his final version. Each of them has some pros and cons. If someone would like to add another one, he/she is welcomed :slight_smile:

Regards

yeah I wonder if this method has been thought through. If you look at the images in the book of Rober Fischer you would think he uses "pixel mode" to draw the spiral. But it looks like whatever route one chooses it is pretty much useless in my opinion, because either you can not scroll or zoom in and out. Or if you use my second code the shape of the spiral depends on for instance how many bars are visible when constructing the spiral. If you change the number of bars and use the same center + start point you get another spiral.

So pixel mode is useless. Only stable spiral is when you use this:

Title = " Ctrl + Left Mouse Button --> START";
stepCount = Param( "stepCount", 512, 1, 1024, 1 );
incr = Param( "Increment (Radians)", 0.1, 0.01, 1, 0.01 );
buttonX = Param( "ButtonX", 10, 0, 1024, 1 );
buttonY = Param( "ButtonY", 20, 0, 1024, 1 );

SetBarsRequired( sbrAll );
GfxSetZOrder( -5 );
GfxSetCoordsMode( 1 );

bdy = 35;
wdt = 150;
GuiSetFont( "Lucida Console", 10 );
GuiButton( "Remove Spiral", 1, buttonX, buttonY, wdt, 30, 7 );
GuiToggle( StaticVarGetText( "direction" ), 2, buttonX, buttonY + bdy, wdt, 30, 7 );
GuiSetColors( 1, 2, 2, colorRed, colorBlack, colorRed, colorRed, ColorBlack, colorRed, colorRed, colorBlack, colorYellow );

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

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

if( GuiGetCheck( 2 ) )
{
    StaticVarSetText( "direction", "ClockWise" );
	DIR = 1;
}
else
{
    StaticVarSetText( "direction", "Counter ClockWise" );
	DIR = -1;
}

bi = BarIndex();
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", x );
        StaticVarSet( "y1", y );
    }

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

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

    RequestMouseMoveRefresh();
}

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" );

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

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

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

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;
    }
}
1 Like

Ed, Milosz, Panos,
Heartiest Greetings for your kind efforts!

Ed, you are right! GfxSetCoordsMode( 1 ) brings instant stability but would ponder upon for sometime on what you said previously and the very next code posted in regards to it:

In pixel mode, it is extremely tough to compete with native functions (at least for me). The calculation of the spiral is precise in accordance to what is shown in Fischer's book.

Moreover, even after you stabilize the spiral, there remains the issue of visualizing it in future bars. So, this only becomes a tool to observe what happened and appreciate Fibonacci.

Having that said, tons of very useful lessons and techniques learned from this topic which could be used in developing other tools. Each one of the presented code shows a newer aspect and your high level of competence.

Meanwhile shall research further onto this.
Once again, Ed, Milosz, Panos ever grateful to you all and many many thanks for sharing your time and efforts :smile:

4 Likes

Santy,

Honestly, don't know yet! But you can read Fischer's book for more information.

In an attempt to figure-out how the in-built drawing tools cater to the angular scaling issues in a scenario wherein we attempt to draw pixels on a charts that have different units on its axes (Time on X and Price on Y), ironically found another Fibonacci tool called The Fibonacci Arc as the closest.

No offence but to my dismay, observed, the similar bar-arc (or curve) disparity upon zooming in/out:

Output%20Chart

The Users' guide as always explains everything:
https://www.amibroker.com/guide/drawtools.html

Fibonacci arc

This new drawing tool generates standard Fibonacci-arcs that are controlled by the trend line drawn with a dotted style. To see the properites of the arcs click on the controlling trend line.

Note that arc radius and central point are relative to the controlling trendline and because Fibonacci arcs must be circular regardless of screen size/resolution and zoom factor the position of the arcs may move in date/price domain.

You can't have the cake and it eat too.... you either keep the shape in pixel domain OR in price/bar domain. Not both.

Controlling trendline stays in place (in bar/price domain) when you zoom. But circular arc connecting the ends can't.

Those analysis "techniques" (Fib arcs/Gann/etc) were born when people were drawing charts on paper. They did not have "zoom" feature. The scaling was set once by defining certain bar:price ratio and kept fixed. They talked about "angles" and similar nonsense.

I wrote many times that speaking about angles and fixed shapes has very little meaning when X and Y axes do not use same units. Bar (time) and dollars (price) are NOT same units.

2 Likes