Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Calculation of Integral Characteristics of Indicator Emissions

Calculation of Integral Characteristics of Indicator Emissions

MetaTrader 5Examples | 22 February 2013, 10:59
7 103 2
Sergey Pavlov
Sergey Pavlov

Introduction

Indicator emissions represent a new and quite promising direction in the study of time series. It is characterized by the fact that the analysis is not focused on indicators as such but on their emissions into the future or past based on which we can actually make a market environment forecast:

  • support and resistance levels in the future;
  • trend direction (price movement);
  • strength of movement accumulated over the past.

My previous article called "Drawing Indicator's Emissions in MQL5" dealt with the algorithm of emission drawing and specified its key features. Let me remind you:

Emission is a set of points located at intersections of lines peculiar to the indicators under consideration.

Emission points, in their turn, have some peculiarities:

  • Emission points of the same type tend to cluster.
  • Dense point clusters can attract or, conversely, repel the price.

Emission gallery:

Emission of DCMV Emission of iMA & iEnvelopes
Emission of DCMV Emission of iMA & iEnvelopes

Fig. 1. Examples of indicator emission plots. Left: emission of the DCMV indicator. Right: emission of the iMA and iEnvelopes indicators.

In illustration of the calculation of integral characteristics of emissions, we will take moving average envelopes (Envelopes) and moving averages (Moving Average) themselves with input parameters as follows:

//--- external variable for storing averaging period of the iEnvelopes indicator
input int   ma_period=140; // averaging period of the iEnvelopes indicator
//--- array for storing deviations of the iEnvelopes indicator
double      ENV[]={0.01,0.0165,0.0273,0.0452,0.0747,01234,0.204,0.3373,0.5576,0.9217,1.5237};
//--- array for storing iMA indicator periods
int         MA[]={4,7,11,19,31,51,85};

So, we will look for intersections of lines peculiar to the selected indicators. The number of lines and their characteristics (averaging periods and deviations) are chosen at random. Emission can in fact be plotted using any set of parameters for these indicators (as long as they intersect in space).

Now that we have chosen the indicators, let us proceed to create an Expert Advisor that is going to serve as a base program for the analysis of emissions. We will need to get the calculated data from the iMA and iEnvelopes technical indicators. I propose to use a method described in the Guide to Using Technical Indicators in Expert Advisors.

To plot the lines whose intersections we need to find, we only need to set two points for each of the lines. Thus, it is sufficient to get indicator values for two bars (e.g. the current and previous) only. The price on the previous bar is static, while the price on the current bar is dynamic and therefore new points keep being generated with every new tick. Here is the code:

//+------------------------------------------------------------------+
//|                                      emission_of_MA_envelope.mq5 |
//|                                           Copyright 2013, DC2008 |
//|                           https://www.mql5.com/ru/users/DC2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2013, DC2008"
#property link      "https://www.mql5.com/ru/users/DC2008"
#property version   "1.00"
//---
#include <GetIndicatorBuffers.mqh>
#include <Emission.mqh>
//--- external variable for storing averaging period of the iEnvelopes indicator
input int   ma_period=140;      // averaging period of the iEnvelopes indicator
//--- array for storing deviations of the iEnvelopes indicator
double      ENV[]={0.01,0.0165,0.0273,0.0452,0.0747,01234,0.204,0.3373,0.5576,0.9217,1.5237};
//--- array for storing the iMA indicator periods
int         MA[]={4,7,11,19,31,51,85};
//--- array for storing pointers to the iMA and iEnvelopes indicators
int         handle_MA[];
int         handle_Envelopes[];
//--- market data
datetime    T[],prevTimeBar=0;
double      H[],L[];
#define     HL(a, b) (a+b)/2
//--- class instances
CEmission      EnvMa(0,300);
PointEmission  pEmission;
//--- drawing styles for points of emission
#define     COLOR_UPPER  C'51,255,255'
#define     COLOR_LOWER  C'0,51,255'
#define     COLOR_MA     C'255,51,255'
color       colorPoint[]={COLOR_UPPER,COLOR_LOWER,COLOR_MA};
CodeColor   styleUpper={158,COLOR_UPPER,SMALL};
CodeColor   styleLower={158,COLOR_LOWER,SMALL};
CodeColor   styleMA={158,COLOR_MA,SMALL};
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   ArraySetAsSeries(T,true);
   ArraySetAsSeries(H,true);
   ArraySetAsSeries(L,true);
//---
   int size=ArraySize(MA);
   ArrayResize(handle_MA,size);
//--- create a pointer to the object - the iMA indicator
   for(int i=0; i<size; i++)
     {
      handle_MA[i]=iMA(NULL,0,MA[i],0,MODE_SMA,PRICE_MEDIAN);
      //--- if an error occurs when creating the object, print the message
      if(handle_MA[i]<0)
        {
         Print("The iMA object[",MA[i],"] has not been created: Error = ",GetLastError());
         //--- forced program termination
         return(-1);
        }
     }
//---
   size=ArraySize(ENV);
   ArrayResize(handle_Envelopes,size);
