Help with IIF function in a loop. Horizontal lines with labels. How to check if the code is efficient?

@JEETU because (in my opinion) your code seems too complicated for what it does, you can analyse my example code. It is four times shorter than yours and very light (just one static variable, no additional refreshes of any kind etc.) How to operate it:

  1. Ctrl + Left Mouse Button --> Select spots
  2. Ctrl + Middle Mouse Button --> Clear selected spots
  3. Ctrl + Middle Mouse button on the right axis (Y/Price) area --> Delete all spots

Lines2

_SECTION_BEGIN("Horizontal Lines - Mouse click");

/// @link https://forum.amibroker.com/t/help-with-iif-function-in-a-loop-how-to-check-if-the-code-is-efficient/7187/7
// By --- Miłosz Mazurkiewicz ---

Title = " Ctrl + Left Mouse Button --> Select spots    |    Ctrl + Middle Mouse Button --> Clear selected spots    |   Ctrl + Middle Mouse button on the right axis area --> Delete all spots";
LookBackPeriod = 500;
SetBarsRequired( LookBackPeriod );
IdSet = Name() + Interval() + GetChartID();
ConditionArray = Nz( StaticVarGet( "ConditionArray" + IdSet ) );
bi = BarIndex();

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

if( CtrlPressed )

{
    x = LastValue( ValueWhen( x == DateTime(), bi ) );
    
    if( MouseButtonPressed & 8 )  ConditionArray[x] = 1; // LMB --> Select spots
    if( MouseButtonPressed & 4 ) ConditionArray[x] = 0; // MMB --> Delete selected spots
    if( MouseButtonPressed & 4 AND x == 0 AND IsEmpty( y ) ) ConditionArray = 0; // MMB on the right axis area --> Delete all spots
    StaticVarSet( "ConditionArray" + IdSet, ConditionArray, persist = False );
}

fvbi = Status( "firstvisiblebarindex" ); lvbi = Status( "lastvisiblebarindex" );
pxchr = Status( "pxchartright" );

GfxSetZOrder( -1 );
GfxSelectFont( "Arial Narrow", 9, 700, False );

Start = Max( 0, fvbi - LookBackPeriod ); End = Min( lvbi, BarCount - 1 );

for( i = Start; i <= End ; i++ )
{
    if( ConditionArray[i] )
    {

        if( C[i] > O[i] ) Color = colorGreen;
        else if( C[i] < O[i] ) Color = colorRed;
            else Color = colorBlue;

        Spot = C[i];
        GfxSetCoordsMode( 1 ); GfxSelectPen( Color, 2);
        GfxSelectSolidBrush( Color );
        GfxCircle( i, Spot, -5 );
        GfxMoveTo( i, Spot ); GfxLineTo( lvbi , Spot );
        GfxSetCoordsMode( 2 );
        GfxSetTextColor( Color );
        GfxTextOut( "" + Spot, pxchr + 3, Spot );
    }
}

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

_SECTION_END();

I used some ideas from my two prior posts: Post 1 and Post 2

You need to modify it according to your needs. The way in which the lines are colored is just an example. You may use Ctrl + mouse bottons clicks approach (it might require some practice) or if you don't like it, get back to Gui Buttons... Change StaticVarSet persist parameter to True or "1" if you want to retrieve the lines after AmiBroker's restart.

My objective was just to show another, simpler approach and some ideas in general so don't treat it as a finished solution.

15 Likes

Samething using only ctrl + left mouse

/// @link https://forum.amibroker.com/t/help-with-iif-function-in-a-loop-how-to-check-if-the-code-is-efficient/7187/7
// By --- Milosz Mazurkiewicz ---
// minor changes by Anderson Wilson

Title = " Ctrl + Left Mouse Button on chart --> Select/Clear selected spots    |   Ctrl + Left Mouse Button on the right axis area --> Delete all spots";
LookBackPeriod = 500;
SetBarsRequired( LookBackPeriod );
IdSet = Name() + Interval() + GetChartID();
ConditionArray = Nz( StaticVarGet( "ConditionArray" + IdSet ) );
bi = BarIndex();

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

