Русский 中文 Español Deutsch 日本語 Português
Visual Optimization of Indicator and Signal Profitability

Visual Optimization of Indicator and Signal Profitability

MetaTrader 4Examples | 21 November 2013, 09:28
8 641 2
Sergey Kravchuk
Sergey Kravchuk

This article is a continuation and development of my previous article "Visual Testing of Profitability of Indicators and Alerts". Having added some interactivity to the parameter changing process and having reworked the study objectives, I have managed to get a new tool that does not only show the prospective trade results based on the signals used but also allows you to immediately get a layout of deals, balance chart and the end result of trading by moving virtual sliders that act as controls for signal parameter values in the main chart.


Introduction

First off, a word of warning for all grail hunters and faultfinders out there: just like the tool described in the previous article, this one is, by no means, a magic wand that will help you avoid losses altogether and only gain profit. It is just a tool that allows you to quickly calculate and display calculation results in a convenient visual form. The indicator drawings should rather be considered as food for trader's thought about trading tactics and signals used. This indicator can, nevertheless, become a quite handy tool in short-term (especially intraday) trading.

The idea to rework the old indicator and give it a new form arose from a naive and clearly sincere comment that was posted together with the description of an Expert Advisor in one of our forum threads: "It's been doing well over 2009. So far, I'm satisfied. To facilitate signal monitoring I have..." The Expert Advisor really did perform well trading the symbol and time frame as its author specified. But any attempt to change the parameters would turn a good performer into an undisguised killer that was losing the entire deposit in almost an instant. And although the author of the Expert Advisor did not propose to use it for live trading, the thought got stuck in my mind - how to not only check the prospective trade results but also assess the trading behavior, the stability of results, if you like. I wanted to make it quick and obvious to be able to visually demonstrate ineffectiveness of some signals and trading tactics. In addition, another thought was haunting my mind: the Expert Advisor could yield a decent profit trading with the existing parameters. I was thinking that if I could trade like that manually, I would be very happy with the results. What if the results could be improved further? Generally speaking, the problem seemed interesting "in itself" and I started looking into the ways to solve it.


Problem Definition

It was clear that the solution had to be based on my indicator from the article "Visual Testing of Profitability of Indicators and Alerts", which meant that the major part of work had already been done and it only required an upgrade. What did the previous implementation lack? Clearly, it was a convenient mechanism of parameter selection. It would be very painstaking to every time have to check the indicator parameters, make changes and then analyze the resulting figure and lines of orders using historical data. This is understandable - the original indicator was intended for checking ready signals, while in this case something else was required. What could be that? Those of you who actively use the Strategy Tester in MT must have a very clear idea about it. To assess trade results, we, above all else, need balance charts and the possibility to modify the initial parameters for calculation of signal values.


Balance Chart

This turned out to be the easiest thing to do. Since all operations are plotted in the chart and the results for each of them are known, all we need is to get them all and sum as running total. An increase (or decrease) chart can be displayed in a separate window. To satisfy the needs of those who like to analyze the results expressed in monetary value or points, we should provide for an appropriate parameter.


Triangular Slider Emulation

It was much more difficult to adjust the automated trading-oriented platform to the tasks that require interaction with the trader. The standard resources allowed me to implement only one sequence of actions: selection of an indicator, call of its properties, modification of parameters and recalculation. Such iteration over at least a dozen of variants takes a great deal of time. By the time you get the results of the tenth variant, you have already completely forgotten the results of the first one.

Since the standard GUI elements of the operating system (buttons, checkboxes, combo boxes, sliders, etc.) are not available for use in indicators and EAs (unless you are going to use DLL libraries written in other programming languages), I had to look for an adequate replacement. I decided to adapt a triangle, an object that can easily be added to any chart, for use as a standard slider that would change its value from a given minimum to a given maximum. If two of its vertices are arranged vertically (or horizontally), while the third one is moved between them, we can get a quite adequate linear model of a slider. A vertical position of such a slider allows getting a smoother (continuous) flow of values. And in order to be able to change several parameters at the same time, we need to implement the possibility to work with several different triangles separately.

A special function has been written for working with triangular sliders:

