1
votes

As an exercise to learn MQL4, I'm writing a custom indicator which just replicates the Simple Moving Average, but without using the builtin iMA() function. The code below paints the indicator onto the chart, but every part of the line except the ending is showing extremely low values. For instance, on the AUD/CAD chart, which is usually, in 0.80-0.90 range, the SMA line is around 0.20-0.30.

The line moves up and down in-sync with the price, and the very last bar seems to have an accurate reading ( as the SMA spikes towards the current price at that point ), but everything else is way low. I've exhausted every possible reason for why this may be happening, and nothing is changing it. Can somebody help me to figure out what is going wrong?

/*
   SELF-CODED 5-DAY SMA
   Set up buffer with style, indexbegin, and color
   for each bar:
       calculate closing price of last 4 bars and current price
       use to find SMA value at that bar
   update last value after every tick
   lock each bar's SMA value once the bar has close
*/
#property strict
#property indicator_chart_window
#property indicator_buffers 1

double buffer[];

int OnInit() 
   {
   IndicatorBuffers(1);
   SetIndexBuffer(0, buffer);
   SetIndexStyle(0, DRAW_LINE);

   return(INIT_SUCCEEDED);
   }

int OnCalculate( const int       rates_total,
                 const int       prev_calculated,
                 const datetime &time[],
                 const double   &open[],
                 const double   &high[],
                 const double   &low[],
                 const double   &close[],
                 const long     &tick_volume[],
                 const long     &volume[],
                 const int      &spread[]
                 )
   {
   int limit = rates_total - prev_calculated;

   buffer[0] = iClose(NULL, 0, 0); 

   for(int b = 1; b<5; b++)
      {
      buffer[0] = buffer[0] + iClose(NULL, 0, b);
      }
   buffer[0] = buffer[0]/5;   

   for(int i = 1; i<limit; i++)
      {
      buffer[i] = iClose(NULL, 0, i); // Placeholder in first for loop interation
      for(int a = 1; a<4; a++) {
         {
         buffer[i] = buffer[i] + iClose(NULL, 0, i+a);
         }
      buffer[i] = buffer[i]/5.0;
      }
   }
return(rates_total);   
}
2
The proposed algorithm is per-se very inefficient and will require a few changes in algorithmical thinking, but StackOverflow promotes MCVE policy to base all code-related subjects to a Complete and Verifiable ( i.e. reapeatable, incl. data-dependent values ) -- so kindly update your post as proposed above. Anyway, welcome to StackOverflow and Wild Worlds of MQL4user3666197
Would you mind posting a few quantitative outputs? Something alike this: Print( "SYMBOL:", _Symbol," PERIOD:", PERIOD_CURRENT, " Last 6 Bars Close[i] := [ ", Close[5], ", ", Close[4], ", ", Close[3], ", ", Close[2], ", ", Close[1], ", ", Close[0]," ], SmaToTEST[1] = ", iCustom( _Symbol, PERIOD_CURRENT, "SMA", 0, 1 ), "SmaToTEST[0] = ", iCustom( _Symbol, PERIOD_CURRENT, "SMA", 0, 0 ) ); ?user3666197

2 Answers