if( CtrlPressed )
{
    x = LastValue( ValueWhen( x == DateTime(), bi ) );
    
    if( MouseButtonPressed & 8 )  // LMB --> Select spots
	{
		if( x == 0 AND IsEmpty( y ) )
			ConditionArray = 0;
		if( ConditionArray[x] )
			ConditionArray[x] = 0;
		else
			ConditionArray[x] = 1;
		StaticVarSet( "ConditionArray" + IdSet, ConditionArray, persist = False );
	}
}

fvbi = Status( "firstvisiblebarindex" );
lvbi = Status( "lastvisiblebarindex" );
pxchr = Status( "pxchartright" );

GfxSetZOrder( -1 );
GfxSelectFont( "Arial Narrow", 9, 700, False );

Start = Max( 0, fvbi - LookBackPeriod );
End = Min( lvbi, BarCount - 1 );

for( i = Start; i <= End ; i++ )
{
    if( ConditionArray[i] )
    {

        if( C[i] > O[i] ) Color = colorGreen;
        else
            if( C[i] < O[i] ) Color = colorRed;
            else Color = colorBlue;

        Spot = C[i];
        GfxSetCoordsMode( 1 );
        GfxSelectPen( Color, 2 );
        GfxSelectSolidBrush( Color );
        GfxCircle( i, Spot, -5 );
        GfxMoveTo( i, Spot );
        GfxLineTo( lvbi , Spot );
        GfxSetCoordsMode( 2 );
        GfxSetTextColor( Color );
        GfxTextOut( "" + Spot, pxchr + 3, Spot );
    }
}

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

6 Likes

Yes, of course I agree with you @awilson. I don't know why I have decided to additionally use Middle Mouse Button in this case :thinking: Using only Left mouse button is a simpler and better choice...

Thanks :slight_smile:

4 Likes

missed else if , corrected

/// @link https://forum.amibroker.com/t/help-with-iif-function-in-a-loop-how-to-check-if-the-code-is-efficient/7187/7
// By --- Milosz Mazurkiewicz ---
// minor changes by Anderson Wilson

Title = " Ctrl + Left Mouse Button on chart --> Select/Clear selected spots    |   Ctrl + Left Mouse Button on the right axis area --> Delete all spots";
LookBackPeriod = 500;
SetBarsRequired( LookBackPeriod );
IdSet = Name() + Interval() + GetChartID();
ConditionArray = Nz( StaticVarGet( "ConditionArray" + IdSet ) );
bi = BarIndex();

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

if( CtrlPressed )
{
    x = LastValue( ValueWhen( x == DateTime(), bi ) );
    
    if( MouseButtonPressed & 8 )  // LMB --> Select spots
	{
		if( x == 0 AND IsEmpty( y ) )
			ConditionArray = 0;
		else if( ConditionArray[x] )
			ConditionArray[x] = 0;
		else
			ConditionArray[x] = 1;
		StaticVarSet( "ConditionArray" + IdSet, ConditionArray, persist = False );
	}
}

fvbi = Status( "firstvisiblebarindex" );
lvbi = Status( "lastvisiblebarindex" );
pxchr = Status( "pxchartright" );

GfxSetZOrder( -1 );
GfxSelectFont( "Arial Narrow", 9, 700, False );

Start = Max( 0, fvbi - LookBackPeriod );
End = Min( lvbi, BarCount - 1 );

for( i = Start; i <= End ; i++ )
{
    if( ConditionArray[i] )
    {

        if( C[i] > O[i] ) Color = colorGreen;
        else
            if( C[i] < O[i] ) Color = colorRed;
            else Color = colorBlue;

        Spot = C[i];
        GfxSetCoordsMode( 1 );
        GfxSelectPen( Color, 2 );
        GfxSelectSolidBrush( Color );
        GfxCircle( i, Spot, -5 );
        GfxMoveTo( i, Spot );
        GfxLineTo( lvbi , Spot );
        GfxSetCoordsMode( 2 );
        GfxSetTextColor( Color );
        GfxTextOut( "" + Spot, pxchr + 3, Spot );
    }
}

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