double ParamValue(int ParamNo, string ParamName, double ParamValue, double vMin, double vMax, color clr)
{
  double Triangle[3],vCur, WMax, WMin; datetime tt1, tt2;

  // if there is no triangle, create it
  if(ObjectFind(ParamName) < 0)
  {
    // determine the chart boundaries in the current scale vertically
    WMax = WindowPriceMax();  WMin = WindowPriceMin();

    // calculate the coordinates of points by time...
    tt1 = Time[0] + Period()*60*ParamNo*20; tt2 = tt1 + Period()*60*20;

    // ... and "price"
    vCur = WMin + (ParamValue - vMin) * (WMax - WMin) / (vMax - vMin);

    // create an object and fill it with the color specified in the parameters
    ObjectCreate(ParamName,OBJ_TRIANGLE, 0, tt1,WMax, tt2,vCur, tt1,WMin);
    ObjectSet(ParamName,OBJPROP_COLOR,clr);
  }

  // the triangle exists - get its coordinates
  Triangle[0] = ObjectGet(ParamName,OBJPROP_PRICE1);
  Triangle[1] = ObjectGet(ParamName,OBJPROP_PRICE2);
  Triangle[2] = ObjectGet(ParamName,OBJPROP_PRICE3);

  // arrange the vertices in the order of "increase"
  ArraySort(Triangle);

  // convert the midpoint coordinate to the scale of real values between vMin and vMax
  vCur = vMin + (Triangle[1] - Triangle[0]) / (Triangle[2] - Triangle[0]) * (vMax - vMin);

  // write the value to the object comment
  ObjectSetText(ParamName,DoubleToStr(vCur,2));

  // return the value to the main module
  return(vCur);

}

The comments provide a quite detailed description of the function algorithm so we will limit ourselves to the explanation of the purpose of function parameters.

ParamNo – number of the parameter used (it determines how the triangles move relative to each other along the time axis). You can afterwards change their positions and sizes, as required.

ParamName – parameter name which must be unique to differentiate between the triangles and be meaningful to be displayed in the tooltip.

ParamValue – the initial parameter value (it is used to correctly place the middle vertex between the minimum value vMin and the maximum value vMax which will be used in optimization).

сlr – color of the triangle.

The following code is used for working with triangles:

MAPeriod  = ParamValue(0, SliderPrefix+"MA Period", MAPeriod, 5, 35, Blue);

RSIPeriod = ParamValue(1, SliderPrefix+"RSI Period", RSIPeriod, 2, 25, Red);

RSILevel  = ParamValue(2, SliderPrefix+"RSI Level", RSILevel, 5, 95, Orange);

The obtained values are further used to calculate the corresponding signals.

To illustrate the indicator development and debugging, I have chosen an indicator proposed by the user Helen (HI_Line_E_RSI_MA.mq4) which basically inspired me to do all this. The signal calculation block had to be rewritten to be suitable for use within my indicator, although the code remained essentially the same:

// fill signal arrays with values and count their number
  for(i=DisplayBars;i>=0;i--)
  {

    double t1=iRSI(NULL,0,RSIPeriod,MAPrice,i+1);
    double t11=iRSI(NULL,0,RSIPeriod,MAPrice,i+2);

    double t2=iMA(NULL,0,MAPeriod,0,MAMode,MAPrice,i+1);
    double t3=iMA(NULL,0,MAPeriod*3,0,MAMode,MAPrice,i+1);

    if (t1>RSILevel&&t11<RSILevel&&t1>t11&&t1>RSILevel&&t2>t3) {sBuy[i]=1; sBuyCnt++;}
    if (t1<(100-RSILevel)&&t11>(100-RSILevel)&&t1<t11&&t1<(100-RSILevel)) {sCloseBuy[i]=1; sBuyCloseCnt++;}

    if (t1<(100-RSILevel)&&t11>(100-RSILevel)&&t1<t11&&t1<(100-RSILevel)&&t2<t3) {sSell[i]=1; sSellCnt++; }
    if (t1>RSILevel&&t11<RSILevel&&t1>t11&&t1>RSILevel) {sCloseSell[i]=1; sSellCloseCnt++;}
  }

