XShift is not pushing the shape : in Plotshape()

I defined a condition which consists of 2 candles, both candles should be closed for consideration.
So I am using 3rd candle as my reference candle and check previous 2 candles. If criteria is met, I plotshape in the candle. Currently shape is plotted in 3rd candle, I want the shape to be plotted in 1st candle.
Here is the syntax:

pk1=Ref(H,-1)<Ref(H,-2) AND Ref(L,-1)>Ref(L,-2) AND Hour()==9 AND Minute()<52;
PlotShapes(IIf(pk1, shapeUpArrow, shapeNone),colorWhite, 0,(h+l)/2,XShift=-2);

You missed yoffset argument of PlotShapes.

PlotShapes(IIf(pk1, shapeUpArrow, shapeNone),colorWhite, 0,(h+l)/2,yoffset=-12,XShift=-2);
1 Like

Thanks for your input.
I thought yoffset is used to control placement of the shape in Y axis.
The helpguide says
"offset - (or distance) parameter (by default -12 ), Offset is expressed in SCREEN pixels. Negative offsets shift symbols down, positive offsets shift symbol up. To place the shape exactly at ypostion, specify 0 as offset ".
But your suggestion worked.


you already got the correct solution, but your original code offers me the opportunity to make some remarks about one particular "feature" of the AFL that I find a bit confusing, hoping someone will explain to me why it exists.
(Skip to the question near the end of this post if you are in a hurry!)

As you already discovered, in your code the value -2 was assigned to the "offset", that is the value that is used in combination with the yposition to calculate the exact vertical position where to place the shape.

But it is interesting to note that your code also included a "XShift=" before a numeric value, but this parameter "name" was simply ignored!.

So, de-facto you did not provide any value for XShift, which by default is zero, and consequently, your shapes were NOT visually shifted left or right as you hoped.

This means that AmiBroker built-in functions DO NOT use functions arguments assigning their values according to the "name" (or "keyword") that you may write in your code.

Hence, all the arguments to the AmiBroker built-in functions are positional arguments (the order of parameters in a function call is necessarily fixed), but some of them, the ones followed by an equal sign and an expression that gives its default value, are optional.

This behavior is different from many other programming languages where some parameters ("keyword arguments" in Python) could be passed in any order when you specify the correct name/keyword.
See Named parameter.

Anyway, I find that optional arguments are a useful AFL language feature; if not used when calling a function they are given the default values.
This produces simpler code, hiding advanced but not-often-used functionality; see Plot() syntax for a ubiquitous example: only the first three parameters are mandatory.

But unfortunately, we must be careful when we need to specify a non-default value to one argument that is positioned after some other optional ones: we cannot omit any argument preceding the one to which we want to assign a specific value. (the function documentation indicates us what "default" values to use).

So what is the purpose of parameters "naming" in AFL?

While the implementation of "keyword arguments" may be a new feature to evaluate for a future version of the AFL, I wonder why, at present, it is possible to use any "parameter names" in the function calls:
Plot(values=C, caption="Close", hue=colorDefault, charttype=styleCandle); 

