Download MetaTrader 5

Arbitrary indicator buffer creation.

To add comments, please log in or register
9047
32
9047  

Hi,

I think this a limitation of the MQL sandbox but I thought I'd ask here just to double check.

I have an indicator the needs to create an arbitrary number of "indicator buffers" based on the input parameters e.g. If the user wants to show 5 results then I need 5 buffers, if they chose 3 then I only need 3 buffers. I've tried the following:

  1. Create a "reasonable" number of buffer declarations. Naturally this has some draw back: maintainability, code bloat, hacky and its just ugly. But it does work. The concepts are outlined in the "pseudo" code below:
    double buffer_0[], buffer_2[], buffer_3[],  
           buffer_4[], buffer_5[], buffer_6[],
           buffer_7[], buffer_8[], buffer_9[],
           .
           .
           buffer_(N-1)[];

    const int MAX_BUFFERS = N;
    int numBuffers;

    void initIndicatorBuffers(int n) {
         IndicatorBuffers(n);

         if (n >= 1) SetIndexBuffer(0, buffer_0);
         if (n > 1) SetIndexBuffer(1, buffer_1);
         if (n > 2) SetIndexBuffer(2, buffer_2);
         .
         .
         .
         if (n == N) SetIndexBuffer(N-1, buffer_(N-1));
    }


    int OnInit() {

        numBuffers = /* some function calls etc to determine required number of buffers. */

        if (numBuffers > MAX_BUFFERS) {
            return(INIT_FAILED);
        }

        initIndicatorBuffers(numBuffers);


    }      
    Note not all error conditions etc are covered.

  2. To avoid arbitrary declarations I tried using structs (given that generalised pointers don't exists in MQL4/5). This solution, well much more elegant, does not work. Arrays held in classes/structs can't be passed around (back to the lack of pointers) and hence can't be used as "indicator buffers" (probably due life cycle constraints etc. and the security sandbox). Again below is some pseudo code of what it roughly looks like (remember this does not work/compile primarily because the arrays inside the buffer_t structs are not initialised and can't be if they are to be used as indicator buffers):

    struct buffer_t {
        double data[];
    };

    buffer_t buffers[];
    int numBuffers;


    int OnInit() {
        int index;

        numBuffers = /* some function calls etc to determine required number of buffers. */
        IndicatorBuffers(numBuffers);
        ArrayResize(buffers, numBuffers);

        for (index = 0; index < numBuffers; index++) {
            SetIndexBuffer(index, buffers[index].data);
        }

        // Other init code...
        //
        //
        //
        

        return(INIT_SUCCEEDED);
    }

  3. Building on the struct concept I tried initialising the arrays with a hack. I created an unnecessary indicator buffer. I then copied the array after in was assigned as and indicator buffer, via the SetIndexBuffer function call. This compiles and runs - sort of. Once you start using the arrays held in the buffer objects the terminal essentially falls over and dies. I'm guess this is due to the unpredictable behaviour of copying the array that has been allocated to an indicator buffer slot and allocating to other indicator buffer slots. Some pseudo code outlining the approach is below:

    double _prototype_buffer[];

    struct buffer_t {
        double data[];
    };

    buffer_t buffers[];
    int numBuffers;


    int OnInit() {
        int index;

        numBuffers = /* some function calls etc to determine required number of buffers. */
        IndicatorBuffers(numBuffers + 1);
        ArrayResize(buffers, numBuffers);

        /* Initialise an array to be an Indicator buffer */
        SetIndexBuffer(numBuffers, _prototype_buffer);
        SetIndexStyle(numBuffers, DRAW_NONE);  

        for (index = 0; index < numBuffers; index++) {
            /* Copy the initialised buffer then use it */
            ArrayCopy(_buffers[index].data, _prototype_buffer);
            SetIndexBuffer(index, buffers[index].data);
        }

        // Other init code...
        //
        //
        //
        

        return(INIT_SUCCEEDED);
    }

I can't think up any other ideas to try. I'm fairly certain the sandbox is preventing me from doing this - I can appreciate why but its dam annoying at the same time.
If anyone can offer some suggestions beyond the first solution that would be greatly appreciated. 

Hope I've missed something simple.

9047.
whroeder1
17111
whroeder1  

9047: (remember this does not work/compile primarily because the arrays inside the buffer_t structs are not initialised):

Compiles just fine and runs just fine for me.
#property strict
#property indicator_chart_window
#property indicator_buffers 5
#property indicator_color1    Lime
#property indicator_label1    "Local Low"
#property indicator_type1     DRAW_ARROW
#define    UP_RIGHT_THIN        228    // Wingding
:
struct buffer_t {
    double data[];
};
buffer_t buffers[];
int OnInit(){
   int numBuffers=5;
    ArrayResize(buffers, numBuffers);
    for (int index = 0; index < numBuffers; index++) {
        SetIndexBuffer(index, buffers[index].data);
    }
   SetIndexArrow(0,UP_RIGHT_THIN);
   SetIndexArrow(1,DOWN_RIGHT_THIN);
   return INIT_SUCCEEDED;
}  // OnInit
#define Local_Low  buffers[0].data             // shims into existing code.
#define Local_High buffers[1].data
int OnCalculate(const int  Rates_total, ... ){ // existing code unmodified
   :
         Local_High[iBar]     = hiCur + ArrowDistance();

Stanislav Korotky
23379
Stanislav Korotky  
You may look at the blog post - Arrayed indicator buffers based on operators overloading. Probably it'll be hepful.
9047
32
9047  
I found issue ... I think :)

Thank you very much for the quick responses :D
To add comments, please log in or register