I have decided not to have the fourth parameter - the period enlargement ratio for getting the long MA - just yet. So I simply use the MA that is three times longer than the main one (MAPeriod*3). If you wish, you can easily implement it on your own.

Another addition to the original indicator is the block of balance curve calculation and display

// plot the balance chart
// get profit values one by one and add them up 
i2 = DisplayBars; bBallance[i2] = 0; 
for(i=DisplayBars-1;i>=0;i--) { if(bBallance[i] != 0) { bBallance[i2] = bBallance[i2+1] + bBallance[i]; i2--;  } }
double multiplier; if(ProfitInPoints) multiplier = 1; else multiplier = ToCurrency;
for(i=0;i<DisplayBars-2;i++) bBallance[i] = bBallance[i+i2+1] * multiplier;
SetIndexStyle(0,DRAW_HISTOGRAM,STYLE_SOLID,ProfitWidth,Blue);

The first loop adds the earlier set operation profit values accumulatively, while the second one gets them together in a single "stack" without any spaces between them. On a side note, there is therefore no point in looking for any correspondence between the upper and lower charts: vertical lines of opening and closing orders are not related to the lower chart in any way (which is why they are disabled by default).

As a result, the text of the indicator is as follows:

/*///——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

    IndicatorOptimizer.mq4 
  
    Visual testing of profitability of indicators and alerts
    
    Copyright © 2009, Sergey Kravchuk, http://forextools.com.ua

/*///——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
#property copyright "Copyright © 2006-2009, Sergey Kravchuk. http://forextools.com.ua"
#property link      "http://forextools.com.ua"

#property indicator_separate_window
#property indicator_buffers 1
#property indicator_level1 0

// parameters for displaying chart elements
extern bool   ShowZZ          = true;           // whether to draw ZZ
extern bool   ShowMARKERS     = false;           // whether to draw vertical marker lines
extern int    ProfitWidth     = 1;
extern bool   ProfitInPoints  = true;

//chart plotting dates
extern datetime DateStart     = D'1.01.1970';
extern datetime DateEnd       = D'31.12.2037';

//profit calculation parameters
extern double LotForCalc      = 0.05;           // lot size for profit calculation

// RSI indicator parameters
extern int    RSIPeriod       = 8;              // RSI calculation period
extern int    RSILevel        = 73;             // RSI calculation level

// MA indicator parameters
extern int    MAPeriod        = 20;             // МА calculation period
extern int    MAMode          = 3;              // 0-MODE_SMA 1-MODE_EMA 2-MODE_SMMA 3-MODE_LWMA
extern int    MAPrice         = 6;              // 0-Close 1-Open 2-High 3-Low 4-(H+L)/2 5-(H+L+C)/3 6-(H+L+2*C)/4


// MACD indicator parameters
extern double MACDOpenLevel   = 3;              
extern double MACDCloseLevel  = 2;
extern double MATrendPeriod   = 26;

// colors of Buy lines
extern color  ColorProfitBuy  = Blue;           // line color for profitable Buys 
extern color  ColorLossBuy    = Red;            // line color for unprofitable Buys
extern color  ColorZeroBuy    = Gray;           // line color for zero-profit Buys

// colors of Sell lines
extern color  ColorProfitSell = Blue;           // line color for profitable Sells
extern color  ColorLossSell   = Red;            // line color for unprofitable Sells
extern color  ColorZeroSell   = Gray;           // line color for zero-profit Sells

// colors of signal lines
extern color  ColorBuy        = CornflowerBlue; // Buy signal line color
extern color  ColorSell       = HotPink;        // Sell signal line color
extern color  ColorClose      = Gainsboro;      // line color of the signal for closing

//—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
double sBuy[],sCloseBuy[],sSell[],sCloseSell[]; // arrays for signals
int sBuyCnt,sSellCnt,sBuyCloseCnt,sSellCloseCnt;// signal counters
int i,DisplayBars;
double bBallance[]; // for the balance on operations
int IndicatorWindowNo;  // indicator window number
//—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// service codes
#define MrakerPrefix "IO_"
#define SliderPrefix "SL_"
#define OP_CLOSE_BUY  678
#define OP_CLOSE_SELL 876
//—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
int init()   
{ 
  SetIndexBuffer(0,bBallance);
  IndicatorShortName("IndicatorOptimizer");
  
  return(0); 
}
int deinit() 
{ 
  ClearMarkers(); 
  ClearSliders();
  return(0); 
}

