How can I write mql4 code (EA) that marks the listed candle patterns with rectangles
Asked Answered
M

2

14

I am quite new to writing mql4 code and would be grateful if I could get some help drawing rectangles when the following candlestick patterns occur:

FIG1:

image taken from https://imgur.com/a/fRoPzsm

Run code snippet

<blockquote class="imgur-embed-pub" lang="en" data-id="a/fRoPzsm"><a href="//imgur.com/a/fRoPzsm">Demand Zone 1</a></blockquote><script async src="//s.imgur.com/min/embed.js" charset="utf-8"></script>

FIG2:

image taken from https://imgur.com/a/4E8KE1R

Run code snippet

<blockquote class="imgur-embed-pub" lang="en" data-id="a/4E8KE1R" data-context="false"><a href="//imgur.com/a/4E8KE1R">Demand Zone 2</a></blockquote><script async src="//s.imgur.com/min/embed.js" charset="utf-8"></script>

and

FIG3:

image taken from https://imgur.com/a/h6D6o6R

Run code snippet

<blockquote class="imgur-embed-pub" lang="en" data-id="a/h6D6o6R"><a href="//imgur.com/a/h6D6o6R">Hidden Demand Zone</a></blockquote><script async src="//s.imgur.com/min/embed.js" charset="utf-8"></script>

and respective Supply Zones
and opening a pending order with specified pips Stop Loss and Take Profit.

Pardon me for not including the images directly. I don't I have enough upvotes to do that.

Here is an explanation of the candlestick patterns in the linked images:

Demand Zone

The general candlestick pattern (Demand Zone) occurs when at least two or more consecutive bullish candles (with the last bullish candle high being the high of the time period) are followed by one or more bearish candles whose high and low are lower than the last bullish candle. Then finally followed by a bullish candle that forms the new high. The rectangle area which is the Demand Zone is taken from the Open to the Low of the last last bearish candle.

Hidden Demand Zone

When a series of consecutive bullish candles has a candle with its low, lower than the previous candle and its High coinciding with its Close, then the Hidden Demand Zone is taken from the low to the open of the bullish candle.

The full explanation is available here for both demand and supply zones.

I am aware that bullish and bearish candles can be determined by


    if ( ( Open[1] - Close[1] ) > 0)
    {
      // candle is bearish
    }
    else
    {
      // candle is bullish
    }

I would really appreciate some help.

Mev answered 2/7, 2019 at 14:27 Comment(7)
So you asking someone to write you code to find those patterns?Archaeology
I need help with writing code to find the patternsMev
SO is not for providing code. If you have an exact question, the people here will happy to help you, but 'do the task for me' - it's a wrong request and I doubt that anyone will do this for you for free.Archaeology
I understand that but here is a case where I've provided what I can do, which is code bullish and bearish candles. I just help with how to apply the conditions to recognize the patterns.Mev
Am here for the bounty, but the bounty has a pretty big scope. The 100 points is not enough for the effort for this. So maybe not. I've done a candlestick stick pattern recognizer in MT4 before at imgur.com/a/Kmumv8U (it identifies patterns in real-time across multiple timeframes). Recognizing the pattern shouldn't be difficult. Drawing that box need some work. I'll be back.Moleskins
@JosephLee I didn't start the bounty but looking from the response to my question I believe apart from a lot of people benefitting from the solution, the solution would also generate several upvotes, maybe more than the bounty itself. That's just my opinion based on the little I've seen here.Mev
@JosephLee I agree with what TopLeft said. It's true that the bounty seems to require a lot, but a lot can also be gained from a good answer. Also to put things in perspective, I didn't ask the question but I've given close to a third of my reputation for the bounty because of my interest in a solution.Litton
M
1