(Check the above code line and you'll see that you can use names/keywords also for NON-optional parameters, without receiving any syntax error/warning).

I see that they could be used in the debugger where you can inspect the variables content moving the mouse over them (when you are in a breakpoint) or drag&drop them in the name column in the "Watch" window.

But I wonder if there are any more useful usages I'm not aware of?
Any comment and clarification are welcome.

I also hope that @Tomasz could provide more details and correct anything that I got wrong and/or point me to any formal documentation to correct any false assumptions/misunderstanding.


I am not quite sure what is the difficulty...


Plot(values=C, caption="Close", hue=colorDefault, charttype=styleCandle); 

Is just the same as if you would write this one:

values = C;
caption = "Close";
hue = colorDefault;
charttype = styleCandle;

Plot(values, caption, hue, charttype); 

But obviously the first one requires less lines of code.

Now of course you could use some other variable name...

price = C;
titlename = "Close";
color = colorDefault;
style = styleCandle;

Plot(price, titlename , color, style); 

And of course could write it in one line again

Plot(price =C, titlename ="Close", color =colorDefault, style =styleCandle); 

Now what's the purpose of argument naming (you asked) or what's advantage of argument naming...

Well, you simply save time if multiple functions always shall use the same argument values
-> less code maintenance effort.

Let's keep staying at your example plot line (it could be any other function case!)...

Let's suppose some fictional user just wants to have different color for each plot but wants to keep every other argument being the same one (even if modifying).

Plot(values=C, caption="Close", colorDefault, charttype=styleLine); 

// all subsequent plot lines always use same argument (except for color)
Plot(values, caption, colorRed, charttype); 
Plot(values, caption, colorGreen, charttype); 
Plot(values, caption, colorBlue, charttype); 

As you can see the advantage of above one is...

If for example he wants to do a change from Close to High array being plotted then he/she only has to modify one time in first plot line.

Plot(values=H, caption="High", colorDefault, charttype=styleLine); 

// all subsequent plot lines always use same argument (except for color)
Plot(values, caption, colorRed, charttype); 
Plot(values, caption, colorGreen, charttype); 
Plot(values, caption, colorBlue, charttype); 

@fxshrat, thanks a lot!

The "named parameters" (callable in any order) feature of other programming languages completely fooled me on this!

No problem...
The above example of changing array in first Plot line only was a bit useless as all plots would be just overlaid on each other but suppose you want to change style for all plots via single variable then it makes more sense.

Plot(var1, "var1", colorDefault, charttype=styleLine | styleOwnScale); 
Plot(var2, "var2", colorRed, charttype); 
Plot(var3, "var3", colorGreen, charttype); 

It was just about argument naming in general (for any function).

Here is another example of argument naming (well, yeah... plot again).
You may leave Plot function alone but only change the array and array names to be plotted.

//INPUTS START #######################
captionstr = "Close,MACD,RSI,LastClose";

arr1 = Close;
arr2 = MACD();
arr3 = RSI();
arr4 = LastValue(Close);
// arr5 to arr10...
//INPUTS END #########################

style = styleLine | styleOwnScale;
startcol = ParamColor( "Start color of Plot", colorTeal );

for( i = 1; i <= 10; i++ ) {
	vg = VarGet("arr" + i);
	if( typeof(vg) == "array" OR typeof(vg) == "number" )
		Plot( vg, StrExtract( captionstr, i-1, ','), startcol + ( ( 2 * i ) % 15 ), style );

@fxshrat, so it is clear that these are NOT AT ALL "named parameters" (as defined in other programming languages) but just standard assignments of a value to a variable, that, as per your examples, can be reused later in the code (i.e., here the = simply is the assignment operator).

So, if one day, AmiBroker will ever consider adding "named arguments" it will need to use a different "operator"; maybe the colon ":" (that seems unused now) in a similar way to CSS syntax.

But probably at present, this not very useful new feature since many built-in functions on average have just 2/3 arguments. Could make more sense for some complex user-defined functions.

Thanks again for your detailed guidance.

--- Thread offtopic ---

This is kind of possible already in custom functions (AFL or DLL function) - but still at fix argument position.

See below custom Matrix function example with 3rd argument requiring string name. That 3rd argument decides what is the final output of MxFind function.

So what does the example function's 3rd argument do?

You have a source matrix "x".

Now (for example) you want to find all elements of matrix "x" being larger than 2 but being smaller than 7 (see lines 209 & 210). So in third argument you insert "[elements]". And so it returns elements 4, 5, 3, 6 of matrix "x" as column vector (since those are larger than 2 but smaller than 7).

On the other hand if user wants to know the row and column indexes of where condition is true in source matrix "x" then third argument would require "[rows, cols]" to be inserted there. Result will be a two columns matrix (1st column outputting row indexes, 2nd column outputting column indexes of elements being larger than 2 but smaller than 7, for example { 1, 0 } of 1st row means that 4 is located at x[1][0] etc )