double ParamValue(int ParamNo, string ParamName, double ParamValue, double vMin, double vMax, color clr)
{
  double Triangle[3],vCur, WMax, WMin; datetime tt1, tt2; 

  // if there is no triangle, create it
  if(ObjectFind(ParamName) < 0)
  {
    // determine the chart boundaries in the current scale vertically
    WMax = WindowPriceMax();  WMin = WindowPriceMin();

    // calculate the coordinates of points by time...
    tt1 = Time[0] + Period()*60*ParamNo*20; tt2 = tt1 + Period()*60*20;
    // ... and "price"
    vCur = WMin + (ParamValue - vMin) * (WMax - WMin) / (vMax - vMin);
    // create an object and fill it with the color specified in the parameters
    ObjectCreate(ParamName,OBJ_TRIANGLE, 0, tt1,WMax, tt2,vCur, tt1,WMin);
    ObjectSet(ParamName,OBJPROP_COLOR,clr);
  }
  // the triangle exists - get its coordinates
  Triangle[0] = ObjectGet(ParamName,OBJPROP_PRICE1);
  Triangle[1] = ObjectGet(ParamName,OBJPROP_PRICE2);
  Triangle[2] = ObjectGet(ParamName,OBJPROP_PRICE3);
  // arrange the vertices in the order of "increase"
  ArraySort(Triangle);
  // convert the midpoint coordinate to the scale of real values between vMin and vMax
  vCur = vMin + (Triangle[1] - Triangle[0]) / (Triangle[2] - Triangle[0]) * (vMax - vMin);
  // write the value to the object comment
  ObjectSetText(ParamName,DoubleToStr(vCur,2)); 
  // return the value to the main module
  return(vCur); 
}