6 Likes

Hi,

Trying to get back home! Can not because the road is blocked!

Looks like the whole mountain has come down due to rain, the locals say that it is like this in many places!
So an extended holiday, but that is another topic.

YOU GUYS ARE GREAT OR WHAT !!! So much to learn, for the time I am just going to click "like this post"
for all , more when I am back home !!

2 Likes

Hi,

Thanks a lot for your guidance.
It is another step in my learning curve - will go though the steps you have mentioned and hopefully progress
to writing more efficient codes.

Trying to learn and with no programme writing experience a bit of a stuggle, slowly will get there.

Hi,

Have copied your code and tried it - no clue as to the AFL used to effectively use only the left button -
very interesting - will have to try and understand the method of coding.
Thanks a lot for sharing.

Hello
Jeetu in the afl that milosz and awilson provide you must use it by pressing the control CTRL button in the keyboard and then left mouse click.

I know for your final project you need about 20 buttons, as you have 20 different cases.
For ALL in ONE afl you cannot use only the CTRL + left click, but also you need toggle buttons to control every individual case differently. Also without buttons you are going to lost, if was me I will not remember what job of witch X keyboard key + left click is doing

I really agree with Milosz that is nice to keep one thread to ask your question as it is about “about clicking” -> store a value to staticvar or matrix -> do the calculation and print the results to the chart
This will be nice thread also for new users without any coding background to read and learn.

@Milosz and @awilson I really enjoy your team work.... I may join on this.
And as I am talking for team work I can see one line of the code that I don’t understand why you did not use SelectedValue(bi) ?

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

have a nice day

1 Like

hi
I just comment the lines that i don’t think we need

if( CtrlPressed )
{
   // x = LastValue( ValueWhen( x == DateTime(), bi ) );
    x = SelectedValue(bi);
    
    if( MouseButtonPressed & 8 )  // LMB --> Select spots
	{
		//if( x == 0 AND IsEmpty( y ) )
		//	ConditionArray = 0; 
		//else 
		
		if( ConditionArray[x] )
			ConditionArray[x] = 0;  
		else
			ConditionArray[x] = 1; 
		StaticVarSet( "ConditionArray" + IdSet, ConditionArray, persist = False );
	}
}
1 Like

@Panos, did you check your version carefully? It doesn't seem to be working properly for two reasons:

  1. You can not remove all lines at once clicking on the right axis area
  2. Using SelectedValue() is a less robust solution comparing to GetCursorXPosition() because it doesn't work as expected when the line selector disappears (which sometimes happen - when you for example click twice, click between bars etc.) GetCursorXPosition() works allways as expected. It returns x coordinates either in screen pixels or in DateTime(). For this reason I have used this line:
x = LastValue( ValueWhen( x == DateTime(), bi ) );

I will get back to this tomorrow, because currently it is 1:45 a.m. in Poland ...

3 Likes

Hi,

This reply is addressed to all, apart from me = who is only asking questions !!!
Great to see the masters discussing a topic, newbies like me will definitely learn from your discussions.

Now to the point made by @Milosz and @Tomasz w.r.t. the in-efficiency of the AFL code , I will definitely
give it the due attention. But for now I am more inclined to go ahead and work towards my goal, unless the
AFL code I use really - VISUALLY - slows the execution.