//--- create a pointer to the object - the iEnvelopes indicator
   for(int i=0; i<size; i++)
     {
      handle_Envelopes[i]=iEnvelopes(NULL,0,ma_period,0,MODE_SMA,PRICE_MEDIAN,ENV[i]);
      //--- if an error occurs when creating the object, print the message
      if(handle_Envelopes[i]<0)
        {
         Print("The iEnvelopes object[",ENV[i],"] has not been created: Error = ",GetLastError());
         //--- forced program termination
         return(-1);
        }
     }
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- market data
   CopyTime(NULL,0,0,2,T);
   CopyHigh(NULL,0,0,2,H);
   CopyLow(NULL,0,0,2,L);
//--- fill the declared arrays with current values from all indicator buffers
   string name;
   uint GTC=GetTickCount();
//---- indicator buffers
   double   ibMA[],ibMA1[];      // arrays for the iMA indicator
   double   ibEnvelopesUpper[];  // array for the iEnvelopes indicator (UPPER_LINE)
   double   ibEnvelopesLower[];  // array for the iEnvelopes indicator (LOWER_LINE)
   for(int i=ArraySize(handle_MA)-1; i>=0; i--)
     {
      if(!CopyBufferAsSeries(handle_MA[i],0,0,2,true,ibMA))
         return;
      //---
      for(int j=ArraySize(handle_Envelopes)-1; j>=0; j--)
        {
         if(!GetEnvelopesBuffers(handle_Envelopes[j],0,2,ibEnvelopesUpper,ibEnvelopesLower,true))
            return;
         //--- find the intersection point of the iEnvelopes(UPPER_LINE) and iMA indicators
         pEmission=EnvMa.CalcPoint(ibEnvelopesUpper[1],ibEnvelopesUpper[0],ibMA[1],ibMA[0],T[0]);
         if(pEmission.real) // if the intersection point is found, draw it in the chart
           {
            name="iEnvelopes(UPPER_LINE)"+(string)j+"=iMA"+(string)i+(string)GTC;
            EnvMa.CreatePoint(name,pEmission,styleUpper);
           }
         //--- find the intersection point of the iEnvelopes(LOWER_LINE) and iMA indicators
         pEmission=EnvMa.CalcPoint(ibEnvelopesLower[1],ibEnvelopesLower[0],ibMA[1],ibMA[0],T[0]);
         if(pEmission.real) // if the intersection point is found, draw it in the chart
           {
            name="iEnvelopes(LOWER_LINE)"+(string)j+"=iMA"+(string)i+(string)GTC;
            EnvMa.CreatePoint(name,pEmission,styleLower);
           }
        }
      //---
      for(int j=ArraySize(handle_MA)-1; j>=0; j--)
        {
         if(i!=j)
           {
            if(!CopyBufferAsSeries(handle_MA[j],0,0,2,true,ibMA1))
               return;
            //--- find the intersection point of the iMA and iMA indicators
            pEmission=EnvMa.CalcPoint(ibMA1[1],ibMA1[0],ibMA[1],ibMA[0],T[0]);
            if(pEmission.real) // if the intersection point is found, draw it in the chart
              {
               name="iMA"+(string)j+"=iMA"+(string)i+(string)GTC;
               EnvMa.CreatePoint(name,pEmission,styleMA);
              }
           }
        }
     }
//--- deletion of the graphical objects of emission not to stuff the chart
   if(T[0]>prevTimeBar) // delete once per bar
     {
      int  total=ObjectsTotal(0,0,-1);
      prevTimeBar=T[0];
      for(int obj=total-1;obj>=0;obj--)
        {
         string obj_name=ObjectName(0,obj,0,OBJ_TEXT);
         datetime obj_time=(datetime)ObjectGetInteger(0,obj_name,OBJPROP_TIME);
         if(obj_time<T[0])
            ObjectDelete(0,obj_name);
        }
      Comment("Emission © DC2008       Objects = ",total);
     }
//---
  }

I am not going to dwell on every detail of this Expert Advisor. The main thing to note here is that to plot the emission, we use a CEmission class instance responsible for the calculation and display of intersection points of any two lines.

//+------------------------------------------------------------------+
//|                                                     Emission.mqh |
//|                                           Copyright 2013, DC2008 |
//|                           https://www.mql5.com/ru/users/DC2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2013, DC2008"
#property link      "https://www.mql5.com/ru/users/DC2008"
#property version   "1.00"
#define  BIG   7    // point size
#define  SMALL 3    // point size
//+------------------------------------------------------------------+
//| pMABB structure                                                  |
//+------------------------------------------------------------------+
struct PointEmission
  {
   double            x;       // X-coordinate of the time point
   double            y;       // Y-coordinate of the price point
   datetime          t;       // t-coordinate of the point's time
   bool              real;    // whether the point exists
  };
//+------------------------------------------------------------------+
//| CodeColor structure                                              |
//+------------------------------------------------------------------+
struct CodeColor
  {
   long              Code;    // point symbol code 
   color             Color;   // point color
   int               Width;   // point size
  };
