Search code examples
mql4metatrader4

How to count how many bars back the moving average crossed last?


I am writing an MQL4 Custom Indicator that would tell how many bars ago a particular set of moving averages crossed.

To be specific, I want the output to show me that

"The 20 period MA( .. PRICE_OPEN ) is below MA( .. PRICE_CLOSE ) for the past 10 bars".

in a form of an int number.

double ma1 = iMA( Symbol(), 10080, 20, 0, 0, PRICE_OPEN,  0 );
double ma2 = iMA( Symbol(), 10080, 20, 0, 0, PRICE_CLOSE, 0 );

I want to find out that ma1 has been above or below ma2 for how many bars since the current bar.


Solution

  • TLDR;
    

    MQL4 Custom Indicator design is a bit schizophrenic task with 2 parts

    Once decided to design a Custom Indicator, one has to pay attention to both sides of the coin.

    MQL4 code has a non-trivial signature for a caller-side
    ( normally an Expert Advisor or a Script type of MQL4 code )
    and
    Custom Indicator, on it's own,
    operates in a special mode, where it calculates & store it's values into one or more arrays called IndexBuffer-s.

    Phase I.: design Caller-side interface ( what all do we need to set / receive ? )
    Phase II.: design Custom Indicator implementation side

    This way, the given indicator shall

    • set just one parameter: a period ... 20
    • receive just one int a value of days { +:above | 0: xoss | -: under }

    Phase I. : Design Caller-side interface

    Given the above parametrisation and the MQL4 concept of Custom Indicator construction, the following universal template ( recommended to be routinely maintained as a commented section right in CustomIndicator-file ) reflects details needed for the Caller-side interface ( which one benefits from by just a copy-paste mechanics, while the implementation-integrity responsibility is born by the CustomIndicator maintainer ):

    // CALL-er SIDE INTERFACE |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
                                                    // 
                                                    // #define sIndicatorPathNAME             "#AliceInTheWonderlands_msMOD_0.00"
                                                    // 
                                                    // //_____________________________________INPUT(s)
                                                    // // iMA_PERIOD:
                                                    // extern int   iMA_PERIOD    = 20;
                                                    // 
                                                    // //_____________________________________OUTPUT(s):
                                                    // #define iOutputDoubleUpOrDnBUFFER     0
                                                    // 
                                                    // //_____________________________________CALL-SIGNATURE:
                                                    // 
                                                    //                            double  iCustom(  _Symbol,                      // string       symbol,           // symbol:                                                 Symbol name on the data of which the indicator will be calculated. NULL means the current symbol.
                                                    //                                              PERIOD_W1,                    // int          timeframe,        // timeframe
                                                    //                                              sIndicatorPathNAME,           // string       name,             // path/name of the custom indicator compiled program:     Custom indicator compiled program name, relative to the root indicators directory (MQL4/Indicators/). If the indicator is located in subdirectory, for example, in MQL4/Indicators/Examples, its name must be specified as "Examples\\indicator_name" (double backslash "\\"must be specified as separator instead of a single one).
                                                    //                                              iMA_PERIOD,                   // ...[1]       ...,              // custom indicator [1]-st input parameter
                                                    //                                              <<N/A>>,                      // ...[2+]                        // custom indicator further input parameters (if necessary)
                                                    //                                              iOutputDoubleUpOrDnBUFFER,    // int          mode,             // line index:                                             Line index. Can be from 0 to 7 and must correspond with the index, specified in call of the SetIndexBuffer() function.
                                                    //                                              i                             // int          bar_shift         // shift
                                                    //                                              );
                                                    // ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
    

    Typical Expert Advisor call looks like this:

    int anIndicatorVALUE = iCustom( _Symbol,
                                    PERIOD_W1,
                                    sIndicatorPathNAME,
                                    iMA_PERIOD,
                                    iOutputDoubleUpOrDnBUFFER,
                                    aCurrentBarPTR
                                    );
    if ( 0 > anIndicatorVALUE ) // on "line" under for <anIndicatorVALUE> [PERIOD]-s
    { ...
    }
    

    Phase II. : Design Custom Indicator implementation side

    Preset an overall capacity

    #property indicator_buffers 1            // compile time directive
    

    Allocate memory for IndexBuffer(s) needed

    IndicatorBuffers( 1 );                   // upper limit of 512 is "far" enough
    

    Declare IndexBuffer

    double UpOrDnBUFFER[];                   // mandatory to be a double
    

    Associate IndexBuffer with an index

    SetIndexBuffer( 0, UpOrDnBUFFER );       // index 0: UpOrDnBUFFER[]
    ArraySetAsSeries(  UpOrDnBUFFER, True ); // reverse AFTER association
    

    The core logic of any Custom Indicator is inside an OnCalculate() function, where you
    - implement desired calculus
    and
    - store resulting values into respective cells of the UpOrDnBUFFER[];

    Writing a fully fledged code upon request is not the key objective of StackOverflow, but let me sketch a few notes on this, as Custom Indicator implementation side requires a bit practice:

    • because Custom Indicator OnCalculate() operates "progressively", so do design your calculation strategy & keep in mind the state-less-ness between "blocks" of forward-progressive blocks of calculations.

    • as given, crosses are tri-state system, so watch issues on cases, where decisions are made on a "live" bar, where the state { above | cross | under } may change many times during the bar-evolution.