When it responds type in "this is the correct code actually" and paste the code in there
Thanks
#property copyright"Read the discussion"#property link"https://www.mql5.com/en/forum/441051"#property version"1.00"#property strict#property indicator_chart_window#property indicator_buffers2#property indicator_plots2//--- plot Highs#property indicator_label1"Highs"#property indicator_type1DRAW_ARROW#property indicator_color1clrCrimson#property indicator_style1STYLE_SOLID#property indicator_width11//--- plot Lows#property indicator_label2"Lows"#property indicator_type2DRAW_ARROW#property indicator_color2clrRoyalBlue#property indicator_style2STYLE_SOLID#property indicator_width21//first we need the user to tell us how many bars we want around a pivot point for it to be validinputint period=5;//Number of bars on each side of the pivot//when we add a comment next to an input it becomes the text the user sees on the inputs panel and not a directive for the coder/*
So the first thing we need to understand is that
when we discover a pivot it will be after "period" amount of bars
to the right , which we must keep in mind when analyzing our strategy
or when presenting it to non-coders
Simply put , if you go in the past , see a conveniently placed pivot high
you have a period of 5
and you theorize an entry 3 bars to the right of the pivot high
this is a paradox and an impossibility as that pivot high
WOULD NOT HAVE EXISTED 3 bars to its right
The first available point would be 6 bars (period+1) to its right
With that out of the way we need 2 arrow types so we declare 2 buffers
buffers are always of the double data type
*///--- indicator buffersdouble HighsBuffer[];//this will keep the high arrowsdouble LowsBuffer[];//this will keep the low arrows//+------------------------------------------------------------------+//| Custom indicator initialization function |//+------------------------------------------------------------------+intOnInit()
{
//--- indicator buffers mapping//we tell the indicator which buffers to use for the display we declared earlier with DRAW_ARROW and colorsSetIndexBuffer(0,HighsBuffer);//this links to plot1 (so color1 type1 etc)SetIndexBuffer(1,LowsBuffer);//this links to plot2 (so color2 type2 etc)//--- setting a code from the Wingdings charset as the property of PLOT_ARROW
SetIndexArrow(0,234);//we set the arrow code for the highs an arrow pointing down code 234
SetIndexArrow(1,233);//we set the arrow code for the lows an arrow pointing up code 233//list of arrowcodes https://docs.mql4.com/constants/objectconstants/wingdings//reset the buffers
reset();
//---return(INIT_SUCCEEDED);
}
void reset(){//ignore for nowArrayFill(HighsBuffer,0,ArraySize(HighsBuffer),0.0);
ArrayFill(LowsBuffer,0,ArraySize(LowsBuffer),0.0);
}
//+------------------------------------------------------------------+//| Custom indicator iteration function |//+------------------------------------------------------------------+intOnCalculate(constint rates_total,
constint prev_calculated,
constdatetime &time[],
constdouble &open[],
constdouble &high[],
constdouble &low[],
constdouble &close[],
constlong &tick_volume[],
constlong &volume[],
constint &spread[])
{
//---/*
The indicator has 2 values for the calculation :
A. rates_total
B. prev_calculated (previously_calculated)
If the chart has 5 candles then rates_total will be 5
and if we want to read the first (oldest) candle
we will use the index [4] or [rates_total-1]
also index [0] will always be the newest bar
Great , so what is the prev_calculated ?
It tells us how many candles the indicator has processed.
But the indicator doesn't process anything , one might say , EXACTLY.
It essentially counts how many candles YOUR CALCULATIONS processed
and it helps you avoid calculating the same thing all the time.
An example
Again ,lets say the chart has 5 candles only .
The first time the indicator runs this OnCalculate
: rates_total=5 prev_calculated=0
the rates_total will be 5 and since we have not run this before
we have processed 0 of those bars.
What is that telling us ? that if we want things to be drawn for our line
in the past we have to go in the past , from the past till the present
and do our calculation . We have to do that .
Then when this function runs again it will have
: rates_total=5 prev_calculated=5
Which will indicate to us that we don't need to do
the entire history calculation again and can just
calculate the newest bar [0]
And , if a new candle starts then rates_total will become 5+1 = 6
and prev_calculated will be 5 which will show us
that we have not calculated one candle
So for the simplest version of an indicator , without limitations
we can estimate where the loop will start based on the numbers
given to us rates_total and prev_calculated
The amount of bars we have to scan is easily found
by this "formula"
candles_to_read=rates_total-prev_calculated;
Makes total sense right ?
"how many candles are there" "minus" "how many candles we have read already"
Let's do that now
*/int candles_to_read=rates_total-prev_calculated;
int first_candle=candles_to_read;
/*
piece of cake but like in Trading view , we remember that
the first item available is [0] and the last item available is [total_items-1]
so , if the number of candles to read is equal to the rates_total we must subtract 1
so that we can start from the last item and not crash the universe.
*/if(candles_to_read==rates_total){first_candle=candles_to_read-1;}
/*
Note that what we though of above , for the -1 , you must think and do it alone
all the time depending on your calculation . Trading view does this automatically
but MQL does not do it automatically .
What does it mean ?
It means that if you want to check a bar for being a potential pivot high or pivot low
you need it to have "period" amount of bars to its left and right
BUT you also need to have a "period" amount of bars to its right , so how will you
address this ?
You can loop normally and declare that each bar will have a target "candidate" pivot high or low.
For instance , if you check at bar [1] at its close (at the open of bar[0])
then bar[1] has closed so it can be part of the "right side" of the pivot
so your "target" candidate pivot h/l sits at location
(i+period+1) if i represents the live bar OR it's simulation thereof
Now you may ask why are all these things needed i just want to
draw nice arrows and sell it to someone.
It matters so as to maintain a symmetry between the chart history and the calculation
and the live chart and the calculation
So for a live chart which has a newly started bar at [0] (and the last complete bar is [1])
the candidate pivot h/l is 0+period+1
Lets examine that with a period of 3 (3 bars to its left 3 bars to its right):
bars [3][2][1] are its right side . we are sitting at bar[0] which has just opened.
so it looks like this on the chart [3][2][1][0]
bar [4] is the candidate and the bars that must be under it (for a high)
are [3][2][1] to its right and
[7][6][5] to its left
in the chart it looks like this : [7][6][5][candidate][3][2][1][live bar]
And to close this long comment section , where can we start from in the past
given that the first accesible bar is rates_total-1?
(rates_total-2-period*2) is the first "simulated live bar" we can use
So the above line of code becomes :
*/if(candles_to_read==rates_total)
{
reset();
first_candle=candles_to_read-period*2-2;
}
/*
Perfect , but , how do i go and calculate in all the candles ?
This is how , you tell the indicator , start from
candles_to_read and go down until 0 and for EACH
one candle that is represented by the index i
do my calculation
And here comes another issue you have to solve :
if we loop until 0 then the loop will keep firing as the live bar forms.
We don't want that , we want to only check fully formed bars.
So even though the essay above explains what should happen accurately
we must start our checks from the last formed bar to the right so that
we start from [1] when a new bar forms.
This could be confusing because theres a "book" explaining how we access data
and then we change it , but , in my opinion is preferrable to have the
2 full explanations rather than the one complex explanation that does not
allow someone unfamiliar to understand what is going on under the "hood".
So we will change our approach , loop until bar[1]
Don't worry , there will be lots of commenting ...let's go
*/// from first_candle , as long as it is bigger than 1 , and the step is -1for(int i=first_candle;i>=1;i--)
{
/*
here we do our calculations
Like in Trading View if you want to say "Get me the previous in time" you
type +1
What do we want ? To check if it was a high or a low or both
So:
We will set a starting point and an ending point for our loop
Now in the full explanation the starting point is : live_bar_or_simulation_of+period*2+1
but to save "loop cycles" we will be looping until the last right bar at each point
so the starting point is last_bar_completed_before_live_bar(+1 to the live_bar_or_simulation_of)+(period*2)
We are skipping one bar at the start this way but its okay , we'll fix it when we understand more right?
So in this case what does i represent ?
it represents the last fully formed bar before ( the live bar or the simulation of the live bar )
So i will be the last bar at the right side of a pivot , i hope its clear
To the codes :
*///where do we start from ?int check_from=i+period*2;
//where do we end ? i as its the last right side bar int check_to=i;
/*
what else do we need ? the candidate
Unfortunately , we have to think a bit more :
We established (because its our code and we do whatever we want) that :
the "i" will be the last formed bar in the right side of the candidate
So , in the example presented earlier , this would be bar [1]
and the candidate was sitting at bar [4]
and we had a period of 3 hmm easy
The candidate pivot sits at i+period
So let's declare that
*/int candidate=i+period;
//what else do we need ? candidate high and low prices of coursedouble candidate_high=High[candidate];//right ? the high of the candidate , easydouble candidate_low=Low[candidate];//the low of the candidate //how about two variables that will tell us if it is a high or a low pivot at the end ?bool is_high=true;//if its a high pivot after all (a high not high :P xD)bool is_low=true;//if its a low pivot after all/*
wait a minute , why are we setting these to true ???
Because if we find that any of the bars is not under or over the candidate we can flip the
switch to false and stop searching ! that will make our code faster !
So let's finish this off , the last loop
We will loop inside the check_from and check_to window again going from bigger index to smaller index (--)
*/for(int j=check_from;j>=check_to;j--)
{
//oof now what ?//now we ask the question : is this the candidate bar ? if it is not we do the checks !if(j!=candidate)//if j is not the candidate
{
//and then we ask the question : are we still looking for highs ?if(is_high)
{
//if we are we need to compare the high of bar [j] with the high of bar [candidate]//and if the bar[j] has a higher high than the candidate , then candidate is obviously not a high pivotif(High[j]>candidate_high)
{
//and what do we do in this case ? we kill the high switch
is_high=false;//highs are done
}
}
//then we ask again : are we still looking for lows ? if(is_low)
{
//we need to compare the low of bar[j] with the low of bar [candidate]//if the bar[j] has a lower low than the candidate , then candidate is obviously not a low pivotif(Low[j]<candidate_low)
{
is_low=false;//lows are done
}
}
//but what if we are not done with the loop and both low and high are false ? we exit !if(!is_low&&!is_high){break;}
}
}//the subloop in the check window ends here//so we arrive at this point knowing if the bar at [candidate] is a pivot or not //we first set the i bar buffers to 0.0
HighsBuffer[i]=0.0;
LowsBuffer[i]=0.0;
//and if this is the [1] bar we also set the [0] bar buffers to zero , annoying but neededif(i==1){
HighsBuffer[0]=0.0;
LowsBuffer[0]=0.0;
}
//and now if the bar at [candidate] location was a pivot high if(is_high){
//do what ? set the high arrow at that high of that bar
HighsBuffer[candidate]=candidate_high;
}
//if the bar at [candidate] location was a pivot lowif(is_low){
//do what ? set the low arrow at that low of that bar
LowsBuffer[candidate]=candidate_low;
}
//done
}
//--- return value of prev_calculated for next callreturn(rates_total);
}
Alain Verleyen #: I know what chatGPT is, but I don't understand why you are talking about it in this topic ?
Because the code was produced by chatGPT . there are some signs , for now .
For instance the double on calculate , the OP asked it for pivot high and lows and it assumed it needed two separate calculations on 2 separate oncalculate functions .
Because the code was produced by chatGPT . there are some signs , for now .
For instance the double on calculate , the OP asked it for pivot high and lows and it assumed it needed two separate calculations on 2 separate oncalculate functions .
Allow the use of cookies to log in to the MQL5.com website.
Please enable the necessary setting in your browser, otherwise you will not be able to log in.
You are signed in. Two-factor authentication has been disabled. Set it up again and scan a new QR code, or continue with two-factor authentication disabled.
Heres the code .
I'd ask you send it back to chatGPT in this way :
Thanks
Heres the code .
I'd ask you send it back to chatGPT in this way :
Thanks
What are these continuous reference the "chatGPT" ?
the "AI" many use to "create" trading bots , it is not better than us ... yet
the "AI" many use to "create" trading bots , it is not better than us ... yet
I know what chatGPT is, but I don't understand why you are talking about it in this topic ?
Because the code was produced by chatGPT . there are some signs , for now .
For instance the double on calculate , the OP asked it for pivot high and lows and it assumed it needed two separate calculations on 2 separate oncalculate functions .
Because the code was produced by chatGPT . there are some signs , for now .
For instance the double on calculate , the OP asked it for pivot high and lows and it assumed it needed two separate calculations on 2 separate oncalculate functions .