Seems these patterns are not fully described, so it is not possible to code them correctly. Ok, let us try with pattern#1. The conditions used for pattern(what seems reasonable from the picture):
1. check at start of the new bar(bar#0).
2. bar 1(which is bar#3 in MQL4 if we compute 0 as the current) must be bullish.
3. bar 2(bar#2) is bearish. (or N bars in case of pattern#2, N can be 2 or more) 4. bar 3(bar#1 in MT4) is bullish.
5. its high=close.
6. its high>high of bar#3.

enum EnmDir
 {
  LONG = 1,
  SHORT=-1,
  NONE = 0,
 };
int getCandleDirection(const int shift)
{
   const double open=iOpen(_Symbol,0,shift), close=iClose(_Symbol,0,shift);
   if(close-open>_Point/2.)
      return LONG;      //bullish
   if(open-close>_Point/2.)
      return SHORT;     //bearish
   return NONE;     //doji
}
bool isPattern1Detected(const EnmDir dir)
{
   if(dir==0)return(false);
   if(getCandleDirection(3)!=dir)
      return false; //rule#2
   if(getCandleDirection(2)+dir!=0)
      return false; //rule#3
   if(getCandleDirection(1)!=dir)
      return false; //rule#4
   if(dir>0)
   {
      if(iHigh(_Symbol,0,1)-iClose(_Symbol,0,1)>_Point/2.)
         return false;  //rule#5 for long
      if(iHigh(_Symbol,0,1)-iHigh(_Symbol,0,3)>_Point/2.)
         return true;   //rule#6 for long
      return false;     //if rule#6 is not hold
   }
   else
   {
      if(iClose(_Symbol,0,1)-iLow(_Symbol,0,1)>_Point/2.)
         return false;  //rule#5 for short
      if(iLow(_Symbol,0,3)-iLow(_Symbol,0,1)>_Point/2.)
         return true;   //rule#6 for short
      return false;     //if rule#6 is not hold
   }
}
bool isPattern2Detected(const EnmDir dir,const int numCandlesAgainst=1)
{
   if(dir==NONE)return(false);
   if(getCandleDirection(1)!=dir)
      return false; //rule#4
   for(int i=1;i<=numCandlesAgainst;i++)
   {
      if(getCandleDirection(1+i)!=dir)
         return(false); //rule#3 - checking that all numCandlesAgainst must be bearish
   }
   if(getCandleDirection(2+numCandlesAgainst)!=dir)
       return false; //rule#2
   if(dir>0)
   {
     if(iHigh(_Symbol,0,1)-iClose(_Symbol,0,1)>_Point/2.)
        return false;  //rule#5 for long
     if(iHigh(_Symbol,0,1)-iHigh(_Symbol,0,2+numCandlesAgainst)>_Point/2.)
        return true;   //rule#6 for long
     return false;     //if rule#6 is not hold
   }
   else
   {
     if(iClose(_Symbol,0,1)-iLow(_Symbol,0,1)>_Point/2.)
        return false;  //rule#5 for short
     if(iLow(_Symbol,0,2+numCandlesAgainst)-iLow(_Symbol,0,1)>_Point/2.)
        return true;   //rule#6 for short
     return false;     //if rule#6 is not hold
   }
}

What else you need here? To detect HL of the rectangle? That is simple, is rules are clear. Let us assume they are: for LONG, up=Open of bar#2, down=low of that bar. Then,

void detectRangeOfZone(double &top,double &bottom,const EnmDir dir)
{
    if(dir>0)
    {
        top=iOpen(_Symbol,0,2);
        bottom=iLow(_Symbol,0,2);
    }
    else if(dir<0)
    {
        top=iClose(_Symbol,0,2);
        bottom=iHigh(_Symbol,0,2);
    }
}

Do you need to draw a rectangle? Ok but how would you decide what is the time to stop drawing? Let us assume N bars to the right is enough, and let us ignore weekends for now(a bit more complicated if keeping in mind weekends when the market is closed).

bool drawRectangle(const int dir,const double top,const double bottom)
{
    const datetime starts=iTime(_Symbol,0,2), ends=starts+PeriodSeconds()*N_bars;//time of start and end of the rectangle
    const string name=prefix+"_"+(dir>0?"DEMAND":"SUPPLY")+"_"+TimeToString(starts);//name would be unique sinse we use time of start of the range. DO NOT FORGET about prefix - it should be declared globally, you would be able to delete all the objects with 'ObjectsDeleteAll()' function that accepts prefix in one of its implementations.

    if(!ObjectCreate(0,name,OBJ_RECTANGLE,0,0,0,0,0))
    {
        printf("%i %s: failed to create %s. error=%d",__LINE__,__FILE__,name,_LastError);
        return false;
    }
    ObjectSetInteger(0,name,OBJPROP_TIME1,starts);
    ObjectSetInteger(0,name,OBJPROP_TIME2,ends);
    ObjectSetDouble(0,name,OBJPROP_PRICE1,top);
    ObjectSetDouble(0,name,OBJPROP_PRICE2,bottom);
    //add color, width, filling color, access modifiers etc, example is here https://docs.mql4.com/ru/constants/objectconstants/enum_object/obj_rectangle
    return true;
}

here is the main block, do not forget to add a new bar check, otherwise the tool would check for objects every tick which is waste of time. string prefix=""; //add some unique prefix for all your objects const int N_bars = 15; //15 bars in this example

void OnDeinit(const int reason){ObjectsDeleteAll(0,prefix);}
void OnTick()
{
    if(!isNewBar())
        return;     //not necessary but waste of time to check every second

    const bool pattern1Up=isPattern1Detected(1), pattern1Dn=isPattern1Detected(-1);
    if(pattern1Up)
    {
        double top,bottom;
        detectRangeOfZone(top,bottom,1);
        drawRectangle(1,top,bottom);
        PlacePendingOrder(1,top,bottom);
    }
    if(pattern1Dn)
    {
        double top,bottom;
        detectRangeOfZone(top,bottom,-1);
        drawRectangle(-1,top,bottom);
        PlacePendingOrder(-1,top,bottom);
    }
}

int PlacePendingOrder(const EnmDir dir,const double oop,const double suggestedSl)
{
   const double lot=0.10;                  //FOR EXAMPLE, PUT YOUR DATA HERE
   const string comment="example for SOF";
   const int magicNumber=123456789;

   int cmd=dir>0 ? OP_BUY : OP_SELL;
   double price=(dir>0 ? Ask : Bid), spread=(Ask-Bid);
   if(dir*(oop-price)>spread)
      cmd+=(OP_BUYSTOP-OP_BUY);
   else if(dir*(price-oop)>spread)
      cmd+=(OP_BUYLIMIT-OP_BUY);

   int attempt=0, ATTEMPTS=5, SLEEP=25, SLIPPAGE=10, result=-1, error=-1;
   while(attempt<ATTEMPTS)
     {
      attempt++;
      RefreshRates();
      if(cmd<=OP_SELL)
        {
         price=dir>0 ? Ask : Bid;
         result=OrderSend(_Symbol,cmd,lot,price,SLIPPAGE,0,0,comment,magicNumber);
        }
      else
        {
         result=OrderSend(_Symbol,cmd,lot,oop,SLIPPAGE,0,0,comment,magicNumber);
        }
      if(result>0)
         break;
      error=_LastError;
      Sleep(SLEEP);
    }
  if(result>0)
    {
     if(OrderSelect(result,SELECT_BY_TICKET))
       {
        price=OrderOpenPrice();
        if(!OrderModify(result,price,suggestedSl,0,OrderExpiration()))
           printf("%i %s: failed to modify %d. error=%d",__LINE__,__FILE__,result,_LastError);
           //tp is zero, sl is suggested SL, put yours when needed
       }
     return result;
    }
    printf("%i %s: failed to place %s at %.5f. error=%d",__LINE__,__FILE__,EnumToString((ENUM_ORDER_TYPE)cmd),(cmd>OP_SELL ? oop : price),error);
    return -1;
}
Millrun answered 6/7, 2019 at 15:21 Comment(19)
Thanks for the answer. I tried to explain the Demand Zones, but I think I didn’t do a good job at it. That’s the main reason I included the YouTube link. The first few minutes explains the pattern in detail, but just to highlight a few things you asked about. Image 1 and 2 in the question are referring to the same pattern. In general, the pattern begins with at least one bullish candle followed by at least one bearish candle which is followed by one bullish candle that has a higher high than the beginning bullish candles. 1/3Mev
So the last bearish candle in the group of bearish candles is what we are interested in. It’s open and low make the demand zone. The hidden demand zone is represented by a bullish candle in a group of at least two bullish candles whose high is higher than the previous candle high and low is lower than the previous candle low. The Open and Low form the Hidden Demand Zone. I guess showing the rectangle for N_bars is okay. I would really appreciate if you could include a complete example that includes setting a pending order with SL and TP when the pattern is recognised. 2/3Mev
Finding the pattern in more generic cases where it contains more than one bearish candle seems to be the main thing missing in your solution and condition 5 as you stated wouldn’t be necessary. 3/3Mev
@DanielKniaz to obtain the bounty on this question you missing a few things, a more generic pattern recogniser based on the images above. I watched the first few minutes of the video as TopLeft recommended and they are well described. Also missing tested code with a pending order example.Litton
@OnlyCodeMatters, All the patterns are different. Ok, pattern#1 and pattern#2 are similar and I added an the code on how to implement #2 (very similar from #1). For #3 - it is different from #1 or #2, and it would be quite easy to code it after you or TS check and understand all steps done in order to recognize pattern#1. Pending order example added, the code will compile after you implement isNewBar() function (an article on MQL5.com or any other implementation, or hiding that block).Millrun
@DanielKniaz I added this code as you requested and also increased the number of bars from 15 to 100 but the rectangles don't show, even in clear cases that I've recognized.Mev
@DanielKniaz I haven't been here for a while, just remembered that I created a bounty :). I've not been able to check the code you've written and won't get the time to before the bounty ends. But from a glance and based on the effort you've put in, I guess your answer is the closest to the desired solution.Litton
@Mev the code of setting color etc to the object is probably not even compile. Show the full code. isNewBar() is from MT5 even though it seems it should work in MT4 also.Millrun
@Litton thank you! I think that the pattern#3 is the one that is the easiest one so everyone interested may code it is having some time and interest.Millrun
@DanielKniaz Here is the full code. I got the isNewBar() from forexfactory and it was stated that it should work for both mql4 and mql5Mev
@Mev first of all need to know whether you need rule#5. it seems it is the one that cuts most of setups. Also, need to think what if open=low for supply zone in pattern#1 (similarly close=high), if they match you do not see the rectangle because it has 0 ticks width, maybe it makes sense to add some restriction for such small zones and do not trade (at least not possible to set SL on that zone)Millrun
@DanielKniaz rule #5 doesn't seem to be necessary so I commented it. Still the rectangles are not drawing. If open=low for supply zone it shouldn't draw.Mev
And can you show an example? I checked and it works at my endMillrun
@DanielKniaz Thanks. Just realised it's showing the rectangles. I thought that it would show the patterns in the past when I scroll back based on N_bars, that's why I thought it wasn't working. How can I get patterns in the past to show?Mev
@Mev there is a function called isPattern1Detected() which takes a parameter 1 by default. it is coded in such a way that you can push your number, which is number of the last bar of the pattern. Also you'll haveto add a param into detectRangeZone() and probably better if it is same as in the other function. replace 2 with shift+1 in that function after adding int shift; add time start and end into drawRectangle() using the same logicMillrun
@DanielKniaz I didn't fully understand your explanation on how to show the patterns in earlier candlesticks. isPattern1Detected() takes an enum parameter which is either -1, 0 or 1 so let's say I want to rectangles to show whenever the pattern is detected for the past week/month/50 candlesticks, I'll need to adjust the enum or something.Mev
@Mev No I talked about the shift, it is 1 by default but you can call any other bar, or group of bars. And you will have to call LONG then SHORT, no need to update enum unless it has any sense.Millrun
@DanielKniaz Apart from making the changes to drawRectangle(), if I understand you well, I am suppose to replace the 1 in getCandleDirection(1) and if(iHigh(_Symbol,0,1)-iHigh(_Symbol,0,3)>_Point/2.) and so on in isPattern1Detected(). I hope we are on the same page. I don't want to change the pattern, I just want to see previous occurrences when I scroll back in time, a bit like a SupDem indicator.Mev
@Mev you need a function isPattern1Detected(const EnmDir dir,const int shift) and detectRangeOfZone(double &top,double &bottom,const EnmDir dir,const int shift) where shift is replaced with number of candle, by default it is 1 (so 2 is replaced with 1+shift etc).Millrun
M
1

I'm late :'( Work kept me from StackOverflow :'( Anyway, I won't be able to provide a full program as per the Bounty, nor can I provide the full code to solve this Demand Zone question.

However, I still would like to provide some "starting ground" for everyone to build their own Pattern Recognizer on MT4 (MQL4).

To keep the text short, I've recorded a YouTube video to describe it. https://youtu.be/WSiyY52QyBI

Anyway, here are the codes:

//+------------------------------------------------------------------+
//|                                                   SO56854700.mq4 |
//|                 Copyright 2019, Joseph Lee, TELEGRAM JosephLee74 |
//|               https://stackoverflow.com/users/1245195/joseph-lee |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Joseph Lee, TELEGRAM JosephLee74"
#property link      "https://stackoverflow.com/users/1245195/joseph-lee"
#property version   "1.00"
#property strict

#include <clsBar.mqh>
#include <stderror.mqh> 
#include <stdlib.mqh> 

//==========================================================================

//-------------------------------------------------------------------
// System Variables
//-------------------------------------------------------------------
double  viPipsToPrice               = 0.0001;
double  viPipsToPoint               = 1;
string  vsDisplay                   = "";

//-------------------------------------------------------------------

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
    ObjectsDeleteAll(); Comment("");
    // Caclulate PipsToPrice & PipsToPoints (old sytle, but works)
    if((Digits == 2) || (Digits == 3)) {viPipsToPrice=0.01;}
    if((Digits == 3) || (Digits == 5)) {viPipsToPoint=10;}

    return(INIT_SUCCEEDED);
}


//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
    ObjectsDeleteAll();
    return(0);
}


//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
    if(!isNewBar())
        return;

    clsCandlestickPattern   voPatterns[];
    clsPatternRecognizer        voRec(Symbol(), true), PERIOD_CURRENT, 0);
    voRec.sbRecognizePatterns(voPatterns);
    for(...
       Display the content with Comment() ...
}
//+------------------------------------------------------------------+

More importantly, is the clsBar.mqh. Note that this is an "include file", and should be located in the include folder. Include file helps us to make our program less clutter, and help us to be able to write re-usable codes. Very useful when writing OOP classes.

clsBar.mqh: Download (OneDrive) https://1drv.ms/u/s!AoLFy6fRYNsvjTU-xSzAADCwGjPQ

Unfortunately, the file is too large to be included in this post. So I have to upload it the OneDrive.

Moleskins answered 11/7, 2019 at 18:48 Comment(2)
@JospehLee the clsBar.mqh seems like it would really make things a lot simpler. I placed the file in the include as you requested and tried running the example you provided with only clsCandlestickPattern voPatterns[]; in the OnTick() but I got several fniGetLargerOf, fniGetSmallerOf and fniGetPricePerPip errors. I would really be glad if you could provide a complete example like drawing two horizontal lines on the open and close of a bearish candle when the bearish engulfing pattern is recognized. This would really put this into perspective on how clsBar.mqh should be used.Mev
I also tried using clsBar.mqh and had a similar issueTurnip

© 2022 - 2024 — McMap. All rights reserved.