//+------------------------------------------------------------------+
//| Base class for emissions                                         |
//+------------------------------------------------------------------+
class CEmission
  {
private:
   int               sec;
   int               lim_Left;   // limiting range of visibility in bars
   int               lim_Right;  // limiting range of visibility in bars

public:
   PointEmission     CalcPoint(double   y1,  // Y-coordinate of straight line 1 on bar [1]
                               double   y0,  // Y-coordinate of straight line 1 on bar [0]
                               double   yy1, // Y-coordinate of straight line 2 on bar [1] 
                               double   yy0, // Y-coordinate of straight line 2 on bar [0]
                               datetime t0   // t-coordinate of the current bar Time[0]
                               );
   bool              CreatePoint(string name,            // point name
                                 PointEmission &point,   // coordinates of the point
                                 CodeColor &style);      // point drawing style
                                 CEmission(int limitLeft,int limitRight);
                    ~CEmission();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CEmission::CEmission(int limitLeft,int limitRight)
  {
   sec=PeriodSeconds();
   lim_Left=limitLeft;
   lim_Right=limitRight;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CEmission::~CEmission()
  {
  }
//+------------------------------------------------------------------+
//| The CalcPoint method of the CEmission class                      |
//+------------------------------------------------------------------+
PointEmission CEmission::CalcPoint(double   y1, // Y-coordinate of straight line 1 on bar [1]
                                   double   y0, // Y-coordinate of straight line 1 on bar [0]
                                   double   yy1,// Y-coordinate of straight line 2 on bar [1]
                                   double   yy0,// Y-coordinate of straight line 2 on bar [0]
                                   datetime t0  // t-coordinate of the current bar Time[0]
                                   )
  {
   PointEmission point={NULL,NULL,NULL,false};
   double y0y1=y0-y1;
   double y1yy1=y1-yy1;
   double yy0yy1=yy0-yy1;
   double del0=yy0yy1-y0y1;
   if(MathAbs(del0)>0)
     {
      point.x=y1yy1/del0;
      if(point.x<lim_Left || point.x>lim_Right) return(point);
      point.y=y1+y0y1*y1yy1/del0;
      if(point.y<0) return(point);
      point.t=t0+(int)(point.x*sec);
      point.real=true;
      return(point);
     }
   return(point);
  }
//+------------------------------------------------------------------+
//| The CreatePoint method of the CEmission class                    |
//+------------------------------------------------------------------+
bool CEmission::CreatePoint(string name,            // point name
                            PointEmission &point,  // coordinates of the point
                            CodeColor &style)      // point drawing style
  {
   if(ObjectCreate(0,name,OBJ_TEXT,0,0,0))
     {
      ObjectSetString(0,name,OBJPROP_FONT,"Wingdings");
      ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_CENTER);
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,style.Width);
      ObjectSetString(0,name,OBJPROP_TEXT,CharToString((uchar)style.Code));
      ObjectSetDouble(0,name,OBJPROP_PRICE,point.y);
      ObjectSetInteger(0,name,OBJPROP_TIME,point.t);
      ObjectSetInteger(0,name,OBJPROP_COLOR,style.Color);
      return(true);
     }
   return(false);
  }

It should be noted that emission points are depicted by means of graphical objects such as Text. First of all, it comes from the fact that object anchors should be aligned to the symbol center. Second, you can vary object size over a wide range. These point properties offer a great potential for getting complex emissions.

Fig. 2. Original emission of the iMA and iEnvelopes indicators

Fig. 2. Original emission of the iMA and iEnvelopes indicators

 

Integral Characteristics of Emissions

So, after placing the proposed Expert Advisor on the chart, we got a lot of points in different colors (see Fig. 2):

  • Aqua - intersections of iMA and iEnvelopes, UPPER_LINE buffer.
  • Blue - intersections of iMA and iEnvelopes, LOWER_LINE buffer.
  • Magenta - intersections of iMA and iMA.

This chaos cannot be used in automated trading. We need signals, levels and other quantitative market characteristics, while here we only get visual images for meditation and chiromancy and no numbers whatsoever.

Integral characteristics of emissions serve to generalize data obtained as a result of indicator emissions. 

The need for integral characteristics of emissions is also driven by the fact that they give opportunities for market research using new types of indicators: integral channels, lines, levels, signals, etc. To determine the most typical emission values, we will start small and calculate the mean price for every point type to further draw horizontal lines through them as shown below:

Fig. 3. Horizontal lines of the mean price for every point type

Fig. 3. Horizontal lines of the mean price for every point type

For this purpose, we will add a few additional code blocks to the existing code. To the data section:

//--- arrays for calculation and display of integral characteristics of emissions
#define     NUMBER_TYPES_POINT   3
double      sum[NUMBER_TYPES_POINT],sumprev[NUMBER_TYPES_POINT];
datetime    sum_time[NUMBER_TYPES_POINT];
int         n[NUMBER_TYPES_POINT],W[NUMBER_TYPES_POINT];
color       colorLine[]={clrAqua,clrBlue,clrMagenta};

To the OnTick() module:

//--- calculation of integral characteristics of emissions
   ArrayInitialize(n,0);
   ArrayInitialize(sum,0.0);
   ArrayInitialize(sum_time,0.0);
   for(int obj=total-1;obj>=0;obj--)
     {
      string   obj_name=ObjectName(0,obj,0,OBJ_TEXT);
      datetime obj_time=(datetime)ObjectGetInteger(0,obj_name,OBJPROP_TIME);
      if(obj_time>T[0])
        {
         color    obj_color=(color)ObjectGetInteger(0,obj_name,OBJPROP_COLOR);
         double   obj_price=ObjectGetDouble(0,obj_name,OBJPROP_PRICE);
         for(int i=ArraySize(n)-1; i>=0; i--)
            if(obj_color==colorPoint[i])
              {
               n[i]++;
               sum[i]+=obj_price;
               sum_time[i]+=obj_time;
              }
        }
     }