0
votes
buffer[i] = iClose(NULL, 0, i);        // Placeholder in first for loop iteration
for ( int a = 1; a < 4; a++ ) {
    {
      buffer[i] = buffer[i] + iClose(NULL, 0, i+a);
      }
buffer[i] = buffer[i]/5.0;

this cannot be correct - ok if "i" is 1,
then
buffer[1] = (Close[1]+Close[1+1]+Close[1+2]+Close[1+3]) / 5,
as you sum from i+0 to i+3, 4 elements not five.

To fix : "a<=4".

it is advised to check number of previously calculated bars so that to update only recent ones, and start from Bars-1 (the earliest bar) and skip a little then calculate all the values in the way

sum       += Close[i]; 
Buffer[i]  = sum / 5; 
sum       -= Close[i+4];
i--;                      // inside a loop
0
votes

Took a harder way ahead, once decided to write a Custom Indicator first

As there are several, and very different, modes of programming in an MQL4 language, parts of which may and must even interact between themselves, the Custom Indicator is out of any questions the hardest of them all.

Let's suppose then, that you have already managed the other formats of the MQL4 programming, and have just issues with Custom Indicator for the said independent 5-Day SMA.

A closer look to the proposed code shows, that your algorithm is wasting CPU-cycles a lot, which is not a good practice in general, but is a lethal practice for Custom Indicator coding, as the all the technical indicators, that are used in MetaTrader Terminal 4, yes, all indicators share just a single thread. So performance tuning is a must for robust, non-blocking and fastest possible operations. Otherwise all your other programming efforts start to choke, due to waiting for a bottlenecked single thread handling all the relevant Custom Indicator-s. No, no, no - this shall never happen in a production grade system.


So, how to attack that?

Let's review the facts:

  1. current, "hot" bar [0] is the only element, that undergoes any evolution ( during bar duration )
  2. Once a current bar [0] ends, all it's associated technical data are "frozen" forever - yes, they will never change

This pair of a trivial observations means, that none ( I repeat NONE ) of any indicator values ought be calculated more than just once.

This logic is also supported in the New-MQL4.56789 syntax, where each OnCalculate() call receives a pair of helper index values
-- how far the evolution of time has gone ( the const int rates_total, ) i.e. how many bars the graph has already recorded
and
-- how far the calculation has proceeded alongside the flow of time ( const int prev_calculated ) i.e. what was the last already calculated ( processed ) amount of bars, when the custom indicator processing was interrupted ( yes, the calculus of the values is interruptible from design and allows for some sort of segmented processing of all the enqueued processing tasks once the MetaTrader Terminal 4 has just started to run, with graphs and indicators to get refreshed and sync-ed again with the external flow of the FX-Market Events.

So, let's start from the "end" of this story, if your current OnCalculate() call has received
int delta_to_process = rates_total - prev_calculated;
never,
indeed NEVER as a principle,
process anything, but the last delta_to_process bars.

Recounting beans from any one of the already counted heaps makes neither a progress in the current task ( the not yet counted heap ), nor any sense to spend a valuable amount of the limited thread CPU-clocks to repeat again the counting process to just receive the same answer for an already answered question.

int OnCalculate( const int       rates_total,
                 const int       prev_calculated,
                 const datetime &time[],
                 const double   &open[],
                 const double   &high[],
                 const double   &low[],
                 const double   &close[],
                 const long     &tick_volume[],
                 const long     &volume[],
                 const int      &spread[]
                 )
   {
   for ( int ii = rates_total - prev_calculated; // from oldest to "hot"[0]
             ii > EMPTY;                         //                      0 > EMPTY
             ii--                                // forward [n,n-1,n-2,..0]
             )
      buffer[ii] = ( iClose( _Symbol, PERIOD_D1, ii+0 ) // an even more brutal form
                   + iClose( _Symbol, PERIOD_D1, ii+1 ) // re-using buffer[ii+1] value
                   + iClose( _Symbol, PERIOD_D1, ii+2 ) // is left for one's
                   + iClose( _Symbol, PERIOD_D1, ii+3 ) // further experimentation
                   + iClose( _Symbol, PERIOD_D1, ii+4 ) // with performance tuning
                   ) / 5.0;                             
   }
   return( rates_total );
}

and do not forget to call this with a properly formatted call:

#define SMA_LINE_NUMBER_1 0                          // self-explanatory buffer-index ( zero-based )

... =  iCustom( _Symbol,                             // NULL is not so readable
                PERIOD_D1,                           // 0 is dangerous on a Graph with a "foreign" TimeFrame
                "aFileNAME_ofYourSMA_MQL4_code",     // yes, the pure FileNAME
                SMA_LINE_NUMBER_1,                   // yes, use the first buffer data ( .DEF-ed in SetIndexBuffer(0, buffer); )
                aBarOffsetToReturn_SMA_VALUE_for     // a reversed stepping number/index into automatically managed TimeSeries arrays
                );