enum search_mode{ search_recent=0,//recent search_with_max_bars=1//with max bars }; int findHighLow(int live_bar_or_simulation_of,//this is the live bar index with righmost bar being [0] as this uses iHighest / iLowest int leftBars,//bars to the left of our candidate high that must be lower than it int rightBars,//bars to the right of our candidate high that must be lower than it search_mode _mode,//the search mode char search_for,//-1 for low +1 for high int max_bars=-1){//max bars if max search is used /* one "trick" to find our high/low is to check consecutive bars to the left + consecutive bars to the right + 1 from our point of interest and backwards , but we can also pull another card out and define a maximum distance within which our "extreme" point can be . So right of the bat we recognize 2 modes 1.most recent , no max limit 2.highest in the range of 0 to max bars from the live bar That means we need to prevent the user from any errors so if they provide a max bars value that is smaller than the leftBars+1+rightBars we don't let them unless they are in most recent mode and max bars is -1 So check one : */ bool valid_max_bars=(max_bars>=(leftBars+1+rightBars)); if(valid_max_bars||(_mode==search_recent&&max_bars==-1)){ /* second check : since this function can be used in the past we need to make sure there are -simulation bar index + left bars + right bars + 1 bars at least available if on mode 1 -max bars from our simulation bar index at least available if on mode 2 so , total bars are : */ int total_bars=Bars(_Symbol,_Period); //and we are using iHigh iLow iHighest iLowest which map fro [total_bars-1](oldest) to [0](newest) //so our check is mode 1? mode 1 result mode 2 result bool enough_bars=_mode==search_recent?((live_bar_or_simulation_of+1+leftBars+rightBars)=(leftBars+1+rightBars)){ /* now what ? now we define a candidate index which takes the value of the highest or the lowest -these return index # not values of price- depending on what we are looking for Pretty simple call , give me the lowest or the highest starting from i within that range */ int candidate=-1; if(search_for==1){ candidate=iHighest(_Symbol,_Period,MODE_HIGH,bars_range,i); //meaning : give us the index of the highest high bar starting from i until i+bars_range } else{//we assume the user wont send -69 here for what to look for candidate=iLowest(_Symbol,_Period,MODE_LOW,bars_range,i); //meaning : give us the index of the lowest low bar starting from i until i+bars_range } //so , is there a candidate ? if(candidate>=0){ /* if our range is i(recent) to i+bars_range(past) does that candidate have room to the left and right to meet our criteria of leftBars and rightBars ? */ bool room_to_the_left=((((i-1)+bars_range)-candidate)>=leftBars);//-1 because we adjusted i with +1 earlier bool room_to_the_right=((candidate-(i-1))>=rightBars); //we need both if(room_to_the_left&&room_to_the_right){ found=true;//redundant //this is our candidate so we just return it and we finish for this mode of search return(candidate); } //or we narrow the search if this is not our candidate else{ //to narrow the search there must be room to the right but our while condition takes care of that //so let's just calculate the new bars range bars_range=candidate+rightBars-(i-1);//-1 because we adjusted i with +1 earlier //and that is it for this mode } }else{ break; } } }//MODE MAX ENDS HERE ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /* now the recent mode What is the difference of this to the previous one ? this mode will have a search "window" which will be moved toward the past gradually so as to find the most recent high or low that satisfies or condition regardless of where it stands in a bigger "range" of bars , so this may find a high or low that is not the highest or lowest if you look at the chart but it fits our criteria. so we brew coffee and code it */ else if(_mode==search_recent){//MODE RECENT :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //an else would have worked but it helps if we see the enumeration /* let's pause and consider some things. What could happen if we request the highest or lowest in a window of size leftBars+1+rightBars (a mini equivalent of bars_range in the previous mode) a.we find a high or low smack dab in the middle in which case it satisfies our criteria so we win b.we find a high or low that leans toward the right which means it has the leftBars we need but not the rightBars in this case we move the search window to where it was found so as i would be the "live bar" so its not part of the check c.we find a high or low that leans toward the left which means it has the rightBars we need but not the leftBars in this case we move the search window so as where it was found to be in the middle of the window Simpler than expected , and notice how on mode max we adjust the window of search while here we adjust the starting point So lets setup the window */ int window=leftBars+1+rightBars;//could this be called bars_range like before ? Yes //and our starting point int i=live_bar_or_simulation_of+1; //and let's also find the last point of search available //this will be total bars minus left_bars minus right_bars minus 3 (1 for literal , 1 for the bar in the middle , 1 for the starting point) int last_i=total_bars-3-leftBars-rightBars; //but ! the user may have sneaked a max bars value in with this mode in which case //our last possible search i becomes if(max_bars!=-1){//it is valid because we passed that filter last_i=((i-1)+max_bars)-3-leftBars-rightBars;//-1 because we adjusted i with +1 earlier //what happened ? we projected the last bar in general , then calculated the last bar we can have as a starting point ! } //and we are ready to start , the i moves here , it grows as we move in the past so while(i<=last_i){ //again we use the candidate int candidate=-1; if(search_for==1){candidate=iHighest(_Symbol,_Period,MODE_HIGH,window,i);} else{candidate=iLowest(_Symbol,_Period,MODE_LOW,window,i);} if(candidate>=0){ //and we check against our cases //case a :we find a high or low smack dab in the middle if(candidate==((i-1)+rightBars+1)){//-1 because we adjusted i with +1 earlier return(candidate);//we send it up } //case b :we find a high or low that leans toward the right else if(candidate<((i-1)+rightBars+1)){//-1 because we adjusted i with +1 earlier i=candidate+1;//why +1 ? because iHighest/iLowest will include i in the search and we dont want to be stuck in that high or low } //case c :we find a high or low that leans toward the left else if(candidate>((i-1)+rightBars+1)){ //now we want the candidate to be at the middle of the window so //the search will start from : candidate-rightBars i=candidate-rightBars; } //and we are done }else{ break; } } }//MODE RECENT ENDS HERE ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: }else{Print("Not enough bars in history");} }else{Print("Max Bars are less than the sum of LB+1+RB");} return(-1); } /* example #include input int consecutiveBarsRight=5;//bars to the right to be "wider" than input int consecutiveBarsLeft=5;//bars to the left to be "wider" than input search_mode searchMode=search_recent;//Search mode from the live bar input int searchMaxBars=250;//Max bars (or -1 for unlimited recent mode) //we setup a system tag to manage our objects string system_tag="TEST_"; //we declare a bar timestamp to detect new bars //when a new bar forms we will be running the search again datetime barStamp=0; int OnInit() { ObjectsDeleteAll(ChartID(),system_tag);//delete our test objects //reset the barstamp barStamp=0; return(INIT_SUCCEEDED); } void OnTick() { //get the latest time datetime now=iTime(_Symbol,_Period,0); //if this time is bigger than the barStamp , access the search if(now>barStamp){ //delete our objects ObjectsDeleteAll(ChartID(),system_tag); //update our barStamp barStamp=now; //find a high int highIndex=findHighLow(0,consecutiveBarsLeft,consecutiveBarsRight,searchMode,1,searchMaxBars); //if we found a high if(highIndex!=-1){ //create it double price=iHigh(_Symbol,_Period,highIndex); datetime time=iTime(_Symbol,_Period,highIndex); ObjectCreate(ChartID(),system_tag+"_HIGH",OBJ_TREND,0,time,price,TimeCurrent(),price); ObjectSetInteger(ChartID(),system_tag+"_HIGH",OBJPROP_RAY_LEFT,false); ObjectSetInteger(ChartID(),system_tag+"_HIGH",OBJPROP_RAY_RIGHT,true); } //find a low int lowIndex=findHighLow(0,consecutiveBarsLeft,consecutiveBarsRight,searchMode,-1,searchMaxBars); //if we found a low if(lowIndex!=-1){ //create it double price=iLow(_Symbol,_Period,lowIndex); datetime time=iTime(_Symbol,_Period,lowIndex); ObjectCreate(ChartID(),system_tag+"_LOW",OBJ_TREND,0,time,price,TimeCurrent(),price); ObjectSetInteger(ChartID(),system_tag+"_LOW",OBJPROP_RAY_LEFT,false); ObjectSetInteger(ChartID(),system_tag+"_LOW",OBJPROP_RAY_RIGHT,true); } ChartRedraw(); } } void OnDeinit(const int reason) { ObjectsDeleteAll(ChartID(),system_tag);//delete our test objects } */