Speed up Object Creation Code

 

Hi,

 

I have an indicator, and part of the code draws trend lines on the chart. There are roughly 200 of these lines on each chart, and i have 12 charts running at once. When I change timeframes, obviously all the trend lines need to be redraw, and with 12 charts open it its very sluggish.

 

Is there a more efficient way to get the indicator to draw these lines when the indicator is initialized? Here is the piece of code that draws the lines:

 

void DoBVLines()
  {
   for(int i=loop-1;i>=0;i--)
     {
      if(IgnoreRed==false && i>0 && red[i]>0)
        {
         string name="BV Line "+Time[i];
         //ObjectDelete(0,name);
         if(ObjectFind(0,name)<0)
           {
            double value=LineOffset*Point;//((iATR(Symbol(),0,50,0))*LineOffset)/100;
            ObjectCreate(0,name,OBJ_TREND,0,Time[i],Low[i],Time[i],0);
            ObjectSetInteger(0,name,OBJPROP_COLOR,clrRed);
            ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
            ObjectSetInteger(0,name,OBJPROP_HIDDEN,true);
            ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DOT);
            //break;
           }
        }

      if(IgnoreLime==false && i>0 && green[i]>0)
        {
         string name="BV Line "+Time[i];
         //ObjectDelete(0,name);
         if(ObjectFind(0,name)<0)
           {
            double value=LineOffset*Point;//((iATR(Symbol(),0,50,0))*LineOffset)/100;
            ObjectCreate(0,name,OBJ_TREND,0,Time[i],Low[i]-value,Time[i],0);
            ObjectSetInteger(0,name,OBJPROP_COLOR,clrLime);
            ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
            ObjectSetInteger(0,name,OBJPROP_HIDDEN,true);
            ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DOT);
            //break;
           }
        }
      if(IgnoreMagenta==false && i>0 && magenta[i]>0)
        {
         string names="BV Line "+Time[i];
         //ObjectDelete(0,names);
         if(ObjectFind(0,names)<0)
           {
            double value=LineOffset*Point;//((iATR(Symbol(),0,50,0))*LineOffset)/100;
            ObjectCreate(0,names,OBJ_TREND,0,Time[i],Low[i]-value,Time[i],0);
            ObjectSetInteger(0,names,OBJPROP_COLOR,clrMagenta);
            ObjectSetInteger(0,names,OBJPROP_SELECTABLE,false);
            ObjectSetInteger(0,names,OBJPROP_HIDDEN,true);
            ObjectSetInteger(0,names,OBJPROP_STYLE,STYLE_DOT);
            //break;
           }
        }
      if(IgnoreWhite==false && i>0 && white[i]>0)
        {
         string name="BV Line "+Time[i];
         //ObjectDelete(0,name);
         if(ObjectFind(0,name)<0)
           {
            double value=LineOffset*Point;//((iATR(Symbol(),0,50,0))*LineOffset)/100;
            ObjectCreate(0,name,OBJ_TREND,0,Time[i],Low[i]-value,Time[i],0);
            ObjectSetInteger(0,name,OBJPROP_COLOR,clrWhite);
            ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
            ObjectSetInteger(0,name,OBJPROP_HIDDEN,true);
            ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DOT);
            //break;
           }
        }
     }
  }

 

 

Thanks for your thoughts! 

 
read1985:

Hi,

 

I have an indicator, and part of the code draws trend lines on the chart. There are roughly 200 of these lines on each chart, and i have 12 charts running at once. When I change timeframes, obviously all the trend lines need to be redraw, and with 12 charts open it its very sluggish.

 

Is there a more efficient way to get the indicator to draw these lines when the indicator is initialized? Here is the piece of code that draws the lines:

 

 

 

Thanks for your thoughts! 


Creating 200 lines should not be sluggish. Have you used the profiler to prove that it is this code that is your bottleneck ?

 

As you are using the same name for each line in each if condition, once a line has been created, it cannot be created again. That makes any following if condition redundant.

You could put

            continue;

wjere you have put

            //break;

in your code.

Have you considered using Histogram Buffers instead of trend lines? I've never tested it, but it may be faster than creating objects.

 
jamescater:

Creating 200 lines should not be sluggish.

I second that. The following code, which creates 200 lines but simply does so on each of the last 200 bars, runs on my computer in under a millisecond:

void OnStart()
{
   ObjectsDeleteAll();

   // Start timer after the clean-up...
   uint tcStart = GetTickCount();
   for (int i = 0; i < 200; i++) {
      string name = "Test" + i;

      ObjectCreate(0,name,OBJ_TREND,0,Time[i],Low[i],Time[i],0);
      ObjectSetInteger(0,name,OBJPROP_COLOR,clrRed);
      ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
      ObjectSetInteger(0,name,OBJPROP_HIDDEN,true);
      ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DOT);
   }
   uint tcEnd = GetTickCount();
   
   MessageBox("Duration of line drawing (milliseconds): " + IntegerToString(tcEnd - tcStart));
}

Similar to what jamescater is suggesting, I would put a timer around the call to DoBVLines(), and start by confirming whether that is really the problem, or whether the slowness is somewhere else.

 

Ok its a bit strange:

 

I did think of trying to do it via a buffer, however I have  #property indicator_separate_window - and I would need to lines placed on the main window, So I will not be able to do this.

 

If I run the profiler it shows that the  DoBVLines() is not sluggish at all. However, if I leave it in the indicator, and I try and change timeframes, it takes about 10-15 seconds to actually load the new TF and indicator. If I comment out the DoBVLines(), then it responds much quicker.


It seems to only do this on changing of timeframes, or initialization. 

 
read1985:

If I comment out the DoBVLines(), then it responds much quicker.

Okay, that is weird. One piece of evidence says that it's slow, whereas another piece of evidence, and all expectation, says that it's quick.

All I can think of is that it's a catalyst for a different problem. For example: there is another indicator (or EA) which also creates objects on the charts, and this other indicator is badly written and gets upset and confused by the presence of objects from other indicators. In other words, hypothetically: the issue isn't what DoBVLines() is doing, but instead what it causes another indicator to do.

What happens if this indicator is the only thing on each chart? Is there still a problem? 

By the way, not that it should make any significant difference or solve the puzzling problem, but DoBVLines() can be marginally speeded up in three ways, given the current code:

  • In all cases, you check i>0 before creating an object. Therefore, you should be able to remove each of these checks, and instead loop down to i > 0 rather than i >= 0 in the main for loop
  • Each possible object from IgnoreRed, IgnoreGreen etc has the same name. Therefore, you can do a single ObjectFind(0,name) before applying each individual check such as IgnoreRed etc, and skip all of them if an object already exists
  • If IgnoreRed is true, then you never create an object if IgnoreGreen is also true (...if that's even a possibility) because of the ObjectFind(). Therefore you can do the checks as a slightly faster if ... else if set of conditions, rather than separate unrelated if blocks. 

In other words: 

for(int i=loop-1;i>0;i--)
  string name="BV Line "+Time[i];
  if(ObjectFind(0,name) >= 0) {
    // Object already exists. Don't run any other checks

  } else {

    if(IgnoreRed==false && red[i]>0) {
       // Create object

    } else if (IgnoreLime==false && green[i]>0) {

    etc..
  }
}
 
jjc:

All I can think of [...]

... What does your object removal code look like, when the indicator is being unloaded? If you turn off DoBVLines(), then you are also in effect turning off whatever it is you do to remove objects from the chart when the indicator is unloaded. It could be the latter which is the problem. That fits with the issue being in changing timeframe, rather than in the first load of the indicator onto a chart.
 

Hah! You are correct! it is in the deinit. I never thought to look at how it removes objects rather than create them...

 

int deinit()
  {
//----
   for(int i=ObjectsTotal()-1;i>=0;i--)
     {
      if(StringFind(ObjectName(i),"BV Line ",0)>=0 || StringFind(ObjectName(i),"Climax Bar",0)>=0)
         ObjectDelete(ObjectName(i));
     }
   Clear_Objects(StringLen(objprefix),objprefix);  //Delete created objects
   EmptyBuffers(NumberOfBars+Std_Deviation_Period+MA_Period); WindowRedraw();
//----
   return(0);
  }

 So it is this part of the deinit that is slowing it down:

 

   for(int i=ObjectsTotal()-1;i>=0;i--)
     {
      if(StringFind(ObjectName(i),"BV Line ",0)>=0 || StringFind(ObjectName(i),"Climax Bar",0)>=0)
         ObjectDelete(ObjectName(i));
     }

 

but why would it slow it down, and is there a faster way to delete all these objects? 

 
read1985:

but why would it slow it down, and is there a faster way to delete all these objects? 

You are doing 3 calls to ObjectName() when you could only do 1, but I am surprised that this is the cause of the problem (rather than the following ClearObjects, EmptyBuffers, or WindowRedraw). I'd be more inclined to guess that you were confusing MT4, and causing internal deadlock, by doing a WindowRedraw() while a chart is being unloaded.

I'd be surprised if the following version of the deletion made a significant difference:

for (int i=ObjectsTotal()-1;i>=0;i--) {
  string strName = ObjectName(0, i);
  if (StringFind(strName, "BV Line") >= 0 || StringFind(strName, "Climax Bar") >= 0) {
    ObjectDelete(0, strName);
  }
}
 
read1985:

Hah! You are correct! it is in the deinit. I never thought to look at how it removes objects rather than create them...

So it is this part of the deinit that is slowing it down:

but why would it slow it down, and is there a faster way to delete all these objects? 

ObjectsDeleteAll(0, "Climax Bar")
ObjectsDeleteAll(0, "BV Line")


The function ObjectsDeleteAll should be faster than your loop

https://docs.mql4.com/objects/objectsdeleteall

 
jamescater:


The function ObjectsDeleteAll should be faster than your loop

https://docs.mql4.com/objects/objectsdeleteall


Thank you! That is much much faster.

 

Appreciate all the help everyone!