//—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
int start() 
{ 
  double Profit=0,P1,P2; int CntProfit=0,CntLoose=0,i1,i2;
  // coefficient for conversion of points to the deposit currency
  double ToCurrency = MarketInfo(Symbol(),MODE_TICKVALUE)*LotForCalc;    
  
  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // delete all markers in case the indicator is redrawn
  ClearMarkers(); 
  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // prepare signal counters
  sBuyCnt=0; sSellCnt=0; sBuyCloseCnt=0; sSellCloseCnt=0; 
  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // allocate memory for signal arrays and zero out their values
  DisplayBars=Bars; // number of bars displayed
  ArrayResize(sBuy,DisplayBars);  ArrayInitialize(sBuy,0);
  ArrayResize(sSell,DisplayBars); ArrayInitialize(sSell,0);
  ArrayResize(sCloseBuy,DisplayBars);  ArrayInitialize(sCloseBuy,0);
  ArrayResize(sCloseSell,DisplayBars); ArrayInitialize(sCloseSell,0);

  // prepare balance calculation
  ArrayInitialize(bBallance,0); 
  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————


  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // creating (if necessary) and getting parameters of the control triangle
  MAPeriod  = ParamValue(0, SliderPrefix+"MA Period", MAPeriod, 5, 35, Blue);
  RSIPeriod = ParamValue(1, SliderPrefix+"RSI Period", RSIPeriod, 2, 25, Red);
  RSILevel  = ParamValue(2, SliderPrefix+"RSI Level", RSILevel, 5, 95, Orange);
  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————


  // fill signal arrays with values and count their number
  for(i=DisplayBars;i>=0;i--) 
  {
    double t1=iRSI(NULL,0,RSIPeriod,MAPrice,i+1);
    double t11=iRSI(NULL,0,RSIPeriod,MAPrice,i+2);

    double t2=iMA(NULL,0,MAPeriod,0,MAMode,MAPrice,i+1);

    double t3=iMA(NULL,0,MAPeriod*3,0,MAMode,MAPrice,i+1);

    if (t1>RSILevel&&t11<RSILevel&&t1>t11&&t1>RSILevel&&t2>t3) {sBuy[i]=1; sBuyCnt++;}
    if (t1<(100-RSILevel)&&t11>(100-RSILevel)&&t1<t11&&t1<(100-RSILevel)) {sCloseBuy[i]=1; sBuyCloseCnt++;}

    if (t1<(100-RSILevel)&&t11>(100-RSILevel)&&t1<t11&&t1<(100-RSILevel)&&t2<t3) {sSell[i]=1; sSellCnt++; } 
    if (t1>RSILevel&&t11<RSILevel&&t1>t11&&t1>RSILevel) {sCloseSell[i]=1; sSellCloseCnt++;}
  }

 
        //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // delete repeated signals leaving only the first ones, on the left of the chart
  for(i=0;i<DisplayBars;i++) 
  {
    if(sBuy[i]==sBuy[i+1]) sBuy[i]=0;
    if(sSell[i]==sSell[i+1]) sSell[i]=0;
    if(sCloseBuy[i]==sCloseBuy[i+1]) sCloseBuy[i]=0;
    if(sCloseSell[i]==sCloseSell[i+1]) sCloseSell[i]=0;
  }
  // delete signals outside the specified date range
  for(i=0;i<DisplayBars;i++) 
  {
    if(Time[i]<DateStart || DateEnd<Time[i]) { sBuy[i]=0; sSell[i]=0; sCloseBuy[i]=0; sCloseSell[i]=0; }
  }
  // add forced closing at the range limit
  if(DateEnd<=Time[0]) { i=iBarShift(Symbol(),Period(),DateEnd); sBuy[i]=0; sSell[i]=0; sCloseBuy[i]=1; sCloseSell[i]=1; }
  if(DateEnd >Time[0]) { sCloseBuy[0]=1; sCloseSell[0]=1; }
  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // count the number of signals
  for(i=0;i<DisplayBars;i++) 
  {
    if(sBuy [i]!=0) sBuyCnt++;  if(sCloseBuy [i]!=0) sBuyCloseCnt++;
    if(sSell[i]!=0) sSellCnt++; if(sCloseSell[i]!=0) sSellCloseCnt++;
  }
  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // set markers, draw ZZ and calculate profit
  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // process Buys
  for(i=DisplayBars-1;i>=0;i--) // get the points
  {
    // find the next point of opening and save its position and price
    for(i1=i;i1>=0;i1--) if(sBuy[i1]!=0) break; 

    // find the next Buy closing point and save its position and price
    for(i2=i1-1;i2>=0;i2--) if(sCloseBuy[i2]!=0) break;
    if(i2<0) i2=0; // for the last non-closed position, calculate closing on the current price
    i=i2; // new bar to continue the search for points of opening

    // determine prices for drawing 
    P1=Open[i1]; P2=Open[i2];  
    
    P1/=Point; P2/=Point; // convert prices to points
    
    // determine the profit and fill the corresponding buffer
    if(i1>=0) 
    { 
      Profit=Profit+P2-P1; // get the cumulative profit
      if(P2-P1>=0) CntProfit++; else CntLoose++; // count the number of orders
      DrawLine(i1,i2,OP_BUY); // draw the order line
      PlaceMarker(i1,OP_BUY); 
      PlaceMarker(i2,OP_CLOSE_BUY); 
      
      bBallance[i2] += P2-P1;
    }
  }
  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // process sells
  for(i=DisplayBars-1;i>=0;i--) // get the points
  {
    // find the next point of opening and save its position and price
    for(i1=i;i1>=0;i1--) if(sSell[i1]!=0) break; 

    // find the next Buy closing point and save its position and price
    for(i2=i1-1;i2>=0;i2--) if(sCloseSell[i2]!=0) break;
    if(i2<0) i2=0; // for the last non-closed position, calculate closing on the current price
    i=i2; // new bar to continue the search for points of opening

    // determine prices for drawing depending on the Optimizm parameter
    P1=Open[i1]; P2=Open[i2]; 
    
    P1/=Point; P2/=Point; // convert prices to points
    
    // if both points exist, determine the profit and fill the corresponding buffer
    if(i1>=0) 
    { 
      Profit=Profit+P1-P2; // get the cumulative profit
      if(P1-P2>=0) CntProfit++; else CntLoose++; // count the number of orders
      DrawLine(i1,i2,OP_SELL); // draw the order line
      PlaceMarker(i1,OP_SELL); 
      PlaceMarker(i2,OP_CLOSE_SELL);
      
      bBallance[i2] += P1-P2;
    }
  }

  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // plot the balance chart
  // get profit values one by one and add them up 
  i2 = DisplayBars; bBallance[i2] = 0; 
  for(i=DisplayBars-1;i>=0;i--) { if(bBallance[i] != 0) { bBallance[i2] = bBallance[i2+1] + bBallance[i]; i2--;  } }
  double multiplier; if(ProfitInPoints) multiplier = 1; else multiplier = ToCurrency;
  for(i=0;i<DisplayBars-2;i++) bBallance[i] = bBallance[i+i2+1] * multiplier;
  SetIndexStyle(0,DRAW_HISTOGRAM,STYLE_SOLID,ProfitWidth,Blue);

  //———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // calculation of the final values for the comment  
  int Cnt=CntProfit+CntLoose; // total number of operations
  string msg="Period: "+TimeToStr(MathMax(DateStart,Time[Bars-1]))+" - "+TimeToStr(MathMin(DateEnd,Time[0]))+"\n\n";
  
  msg=msg + 
  "RSI period: " + RSIPeriod + "\n" +
  "RSI level: " + RSILevel + "\n" +
  "МА period: " + MAPeriod + "\n\n" +
  sBuyCnt+" Buys and "+sBuyCloseCnt+" their closings\n"+
  sSellCnt+" Sells and "+sSellCloseCnt+" their closings\n\n";
  
  // total time in days
  int TotalDays = (MathMin(DateEnd,Time[0])-MathMax(DateStart,Time[Bars-1]))/60/60/24; //convert seconds to days
  if(TotalDays<=0) TotalDays=1; // to avoid zero divide for an incomplete day
  
  if(Cnt==0) msg=msg+("No operation");
  else msg=msg+
  (
    DoubleToStr(Profit,0)+" points for "+Cnt+" operations over "+TotalDays+" days\n"+
    DoubleToStr(Profit/Cnt,1)+" points per operation ("+
    DoubleToStr(Profit/TotalDays,1)+" per day)\n\n"+
    "When trading the lot "+DoubleToStr(LotForCalc,2)+" get in "+AccountCurrency()+":\n"+
    DoubleToStr(Profit*ToCurrency,0)+" total, "+
    DoubleToStr(Profit/Cnt*ToCurrency,1)+" per operation ("+
    DoubleToStr(Profit/TotalDays*ToCurrency,1)+" per day)\n\n"+
    CntProfit+" profitable ("+DoubleToStr(((CntProfit)*1.0/Cnt*1.0)*100.0,1)+"%)\n"+
    CntLoose+" losing ("+DoubleToStr(((CntLoose)*1.0/Cnt*1.0)*100.0,1)+"%)"
  );
  Comment(msg);
  
}
//—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————