//--- displaying integral characteristics of emissions
   for(int i=ArraySize(n)-1; i>=0; i--)
     {
      if(n[i]>0)
        {
         name="H.line."+(string)i;
         ObjectCreate(0,name,OBJ_HLINE,0,0,0,0);
         ObjectSetInteger(0,name,OBJPROP_COLOR,colorLine[i]);
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DASHDOT);
         ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
         ObjectSetDouble(0,name,OBJPROP_PRICE,sum[i]/n[i]);
        }
     }

We move on. Let us now calculate the mean time value for every point set and mark it on the corresponding line of the mean price (see Fig. 4). Thus, we have obtained the first quantitative characteristics of emissions which are never static, always moving in space.

The chart only shows their momentary positions. We need to somehow keep them fixed in history to be able to study them afterwards. So far it is still unclear how this can be done and we need to give it careful consideration... In the meantime, we will make further improvement and display the number of points involved in the calculation next to the markers in the chart. These are some sort of weights of the obtained characteristics that will also be useful in further analysis.

Fig. 4. Markers at intersection points of the mean price and mean time

Fig. 4. Markers at intersection points of the mean price and mean time

However, for convenience of the analysis, we will use their percentage ratios. Since the main emission points are the ones resulting from intersections of the iMA and iEnvelopes indicators, we will consider their sum to be 100%. Let us see what we have now:

Fig. 5. Percentage ratio for every type of emission points

Fig. 5. Percentage ratio for every type of emission points

If we add up the three values, they will give more than 100% in total. The value of 34.4 displayed in magenta is the property of intersection points of iMA and iMA at a certain point of time, i.e. the indicator has crossed itself, but with different input data. In this case, this is a reference value and we might later think of the way how it can be used in market analysis.

However, another problem arises as we get percentage ratios of the number of points: how can we also fix percentage values of emission characteristics in history, especially as they do vary, too?!

 

Graphical Analysis

Although we now have the integral characteristics of emissions, we are still not close enough to the analysis and development of a trading strategy based on the data obtained. However, a careful reader must have already spotted a solution to this problem (see Fig. 1). The solution is as follows: I propose to draw integral curves using different thickness that would be proportional to the percentage ratio of the main emission points.

The current portion of the curve will be plotted along the mean price line between the current and previous bar, keeping in mind that these coordinates are in fact taken from the future. It is some sort of leading integral channel of indicator emissions. I know it sounds very confusing indeed... And you must be thinking whether you should read on. But I hope this will get more and more interesting as we proceed.

Fig. 6. Integral channel of indicator emissions

Fig. 6. Integral channel of indicator emissions

So we seem to have found some use for the "iMA & iMA" emission (displayed in magenta in the chart). And we got a new indicator - the integrated moving average.

Now we go back to the code of the Expert Advisor to see what changes took place in the OnTick() module:

//--- displaying integral characteristics of emissions
   ArrayInitialize(W,10);
   W[ArrayMaximum(n)]=20;
   W[ArrayMinimum(n)]=3;
   for(int i=ArraySize(n)-1; i>=0; i--)
     {
      if(n[i]>0)
        {
         //--- horizontal lines of mean prices
         name="H.line."+(string)i;
         ObjectCreate(0,name,OBJ_HLINE,0,0,0,0);
         ObjectSetInteger(0,name,OBJPROP_COLOR,colorLine[i]);
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DASHDOT);
         ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
         ObjectSetDouble(0,name,OBJPROP_PRICE,sum[i]/n[i]);
         //--- markers
         name="P."+(string)i;
         ObjectCreate(0,name,OBJ_TEXT,0,0,0);
         ObjectSetString(0,name,OBJPROP_FONT,"Wingdings");
         ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_CENTER);
         ObjectSetInteger(0,name,OBJPROP_FONTSIZE,17);
         ObjectSetString(0,name,OBJPROP_TEXT,CharToString(163));
         ObjectSetInteger(0,name,OBJPROP_COLOR,colorLine[i]);
         ObjectSetDouble(0,name,OBJPROP_PRICE,sum[i]/n[i]);
         ObjectSetInteger(0,name,OBJPROP_TIME,sum_time[i]/n[i]);
         //--- integral curves
         name="T"+(string)i+".line"+(string)T[1];
         ObjectCreate(0,name,OBJ_TREND,0,0,0);
         ObjectSetInteger(0,name,OBJPROP_COLOR,colorLine[i]);
         ObjectSetInteger(0,name,OBJPROP_WIDTH,W[i]);
         if(sumprev[i]>0)
           {
            ObjectSetDouble(0,name,OBJPROP_PRICE,0,sumprev[i]);
            ObjectSetInteger(0,name,OBJPROP_TIME,0,T[1]);
            ObjectSetDouble(0,name,OBJPROP_PRICE,1,(sum[i]/n[i]));
            ObjectSetInteger(0,name,OBJPROP_TIME,1,T[0]);
           }
         //--- numerical values of integral characteristics
         name="Text"+(string)i+".control";
         ObjectCreate(0,name,OBJ_TEXT,0,0,0);
         ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER);
         ObjectSetInteger(0,name,OBJPROP_FONTSIZE,30);
         ObjectSetInteger(0,name,OBJPROP_COLOR,colorLine[i]);
         string str=DoubleToString((double)n[i]/(double)(n[0]+n[1])*100,1);
         ObjectSetString(0,name,OBJPROP_TEXT,str);
         ObjectSetDouble(0,name,OBJPROP_PRICE,0,(sum[i]/n[i]));
         ObjectSetInteger(0,name,OBJPROP_TIME,0,sum_time[i]/n[i]);
        }
     }

Let us continue our graphical analysis. But something is missing... It appears that we have missed another important emission characteristic. Integral curves were plotted only based on the mean prices. Yet we need to consider the mean time coordinate. Take a look at the figure below and pay special attention to the channel limits:

  • The aqua line is the upper limit of the channel.
  • The blue line is the lower limit of the channel.

We need to identify the marker that was closer in time to the zero bar.

Leading upper limit of the channel
Leading lower limit of the channel

Fig. 7. Integral characteristics leading in time. Left: leading upper limit of the channel. Right: leading lower limit of the channel.

This problem can be solved as follows: we add the price line (PRICE_MEDIAN) to the price chart and make the line change its color depending on the color of the marker (aqua or blue) that is closer to the last bar (see Fig. 7). Further, we insert the following code block to the existing code:

//---
   if(n[ArrayMinimum(n)]>0)
     {
      datetime d[2];
      for(int j=0;j<2;j++)
        {
         d[j]=sum_time[j]/n[j];
        }
      int i=ArrayMinimum(d);

      name="Price.line"+(string)T[1];
      ObjectCreate(0,name,OBJ_TREND,0,0,0);
      ObjectSetInteger(0,name,OBJPROP_WIDTH,8);
      ObjectSetDouble(0,name,OBJPROP_PRICE,0,HL(H[1],L[1]));
      ObjectSetInteger(0,name,OBJPROP_TIME,0,T[1]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,HL(H[0],L[0]));
      ObjectSetInteger(0,name,OBJPROP_TIME,1,T[0]);
      ObjectSetInteger(0,name,OBJPROP_COLOR,colorLine1[i]);
     }
//---

Now get ready for the next step. What if we try to plot emissions based on integral characteristics of original emissions, something like second-order emissions? After all, those lines also intersect each other and should, consequently, have emission points. Let us see what may come of it. Enhance the previous code block by adding the following lines of code:

      //--- emissions of integral characteristics of the original emissions
      pEmission=EnvMa.CalcPoint(sumprev[0],sum[0]/n[0],sumprev[2],sum[2]/n[2],T[0]);
      if(pEmission.real) // if the intersection point is found, draw it in the chart
        {
         name="test/up"+(string)GTC;
         EnvMa.CreatePoint(name,pEmission,styleUpper2);
        }
      pEmission=EnvMa.CalcPoint(sumprev[1],sum[1]/n[1],sumprev[2],sum[2]/n[2],T[0]);
      if(pEmission.real) // if the intersection point is found, draw it in the chart
        {
         name="test/dn"+(string)GTC;
         EnvMa.CreatePoint(name,pEmission,styleLower2);
        }

And insert the following lines into the data section:

#define     COLOR_2_UPPER  C'102,255,255'
#define     COLOR_2_LOWER  C'51,102,255'
CodeColor   styleUpper2={178,COLOR_2_UPPER,BIG};
CodeColor   styleLower2={178,COLOR_2_LOWER,BIG};

You can check the results in the figure below. We can see new points that do not suggest anything so far.

Fig. 8. Emissions of integral lines

Fig. 8. Emissions of integral lines

Integral characteristics can obviously also be calculated for new points (see Fig. 9) with their emissions plotted in the chart and so on until it becomes unfeasible!

Emission Emission
Emission Emission

Fig. 9. Integral characteristics of emissions

So we plotted everything we needed and obtained the integral characteristics of emissions. We can now proceed to their analysis and development of a trading strategy. But it still seems impossible! What is blocking us now?

 

Time Series of Emissions

Graphical analysis enables us to study integral characteristics of emissions but it is too resource intensive. If we try to run the proposed code in the visual mode of the strategy tester, the testing speed will soon drop to zero! This is due to the large number of graphical objects in the chart.

So one would naturally want to get rid of all that abundance of points and only leave the integral curves. To solve this problem, we will use special arrays (buffers).

Time series of emissions are specially arranged arrays where information on emissions is accumulated.

They differ from standard time series in that the data they contain is not sequenced by time, even though time is the key field.

Time Series of Emissions

Fig. 10. Time series of emission characteristics

These arrays are arranged in such a way that new elements are stored in empty cells or in cells filled with old values. For this purpose, we will use the CTimeEmission class. Here is how it is implemented in the code:

//+------------------------------------------------------------------+
//|                                                 TimeEmission.mqh |
//|                                           Copyright 2013, DC2008 |
//|                           https://www.mql5.com/ru/users/DC2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2013, DC2008"
#property link      "https://www.mql5.com/ru/users/DC2008"
#property version   "1.00"
//---
#include <Emission.mqh>
#define ARRMAX       64
#define ARRDELTA     8
//+------------------------------------------------------------------+
//| pIntegral structure                                              |
//+------------------------------------------------------------------+
struct pIntegral
  {
   double            y;       // Y-coordinate of the price point (mean price of the points with the same time)
   datetime          t;       // t-coordinate of the point's time
   int               n;       // n-number of points with the same time
  };
//+------------------------------------------------------------------+
//| Base class for time series of emissions                          |
//+------------------------------------------------------------------+
class CTimeEmission
  {
private:
   pIntegral         time_series_Emission[]; // time series of emission
   int               size_ts; // number of elements in time series 
   datetime           t[1];
public:
   //--- method of writing new elements to time series of emission
   void              Write(PointEmission &point);
   //--- method of reading integral characteristics of emissions
   pIntegral         Read();
                     CTimeEmission();
                    ~CTimeEmission();
  };

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CTimeEmission::CTimeEmission()
  {
   ArrayResize(time_series_Emission,ARRMAX,ARRMAX);
   size_ts=ArraySize(time_series_Emission);
   for(int i=size_ts-1; i>=0; i--)
      time_series_Emission[i].t=0;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CTimeEmission::~CTimeEmission()
  {
  }
//+------------------------------------------------------------------+
//| The Write method of the CTimeEmission class                      |
//+------------------------------------------------------------------+
void CTimeEmission::Write(PointEmission &point)
  {
   CopyTime(NULL,0,0,1,t);
   size_ts=ArraySize(time_series_Emission);
   for(int k=0;k<size_ts;k++)
     {
      if(time_series_Emission[k].t<t[0]) // find the first empty cell
        {
         if(k>size_ts-ARRDELTA)
           {   // increase the array size, if necessary
            int narr=ArrayResize(time_series_Emission,size_ts+ARRMAX,ARRMAX);
            for(int l=size_ts-1;l<narr;l++)
               time_series_Emission[l].t=0;
           }
         time_series_Emission[k].y=point.y;
         time_series_Emission[k].t=point.t;
         time_series_Emission[k].n=1;
         return;
        }
      if(time_series_Emission[k].t==point.t) // find the first similar cell
        {
         time_series_Emission[k].y=(time_series_Emission[k].y*time_series_Emission[k].n+point.y)/(time_series_Emission[k].n+1);
         time_series_Emission[k].n++;
         return;
        }
     }
  }
//+------------------------------------------------------------------+
//| The Read method of the CTimeEmission class                       |
//+------------------------------------------------------------------+
pIntegral CTimeEmission::Read()
  {
   CopyTime(NULL,0,0,1,t);
   pIntegral property_Emission={0.0,0,0};
   size_ts=ArraySize(time_series_Emission);
   for(int k=0;k<size_ts;k++)
     {
      if(time_series_Emission[k].t>=t[0])
        {
         property_Emission.y+=time_series_Emission[k].y*time_series_Emission[k].n;
         property_Emission.t+=(time_series_Emission[k].t-t[0])*time_series_Emission[k].n;
         property_Emission.n+=time_series_Emission[k].n;
        }
     }
   if(property_Emission.n>0)
     {
      property_Emission.y=property_Emission.y/property_Emission.n;
      property_Emission.t=property_Emission.t/property_Emission.n+t[0];
     }
   return(property_Emission);
  }

Here we can see the implementation of two class methods: writing emission points to time series and reading values of integral characteristics of emissions.

 

Parsimonious Calculation of Integral Characteristics

Now that we have the time series of emissions, we can start creating a parsimonious algorithm for the calculation of integral characteristics to further develop a trading strategy. Let us update the original Expert Advisor:

//+------------------------------------------------------------------+
//|                                   emission_of_MA_envelope_ts.mq5 |
//|                                           Copyright 2013, DC2008 |
//|                           https://www.mql5.com/ru/users/DC2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2013, DC2008"
#property link      "https://www.mql5.com/ru/users/DC2008"
#property version   "1.00"
//---
#include <GetIndicatorBuffers.mqh>
#include <Emission.mqh>
#include <TimeEmission.mqh>
//--- number of point types
#define     NUMBER_TYPES_POINT   3
//--- array for storing the iMA indicator periods
int      MA[]={4,7,11,19,31,51,85};
//--- external variable for storing averaging period of the iEnvelopes indicator
input int ma_period=140; // averaging period of the iEnvelopes indicator
//--- array for storing deviations of the iEnvelopes indicator
double   ENV[]={0.01,0.0165,0.0273,0.0452,0.0747,01234,0.204,0.3373,0.5576,0.9217,1.5237};
//--- array for storing pointers to the iMA indicator
int      handle_MA[];
//--- array for storing pointers to the iEnvelopes indicator
int      handle_Envelopes[];
//--- market data
datetime    T[],prevTimeBar=0;
double      H[],L[];
#define     HL(a, b) (a+b)/2
//--- class instances
CEmission      EnvMa(0,200);
PointEmission  pEmission;
CTimeEmission  tsMA[NUMBER_TYPES_POINT];
pIntegral      integral[NUMBER_TYPES_POINT];
//--- drawing styles for points of emission
#define     DEL            500
//--- arrays for calculation and display of integral characteristics of emissions
double      sumprev[NUMBER_TYPES_POINT];
int         n[NUMBER_TYPES_POINT],W[NUMBER_TYPES_POINT];
color       colorLine[]={clrAqua,clrBlue,clrMagenta};
int         fontPoint[]={30,30,30};
int         fontMarker[]={16,16,16};
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   ArraySetAsSeries(T,true);
   ArraySetAsSeries(H,true);
   ArraySetAsSeries(L,true);
   ArrayInitialize(sumprev,0.0);
//---
   int size=ArraySize(MA);
   ArrayResize(handle_MA,size);
//--- create a pointer to the object - the iMA indicator
   for(int i=0; i<size; i++)
     {
      handle_MA[i]=iMA(NULL,0,MA[i],0,MODE_SMA,PRICE_MEDIAN);
      //--- if an error occurs when creating the object, print the message
      if(handle_MA[i]<0)
        {
         Print("The iMA object[",MA[i],"] has not been created: Error = ",GetLastError());
         //--- forced program termination
         return(-1);
        }
     }
//+------------------------------------------------------------------+
   size=ArraySize(ENV);
   ArrayResize(handle_Envelopes,size);
//--- create a pointer to the object - the iEnvelopes indicator
   for(int i=0; i<size; i++)
     {
      handle_Envelopes[i]=iEnvelopes(NULL,0,ma_period,0,MODE_SMA,PRICE_MEDIAN,ENV[i]);
      //--- if an error occurs when creating the object, print the message
      if(handle_Envelopes[i]<0)
        {
         Print("The iEnvelopes object[",ENV[i],"] has not been created: Error = ",GetLastError());
         //--- forced program termination
         return(-1);
        }
     }
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- market data
   CopyTime(NULL,0,0,2,T);
   CopyHigh(NULL,0,0,2,H);
   CopyLow(NULL,0,0,2,L);
//--- fill the declared arrays with current values from all indicator buffers
   string name;
   uint GTC=GetTickCount();
//---- indicator buffers
   double   ibMA[],ibMA1[];      // arrays for the iMA indicator
   double   ibEnvelopesUpper[];  // array for the iEnvelopes indicator (UPPER_LINE)
   double   ibEnvelopesLower[];  // array for the iEnvelopes indicator (LOWER_LINE)
   for(int i=ArraySize(handle_MA)-1; i>=0; i--)
     {
      if(!CopyBufferAsSeries(handle_MA[i],0,0,2,true,ibMA))
         return;
      //---
      for(int j=ArraySize(handle_Envelopes)-1; j>=0; j--)
        {
         if(!GetEnvelopesBuffers(handle_Envelopes[j],0,2,ibEnvelopesUpper,ibEnvelopesLower,true))
            return;
         //--- find the intersection point of the iEnvelopes(UPPER_LINE) and iMA indicators
         pEmission=EnvMa.CalcPoint(ibEnvelopesUpper[1],ibEnvelopesUpper[0],ibMA[1],ibMA[0],T[0]);
         if(pEmission.real) // if the intersection point is found, add it to the time series of emission
            tsMA[0].Write(pEmission);
         //--- find the intersection point of the iEnvelopes(LOWER_LINE) and iMA indicators
         pEmission=EnvMa.CalcPoint(ibEnvelopesLower[1],ibEnvelopesLower[0],ibMA[1],ibMA[0],T[0]);
         if(pEmission.real) // if the intersection point is found, add it to the time series of emission
            tsMA[1].Write(pEmission);
        }
      //---
      for(int j=ArraySize(handle_MA)-1; j>=0; j--)
        {
         if(i!=j)
           {
            if(!CopyBufferAsSeries(handle_MA[j],0,0,2,true,ibMA1))
               return;
            //--- find the intersection point of the iMA and iMA indicators
            pEmission=EnvMa.CalcPoint(ibMA1[1],ibMA1[0],ibMA[1],ibMA[0],T[0]);
            if(pEmission.real) // if the intersection point is found, add it to the time series of emission
               tsMA[2].Write(pEmission);
           }
        }
     }
//--- deletion of the graphical objects of emission not to stuff the chart
   if(T[0]>prevTimeBar)
     {
      prevTimeBar=T[0];
      //---
      for(int i=ArraySize(n)-1; i>=0; i--)
         sumprev[i]=integral[i].y;
      //---
      for(int obj=ObjectsTotal(0,0,-1)-1;obj>=0;obj--)
        {
         string obj_name=ObjectName(0,obj,0,OBJ_TREND);
         datetime obj_time=(datetime)ObjectGetInteger(0,obj_name,OBJPROP_TIME);
         if(obj_time<T[0]-DEL*PeriodSeconds())
            ObjectDelete(0,obj_name);
        }
      Comment("Emission © DC2008   Graphical objects = ",ObjectsTotal(0,0,-1));
     }
//--- calculation of integral characteristics of emission
   for(int i=ArraySize(n)-1; i>=0; i--)
      integral[i]=tsMA[i].Read();
//--- displaying integral characteristics of emission
   ArrayInitialize(W,5);
   if(integral[0].n>integral[1].n)
     {
      W[0]=20;
      W[1]=10;
     }
   else
     {
      W[0]=10;
      W[1]=20;
     }
   for(int i=ArraySize(n)-1; i>=0; i--)
     {
      //--- horizontal lines of mean prices
      name="H.line."+(string)i;
      ObjectCreate(0,name,OBJ_HLINE,0,0,0,0);
      ObjectSetInteger(0,name,OBJPROP_COLOR,colorLine[i]);
      ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DASHDOT);
      ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
      ObjectSetDouble(0,name,OBJPROP_PRICE,integral[i].y);
      //--- markers
      name="P."+(string)i;
      ObjectCreate(0,name,OBJ_TEXT,0,0,0);
      ObjectSetString(0,name,OBJPROP_FONT,"Wingdings");
      ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_CENTER);
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,fontMarker[i]);
      ObjectSetString(0,name,OBJPROP_TEXT,CharToString(163));
      ObjectSetInteger(0,name,OBJPROP_COLOR,colorLine[i]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,integral[i].y);
      ObjectSetInteger(0,name,OBJPROP_TIME,integral[i].t);
      //--- integral curves
      name="T"+(string)i+".line"+(string)T[1];
      ObjectCreate(0,name,OBJ_TREND,0,0,0);
      ObjectSetInteger(0,name,OBJPROP_COLOR,colorLine[i]);
      ObjectSetInteger(0,name,OBJPROP_WIDTH,W[i]);
      if(sumprev[i]>0)
        {
         ObjectSetDouble(0,name,OBJPROP_PRICE,0,sumprev[i]);
         ObjectSetInteger(0,name,OBJPROP_TIME,0,T[1]);
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,integral[i].y);
         ObjectSetInteger(0,name,OBJPROP_TIME,1,T[0]);
        }
      //--- numerical values of integral characteristics
      if(integral[0].n+integral[1].n>0)
        {
         name="Text"+(string)i+".control";
         ObjectCreate(0,name,OBJ_TEXT,0,0,0);
         ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER);
         ObjectSetInteger(0,name,OBJPROP_FONTSIZE,fontPoint[i]);
         ObjectSetInteger(0,name,OBJPROP_COLOR,colorLine[i]);
         string str=DoubleToString((double)integral[i].n/(double)(integral[0].n+integral[1].n)*100,1);
         ObjectSetString(0,name,OBJPROP_TEXT,str);
         ObjectSetDouble(0,name,OBJPROP_PRICE,0,integral[i].y);
         ObjectSetInteger(0,name,OBJPROP_TIME,0,integral[i].t);
        }
     }
  }

The code has become shorter, while the calculation speed has increased. Now you can test and optimize your trading robots without visualization! 

 

Use of Integral Characteristics in Trading

Integral characteristics can be used as a generator of signals for:

  • channel breakthrough,
  • intersection with each other or the price,
  • change in the direction.
For example, the figure below shows how integral characteristics of emissions can hypothetically be used at intersections with the price. Sell signals are generated when the price is crossed upward by the blue curve and Buy signals are generated at the downward crossover of the price with the aqua curve.

Fig.  11. Trading signals at intersections of integral characteristics of emissions

Fig.  11. Trading signals at intersections of integral characteristics of emissions

 

Conclusion

  1. The calculation of integral characteristics of indicator emissions provides new tools and methods for market analysis (time series).
  2. Using time series, we have managed to increase the speed of calculations of integral characteristics.
  3. And it has opened up a possibility for us to develop automated trading strategies that use emissions.

Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/610

Last comments | Go to discussion (2)
Sergey Pavlov
Sergey Pavlov | 22 Feb 2013 at 17:20

Take a look video

 

Sergey Pavlov
Sergey Pavlov | 21 Mar 2013 at 08:33

Video on emission

 

MQL5 Cookbook: Using Different Print Modes MQL5 Cookbook: Using Different Print Modes
This is the first article of the MQL5 Cookbook series. I will start with simple examples to allow those who are taking their first steps in programming to gradually become familiar with the new language. I remember my first efforts at designing and programming trading systems which I may say was quite difficult, given the fact that it was the first programming language in my life. However, it turned out to be easier than I thought and it only took me a few months before I could develop a fairly complex program.
MetaTrader 4 Expert Advisor exchanges information with the outside world MetaTrader 4 Expert Advisor exchanges information with the outside world
A simple, universal and reliable solution of information exchange between МetaТrader 4 Expert Advisor and the outside world. Suppliers and consumers of the information can be located on different computers, the connection is performed through the global IP addresses.
MQL5 Cookbook: Getting Position Properties MQL5 Cookbook: Getting Position Properties
In this article, we will create a script that gets all position properties and displays them to the user in a dialog box. Upon running the script, you will be able to select out of two modes available in the drop-down list in the external parameters: to either view position properties only on the current symbol or to view position properties on all symbols.
Change Expert Advisor Parameters From the User Panel "On the Fly" Change Expert Advisor Parameters From the User Panel "On the Fly"
This article provides a small example demonstrating the implementation of an Expert Advisor whose parameters can be controlled from the user panel. When changing the parameters "on the fly", the Expert Advisor writes the values obtained from the info panel to a file to further read them from the file and display accordingly on the panel. This article may be relevant to those who trade manually or in semi-automatic mode.