The point made by @PanoS is correct, thinking about the "range" selections required - I am inclined to go with
with a total of 10 ranges (3=internal retracements(6 clicks) , 3=external retracements(6 clicks) / extensions and 4=projections ( 4 x 3 =12clicks) ; total = 24clicks !!

Really mind boggling for me .

Did some work with the code from @beppe, basically just changed the visual look, added 23.6% for internal retracement .

List of TO DO TASKS---
/[1]. TO DO = I SHOULD BE ABLE TO DECIDE WHAT ANY SELECTED RANGE (2 BARS=INTRET OR EXRET // 3 BARS = PROJECTION)
PLOTS . AT PRESENT WHAT I SELECT WITH PARAMTOGGLE IS PLOTTED FOR ALL RANGE SELECTIONS,ie. IF I SELECT
"intret" WITH PARAMTOGGLE AND USE 3 RANGES THEN ALL 3 WILL PLOT ONLY "inret". I SHOULD BE ABLE TO DECIDE
WHAT PLOTS (intret / exret or projection) FOR EACH RANGE . use GUI or AFL? INCREASE RANGE SELECTION FROM PRESENT "3"
/

/* [2]. TO DO = DELETE ANY SELECTED RANGE (intret, exret or projection). use GUI or AFL?. */

/* [3]. TO DO = REQUIRE "ONLY" CLEAR ALL STUDIES FROM THIS CHART SO REMOVE
"CLEARALL" paramToggle*/

/* 4]. TO DO = START WORKING WITH TIME / BARS --- X-AXIS === HOW TO PLOT AT DIFFERENT "Y-AXIS LEVELS,
FOR Lo to Hi PLOT ABOVE PRICE & FOR Hi to Lo PLOT BELOW PRICE, START WITH 3 STUDIES (3DIFFERENT LEVELS)
FOR EACH */
2 screen capture to clarify point [1] ;

image

image

Once again, thanks to all of you!

@JEETU this is actually my code (modified by @awilson) which I have prepared to show you that the results you were trying to accomplish can be achieved in a simple and effective way. @awilson has very logically noticed (:+1:), that in this case there is no need to use additionally middle mouse button to remove the lines and modified the lines which are responsible for handling mouse clicks. When preparing this code I simply used some parts from my other code where I was also using the middle mouse button and didn't change it - although I should have - because it was very logical. Simplest solutions are usually the best.

The whole idea behind this code is very simple. It uses just one Static variable --> ConditionArray to identify which bars have been selected via Ctrl + LMB click. Initially all array ellements equals 0 (zero). When you select some bar, array element at this place changes it's value to 1 (one). If you click one more time, it changes it's value back to zero.

In the next part of the code, it iterates through all visible bars (plus the lookback period to allow displaying all the lines when the chart is zoomed in or scrolled to the right). If it finds an array element which equals 1 (one) it plots the line and the label. As you can see, the idea is really simple and the code could be even more simplified.

I understand that, but believe me - if you had based your solution on your initial code and started adding next functionalities, you would have shortly hit the wall and start asking on the forum: why my code is getting so slow (or even worse: why Amibroker is so slow :wink: ) or why am I getting strange errors every few minutes? Your code performs a really simple task and does it in a very inefficient way. I pay special attention to the efficiency of my codes and I couldn't accept, that in one code you used SetBarsRequired(sbrall,sbrall) + RequestTimedRefresh(0.1) + RequestMouseMoveRefresh() without a single reason to do so and additionally you were calling even hundreds of thousands Static Variables (when one is enough)!

Whether you are going to use some ideas from this code, or not - is up to you. As I said, I just wanted to show an alternative and much more efficient approach to save you from troubles.

Regards.

3 Likes

Panagioti, I'm not a specialist in Elliott, Fibo, different kind of retracements etc. - I don't use them that often, besides I won't have much free time in the upcoming days, so don't count me in. I'm afraid, I'm not going to embark on this project ...

Best :slight_smile:

1 Like

Hello milosz
You said that we can not remove all lines at once clicking on the right axis area

Yes you right. I really miss this point.
Thanks for the tip.
I bet is your turn to go for holiday's. I wish you all the best

3 Likes

Hi,

Started studying this topic "_TRACE () and _TRACEF ()" - thanks a lot for the link to various examples.

Hi,

Yes it is your code and modified by @awilson, no disrespect meant, I just replied to the last post and it happened to be of @awilson.

Your guidance on coding is being followed - albeit slowly - as I still do not understand a lot of AFL functions.

Thanks for your advice - my only request is - "have a little patience with "newbies" :smile:

Hi,

Started trying to use these functions, still going over my head !

Thanks a lot.

@JEETU, as far as I understand reading your previous posts, you are on the journey to learn AFL and "programming" at the same time.

While your individual situation and previous learning experiences may be very different from mine, I still hope that you'll consider the following notes to speed up this process.

First of all, let's repeat an old adage: "Start Small (and be patient...)

For this reason, I suggest you to spend a lot of time studying the documentation of each new function that you are considering to include in your formulas.

I still learn a lot running each of the code samples that are available in the "Example" section of each function documentation page.

Before incorporating a new function in a more complicated formula, I frequently spend some time testing all the code examples provided, trying to fully understand what they are doing (this is especially true for the low-level graphics and mouse/Windows controls ones).

To keep it simple, I test each example in its own new formula and I experiment with all the different parameters (also overriding the default arguments).

I encourage you to do the same. Then try to rewrite your own short code sample using what you learned.

Once you have grasped the purpose of each function, take some time to look at more advanced examples (full-fledged formulas) to see how to combine them in different contexts. And again, take your time!

And, as you are already doing, learn asap how to debug your code.
We all make mistakes, so it is important to learn how to spot them: in addition to _TRACE, exploration, there is also the debugger that allows you to single-step thru your formula and set breakpoints to watch the content of your variables.

The above practices may help you to take care of the "AFL" part, but IMO you should also strive to improve your overall programming skill and knowledge.

I already provided some books suggestions here (or explore this list of free programming languages titles), but today there are also multiple handy online resources to learn "How to program" (many are free). Why not take a look?

Happy learning!

5 Likes

Hi,

I have probably wrongly stated my intentions , so my clarification.

I only require to learn AFL and writing code (formula) in AFL - the reference to programming is only in this context .

I had read the contents at " Is it possible to run Python on Amibroker?"
earlier, did so again after your above post.

To me it is very-------very clear that I am to stay on the path of AFL and only AFL. For this the tools I have are
available at "amibroker.com" and "https://forum.amibroker.com/".

I tried to study AFL much before I purchased Amibroker, using the free download version,
printed out the AFL functions, (could not go to the library - members only), there are numerous AFL formulas
available on the net and was using them to gain some understanding!

However in many cases what is mentioned in the AFL function reference and how the function is used
practically is dfferent - hence the confusion exists. It took a while to get the hang of using "StrToNum( string )"
and "NumToStr". But staticvars are taking longer! In the AFL function the var / varname is always shown in
" ", however in practical usage (example only --StaticVarSet( YStaticName + bis, y ); there is no use of " ".
Again from the AFL function reference - StaticVarGet( ''varname', align = True' ) == " ' , '.

So these are some of the confusions I am working with.

Another thing I have realized is that I am trying to run without being first able to stand up !
So I am spending more time and effort on basics, advise well taken from this forum.

I started this thread with "IIF function in a loop", and it has become much more, so much knowledge has been
passed on, really humbles me.

Respecting the observation made by @Milosz in another thread I am exiting from this thread.

I must express my thanks to all who have participated and provided gems from their experience.

Since my goal is to use mouse clicks for fib price and time studies I will continue on this thread --

https://forum.amibroker.com/t/it-is-possible-but-it-is-not-that-easy/7129.

Thanks a ton, many tons :sunny:

Hi,

A few days back I was studying your code trying to follow your advise of "simple" codes.

Noticed the use of "if( MouseButtonPressed & 8 )" (8 = * if window just received mouse click (version 5.06 and higher only), instead of 8 we should use "1".

I think your code works because of "4" -middle button" being used for deletion.

The code from @awilson also uses " if( MouseButtonPressed & 8 )" -- when plotted on a chart - one can use any button, then for left button I tried "1" and for middle button "4", both work, with "8" either button works!

After plotting it took me quite some time to understand what was happening, the AFL reference
helped in understanding the use of the code numbers.

Hence my current conclusion is that if I want to use the left button I have to use "1".

I will be grateful for your advice.

Thanks a lot