//—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// deleting all objects from our chart
void ClearMarkers() 
{ 
  for(int i=ObjectsTotal()-1;i>=0;i--) if(StringFind(ObjectName(i),MrakerPrefix)==0) ObjectDelete(ObjectName(i)); 
}
void ClearSliders() 
{ 
  for(int i=ObjectsTotal()-1;i>=0;i--) if(StringFind(ObjectName(i),SliderPrefix)==0) ObjectDelete(ObjectName(i)); 
}
//—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// setting a vertical line - the op_type operation marker
void PlaceMarker(int i, int op_type)
{
  if(!ShowMARKERS) return; // displaying markers is disabled

  color MarkerColor; string MarkName; 

  // __ so that the closing line is drawn below the opening line by sorting 
  if(op_type==OP_CLOSE_SELL) { MarkerColor=ColorClose; MarkName=MrakerPrefix+"__SELL_"+i; }
  if(op_type==OP_CLOSE_BUY)  { MarkerColor=ColorClose; MarkName=MrakerPrefix+"__BUY_"+i;  }  
  if(op_type==OP_SELL)       { MarkerColor=ColorSell;  MarkName=MrakerPrefix+"_SELL_"+i;  }
  if(op_type==OP_BUY)        { MarkerColor=ColorBuy;   MarkName=MrakerPrefix+"_BUY_"+i;   }

  ObjectCreate(MarkName,OBJ_VLINE,0,Time[i],0); 
  ObjectSet(MarkName,OBJPROP_WIDTH,1); 
  if(op_type==OP_CLOSE_BUY || op_type==OP_CLOSE_SELL) ObjectSet(MarkName,OBJPROP_STYLE,STYLE_SOLID); 
  else ObjectSet(MarkName,OBJPROP_STYLE,STYLE_DOT);
  ObjectSet(MarkName,OBJPROP_BACK,True);  

  ObjectSet(MarkName,OBJPROP_COLOR,MarkerColor);
}
//—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// setting the line on the chart for the op_type operation type using the Optimizm parameter values
void DrawLine(int i1,int i2, int op_type)
{
  if(!ShowZZ) return; // displaying ZZ is disabled
  
  color СurColor;
  string MarkName=MrakerPrefix+"_"+i1+"_"+i2;
  double P1,P2;
  
  // determine prices for drawing depending on the Optimizm parameter
  P1=Open[i1]; P2=Open[i2];  

  ObjectCreate(MarkName,OBJ_TREND,0,Time[i1],P1,Time[i2],P2);
  
  ObjectSet(MarkName,OBJPROP_RAY,False); // draw segments instead of lines
  ObjectSet(MarkName,OBJPROP_BACK,False);
  ObjectSet(MarkName,OBJPROP_STYLE,STYLE_SOLID);
  ObjectSet(MarkName,OBJPROP_WIDTH,2);

  // set the line color depending on profitability of the operation
  if(op_type==OP_BUY) 
  { 
    if(P1 <P2) СurColor = ColorProfitBuy;
    if(P1==P2) СurColor = ColorZeroBuy;
    if(P1 >P2) СurColor = ColorLossBuy;
  }
  if(op_type==OP_SELL) 
  { 
    if(P1 >P2) СurColor = ColorProfitSell;
    if(P1==P2) СurColor = ColorZeroSell;
    if(P1 <P2) СurColor = ColorLossSell;
  }
  ObjectSet(MarkName,OBJPROP_COLOR,СurColor);
}
//—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

The detailed description of the algorithm and its operation can be found in my previous article. It should be noted here that I removed the workaround for the Optimizm parameter. In this version of the indicator, signals for the current bar are calculated based on values of the previous, fully completed bars. This suggests the use of the classical approach to trading based on the bar analysis: having analyzed the results of a freshly completed bar, you open a position at the beginning of the current one. It allows us to say that the calculation results obtained are quite reliable.


Optimizer in Action

After compiling the indicator and adding it to the chart, we can proceed to the analysis. This process can be described in detail and at length but it all becomes very clear if you watch a small animation demonstrating live operation of the indicator.

We must note one important thing here: the indicator recalculates its values at each new tick so after moving the triangular slider we should either wait for the new price or refresh the chart manually or use the tick emulator.

Working with the optimizer now turns into a peculiar game with it, known as "what if?". You simply select triangular sliders and start moving their midpoints up/down. The horizontal position of the triangle vertex does not matter; important is only the vertical position of vertices. Thus, you move triangle midpoints and try to grasp what these moves lead to. Your intuition and your hands-on experience will certainly prompt you what and where to move. Well, if you lack the experience, give the trial and error method a go: try, look for variants that would be suitable and later on trade based on the parameters determined.


How to Use the Indicator. Part One: What NOT to Do

This indicator, just like my previous indicator, should be used whilst understanding what you do and what results you get. You can replace the signal calculation block with your own and increase or decrease the number of signals used. But when looking at the results always bear in mind that if you managed to optimize the indicator parameters for EURUSD М30 and it shows a good profit curve, it does not at all mean that the results will be just as great when trading another currency pair or a different time frame or both. To understand why this is so, simply take a look at the М1 and D1 charts. The difference in the nature of bar changes and bar sizes is so obvious that there is no need in further comments.

The difference between currency pairs is not so clear but it can also easily be identified using, for example, the technique described in my article "Market Diagnostics by Pulse". The screenshots provided in that article will demonstrate why you should not expect the same results on all currency pairs, even though the signal calculation block settings are the same.


Ho to Use the Indicator. Part Two: What MUST be Done

It is however not as bad as it seems. As some philosophers say, "demerits are extensions of our merits". If we see the flaws but are yet to spot the advantages, it does not mean there are none. Have a look at the D1 chart with a fairly long steady uptrend. Do you really think that the indicator optimized based on the beginning of this period will not yield profit in the middle of this trend?!

If you decide to use this tool in trading, just like I am going to use it, from time to time you will just need to adjust it for a certain currency pair and time frame. You can take the same Expert Advisor proposed by Helen and try it in automated trading, adjusting its parameters to the optimization results every now and then, especially as the indicator allows you to effortlessly run this process many times a day and thus use it for trading even on minute charts (М5, М15 and М30).


Conclusion

The market is fundamentally unpredictable. Any 100% reliable and verified signal may in a blink of an eye be turned into its opposite by an important news release. In discussing this article, aces of trading will probably try to convince you that you will never know where the price will move and consequently never get a stable profit if you do not take into consideration such things as fundamental signals or if you do not have trade volume data. Well, it is their opinion, this is how they trade and succeed. One can use this trading approach, managing more than one thousand dollars.

I believe that for small deposits this indicator can become one of the most simple and reliable tools, if you like. You will never get the "full picture" of the price movement, so it is useless to look for reversal points and try to squeeze every cent out of the market. A long trend over days and weeks will likely have some general pattern and if you can see its beginning and optimize your signal calculation system according to it, you will be able to successfully earn on the optimized parameters for, probably, not so short a time period. But even if the "wind changes", you will not no longer consider it a big problem since the optimization process will now be much less laborious than it was before you started using this indicator.

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

Attached files |
IndicatorOptimizer.zip (1820.28 KB)
Last comments | Go to discussion (2)
mighty6469
mighty6469 | 20 Dec 2013 at 16:40

Great.

Thank you.

Khai Nguyen
Khai Nguyen | 26 Oct 2022 at 07:58
could you advise how can I use this with something like divergence indication evaluation?
Advanced Analysis of a Trading Account Advanced Analysis of a Trading Account
The article deals with the automatic system for analyzing any trading account in MetaTrader 4 terminal. Technical aspects of a generated report and interpretation of the obtained results are considered. Conclusions on improving trading factors are drawn after the detailed review of the report. MQLab™ Graphic Report script is used for analysis.
Raise Your Linear Trading Systems to the Power Raise Your Linear Trading Systems to the Power
Today's article shows intermediate MQL5 programmers how they can get more profit from their linear trading systems (Fixed Lot) by easily implementing the so-called technique of exponentiation. This is because the resulting equity curve growth is then geometric, or exponential, taking the form of a parabola. Specifically, we will implement a practical MQL5 variant of the Fixed Fractional position sizing developed by Ralph Vince.
Lite_EXPERT2.mqh: Expert Advisor Implementation Examples Lite_EXPERT2.mqh: Expert Advisor Implementation Examples
In this article, the author continues to familiarize the readers with the Lite_EXPERT2.mqh functions using real Expert Advisor implementation examples. The article deals with the idea of using floating pending orders and pending orders that vary dynamically from deal to deal which are determined based on Average True Range (ATR) indicator values.
MQL5 Cookbook: Indicator Subwindow Controls - Scrollbar MQL5 Cookbook: Indicator Subwindow Controls - Scrollbar
Let's continue exploring various controls and this time turn our attention to scrollbar. Just like in the previous article entitled "MQL5 Cookbook: Indicator Subwindow Controls - Buttons", all operations will be performed in the indicator subwindow. Take a moment to read the above mentioned article as it provides a detailed description of working with events in the OnChartEvent() function, while this point will only be casually touched upon in this article. For illustrative purposes, this time around we will create a vertical scrollbar for a large list of all financial instrument properties that can be obtained using MQL5 resources.