Example of how to use CiCustom

 

After some in depth debugging, I realized I've been using CiCustom wrong. The documentation is unclear but by implementation it is now apparent that CiCustom was intended to derive subclasses. Making custom indicator classes is very easy of you use the following pattern. 

  1. Inherit CiCustom
  2. Make a custom Create method that's applicable to the indicator setup. 
  3. Override the Initialize method. 

#include <indicators/custom.mqh>
#include <mqlparams.mqh>

#define INDICATOR_NAME "alexstal_zigzagprof"
#define INITIAL_BUFFER_SIZE 2048

class CiZigZagProf : public CiCustom {
protected:
   virtual bool      Initialize(const string symbol, 
                                const ENUM_TIMEFRAMES period, 
                                const int num_params, 
                                const MqlParam &params[]
                     ) override;  
public:
   virtual bool      Create(string symbol, 
                            ENUM_TIMEFRAMES period, 
                            int zigzag_period, 
                            int min_amplitude = 10, 
                            int min_motion = 0,
                            bool use_smaller_tf = true
                     );                         
   virtual double    upBuffer(int index) { return this.GetData(0, index); }
   virtual double    dnBuffer(int index) { return this.GetData(1, index); }
};
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CiZigZagProf::Create(  string symbol, 
                            ENUM_TIMEFRAMES tf, 
                            int zigzag_period, 
                            int min_amplitude = 10, 
                            int min_motion = 0,
                            bool use_smaller_tf = true
) {
   // #1 Setup the MQL params array for the custom indicator.
   CMqlParams params;
   params.Set(INDICATOR_NAME, TYPE_STRING)
         .Set(zigzag_period, TYPE_UCHAR)
         .Set(min_amplitude, TYPE_UCHAR)
         .Set(min_motion, TYPE_UCHAR)
         .Set(use_smaller_tf, TYPE_BOOL);
   // #2 Call the parent Create method with the params
   if (!CiCustom::Create(symbol, tf, IND_CUSTOM, params.Total(), params.params))
      return false;
   // #3 Resize the buffer to the desired initial size
   if (!this.BufferResize(INITIAL_BUFFER_SIZE))
      return false;
   return true;
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CiZigZagProf::Initialize(const string symbol, 
                              const ENUM_TIMEFRAMES period, 
                              const int num_params, 
                              const MqlParam &params[]
) {
   // #1 Specify if this indicator redraws
   this.Redrawer(true);
   // #2 Specify the number of indicator buffers to be used. 
   if (!this.NumBuffers(2))
      return false; 
   // #3 Call super.Initialize 
   if (!CiCustom::Initialize(symbol, period, num_params, params))
      return false;
   return true;
}
Step on New Rails: Custom Indicators in MQL5
Step on New Rails: Custom Indicators in MQL5
  • www.mql5.com
Finally we've got an opportunity to try the new trade terminal - MetaTrader 5. No doubt, it is noteworthy and has many new features as compared to its predecessor. The important advantages of this platform among others are: Essentially modified language allowing now to use the object-oriented programming, still allowing to use the rich...
 
nicholish en:

After some in depth debugging, I realized I've been using CiCustom wrong. The documentation is unclear but by implementation it is now apparent that CiCustom was intended to derive subclasses. Making custom indicator classes is very easy of you use the following pattern. 

  1. Inherit CiCustom
  2. Make a custom Create method that's applicable to the indicator setup. 
  3. Override the Initialize method. 

Hello, sfortunately for me (v5.00 build 2485) it's doesn't solve the CopyBuffer() failure after the first buffer, a very critical problem for me now.

Can you gently post the relative two buffers indicator code so I can do a test with your solution?

Thank you in advance for everyone can help me to solve this problem.

This is my pseudo-code and I'm able to retrieve data only for the first buffer :

//the class
class CiOverlap : public CiCustom
{
private:
   double            m_up_buffer[];
   double            m_body_buffer[];
   double            m_down_buffer[];
   ...

//the indicator
double         UpBuffer[];
double         BodyBuffer[];
double         DownBuffer[];
...
int OnInit(){
   ...
   if(!SetIndexBuffer(0, UpBuffer, INDICATOR_DATA))  { warning here };
   if(!SetIndexBuffer(1, BodyBuffer,INDICATOR_DATA)) { warning here };
   if(!SetIndexBuffer(2, DownBuffer, INDICATOR_DATA)){ warning here };   

   --> as expected is always ok
}
int OnCalculate(...){
   --> some computations here
   msg="UpBuffer.size = " + IntegerToString(ArraySize(UpBuffer));__Log__(Debug,msg); 
   --> the same for other buffers..   
   
   --> arrays are ALWAYS EQUALLY SIZED with their right computed values
   --> mean no problem inside the indicator
}


nb. this CiOverlap class code is triggered by CExpert::Processing method (on my CExpert derived class)
      ...
      buffer_index = 0;
      if (CIndicator::GetData(0, items_to_be_copied, buffer_index, _buf) > 0) { --> IS ALWAYS OK!  }
      buffer_index = 1;
      if (CIndicator::GetData(0, items_to_be_copied, buffer_index, _buf) > 0) { --> never enter here  }
      else{ --> IS ALWAYS (-1) }
      
      --> the same for the other buffers

I try different approach :

   up_total =::CopyBuffer(m_handle, 0, 0, items,m_up_buffer);     --> ALWAYS RIGHT
   this.Refresh();
   body_total  =::CopyBuffer(m_handle, 1, 0, items,m_body_buffer); --> IS ALWAYS (-1)
   down_total =::CopyBuffer(m_handle, 2, 0, items,m_down_buffer);  --> IS ALWAYS (-1)
   
   OR:
   
   ((CIndicatorBuffer*)At(buffer_index)).Refresh(m_handle,buffer_index);
   if (this.GetData(0, items, buffer_index, _buf) > 0)
        
        --> ALWAYS OK for buffer_index = 0        
        --> ALWAYS(-1) with buffer_index > 0


UPDATE:

I fixed my bug, the problem arise because I define more than one buffer with INDICATOR_DATA flag and the SL silently accept only one (the first). So all the succedings access (via CopyBuffer/GetData) to that buffers give always -1.

I really thanks everyone here for that great community and extremely professional articles.

my work can now move forward again!

Example of how to use CiCustom
Example of how to use CiCustom
  • 2020.03.27
  • www.mql5.com
After some in depth debugging, I realized I've been using CiCustom wrong...
 

Hello @nicholish en, thank you for this article.

could you provide an example of the mq5 file that is calling this class ? 

I am also wondering how to pass the parameters source mq5 input parameters to the alexstal_zigzagprof indicator once the program is running (Do I need to change to extern instead of input or is there another way?). 

Thank you ! :)

Reason: